summaryrefslogtreecommitdiff
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/sandbox
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/OWNERS8
-rw-r--r--chromium/sandbox/linux/sandbox_linux.gypi226
-rw-r--r--chromium/sandbox/linux/sandbox_linux_test_sources.gypi39
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/Makefile30
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/basicblock.cc16
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/basicblock.h51
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/bpf_tests.h122
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen.cc739
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen.h156
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc445
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/demo.cc524
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/die.cc83
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/die.h62
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode.cc106
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode.h192
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc81
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/instruction.h63
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h197
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/port.h36
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc985
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h271
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h23
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc1729
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.cc243
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.h123
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc95
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h56
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc135
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc185
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/trap.cc344
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/trap.h120
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/verifier.cc449
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/verifier.h48
-rw-r--r--chromium/sandbox/linux/services/android_arm_ucontext.h32
-rw-r--r--chromium/sandbox/linux/services/android_i386_ucontext.h79
-rw-r--r--chromium/sandbox/linux/services/android_ucontext.h22
-rw-r--r--chromium/sandbox/linux/services/arm_linux_syscalls.h1377
-rw-r--r--chromium/sandbox/linux/services/broker_process.cc522
-rw-r--r--chromium/sandbox/linux/services/broker_process.h91
-rw-r--r--chromium/sandbox/linux/services/broker_process_unittest.cc438
-rw-r--r--chromium/sandbox/linux/services/libc_urandom_override.cc236
-rw-r--r--chromium/sandbox/linux/services/libc_urandom_override.h14
-rw-r--r--chromium/sandbox/linux/services/linux_syscalls.h25
-rw-r--r--chromium/sandbox/linux/services/x86_32_linux_syscalls.h1398
-rw-r--r--chromium/sandbox/linux/services/x86_64_linux_syscalls.h1266
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc179
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client.h59
-rw-r--r--chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc94
-rw-r--r--chromium/sandbox/linux/suid/common/sandbox.h43
-rw-r--r--chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h73
-rw-r--r--chromium/sandbox/linux/suid/linux_util.c135
-rw-r--r--chromium/sandbox/linux/suid/linux_util.h21
-rw-r--r--chromium/sandbox/linux/suid/process_util.h32
-rw-r--r--chromium/sandbox/linux/suid/process_util_linux.c106
-rw-r--r--chromium/sandbox/linux/suid/sandbox.c482
-rw-r--r--chromium/sandbox/sandbox.gyp30
-rw-r--r--chromium/sandbox/win/sandbox_poc/main_ui_window.cc668
-rw-r--r--chromium/sandbox/win/sandbox_poc/main_ui_window.h193
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/exports.h89
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/fs.cc54
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/handles.cc186
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/invasive.cc196
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/network.cc66
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/pocdll.cc27
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj218
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc102
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/registry.cc63
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc66
-rw-r--r--chromium/sandbox/win/sandbox_poc/pocdll/utils.h65
-rw-r--r--chromium/sandbox/win/sandbox_poc/resource.h30
-rw-r--r--chromium/sandbox/win/sandbox_poc/sandbox.cc182
-rw-r--r--chromium/sandbox/win/sandbox_poc/sandbox.h10
-rw-r--r--chromium/sandbox/win/sandbox_poc/sandbox.icobin0 -> 1078 bytes
-rw-r--r--chromium/sandbox/win/sandbox_poc/sandbox.rc136
-rw-r--r--chromium/sandbox/win/sandbox_poc/sandbox_poc.vcproj202
-rw-r--r--chromium/sandbox/win/sandbox_standalone.sln127
-rw-r--r--chromium/sandbox/win/sandbox_win.gypi364
-rw-r--r--chromium/sandbox/win/src/Wow64.cc219
-rw-r--r--chromium/sandbox/win/src/Wow64.h50
-rw-r--r--chromium/sandbox/win/src/Wow64_64.cc18
-rw-r--r--chromium/sandbox/win/src/acl.cc122
-rw-r--r--chromium/sandbox/win/src/acl.h41
-rw-r--r--chromium/sandbox/win/src/app_container.cc174
-rw-r--r--chromium/sandbox/win/src/app_container.h68
-rw-r--r--chromium/sandbox/win/src/app_container_test.cc143
-rw-r--r--chromium/sandbox/win/src/app_container_unittest.cc58
-rw-r--r--chromium/sandbox/win/src/broker_services.cc510
-rw-r--r--chromium/sandbox/win/src/broker_services.h114
-rw-r--r--chromium/sandbox/win/src/crosscall_client.h483
-rw-r--r--chromium/sandbox/win/src/crosscall_params.h293
-rw-r--r--chromium/sandbox/win/src/crosscall_server.cc301
-rw-r--r--chromium/sandbox/win/src/crosscall_server.h224
-rw-r--r--chromium/sandbox/win/src/eat_resolver.cc91
-rw-r--r--chromium/sandbox/win/src/eat_resolver.h48
-rw-r--r--chromium/sandbox/win/src/file_policy_test.cc599
-rw-r--r--chromium/sandbox/win/src/filesystem_dispatcher.cc297
-rw-r--r--chromium/sandbox/win/src/filesystem_dispatcher.h57
-rw-r--r--chromium/sandbox/win/src/filesystem_interception.cc351
-rw-r--r--chromium/sandbox/win/src/filesystem_interception.h53
-rw-r--r--chromium/sandbox/win/src/filesystem_policy.cc386
-rw-r--r--chromium/sandbox/win/src/filesystem_policy.h107
-rw-r--r--chromium/sandbox/win/src/handle_closer.cc202
-rw-r--r--chromium/sandbox/win/src/handle_closer.h75
-rw-r--r--chromium/sandbox/win/src/handle_closer_agent.cc145
-rw-r--r--chromium/sandbox/win/src/handle_closer_agent.h37
-rw-r--r--chromium/sandbox/win/src/handle_closer_test.cc194
-rw-r--r--chromium/sandbox/win/src/handle_dispatcher.cc90
-rw-r--r--chromium/sandbox/win/src/handle_dispatcher.h37
-rw-r--r--chromium/sandbox/win/src/handle_inheritance_test.cc55
-rw-r--r--chromium/sandbox/win/src/handle_interception.cc45
-rw-r--r--chromium/sandbox/win/src/handle_interception.h24
-rw-r--r--chromium/sandbox/win/src/handle_policy.cc94
-rw-r--r--chromium/sandbox/win/src/handle_policy.h41
-rw-r--r--chromium/sandbox/win/src/handle_policy_test.cc114
-rw-r--r--chromium/sandbox/win/src/handle_table.cc183
-rw-r--r--chromium/sandbox/win/src/handle_table.h159
-rw-r--r--chromium/sandbox/win/src/integrity_level_test.cc90
-rw-r--r--chromium/sandbox/win/src/interception.cc550
-rw-r--r--chromium/sandbox/win/src/interception.h272
-rw-r--r--chromium/sandbox/win/src/interception_agent.cc233
-rw-r--r--chromium/sandbox/win/src/interception_agent.h87
-rw-r--r--chromium/sandbox/win/src/interception_internal.h76
-rw-r--r--chromium/sandbox/win/src/interception_unittest.cc212
-rw-r--r--chromium/sandbox/win/src/interceptors.h54
-rw-r--r--chromium/sandbox/win/src/interceptors_64.cc268
-rw-r--r--chromium/sandbox/win/src/interceptors_64.h169
-rw-r--r--chromium/sandbox/win/src/internal_types.h75
-rw-r--r--chromium/sandbox/win/src/ipc_ping_test.cc58
-rw-r--r--chromium/sandbox/win/src/ipc_tags.h37
-rw-r--r--chromium/sandbox/win/src/ipc_unittest.cc641
-rw-r--r--chromium/sandbox/win/src/job.cc116
-rw-r--r--chromium/sandbox/win/src/job.h62
-rw-r--r--chromium/sandbox/win/src/job_unittest.cc193
-rw-r--r--chromium/sandbox/win/src/named_pipe_dispatcher.cc66
-rw-r--r--chromium/sandbox/win/src/named_pipe_dispatcher.h37
-rw-r--r--chromium/sandbox/win/src/named_pipe_interception.cc72
-rw-r--r--chromium/sandbox/win/src/named_pipe_interception.h36
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy.cc86
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy.h45
-rw-r--r--chromium/sandbox/win/src/named_pipe_policy_test.cc78
-rw-r--r--chromium/sandbox/win/src/nt_internals.h619
-rw-r--r--chromium/sandbox/win/src/policy_broker.cc115
-rw-r--r--chromium/sandbox/win/src/policy_broker.h23
-rw-r--r--chromium/sandbox/win/src/policy_engine_opcodes.cc454
-rw-r--r--chromium/sandbox/win/src/policy_engine_opcodes.h380
-rw-r--r--chromium/sandbox/win/src/policy_engine_params.h202
-rw-r--r--chromium/sandbox/win/src/policy_engine_processor.cc107
-rw-r--r--chromium/sandbox/win/src/policy_engine_processor.h145
-rw-r--r--chromium/sandbox/win/src/policy_engine_unittest.cc102
-rw-r--r--chromium/sandbox/win/src/policy_low_level.cc348
-rw-r--r--chromium/sandbox/win/src/policy_low_level.h182
-rw-r--r--chromium/sandbox/win/src/policy_low_level_unittest.cc617
-rw-r--r--chromium/sandbox/win/src/policy_opcodes_unittest.cc344
-rw-r--r--chromium/sandbox/win/src/policy_params.h66
-rw-r--r--chromium/sandbox/win/src/policy_target.cc127
-rw-r--r--chromium/sandbox/win/src/policy_target.h45
-rw-r--r--chromium/sandbox/win/src/policy_target_test.cc337
-rw-r--r--chromium/sandbox/win/src/process_mitigations.cc332
-rw-r--r--chromium/sandbox/win/src/process_mitigations.h44
-rw-r--r--chromium/sandbox/win/src/process_mitigations_test.cc207
-rw-r--r--chromium/sandbox/win/src/process_policy_test.cc384
-rw-r--r--chromium/sandbox/win/src/process_thread_dispatcher.cc245
-rw-r--r--chromium/sandbox/win/src/process_thread_dispatcher.h47
-rw-r--r--chromium/sandbox/win/src/process_thread_interception.cc447
-rw-r--r--chromium/sandbox/win/src/process_thread_interception.h101
-rw-r--r--chromium/sandbox/win/src/process_thread_policy.cc242
-rw-r--r--chromium/sandbox/win/src/process_thread_policy.h82
-rw-r--r--chromium/sandbox/win/src/registry_dispatcher.cc161
-rw-r--r--chromium/sandbox/win/src/registry_dispatcher.h39
-rw-r--r--chromium/sandbox/win/src/registry_interception.cc176
-rw-r--r--chromium/sandbox/win/src/registry_interception.h38
-rw-r--r--chromium/sandbox/win/src/registry_policy.cc225
-rw-r--r--chromium/sandbox/win/src/registry_policy.h57
-rw-r--r--chromium/sandbox/win/src/registry_policy_test.cc289
-rw-r--r--chromium/sandbox/win/src/resolver.cc62
-rw-r--r--chromium/sandbox/win/src/resolver.h105
-rw-r--r--chromium/sandbox/win/src/resolver_32.cc88
-rw-r--r--chromium/sandbox/win/src/resolver_64.cc69
-rw-r--r--chromium/sandbox/win/src/restricted_token.cc472
-rw-r--r--chromium/sandbox/win/src/restricted_token.h198
-rw-r--r--chromium/sandbox/win/src/restricted_token_unittest.cc593
-rw-r--r--chromium/sandbox/win/src/restricted_token_utils.cc344
-rw-r--r--chromium/sandbox/win/src/restricted_token_utils.h83
-rw-r--r--chromium/sandbox/win/src/sandbox.cc44
-rw-r--r--chromium/sandbox/win/src/sandbox.h165
-rw-r--r--chromium/sandbox/win/src/sandbox.vcproj658
-rw-r--r--chromium/sandbox/win/src/sandbox_factory.h50
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_types.h46
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.cc599
-rw-r--r--chromium/sandbox/win/src/sandbox_nt_util.h173
-rw-r--r--chromium/sandbox/win/src/sandbox_policy.h227
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.cc675
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.h160
-rw-r--r--chromium/sandbox/win/src/sandbox_types.h91
-rw-r--r--chromium/sandbox/win/src/sandbox_utils.cc38
-rw-r--r--chromium/sandbox/win/src/sandbox_utils.h27
-rw-r--r--chromium/sandbox/win/src/security_level.h203
-rw-r--r--chromium/sandbox/win/src/service_resolver.cc42
-rw-r--r--chromium/sandbox/win/src/service_resolver.h148
-rw-r--r--chromium/sandbox/win/src/service_resolver_32.cc420
-rw-r--r--chromium/sandbox/win/src/service_resolver_64.cc193
-rw-r--r--chromium/sandbox/win/src/service_resolver_unittest.cc227
-rw-r--r--chromium/sandbox/win/src/shared_handles.cc67
-rw-r--r--chromium/sandbox/win/src/shared_handles.h108
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_client.cc152
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_client.h136
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_server.cc410
-rw-r--r--chromium/sandbox/win/src/sharedmem_ipc_server.h127
-rw-r--r--chromium/sandbox/win/src/sid.cc26
-rw-r--r--chromium/sandbox/win/src/sid.h29
-rw-r--r--chromium/sandbox/win/src/sid_unittest.cc71
-rw-r--r--chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp92
-rw-r--r--chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp1159
-rw-r--r--chromium/sandbox/win/src/sidestep/mini_disassembler.cpp395
-rw-r--r--chromium/sandbox/win/src/sidestep/mini_disassembler.h156
-rw-r--r--chromium/sandbox/win/src/sidestep/mini_disassembler_types.h197
-rw-r--r--chromium/sandbox/win/src/sidestep/preamble_patcher.h111
-rw-r--r--chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp179
-rw-r--r--chromium/sandbox/win/src/sidestep_resolver.cc202
-rw-r--r--chromium/sandbox/win/src/sidestep_resolver.h73
-rw-r--r--chromium/sandbox/win/src/sync_dispatcher.cc86
-rw-r--r--chromium/sandbox/win/src/sync_dispatcher.h38
-rw-r--r--chromium/sandbox/win/src/sync_interception.cc107
-rw-r--r--chromium/sandbox/win/src/sync_interception.h41
-rw-r--r--chromium/sandbox/win/src/sync_policy.cc112
-rw-r--r--chromium/sandbox/win/src/sync_policy.h51
-rw-r--r--chromium/sandbox/win/src/sync_policy_test.cc146
-rw-r--r--chromium/sandbox/win/src/sync_policy_test.h18
-rw-r--r--chromium/sandbox/win/src/target_interceptions.cc100
-rw-r--r--chromium/sandbox/win/src/target_interceptions.h35
-rw-r--r--chromium/sandbox/win/src/target_process.cc337
-rw-r--r--chromium/sandbox/win/src/target_process.h132
-rw-r--r--chromium/sandbox/win/src/target_services.cc194
-rw-r--r--chromium/sandbox/win/src/target_services.h71
-rw-r--r--chromium/sandbox/win/src/threadpool_unittest.cc94
-rw-r--r--chromium/sandbox/win/src/unload_dll_test.cc96
-rw-r--r--chromium/sandbox/win/src/win2k_threadpool.cc60
-rw-r--r--chromium/sandbox/win/src/win2k_threadpool.h58
-rw-r--r--chromium/sandbox/win/src/win_utils.cc323
-rw-r--r--chromium/sandbox/win/src/win_utils.h110
-rw-r--r--chromium/sandbox/win/src/win_utils_unittest.cc82
-rw-r--r--chromium/sandbox/win/src/window.cc142
-rw-r--r--chromium/sandbox/win/src/window.h39
-rw-r--r--chromium/sandbox/win/tools/finder/finder.cc64
-rw-r--r--chromium/sandbox/win/tools/finder/finder.h143
-rw-r--r--chromium/sandbox/win/tools/finder/finder.vcproj201
-rw-r--r--chromium/sandbox/win/tools/finder/finder_fs.cc117
-rw-r--r--chromium/sandbox/win/tools/finder/finder_kernel.cc248
-rw-r--r--chromium/sandbox/win/tools/finder/finder_registry.cc93
-rw-r--r--chromium/sandbox/win/tools/finder/main.cc147
-rw-r--r--chromium/sandbox/win/tools/finder/ntundoc.h275
-rw-r--r--chromium/sandbox/win/tools/launcher/launcher.cc156
-rw-r--r--chromium/sandbox/win/tools/launcher/launcher.vcproj177
-rw-r--r--chromium/sandbox/win/wow_helper.sln19
-rw-r--r--chromium/sandbox/win/wow_helper/service64_resolver.cc342
-rw-r--r--chromium/sandbox/win/wow_helper/service64_resolver.h72
-rw-r--r--chromium/sandbox/win/wow_helper/target_code.cc34
-rw-r--r--chromium/sandbox/win/wow_helper/target_code.h41
-rw-r--r--chromium/sandbox/win/wow_helper/wow_helper.cc123
-rwxr-xr-xchromium/sandbox/win/wow_helper/wow_helper.exebin0 -> 67072 bytes
-rw-r--r--chromium/sandbox/win/wow_helper/wow_helper.pdbbin0 -> 699392 bytes
-rw-r--r--chromium/sandbox/win/wow_helper/wow_helper.vcproj223
262 files changed, 49861 insertions, 0 deletions
diff --git a/chromium/sandbox/OWNERS b/chromium/sandbox/OWNERS
new file mode 100644
index 00000000000..018422fcc80
--- /dev/null
+++ b/chromium/sandbox/OWNERS
@@ -0,0 +1,8 @@
+cpu@chromium.org
+jschuh@chromium.org
+nsylvain@chromium.org
+rvargas@chromium.org
+# For Linux:
+markus@chromium.org
+jln@chromium.org
+cevans@chromium.org
diff --git a/chromium/sandbox/linux/sandbox_linux.gypi b/chromium/sandbox/linux/sandbox_linux.gypi
new file mode 100644
index 00000000000..29639f8f648
--- /dev/null
+++ b/chromium/sandbox/linux/sandbox_linux.gypi
@@ -0,0 +1,226 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'conditions': [
+ ['OS=="linux"', {
+ 'compile_suid_client': 1,
+ }, {
+ 'compile_suid_client': 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,
+ }],
+ ],
+ },
+ 'target_defaults': {
+ 'target_conditions': [
+ # All linux/ files will automatically be excluded on Android
+ # so make sure we re-include them explicitly.
+ ['OS == "android"', {
+ 'sources/': [
+ ['include', '^linux/'],
+ ],
+ }],
+ ],
+ },
+ '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.
+ {
+ 'target_name': 'sandbox',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_services',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'dependencies': [
+ 'suid_sandbox_client',
+ ],
+ }],
+ # Compile seccomp BPF when we support it.
+ [ 'compile_seccomp_bpf==1', {
+ 'dependencies': [
+ 'seccomp_bpf',
+ ],
+ }],
+ ],
+ },
+ {
+ # The main sandboxing test target.
+ 'target_name': 'sandbox_linux_unittests',
+ 'includes': [
+ 'sandbox_linux_test_sources.gypi',
+ ],
+ 'type': 'executable',
+ },
+ {
+ # 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" and gtest_target_type == "shared_library"', {
+ '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',
+ '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/port.h',
+ 'seccomp-bpf/sandbox_bpf.cc',
+ 'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/sandbox_bpf_policy_forward.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',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services_headers',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ {
+ # The setuid sandbox, for Linux
+ 'target_name': 'chrome_sandbox',
+ 'type': 'executable',
+ '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',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ { 'target_name': 'sandbox_services',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/broker_process.cc',
+ 'services/broker_process.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ { 'target_name': 'sandbox_services_headers',
+ 'type': 'none',
+ 'sources': [
+ 'services/android_arm_ucontext.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',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ # We make this its own target so that it does not interfere
+ # with our tests.
+ 'target_name': 'libc_urandom_override',
+ 'type': 'static_library',
+ 'sources': [
+ 'services/libc_urandom_override.cc',
+ 'services/libc_urandom_override.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ {
+ 'target_name': 'suid_sandbox_client',
+ 'type': 'static_library',
+ 'sources': [
+ 'suid/common/sandbox.h',
+ 'suid/common/suid_unsafe_environment_variables.h',
+ 'suid/client/setuid_sandbox_client.cc',
+ 'suid/client/setuid_sandbox_client.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ ],
+ 'conditions': [
+ # Strategy copied from base_unittests_apk in base/base.gyp.
+ [ 'OS=="android" and gtest_target_type == "shared_library"', {
+ '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',
+ ],
+ 'includes': [ '../../build/apk_test.gypi' ],
+ }
+ ],
+ }],
+ ],
+}
diff --git a/chromium/sandbox/linux/sandbox_linux_test_sources.gypi b/chromium/sandbox/linux/sandbox_linux_test_sources.gypi
new file mode 100644
index 00000000000..81190cd564e
--- /dev/null
+++ b/chromium/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -0,0 +1,39 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Tests need to be compiled in the same link unit, so we have to list them
+# in a separate .gypi file.
+{
+ 'dependencies': [
+ 'sandbox',
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'tests/main.cc',
+ 'tests/unit_tests.cc',
+ 'tests/unit_tests.h',
+ 'services/broker_process_unittest.cc',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'sources': [
+ 'suid/client/setuid_sandbox_client_unittest.cc',
+ ],
+ }],
+ [ 'compile_seccomp_bpf==1', {
+ 'sources': [
+ 'seccomp-bpf/bpf_tests.h',
+ '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',
+ ],
+ }],
+ ],
+}
diff --git a/chromium/sandbox/linux/seccomp-bpf/Makefile b/chromium/sandbox/linux/seccomp-bpf/Makefile
new file mode 100644
index 00000000000..6b355805970
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/Makefile
@@ -0,0 +1,30 @@
+DEF_CFLAGS = -g -O3 -Wall -Werror -Wextra -Wno-missing-field-initializers -fPIC -I.
+DEF_CPPFLAGS = -D_GNU_SOURCE -DSECCOMP_BPF_STANDALONE -iquote ../../..
+DEF_LDFLAGS = -g -lpthread
+DEPFLAGS = -MMD -MF .$@.d
+MODS := demo sandbox_bpf basicblock codegen die errorcode syscall syscall_iterator trap verifier
+OBJS64 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o64/')
+OBJS32 := $(shell echo ${MODS} | xargs -n 1 | sed -e 's/$$/.o32/')
+ALL_OBJS = $(OBJS32) $(OBJS64)
+DEP_FILES = $(wildcard $(foreach f,$(ALL_OBJS),.$(f).d))
+
+.SUFFIXES: .o64 .o32
+
+all: demo32 demo64
+
+clean:
+ $(RM) demo32 demo64
+ $(RM) *.o *.o32 *.o64 .*.d
+ $(RM) core core.* vgcore vgcore.* strace.log*
+
+-include $(DEP_FILES)
+
+demo32: ${OBJS32}
+ ${CXX} -m32 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS}
+demo64: ${OBJS64}
+ ${CXX} -m64 -o $@ $+ ${DEF_LDFLAGS} ${LDFLAGS}
+
+.cc.o32:
+ ${CXX} -m32 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
+.cc.o64:
+ ${CXX} -m64 ${DEF_CFLAGS} ${DEF_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} ${DEPFLAGS} -c -o $@ $<
diff --git a/chromium/sandbox/linux/seccomp-bpf/basicblock.cc b/chromium/sandbox/linux/seccomp-bpf/basicblock.cc
new file mode 100644
index 00000000000..bf27c582e29
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/basicblock.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/basicblock.h"
+
+
+namespace playground2 {
+
+BasicBlock::BasicBlock() {
+}
+
+BasicBlock::~BasicBlock() {
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/basicblock.h b/chromium/sandbox/linux/seccomp-bpf/basicblock.h
new file mode 100644
index 00000000000..1782a8076bd
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/basicblock.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__
+
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/instruction.h"
+
+
+namespace playground2 {
+
+struct BasicBlock {
+ BasicBlock();
+ ~BasicBlock();
+
+ // Our implementation of the code generator uses a "Less" operator to
+ // identify common sequences of basic blocks. This would normally be
+ // really easy to do, but STL requires us to wrap the comparator into
+ // a class. We begrudgingly add some code here that provides this wrapping.
+ template<class T> class Less {
+ public:
+ Less(const T& data, int (*cmp)(const BasicBlock *, const BasicBlock *,
+ const T& data))
+ : data_(data),
+ cmp_(cmp) {
+ }
+
+ bool operator() (const BasicBlock *a, const BasicBlock *b) const {
+ return cmp_(a, b, data_) < 0;
+ }
+
+ private:
+ const T& data_;
+ int (*cmp_)(const BasicBlock *, const BasicBlock *, const T&);
+ };
+
+ // Basic blocks are essentially nothing more than a set of instructions.
+ std::vector<Instruction *> instructions;
+
+ // In order to compute relative branch offsets we need to keep track of
+ // how far our block is away from the very last basic block. The "offset_"
+ // is measured in number of BPF instructions.
+ int offset;
+};
+
+} // namespace playground2
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
new file mode 100644
index 00000000000..83fc10a734f
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#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 "build/build_config.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() 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.
+// BPF_TEST() takes a C++ data type as an optional fourth parameter. If
+// present, this sets up a variable that can be accessed as "BPF_AUX". This
+// variable will be passed as an argument to the "policy" function. Policies
+// would typically use it as an argument to Sandbox::Trap(), if they want to
+// communicate data between the BPF_TEST() and a Trap() function.
+#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 {
+ public:
+ typedef Aux AuxType;
+
+ class TestArgs {
+ public:
+ TestArgs(void (*t)(AuxType&), playground2::Sandbox::EvaluateSyscall p)
+ : test_(t),
+ policy_(p),
+ aux_() {
+ }
+
+ void (*test() const)(AuxType&) { return test_; }
+ playground2::Sandbox::EvaluateSyscall policy() const { return policy_; }
+
+ private:
+ friend class BpfTests;
+
+ void (*test_)(AuxType&);
+ playground2::Sandbox::EvaluateSyscall policy_;
+ AuxType aux_;
+ };
+
+ static void TestWrapper(void *void_arg) {
+ TestArgs *arg = reinterpret_cast<TestArgs *>(void_arg);
+ playground2::Die::EnableSimpleExit();
+ if (playground2::Sandbox::SupportsSeccompSandbox(-1) ==
+ playground2::Sandbox::STATUS_AVAILABLE) {
+ // Ensure the the sandbox is actually available at this time
+ int proc_fd;
+ BPF_ASSERT((proc_fd = open("/proc", O_RDONLY|O_DIRECTORY)) >= 0);
+ BPF_ASSERT(playground2::Sandbox::SupportsSeccompSandbox(proc_fd) ==
+ playground2::Sandbox::STATUS_AVAILABLE);
+
+ // Initialize and then start the sandbox with our custom policy
+ playground2::Sandbox sandbox;
+ sandbox.set_proc_fd(proc_fd);
+ sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
+ sandbox.Sandbox::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.
+ playground2::Sandbox sandbox;
+ sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_);
+ playground2::Sandbox::Program *program =
+ sandbox.AssembleFilter(true /* force_verification */);
+ delete program;
+ sandbox::UnitTests::IgnoreThisTest();
+ }
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BpfTests);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.cc b/chromium/sandbox/linux/seccomp-bpf/codegen.cc
new file mode 100644
index 00000000000..17b5d846dc9
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/codegen.cc
@@ -0,0 +1,739 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+
+
+namespace {
+
+// Helper function for Traverse().
+void TraverseRecursively(std::set<playground2::Instruction *> *visited,
+ playground2::Instruction *instruction) {
+ if (visited->find(instruction) == visited->end()) {
+ visited->insert(instruction);
+ switch (BPF_CLASS(instruction->code)) {
+ case BPF_JMP:
+ if (BPF_OP(instruction->code) != BPF_JA) {
+ TraverseRecursively(visited, instruction->jf_ptr);
+ }
+ TraverseRecursively(visited, instruction->jt_ptr);
+ break;
+ case BPF_RET:
+ break;
+ default:
+ TraverseRecursively(visited, instruction->next);
+ break;
+ }
+ }
+}
+
+} // namespace
+
+namespace playground2 {
+
+CodeGen::CodeGen()
+ : compiled_(false) {
+}
+
+CodeGen::~CodeGen() {
+ for (Instructions::iterator iter = instructions_.begin();
+ iter != instructions_.end();
+ ++iter) {
+ delete *iter;
+ }
+ for (BasicBlocks::iterator iter = basic_blocks_.begin();
+ iter != basic_blocks_.end();
+ ++iter) {
+ delete *iter;
+ }
+}
+
+void CodeGen::PrintProgram(const Sandbox::Program& program) {
+ for (Sandbox::Program::const_iterator iter = program.begin();
+ iter != program.end();
+ ++iter) {
+ int ip = (int)(iter - program.begin());
+ fprintf(stderr, "%3d) ", ip);
+ switch (BPF_CLASS(iter->code)) {
+ case BPF_LD:
+ if (iter->code == BPF_LD+BPF_W+BPF_ABS) {
+ fprintf(stderr, "LOAD %d // ", (int)iter->k);
+ if (iter->k == offsetof(struct arch_seccomp_data, nr)) {
+ fprintf(stderr, "System call number\n");
+ } else if (iter->k == offsetof(struct arch_seccomp_data, arch)) {
+ fprintf(stderr, "Architecture\n");
+ } else if (iter->k == offsetof(struct arch_seccomp_data,
+ instruction_pointer)) {
+ fprintf(stderr, "Instruction pointer (LSB)\n");
+ } else if (iter->k == offsetof(struct arch_seccomp_data,
+ instruction_pointer) + 4) {
+ fprintf(stderr, "Instruction pointer (MSB)\n");
+ } else if (iter->k >= offsetof(struct arch_seccomp_data, args) &&
+ iter->k < offsetof(struct arch_seccomp_data, args)+48 &&
+ (iter->k-offsetof(struct arch_seccomp_data, args))%4 == 0) {
+ fprintf(stderr, "Argument %d (%cSB)\n",
+ (int)(iter->k-offsetof(struct arch_seccomp_data, args))/8,
+ (iter->k-offsetof(struct arch_seccomp_data,
+ args))%8 ? 'M' : 'L');
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ } else {
+ fprintf(stderr, "LOAD ???\n");
+ }
+ break;
+ case BPF_JMP:
+ if (BPF_OP(iter->code) == BPF_JA) {
+ fprintf(stderr, "JMP %d\n", ip + iter->k + 1);
+ } else {
+ fprintf(stderr, "if A %s 0x%x; then JMP %d else JMP %d\n",
+ BPF_OP(iter->code) == BPF_JSET ? "&" :
+ BPF_OP(iter->code) == BPF_JEQ ? "==" :
+ BPF_OP(iter->code) == BPF_JGE ? ">=" :
+ BPF_OP(iter->code) == BPF_JGT ? ">" : "???",
+ (int)iter->k,
+ ip + iter->jt + 1, ip + iter->jf + 1);
+ }
+ break;
+ case BPF_RET:
+ fprintf(stderr, "RET 0x%x // ", iter->k);
+ if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) {
+ fprintf(stderr, "Trap #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if ((iter->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ fprintf(stderr, "errno = %d\n", iter->k & SECCOMP_RET_DATA);
+ } else if (iter->k == SECCOMP_RET_ALLOW) {
+ fprintf(stderr, "Allowed\n");
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ break;
+ case BPF_ALU:
+ fprintf(stderr, BPF_OP(iter->code) == BPF_NEG
+ ? "A := -A\n" : "A := A %s 0x%x\n",
+ BPF_OP(iter->code) == BPF_ADD ? "+" :
+ BPF_OP(iter->code) == BPF_SUB ? "-" :
+ BPF_OP(iter->code) == BPF_MUL ? "*" :
+ BPF_OP(iter->code) == BPF_DIV ? "/" :
+ BPF_OP(iter->code) == BPF_MOD ? "%" :
+ BPF_OP(iter->code) == BPF_OR ? "|" :
+ BPF_OP(iter->code) == BPF_XOR ? "^" :
+ BPF_OP(iter->code) == BPF_AND ? "&" :
+ BPF_OP(iter->code) == BPF_LSH ? "<<" :
+ BPF_OP(iter->code) == BPF_RSH ? ">>" : "???",
+ (int)iter->k);
+ break;
+ default:
+ fprintf(stderr, "???\n");
+ break;
+ }
+ }
+ return;
+}
+
+Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k,
+ Instruction *next) {
+ // We can handle non-jumping instructions and "always" jumps. Both of
+ // them are followed by exactly one "next" instruction.
+ // We allow callers to defer specifying "next", but then they must call
+ // "joinInstructions" later.
+ if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
+ SANDBOX_DIE("Must provide both \"true\" and \"false\" branch "
+ "for a BPF_JMP");
+ }
+ if (next && BPF_CLASS(code) == BPF_RET) {
+ SANDBOX_DIE("Cannot append instructions after a return statement");
+ }
+ if (BPF_CLASS(code) == BPF_JMP) {
+ // "Always" jumps use the "true" branch target, only.
+ Instruction *insn = new Instruction(code, 0, next, NULL);
+ instructions_.push_back(insn);
+ return insn;
+ } else {
+ // Non-jumping instructions do not use any of the branch targets.
+ Instruction *insn = new Instruction(code, k, next);
+ instructions_.push_back(insn);
+ return insn;
+ }
+}
+
+Instruction *CodeGen::MakeInstruction(uint16_t code, const ErrorCode& err) {
+ if (BPF_CLASS(code) != BPF_RET) {
+ SANDBOX_DIE("ErrorCodes can only be used in return expressions");
+ }
+ if (err.error_type_ != ErrorCode::ET_SIMPLE &&
+ err.error_type_ != ErrorCode::ET_TRAP) {
+ SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program");
+ }
+ return MakeInstruction(code, err.err_);
+}
+
+Instruction *CodeGen::MakeInstruction(uint16_t code, uint32_t k,
+ Instruction *jt, Instruction *jf) {
+ // We can handle all conditional jumps. They are followed by both a
+ // "true" and a "false" branch.
+ if (BPF_CLASS(code) != BPF_JMP || BPF_OP(code) == BPF_JA) {
+ SANDBOX_DIE("Expected a BPF_JMP instruction");
+ }
+ if (!jt && !jf) {
+ // We allow callers to defer specifying exactly one of the branch
+ // targets. It must then be set later by calling "JoinInstructions".
+ SANDBOX_DIE("Branches must jump to a valid instruction");
+ }
+ Instruction *insn = new Instruction(code, k, jt, jf);
+ instructions_.push_back(insn);
+ return insn;
+}
+
+void CodeGen::JoinInstructions(Instruction *head, Instruction *tail) {
+ // Merge two instructions, or set the branch target for an "always" jump.
+ // This function should be called, if the caller didn't initially provide
+ // a value for "next" when creating the instruction.
+ if (BPF_CLASS(head->code) == BPF_JMP) {
+ if (BPF_OP(head->code) == BPF_JA) {
+ if (head->jt_ptr) {
+ SANDBOX_DIE("Cannot append instructions in the middle of a sequence");
+ }
+ head->jt_ptr = tail;
+ } else {
+ if (!head->jt_ptr && head->jf_ptr) {
+ head->jt_ptr = tail;
+ } else if (!head->jf_ptr && head->jt_ptr) {
+ head->jf_ptr = tail;
+ } else {
+ SANDBOX_DIE("Cannot append instructions after a jump");
+ }
+ }
+ } else if (BPF_CLASS(head->code) == BPF_RET) {
+ SANDBOX_DIE("Cannot append instructions after a return statement");
+ } else if (head->next) {
+ SANDBOX_DIE("Cannot append instructions in the middle of a sequence");
+ } else {
+ head->next = tail;
+ }
+ return;
+}
+
+void CodeGen::Traverse(Instruction *instruction,
+ void (*fnc)(Instruction *, void *), void *aux) {
+ std::set<Instruction *> visited;
+ TraverseRecursively(&visited, instruction);
+ for (std::set<Instruction *>::const_iterator iter = visited.begin();
+ iter != visited.end();
+ ++iter) {
+ fnc(*iter, aux);
+ }
+}
+
+void CodeGen::FindBranchTargets(const Instruction& instructions,
+ BranchTargets *branch_targets) {
+ // Follow all possible paths through the "instructions" graph and compute
+ // a list of branch targets. This will later be needed to compute the
+ // boundaries of basic blocks.
+ // We maintain a set of all instructions that we have previously seen. This
+ // set ultimately converges on all instructions in the program.
+ std::set<const Instruction *> seen_instructions;
+ Instructions stack;
+ for (const Instruction *insn = &instructions; insn; ) {
+ seen_instructions.insert(insn);
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // Found a jump. Increase count of incoming edges for each of the jump
+ // targets.
+ ++(*branch_targets)[insn->jt_ptr];
+ if (BPF_OP(insn->code) != BPF_JA) {
+ ++(*branch_targets)[insn->jf_ptr];
+ stack.push_back(const_cast<Instruction *>(insn));
+ }
+ // Start a recursive decent for depth-first traversal.
+ if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
+ // We haven't seen the "true" branch yet. Traverse it now. We have
+ // already remembered the "false" branch on the stack and will
+ // traverse it later.
+ insn = insn->jt_ptr;
+ continue;
+ } else {
+ // Now try traversing the "false" branch.
+ insn = NULL;
+ }
+ } else {
+ // This is a non-jump instruction, just continue to the next instruction
+ // (if any). It's OK if "insn" becomes NULL when reaching a return
+ // instruction.
+ if (!insn->next != (BPF_CLASS(insn->code) == BPF_RET)) {
+ SANDBOX_DIE("Internal compiler error; return instruction must be at "
+ "the end of the BPF program");
+ }
+ if (seen_instructions.find(insn->next) == seen_instructions.end()) {
+ insn = insn->next;
+ } else {
+ // We have seen this instruction before. That could happen if it is
+ // a branch target. No need to continue processing.
+ insn = NULL;
+ }
+ }
+ while (!insn && !stack.empty()) {
+ // We are done processing all the way to a leaf node, backtrack up the
+ // stack to any branches that we haven't processed yet. By definition,
+ // this has to be a "false" branch, as we always process the "true"
+ // branches right away.
+ insn = stack.back();
+ stack.pop_back();
+ if (seen_instructions.find(insn->jf_ptr) == seen_instructions.end()) {
+ // We haven't seen the "false" branch yet. So, that's where we'll
+ // go now.
+ insn = insn->jf_ptr;
+ } else {
+ // We have seen both the "true" and the "false" branch, continue
+ // up the stack.
+ if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) {
+ SANDBOX_DIE("Internal compiler error; cannot find all "
+ "branch targets");
+ }
+ insn = NULL;
+ }
+ }
+ }
+ return;
+}
+
+BasicBlock *CodeGen::MakeBasicBlock(Instruction *head,
+ Instruction *tail) {
+ // Iterate over all the instructions between "head" and "tail" and
+ // insert them into a new basic block.
+ BasicBlock *bb = new BasicBlock;
+ for (;; head = head->next) {
+ bb->instructions.push_back(head);
+ if (head == tail) {
+ break;
+ }
+ if (BPF_CLASS(head->code) == BPF_JMP) {
+ SANDBOX_DIE("Found a jump inside of a basic block");
+ }
+ }
+ basic_blocks_.push_back(bb);
+ return bb;
+}
+
+void CodeGen::AddBasicBlock(Instruction *head,
+ Instruction *tail,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks *basic_blocks,
+ BasicBlock **firstBlock) {
+ // Add a new basic block to "basic_blocks". Also set "firstBlock", if it
+ // has not been set before.
+ BranchTargets::const_iterator iter = branch_targets.find(head);
+ if ((iter == branch_targets.end()) != !*firstBlock ||
+ !*firstBlock != basic_blocks->empty()) {
+ SANDBOX_DIE("Only the very first basic block should have no "
+ "incoming jumps");
+ }
+ BasicBlock *bb = MakeBasicBlock(head, tail);
+ if (!*firstBlock) {
+ *firstBlock = bb;
+ }
+ (*basic_blocks)[head] = bb;
+ return;
+}
+
+BasicBlock *CodeGen::CutGraphIntoBasicBlocks(
+ Instruction *instructions, const BranchTargets& branch_targets,
+ TargetsToBlocks *basic_blocks) {
+ // Textbook implementation of a basic block generator. All basic blocks
+ // start with a branch target and end with either a return statement or
+ // a jump (or are followed by an instruction that forms the beginning of a
+ // new block). Both conditional and "always" jumps are supported.
+ BasicBlock *first_block = NULL;
+ std::set<const Instruction *> seen_instructions;
+ Instructions stack;
+ Instruction *tail = NULL;
+ Instruction *head = instructions;
+ for (Instruction *insn = head; insn; ) {
+ if (seen_instructions.find(insn) != seen_instructions.end()) {
+ // We somehow went in a circle. This should never be possible. Not even
+ // cyclic graphs are supposed to confuse us this much.
+ SANDBOX_DIE("Internal compiler error; cannot compute basic blocks");
+ }
+ seen_instructions.insert(insn);
+ if (tail && branch_targets.find(insn) != branch_targets.end()) {
+ // We reached a branch target. Start a new basic block (this means,
+ // flushing the previous basic block first).
+ AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block);
+ head = insn;
+ }
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // We reached a jump instruction, this completes our current basic
+ // block. Flush it and continue by traversing both the true and the
+ // false branch of the jump. We need to maintain a stack to do so.
+ AddBasicBlock(head, insn, branch_targets, basic_blocks, &first_block);
+ if (BPF_OP(insn->code) != BPF_JA) {
+ stack.push_back(insn->jf_ptr);
+ }
+ insn = insn->jt_ptr;
+
+ // If we are jumping to an instruction that we have previously
+ // processed, we are done with this branch. Continue by backtracking
+ // up the stack.
+ while (seen_instructions.find(insn) != seen_instructions.end()) {
+ backtracking:
+ if (stack.empty()) {
+ // We successfully traversed all reachable instructions.
+ return first_block;
+ } else {
+ // Going up the stack.
+ insn = stack.back();
+ stack.pop_back();
+ }
+ }
+ // Starting a new basic block.
+ tail = NULL;
+ head = insn;
+ } else {
+ // We found a non-jumping instruction, append it to current basic
+ // block.
+ tail = insn;
+ insn = insn->next;
+ if (!insn) {
+ // We reached a return statement, flush the current basic block and
+ // backtrack up the stack.
+ AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block);
+ goto backtracking;
+ }
+ }
+ }
+ return first_block;
+}
+
+// We define a comparator that inspects the sequence of instructions in our
+// basic block and any blocks referenced by this block. This function can be
+// used in a "less" comparator for the purpose of storing pointers to basic
+// blocks in STL containers; this gives an easy option to use STL to find
+// shared tail sequences of basic blocks.
+static int PointerCompare(const BasicBlock *block1, const BasicBlock *block2,
+ const TargetsToBlocks& blocks) {
+ // Return <0, 0, or >0 depending on the ordering of "block1" and "block2".
+ // If we are looking at the exact same block, this is trivial and we don't
+ // need to do a full comparison.
+ if (block1 == block2) {
+ return 0;
+ }
+
+ // We compare the sequence of instructions in both basic blocks.
+ const Instructions& insns1 = block1->instructions;
+ const Instructions& insns2 = block2->instructions;
+ 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;
+ }
+
+ // 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 {
+ return insn1.code - insn2.code;
+ }
+ }
+}
+
+void CodeGen::MergeTails(TargetsToBlocks *blocks) {
+ // We enter all of our basic blocks into a set using the BasicBlock::Less()
+ // comparator. This naturally results in blocks with identical tails of
+ // instructions to map to the same entry in the set. Whenever we discover
+ // that a particular chain of instructions is already in the set, we merge
+ // the basic blocks and update the pointer in the "blocks" map.
+ // Returns the number of unique basic blocks.
+ // N.B. We don't merge instructions on a granularity that is finer than
+ // a basic block. In practice, this is sufficiently rare that we don't
+ // incur a big cost.
+ // Similarly, we currently don't merge anything other than tails. In
+ // the future, we might decide to revisit this decision and attempt to
+ // merge arbitrary sub-sequences of instructions.
+ BasicBlock::Less<TargetsToBlocks> less(*blocks, PointerCompare);
+ typedef std::set<BasicBlock *, BasicBlock::Less<TargetsToBlocks> > Set;
+ Set seen_basic_blocks(less);
+ for (TargetsToBlocks::iterator iter = blocks->begin();
+ iter != blocks->end();
+ ++iter) {
+ BasicBlock *bb = iter->second;
+ Set::const_iterator entry = seen_basic_blocks.find(bb);
+ if (entry == seen_basic_blocks.end()) {
+ // This is the first time we see this particular sequence of
+ // instructions. Enter the basic block into the set of known
+ // basic blocks.
+ seen_basic_blocks.insert(bb);
+ } else {
+ // We have previously seen another basic block that defines the same
+ // sequence of instructions. Merge the two blocks and update the
+ // pointer in the "blocks" map.
+ iter->second = *entry;
+ }
+ }
+}
+
+void CodeGen::ComputeIncomingBranches(BasicBlock *block,
+ const TargetsToBlocks& targets_to_blocks,
+ IncomingBranches *incoming_branches) {
+ // We increment the number of incoming branches each time we encounter a
+ // basic block. But we only traverse recursively the very first time we
+ // encounter a new block. This is necessary to make topological sorting
+ // work correctly.
+ if (++(*incoming_branches)[block] == 1) {
+ Instruction *last_insn = block->instructions.back();
+ if (BPF_CLASS(last_insn->code) == BPF_JMP) {
+ ComputeIncomingBranches(
+ targets_to_blocks.find(last_insn->jt_ptr)->second,
+ targets_to_blocks, incoming_branches);
+ if (BPF_OP(last_insn->code) != BPF_JA) {
+ ComputeIncomingBranches(
+ targets_to_blocks.find(last_insn->jf_ptr)->second,
+ targets_to_blocks, incoming_branches);
+ }
+ } else if (BPF_CLASS(last_insn->code) != BPF_RET) {
+ ComputeIncomingBranches(targets_to_blocks.find(last_insn->next)->second,
+ targets_to_blocks, incoming_branches);
+ }
+ }
+}
+
+void CodeGen::TopoSortBasicBlocks(BasicBlock *first_block,
+ const TargetsToBlocks& blocks,
+ BasicBlocks *basic_blocks) {
+ // Textbook implementation of a toposort. We keep looking for basic blocks
+ // that don't have any incoming branches (initially, this is just the
+ // "first_block") and add them to the topologically sorted list of
+ // "basic_blocks". As we do so, we remove outgoing branches. This potentially
+ // ends up making our descendants eligible for the sorted list. The
+ // sorting algorithm terminates when there are no more basic blocks that have
+ // no incoming branches. If we didn't move all blocks from the set of
+ // "unordered_blocks" to the sorted list of "basic_blocks", there must have
+ // been a cyclic dependency. This should never happen in a BPF program, as
+ // well-formed BPF programs only ever have forward branches.
+ IncomingBranches unordered_blocks;
+ ComputeIncomingBranches(first_block, blocks, &unordered_blocks);
+
+ std::set<BasicBlock *> heads;
+ for (;;) {
+ // Move block from "unordered_blocks" to "basic_blocks".
+ basic_blocks->push_back(first_block);
+
+ // Inspect last instruction in the basic block. This is typically either a
+ // jump or a return statement. But it could also be a "normal" instruction
+ // that is followed by a jump target.
+ Instruction *last_insn = first_block->instructions.back();
+ if (BPF_CLASS(last_insn->code) == BPF_JMP) {
+ // Remove outgoing branches. This might end up moving our descendants
+ // into set of "head" nodes that no longer have any incoming branches.
+ TargetsToBlocks::const_iterator iter;
+ if (BPF_OP(last_insn->code) != BPF_JA) {
+ iter = blocks.find(last_insn->jf_ptr);
+ if (!--unordered_blocks[iter->second]) {
+ heads.insert(iter->second);
+ }
+ }
+ iter = blocks.find(last_insn->jt_ptr);
+ if (!--unordered_blocks[iter->second]) {
+ first_block = iter->second;
+ continue;
+ }
+ } else if (BPF_CLASS(last_insn->code) != BPF_RET) {
+ // We encountered an instruction that doesn't change code flow. Try to
+ // pick the next "first_block" from "last_insn->next", if possible.
+ TargetsToBlocks::const_iterator iter;
+ iter = blocks.find(last_insn->next);
+ if (!--unordered_blocks[iter->second]) {
+ first_block = iter->second;
+ continue;
+ } else {
+ // Our basic block is supposed to be followed by "last_insn->next",
+ // but dependencies prevent this from happening. Insert a BPF_JA
+ // instruction to correct the code flow.
+ Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, last_insn->next);
+ first_block->instructions.push_back(ja);
+ last_insn->next = ja;
+ }
+ }
+ if (heads.empty()) {
+ if (unordered_blocks.size() != basic_blocks->size()) {
+ SANDBOX_DIE("Internal compiler error; cyclic graph detected");
+ }
+ return;
+ }
+ // Proceed by picking an arbitrary node from the set of basic blocks that
+ // do not have any incoming branches.
+ first_block = *heads.begin();
+ heads.erase(heads.begin());
+ }
+}
+
+void CodeGen::ComputeRelativeJumps(BasicBlocks *basic_blocks,
+ const TargetsToBlocks& targets_to_blocks) {
+ // While we previously used pointers in jt_ptr and jf_ptr to link jump
+ // instructions to their targets, we now convert these jumps to relative
+ // jumps that are suitable for loading the BPF program into the kernel.
+ int offset = 0;
+
+ // Since we just completed a toposort, all jump targets are guaranteed to
+ // go forward. This means, iterating over the basic blocks in reverse makes
+ // it trivial to compute the correct offsets.
+ BasicBlock *bb = NULL;
+ BasicBlock *last_bb = NULL;
+ for (BasicBlocks::reverse_iterator iter = basic_blocks->rbegin();
+ iter != basic_blocks->rend();
+ ++iter) {
+ last_bb = bb;
+ bb = *iter;
+ Instruction *insn = bb->instructions.back();
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ // Basic block ended in a jump instruction. We can now compute the
+ // appropriate offsets.
+ if (BPF_OP(insn->code) == BPF_JA) {
+ // "Always" jumps use the 32bit "k" field for the offset, instead
+ // of the 8bit "jt" and "jf" fields.
+ int jmp =
+ offset - targets_to_blocks.find(insn->jt_ptr)->second->offset;
+ insn->k = jmp;
+ insn->jt = insn->jf = 0;
+ } else {
+ // The offset computations for conditional jumps are just the same
+ // as for "always" jumps.
+ int jt = offset-targets_to_blocks.find(insn->jt_ptr)->second->offset;
+ int jf = offset-targets_to_blocks.find(insn->jf_ptr)->second->offset;
+
+ // There is an added complication, because conditional relative jumps
+ // can only jump at most 255 instructions forward. If we have to jump
+ // further, insert an extra "always" jump.
+ Instructions::size_type jmp = bb->instructions.size();
+ if (jt > 255 || (jt == 255 && jf > 255)) {
+ Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, insn->jt_ptr);
+ bb->instructions.push_back(ja);
+ ja->k = jt;
+ ja->jt = ja->jf = 0;
+
+ // The newly inserted "always" jump, of course, requires us to adjust
+ // the jump targets in the original conditional jump.
+ jt = 0;
+ ++jf;
+ }
+ if (jf > 255) {
+ Instruction *ja = MakeInstruction(BPF_JMP+BPF_JA, 0, insn->jf_ptr);
+ bb->instructions.insert(bb->instructions.begin() + jmp, ja);
+ ja->k = jf;
+ ja->jt = ja->jf = 0;
+
+ // Again, we have to adjust the jump targets in the original
+ // conditional jump.
+ ++jt;
+ jf = 0;
+ }
+
+ // Now we can finally set the relative jump targets in the conditional
+ // jump instruction. Afterwards, we must no longer access the jt_ptr
+ // and jf_ptr fields.
+ insn->jt = jt;
+ insn->jf = jf;
+ }
+ } else if (BPF_CLASS(insn->code) != BPF_RET &&
+ targets_to_blocks.find(insn->next)->second != last_bb) {
+ SANDBOX_DIE("Internal compiler error; invalid basic block encountered");
+ }
+
+ // Proceed to next basic block.
+ offset += bb->instructions.size();
+ bb->offset = offset;
+ }
+ return;
+}
+
+void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks,
+ Sandbox::Program *program) {
+ // Our basic blocks have been sorted and relative jump offsets have been
+ // computed. The last remaining step is for all the instructions in our
+ // basic blocks to be concatenated into a BPF program.
+ program->clear();
+ for (BasicBlocks::const_iterator bb_iter = basic_blocks.begin();
+ bb_iter != basic_blocks.end();
+ ++bb_iter) {
+ const BasicBlock& bb = **bb_iter;
+ for (Instructions::const_iterator insn_iter = bb.instructions.begin();
+ insn_iter != bb.instructions.end();
+ ++insn_iter) {
+ const Instruction& insn = **insn_iter;
+ program->push_back(
+ (struct sock_filter) { insn.code, insn.jt, insn.jf, insn.k });
+ }
+ }
+ return;
+}
+
+void CodeGen::Compile(Instruction *instructions, Sandbox::Program *program) {
+ if (compiled_) {
+ SANDBOX_DIE("Cannot call Compile() multiple times. Create a new code "
+ "generator instead");
+ }
+ compiled_ = true;
+
+ BranchTargets branch_targets;
+ FindBranchTargets(*instructions, &branch_targets);
+ TargetsToBlocks all_blocks;
+ BasicBlock *first_block =
+ CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks);
+ MergeTails(&all_blocks);
+ BasicBlocks basic_blocks;
+ TopoSortBasicBlocks(first_block, all_blocks, &basic_blocks);
+ ComputeRelativeJumps(&basic_blocks, all_blocks);
+ ConcatenateBasicBlocks(basic_blocks, program);
+ return;
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen.h b/chromium/sandbox/linux/seccomp-bpf/codegen.h
new file mode 100644
index 00000000000..88521c2b52d
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/codegen.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/basicblock.h"
+#include "sandbox/linux/seccomp-bpf/instruction.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+
+namespace playground2 {
+
+typedef std::vector<Instruction *> Instructions;
+typedef std::vector<BasicBlock *> BasicBlocks;
+typedef std::map<const Instruction *, int> BranchTargets;
+typedef std::map<const Instruction *, BasicBlock *> TargetsToBlocks;
+typedef std::map<const BasicBlock *, int> IncomingBranches;
+
+// The code generator instantiates a basic compiler that can convert a
+// graph of BPF instructions into a well-formed stream of BPF instructions.
+// Most notably, it ensures that jumps are always forward and don't exceed
+// the limit of 255 instructions imposed by the instruction set.
+//
+// Callers would typically create a new CodeGen object and then use it to
+// build a DAG of Instructions. They'll eventually call Compile() to convert
+// this DAG to a Sandbox::Program.
+//
+// Instructions can be chained at the time when they are created, or they
+// can be joined later by calling JoinInstructions().
+//
+// CodeGen gen;
+// Instruction *dag, *branch;
+// dag =
+// gen.MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+// offsetof(struct arch_seccomp_data, nr),
+// branch =
+// gen.MakeInstruction(BPF_JMP+BPF_EQ+BPF_K, __NR_getpid,
+// Trap(GetPidHandler, NULL), NULL);
+// gen.JoinInstructions(branch,
+// gen.MakeInstruction(BPF_RET+BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED)));
+//
+// // Simplified code follows; in practice, it is important to avoid calling
+// // any C++ destructors after starting the sandbox.
+// Sandbox::Program program;
+// gen.Compile(dag, program);
+// const struct sock_fprog prog = {
+// static_cast<unsigned short>(program->size()), &program[0] };
+// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+//
+class CodeGen {
+ public:
+ CodeGen();
+ ~CodeGen();
+
+ // This is a helper method that can be used for debugging purposes. It is
+ // not normally called.
+ static void PrintProgram(const Sandbox::Program& program);
+
+ // Create a new instruction. Instructions form a DAG. The instruction objects
+ // are owned by the CodeGen object. They do not need to be explicitly
+ // deleted.
+ // For details on the possible parameters refer to <linux/filter.h>
+ Instruction *MakeInstruction(uint16_t code, uint32_t k,
+ Instruction *next = NULL);
+ Instruction *MakeInstruction(uint16_t code, const ErrorCode& err);
+ Instruction *MakeInstruction(uint16_t code, uint32_t k,
+ Instruction *jt, Instruction *jf);
+
+ // Join two (sequences of) instructions. This is useful, if the "next"
+ // parameter had not originally been given in the call to MakeInstruction(),
+ // or if a (conditional) jump still has an unsatisfied target.
+ void JoinInstructions(Instruction *head, Instruction *tail);
+
+ // Traverse the graph of instructions and visit each instruction once.
+ // Traversal order is implementation-defined. It is acceptable to make
+ // changes to the graph from within the callback function. These changes
+ // do not affect traversal.
+ // The "fnc" function gets called with both the instruction and the opaque
+ // "aux" pointer.
+ void Traverse(Instruction *, void (*fnc)(Instruction *, void *aux),
+ void *aux);
+
+ // Compiles the graph of instructions into a BPF program that can be passed
+ // to the kernel. Please note that this function modifies the graph in place
+ // and must therefore only be called once per graph.
+ void Compile(Instruction *instructions, Sandbox::Program *program);
+
+ private:
+ friend class CodeGenUnittestHelper;
+
+ // Find all the instructions that are the target of BPF_JMPs.
+ void FindBranchTargets(const Instruction& instructions,
+ BranchTargets *branch_targets);
+
+ // Combine instructions between "head" and "tail" into a new basic block.
+ // Basic blocks are defined as sequences of instructions whose only branch
+ // target is the very first instruction; furthermore, any BPF_JMP or BPF_RET
+ // instruction must be at the very end of the basic block.
+ BasicBlock *MakeBasicBlock(Instruction *head, Instruction *tail);
+
+ // Creates a basic block and adds it to "basic_blocks"; sets "first_block"
+ // if it is still NULL.
+ void AddBasicBlock(Instruction *head, Instruction *tail,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks *basic_blocks, BasicBlock **first_block);
+
+ // Cuts the DAG of instructions into basic blocks.
+ BasicBlock *CutGraphIntoBasicBlocks(Instruction *instructions,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks *blocks);
+
+ // Find common tail sequences of basic blocks and coalesce them.
+ void MergeTails(TargetsToBlocks *blocks);
+
+ // For each basic block, compute the number of incoming branches.
+ void ComputeIncomingBranches(BasicBlock *block,
+ const TargetsToBlocks& targets_to_blocks,
+ IncomingBranches *incoming_branches);
+
+ // Topologically sort the basic blocks so that all jumps are forward jumps.
+ // This is a requirement for any well-formed BPF program.
+ void TopoSortBasicBlocks(BasicBlock *first_block,
+ const TargetsToBlocks& blocks,
+ BasicBlocks *basic_blocks);
+
+ // Convert jt_ptr_ and jf_ptr_ fields in BPF_JMP instructions to valid
+ // jt_ and jf_ jump offsets. This can result in BPF_JA instructions being
+ // inserted, if we need to jump over more than 256 instructions.
+ void ComputeRelativeJumps(BasicBlocks *basic_blocks,
+ const TargetsToBlocks& targets_to_blocks);
+
+ // Concatenate instructions from all basic blocks into a BPF program that
+ // can be passed to the kernel.
+ void ConcatenateBasicBlocks(const BasicBlocks&, Sandbox::Program *program);
+
+ // We stick all instructions and basic blocks into pools that get destroyed
+ // when the CodeGen object is destroyed. This way, we neither need to worry
+ // about explicitly managing ownership, nor do we need to worry about using
+ // smart pointers in the presence of circular references.
+ Instructions instructions_;
+ BasicBlocks basic_blocks_;
+
+ // Compile() must only ever be called once as it makes destructive changes
+ // to the DAG.
+ bool compiled_;
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc
new file mode 100644
index 00000000000..d24bcf280a6
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc
@@ -0,0 +1,445 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <set>
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace playground2 {
+
+class SandboxUnittestHelper : public Sandbox {
+ public:
+ typedef Sandbox::Program Program;
+};
+
+// We want to access some of the private methods in the code generator. We
+// do so by defining a "friend" that makes these methods public for us.
+class CodeGenUnittestHelper : public CodeGen {
+ public:
+ void FindBranchTargets(const Instruction& instructions,
+ BranchTargets *branch_targets) {
+ CodeGen::FindBranchTargets(instructions, branch_targets);
+ }
+
+ BasicBlock *CutGraphIntoBasicBlocks(Instruction *insns,
+ const BranchTargets& branch_targets,
+ TargetsToBlocks *blocks) {
+ return CodeGen::CutGraphIntoBasicBlocks(insns, branch_targets, blocks);
+ }
+
+ void MergeTails(TargetsToBlocks *blocks) {
+ CodeGen::MergeTails(blocks);
+ }
+};
+
+enum { NO_FLAGS = 0x0000,
+ HAS_MERGEABLE_TAILS = 0x0001,
+};
+
+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,
+ ErrorCode(ErrorCode::ERR_ALLOWED));
+}
+
+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)));
+}
+
+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
+
+ // N.B.: As the instructions in both sides of the branch are already
+ // the same object, we do not actually have any "mergeable" branches.
+ // 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 *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)
+ // 1: JUMP if eq 42 then $2 else $4 (insn4)
+ // 2: JUMP to $3 (insn1)
+ // 3: LD 42 (insn0)
+ // RET ErrorCode(42) (insn2)
+ // 4: LD 42 (insn3)
+ // RET ErrorCode(42) (insn3+)
+ *flags = HAS_MERGEABLE_TAILS;
+
+ 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->k == 42);
+ SANDBOX_ASSERT(insn0->next == NULL);
+
+ Instruction *insn1 = codegen->MakeInstruction(BPF_JMP+BPF_JA, 0, insn0);
+ SANDBOX_ASSERT(insn1);
+ SANDBOX_ASSERT(insn1->code == BPF_JMP+BPF_JA);
+ SANDBOX_ASSERT(insn1->jt_ptr == insn0);
+
+ Instruction *insn2 = codegen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(42));
+ SANDBOX_ASSERT(insn2);
+ 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 *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->k == 42);
+ SANDBOX_ASSERT(insn4->jt_ptr == insn1);
+ SANDBOX_ASSERT(insn4->jf_ptr == insn3);
+
+ codegen->JoinInstructions(insn0, insn2);
+ SANDBOX_ASSERT(insn0->next == insn2);
+
+ 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->k == 23);
+ SANDBOX_ASSERT(insn5->next == insn4);
+
+ // Force a basic block that ends in neither a jump instruction nor a return
+ // instruction. It only contains "insn5". This exercises one of the less
+ // common code paths in the topo-sort algorithm.
+ // 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);
+
+ return insn6;
+}
+
+void ForAllPrograms(void (*test)(CodeGenUnittestHelper *, Instruction *, int)){
+ Instruction *(*function_table[])(CodeGen *codegen, int *flags) = {
+ SampleProgramOneInstruction,
+ SampleProgramSimpleBranch,
+ SampleProgramAtypicalBranch,
+ SampleProgramComplex,
+ };
+
+ for (size_t i = 0; i < arraysize(function_table); ++i) {
+ CodeGenUnittestHelper codegen;
+ int flags = NO_FLAGS;
+ Instruction *prg = function_table[i](&codegen, &flags);
+ test(&codegen, prg, flags);
+ }
+}
+
+void MakeInstruction(CodeGenUnittestHelper *codegen,
+ Instruction *program, int) {
+ // Nothing to do here
+}
+
+SANDBOX_TEST(CodeGen, MakeInstruction) {
+ ForAllPrograms(MakeInstruction);
+}
+
+void FindBranchTargets(CodeGenUnittestHelper *codegen, Instruction *prg, int) {
+ BranchTargets branch_targets;
+ codegen->FindBranchTargets(*prg, &branch_targets);
+
+ // Verifying the general properties that should be true for every
+ // well-formed BPF program.
+ // Perform a depth-first traversal of the BPF program an verify that all
+ // 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;
+ BranchTargets::const_iterator end = branch_targets.end();
+ for (Instruction *insn = prg;;) {
+ all_instructions.insert(insn);
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
+ target_instructions.insert(insn->jt_ptr);
+ SANDBOX_ASSERT(insn->jt_ptr != NULL);
+ SANDBOX_ASSERT(branch_targets.find(insn->jt_ptr) != end);
+ if (BPF_OP(insn->code) != BPF_JA) {
+ target_instructions.insert(insn->jf_ptr);
+ SANDBOX_ASSERT(insn->jf_ptr != NULL);
+ SANDBOX_ASSERT(branch_targets.find(insn->jf_ptr) != end);
+ stack.push_back(insn->jf_ptr);
+ }
+ insn = insn->jt_ptr;
+ } else if (BPF_CLASS(insn->code) == BPF_RET) {
+ SANDBOX_ASSERT(insn->next == NULL);
+ if (stack.empty()) {
+ break;
+ }
+ insn = stack.back();
+ stack.pop_back();
+ } else {
+ SANDBOX_ASSERT(insn->next != NULL);
+ insn = insn->next;
+ }
+ }
+ SANDBOX_ASSERT(target_instructions.size() == branch_targets.size());
+
+ // We can now subtract the set of the branch targets from the set of all
+ // instructions. This gives us a set with the instructions that nobody
+ // ever jumps to. Verify that they are no included in the
+ // "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(),
+ non_target_instructions.begin());
+ for (Instructions::const_iterator iter = non_target_instructions.begin();
+ iter != non_target_instructions.end();
+ ++iter) {
+ SANDBOX_ASSERT(branch_targets.find(*iter) == end);
+ }
+}
+
+SANDBOX_TEST(CodeGen, FindBranchTargets) {
+ ForAllPrograms(FindBranchTargets);
+}
+
+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);
+ SANDBOX_ASSERT(first_block != NULL);
+ SANDBOX_ASSERT(first_block->instructions.size() > 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
+ // instruction forms the beginning of a new basic block. There should be
+ // no other jumps or return instructions in the middle of a basic block.
+ for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin();
+ bb_iter != all_blocks.end();
+ ++bb_iter) {
+ BasicBlock *bb = bb_iter->second;
+ SANDBOX_ASSERT(bb != NULL);
+ SANDBOX_ASSERT(bb->instructions.size() > 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();;){
+ insn = *insn_iter;
+ if (++insn_iter != bb->instructions.end()) {
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_JMP);
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) != BPF_RET);
+ } else {
+ SANDBOX_ASSERT(BPF_CLASS(insn->code) == BPF_JMP ||
+ BPF_CLASS(insn->code) == BPF_RET ||
+ branch_targets.find(insn->next) !=
+ branch_targets.end());
+ break;
+ }
+ SANDBOX_ASSERT(branch_targets.find(*insn_iter) == branch_targets.end());
+ }
+ }
+}
+
+SANDBOX_TEST(CodeGen, CutGraphIntoBasicBlocks) {
+ ForAllPrograms(CutGraphIntoBasicBlocks);
+}
+
+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);
+
+ // The shape of our graph and thus the function of our program should
+ // still be unchanged after we run MergeTails(). We verify this by
+ // serializing the graph and verifying that it is still the same.
+ // We also verify that at least some of the edges changed because of
+ // tail merging.
+ std::string graph[2];
+ std::string edges[2];
+
+ // The loop executes twice. After the first run, we call MergeTails() on
+ // our graph.
+ for (int i = 0;;) {
+ // Traverse the entire program in depth-first order.
+ 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.
+ // The stream of instructions should be unchanged after MergeTails().
+ for (Instructions::const_iterator iter = bb->instructions.begin();
+ iter != bb->instructions.end();
+ ++iter) {
+ 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),
+ 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));
+
+ // 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();
+ 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
+ // only has a single branch.
+ if (BPF_OP(insn->code) != BPF_JA) {
+ stack.push_back(all_blocks[insn->jf_ptr]);
+ }
+ bb = all_blocks[insn->jt_ptr];
+ } else if (BPF_CLASS(insn->code) == BPF_RET) {
+ // After a BPF_RET, see if we need to back track.
+ if (stack.empty()) {
+ break;
+ }
+ bb = stack.back();
+ stack.pop_back();
+ } else {
+ // For "normal" instructions, just follow to the next basic block.
+ bb = all_blocks[insn->next];
+ }
+ }
+
+ // Our loop runs exactly two times.
+ if (++i > 1) {
+ break;
+ }
+ codegen->MergeTails(&all_blocks);
+ }
+ SANDBOX_ASSERT(graph[0] == graph[1]);
+ if (flags & HAS_MERGEABLE_TAILS) {
+ SANDBOX_ASSERT(edges[0] != edges[1]);
+ } else {
+ SANDBOX_ASSERT(edges[0] == edges[1]);
+ }
+}
+
+SANDBOX_TEST(CodeGen, MergeTails) {
+ ForAllPrograms(MergeTails);
+}
+
+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.
+ // This provides a pretty good unittest.
+ // We hand-crafted the program returned by SampleProgram() to exercise
+ // several of the more interesting code-paths. See comments in
+ // SampleProgram() for details.
+ // In addition to relying on the internal consistency checks in the compiler,
+ // we also serialize the graph and the resulting BPF program and compare
+ // them. With the exception of BPF_JA instructions that might have been
+ // inserted, both instruction streams should be equivalent.
+ // As Compile() modifies the instructions, we have to serialize the graph
+ // before calling Compile().
+ std::string source;
+ Instructions source_stack;
+ 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).
+ next = insn->jt_ptr;
+ continue;
+ } else {
+ source_stack.push_back(insn->jf_ptr);
+ next = insn->jt_ptr;
+ }
+ } else if (BPF_CLASS(insn->code) == BPF_RET) {
+ if (source_stack.empty()) {
+ next = NULL;
+ } else {
+ next = source_stack.back();
+ source_stack.pop_back();
+ }
+ } else {
+ next = insn->next;
+ }
+ // 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),
+ sizeof(insn->code));
+ source.append(reinterpret_cast<const char *>(&insn->k),
+ sizeof(insn->k));
+ }
+
+ // Compile the program
+ SandboxUnittestHelper::Program bpf;
+ codegen->Compile(prg, &bpf);
+
+ // Serialize the resulting BPF instructions.
+ std::string assembly;
+ std::vector<int> assembly_stack;
+ for (int idx = 0; idx >= 0;) {
+ SANDBOX_ASSERT(idx < (int)bpf.size());
+ struct sock_filter& insn = bpf[idx];
+ if (BPF_CLASS(insn.code) == BPF_JMP) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ // Do not serialize BPF_JA instructions (see above).
+ idx += insn.k + 1;
+ continue;
+ } else {
+ assembly_stack.push_back(idx + insn.jf + 1);
+ idx += insn.jt + 1;
+ }
+ } else if (BPF_CLASS(insn.code) == BPF_RET) {
+ if (assembly_stack.empty()) {
+ idx = -1;
+ } else {
+ idx = assembly_stack.back();
+ assembly_stack.pop_back();
+ }
+ } else {
+ ++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));
+ }
+ SANDBOX_ASSERT(source == assembly);
+}
+
+SANDBOX_TEST(CodeGen, All) {
+ ForAllPrograms(CompileAndCompare);
+}
+
+} // namespace playground2
diff --git a/chromium/sandbox/linux/seccomp-bpf/demo.cc b/chromium/sandbox/linux/seccomp-bpf/demo.cc
new file mode 100644
index 00000000000..b2622ec452d
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/demo.cc
@@ -0,0 +1,524 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/unistd.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+using playground2::arch_seccomp_data;
+using playground2::ErrorCode;
+using playground2::Sandbox;
+
+#define ERR EPERM
+
+// We don't expect our sandbox to do anything useful yet. So, we will fail
+// almost immediately. For now, force the code to continue running. The
+// following line should be removed as soon as the sandbox is starting to
+// actually enforce restrictions in a meaningful way:
+#define _exit(x) do { } while (0)
+
+namespace {
+
+bool SendFds(int transport, const void *buf, size_t len, ...) {
+ int count = 0;
+ va_list ap;
+ va_start(ap, len);
+ while (va_arg(ap, int) >= 0) {
+ ++count;
+ }
+ va_end(ap);
+ if (!count) {
+ return false;
+ }
+ char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ struct iovec iov[2] = { { 0 } };
+ struct msghdr msg = { 0 };
+ int dummy = 0;
+ iov[0].iov_base = &dummy;
+ iov[0].iov_len = sizeof(dummy);
+ if (buf && len > 0) {
+ iov[1].iov_base = const_cast<void *>(buf);
+ iov[1].iov_len = len;
+ }
+ msg.msg_iov = iov;
+ msg.msg_iovlen = (buf && len > 0) ? 2 : 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_LEN(count*sizeof(int));
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(count*sizeof(int));
+ va_start(ap, len);
+ for (int i = 0, fd; (fd = va_arg(ap, int)) >= 0; ++i) {
+ (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i] = fd;
+ }
+ return sendmsg(transport, &msg, 0) ==
+ static_cast<ssize_t>(sizeof(dummy) + ((buf && len > 0) ? len : 0));
+}
+
+bool GetFds(int transport, void *buf, size_t *len, ...) {
+ int count = 0;
+ va_list ap;
+ va_start(ap, len);
+ for (int *fd; (fd = va_arg(ap, int *)) != NULL; ++count) {
+ *fd = -1;
+ }
+ va_end(ap);
+ if (!count) {
+ return false;
+ }
+ char cmsg_buf[CMSG_SPACE(count*sizeof(int))];
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ struct iovec iov[2] = { { 0 } };
+ struct msghdr msg = { 0 };
+ int err;
+ iov[0].iov_base = &err;
+ iov[0].iov_len = sizeof(int);
+ if (buf && len && *len > 0) {
+ iov[1].iov_base = buf;
+ iov[1].iov_len = *len;
+ }
+ msg.msg_iov = iov;
+ msg.msg_iovlen = (buf && len && *len > 0) ? 2 : 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = CMSG_LEN(count*sizeof(int));
+ ssize_t bytes = recvmsg(transport, &msg, 0);
+ if (len) {
+ *len = bytes > static_cast<int>(sizeof(int)) ? bytes - sizeof(int) : 0;
+ }
+ if (bytes != static_cast<ssize_t>(sizeof(int) + iov[1].iov_len)) {
+ if (bytes >= 0) {
+ errno = 0;
+ }
+ return false;
+ }
+ if (err) {
+ // "err" is the first four bytes of the payload. If these are non-zero,
+ // the sender on the other side of the socketpair sent us an errno value.
+ // We don't expect to get any file handles in this case.
+ errno = err;
+ return false;
+ }
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+ if ((msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) ||
+ !cmsg ||
+ cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_RIGHTS ||
+ cmsg->cmsg_len != CMSG_LEN(count*sizeof(int))) {
+ errno = EBADF;
+ return false;
+ }
+ va_start(ap, len);
+ for (int *fd, i = 0; (fd = va_arg(ap, int *)) != NULL; ++i) {
+ *fd = (reinterpret_cast<int *>(CMSG_DATA(cmsg)))[i];
+ }
+ va_end(ap);
+ return true;
+}
+
+
+// POSIX doesn't define any async-signal safe function for converting
+// an integer to ASCII. We'll have to define our own version.
+// itoa_r() converts a (signed) integer to ASCII. It returns "buf", if the
+// conversion was successful or NULL otherwise. It never writes more than "sz"
+// bytes. Output will be truncated as needed, and a NUL character is always
+// appended.
+char *itoa_r(int i, char *buf, size_t sz) {
+ // Make sure we can write at least one NUL byte.
+ size_t n = 1;
+ if (n > sz) {
+ return NULL;
+ }
+
+ // Handle negative numbers.
+ char *start = buf;
+ int minint = 0;
+ if (i < 0) {
+ // Make sure we can write the '-' character.
+ if (++n > sz) {
+ *start = '\000';
+ return NULL;
+ }
+ *start++ = '-';
+
+ // Turn our number positive.
+ if (i == -i) {
+ // The lowest-most negative integer needs special treatment.
+ minint = 1;
+ i = -(i + 1);
+ } else {
+ // "Normal" negative numbers are easy.
+ i = -i;
+ }
+ }
+
+ // Loop until we have converted the entire number. Output at least one
+ // character (i.e. '0').
+ char *ptr = start;
+ do {
+ // Make sure there is still enough space left in our output buffer.
+ if (++n > sz) {
+ buf = NULL;
+ goto truncate;
+ }
+
+ // Output the next digit and (if necessary) compensate for the lowest-most
+ // negative integer needing special treatment. This works because, no
+ // matter the bit width of the integer, the lowest-most integer always ends
+ // in 2, 4, 6, or 8.
+ *ptr++ = i%10 + '0' + minint;
+ minint = 0;
+ i /= 10;
+ } while (i);
+ truncate: // Terminate the output with a NUL character.
+ *ptr = '\000';
+
+ // Conversion to ASCII actually resulted in the digits being in reverse
+ // order. We can't easily generate them in forward order, as we can't tell
+ // the number of characters needed until we are done converting.
+ // So, now, we reverse the string (except for the possible "-" sign).
+ while (--ptr > start) {
+ char ch = *ptr;
+ *ptr = *start;
+ *start++ = ch;
+ }
+ return buf;
+}
+
+// This handler gets called, whenever we encounter a system call that we
+// don't recognize explicitly. For the purposes of this program, we just
+// log the system call and then deny it. More elaborate sandbox policies
+// might try to evaluate the system call in user-space, instead.
+// The only notable complication is that this function must be async-signal
+// safe. This restricts the libary functions that we can call.
+intptr_t DefaultHandler(const struct arch_seccomp_data& data, void *) {
+ static const char msg0[] = "Disallowed system call #";
+ static const char msg1[] = "\n";
+ char buf[sizeof(msg0) - 1 + 25 + sizeof(msg1)];
+
+ *buf = '\000';
+ strncat(buf, msg0, sizeof(buf));
+
+ char *ptr = strrchr(buf, '\000');
+ itoa_r(data.nr, ptr, sizeof(buf) - (ptr - buf));
+
+ ptr = strrchr(ptr, '\000');
+ strncat(ptr, msg1, sizeof(buf) - (ptr - buf));
+
+ ptr = strrchr(ptr, '\000');
+ if (HANDLE_EINTR(write(2, buf, ptr - buf))) { }
+
+ return -ERR;
+}
+
+ErrorCode Evaluator(Sandbox *sandbox, int sysno, void *) {
+ switch (sysno) {
+#if defined(__NR_accept)
+ case __NR_accept: case __NR_accept4:
+#endif
+ case __NR_alarm:
+ case __NR_brk:
+ case __NR_clock_gettime:
+ case __NR_close:
+ case __NR_dup: case __NR_dup2:
+ case __NR_epoll_create: case __NR_epoll_ctl: case __NR_epoll_wait:
+ case __NR_exit: case __NR_exit_group:
+ case __NR_fcntl:
+#if defined(__NR_fcntl64)
+ case __NR_fcntl64:
+#endif
+ case __NR_fdatasync:
+ case __NR_fstat:
+#if defined(__NR_fstat64)
+ case __NR_fstat64:
+#endif
+ case __NR_ftruncate:
+ case __NR_futex:
+ case __NR_getdents: case __NR_getdents64:
+ case __NR_getegid:
+#if defined(__NR_getegid32)
+ case __NR_getegid32:
+#endif
+ case __NR_geteuid:
+#if defined(__NR_geteuid32)
+ case __NR_geteuid32:
+#endif
+ case __NR_getgid:
+#if defined(__NR_getgid32)
+ case __NR_getgid32:
+#endif
+ case __NR_getitimer: case __NR_setitimer:
+#if defined(__NR_getpeername)
+ case __NR_getpeername:
+#endif
+ case __NR_getpid: case __NR_gettid:
+#if defined(__NR_getsockname)
+ case __NR_getsockname:
+#endif
+ case __NR_gettimeofday:
+ case __NR_getuid:
+#if defined(__NR_getuid32)
+ case __NR_getuid32:
+#endif
+#if defined(__NR__llseek)
+ case __NR__llseek:
+#endif
+ case __NR_lseek:
+ case __NR_nanosleep:
+ case __NR_pipe: case __NR_pipe2:
+ case __NR_poll:
+ case __NR_pread64: case __NR_preadv:
+ case __NR_pwrite64: case __NR_pwritev:
+ case __NR_read: case __NR_readv:
+ case __NR_restart_syscall:
+ case __NR_set_robust_list:
+ case __NR_rt_sigaction:
+#if defined(__NR_sigaction)
+ case __NR_sigaction:
+#endif
+#if defined(__NR_signal)
+ case __NR_signal:
+#endif
+ case __NR_rt_sigprocmask:
+#if defined(__NR_sigprocmask)
+ case __NR_sigprocmask:
+#endif
+#if defined(__NR_shutdown)
+ case __NR_shutdown:
+#endif
+ case __NR_rt_sigreturn:
+#if defined(__NR_sigreturn)
+ case __NR_sigreturn:
+#endif
+#if defined(__NR_socketpair)
+ case __NR_socketpair:
+#endif
+ case __NR_time:
+ case __NR_uname:
+ case __NR_write: case __NR_writev:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+
+ case __NR_prctl:
+ // Allow PR_SET_DUMPABLE and PR_GET_DUMPABLE. Do not allow anything else.
+ return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ PR_SET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ PR_GET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Trap(DefaultHandler, NULL)));
+
+ // The following system calls are temporarily permitted. This must be
+ // tightened later. But we currently don't implement enough of the sandboxing
+ // API to do so.
+ // As is, this sandbox isn't exactly safe :-/
+#if defined(__NR_sendmsg)
+ case __NR_sendmsg: case __NR_sendto:
+ case __NR_recvmsg: case __NR_recvfrom:
+ case __NR_getsockopt: case __NR_setsockopt:
+#elif defined(__NR_socketcall)
+ case __NR_socketcall:
+#endif
+#if defined(__NR_shmat)
+ case __NR_shmat: case __NR_shmctl: case __NR_shmdt: case __NR_shmget:
+#elif defined(__NR_ipc)
+ case __NR_ipc:
+#endif
+#if defined(__NR_mmap2)
+ case __NR_mmap2:
+#else
+ case __NR_mmap:
+#endif
+#if defined(__NR_ugetrlimit)
+ case __NR_ugetrlimit:
+#endif
+ case __NR_getrlimit:
+ case __NR_ioctl:
+ case __NR_clone:
+ case __NR_munmap: case __NR_mprotect: case __NR_madvise:
+ case __NR_remap_file_pages:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+
+ // Everything that isn't explicitly allowed is denied.
+ default:
+ return sandbox->Trap(DefaultHandler, NULL);
+ }
+}
+
+void *ThreadFnc(void *arg) {
+ return arg;
+}
+
+void *SendmsgStressThreadFnc(void *arg) {
+ if (arg) { }
+ static const int repetitions = 100;
+ static const int kNumFds = 3;
+ for (int rep = 0; rep < repetitions; ++rep) {
+ int fds[2 + kNumFds];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
+ perror("socketpair()");
+ _exit(1);
+ }
+ size_t len = 4;
+ char buf[4];
+ if (!SendFds(fds[0], "test", 4, fds[1], fds[1], fds[1], -1) ||
+ !GetFds(fds[1], buf, &len, fds+2, fds+3, fds+4, NULL) ||
+ len != 4 ||
+ memcmp(buf, "test", len) ||
+ write(fds[2], "demo", 4) != 4 ||
+ read(fds[0], buf, 4) != 4 ||
+ memcmp(buf, "demo", 4)) {
+ perror("sending/receiving of fds");
+ _exit(1);
+ }
+ for (int i = 0; i < 2+kNumFds; ++i) {
+ if (close(fds[i])) {
+ perror("close");
+ _exit(1);
+ }
+ }
+ }
+ return NULL;
+}
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ if (argc) { }
+ if (argv) { }
+ int proc_fd = open("/proc", O_RDONLY|O_DIRECTORY);
+ if (Sandbox::SupportsSeccompSandbox(proc_fd) !=
+ Sandbox::STATUS_AVAILABLE) {
+ perror("sandbox");
+ _exit(1);
+ }
+ Sandbox sandbox;
+ sandbox.set_proc_fd(proc_fd);
+ sandbox.SetSandboxPolicy(Evaluator, NULL);
+ sandbox.StartSandbox();
+
+ // Check that we can create threads
+ pthread_t thr;
+ if (!pthread_create(&thr, NULL, ThreadFnc,
+ reinterpret_cast<void *>(0x1234))) {
+ void *ret;
+ pthread_join(thr, &ret);
+ if (ret != reinterpret_cast<void *>(0x1234)) {
+ perror("clone() failed");
+ _exit(1);
+ }
+ } else {
+ perror("clone() failed");
+ _exit(1);
+ }
+
+ // Check that we handle restart_syscall() without dieing. This is a little
+ // tricky to trigger. And I can't think of a good way to verify whether it
+ // actually executed.
+ signal(SIGALRM, SIG_IGN);
+ const struct itimerval tv = { { 0, 0 }, { 0, 5*1000 } };
+ const struct timespec tmo = { 0, 100*1000*1000 };
+ setitimer(ITIMER_REAL, &tv, NULL);
+ nanosleep(&tmo, NULL);
+
+ // Check that we can query the size of the stack, but that all other
+ // calls to getrlimit() fail.
+ if (((errno = 0), !getrlimit(RLIMIT_STACK, NULL)) || errno != EFAULT ||
+ ((errno = 0), !getrlimit(RLIMIT_CORE, NULL)) || errno != ERR) {
+ perror("getrlimit()");
+ _exit(1);
+ }
+
+ // Check that we can query TCGETS and TIOCGWINSZ, but no other ioctls().
+ if (((errno = 0), !ioctl(2, TCGETS, NULL)) || errno != EFAULT ||
+ ((errno = 0), !ioctl(2, TIOCGWINSZ, NULL)) || errno != EFAULT ||
+ ((errno = 0), !ioctl(2, TCSETS, NULL)) || errno != ERR) {
+ perror("ioctl()");
+ _exit(1);
+ }
+
+ // Check that prctl() can manipulate the dumpable flag, but nothing else.
+ if (((errno = 0), !prctl(PR_GET_DUMPABLE)) || errno ||
+ ((errno = 0), prctl(PR_SET_DUMPABLE, 1)) || errno ||
+ ((errno = 0), !prctl(PR_SET_SECCOMP, 0)) || errno != ERR) {
+ perror("prctl()");
+ _exit(1);
+ }
+
+ // Check that we can send and receive file handles.
+ int fds[3];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) {
+ perror("socketpair()");
+ _exit(1);
+ }
+ size_t len = 4;
+ char buf[4];
+ if (!SendFds(fds[0], "test", 4, fds[1], -1) ||
+ !GetFds(fds[1], buf, &len, fds+2, NULL) ||
+ len != 4 ||
+ memcmp(buf, "test", len) ||
+ write(fds[2], "demo", 4) != 4 ||
+ read(fds[0], buf, 4) != 4 ||
+ memcmp(buf, "demo", 4) ||
+ close(fds[0]) ||
+ close(fds[1]) ||
+ close(fds[2])) {
+ perror("sending/receiving of fds");
+ _exit(1);
+ }
+
+ // Check whether SysV IPC works.
+ int shmid;
+ void *addr;
+ if ((shmid = shmget(IPC_PRIVATE, 4096, IPC_CREAT|0600)) < 0 ||
+ (addr = shmat(shmid, NULL, 0)) == reinterpret_cast<void *>(-1) ||
+ shmdt(addr) ||
+ shmctl(shmid, IPC_RMID, NULL)) {
+ perror("sysv IPC");
+ _exit(1);
+ }
+
+ // Print a message so that the user can see the sandbox is activated.
+ time_t tm = time(NULL);
+ printf("Sandbox has been started at %s", ctime(&tm));
+
+ // Stress-test the sendmsg() code
+ static const int kSendmsgStressNumThreads = 10;
+ pthread_t sendmsgStressThreads[kSendmsgStressNumThreads];
+ for (int i = 0; i < kSendmsgStressNumThreads; ++i) {
+ if (pthread_create(sendmsgStressThreads + i, NULL,
+ SendmsgStressThreadFnc, NULL)) {
+ perror("pthread_create");
+ _exit(1);
+ }
+ }
+ for (int i = 0; i < kSendmsgStressNumThreads; ++i) {
+ pthread_join(sendmsgStressThreads[i], NULL);
+ }
+
+ return 0;
+}
diff --git a/chromium/sandbox/linux/seccomp-bpf/die.cc b/chromium/sandbox/linux/seccomp-bpf/die.cc
new file mode 100644
index 00000000000..4962c4d30b9
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/die.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+
+#include <string>
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+
+namespace playground2 {
+
+void Die::ExitGroup() {
+ // exit_group() should exit our program. After all, it is defined as a
+ // function that doesn't return. But things can theoretically go wrong.
+ // 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);
+
+ // 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
+ // to a defined state; but we have not way to verify whether we actually
+ // succeeded in doing so. Nonetheless, triggering a fatal signal could help
+ // us terminate.
+ signal(SIGSEGV, SIG_DFL);
+ SandboxSyscall(__NR_prctl, PR_SET_DUMPABLE, (void *)0, (void *)0, (void *)0);
+ if (*(volatile char *)0) { }
+
+ // If there is no way for us to ask for the program to exit, the next
+ // best thing we can do is to loop indefinitely. Maybe, somebody will notice
+ // and file a bug...
+ // 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);
+ }
+}
+
+void Die::SandboxDie(const char *msg, const char *file, int line) {
+ if (simple_exit_) {
+ LogToStderr(msg, file, line);
+ } else {
+ #if defined(SECCOMP_BPF_STANDALONE)
+ Die::LogToStderr(msg, file, line);
+ #else
+ logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
+ #endif
+ }
+ ExitGroup();
+}
+
+void Die::SandboxInfo(const char *msg, const char *file, int line) {
+ if (!suppress_info_) {
+ #if defined(SECCOMP_BPF_STANDALONE)
+ Die::LogToStderr(msg, file, line);
+ #else
+ logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
+ #endif
+ }
+}
+
+void Die::LogToStderr(const char *msg, const char *file, int line) {
+ if (msg) {
+ char buf[40];
+ snprintf(buf, sizeof(buf), "%d", line);
+ std::string s = std::string(file) + ":" + buf + ":" + msg + "\n";
+
+ // No need to loop. Short write()s are unlikely and if they happen we
+ // probably prefer them over a loop that blocks.
+ if (HANDLE_EINTR(SandboxSyscall(__NR_write, 2, s.c_str(), s.length()))) { }
+ }
+}
+
+bool Die::simple_exit_ = false;
+bool Die::suppress_info_ = false;
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/die.h b/chromium/sandbox/linux/seccomp-bpf/die.h
new file mode 100644
index 00000000000..f15f10877e9
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/die.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+
+#include "sandbox/linux/seccomp-bpf/port.h"
+
+
+namespace playground2 {
+
+class Die {
+ public:
+ // This is the main API for using this file. Prints a error message and
+ // exits with a fatal error.
+ #define SANDBOX_DIE(m) playground2::Die::SandboxDie(m, __FILE__, __LINE__)
+
+ // Adds an informational message to the log file or stderr as appropriate.
+ #define SANDBOX_INFO(m) playground2::Die::SandboxInfo(m, __FILE__, __LINE__)
+
+ // Terminate the program, even if the current sandbox policy prevents some
+ // of the more commonly used functions used for exiting.
+ // Most users would want to call SANDBOX_DIE() instead, as it logs extra
+ // information. But calling ExitGroup() is correct and in some rare cases
+ // preferable. So, we make it part of the public API.
+ static void ExitGroup() __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_DIE(). There is normally no reason
+ // to call it directly unless you are defining your own exiting macro.
+ static void SandboxDie(const char *msg, const char *file, int line)
+ __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_INFO(). There is normally no reason
+ // to call it directly unless you are defining your own logging macro.
+ static void SandboxInfo(const char *msg, const char *file, int line);
+
+ // Writes a message to stderr. Used as a fall-back choice, if we don't have
+ // any other way to report an error.
+ static void LogToStderr(const char *msg, const char *file, int line);
+
+ // We generally want to run all exit handlers. This means, on SANDBOX_DIE()
+ // we should be calling LOG(FATAL). But there are some situations where
+ // we just need to print a message and then terminate. This would typically
+ // happen in cases where we consume the error message internally (e.g. in
+ // unit tests or in the supportsSeccompSandbox() method).
+ static void EnableSimpleExit() { simple_exit_ = true; }
+
+ // Sometimes we need to disable all informational messages (e.g. from within
+ // unittests).
+ static void SuppressInfoMessages(bool flag) { suppress_info_ = flag; }
+
+ private:
+ static bool simple_exit_;
+ static bool suppress_info_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Die);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode.cc b/chromium/sandbox/linux/seccomp-bpf/errorcode.cc
new file mode 100644
index 00000000000..ab89d73c3fa
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/errorcode.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+
+namespace playground2 {
+
+ErrorCode::ErrorCode(int err) {
+ switch (err) {
+ case ERR_ALLOWED:
+ err_ = SECCOMP_RET_ALLOW;
+ error_type_ = ET_SIMPLE;
+ break;
+ case ERR_MIN_ERRNO ... ERR_MAX_ERRNO:
+ err_ = SECCOMP_RET_ERRNO + err;
+ error_type_ = ET_SIMPLE;
+ break;
+ default:
+ SANDBOX_DIE("Invalid use of ErrorCode object");
+ }
+}
+
+ErrorCode::ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe,
+ uint16_t id)
+ : error_type_(ET_TRAP),
+ fnc_(fnc),
+ aux_(const_cast<void *>(aux)),
+ safe_(safe),
+ err_(SECCOMP_RET_TRAP + id) {
+}
+
+ErrorCode::ErrorCode(int argno, ArgType width, Operation op, uint64_t value,
+ const ErrorCode *passed, const ErrorCode *failed)
+ : error_type_(ET_COND),
+ value_(value),
+ argno_(argno),
+ width_(width),
+ op_(op),
+ passed_(passed),
+ failed_(failed),
+ err_(SECCOMP_RET_INVALID) {
+ if (op < 0 || op >= OP_NUM_OPS) {
+ SANDBOX_DIE("Invalid opcode in BPF sandbox rules");
+ }
+}
+
+bool ErrorCode::Equals(const ErrorCode& err) const {
+ if (error_type_ == ET_INVALID || err.error_type_ == ET_INVALID) {
+ SANDBOX_DIE("Dereferencing invalid ErrorCode");
+ }
+ if (error_type_ != err.error_type_) {
+ return false;
+ }
+ if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
+ return err_ == err.err_;
+ } else if (error_type_ == ET_COND) {
+ return value_ == err.value_ &&
+ argno_ == err.argno_ &&
+ width_ == err.width_ &&
+ op_ == err.op_ &&
+ passed_->Equals(*err.passed_) &&
+ failed_->Equals(*err.failed_);
+ } else {
+ SANDBOX_DIE("Corrupted ErrorCode");
+ }
+}
+
+bool ErrorCode::LessThan(const ErrorCode& err) const {
+ // Implementing a "LessThan()" operator allows us to use ErrorCode objects
+ // as keys in STL containers; most notably, it also allows us to put them
+ // into std::set<>. Actual ordering is not important as long as it is
+ // deterministic.
+ if (error_type_ == ET_INVALID || err.error_type_ == ET_INVALID) {
+ SANDBOX_DIE("Dereferencing invalid ErrorCode");
+ }
+ if (error_type_ != err.error_type_) {
+ return error_type_ < err.error_type_;
+ } else {
+ if (error_type_ == ET_SIMPLE || error_type_ == ET_TRAP) {
+ return err_ < err.err_;
+ } else if (error_type_ == ET_COND) {
+ if (value_ != err.value_) {
+ return value_ < err.value_;
+ } else if (argno_ != err.argno_) {
+ return argno_ < err.argno_;
+ } else if (width_ != err.width_) {
+ return width_ < err.width_;
+ } else if (op_ != err.op_) {
+ return op_ < err.op_;
+ } else if (!passed_->Equals(*err.passed_)) {
+ return passed_->LessThan(*err.passed_);
+ } else if (!failed_->Equals(*err.failed_)) {
+ return failed_->LessThan(*err.failed_);
+ } else {
+ return false;
+ }
+ } else {
+ SANDBOX_DIE("Corrupted ErrorCode");
+ }
+ }
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode.h b/chromium/sandbox/linux/seccomp-bpf/errorcode.h
new file mode 100644
index 00000000000..61ec11013da
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/errorcode.h
@@ -0,0 +1,192 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
+
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+namespace playground2 {
+
+struct arch_seccomp_data;
+
+// This class holds all the possible values that can be returned by a sandbox
+// policy.
+// We can either wrap a symbolic ErrorCode (i.e. ERR_XXX enum values), an
+// errno value (in the range 0..4095), a pointer to a TrapFnc callback
+// handling a SECCOMP_RET_TRAP trap, or a complex constraint.
+// 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 {
+ public:
+ enum {
+ // Allow this system call. The value of ERR_ALLOWED is pretty much
+ // completely arbitrary. But we want to pick it so that is is unlikely
+ // to be passed in accidentally, when the user intended to return an
+ // "errno" (see below) value instead.
+ ERR_ALLOWED = 0x04000000,
+
+ // 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.
+ // This is very different from return ERR_ALLOWED.
+ ERR_MIN_ERRNO = 0,
+ // TODO(markus): Android only supports errno up to 255
+ // (crbug.com/181647).
+ ERR_MAX_ERRNO = 4095,
+ };
+
+ // While BPF filter programs always operate on 32bit quantities, the kernel
+ // always sees system call arguments as 64bit values. This statement is true
+ // no matter whether the host system is natively operating in 32bit or 64bit.
+ // The BPF compiler hides the fact that BPF instructions cannot directly
+ // access 64bit quantities. But policies are still advised to specify whether
+ // a system call expects a 32bit or a 64bit quantity.
+ enum ArgType {
+ // When passed as an argument to Sandbox::Cond(), TP_32BIT requests that
+ // the conditional test should operate on the 32bit part of the system call
+ // argument.
+ // On 64bit architectures, this verifies that user space did not pass
+ // a 64bit value as an argument to the system call. If it did, that will be
+ // interpreted as an attempt at breaking the sandbox and results in the
+ // program getting terminated.
+ // In other words, only perform a 32bit test, if you are sure this
+ // particular system call would never legitimately take a 64bit
+ // argument.
+ // Implementation detail: TP_32BIT does two things. 1) it restricts the
+ // conditional test to operating on the LSB only, and 2) it adds code to
+ // the BPF filter program verifying that the MSB the kernel received from
+ // user space is either 0, or 0xFFFFFFFF; the latter is acceptable, iff bit
+ // 31 was set in the system call argument. It deals with 32bit arguments
+ // having been sign extended.
+ TP_32BIT,
+
+ // When passed as an argument to Sandbox::Cond(), TP_64BIT requests that
+ // the conditional test should operate on the full 64bit argument. It is
+ // generally harmless to perform a 64bit test on 32bit systems, as the
+ // kernel will always see the top 32 bits of all arguments as zero'd out.
+ // This approach has the desirable property that for tests of pointer
+ // values, we can always use TP_64BIT no matter the host architecture.
+ // But of course, that also means, it is possible to write conditional
+ // policies that turn into no-ops on 32bit systems; this is by design.
+ TP_64BIT,
+ };
+
+ enum Operation {
+ // Test whether the system call argument is equal to the operand.
+ OP_EQUAL,
+
+ // Test whether the system call argument is greater (or equal) to the
+ // operand. Please note that all tests always operate on unsigned
+ // values. You can generally emulate signed tests, if that's what you
+ // need.
+ // TODO(markus): Check whether we should automatically emulate signed
+ // operations.
+ OP_GREATER_UNSIGNED, OP_GREATER_EQUAL_UNSIGNED,
+
+ // Tests a system call argument against a bit mask.
+ // The "ALL_BITS" variant performs this test: "arg & mask == mask"
+ // This implies that a mask of zero always results in a passing test.
+ // The "ANY_BITS" variant performs this test: "arg & mask != 0"
+ // This implies that a mask of zero always results in a failing test.
+ OP_HAS_ALL_BITS, OP_HAS_ANY_BITS,
+
+ // Total number of operations.
+ OP_NUM_OPS,
+ };
+
+ enum ErrorType {
+ ET_INVALID, ET_SIMPLE, ET_TRAP, ET_COND,
+ };
+
+ // We allow the default constructor, as it makes the ErrorCode class
+ // much easier to use. But if we ever encounter an invalid ErrorCode
+ // when compiling a BPF filter, we deliberately generate an invalid
+ // program that will get flagged both by our Verifier class and by
+ // the Linux kernel.
+ ErrorCode() :
+ error_type_(ET_INVALID),
+ err_(SECCOMP_RET_INVALID) {
+ }
+ explicit ErrorCode(int err);
+
+ // For all practical purposes, ErrorCodes are treated as if they were
+ // structs. The copy constructor and assignment operator are trivial and
+ // we do not need to explicitly specify them.
+ // Most notably, it is in fact perfectly OK to directly copy the passed_ and
+ // failed_ field. They only ever get set by our private constructor, and the
+ // callers handle life-cycle management for these objects.
+
+ // Destructor
+ ~ErrorCode() { }
+
+ bool Equals(const ErrorCode& err) const;
+ bool LessThan(const ErrorCode& err) const;
+
+ uint32_t err() const { return err_; }
+ ErrorType error_type() const { return error_type_; }
+
+ bool safe() const { return safe_; }
+
+ uint64_t value() const { return value_; }
+ int argno() const { return argno_; }
+ ArgType width() const { return width_; }
+ Operation op() const { return op_; }
+ const ErrorCode *passed() const { return passed_; }
+ const ErrorCode *failed() const { return failed_; }
+
+ struct LessThan {
+ bool operator()(const ErrorCode& a, const ErrorCode& b) const {
+ return a.LessThan(b);
+ }
+ };
+
+ private:
+ friend class CodeGen;
+ friend class Sandbox;
+ friend class Trap;
+
+ // If we are wrapping a callback, we must assign a unique id. This id is
+ // how the kernel tells us which one of our different SECCOMP_RET_TRAP
+ // cases has been triggered.
+ ErrorCode(Trap::TrapFnc fnc, const void *aux, bool safe, uint16_t id);
+
+ // Some system calls require inspection of arguments. This constructor
+ // allows us to specify additional constraints.
+ ErrorCode(int argno, ArgType width, Operation op, uint64_t value,
+ const ErrorCode *passed, const ErrorCode *failed);
+
+ ErrorType error_type_;
+
+ union {
+ // Fields needed for SECCOMP_RET_TRAP callbacks
+ struct {
+ Trap::TrapFnc fnc_; // Callback function and arg, if trap was
+ void *aux_; // triggered by the kernel's BPF filter.
+ bool safe_; // Keep sandbox active while calling fnc_()
+ };
+
+ // Fields needed when inspecting additional arguments.
+ struct {
+ uint64_t value_; // Value that we are comparing with.
+ int argno_; // Syscall arg number that we are inspecting.
+ ArgType width_; // Whether we are looking at a 32/64bit value.
+ Operation op_; // Comparison operation.
+ const ErrorCode *passed_; // Value to be returned if comparison passed,
+ const ErrorCode *failed_; // or if it failed.
+ };
+ };
+
+ // 32bit field used for all possible types of ErrorCode values. This is
+ // the value that uniquely identifies any ErrorCode and it (typically) can
+ // be emitted directly into a BPF filter program.
+ uint32_t err_;
+
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
new file mode 100644
index 00000000000..10f21324593
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+using namespace playground2;
+
+namespace {
+
+SANDBOX_TEST(ErrorCode, ErrnoConstructor) {
+ ErrorCode e0;
+ SANDBOX_ASSERT(e0.err() == SECCOMP_RET_INVALID);
+
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(e1.err() == SECCOMP_RET_ALLOW);
+
+ ErrorCode e2(EPERM);
+ SANDBOX_ASSERT(e2.err() == SECCOMP_RET_ERRNO + EPERM);
+
+ Sandbox sandbox;
+ ErrorCode e3 = sandbox.Trap(NULL, NULL);
+ SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP);
+}
+
+SANDBOX_TEST(ErrorCode, Trap) {
+ Sandbox sandbox;
+ ErrorCode e0 = sandbox.Trap(NULL, "a");
+ ErrorCode e1 = sandbox.Trap(NULL, "b");
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 ==
+ (e1.err() & SECCOMP_RET_DATA));
+
+ ErrorCode e2 = sandbox.Trap(NULL, "a");
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) ==
+ (e2.err() & SECCOMP_RET_DATA));
+}
+
+SANDBOX_TEST(ErrorCode, Equals) {
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ ErrorCode e2(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(e1.Equals(e1));
+ SANDBOX_ASSERT(e1.Equals(e2));
+ SANDBOX_ASSERT(e2.Equals(e1));
+
+ ErrorCode e3(EPERM);
+ SANDBOX_ASSERT(!e1.Equals(e3));
+
+ Sandbox sandbox;
+ ErrorCode e4 = sandbox.Trap(NULL, "a");
+ ErrorCode e5 = sandbox.Trap(NULL, "b");
+ ErrorCode e6 = sandbox.Trap(NULL, "a");
+ SANDBOX_ASSERT(!e1.Equals(e4));
+ SANDBOX_ASSERT(!e3.Equals(e4));
+ SANDBOX_ASSERT(!e5.Equals(e4));
+ SANDBOX_ASSERT( e6.Equals(e4));
+}
+
+SANDBOX_TEST(ErrorCode, LessThan) {
+ ErrorCode e1(ErrorCode::ERR_ALLOWED);
+ ErrorCode e2(ErrorCode::ERR_ALLOWED);
+ SANDBOX_ASSERT(!e1.LessThan(e1));
+ SANDBOX_ASSERT(!e1.LessThan(e2));
+ SANDBOX_ASSERT(!e2.LessThan(e1));
+
+ ErrorCode e3(EPERM);
+ SANDBOX_ASSERT(!e1.LessThan(e3));
+ SANDBOX_ASSERT( e3.LessThan(e1));
+
+ Sandbox sandbox;
+ ErrorCode e4 = sandbox.Trap(NULL, "a");
+ ErrorCode e5 = sandbox.Trap(NULL, "b");
+ ErrorCode e6 = sandbox.Trap(NULL, "a");
+ SANDBOX_ASSERT(e1.LessThan(e4));
+ SANDBOX_ASSERT(e3.LessThan(e4));
+ SANDBOX_ASSERT(e4.LessThan(e5));
+ SANDBOX_ASSERT(!e4.LessThan(e6));
+ SANDBOX_ASSERT(!e6.LessThan(e4));
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/instruction.h b/chromium/sandbox/linux/seccomp-bpf/instruction.h
new file mode 100644
index 00000000000..0fc8123280e
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/instruction.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__
+
+#include <stdint.h>
+
+
+namespace playground2 {
+
+// The fields in this structure have the same meaning as the corresponding
+// fields in "struct sock_filter". See <linux/filter.h> for a lot more
+// detail.
+// code -- Opcode of the instruction. This is typically a bitwise
+// combination BPF_XXX values.
+// k -- Operand; BPF instructions take zero or one operands. Operands
+// are 32bit-wide constants, if present. They can be immediate
+// values (if BPF_K is present in "code_"), addresses (if BPF_ABS
+// is present in "code_"), or relative jump offsets (if BPF_JMP
+// and BPF_JA are present in "code_").
+// jt, jf -- all conditional jumps have a 8bit-wide jump offset that allows
+// jumps of up to 256 instructions forward. Conditional jumps are
+// identified by BPF_JMP in "code_", but the lack of BPF_JA.
+// Conditional jumps have a "t"rue and "f"alse branch.
+struct Instruction {
+ // Constructor for an non-jumping instruction or for an unconditional
+ // "always" jump.
+ Instruction(uint16_t c, uint32_t parm, Instruction *n) :
+ code(c), next(n), k(parm) { }
+
+ // Constructor for a conditional jump instruction.
+ Instruction(uint16_t c, uint32_t parm, Instruction *jt, Instruction *jf) :
+ code(c), jt_ptr(jt), jf_ptr(jf), k(parm) { }
+
+ uint16_t code;
+ union {
+ // When code generation is complete, we will have computed relative
+ // branch targets that are in the range 0..255.
+ struct {
+ uint8_t jt, jf;
+ };
+
+ // While assembling the BPF program, we use pointers for branch targets.
+ // Once we have computed basic blocks, these pointers will be entered as
+ // keys in a TargetsToBlocks map and should no longer be dereferenced
+ // directly.
+ struct {
+ Instruction *jt_ptr, *jf_ptr;
+ };
+
+ // While assembling the BPF program, non-jumping instructions are linked
+ // by the "next_" pointer. This field is no longer needed when we have
+ // computed basic blocks.
+ Instruction *next;
+ };
+ uint32_t k;
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h b/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h
new file mode 100644
index 00000000000..0de0259da39
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
+
+// The Seccomp2 kernel ABI is not part of older versions of glibc.
+// As we can't break compilation with these versions of the library,
+// we explicitly define all missing symbols.
+// If we ever decide that we can now rely on system headers, the following
+// include files should be enabled:
+// #include <linux/audit.h>
+// #include <linux/seccomp.h>
+
+#include <asm/unistd.h>
+#include <linux/filter.h>
+
+// For audit.h
+#ifndef EM_ARM
+#define EM_ARM 40
+#endif
+#ifndef EM_386
+#define EM_386 3
+#endif
+#ifndef EM_X86_64
+#define EM_X86_64 62
+#endif
+
+#ifndef __AUDIT_ARCH_64BIT
+#define __AUDIT_ARCH_64BIT 0x80000000
+#endif
+#ifndef __AUDIT_ARCH_LE
+#define __AUDIT_ARCH_LE 0x40000000
+#endif
+#ifndef AUDIT_ARCH_ARM
+#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_I386
+#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_X86_64
+#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#endif
+
+// For prctl.h
+#ifndef PR_SET_SECCOMP
+#define PR_SET_SECCOMP 22
+#define PR_GET_SECCOMP 21
+#endif
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+#ifndef IPC_64
+#define IPC_64 0x0100
+#endif
+
+#ifndef BPF_MOD
+#define BPF_MOD 0x90
+#endif
+#ifndef BPF_XOR
+#define BPF_XOR 0xA0
+#endif
+
+// In order to build will older tool chains, we currently have to avoid
+// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
+// our own definitions of the seccomp kernel ABI.
+#ifndef SECCOMP_MODE_FILTER
+#define SECCOMP_MODE_DISABLED 0
+#define SECCOMP_MODE_STRICT 1
+#define SECCOMP_MODE_FILTER 2 // User user-supplied filter
+#endif
+
+#ifndef SECCOMP_RET_KILL
+// Return values supported for BPF filter programs. Please note that the
+// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
+// ever be used internally, and would result in the kernel killing our process.
+#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
+#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
+#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
+#define SECCOMP_RET_DATA 0x0000ffffU // sections
+#else
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#endif
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif
+
+// Impose some reasonable maximum BPF program size. Realistically, the
+// kernel probably has much lower limits. But by limiting to less than
+// 30 bits, we can ease requirements on some of our data types.
+#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
+
+#if defined(__i386__)
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_I386
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#elif defined(__x86_64__)
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+#define SECCOMP_ARCH AUDIT_ARCH_X86_64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#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,
+// at |__ARM_NR_BASE+0x00fff0|.
+// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
+#define MIN_SYSCALL ((unsigned int)__NR_SYSCALL_BASE)
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u)
+#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE)
+#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
+#define MIN_GHOST_SYSCALL ((unsigned int)__ARM_NR_BASE + 0xfff0u)
+#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u)
+
+#define SECCOMP_ARCH AUDIT_ARCH_ARM
+
+// ARM sigcontext_t is different from i386/x86_64.
+// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
+// ARM EABI syscall convention.
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#else
+#error Unsupported target platform
+
+#endif
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/port.h b/chromium/sandbox/linux/seccomp-bpf/port.h
new file mode 100644
index 00000000000..f10b1481de5
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/port.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Commonly used macro definitions to make the code build in different
+// target environments (e.g. as part of Chrome vs. stand-alone)
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
+
+#if !defined(SECCOMP_BPF_STANDALONE)
+ #include "base/basictypes.h"
+ #include "base/logging.h"
+ #include "base/posix/eintr_wrapper.h"
+#else
+ #define arraysize(x) (sizeof(x)/sizeof(*(x)))
+
+ #define HANDLE_EINTR TEMP_FAILURE_RETRY
+
+ #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+ #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+ template <bool>
+ struct CompileAssert {
+ };
+
+ #define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+#endif
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_PORT_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
new file mode 100644
index 00000000000..1dc5eae0428
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -0,0 +1,985 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Some headers on Android are missing cdefs: crbug.com/172337.
+// (We can't use OS_ANDROID here since build_config.h is not included).
+#if defined(ANDROID)
+#include <sys/cdefs.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef SECCOMP_BPF_STANDALONE
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+#include "sandbox/linux/seccomp-bpf/verifier.h"
+
+namespace {
+
+using playground2::ErrorCode;
+using playground2::Instruction;
+using playground2::Sandbox;
+using playground2::Trap;
+using playground2::arch_seccomp_data;
+
+const int kExpectedExitCode = 100;
+
+template<class T> int popcount(T x);
+template<> int popcount<unsigned int>(unsigned int x) {
+ return __builtin_popcount(x);
+}
+template<> int popcount<unsigned long>(unsigned long x) {
+ return __builtin_popcountl(x);
+}
+template<> int popcount<unsigned long long>(unsigned long long x) {
+ return __builtin_popcountll(x);
+}
+
+void WriteFailedStderrSetupMessage(int out_fd) {
+ const char* error_string = strerror(errno);
+ static const char msg[] = "You have reproduced a puzzling issue.\n"
+ "Please, report to crbug.com/152530!\n"
+ "Failed to set up stderr: ";
+ if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string &&
+ HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 &&
+ HANDLE_EINTR(write(out_fd, "\n", 1))) {
+ }
+}
+
+// We define a really simple sandbox policy. It is just good enough for us
+// to tell that the sandbox has actually been activated.
+ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) __attribute__((const));
+ErrorCode ProbeEvaluator(Sandbox *, int sysnum, void *) {
+ 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);
+ }
+}
+
+void ProbeProcess(void) {
+ if (syscall(__NR_getpid) < 0 && errno == EPERM) {
+ syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
+ }
+}
+
+ErrorCode AllowAllEvaluator(Sandbox *, int sysnum, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysnum)) {
+ return ErrorCode(ENOSYS);
+ }
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+}
+
+void TryVsyscallProcess(void) {
+ time_t current_time;
+ // time() is implemented as a vsyscall. With an older glibc, with
+ // vsyscall=emulate and some versions of the seccomp BPF patch
+ // we may get SIGKILL-ed. Detect this!
+ if (time(&current_time) != static_cast<time_t>(-1)) {
+ syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode));
+ }
+}
+
+bool IsSingleThreaded(int proc_fd) {
+ if (proc_fd < 0) {
+ // Cannot determine whether program is single-threaded. Hope for
+ // the best...
+ return true;
+ }
+
+ struct stat sb;
+ int task = -1;
+ if ((task = openat(proc_fd, "self/task", O_RDONLY|O_DIRECTORY)) < 0 ||
+ fstat(task, &sb) != 0 ||
+ sb.st_nlink != 3 ||
+ HANDLE_EINTR(close(task))) {
+ if (task >= 0) {
+ if (HANDLE_EINTR(close(task))) { }
+ }
+ return false;
+ }
+ return true;
+}
+
+bool IsDenied(const ErrorCode& code) {
+ return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP ||
+ (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) &&
+ code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO));
+}
+
+// Function that can be passed as a callback function to CodeGen::Traverse().
+// Checks whether the "insn" returns an UnsafeTrap() ErrorCode. If so, it
+// sets the "bool" variable pointed to by "aux".
+void CheckForUnsafeErrorCodes(Instruction *insn, void *aux) {
+ bool *is_unsafe = static_cast<bool *>(aux);
+ if (!*is_unsafe) {
+ if (BPF_CLASS(insn->code) == BPF_RET &&
+ insn->k > SECCOMP_RET_TRAP &&
+ insn->k - SECCOMP_RET_TRAP <= SECCOMP_RET_DATA) {
+ const ErrorCode& err =
+ Trap::ErrorCodeFromTrapId(insn->k & SECCOMP_RET_DATA);
+ if (err.error_type() != ErrorCode::ET_INVALID && !err.safe()) {
+ *is_unsafe = true;
+ }
+ }
+ }
+}
+
+// A Trap() handler that returns an "errno" value. The value is encoded
+// in the "aux" parameter.
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void *aux) {
+ // TrapFnc functions report error by following the native kernel convention
+ // of returning an exit code in the range of -1..-4096. They do not try to
+ // set errno themselves. The glibc wrapper that triggered the SIGSYS will
+ // ultimately do so for us.
+ int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
+ return -err;
+}
+
+// Function that can be passed as a callback function to CodeGen::Traverse().
+// Checks whether the "insn" returns an errno value from a BPF filter. If so,
+// it rewrites the instruction to instead call a Trap() handler that does
+// the same thing. "aux" is ignored.
+void RedirectToUserspace(Instruction *insn, void *aux) {
+ // When inside an UnsafeTrap() callback, we want to allow all system calls.
+ // This means, we must conditionally disable the sandbox -- and that's not
+ // something that kernel-side BPF filters can do, as they cannot inspect
+ // any state other than the syscall arguments.
+ // But if we redirect all error handlers to user-space, then we can easily
+ // make this decision.
+ // The performance penalty for this extra round-trip to user-space is not
+ // actually that bad, as we only ever pay it for denied system calls; and a
+ // typical program has very few of these.
+ Sandbox *sandbox = static_cast<Sandbox *>(aux);
+ if (BPF_CLASS(insn->code) == BPF_RET &&
+ (insn->k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ insn->k = sandbox->Trap(ReturnErrno,
+ reinterpret_cast<void *>(insn->k & SECCOMP_RET_DATA)).err();
+ }
+}
+
+// Stackable wrapper around an Evaluators handler. Changes ErrorCodes
+// returned by a system call evaluator to match the changes made by
+// RedirectToUserspace(). "aux" should be pointer to wrapped system call
+// evaluator.
+ErrorCode RedirectToUserspaceEvalWrapper(Sandbox *sandbox, int sysnum,
+ void *aux) {
+ // We need to replicate the behavior of RedirectToUserspace(), so that our
+ // Verifier can still work correctly.
+ Sandbox::Evaluators *evaluators =
+ reinterpret_cast<Sandbox::Evaluators *>(aux);
+ const std::pair<Sandbox::EvaluateSyscall, void *>& evaluator =
+ *evaluators->begin();
+
+ ErrorCode err = evaluator.first(sandbox, sysnum, evaluator.second);
+ if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) {
+ return sandbox->Trap(ReturnErrno,
+ reinterpret_cast<void *>(err.err() & SECCOMP_RET_DATA));
+ }
+ return err;
+}
+
+intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) {
+ SANDBOX_DIE(static_cast<char *>(aux));
+}
+
+} // namespace
+
+// The kernel gives us a sandbox, we turn it into a playground :-)
+// This is version 2 of the playground; version 1 was built on top of
+// pre-BPF seccomp mode.
+namespace playground2 {
+
+Sandbox::Sandbox()
+ : quiet_(false),
+ proc_fd_(-1),
+ evaluators_(new Evaluators),
+ conds_(new Conds) {
+}
+
+Sandbox::~Sandbox() {
+ // It is generally unsafe to call any memory allocator operations or to even
+ // call arbitrary destructors after having installed a new policy. We just
+ // have no way to tell whether this policy would allow the system calls that
+ // the constructors can trigger.
+ // So, we normally destroy all of our complex state prior to starting the
+ // sandbox. But this won't happen, if the Sandbox object was created and
+ // never actually used to set up a sandbox. So, just in case, we are
+ // destroying any remaining state.
+ // The "if ()" statements are technically superfluous. But let's be explicit
+ // that we really don't want to run any code, when we already destroyed
+ // objects before setting up the sandbox.
+ if (evaluators_) {
+ delete evaluators_;
+ }
+ if (conds_) {
+ delete conds_;
+ }
+}
+
+bool Sandbox::IsValidSyscallNumber(int sysnum) {
+ return SyscallIterator::IsValid(sysnum);
+}
+
+
+bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(),
+ Sandbox::EvaluateSyscall syscall_evaluator,
+ void *aux) {
+ // Block all signals before forking a child process. This prevents an
+ // attacker from manipulating our test by sending us an unexpected signal.
+ sigset_t old_mask, new_mask;
+ if (sigfillset(&new_mask) ||
+ sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) {
+ SANDBOX_DIE("sigprocmask() failed");
+ }
+ int fds[2];
+ if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) {
+ SANDBOX_DIE("pipe() failed");
+ }
+
+ if (fds[0] <= 2 || fds[1] <= 2) {
+ SANDBOX_DIE("Process started without standard file descriptors");
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ // Die if we cannot fork(). We would probably fail a little later
+ // anyway, as the machine is likely very close to running out of
+ // memory.
+ // But what we don't want to do is return "false", as a crafty
+ // attacker might cause fork() to fail at will and could trick us
+ // into running without a sandbox.
+ sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails
+ SANDBOX_DIE("fork() failed unexpectedly");
+ }
+
+ // In the child process
+ if (!pid) {
+ // Test a very simple sandbox policy to verify that we can
+ // successfully turn on sandboxing.
+ Die::EnableSimpleExit();
+
+ errno = 0;
+ if (HANDLE_EINTR(close(fds[0]))) {
+ // This call to close() has been failing in strange ways. See
+ // crbug.com/152530. So we only fail in debug mode now.
+#if !defined(NDEBUG)
+ WriteFailedStderrSetupMessage(fds[1]);
+ SANDBOX_DIE(NULL);
+#endif
+ }
+ if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) {
+ // Stderr could very well be a file descriptor to .xsession-errors, or
+ // another file, which could be backed by a file system that could cause
+ // dup2 to fail while trying to close stderr. It's important that we do
+ // not fail on trying to close stderr.
+ // If dup2 fails here, we will continue normally, this means that our
+ // parent won't cause a fatal failure if something writes to stderr in
+ // this child.
+#if !defined(NDEBUG)
+ // In DEBUG builds, we still want to get a report.
+ WriteFailedStderrSetupMessage(fds[1]);
+ SANDBOX_DIE(NULL);
+#endif
+ }
+ if (HANDLE_EINTR(close(fds[1]))) {
+ // This call to close() has been failing in strange ways. See
+ // crbug.com/152530. So we only fail in debug mode now.
+#if !defined(NDEBUG)
+ WriteFailedStderrSetupMessage(fds[1]);
+ SANDBOX_DIE(NULL);
+#endif
+ }
+
+ SetSandboxPolicy(syscall_evaluator, aux);
+ StartSandbox();
+
+ // Run our code in the sandbox.
+ code_in_sandbox();
+
+ // code_in_sandbox() is not supposed to return here.
+ SANDBOX_DIE(NULL);
+ }
+
+ // In the parent process.
+ if (HANDLE_EINTR(close(fds[1]))) {
+ SANDBOX_DIE("close() failed");
+ }
+ if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) {
+ SANDBOX_DIE("sigprocmask() failed");
+ }
+ int status;
+ if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) {
+ SANDBOX_DIE("waitpid() failed unexpectedly");
+ }
+ bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode;
+
+ // If we fail to support sandboxing, there might be an additional
+ // error message. If so, this was an entirely unexpected and fatal
+ // failure. We should report the failure and somebody must fix
+ // things. This is probably a security-critical bug in the sandboxing
+ // code.
+ if (!rc) {
+ char buf[4096];
+ ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1));
+ if (len > 0) {
+ while (len > 1 && buf[len-1] == '\n') {
+ --len;
+ }
+ buf[len] = '\000';
+ SANDBOX_DIE(buf);
+ }
+ }
+ if (HANDLE_EINTR(close(fds[0]))) {
+ SANDBOX_DIE("close() failed");
+ }
+
+ return rc;
+}
+
+bool Sandbox::KernelSupportSeccompBPF() {
+ return
+ RunFunctionInPolicy(ProbeProcess, ProbeEvaluator, 0) &&
+ RunFunctionInPolicy(TryVsyscallProcess, AllowAllEvaluator, 0);
+}
+
+Sandbox::SandboxStatus Sandbox::SupportsSeccompSandbox(int proc_fd) {
+ // It the sandbox is currently active, we clearly must have support for
+ // sandboxing.
+ if (status_ == STATUS_ENABLED) {
+ return status_;
+ }
+
+ // Even if the sandbox was previously available, something might have
+ // changed in our run-time environment. Check one more time.
+ if (status_ == STATUS_AVAILABLE) {
+ if (!IsSingleThreaded(proc_fd)) {
+ status_ = STATUS_UNAVAILABLE;
+ }
+ return status_;
+ }
+
+ if (status_ == STATUS_UNAVAILABLE && IsSingleThreaded(proc_fd)) {
+ // All state transitions resulting in STATUS_UNAVAILABLE are immediately
+ // preceded by STATUS_AVAILABLE. Furthermore, these transitions all
+ // happen, if and only if they are triggered by the process being multi-
+ // threaded.
+ // In other words, if a single-threaded process is currently in the
+ // STATUS_UNAVAILABLE state, it is safe to assume that sandboxing is
+ // actually available.
+ status_ = STATUS_AVAILABLE;
+ return status_;
+ }
+
+ // If we have not previously checked for availability of the sandbox or if
+ // we otherwise don't believe to have a good cached value, we have to
+ // perform a thorough check now.
+ if (status_ == STATUS_UNKNOWN) {
+ // We create our own private copy of a "Sandbox" object. This ensures that
+ // the object does not have any policies configured, that might interfere
+ // with the tests done by "KernelSupportSeccompBPF()".
+ Sandbox sandbox;
+
+ // By setting "quiet_ = true" we suppress messages for expected and benign
+ // failures (e.g. if the current kernel lacks support for BPF filters).
+ sandbox.quiet_ = true;
+ sandbox.set_proc_fd(proc_fd);
+ status_ = sandbox.KernelSupportSeccompBPF()
+ ? STATUS_AVAILABLE : STATUS_UNSUPPORTED;
+
+ // As we are performing our tests from a child process, the run-time
+ // environment that is visible to the sandbox is always guaranteed to be
+ // single-threaded. Let's check here whether the caller is single-
+ // threaded. Otherwise, we mark the sandbox as temporarily unavailable.
+ if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) {
+ status_ = STATUS_UNAVAILABLE;
+ }
+ }
+ return status_;
+}
+
+void Sandbox::set_proc_fd(int proc_fd) {
+ proc_fd_ = proc_fd;
+}
+
+void Sandbox::StartSandbox() {
+ if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) {
+ SANDBOX_DIE("Trying to start sandbox, even though it is known to be "
+ "unavailable");
+ } else if (!evaluators_ || !conds_) {
+ SANDBOX_DIE("Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
+ }
+ if (proc_fd_ < 0) {
+ proc_fd_ = open("/proc", O_RDONLY|O_DIRECTORY);
+ }
+ if (proc_fd_ < 0) {
+ // 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_)) {
+ SANDBOX_DIE("Cannot start sandbox, if process is already multi-threaded");
+ }
+
+ // We no longer need access to any files in /proc. We want to do this
+ // before installing the filters, just in case that our policy denies
+ // close().
+ if (proc_fd_ >= 0) {
+ if (HANDLE_EINTR(close(proc_fd_))) {
+ SANDBOX_DIE("Failed to close file descriptor for /proc");
+ }
+ proc_fd_ = -1;
+ }
+
+ // Install the filters.
+ InstallFilter();
+
+ // We are now inside the sandbox.
+ status_ = STATUS_ENABLED;
+}
+
+void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator,
+ void *aux) {
+ for (SyscallIterator iter(true); !iter.Done(); ) {
+ uint32_t sysnum = iter.Next();
+ if (!IsDenied(syscall_evaluator(this, sysnum, aux))) {
+ SANDBOX_DIE("Policies should deny system calls that are outside the "
+ "expected range (typically MIN_SYSCALL..MAX_SYSCALL)");
+ }
+ }
+ return;
+}
+
+void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) {
+ if (!evaluators_ || !conds_) {
+ SANDBOX_DIE("Cannot change policy after sandbox has started");
+ }
+ PolicySanityChecks(syscall_evaluator, aux);
+ evaluators_->push_back(std::make_pair(syscall_evaluator, aux));
+}
+
+void Sandbox::InstallFilter() {
+ // We want to be very careful in not imposing any requirements on the
+ // policies that are set with SetSandboxPolicy(). This means, as soon as
+ // the sandbox is active, we shouldn't be relying on libraries that could
+ // be making system calls. This, for example, means we should avoid
+ // using the heap and we should avoid using STL functions.
+ // Temporarily copy the contents of the "program" vector into a
+ // stack-allocated array; and then explicitly destroy that object.
+ // This makes sure we don't ex- or implicitly call new/delete after we
+ // installed the BPF filter program in the kernel. Depending on the
+ // system memory allocator that is in effect, these operators can result
+ // in system calls to things like munmap() or brk().
+ Program *program = AssembleFilter(false /* force_verification */);
+
+ struct sock_filter bpf[program->size()];
+ const struct sock_fprog prog = {
+ static_cast<unsigned short>(program->size()), bpf };
+ memcpy(bpf, &(*program)[0], sizeof(bpf));
+ delete program;
+
+ // Release memory that is no longer needed
+ delete evaluators_;
+ delete conds_;
+ evaluators_ = NULL;
+ conds_ = NULL;
+
+ // Install BPF filter program
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs");
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to turn on BPF filters");
+ }
+ }
+
+ return;
+}
+
+Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) {
+#if !defined(NDEBUG)
+ force_verification = true;
+#endif
+
+ // Verify that the user pushed a policy.
+ if (evaluators_->empty()) {
+ SANDBOX_DIE("Failed to configure system call filters");
+ }
+
+ // We can't handle stacked evaluators, yet. We'll get there eventually
+ // though. Hang tight.
+ if (evaluators_->size() != 1) {
+ SANDBOX_DIE("Not implemented");
+ }
+
+ // Assemble the BPF filter program.
+ CodeGen *gen = new CodeGen();
+ if (!gen) {
+ SANDBOX_DIE("Out of memory");
+ }
+
+ // If the architecture doesn't match SECCOMP_ARCH, disallow the
+ // system call.
+ Instruction *tail;
+ Instruction *head =
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_ARCH_IDX,
+ tail =
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_ARCH,
+ NULL,
+ gen->MakeInstruction(BPF_RET+BPF_K,
+ Kill("Invalid audit architecture in BPF filter"))));
+
+ bool has_unsafe_traps = false;
+ {
+ // Evaluate all possible system calls and group their ErrorCodes into
+ // ranges of identical codes.
+ Ranges ranges;
+ FindRanges(&ranges);
+
+ // Compile the system call ranges to an optimized BPF jumptable
+ Instruction *jumptable =
+ AssembleJumpTable(gen, ranges.begin(), ranges.end());
+
+ // If there is at least one UnsafeTrap() in our program, the entire sandbox
+ // is unsafe. We need to modify the program so that all non-
+ // SECCOMP_RET_ALLOW ErrorCodes are handled in user-space. This will then
+ // allow us to temporarily disable sandboxing rules inside of callbacks to
+ // UnsafeTrap().
+ gen->Traverse(jumptable, CheckForUnsafeErrorCodes, &has_unsafe_traps);
+
+ // Grab the system call number, so that we can implement jump tables.
+ Instruction *load_nr =
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_NR_IDX);
+
+ // If our BPF program has unsafe jumps, enable support for them. This
+ // test happens very early in the BPF filter program. Even before we
+ // consider looking at system call numbers.
+ // As support for unsafe jumps essentially defeats all the security
+ // measures that the sandbox provides, we print a big warning message --
+ // 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) {
+ SANDBOX_DIE("Support for UnsafeTrap() has not yet been ported to this "
+ "architecture");
+ }
+
+ EvaluateSyscall evaluateSyscall = evaluators_->begin()->first;
+ void *aux = evaluators_->begin()->second;
+ if (!evaluateSyscall(this, __NR_rt_sigprocmask, aux).
+ Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) ||
+ !evaluateSyscall(this, __NR_rt_sigreturn, aux).
+ Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+#if defined(__NR_sigprocmask)
+ || !evaluateSyscall(this, __NR_sigprocmask, aux).
+ Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+#endif
+#if defined(__NR_sigreturn)
+ || !evaluateSyscall(this, __NR_sigreturn, aux).
+ Equals(ErrorCode(ErrorCode::ERR_ALLOWED))
+#endif
+ ) {
+ SANDBOX_DIE("Invalid seccomp policy; if using UnsafeTrap(), you must "
+ "unconditionally allow sigreturn() and sigprocmask()");
+ }
+
+ if (!Trap::EnableUnsafeTrapsInSigSysHandler()) {
+ // We should never be able to get here, as UnsafeTrap() should never
+ // actually return a valid ErrorCode object unless the user set the
+ // CHROME_SANDBOX_DEBUGGING environment variable; and therefore,
+ // "has_unsafe_traps" would always be false. But better double-check
+ // than enabling dangerous code.
+ SANDBOX_DIE("We'd rather die than enable unsafe traps");
+ }
+ 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));
+ 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);
+#endif
+
+ // BPF cannot do native 64bit comparisons. On 64bit architectures, we
+ // have to compare both 32bit halves of the instruction pointer. If they
+ // match what we expect, we return ERR_ALLOWED. If either or both don't
+ // match, we continue evalutating the rest of the sandbox policy.
+ Instruction *escape_hatch =
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_LSB_IDX,
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, low,
+#if __SIZEOF_POINTER__ > 4
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS, SECCOMP_IP_MSB_IDX,
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, hi,
+#endif
+ gen->MakeInstruction(BPF_RET+BPF_K, ErrorCode(ErrorCode::ERR_ALLOWED)),
+#if __SIZEOF_POINTER__ > 4
+ load_nr)),
+#endif
+ load_nr));
+ gen->JoinInstructions(tail, escape_hatch);
+ } else {
+ gen->JoinInstructions(tail, load_nr);
+ }
+ tail = load_nr;
+
+ // On Intel architectures, verify that system call numbers are in the
+ // expected number range. The older i386 and x86-64 APIs clear bit 30
+ // on all system calls. The newer x32 API always sets bit 30.
+#if defined(__i386__) || defined(__x86_64__)
+ Instruction *invalidX32 =
+ gen->MakeInstruction(BPF_RET+BPF_K,
+ Kill("Illegal mixing of system call ABIs").err_);
+ Instruction *checkX32 =
+#if defined(__x86_64__) && defined(__ILP32__)
+ gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, 0, invalidX32);
+#else
+ gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K, 0x40000000, invalidX32, 0);
+#endif
+ gen->JoinInstructions(tail, checkX32);
+ tail = checkX32;
+#endif
+
+ // Append jump table to our pre-amble
+ gen->JoinInstructions(tail, jumptable);
+ }
+
+ // Turn the DAG into a vector of instructions.
+ Program *program = new Program();
+ gen->Compile(head, program);
+ delete gen;
+
+ // Make sure compilation resulted in BPF program that executes
+ // correctly. Otherwise, there is an internal error in our BPF compiler.
+ // There is really nothing the caller can do until the bug is fixed.
+ if (force_verification) {
+ // Verification is expensive. We only perform this step, if we are
+ // compiled in debug mode, or if the caller explicitly requested
+ // verification.
+ VerifyProgram(*program, has_unsafe_traps);
+ }
+
+ return program;
+}
+
+void Sandbox::VerifyProgram(const Program& program, bool has_unsafe_traps) {
+ // If we previously rewrote the BPF program so that it calls user-space
+ // whenever we return an "errno" value from the filter, then we have to
+ // wrap our system call evaluator to perform the same operation. Otherwise,
+ // the verifier would also report a mismatch in return codes.
+ Evaluators redirected_evaluators;
+ redirected_evaluators.push_back(
+ std::make_pair(RedirectToUserspaceEvalWrapper, evaluators_));
+
+ const char *err = NULL;
+ if (!Verifier::VerifyBPF(
+ this,
+ program,
+ has_unsafe_traps ? redirected_evaluators : *evaluators_,
+ &err)) {
+ CodeGen::PrintProgram(program);
+ SANDBOX_DIE(err);
+ }
+}
+
+void Sandbox::FindRanges(Ranges *ranges) {
+ // Please note that "struct seccomp_data" defines system calls as a signed
+ // int32_t, but BPF instructions always operate on unsigned quantities. We
+ // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
+ // and then verifying that the rest of the number range (both positive and
+ // negative) all return the same ErrorCode.
+ EvaluateSyscall evaluate_syscall = evaluators_->begin()->first;
+ void *aux = evaluators_->begin()->second;
+ uint32_t old_sysnum = 0;
+ ErrorCode old_err = evaluate_syscall(this, old_sysnum, aux);
+ ErrorCode invalid_err = evaluate_syscall(this, MIN_SYSCALL - 1,
+ aux);
+ for (SyscallIterator iter(false); !iter.Done(); ) {
+ uint32_t sysnum = iter.Next();
+ ErrorCode err = evaluate_syscall(this, static_cast<int>(sysnum), aux);
+ 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");
+ }
+ if (!err.Equals(old_err) || iter.Done()) {
+ ranges->push_back(Range(old_sysnum, sysnum - 1, old_err));
+ old_sysnum = sysnum;
+ old_err = err;
+ }
+ }
+}
+
+Instruction *Sandbox::AssembleJumpTable(CodeGen *gen,
+ Ranges::const_iterator start,
+ Ranges::const_iterator stop) {
+ // We convert the list of system call ranges into jump table that performs
+ // a binary search over the ranges.
+ // As a sanity check, we need to have at least one distinct ranges for us
+ // to be able to build a jump table.
+ if (stop - start <= 0) {
+ SANDBOX_DIE("Invalid set of system call ranges");
+ } else if (stop - start == 1) {
+ // If we have narrowed things down to a single range object, we can
+ // return from the BPF filter program.
+ return RetExpression(gen, start->err);
+ }
+
+ // Pick the range object that is located at the mid point of our list.
+ // We compare our system call number against the lowest valid system call
+ // number in this range object. If our number is lower, it is outside of
+ // this range object. If it is greater or equal, it might be inside.
+ Ranges::const_iterator mid = start + (stop - start)/2;
+
+ // Sub-divide the list of ranges and continue recursively.
+ Instruction *jf = AssembleJumpTable(gen, start, mid);
+ Instruction *jt = AssembleJumpTable(gen, mid, stop);
+ return gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, mid->from, jt, jf);
+}
+
+Instruction *Sandbox::RetExpression(CodeGen *gen, const ErrorCode& err) {
+ if (err.error_type_ == ErrorCode::ET_COND) {
+ return CondExpression(gen, err);
+ } else {
+ return gen->MakeInstruction(BPF_RET+BPF_K, err);
+ }
+}
+
+Instruction *Sandbox::CondExpression(CodeGen *gen, const ErrorCode& cond) {
+ // We can only inspect the six system call arguments that are passed in
+ // CPU registers.
+ if (cond.argno_ < 0 || cond.argno_ >= 6) {
+ SANDBOX_DIE("Internal compiler error; invalid argument number "
+ "encountered");
+ }
+
+ // BPF programs operate on 32bit entities. Load both halfs of the 64bit
+ // system call argument and then generate suitable conditional statements.
+ Instruction *msb_head =
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+ SECCOMP_ARG_MSB_IDX(cond.argno_));
+ Instruction *msb_tail = msb_head;
+ Instruction *lsb_head =
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+ SECCOMP_ARG_LSB_IDX(cond.argno_));
+ Instruction *lsb_tail = lsb_head;
+
+ // Emit a suitable comparison statement.
+ switch (cond.op_) {
+ case ErrorCode::OP_EQUAL:
+ // Compare the least significant bits for equality
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ static_cast<uint32_t>(cond.value_),
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+
+ // If we are looking at a 64bit argument, we need to also compare the
+ // most significant bits.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ static_cast<uint32_t>(cond.value_ >> 32),
+ lsb_head,
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(msb_head, msb_tail);
+ }
+ break;
+ case ErrorCode::OP_HAS_ALL_BITS:
+ // Check the bits in the LSB half of the system call argument. Our
+ // OP_HAS_ALL_BITS operator passes, iff all of the bits are set. This is
+ // different from the kernel's BPF_JSET operation which passes, if any of
+ // the bits are set.
+ // Of course, if there is only a single set bit (or none at all), then
+ // things get easier.
+ {
+ uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+ int lsb_bit_count = popcount(lsb_bits);
+ if (lsb_bit_count == 0) {
+ // No bits are set in the LSB half. The test will always pass.
+ lsb_head = RetExpression(gen, *cond.passed_);
+ lsb_tail = NULL;
+ } else if (lsb_bit_count == 1) {
+ // Exactly one bit is set in the LSB half. We can use the BPF_JSET
+ // operator.
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+ } else {
+ // More than one bit is set in the LSB half. We need to combine
+ // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+ // set in the system call argument.
+ gen->JoinInstructions(lsb_head,
+ gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
+ lsb_bits,
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_))));
+ }
+ }
+
+ // If we are looking at a 64bit argument, we need to also check the bits
+ // in the MSB half of the system call argument.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+ int msb_bit_count = popcount(msb_bits);
+ if (msb_bit_count == 0) {
+ // No bits are set in the MSB half. The test will always pass.
+ msb_head = lsb_head;
+ } else if (msb_bit_count == 1) {
+ // Exactly one bit is set in the MSB half. We can use the BPF_JSET
+ // operator.
+ msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ msb_bits,
+ lsb_head,
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(msb_head, msb_tail);
+ } else {
+ // More than one bit is set in the MSB half. We need to combine
+ // BPF_AND and BPF_JEQ to test whether all of these bits are in fact
+ // set in the system call argument.
+ gen->JoinInstructions(msb_head,
+ gen->MakeInstruction(BPF_ALU+BPF_AND+BPF_K,
+ msb_bits,
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K,
+ msb_bits,
+ lsb_head,
+ RetExpression(gen, *cond.failed_))));
+ }
+ }
+ break;
+ case ErrorCode::OP_HAS_ANY_BITS:
+ // Check the bits in the LSB half of the system call argument. Our
+ // OP_HAS_ANY_BITS operator passes, iff any of the bits are set. This maps
+ // nicely to the kernel's BPF_JSET operation.
+ {
+ uint32_t lsb_bits = static_cast<uint32_t>(cond.value_);
+ if (!lsb_bits) {
+ // No bits are set in the LSB half. The test will always fail.
+ lsb_head = RetExpression(gen, *cond.failed_);
+ lsb_tail = NULL;
+ } else {
+ lsb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ lsb_bits,
+ RetExpression(gen, *cond.passed_),
+ RetExpression(gen, *cond.failed_));
+ gen->JoinInstructions(lsb_head, lsb_tail);
+ }
+ }
+
+ // If we are looking at a 64bit argument, we need to also check the bits
+ // in the MSB half of the system call argument.
+ if (cond.width_ == ErrorCode::TP_64BIT) {
+ uint32_t msb_bits = static_cast<uint32_t>(cond.value_ >> 32);
+ if (!msb_bits) {
+ // No bits are set in the MSB half. The test will always fail.
+ msb_head = lsb_head;
+ } else {
+ msb_tail = gen->MakeInstruction(BPF_JMP+BPF_JSET+BPF_K,
+ msb_bits,
+ RetExpression(gen, *cond.passed_),
+ lsb_head);
+ gen->JoinInstructions(msb_head, msb_tail);
+ }
+ }
+ break;
+ default:
+ // TODO(markus): Need to add support for OP_GREATER
+ SANDBOX_DIE("Not implemented");
+ break;
+ }
+
+ // Ensure that we never pass a 64bit value, when we only expect a 32bit
+ // value. This is somewhat complicated by the fact that on 64bit systems,
+ // callers could legitimately pass in a non-zero value in the MSB, iff the
+ // LSB has been sign-extended into the MSB.
+ if (cond.width_ == ErrorCode::TP_32BIT) {
+ if (cond.value_ >> 32) {
+ SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
+ "against a 64bit constant; this test is always false.");
+ }
+
+ Instruction *invalid_64bit = RetExpression(gen, Unexpected64bitArgument());
+ #if __SIZEOF_POINTER__ > 4
+ invalid_64bit =
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0xFFFFFFFF,
+ gen->MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+ SECCOMP_ARG_LSB_IDX(cond.argno_),
+ gen->MakeInstruction(BPF_JMP+BPF_JGE+BPF_K, 0x80000000,
+ lsb_head,
+ invalid_64bit)),
+ invalid_64bit);
+ #endif
+ gen->JoinInstructions(
+ msb_tail,
+ gen->MakeInstruction(BPF_JMP+BPF_JEQ+BPF_K, 0,
+ lsb_head,
+ invalid_64bit));
+ }
+
+ return msb_head;
+}
+
+ErrorCode Sandbox::Unexpected64bitArgument() {
+ return Kill("Unexpected 64bit argument detected");
+}
+
+ErrorCode Sandbox::Trap(Trap::TrapFnc fnc, const void *aux) {
+ return Trap::MakeTrap(fnc, aux, true /* Safe Trap */);
+}
+
+ErrorCode Sandbox::UnsafeTrap(Trap::TrapFnc fnc, const void *aux) {
+ return Trap::MakeTrap(fnc, aux, false /* Unsafe Trap */);
+}
+
+intptr_t Sandbox::ForwardSyscall(const struct arch_seccomp_data& args) {
+ 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]));
+}
+
+ErrorCode Sandbox::Cond(int argno, ErrorCode::ArgType width,
+ ErrorCode::Operation op, uint64_t value,
+ const ErrorCode& passed, const ErrorCode& failed) {
+ return ErrorCode(argno, width, op, value,
+ &*conds_->insert(passed).first,
+ &*conds_->insert(failed).first);
+}
+
+ErrorCode Sandbox::Kill(const char *msg) {
+ return Trap(BpfFailure, const_cast<char *>(msg));
+}
+
+Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN;
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
new file mode 100644
index 00000000000..f2653b077ac
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -0,0 +1,271 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
+
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h"
+
+namespace playground2 {
+
+struct arch_seccomp_data {
+ int nr;
+ uint32_t arch;
+ uint64_t instruction_pointer;
+ uint64_t args[6];
+};
+
+struct arch_sigsys {
+ void *ip;
+ int nr;
+ unsigned int arch;
+};
+
+class CodeGen;
+class SandboxUnittestHelper;
+struct Instruction;
+
+class Sandbox {
+ public:
+ enum SandboxStatus {
+ STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox()
+ STATUS_UNSUPPORTED, // The kernel does not appear to support sandboxing
+ STATUS_UNAVAILABLE, // Currently unavailable but might work again later
+ STATUS_AVAILABLE, // Sandboxing is available but not currently active
+ STATUS_ENABLED // The sandbox is now active
+ };
+
+ // BpfSandboxPolicy is the following type:
+ // ErrorCode (Sandbox *sb, int sysnum, void *aux);
+ // When calling setSandboxPolicy(), the caller can provide an arbitrary
+ // pointer in |aux|. This pointer will then be forwarded to the sandbox
+ // policy each time a call is made through an EvaluateSyscall function
+ // pointer. One common use case would be to pass the "aux" pointer as an
+ // argument to Trap() functions.
+ typedef BpfSandboxPolicy* EvaluateSyscall;
+ typedef std::vector<std::pair<EvaluateSyscall, void *> >Evaluators;
+
+ // A vector of BPF instructions that need to be installed as a filter
+ // program in the kernel.
+ typedef std::vector<struct sock_filter> Program;
+
+ // Constructors and destructors.
+ // NOTE: Setting a policy and starting the sandbox is a one-way operation.
+ // The kernel does not provide any option for unloading a loaded
+ // sandbox. Strictly speaking, that means we should disallow calling
+ // the destructor, if StartSandbox() has ever been called. In practice,
+ // this makes it needlessly complicated to operate on "Sandbox"
+ // objects. So, we instead opted to allow object destruction. But it
+ // should be noted that during its lifetime, the object probably made
+ // irreversible state changes to the runtime environment. These changes
+ // stay in effect even after the destructor has been run.
+ Sandbox();
+ ~Sandbox();
+
+ // Checks whether a particular system call number is valid on the current
+ // architecture. E.g. on ARM there's a non-contiguous range of private
+ // system calls.
+ static bool IsValidSyscallNumber(int sysnum);
+
+ // There are a lot of reasons why the Seccomp sandbox might not be available.
+ // This could be because the kernel does not support Seccomp mode, or it
+ // could be because another sandbox is already active.
+ // "proc_fd" should be a file descriptor for "/proc", or -1 if not
+ // provided by the caller.
+ static SandboxStatus SupportsSeccompSandbox(int proc_fd);
+
+ // The sandbox needs to be able to access files in "/proc/self". If this
+ // directory is not accessible when "startSandbox()" gets called, the caller
+ // can provide an already opened file descriptor by calling "set_proc_fd()".
+ // The sandbox becomes the new owner of this file descriptor and will
+ // 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.
+ void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux);
+
+ // We can use ErrorCode to request calling of a trap handler. This method
+ // performs the required wrapping of the callback function into an
+ // ErrorCode object.
+ // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall
+ // for a description of how to pass data from SetSandboxPolicy() to a Trap()
+ // handler.
+ ErrorCode Trap(Trap::TrapFnc fnc, const void *aux);
+
+ // Calls a user-space trap handler and disables all sandboxing for system
+ // calls made from this trap handler.
+ // This feature is available only if explicitly enabled by the user having
+ // set the CHROME_SANDBOX_DEBUGGING environment variable.
+ // Returns an ET_INVALID ErrorCode, if called when not enabled.
+ // NOTE: This feature, by definition, disables all security features of
+ // the sandbox. It should never be used in production, but it can be
+ // very useful to diagnose code that is incompatible with the sandbox.
+ // If even a single system call returns "UnsafeTrap", the security of
+ // entire sandbox should be considered compromised.
+ ErrorCode UnsafeTrap(Trap::TrapFnc fnc, const void *aux);
+
+ // From within an UnsafeTrap() it is often useful to be able to execute
+ // the system call that triggered the trap. The ForwardSyscall() method
+ // makes this easy. It is more efficient than calling glibc's syscall()
+ // function, as it avoid the extra round-trip to the signal handler. And
+ // it automatically does the correct thing to report kernel-style error
+ // conditions, rather than setting errno. See the comments for TrapFnc for
+ // details. In other words, the return value from ForwardSyscall() is
+ // directly suitable as a return value for a trap handler.
+ static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
+
+ // We can also use ErrorCode to request evaluation of a conditional
+ // statement based on inspection of system call parameters.
+ // This method wrap an ErrorCode object around the conditional statement.
+ // Argument "argno" (1..6) will be compared to "value" using comparator
+ // "op". If the condition is true "passed" will be returned, otherwise
+ // "failed".
+ // If "is32bit" is set, the argument must in the range of 0x0..(1u << 32 - 1)
+ // If it is outside this range, the sandbox treats the system call just
+ // the same as any other ABI violation (i.e. it aborts with an error
+ // message).
+ ErrorCode Cond(int argno, ErrorCode::ArgType is_32bit,
+ ErrorCode::Operation op,
+ uint64_t value, const ErrorCode& passed,
+ const ErrorCode& failed);
+
+ // Kill the program and print an error message.
+ ErrorCode Kill(const char *msg);
+
+ // This is the main public entry point. It finds all system calls that
+ // need rewriting, sets up the resources needed by the sandbox, and
+ // enters Seccomp mode.
+ // 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
+ // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+ // a new policy requires making system calls, that might already be
+ // 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();
+
+ // Assembles a BPF filter program from the current policy. After calling this
+ // function, you must not call any other sandboxing function.
+ // Typically, AssembleFilter() is only used by unit tests and by sandbox
+ // internals. It should not be used by production code.
+ // For performance reasons, we normally only run the assembled BPF program
+ // through the verifier, iff the program was built in debug mode.
+ // But by setting "force_verification", the caller can request that the
+ // verifier is run unconditionally. This is useful for unittests.
+ Program *AssembleFilter(bool force_verification);
+
+ // Returns the fatal ErrorCode that is used to indicate that somebody
+ // attempted to pass a 64bit value in a 32bit system call argument.
+ // This method is primarily needed for testing purposes.
+ ErrorCode Unexpected64bitArgument();
+
+ private:
+ friend class CodeGen;
+ friend class SandboxUnittestHelper;
+ friend class ErrorCode;
+
+ struct Range {
+ Range(uint32_t f, uint32_t t, const ErrorCode& e)
+ : from(f),
+ to(t),
+ err(e) {
+ }
+ uint32_t from, to;
+ ErrorCode err;
+ };
+ typedef std::vector<Range> Ranges;
+ typedef std::map<uint32_t, ErrorCode> ErrMap;
+ typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
+
+ // Get a file descriptor pointing to "/proc", if currently available.
+ int proc_fd() { return proc_fd_; }
+
+ // Creates a subprocess and runs "code_in_sandbox" inside of the specified
+ // 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);
+
+ // Performs a couple of sanity checks to verify that the kernel supports the
+ // features that we need for successful sandboxing.
+ // The caller has to make sure that "this" has not yet been initialized with
+ // any other policies.
+ bool KernelSupportSeccompBPF();
+
+ // Verify that the current policy passes some basic sanity checks.
+ void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux);
+
+ // Assembles and installs a filter based on the policy that has previously
+ // been configured with SetSandboxPolicy().
+ void InstallFilter();
+
+ // 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
+ // by the sandbox internals. It should not be used by production code.
+ void VerifyProgram(const Program& program, bool has_unsafe_traps);
+
+ // Finds all the ranges of system calls that need to be handled. Ranges are
+ // sorted in ascending order of system call numbers. There are no gaps in the
+ // ranges. System calls with identical ErrorCodes are coalesced into a single
+ // range.
+ void FindRanges(Ranges *ranges);
+
+ // Returns a BPF program snippet that implements a jump table for the
+ // given range of system call numbers. This function runs recursively.
+ Instruction *AssembleJumpTable(CodeGen *gen,
+ Ranges::const_iterator start,
+ Ranges::const_iterator stop);
+
+ // Returns a BPF program snippet that makes the BPF filter program exit
+ // with the given ErrorCode "err". N.B. the ErrorCode may very well be a
+ // conditional expression; if so, this function will recursively call
+ // CondExpression() and possibly RetExpression() to build a complex set of
+ // instructions.
+ Instruction *RetExpression(CodeGen *gen, const ErrorCode& err);
+
+ // Returns a BPF program that evaluates the conditional expression in
+ // "cond" and returns the appropriate value from the BPF filter program.
+ // This function recursively calls RetExpression(); it should only ever be
+ // called from RetExpression().
+ Instruction *CondExpression(CodeGen *gen, const ErrorCode& cond);
+
+ static SandboxStatus status_;
+
+ bool quiet_;
+ int proc_fd_;
+ Evaluators *evaluators_;
+ Conds *conds_;
+
+ DISALLOW_COPY_AND_ASSIGN(Sandbox);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h
new file mode 100644
index 00000000000..afc9d870368
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_policy_forward.h
@@ -0,0 +1,23 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_FORWARD_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_FORWARD_H_
+
+#include "base/callback_forward.h"
+
+namespace playground2 {
+
+class Sandbox;
+class ErrorCode;
+typedef ErrorCode BpfSandboxPolicy(
+ Sandbox* sandbox_compiler,
+ int system_call_number,
+ void* aux);
+
+typedef base::Callback<BpfSandboxPolicy> BpfSandboxPolicyCallback;
+
+} // namespace playground2
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_FORWARD_H_
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
new file mode 100644
index 00000000000..1713e8f2029
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -0,0 +1,1729 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#if defined(ANDROID)
+// Work-around for buggy headers in Android's NDK
+#define __user
+#endif
+#include <linux/futex.h>
+
+#include <ostream>
+
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/seccomp-bpf/verifier.h"
+#include "sandbox/linux/services/broker_process.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Workaround for Android's prctl.h file.
+#ifndef PR_GET_ENDIAN
+#define PR_GET_ENDIAN 19
+#endif
+#ifndef PR_CAPBSET_READ
+#define PR_CAPBSET_READ 23
+#define PR_CAPBSET_DROP 24
+#endif
+
+using namespace playground2;
+using sandbox::BrokerProcess;
+
+namespace {
+
+const int kExpectedReturnValue = 42;
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// This test should execute no matter whether we have kernel support. So,
+// we make it a TEST() instead of a BPF_TEST().
+TEST(SandboxBpf, CallSupports) {
+ // We check that we don't crash, but it's ok if the kernel doesn't
+ // support it.
+ bool seccomp_bpf_supported =
+ Sandbox::SupportsSeccompSandbox(-1) == Sandbox::STATUS_AVAILABLE;
+ // We want to log whether or not seccomp BPF is actually supported
+ // since actual test coverage depends on it.
+ RecordProperty("SeccompBPFSupported",
+ seccomp_bpf_supported ? "true." : "false.");
+ std::cout << "Seccomp BPF supported: "
+ << (seccomp_bpf_supported ? "true." : "false.")
+ << "\n";
+ RecordProperty("PointerSize", sizeof(void*));
+ std::cout << "Pointer size: " << sizeof(void*) << "\n";
+}
+
+SANDBOX_TEST(SandboxBpf, CallSupportsTwice) {
+ Sandbox::SupportsSeccompSandbox(-1);
+ Sandbox::SupportsSeccompSandbox(-1);
+}
+
+// BPF_TEST does a lot of the boiler-plate code around setting up a
+// policy and optional passing data between the caller, the policy and
+// any Trap() handlers. This is great for writing short and concise tests,
+// and it helps us accidentally forgetting any of the crucial steps in
+// 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) {
+ BPF_ASSERT(aux);
+ pid_t *pid_ptr = static_cast<pid_t *>(aux);
+ return (*pid_ptr)++;
+}
+
+ErrorCode VerboseAPITestingPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ } else if (sysno == __NR_getpid) {
+ return sandbox->Trap(FakeGetPid, aux);
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+SANDBOX_TEST(SandboxBpf, DISABLE_ON_TSAN(VerboseAPITesting)) {
+ if (Sandbox::SupportsSeccompSandbox(-1) ==
+ playground2::Sandbox::STATUS_AVAILABLE) {
+ pid_t test_var = 0;
+ Sandbox sandbox;
+ sandbox.SetSandboxPolicy(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.
+ }
+}
+
+// A simple blacklist test
+
+ErrorCode BlacklistNanosleepPolicy(Sandbox *, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_nanosleep:
+ return ErrorCode(EACCES);
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
+ // nanosleep() should be denied
+ const struct timespec ts = {0, 0};
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_nanosleep, &ts, NULL) == -1);
+ BPF_ASSERT(errno == EACCES);
+}
+
+// Now do a simple whitelist test
+
+ErrorCode WhitelistGetpidPolicy(Sandbox *, int sysno, void *) {
+ switch (sysno) {
+ case __NR_getpid:
+ case __NR_exit_group:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ default:
+ return ErrorCode(ENOMEM);
+ }
+}
+
+BPF_TEST(SandboxBpf, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
+ // getpid() should be allowed
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getpid) > 0);
+ BPF_ASSERT(errno == 0);
+
+ // getpgid() should be denied
+ BPF_ASSERT(getpgid(0) == -1);
+ BPF_ASSERT(errno == ENOMEM);
+}
+
+// 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);
+ *(static_cast<int*>(aux)) = kExpectedReturnValue;
+ return -ENOMEM;
+}
+
+ErrorCode BlacklistNanosleepPolicySigsys(Sandbox *sandbox, int sysno,
+ void *aux) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_nanosleep:
+ return sandbox->Trap(EnomemHandler, aux);
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, BasicBlacklistWithSigsys,
+ BlacklistNanosleepPolicySigsys, 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;
+ 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);
+}
+
+// A simple test that verifies we can return arbitrary errno values.
+
+ErrorCode ErrnoTestPolicy(Sandbox *, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_dup2:
+ // Pretend that dup2() worked, but don't actually do anything.
+ return ErrorCode(0);
+ case __NR_setuid:
+#if defined(__NR_setuid32)
+ case __NR_setuid32:
+#endif
+ // Return errno = 1.
+ return ErrorCode(1);
+ case __NR_setgid:
+#if defined(__NR_setgid32)
+ case __NR_setgid32:
+#endif
+ // Return maximum errno value (typically 4095).
+ return ErrorCode(ErrorCode::ERR_MAX_ERRNO);
+ case __NR_uname:
+ // Return errno = 42;
+ return ErrorCode(42);
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, ErrnoTest, ErrnoTestPolicy) {
+ // Verify that dup2() returns success, but doesn't actually run.
+ int fds[4];
+ BPF_ASSERT(pipe(fds) == 0);
+ BPF_ASSERT(pipe(fds+2) == 0);
+ BPF_ASSERT(dup2(fds[2], fds[0]) == 0);
+ char buf[1] = { };
+ BPF_ASSERT(write(fds[1], "\x55", 1) == 1);
+ BPF_ASSERT(write(fds[3], "\xAA", 1) == 1);
+ BPF_ASSERT(read(fds[0], buf, 1) == 1);
+
+ // If dup2() executed, we will read \xAA, but it dup2() has been turned
+ // into a no-op by our policy, then we will read \x55.
+ BPF_ASSERT(buf[0] == '\x55');
+
+ // Verify that we can return the minimum and maximum errno values.
+ errno = 0;
+ BPF_ASSERT(setuid(0) == -1);
+ BPF_ASSERT(errno == 1);
+
+ // On Android, errno is only supported up to 255, otherwise errno
+ // processing is skipped.
+ // We work around this (crbug.com/181647).
+ if (sandbox::IsAndroid() && setgid(0) != -1) {
+ errno = 0;
+ BPF_ASSERT(setgid(0) == -ErrorCode::ERR_MAX_ERRNO);
+ BPF_ASSERT(errno == 0);
+ } else {
+ errno = 0;
+ BPF_ASSERT(setgid(0) == -1);
+ BPF_ASSERT(errno == ErrorCode::ERR_MAX_ERRNO);
+ }
+
+ // Finally, test an errno in between the minimum and maximum.
+ errno = 0;
+ struct utsname uts_buf;
+ BPF_ASSERT(uname(&uts_buf) == -1);
+ BPF_ASSERT(errno == 42);
+}
+
+// Testing the stacking of two sandboxes
+
+ErrorCode StackingPolicyPartOne(Sandbox *sandbox, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ }
+
+ 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);
+ }
+}
+
+ErrorCode StackingPolicyPartTwo(Sandbox *sandbox, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ }
+
+ 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);
+ }
+}
+
+BPF_TEST(SandboxBpf, StackingPolicy, StackingPolicyPartOne) {
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getppid, 0) > 0);
+ BPF_ASSERT(errno == 0);
+
+ BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ // Stack a second sandbox with its own policy. Verify that we can further
+ // restrict filters, but we cannot relax existing filters.
+ Sandbox sandbox;
+ sandbox.SetSandboxPolicy(StackingPolicyPartTwo, NULL);
+ sandbox.StartSandbox();
+
+ errno = 0;
+ BPF_ASSERT(syscall(__NR_getppid, 0) == -1);
+ BPF_ASSERT(errno == EINVAL);
+
+ BPF_ASSERT(syscall(__NR_getppid, 1) == -1);
+ BPF_ASSERT(errno == EPERM);
+}
+
+// A more complex, but synthetic policy. This tests the correctness of the BPF
+// program by iterating through all syscalls and checking for an errno that
+// depends on the syscall number. Unlike the Verifier, this exercises the BPF
+// interpreter in the kernel.
+
+// We try to make sure we exercise optimizations in the BPF compiler. We make
+// sure that the compiler can have an opportunity to coalesce syscalls with
+// contiguous numbers and we also make sure that disjoint sets can return the
+// same errno.
+int SysnoToRandomErrno(int sysno) {
+ // Small contiguous sets of 3 system calls return an errno equal to the
+ // index of that set + 1 (so that we never return a NUL errno).
+ return ((sysno & ~3) >> 2) % 29 + 1;
+}
+
+ErrorCode SyntheticPolicy(Sandbox *, int sysno, void *) {
+ if (!Sandbox::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 {
+ return ErrorCode(SysnoToRandomErrno(sysno));
+ }
+}
+
+BPF_TEST(SandboxBpf, SyntheticPolicy, SyntheticPolicy) {
+ // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
+ // overflow.
+ BPF_ASSERT(
+ std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
+ static_cast<int>(MAX_PUBLIC_SYSCALL));
+
+ for (int syscall_number = static_cast<int>(MIN_SYSCALL);
+ syscall_number <= static_cast<int>(MAX_PUBLIC_SYSCALL);
+ ++syscall_number) {
+ if (syscall_number == __NR_exit_group ||
+ syscall_number == __NR_write) {
+ // exit_group() is special
+ continue;
+ }
+ errno = 0;
+ BPF_ASSERT(syscall(syscall_number) == -1);
+ BPF_ASSERT(errno == SysnoToRandomErrno(syscall_number));
+ }
+}
+
+#if defined(__arm__)
+// A simple policy that tests whether ARM private system calls are supported
+// by our BPF compiler and by the BPF interpreter in the kernel.
+
+// For ARM private system calls, return an errno equal to their offset from
+// MIN_PRIVATE_SYSCALL plus 1 (to avoid NUL errno).
+int ArmPrivateSysnoToErrno(int sysno) {
+ if (sysno >= static_cast<int>(MIN_PRIVATE_SYSCALL) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return (sysno - MIN_PRIVATE_SYSCALL) + 1;
+ } else {
+ return ENOSYS;
+ }
+}
+
+ErrorCode ArmPrivatePolicy(Sandbox *, int sysno, void *) {
+ if (!Sandbox::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 {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, ArmPrivatePolicy, ArmPrivatePolicy) {
+ for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
+ syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
+ ++syscall_number) {
+ errno = 0;
+ BPF_ASSERT(syscall(syscall_number) == -1);
+ BPF_ASSERT(errno == ArmPrivateSysnoToErrno(syscall_number));
+ }
+}
+#endif // defined(__arm__)
+
+intptr_t CountSyscalls(const struct arch_seccomp_data& args, void *aux) {
+ // Count all invocations of our callback function.
+ ++*reinterpret_cast<int *>(aux);
+
+ // Verify that within the callback function all filtering is temporarily
+ // disabled.
+ BPF_ASSERT(syscall(__NR_getpid) > 1);
+
+ // Verify that we can now call the underlying system call without causing
+ // infinite recursion.
+ return Sandbox::ForwardSyscall(args);
+}
+
+ErrorCode GreyListedPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ // The use of UnsafeTrap() causes us to print a warning message. This is
+ // generally desirable, but it results in the unittest failing, as it doesn't
+ // expect any messages on "stderr". So, temporarily disable messages. The
+ // BPF_TEST() is guaranteed to turn messages back on, after the policy
+ // function has completed.
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ // Some system calls must always be allowed, if our policy wants to make
+ // use of UnsafeTrap()
+ if (sysno == __NR_rt_sigprocmask ||
+ sysno == __NR_rt_sigreturn
+#if defined(__NR_sigprocmask)
+ || sysno == __NR_sigprocmask
+#endif
+#if defined(__NR_sigreturn)
+ || sysno == __NR_sigreturn
+#endif
+ ) {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ } else if (sysno == __NR_getpid) {
+ // Disallow getpid()
+ return ErrorCode(EPERM);
+ } else if (Sandbox::IsValidSyscallNumber(sysno)) {
+ // Allow (and count) all other system calls.
+ return sandbox->UnsafeTrap(CountSyscalls, aux);
+ } else {
+ return ErrorCode(ENOSYS);
+ }
+}
+
+BPF_TEST(SandboxBpf, GreyListedPolicy,
+ GreyListedPolicy, int /* BPF_AUX */) {
+ BPF_ASSERT(syscall(__NR_getpid) == -1);
+ BPF_ASSERT(errno == EPERM);
+ BPF_ASSERT(BPF_AUX == 0);
+ BPF_ASSERT(syscall(__NR_geteuid) == syscall(__NR_getuid));
+ BPF_ASSERT(BPF_AUX == 2);
+ char name[17] = { };
+ BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
+ (void *)NULL, (void *)NULL));
+ BPF_ASSERT(BPF_AUX == 3);
+ BPF_ASSERT(*name);
+}
+
+SANDBOX_TEST(SandboxBpf, EnableUnsafeTrapsInSigSysHandler) {
+ // Disabling warning messages that could confuse our test framework.
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ unsetenv(kSandboxDebuggingEnv);
+ SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == false);
+ setenv(kSandboxDebuggingEnv, "", 1);
+ SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == false);
+ setenv(kSandboxDebuggingEnv, "t", 1);
+ SANDBOX_ASSERT(Trap::EnableUnsafeTrapsInSigSysHandler() == true);
+}
+
+intptr_t PrctlHandler(const struct arch_seccomp_data& args, void *) {
+ if (args.args[0] == PR_CAPBSET_DROP &&
+ static_cast<int>(args.args[1]) == -1) {
+ // prctl(PR_CAPBSET_DROP, -1) is never valid. The kernel will always
+ // return an error. But our handler allows this call.
+ return 0;
+ } else {
+ return Sandbox::ForwardSyscall(args);
+ }
+}
+
+ErrorCode PrctlPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ if (sysno == __NR_prctl) {
+ // Handle prctl() inside an UnsafeTrap()
+ return sandbox->UnsafeTrap(PrctlHandler, NULL);
+ } else if (Sandbox::IsValidSyscallNumber(sysno)) {
+ // Allow all other system calls.
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ } else {
+ return ErrorCode(ENOSYS);
+ }
+}
+
+BPF_TEST(SandboxBpf, ForwardSyscall, PrctlPolicy) {
+ // This call should never be allowed. But our policy will intercept it and
+ // let it pass successfully.
+ BPF_ASSERT(!prctl(PR_CAPBSET_DROP, -1, (void *)NULL, (void *)NULL,
+ (void *)NULL));
+
+ // Verify that the call will fail, if it makes it all the way to the kernel.
+ BPF_ASSERT(prctl(PR_CAPBSET_DROP, -2, (void *)NULL, (void *)NULL,
+ (void *)NULL) == -1);
+
+ // And verify that other uses of prctl() work just fine.
+ char name[17] = { };
+ BPF_ASSERT(!syscall(__NR_prctl, PR_GET_NAME, name, (void *)NULL,
+ (void *)NULL, (void *)NULL));
+ BPF_ASSERT(*name);
+
+ // Finally, verify that system calls other than prctl() are completely
+ // unaffected by our policy.
+ struct utsname uts = { };
+ BPF_ASSERT(!uname(&uts));
+ BPF_ASSERT(!strcmp(uts.sysname, "Linux"));
+}
+
+intptr_t AllowRedirectedSyscall(const struct arch_seccomp_data& args, void *) {
+ return Sandbox::ForwardSyscall(args);
+}
+
+ErrorCode RedirectAllSyscallsPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ // Some system calls must always be allowed, if our policy wants to make
+ // use of UnsafeTrap()
+ if (sysno == __NR_rt_sigprocmask ||
+ sysno == __NR_rt_sigreturn
+#if defined(__NR_sigprocmask)
+ || sysno == __NR_sigprocmask
+#endif
+#if defined(__NR_sigreturn)
+ || sysno == __NR_sigreturn
+#endif
+ ) {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ } else if (Sandbox::IsValidSyscallNumber(sysno)) {
+ return sandbox->UnsafeTrap(AllowRedirectedSyscall, aux);
+ } else {
+ return ErrorCode(ENOSYS);
+ }
+}
+
+int bus_handler_fd_ = -1;
+
+void SigBusHandler(int, siginfo_t *info, void *void_context) {
+ BPF_ASSERT(write(bus_handler_fd_, "\x55", 1) == 1);
+}
+
+BPF_TEST(SandboxBpf, SigBus, RedirectAllSyscallsPolicy) {
+ // We use the SIGBUS bit in the signal mask as a thread-local boolean
+ // value in the implementation of UnsafeTrap(). This is obviously a bit
+ // of a hack that could conceivably interfere with code that uses SIGBUS
+ // in more traditional ways. This test verifies that basic functionality
+ // of SIGBUS is not impacted, but it is certainly possibly to construe
+ // more complex uses of signals where our use of the SIGBUS mask is not
+ // 100% transparent. This is expected behavior.
+ int fds[2];
+ BPF_ASSERT(pipe(fds) == 0);
+ bus_handler_fd_ = fds[1];
+ struct sigaction sa = { };
+ sa.sa_sigaction = SigBusHandler;
+ sa.sa_flags = SA_SIGINFO;
+ BPF_ASSERT(sigaction(SIGBUS, &sa, NULL) == 0);
+ raise(SIGBUS);
+ char c = '\000';
+ BPF_ASSERT(read(fds[0], &c, 1) == 1);
+ BPF_ASSERT(close(fds[0]) == 0);
+ BPF_ASSERT(close(fds[1]) == 0);
+ BPF_ASSERT(c == 0x55);
+}
+
+BPF_TEST(SandboxBpf, SigMask, RedirectAllSyscallsPolicy) {
+ // Signal masks are potentially tricky to handle. For instance, if we
+ // ever tried to update them from inside a Trap() or UnsafeTrap() handler,
+ // the call to sigreturn() at the end of the signal handler would undo
+ // all of our efforts. So, it makes sense to test that sigprocmask()
+ // works, even if we have a policy in place that makes use of UnsafeTrap().
+ // In practice, this works because we force sigprocmask() to be handled
+ // entirely in the kernel.
+ sigset_t mask0, mask1, mask2;
+
+ // Call sigprocmask() to verify that SIGUSR2 wasn't blocked, if we didn't
+ // change the mask (it shouldn't have been, as it isn't blocked by default
+ // in POSIX).
+ //
+ // Use SIGUSR2 because Android seems to use SIGUSR1 for some purpose.
+ sigemptyset(&mask0);
+ BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, &mask1));
+ BPF_ASSERT(!sigismember(&mask1, SIGUSR2));
+
+ // Try again, and this time we verify that we can block it. This
+ // requires a second call to sigprocmask().
+ sigaddset(&mask0, SIGUSR2);
+ BPF_ASSERT(!sigprocmask(SIG_BLOCK, &mask0, NULL));
+ BPF_ASSERT(!sigprocmask(SIG_BLOCK, NULL, &mask2));
+ BPF_ASSERT( sigismember(&mask2, SIGUSR2));
+}
+
+BPF_TEST(SandboxBpf, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
+ // An UnsafeTrap() (or for that matter, a Trap()) has to report error
+ // conditions by returning an exit code in the range -1..-4096. This
+ // should happen automatically if using ForwardSyscall(). If the TrapFnc()
+ // uses some other method to make system calls, then it is responsible
+ // for computing the correct return code.
+ // This test verifies that ForwardSyscall() does the correct thing.
+
+ // The glibc system wrapper will ultimately set errno for us. So, from normal
+ // userspace, all of this should be completely transparent.
+ errno = 0;
+ BPF_ASSERT(close(-1) == -1);
+ BPF_ASSERT(errno == EBADF);
+
+ // Explicitly avoid the glibc wrapper. This is not normally the way anybody
+ // would make system calls, but it allows us to verify that we don't
+ // accidentally mess with errno, when we shouldn't.
+ errno = 0;
+ struct arch_seccomp_data args = { };
+ args.nr = __NR_close;
+ args.args[0] = -1;
+ BPF_ASSERT(Sandbox::ForwardSyscall(args) == -EBADF);
+ BPF_ASSERT(errno == 0);
+}
+
+// Test a trap handler that makes use of a broker process to open().
+
+class InitializedOpenBroker {
+ public:
+ InitializedOpenBroker() : initialized_(false) {
+ std::vector<std::string> allowed_files;
+ allowed_files.push_back("/proc/allowed");
+ allowed_files.push_back("/proc/cpuinfo");
+
+ broker_process_.reset(new BrokerProcess(allowed_files,
+ std::vector<std::string>()));
+ BPF_ASSERT(broker_process() != NULL);
+ BPF_ASSERT(broker_process_->Init(NULL));
+
+ initialized_ = true;
+ }
+ bool initialized() { return initialized_; }
+ class BrokerProcess* broker_process() { return broker_process_.get(); }
+ private:
+ bool initialized_;
+ scoped_ptr<class BrokerProcess> broker_process_;
+ DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
+};
+
+intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
+ void *aux) {
+ BPF_ASSERT(aux);
+ BrokerProcess* broker_process = static_cast<BrokerProcess*>(aux);
+ switch(args.nr) {
+ case __NR_access:
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+ case __NR_open:
+ return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+ case __NR_openat:
+ // We only call open() so if we arrive here, it's because glibc uses
+ // the openat() system call.
+ BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
+ return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+ default:
+ BPF_ASSERT(false);
+ return -ENOSYS;
+ }
+}
+
+ErrorCode DenyOpenPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ InitializedOpenBroker* iob = static_cast<InitializedOpenBroker*>(aux);
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ }
+
+ switch (sysno) {
+ case __NR_access:
+ case __NR_open:
+ case __NR_openat:
+ // We get a InitializedOpenBroker class, but our trap handler wants
+ // the BrokerProcess object.
+ return ErrorCode(sandbox->Trap(BrokerOpenTrapHandler,
+ iob->broker_process()));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+// We use a InitializedOpenBroker class, so that we can run unsandboxed
+// code in its constructor, which is the only way to do so in a BPF_TEST.
+BPF_TEST(SandboxBpf, UseOpenBroker, DenyOpenPolicy,
+ 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"
+ BPF_ASSERT(broker_process->Open("/proc/denied", O_RDONLY) == -EPERM);
+ BPF_ASSERT(broker_process->Access("/proc/denied", R_OK) == -EPERM);
+ BPF_ASSERT(broker_process->Open("/proc/allowed", O_RDONLY) == -ENOENT);
+ BPF_ASSERT(broker_process->Access("/proc/allowed", R_OK) == -ENOENT);
+
+ // Now use glibc's open() as an external library would.
+ BPF_ASSERT(open("/proc/denied", O_RDONLY) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ BPF_ASSERT(open("/proc/allowed", O_RDONLY) == -1);
+ BPF_ASSERT(errno == ENOENT);
+
+ // Also test glibc's openat(), some versions of libc use it transparently
+ // instead of open().
+ BPF_ASSERT(openat(AT_FDCWD, "/proc/denied", O_RDONLY) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ BPF_ASSERT(openat(AT_FDCWD, "/proc/allowed", O_RDONLY) == -1);
+ BPF_ASSERT(errno == ENOENT);
+
+ // And test glibc's access().
+ BPF_ASSERT(access("/proc/denied", R_OK) == -1);
+ BPF_ASSERT(errno == EPERM);
+
+ BPF_ASSERT(access("/proc/allowed", R_OK) == -1);
+ BPF_ASSERT(errno == ENOENT);
+
+ // This is also white listed and does exist.
+ int cpu_info_access = access("/proc/cpuinfo", R_OK);
+ BPF_ASSERT(cpu_info_access == 0);
+ int cpu_info_fd = open("/proc/cpuinfo", O_RDONLY);
+ BPF_ASSERT(cpu_info_fd >= 0);
+ char buf[1024];
+ BPF_ASSERT(read(cpu_info_fd, buf, sizeof(buf)) > 0);
+}
+
+// Simple test demonstrating how to use Sandbox::Cond()
+
+ErrorCode SimpleCondTestPolicy(Sandbox *sandbox, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ }
+
+ // 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) {
+ case __NR_open:
+ // Allow opening files for reading, but don't allow writing.
+ COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_be_all_zero_bits);
+ return sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ O_ACCMODE /* 0x3 */,
+ ErrorCode(EROFS),
+ ErrorCode(ErrorCode::ERR_ALLOWED));
+ case __NR_prctl:
+ // Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
+ // disallow everything else.
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ PR_SET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ PR_GET_DUMPABLE,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ ErrorCode(ENOMEM)));
+ default:
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, SimpleCondTest, SimpleCondTestPolicy) {
+ int fd;
+ BPF_ASSERT((fd = open("/proc/self/comm", O_RDWR)) == -1);
+ BPF_ASSERT(errno == EROFS);
+ BPF_ASSERT((fd = open("/proc/self/comm", O_RDONLY)) >= 0);
+ close(fd);
+
+ int ret;
+ BPF_ASSERT((ret = prctl(PR_GET_DUMPABLE)) >= 0);
+ BPF_ASSERT(prctl(PR_SET_DUMPABLE, 1-ret) == 0);
+ BPF_ASSERT(prctl(PR_GET_ENDIAN, &ret) == -1);
+ BPF_ASSERT(errno == ENOMEM);
+}
+
+// This test exercises the Sandbox::Cond() method by building a complex
+// tree of conditional equality operations. It then makes system calls and
+// verifies that they return the values that we expected from our BPF
+// program.
+class EqualityStressTest {
+ public:
+ EqualityStressTest() {
+ // We want a deterministic test
+ srand(0);
+
+ // Iterates over system call numbers and builds a random tree of
+ // equality tests.
+ // We are actually constructing a graph of ArgValue objects. This
+ // graph will later be used to a) compute our sandbox policy, and
+ // b) drive the code that verifies the output from the BPF program.
+ COMPILE_ASSERT(kNumTestCases < (int)(MAX_PUBLIC_SYSCALL-MIN_SYSCALL-10),
+ num_test_cases_must_be_significantly_smaller_than_num_system_calls);
+ for (int sysno = MIN_SYSCALL, end = kNumTestCases; sysno < end; ++sysno) {
+ if (IsReservedSyscall(sysno)) {
+ // Skip reserved system calls. This ensures that our test frame
+ // work isn't impacted by the fact that we are overriding
+ // a lot of different system calls.
+ ++end;
+ arg_values_.push_back(NULL);
+ } else {
+ arg_values_.push_back(RandomArgValue(rand() % kMaxArgs, 0,
+ rand() % kMaxArgs));
+ }
+ }
+ }
+
+ ~EqualityStressTest() {
+ for (std::vector<ArgValue *>::iterator iter = arg_values_.begin();
+ iter != arg_values_.end();
+ ++iter) {
+ DeleteArgValue(*iter);
+ }
+ }
+
+ ErrorCode Policy(Sandbox *sandbox, int sysno) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else if (sysno < 0 || sysno >= (int)arg_values_.size() ||
+ IsReservedSyscall(sysno)) {
+ // We only return ErrorCode values for the system calls that
+ // are part of our test data. Every other system call remains
+ // allowed.
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ } else {
+ // ToErrorCode() turns an ArgValue object into an ErrorCode that is
+ // suitable for use by a sandbox policy.
+ return ToErrorCode(sandbox, arg_values_[sysno]);
+ }
+ }
+
+ void VerifyFilter() {
+ // Iterate over all system calls. Skip the system calls that have
+ // previously been determined as being reserved.
+ for (int sysno = 0; sysno < (int)arg_values_.size(); ++sysno) {
+ if (!arg_values_[sysno]) {
+ // Skip reserved system calls.
+ continue;
+ }
+ // Verify that system calls return the values that we expect them to
+ // return. This involves passing different combinations of system call
+ // parameters in order to exercise all possible code paths through the
+ // BPF filter program.
+ // We arbitrarily start by setting all six system call arguments to
+ // zero. And we then recursive traverse our tree of ArgValues to
+ // determine the necessary combinations of parameters.
+ intptr_t args[6] = { };
+ Verify(sysno, args, *arg_values_[sysno]);
+ }
+ }
+
+ private:
+ struct ArgValue {
+ int argno; // Argument number to inspect.
+ int size; // Number of test cases (must be > 0).
+ struct Tests {
+ uint32_t k_value; // Value to compare syscall arg against.
+ int err; // If non-zero, errno value to return.
+ struct ArgValue *arg_value; // Otherwise, more args needs inspecting.
+ } *tests;
+ int err; // If none of the tests passed, this is what
+ struct ArgValue *arg_value; // we'll return (this is the "else" branch).
+ };
+
+ bool IsReservedSyscall(int sysno) {
+ // There are a handful of system calls that we should never use in our
+ // test cases. These system calls are needed to allow the test framework
+ // to run properly.
+ // If we wanted to write fully generic code, there are more system calls
+ // that could be listed here, and it is quite difficult to come up with a
+ // truly comprehensive list. After all, we are deliberately making system
+ // calls unavailable. In practice, we have a pretty good idea of the system
+ // calls that will be made by this particular test. So, this small list is
+ // sufficient. But if anybody copy'n'pasted this code for other uses, they
+ // would have to review that the list.
+ return sysno == __NR_read ||
+ sysno == __NR_write ||
+ sysno == __NR_exit ||
+ sysno == __NR_exit_group ||
+ sysno == __NR_restart_syscall;
+ }
+
+ ArgValue *RandomArgValue(int argno, int args_mask, int remaining_args) {
+ // Create a new ArgValue and fill it with random data. We use as bit mask
+ // to keep track of the system call parameters that have previously been
+ // set; this ensures that we won't accidentally define a contradictory
+ // set of equality tests.
+ struct ArgValue *arg_value = new ArgValue();
+ args_mask |= 1 << argno;
+ arg_value->argno = argno;
+
+ // Apply some restrictions on just how complex our tests can be.
+ // Otherwise, we end up with a BPF program that is too complicated for
+ // the kernel to load.
+ int fan_out = kMaxFanOut;
+ if (remaining_args > 3) {
+ fan_out = 1;
+ } else if (remaining_args > 2) {
+ fan_out = 2;
+ }
+
+ // Create a couple of different test cases with randomized values that
+ // we want to use when comparing system call parameter number "argno".
+ arg_value->size = rand() % fan_out + 1;
+ arg_value->tests = new ArgValue::Tests[arg_value->size];
+
+ uint32_t k_value = rand();
+ for (int n = 0; n < arg_value->size; ++n) {
+ // Ensure that we have unique values
+ k_value += rand() % (RAND_MAX/(kMaxFanOut+1)) + 1;
+
+ // There are two possible types of nodes. Either this is a leaf node;
+ // in that case, we have completed all the equality tests that we
+ // wanted to perform, and we can now compute a random "errno" value that
+ // we should return. Or this is part of a more complex boolean
+ // expression; in that case, we have to recursively add tests for some
+ // of system call parameters that we have not yet included in our
+ // tests.
+ arg_value->tests[n].k_value = k_value;
+ if (!remaining_args || (rand() & 1)) {
+ arg_value->tests[n].err = (rand() % 1000) + 1;
+ arg_value->tests[n].arg_value = NULL;
+ } else {
+ arg_value->tests[n].err = 0;
+ arg_value->tests[n].arg_value =
+ RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+ }
+ }
+ // Finally, we have to define what we should return if none of the
+ // previous equality tests pass. Again, we can either deal with a leaf
+ // node, or we can randomly add another couple of tests.
+ if (!remaining_args || (rand() & 1)) {
+ arg_value->err = (rand() % 1000) + 1;
+ arg_value->arg_value = NULL;
+ } else {
+ arg_value->err = 0;
+ arg_value->arg_value =
+ RandomArgValue(RandomArg(args_mask), args_mask, remaining_args - 1);
+ }
+ // We have now built a new (sub-)tree of ArgValues defining a set of
+ // boolean expressions for testing random system call arguments against
+ // random values. Return this tree to our caller.
+ return arg_value;
+ }
+
+ int RandomArg(int args_mask) {
+ // Compute a random system call parameter number.
+ int argno = rand() % kMaxArgs;
+
+ // Make sure that this same parameter number has not previously been
+ // used. Otherwise, we could end up with a test that is impossible to
+ // satisfy (e.g. args[0] == 1 && args[0] == 2).
+ while (args_mask & (1 << argno)) {
+ argno = (argno + 1) % kMaxArgs;
+ }
+ return argno;
+ }
+
+ void DeleteArgValue(ArgValue *arg_value) {
+ // Delete an ArgValue and all of its child nodes. This requires
+ // recursively descending into the tree.
+ if (arg_value) {
+ if (arg_value->size) {
+ for (int n = 0; n < arg_value->size; ++n) {
+ if (!arg_value->tests[n].err) {
+ DeleteArgValue(arg_value->tests[n].arg_value);
+ }
+ }
+ delete[] arg_value->tests;
+ }
+ if (!arg_value->err) {
+ DeleteArgValue(arg_value->arg_value);
+ }
+ delete arg_value;
+ }
+ }
+
+ ErrorCode ToErrorCode(Sandbox *sandbox, ArgValue *arg_value) {
+ // Compute the ErrorCode that should be returned, if none of our
+ // tests succeed (i.e. the system call parameter doesn't match any
+ // of the values in arg_value->tests[].k_value).
+ ErrorCode err;
+ if (arg_value->err) {
+ // If this was a leaf node, return the errno value that we expect to
+ // return from the BPF filter program.
+ err = ErrorCode(arg_value->err);
+ } else {
+ // If this wasn't a leaf node yet, recursively descend into the rest
+ // of the tree. This will end up adding a few more Sandbox::Cond()
+ // tests to our ErrorCode.
+ err = ToErrorCode(sandbox, arg_value->arg_value);
+ }
+
+ // Now, iterate over all the test cases that we want to compare against.
+ // This builds a chain of Sandbox::Cond() tests
+ // (aka "if ... elif ... elif ... elif ... fi")
+ for (int n = arg_value->size; n-- > 0; ) {
+ ErrorCode matched;
+ // Again, we distinguish between leaf nodes and subtrees.
+ if (arg_value->tests[n].err) {
+ matched = ErrorCode(arg_value->tests[n].err);
+ } else {
+ matched = ToErrorCode(sandbox, arg_value->tests[n].arg_value);
+ }
+ // For now, all of our tests are limited to 32bit.
+ // We have separate tests that check the behavior of 32bit vs. 64bit
+ // conditional expressions.
+ err = sandbox->Cond(arg_value->argno, ErrorCode::TP_32BIT,
+ ErrorCode::OP_EQUAL, arg_value->tests[n].k_value,
+ matched, err);
+ }
+ return err;
+ }
+
+ void Verify(int sysno, intptr_t *args, const ArgValue& arg_value) {
+ uint32_t mismatched = 0;
+ // Iterate over all the k_values in arg_value.tests[] and verify that
+ // we see the expected return values from system calls, when we pass
+ // the k_value as a parameter in a system call.
+ for (int n = arg_value.size; n-- > 0; ) {
+ mismatched += arg_value.tests[n].k_value;
+ args[arg_value.argno] = arg_value.tests[n].k_value;
+ if (arg_value.tests[n].err) {
+ VerifyErrno(sysno, args, arg_value.tests[n].err);
+ } else {
+ Verify(sysno, args, *arg_value.tests[n].arg_value);
+ }
+ }
+ // Find a k_value that doesn't match any of the k_values in
+ // arg_value.tests[]. In most cases, the current value of "mismatched"
+ // would fit this requirement. But on the off-chance that it happens
+ // to collide, we double-check.
+ try_again:
+ for (int n = arg_value.size; n-- > 0; ) {
+ if (mismatched == arg_value.tests[n].k_value) {
+ ++mismatched;
+ goto try_again;
+ }
+ }
+ // Now verify that we see the expected return value from system calls,
+ // if we pass a value that doesn't match any of the conditions (i.e. this
+ // is testing the "else" clause of the conditions).
+ args[arg_value.argno] = mismatched;
+ if (arg_value.err) {
+ VerifyErrno(sysno, args, arg_value.err);
+ } else {
+ Verify(sysno, args, *arg_value.arg_value);
+ }
+ // Reset args[arg_value.argno]. This is not technically needed, but it
+ // makes it easier to reason about the correctness of our tests.
+ args[arg_value.argno] = 0;
+ }
+
+ void VerifyErrno(int sysno, intptr_t *args, int err) {
+ // We installed BPF filters that return different errno values
+ // based on the system call number and the parameters that we decided
+ // to pass in. Verify that this condition holds true.
+ BPF_ASSERT(SandboxSyscall(sysno,
+ args[0], args[1], args[2],
+ args[3], args[4], args[5]) == -err);
+ }
+
+ // Vector of ArgValue trees. These trees define all the possible boolean
+ // expressions that we want to turn into a BPF filter program.
+ std::vector<ArgValue *> arg_values_;
+
+ // Don't increase these values. We are pushing the limits of the maximum
+ // BPF program that the kernel will allow us to load. If the values are
+ // increased too much, the test will start failing.
+ static const int kNumIterations = 3;
+ static const int kNumTestCases = 40;
+ static const int kMaxFanOut = 3;
+ static const int kMaxArgs = 6;
+};
+
+ErrorCode EqualityStressTestPolicy(Sandbox *sandbox, int sysno, void *aux) {
+ return reinterpret_cast<EqualityStressTest *>(aux)->Policy(sandbox, sysno);
+}
+
+BPF_TEST(SandboxBpf, EqualityTests, EqualityStressTestPolicy,
+ EqualityStressTest /* BPF_AUX */) {
+ BPF_AUX.VerifyFilter();
+}
+
+ErrorCode EqualityArgumentWidthPolicy(Sandbox *sandbox, int sysno, void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else if (sysno == __NR_uname) {
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ 0x55555555, ErrorCode(1), ErrorCode(2)),
+ // The BPF compiler and the BPF interpreter in the kernel are
+ // (mostly) agnostic of the host platform's word size. The compiler
+ // will happily generate code that tests a 64bit value, and the
+ // interpreter will happily perform this test.
+ // But unless there is a kernel bug, there is no way for us to pass
+ // in a 64bit quantity on a 32bit platform. The upper 32bits should
+ // always be zero. So, this test should always evaluate as false on
+ // 32bit systems.
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_EQUAL,
+ 0x55555555AAAAAAAAULL, ErrorCode(1), ErrorCode(2)));
+ } else {
+ 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);
+#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);
+#else
+ BPF_ASSERT(SandboxSyscall(__NR_uname, 1, 0x55555555) == -2);
+#endif
+}
+
+#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_DEATH_TEST(SandboxBpf, EqualityArgumentUnallowed64bit,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityArgumentWidthPolicy) {
+ SandboxSyscall(__NR_uname, 0, 0x5555555555555555ULL);
+}
+#endif
+
+ErrorCode EqualityWithNegativeArgumentsPolicy(Sandbox *sandbox, int sysno,
+ void *) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // 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 {
+ 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);
+}
+
+#if __SIZEOF_POINTER__ > 4
+BPF_DEATH_TEST(SandboxBpf, EqualityWithNegative64bitArguments,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityWithNegativeArgumentsPolicy) {
+ // When expecting a 32bit system call argument, we look at the MSB of the
+ // 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);
+}
+#endif
+
+ErrorCode AllBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
+ // Test the OP_HAS_ALL_BITS conditional test operator with a couple of
+ // different bitmasks. We try to find bitmasks that could conceivably
+ // touch corner cases.
+ // For all of these tests, we override the uname(). We can make use with
+ // a single system call number, as we use the first system call argument to
+ // select the different bit masks that we want to test against.
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else if (sysno == __NR_uname) {
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x100000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x300000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ALL_BITS,
+ 0x100000001ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Kill("Invalid test case number"))))))))))));
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+// Define a macro that performs tests using our test policy.
+// NOTE: Not all of the arguments in this macro are actually used!
+// They are here just to serve as documentation of the conditions
+// implemented in the test policy.
+// Most notably, "op" and "mask" are unused by the macro. If you want
+// to make changes to these values, you will have to edit the
+// test policy instead.
+#define BITMASK_TEST(testcase, arg, op, mask, expected_value) \
+ BPF_ASSERT(SandboxSyscall(__NR_uname, (testcase), (arg)) == (expected_value))
+
+// Our uname() system call returns ErrorCode(1) for success and
+// ErrorCode(0) for failure. SandboxSyscall() turns this into an
+// exit code of -1 or 0.
+#define EXPECT_FAILURE 0
+#define EXPECT_SUCCESS -1
+
+// A couple of our tests behave differently on 32bit and 64bit systems, as
+// there is no way for a 32bit system call to pass in a 64bit system call
+// argument "arg".
+// We expect these tests to succeed on 64bit systems, but to tail on 32bit
+// systems.
+#define EXPT64_SUCCESS \
+ (sizeof(void *) > 4 ? EXPECT_SUCCESS : EXPECT_FAILURE)
+
+BPF_TEST(SandboxBpf, AllBitTests, AllBitTestPolicy) {
+ // 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);
+ BITMASK_TEST( 0, 3, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, 0xFFFFFFFFU, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, -1LL, ALLBITS32, 0, EXPECT_SUCCESS);
+
+ // 32bit test: all of 0x1
+ BITMASK_TEST( 1, 0, ALLBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 1, ALLBITS32, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 1, 2, ALLBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 3, ALLBITS32, 0x1, EXPECT_SUCCESS);
+
+ // 32bit test: all of 0x3
+ BITMASK_TEST( 2, 0, ALLBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 1, ALLBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 2, ALLBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 3, ALLBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 7, ALLBITS32, 0x3, EXPECT_SUCCESS);
+
+ // 32bit test: all of 0x80000000
+ BITMASK_TEST( 3, 0, ALLBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x40000000U, ALLBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x80000000U, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, 0xC0000000U, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, -0x80000000LL, ALLBITS32, 0x80000000, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x0 (should always be true)
+ BITMASK_TEST( 4, 0, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 1, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 3, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 0xFFFFFFFFU, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 0x100000000LL, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, 0x300000000LL, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4,0x8000000000000000LL, ALLBITS64, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 4, -1LL, ALLBITS64, 0, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x1
+ BITMASK_TEST( 5, 0, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 1, ALLBITS64, 1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 2, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 3, ALLBITS64, 1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000000LL, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000001LL, ALLBITS64, 1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000002LL, ALLBITS64, 1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000003LL, ALLBITS64, 1, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x3
+ BITMASK_TEST( 6, 0, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 1, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 2, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 3, ALLBITS64, 3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 7, ALLBITS64, 3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000000LL, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000001LL, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000002LL, ALLBITS64, 3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000003LL, ALLBITS64, 3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000007LL, ALLBITS64, 3, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x80000000
+ BITMASK_TEST( 7, 0, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x40000000U, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x80000000U, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0xC0000000U, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x80000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x100000000LL, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x140000000LL, ALLBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x180000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x1C0000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x180000000LL, ALLBITS64, 0x80000000, EXPECT_SUCCESS);
+
+ // 64bit test: all of 0x100000000
+ BITMASK_TEST( 8, 0x000000000LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000000LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000000LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000000LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x000000001LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000001LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000001LL, ALLBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000001LL, ALLBITS64,0x100000000, EXPT64_SUCCESS);
+
+ // 64bit test: all of 0x300000000
+ BITMASK_TEST( 9, 0x000000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x200000000LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x300000000LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000000LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x000000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x200000001LL, ALLBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x300000001LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000001LL, ALLBITS64,0x300000000, EXPT64_SUCCESS);
+
+ // 64bit test: all of 0x100000001
+ BITMASK_TEST(10, 0x000000000LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, 0x000000001LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, 0x100000000LL, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, 0x100000001LL, ALLBITS64,0x100000001, EXPT64_SUCCESS);
+ BITMASK_TEST(10, 0xFFFFFFFFU, ALLBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST(10, -1L, ALLBITS64,0x100000001, EXPT64_SUCCESS);
+}
+
+ErrorCode AnyBitTestPolicy(Sandbox *sandbox, int sysno, void *) {
+ // Test the OP_HAS_ANY_BITS conditional test operator with a couple of
+ // different bitmasks. We try to find bitmasks that could conceivably
+ // touch corner cases.
+ // For all of these tests, we override the uname(). We can make use with
+ // a single system call number, as we use the first system call argument to
+ // select the different bit masks that we want to test against.
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else if (sysno == __NR_uname) {
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 0,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 1,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 2,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 3,
+ sandbox->Cond(1, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ // All the following tests don't really make much sense on 32bit
+ // systems. They will always evaluate as false.
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 4,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x0,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 5,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x1,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 6,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x3,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 7,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x80000000,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 8,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x100000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 9,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x300000000ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL, 10,
+ sandbox->Cond(1, ErrorCode::TP_64BIT, ErrorCode::OP_HAS_ANY_BITS,
+ 0x100000001ULL,
+ ErrorCode(1), ErrorCode(0)),
+
+ sandbox->Kill("Invalid test case number"))))))))))));
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+BPF_TEST(SandboxBpf, AnyBitTests, AnyBitTestPolicy) {
+ // 32bit test: any of 0x0 (should always be false)
+ BITMASK_TEST( 0, 0, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 1, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 3, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 0xFFFFFFFFU, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, -1LL, ANYBITS32, 0x0, EXPECT_FAILURE);
+
+ // 32bit test: any of 0x1
+ BITMASK_TEST( 1, 0, ANYBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 1, ANYBITS32, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 1, 2, ANYBITS32, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 1, 3, ANYBITS32, 0x1, EXPECT_SUCCESS);
+
+ // 32bit test: any of 0x3
+ BITMASK_TEST( 2, 0, ANYBITS32, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 2, 1, ANYBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 2, ANYBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 3, ANYBITS32, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 2, 7, ANYBITS32, 0x3, EXPECT_SUCCESS);
+
+ // 32bit test: any of 0x80000000
+ BITMASK_TEST( 3, 0, ANYBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x40000000U, ANYBITS32, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 3, 0x80000000U, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, 0xC0000000U, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 3, -0x80000000LL, ANYBITS32, 0x80000000, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x0 (should always be false)
+ BITMASK_TEST( 4, 0, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 1, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 3, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 0xFFFFFFFFU, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 0x100000000LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, 0x300000000LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4,0x8000000000000000LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 4, -1LL, ANYBITS64, 0x0, EXPECT_FAILURE);
+
+ // 64bit test: any of 0x1
+ BITMASK_TEST( 5, 0, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 1, ANYBITS64, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 2, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 3, ANYBITS64, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000001LL, ANYBITS64, 0x1, EXPECT_SUCCESS);
+ BITMASK_TEST( 5, 0x100000000LL, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000002LL, ANYBITS64, 0x1, EXPECT_FAILURE);
+ BITMASK_TEST( 5, 0x100000003LL, ANYBITS64, 0x1, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x3
+ BITMASK_TEST( 6, 0, ANYBITS64, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 1, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 2, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 3, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 7, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000000LL, ANYBITS64, 0x3, EXPECT_FAILURE);
+ BITMASK_TEST( 6, 0x100000001LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000002LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000003LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+ BITMASK_TEST( 6, 0x100000007LL, ANYBITS64, 0x3, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x80000000
+ BITMASK_TEST( 7, 0, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x40000000U, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x80000000U, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0xC0000000U, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x80000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x100000000LL, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x140000000LL, ANYBITS64, 0x80000000, EXPECT_FAILURE);
+ BITMASK_TEST( 7, 0x180000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, 0x1C0000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+ BITMASK_TEST( 7, -0x180000000LL, ANYBITS64, 0x80000000, EXPECT_SUCCESS);
+
+ // 64bit test: any of 0x100000000
+ BITMASK_TEST( 8, 0x000000000LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000000LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000000LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000000LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x000000001LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x100000001LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 8, 0x200000001LL, ANYBITS64,0x100000000, EXPECT_FAILURE);
+ BITMASK_TEST( 8, 0x300000001LL, ANYBITS64,0x100000000, EXPT64_SUCCESS);
+
+ // 64bit test: any of 0x300000000
+ BITMASK_TEST( 9, 0x000000000LL, ANYBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x200000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x300000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000000LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x000000001LL, ANYBITS64,0x300000000, EXPECT_FAILURE);
+ BITMASK_TEST( 9, 0x100000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x200000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x300000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+ BITMASK_TEST( 9, 0x700000001LL, ANYBITS64,0x300000000, EXPT64_SUCCESS);
+
+ // 64bit test: any of 0x100000001
+ BITMASK_TEST( 10, 0x000000000LL, ANYBITS64,0x100000001, EXPECT_FAILURE);
+ BITMASK_TEST( 10, 0x000000001LL, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+ BITMASK_TEST( 10, 0x100000000LL, ANYBITS64,0x100000001, EXPT64_SUCCESS);
+ BITMASK_TEST( 10, 0x100000001LL, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+ BITMASK_TEST( 10, 0xFFFFFFFFU, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+ BITMASK_TEST( 10, -1L, ANYBITS64,0x100000001, EXPECT_SUCCESS);
+}
+
+intptr_t PthreadTrapHandler(const struct arch_seccomp_data& args, void *aux) {
+ if (args.args[0] != (CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD)) {
+ // We expect to get called for an attempt to fork(). No need to log that
+ // call. But if we ever get called for anything else, we want to verbosely
+ // print as much information as possible.
+ const char *msg = (const char *)aux;
+ printf("Clone() was called with unexpected arguments\n"
+ " nr: %d\n"
+ " 1: 0x%llX\n"
+ " 2: 0x%llX\n"
+ " 3: 0x%llX\n"
+ " 4: 0x%llX\n"
+ " 5: 0x%llX\n"
+ " 6: 0x%llX\n"
+ "%s\n",
+ args.nr,
+ (long long)args.args[0], (long long)args.args[1],
+ (long long)args.args[2], (long long)args.args[3],
+ (long long)args.args[4], (long long)args.args[5],
+ msg);
+ }
+ return -EPERM;
+}
+
+ErrorCode PthreadPolicyEquality(Sandbox *sandbox, int sysno, void *aux) {
+ // This policy allows creating threads with pthread_create(). But it
+ // doesn't allow any other uses of clone(). Most notably, it does not
+ // allow callers to implement fork() or vfork() by passing suitable flags
+ // to the clone() system call.
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else 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
+ // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED.
+ // The following policy is very strict. It only allows the exact masks
+ // that we have seen in known implementations. It is probably somewhat
+ // stricter than what we would want to do.
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+ CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
+ CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_EQUAL,
+ CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+ CLONE_THREAD|CLONE_SYSVSEM|CLONE_DETACHED,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Trap(PthreadTrapHandler, aux)));
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+ErrorCode PthreadPolicyBitMask(Sandbox *sandbox, int sysno, void *aux) {
+ // This policy allows creating threads with pthread_create(). But it
+ // doesn't allow any other uses of clone(). Most notably, it does not
+ // allow callers to implement fork() or vfork() by passing suitable flags
+ // to the clone() system call.
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ // FIXME: we should really not have to do that in a trivial policy
+ return ErrorCode(ENOSYS);
+ } else 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
+ // do any of this, but it sets the obsolete (and no-op) CLONE_DETACHED.
+ // The following policy allows for either combination of flags, but it
+ // is generally a little more conservative than strictly necessary. We
+ // err on the side of rather safe than sorry.
+ // Very noticeably though, we disallow fork() (which is often just a
+ // wrapper around clone()).
+ return sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ ~uint32(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+ CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|
+ CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID|
+ CLONE_DETACHED),
+ sandbox->Trap(PthreadTrapHandler,
+ "Unexpected CLONE_XXX flag found"),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|
+ CLONE_THREAD|CLONE_SYSVSEM,
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ALL_BITS,
+ CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
+ ErrorCode(ErrorCode::ERR_ALLOWED),
+ sandbox->Cond(0, ErrorCode::TP_32BIT, ErrorCode::OP_HAS_ANY_BITS,
+ CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID,
+ sandbox->Trap(PthreadTrapHandler,
+ "Must set either all or none of the TLS"
+ " and futex bits in call to clone()"),
+ ErrorCode(ErrorCode::ERR_ALLOWED))),
+ sandbox->Trap(PthreadTrapHandler,
+ "Missing mandatory CLONE_XXX flags "
+ "when creating new thread")));
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+static void *ThreadFnc(void *arg) {
+ ++*reinterpret_cast<int *>(arg);
+ SandboxSyscall(__NR_futex, arg, FUTEX_WAKE, 1, 0, 0, 0);
+ return NULL;
+}
+
+static void PthreadTest() {
+ // Attempt to start a joinable thread. This should succeed.
+ pthread_t thread;
+ int thread_ran = 0;
+ BPF_ASSERT(!pthread_create(&thread, NULL, ThreadFnc, &thread_ran));
+ BPF_ASSERT(!pthread_join(thread, NULL));
+ BPF_ASSERT(thread_ran);
+
+ // Attempt to start a detached thread. This should succeed.
+ thread_ran = 0;
+ pthread_attr_t attr;
+ BPF_ASSERT(!pthread_attr_init(&attr));
+ BPF_ASSERT(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
+ BPF_ASSERT(!pthread_create(&thread, &attr, ThreadFnc, &thread_ran));
+ BPF_ASSERT(!pthread_attr_destroy(&attr));
+ while (SandboxSyscall(__NR_futex, &thread_ran, FUTEX_WAIT,
+ 0, 0, 0, 0) == -EINTR) {
+ }
+ BPF_ASSERT(thread_ran);
+
+ // Attempt to fork() a process using clone(). This should fail. We use the
+ // same flags that glibc uses when calling fork(). But we don't actually
+ // try calling the fork() implementation in the C run-time library, as
+ // 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_TEST(SandboxBpf, PthreadEquality, PthreadPolicyEquality) {
+ PthreadTest();
+}
+
+BPF_TEST(SandboxBpf, PthreadBitMask, PthreadPolicyBitMask) {
+ PthreadTest();
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.cc b/chromium/sandbox/linux/seccomp-bpf/syscall.cc
new file mode 100644
index 00000000000..8b09a684866
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <asm/unistd.h>
+#include <errno.h>
+
+#include "sandbox/linux/seccomp-bpf/port.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+
+namespace playground2 {
+
+ 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"
+#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"
+#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"
+#if defined(__thumb__)
+ ".thumb_func\n"
+#else
+ ".arm\n"
+#endif
+ "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"
+#else
+ "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.
+#if defined(__thumb__)
+ "2:pop {r7, pc}\n"
+ ".cfi_endproc\n"
+#else
+ "2:ldmfd sp!, {fp, pc}\n"
+#endif
+ ".fnend\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#endif
+ ); // asm
+
+intptr_t SandboxSyscall(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
+ // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect
+ // 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),
+ pointer_types_and_intptr_must_be_exactly_the_same_size);
+
+ 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.
+#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)
+ : "esp", "memory", "ecx", "edx");
+#elif defined(__x86_64__)
+ intptr_t ret = nr;
+ {
+ 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)
+ : "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;
+ asm volatile(
+ "bl SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "lr", "memory", "r1", "r2", "r3", "r4", "r5"
+#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
+ );
+ ret = inout;
+ }
+#else
+ errno = ENOSYS;
+ intptr_t ret = -1;
+#endif
+ return ret;
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.h b/chromium/sandbox/linux/seccomp-bpf/syscall.h
new file mode 100644
index 00000000000..39b1bcaa4a5
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+
+#include <stdint.h>
+
+namespace playground2 {
+
+// 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
+
+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);
+}
+
+#endif // Pre-C++11
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc
new file mode 100644
index 00000000000..4ea979a63f3
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/linux_seccomp.h"
+#include "sandbox/linux/seccomp-bpf/port.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+
+namespace playground2 {
+
+uint32_t SyscallIterator::Next() {
+ if (done_) {
+ return num_;
+ }
+
+ uint32_t val;
+ do {
+ // |num_| has been initialized to 0, which we assume is also MIN_SYSCALL.
+ // This true for supported architectures (Intel and ARM EABI).
+ COMPILE_ASSERT(MIN_SYSCALL == 0u,
+ min_syscall_should_always_be_zero);
+ val = num_;
+
+ // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
+ // on Intel architectures, but leaves room for private syscalls on ARM.
+ if (num_ <= MAX_PUBLIC_SYSCALL) {
+ if (invalid_only_ && num_ < MAX_PUBLIC_SYSCALL) {
+ num_ = MAX_PUBLIC_SYSCALL;
+ } else {
+ ++num_;
+ }
+#if defined(__arm__)
+ // ARM EABI includes "ARM private" system calls starting at
+ // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
+ // MIN_GHOST_SYSCALL.
+ } else if (num_ < MIN_PRIVATE_SYSCALL - 1) {
+ num_ = MIN_PRIVATE_SYSCALL - 1;
+ } else if (num_ <= MAX_PRIVATE_SYSCALL) {
+ if (invalid_only_ && num_ < MAX_PRIVATE_SYSCALL) {
+ num_ = MAX_PRIVATE_SYSCALL;
+ } else {
+ ++num_;
+ }
+ } else if (num_ < MIN_GHOST_SYSCALL - 1) {
+ num_ = MIN_GHOST_SYSCALL - 1;
+ } else if (num_ <= MAX_SYSCALL) {
+ if (invalid_only_ && num_ < MAX_SYSCALL) {
+ num_ = MAX_SYSCALL;
+ } else {
+ ++num_;
+ }
+#endif
+ // BPF programs only ever operate on unsigned quantities. So, that's how
+ // we iterate; we return values from 0..0xFFFFFFFFu. But there are places,
+ // where the kernel might interpret system call numbers as signed
+ // quantities, so the boundaries between signed and unsigned values are
+ // potential problem cases. We want to explicitly return these values from
+ // our iterator.
+ } else if (num_ < 0x7FFFFFFFu) {
+ num_ = 0x7FFFFFFFu;
+ } else if (num_ < 0x80000000u) {
+ num_ = 0x80000000u;
+ } else if (num_ < 0xFFFFFFFFu) {
+ num_ = 0xFFFFFFFFu;
+ }
+ } while (invalid_only_ && IsValid(val));
+
+ done_ |= val == 0xFFFFFFFFu;
+ return val;
+}
+
+bool SyscallIterator::IsValid(uint32_t num) {
+ uint32_t min_syscall = MIN_SYSCALL;
+ if (num >= min_syscall && num <= MAX_PUBLIC_SYSCALL) {
+ return true;
+ }
+ if (IsArmPrivate(num)) {
+ return true;
+ }
+ return false;
+}
+
+#if defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+bool SyscallIterator::IsArmPrivate(uint32_t num) {
+ return (num >= MIN_PRIVATE_SYSCALL && num <= MAX_PRIVATE_SYSCALL) ||
+ (num >= MIN_GHOST_SYSCALL && num <= MAX_SYSCALL);
+}
+#else
+bool SyscallIterator::IsArmPrivate(uint32_t) {
+ return false;
+}
+#endif
+
+} // namespace
+
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h
new file mode 100644
index 00000000000..e17593d844f
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+
+#include <stdint.h>
+
+namespace playground2 {
+
+// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
+// iterator is aware of how system calls look like and will skip quickly
+// over ranges that can't contain system calls. It iterates more slowly
+// whenever it reaches a range that is potentially problematic, returning
+// the last invalid value before a valid range of system calls, and the
+// first invalid value after a valid range of syscalls. It iterates over
+// individual values whenever it is in the normal range for system calls
+// (typically MIN_SYSCALL..MAX_SYSCALL).
+// If |invalid_only| is true, this iterator will only return invalid
+// syscall numbers, but will still skip quickly over invalid ranges,
+// returning the first invalid value in the range and then skipping
+// to the last invalid value in the range.
+//
+// Example usage:
+// for (SyscallIterator iter(false); !iter.Done(); ) {
+// uint32_t sysnum = iter.Next();
+// // Do something with sysnum.
+// }
+//
+// TODO(markus): Make this a classic C++ iterator.
+class SyscallIterator {
+ public:
+ explicit SyscallIterator(bool invalid_only)
+ : invalid_only_(invalid_only),
+ done_(false),
+ num_(0) {}
+
+ bool Done() const { return done_; }
+ uint32_t Next();
+ static bool IsValid(uint32_t num);
+
+ private:
+ static bool IsArmPrivate(uint32_t num);
+
+ bool invalid_only_;
+ bool done_;
+ uint32_t num_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallIterator);
+};
+
+} // namespace playground2
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__
+
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
new file mode 100644
index 00000000000..26f11ce5e34
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+using namespace playground2;
+
+namespace {
+
+SANDBOX_TEST(SyscallIterator, Monotonous) {
+ for (int i = 0; i < 2; ++i) {
+ bool invalid_only = !i; // Testing both |invalid_only| cases.
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+
+ if (!invalid_only) {
+ // The iterator should start at 0.
+ SANDBOX_ASSERT(next == 0);
+ }
+ for (uint32_t last = next; !iter.Done(); last = next) {
+ next = iter.Next();
+ SANDBOX_ASSERT(last < next);
+ }
+ // The iterator should always return 0xFFFFFFFFu as the last value.
+ SANDBOX_ASSERT(next == 0xFFFFFFFFu);
+ }
+}
+
+SANDBOX_TEST(SyscallIterator, PublicSyscallRange) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+
+ // The iterator should cover the public syscall range
+ // MIN_SYSCALL..MAX_PUBLIC_SYSCALL, without skipping syscalls.
+ // We're assuming MIN_SYSCALL == 0 for all architectures,
+ // this is currently valid for Intel and ARM EABI.
+ SANDBOX_ASSERT(MIN_SYSCALL == 0);
+ SANDBOX_ASSERT(next == MIN_SYSCALL);
+ for (uint32_t last = next; next < MAX_PUBLIC_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
+}
+
+#if defined(__arm__)
+SANDBOX_TEST(SyscallIterator, ARMPrivateSyscallRange) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+ while (next < MIN_PRIVATE_SYSCALL - 1) {
+ next = iter.Next();
+ }
+ // The iterator should cover the ARM private syscall range
+ // without skipping syscalls.
+ SANDBOX_ASSERT(next == MIN_PRIVATE_SYSCALL - 1);
+ for (uint32_t last = next; next < MAX_PRIVATE_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_PRIVATE_SYSCALL + 1);
+}
+
+SANDBOX_TEST(SyscallIterator, ARMHiddenSyscallRange) {
+ SyscallIterator iter(false);
+ uint32_t next = iter.Next();
+ while (next < MIN_GHOST_SYSCALL - 1) {
+ next = iter.Next();
+ }
+ // The iterator should cover the ARM hidden syscall range
+ // without skipping syscalls.
+ SANDBOX_ASSERT(next == MIN_GHOST_SYSCALL - 1);
+ for (uint32_t last = next; next < MAX_SYSCALL + 1; last = next) {
+ SANDBOX_ASSERT((next = iter.Next()) == last + 1);
+ }
+ SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
+}
+#endif
+
+SANDBOX_TEST(SyscallIterator, Invalid) {
+ for (int i = 0; i < 2; ++i) {
+ bool invalid_only = !i; // Testing both |invalid_only| cases.
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+
+ while (next < MAX_SYSCALL + 1) {
+ next = iter.Next();
+ }
+
+ SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
+ while (next < 0x7FFFFFFFu) {
+ next = iter.Next();
+ }
+
+ // The iterator should return the signed/unsigned corner cases.
+ SANDBOX_ASSERT(next == 0x7FFFFFFFu);
+ next = iter.Next();
+ SANDBOX_ASSERT(next == 0x80000000u);
+ SANDBOX_ASSERT(!iter.Done());
+ next = iter.Next();
+ SANDBOX_ASSERT(iter.Done());
+ SANDBOX_ASSERT(next == 0xFFFFFFFFu);
+ }
+}
+
+SANDBOX_TEST(SyscallIterator, InvalidOnly) {
+ bool invalid_only = true;
+ SyscallIterator iter(invalid_only);
+ uint32_t next = iter.Next();
+ // We're assuming MIN_SYSCALL == 0 for all architectures,
+ // this is currently valid for Intel and ARM EABI.
+ // First invalid syscall should then be |MAX_PUBLIC_SYSCALL + 1|.
+ SANDBOX_ASSERT(MIN_SYSCALL == 0);
+ SANDBOX_ASSERT(next == MAX_PUBLIC_SYSCALL + 1);
+
+#if defined(__arm__)
+ next = iter.Next();
+ // The iterator should skip until the last invalid syscall in this range.
+ SANDBOX_ASSERT(next == MIN_PRIVATE_SYSCALL - 1);
+ while (next <= MAX_PRIVATE_SYSCALL) {
+ next = iter.Next();
+ }
+
+ next = iter.Next();
+ // The iterator should skip until the last invalid syscall in this range.
+ SANDBOX_ASSERT(next == MIN_GHOST_SYSCALL - 1);
+ while (next <= MAX_SYSCALL) {
+ next = iter.Next();
+ }
+ SANDBOX_ASSERT(next == MAX_SYSCALL + 1);
+#endif
+}
+
+} // namespace
+
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
new file mode 100644
index 00000000000..136deb615cb
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <asm/unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace playground2;
+
+namespace {
+
+// Different platforms use different symbols for the six-argument version
+// of the mmap() system call. Test for the correct symbol at compile time.
+#ifdef __NR_mmap2
+const int kMMapNr = __NR_mmap2;
+#else
+const int kMMapNr = __NR_mmap;
+#endif
+
+TEST(Syscall, WellKnownEntryPoint) {
+ // Test that SandboxSyscall(-1) is handled specially. Don't do this on ARM,
+ // where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+ // are still testing ARM code in the next set of tests.
+#if !defined(__arm__)
+ EXPECT_NE(SandboxSyscall(-1), syscall(-1));
+#endif
+
+ // If possible, test that SandboxSyscall(-1) returns the address right after
+ // a kernel entry point.
+#if defined(__i386__)
+ EXPECT_EQ(0x80CDu, ((uint16_t *)SandboxSyscall(-1))[-1]); // INT 0x80
+#elif defined(__x86_64__)
+ EXPECT_EQ(0x050Fu, ((uint16_t *)SandboxSyscall(-1))[-1]); // SYSCALL
+#elif defined(__arm__)
+#if defined(__thumb__)
+ EXPECT_EQ(0xDF00u, ((uint16_t *)SandboxSyscall(-1))[-1]); // SWI 0
+#else
+ EXPECT_EQ(0xEF000000u, ((uint32_t *)SandboxSyscall(-1))[-1]); // SVC 0
+#endif
+#else
+ #warning Incomplete test case; need port for target platform
+#endif
+}
+
+TEST(Syscall, TrivialSyscallNoArgs) {
+ // Test that we can do basic system calls
+ EXPECT_EQ(SandboxSyscall(__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 = HANDLE_EINTR(SandboxSyscall(__NR_close, new_fd));
+ ASSERT_EQ(close_return_value, 0);
+}
+
+// SIGSYS trap handler that will be called on __NR_uname.
+intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void *aux) {
+ // |aux| is a pointer to our BPF_AUX.
+ std::vector<uint64_t>* const seen_syscall_args =
+ static_cast<std::vector<uint64_t>*>(aux);
+ BPF_ASSERT(arraysize(args.args) == 6);
+ seen_syscall_args->assign(args.args, args.args + arraysize(args.args));
+ return -ENOMEM;
+}
+
+ErrorCode CopyAllArgsOnUnamePolicy(Sandbox *sandbox, int sysno, void *aux) {
+ if (!Sandbox::IsValidSyscallNumber(sysno)) {
+ return ErrorCode(ENOSYS);
+ }
+ if (sysno == __NR_uname) {
+ return sandbox->Trap(CopySyscallArgsToAux, aux);
+ } else {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+}
+
+// We are testing SandboxSyscall() by making use of a BPF filter that allows us
+// to inspect the system call arguments that the kernel saw.
+BPF_TEST(Syscall, SyntheticSixArgs, CopyAllArgsOnUnamePolicy,
+ 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
+ // implementation details of kernel BPF filters and we will need to document
+ // the expected behavior very clearly.
+ int syscall_args[6];
+ for (size_t i = 0; i < arraysize(syscall_args); ++i) {
+ syscall_args[i] = kExpectedValue + i;
+ }
+
+ // 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);
+
+ // We expect the trap handler to have copied the 6 arguments.
+ 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]));
+}
+
+TEST(Syscall, ComplexSyscallSixArgs) {
+ int fd;
+ ASSERT_LE(0, fd = SandboxSyscall(__NR_open, "/dev/null", O_RDWR, 0L));
+
+ // Use mmap() to allocate some read-only memory
+ char *addr0;
+ ASSERT_NE((char *)NULL,
+ addr0 = reinterpret_cast<char *>(
+ SandboxSyscall(kMMapNr, (void *)NULL, 4096, PROT_READ,
+ MAP_PRIVATE|MAP_ANONYMOUS, fd, 0L)));
+
+ // 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)));
+ ++*addr1; // This should not seg fault
+
+ // Clean up
+ EXPECT_EQ(0, SandboxSyscall(__NR_munmap, addr1, 4096L));
+ EXPECT_EQ(0, HANDLE_EINTR(SandboxSyscall(__NR_close, fd)));
+
+ // Check that the offset argument (i.e. the sixth argument) is processed
+ // correctly.
+ ASSERT_GE(fd = SandboxSyscall(__NR_open, "/proc/self/exe", O_RDONLY, 0L), 0);
+ char *addr2, *addr3;
+ ASSERT_NE((char *)NULL,
+ addr2 = reinterpret_cast<char *>(
+ SandboxSyscall(kMMapNr, (void *)NULL, 8192L, PROT_READ,
+ MAP_PRIVATE, fd, 0L)));
+ ASSERT_NE((char *)NULL,
+ addr3 = reinterpret_cast<char *>(
+ SandboxSyscall(kMMapNr, (void *)NULL, 4096L, PROT_READ,
+ MAP_PRIVATE, fd,
+#if defined(__NR_mmap2)
+ 1L
+#else
+ 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(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, HANDLE_EINTR(SandboxSyscall(__NR_close, fd)));
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.cc b/chromium/sandbox/linux/seccomp-bpf/trap.cc
new file mode 100644
index 00000000000..78a78ee5d09
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/trap.cc
@@ -0,0 +1,344 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#ifndef SECCOMP_BPF_STANDALONE
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+#include "sandbox/linux/seccomp-bpf/codegen.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+// Android's signal.h doesn't define ucontext etc.
+#if defined(OS_ANDROID)
+#include "sandbox/linux/services/android_ucontext.h"
+#endif
+
+#include <limits>
+
+
+namespace {
+
+const int kCapacityIncrement = 20;
+
+// Unsafe traps can only be turned on, if the user explicitly allowed them
+// by setting the CHROME_SANDBOX_DEBUGGING environment variable.
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// We need to tell whether we are performing a "normal" callback, or
+// whether we were called recursively from within a UnsafeTrap() callback.
+// This is a little tricky to do, because we need to somehow get access to
+// per-thread data from within a signal context. Normal TLS storage is not
+// safely accessible at this time. We could roll our own, but that involves
+// a lot of complexity. Instead, we co-opt one bit in the signal mask.
+// If BUS is blocked, we assume that we have been called recursively.
+// There is a possibility for collision with other code that needs to do
+// this, but in practice the risks are low.
+// If SIGBUS turns out to be a problem, we could instead co-opt one of the
+// realtime signals. There are plenty of them. Unfortunately, there is no
+// way to mark a signal as allocated. So, the potential for collision is
+// possibly even worse.
+bool GetIsInSigHandler(const ucontext_t *ctx) {
+ // Note: on Android, sigismember does not take a pointer to const.
+ return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), SIGBUS);
+}
+
+void SetIsInSigHandler() {
+ sigset_t mask;
+ if (sigemptyset(&mask) ||
+ sigaddset(&mask, SIGBUS) ||
+ sigprocmask(SIG_BLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to block SIGBUS");
+ }
+}
+
+} // namespace
+
+namespace playground2 {
+
+Trap::Trap()
+ : trap_array_(NULL),
+ trap_array_size_(0),
+ trap_array_capacity_(0),
+ has_unsafe_traps_(false) {
+ // Set new SIGSYS handler
+ struct sigaction sa = { };
+ sa.sa_sigaction = SigSysAction;
+ sa.sa_flags = SA_SIGINFO | SA_NODEFER;
+ if (sigaction(SIGSYS, &sa, NULL) < 0) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+
+ // Unmask SIGSYS
+ sigset_t mask;
+ if (sigemptyset(&mask) ||
+ sigaddset(&mask, SIGSYS) ||
+ sigprocmask(SIG_UNBLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+}
+
+Trap *Trap::GetInstance() {
+ // Note: This class is not thread safe. It is the caller's responsibility
+ // to avoid race conditions. Normally, this is a non-issue as the sandbox
+ // can only be initialized if there are no other threads present.
+ // Also, this is not a normal singleton. Once created, the global trap
+ // object must never be destroyed again.
+ if (!global_trap_) {
+ global_trap_ = new Trap();
+ if (!global_trap_) {
+ SANDBOX_DIE("Failed to allocate global trap handler");
+ }
+ }
+ return global_trap_;
+}
+
+void Trap::SigSysAction(int nr, siginfo_t *info, void *void_context) {
+ if (!global_trap_) {
+ SANDBOX_DIE("This can't happen. Found no global singleton instance "
+ "for Trap() handling.");
+ }
+ global_trap_->SigSys(nr, info, void_context);
+}
+
+void Trap::SigSys(int nr, siginfo_t *info, void *void_context) {
+ // Various sanity checks to make sure we actually received a signal
+ // triggered by a BPF filter. If something else triggered SIGSYS
+ // (e.g. kill()), there is really nothing we can do with this signal.
+ if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context ||
+ info->si_errno <= 0 ||
+ static_cast<size_t>(info->si_errno) > trap_array_size_) {
+ // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
+ // safe and can lead to bugs. We should eventually implement a different
+ // logging and reporting mechanism that is safe to be called from
+ // the sigSys() handler.
+ // TODO: If we feel confident that our code otherwise works correctly, we
+ // could actually make an argument that spurious SIGSYS should
+ // just get silently ignored. TBD
+ SANDBOX_DIE("Unexpected SIGSYS received.");
+ }
+
+ // Signal handlers should always preserve "errno". Otherwise, we could
+ // trigger really subtle bugs.
+ const int old_errno = errno;
+
+ // Obtain the signal context. This, most notably, gives us access to
+ // all CPU registers at the time of the signal.
+ ucontext_t *ctx = reinterpret_cast<ucontext_t *>(void_context);
+
+ // Obtain the siginfo information that is specific to SIGSYS. Unfortunately,
+ // most versions of glibc don't include this information in siginfo_t. So,
+ // we need to explicitly copy it into a arch_sigsys structure.
+ struct arch_sigsys sigsys;
+ memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
+
+ // Some more sanity checks.
+ if (sigsys.ip != reinterpret_cast<void *>(SECCOMP_IP(ctx)) ||
+ sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) ||
+ sigsys.arch != SECCOMP_ARCH) {
+ SANDBOX_DIE("Sanity checks are failing after receiving SIGSYS.");
+ }
+
+ intptr_t rc;
+ if (has_unsafe_traps_ && GetIsInSigHandler(ctx)) {
+ errno = old_errno;
+ if (sigsys.nr == __NR_clone) {
+ 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));
+ } else {
+ const ErrorCode& err = trap_array_[info->si_errno - 1];
+ if (!err.safe_) {
+ SetIsInSigHandler();
+ }
+
+ // Copy the seccomp-specific data into a arch_seccomp_data structure. This
+ // is what we are showing to TrapFnc callbacks that the system call
+ // evaluator registered with the sandbox.
+ struct arch_seccomp_data data = {
+ sigsys.nr,
+ SECCOMP_ARCH,
+ reinterpret_cast<uint64_t>(sigsys.ip),
+ {
+ static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM6(ctx))
+ }
+ };
+
+ // Now call the TrapFnc callback associated with this particular instance
+ // of SECCOMP_RET_TRAP.
+ rc = err.fnc_(data, err.aux_);
+ }
+
+ // Update the CPU register that stores the return code of the system call
+ // that we just handled, and restore "errno" to the value that it had
+ // before entering the signal handler.
+ SECCOMP_RESULT(ctx) = static_cast<greg_t>(rc);
+ errno = old_errno;
+
+ return;
+}
+
+bool Trap::TrapKey::operator<(const TrapKey& o) const {
+ if (fnc != o.fnc) {
+ return fnc < o.fnc;
+ } else if (aux != o.aux) {
+ return aux < o.aux;
+ } else {
+ return safe < o.safe;
+ }
+}
+
+ErrorCode Trap::MakeTrap(TrapFnc fnc, const void *aux, bool safe) {
+ return GetInstance()->MakeTrapImpl(fnc, aux, safe);
+}
+
+ErrorCode Trap::MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe) {
+ if (!safe && !SandboxDebuggingAllowedByUser()) {
+ // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
+ // we never return an ErrorCode that is marked as "unsafe". This also
+ // 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)).
+
+ // 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
+ // to understand. Removing the SANDBOX_DIE() allows callers to easyly check
+ // whether unsafe traps are supported (by checking whether the returned
+ // ErrorCode is ET_INVALID).
+ SANDBOX_DIE("Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
+ "is enabled");
+
+ return ErrorCode();
+ }
+
+ // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
+ // of a SECCOMP_RET_TRAP.
+ TrapKey key(fnc, aux, safe);
+ TrapIds::const_iterator iter = trap_ids_.find(key);
+
+ // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
+ // us to associate trap with the appropriate handler. The kernel allows us
+ // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
+ // avoid 0, as it could be confused for a trap without any specific id.
+ // The nice thing about sequentially numbered identifiers is that we can also
+ // trivially look them up from our signal handler without making any system
+ // calls that might be async-signal-unsafe.
+ // In order to do so, we store all of our traps in a C-style trap_array_.
+ uint16_t id;
+ if (iter != trap_ids_.end()) {
+ // We have seen this pair before. Return the same id that we assigned
+ // earlier.
+ id = iter->second;
+ } else {
+ // This is a new pair. Remember it and assign a new id.
+ if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
+ trap_array_size_ >= std::numeric_limits<typeof(id)>::max()) {
+ // In practice, this is pretty much impossible to trigger, as there
+ // are other kernel limitations that restrict overall BPF program sizes.
+ SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
+ }
+ id = trap_array_size_ + 1;
+
+ // Our callers ensure that there are no other threads accessing trap_array_
+ // concurrently (typically this is done by ensuring that we are single-
+ // threaded while the sandbox is being set up). But we nonetheless are
+ // modifying a life data structure that could be accessed any time a
+ // system call is made; as system calls could be triggering SIGSYS.
+ // So, we have to be extra careful that we update trap_array_ atomically.
+ // In particular, this means we shouldn't be using realloc() to resize it.
+ // Instead, we allocate a new array, copy the values, and then switch the
+ // pointer. We only really care about the pointer being updated atomically
+ // and the data that is pointed to being valid, as these are the only
+ // values accessed from the signal handler. It is OK if trap_array_size_
+ // is inconsistent with the pointer, as it is monotonously increasing.
+ // Also, we only care about compiler barriers, as the signal handler is
+ // triggered synchronously from a system call. We don't have to protect
+ // against issues with the memory model or with completely asynchronous
+ // events.
+ if (trap_array_size_ >= trap_array_capacity_) {
+ trap_array_capacity_ += kCapacityIncrement;
+ ErrorCode *old_trap_array = trap_array_;
+ ErrorCode *new_trap_array = new ErrorCode[trap_array_capacity_];
+
+ // Language specs are unclear on whether the compiler is allowed to move
+ // the "delete[]" above our preceding assignments and/or memory moves,
+ // iff the compiler believes that "delete[]" doesn't have any other
+ // global side-effects.
+ // We insert optimization barriers to prevent this from happening.
+ // The first barrier is probably not needed, but better be explicit in
+ // what we want to tell the compiler.
+ // The clang developer mailing list couldn't answer whether this is a
+ // legitimate worry; but they at least thought that the barrier is
+ // sufficient to prevent the (so far hypothetical) problem of re-ordering
+ // of instructions by the compiler.
+ memcpy(new_trap_array, trap_array_, trap_array_size_*sizeof(ErrorCode));
+ asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
+ trap_array_ = new_trap_array;
+ asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
+
+ delete[] old_trap_array;
+ }
+ trap_ids_[key] = id;
+ trap_array_[trap_array_size_] = ErrorCode(fnc, aux, safe, id);
+ return trap_array_[trap_array_size_++];
+ }
+
+ return ErrorCode(fnc, aux, safe, id);
+}
+
+bool Trap::SandboxDebuggingAllowedByUser() const {
+ const char *debug_flag = getenv(kSandboxDebuggingEnv);
+ return debug_flag && *debug_flag;
+}
+
+
+bool Trap::EnableUnsafeTrapsInSigSysHandler() {
+ Trap *trap = GetInstance();
+ if (!trap->has_unsafe_traps_) {
+ // Unsafe traps are a one-way fuse. Once enabled, they can never be turned
+ // off again.
+ // We only allow enabling unsafe traps, if the user explicitly set an
+ // appropriate environment variable. This prevents bugs that accidentally
+ // disable all sandboxing for all users.
+ if (trap->SandboxDebuggingAllowedByUser()) {
+ // We only ever print this message once, when we enable unsafe traps the
+ // first time.
+ SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
+ trap->has_unsafe_traps_ = true;
+ } else {
+ SANDBOX_INFO("Cannot disable sandbox and use unsafe traps unless "
+ "CHROME_SANDBOX_DEBUGGING is turned on first");
+ }
+ }
+ // Returns the, possibly updated, value of has_unsafe_traps_.
+ return trap->has_unsafe_traps_;
+}
+
+ErrorCode Trap::ErrorCodeFromTrapId(uint16_t id) {
+ if (global_trap_ && id > 0 && id <= global_trap_->trap_array_size_) {
+ return global_trap_->trap_array_[id - 1];
+ } else {
+ return ErrorCode();
+ }
+}
+
+Trap *Trap::global_trap_;
+
+} // namespace playground2
diff --git a/chromium/sandbox/linux/seccomp-bpf/trap.h b/chromium/sandbox/linux/seccomp-bpf/trap.h
new file mode 100644
index 00000000000..db29757c0db
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/trap.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+
+#include <signal.h>
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "sandbox/linux/seccomp-bpf/port.h"
+
+
+namespace playground2 {
+
+class ErrorCode;
+
+// The Trap class allows a BPF filter program to branch out to user space by
+// raising a SIGSYS signal.
+// N.B.: This class does not perform any synchronization operations. If
+// modifications are made to any of the traps, it is the caller's
+// responsibility to ensure that this happens in a thread-safe fashion.
+// 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 {
+ 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
+ // installed; it does so by returning a suitable ErrorCode() from the
+ // syscallEvaluator. See the ErrorCode() constructor for how to pass in
+ // the function pointer.
+ // Please note that TrapFnc is executed from signal context and must be
+ // async-signal safe:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+ // Also note that it follows the calling convention of native system calls.
+ // In other words, it reports an error by returning an exit code in the
+ // range -1..-4096. It should not set errno when reporting errors; on the
+ // other hand, accidentally modifying errno is harmless and the changes will
+ // be undone afterwards.
+ typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void *aux);
+
+ // Registers a new trap handler and sets up the appropriate SIGSYS handler
+ // as needed.
+ // N.B.: This makes a permanent state change. Traps cannot be unregistered,
+ // as that would break existing BPF filters that are still active.
+ static ErrorCode MakeTrap(TrapFnc fnc, const void *aux, bool safe);
+
+ // Enables support for unsafe traps in the SIGSYS signal handler. This is a
+ // one-way fuse. It works in conjunction with the BPF compiler emitting code
+ // that unconditionally allows system calls, if they have a magic return
+ // address (i.e. SandboxSyscall(-1)).
+ // Once unsafe traps are enabled, the sandbox is essentially compromised.
+ // But this is still a very useful feature for debugging purposes. Use with
+ // care. This feature is availably only if enabled by the user (see above).
+ // Returns "true", if unsafe traps were turned on.
+ static bool EnableUnsafeTrapsInSigSysHandler();
+
+ // Returns the ErrorCode associate with a particular trap id.
+ 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;
+ const void *aux;
+ bool safe;
+ bool operator<(const TrapKey&) const;
+ };
+ typedef std::map<TrapKey, uint16_t> TrapIds;
+
+ // 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
+ // singleton.
+ // It also gracefully deals with methods that should check for the singleton,
+ // but avoid instantiating it, if it doesn't exist yet
+ // (e.g. ErrorCodeFromTrapId()).
+ static Trap *GetInstance();
+ static void SigSysAction(int nr, siginfo_t *info, void *void_context);
+
+ void SigSys(int nr, siginfo_t *info, void *void_context);
+ ErrorCode MakeTrapImpl(TrapFnc fnc, const void *aux, bool safe);
+ bool SandboxDebuggingAllowedByUser() const;
+
+
+
+ // We have a global singleton that handles all of our SIGSYS traps. This
+ // variable must never be deallocated after it has been set up initially, as
+ // there is no way to reset in-kernel BPF filters that generate SIGSYS
+ // events.
+ static Trap *global_trap_;
+
+ TrapIds trap_ids_; // Maps from TrapKeys to numeric ids
+ ErrorCode *trap_array_; // Array of ErrorCodes indexed by ids
+ size_t trap_array_size_; // Currently used size of array
+ size_t trap_array_capacity_; // Currently allocated capacity of array
+ bool has_unsafe_traps_; // Whether unsafe traps have been enabled
+
+ // Our constructor is private. A shared global instance is created
+ // automatically as needed.
+ // Copying and assigning is unimplemented. It doesn't make sense for a
+ // singleton.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Trap);
+};
+
+} // namespace playground2
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
diff --git a/chromium/sandbox/linux/seccomp-bpf/verifier.cc b/chromium/sandbox/linux/seccomp-bpf/verifier.cc
new file mode 100644
index 00000000000..60c0eab20d6
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/verifier.cc
@@ -0,0 +1,449 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall_iterator.h"
+#include "sandbox/linux/seccomp-bpf/verifier.h"
+
+
+namespace {
+
+using playground2::ErrorCode;
+using playground2::Sandbox;
+using playground2::Verifier;
+using playground2::arch_seccomp_data;
+
+struct State {
+ State(const std::vector<struct sock_filter>& p,
+ const struct arch_seccomp_data& d) :
+ program(p),
+ data(d),
+ ip(0),
+ accumulator(0),
+ acc_is_valid(false) {
+ }
+ const std::vector<struct sock_filter>& program;
+ const struct arch_seccomp_data& data;
+ unsigned int ip;
+ uint32_t accumulator;
+ bool acc_is_valid;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(State);
+};
+
+uint32_t EvaluateErrorCode(Sandbox *sandbox, const ErrorCode& code,
+ const struct arch_seccomp_data& data) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ return code.err();
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.width() == ErrorCode::TP_32BIT &&
+ (data.args[code.argno()] >> 32) &&
+ (data.args[code.argno()] & 0xFFFFFFFF80000000ull) !=
+ 0xFFFFFFFF80000000ull) {
+ return sandbox->Unexpected64bitArgument().err();
+ }
+ switch (code.op()) {
+ case ErrorCode::OP_EQUAL:
+ return EvaluateErrorCode(sandbox,
+ (code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) == code.value()
+ ? *code.passed()
+ : *code.failed(),
+ data);
+ case ErrorCode::OP_HAS_ALL_BITS:
+ return EvaluateErrorCode(sandbox,
+ ((code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) & code.value())
+ == code.value()
+ ? *code.passed()
+ : *code.failed(),
+ data);
+ case ErrorCode::OP_HAS_ANY_BITS:
+ return EvaluateErrorCode(sandbox,
+ (code.width() == ErrorCode::TP_32BIT
+ ? uint32_t(data.args[code.argno()])
+ : data.args[code.argno()]) & code.value()
+ ? *code.passed()
+ : *code.failed(),
+ data);
+ default:
+ return SECCOMP_RET_INVALID;
+ }
+ } else {
+ return SECCOMP_RET_INVALID;
+ }
+}
+
+bool VerifyErrorCode(Sandbox *sandbox,
+ const std::vector<struct sock_filter>& program,
+ struct arch_seccomp_data *data,
+ const ErrorCode& root_code,
+ const ErrorCode& code,
+ const char **err) {
+ if (code.error_type() == ErrorCode::ET_SIMPLE ||
+ code.error_type() == ErrorCode::ET_TRAP) {
+ uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
+ if (*err) {
+ return false;
+ } else if (computed_ret != EvaluateErrorCode(sandbox, root_code, *data)) {
+ // For efficiency's sake, we'd much rather compare "computed_ret"
+ // against "code.err()". This works most of the time, but it doesn't
+ // always work for nested conditional expressions. The test values
+ // that we generate on the fly to probe expressions can trigger
+ // code flow decisions in multiple nodes of the decision tree, and the
+ // only way to compute the correct error code in that situation is by
+ // calling EvaluateErrorCode().
+ *err = "Exit code from BPF program doesn't match";
+ return false;
+ }
+ } else if (code.error_type() == ErrorCode::ET_COND) {
+ if (code.argno() < 0 || code.argno() >= 6) {
+ *err = "Invalid argument number in error code";
+ return false;
+ }
+ switch (code.op()) {
+ case ErrorCode::OP_EQUAL:
+ // Verify that we can check a 32bit value (or the LSB of a 64bit value)
+ // for equality.
+ data->args[code.argno()] = code.value();
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ *code.passed(), err)) {
+ return false;
+ }
+
+ // Change the value to no longer match and verify that this is detected
+ // as an inequality.
+ data->args[code.argno()] = code.value() ^ 0x55AA55AA;
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ *code.failed(), err)) {
+ return false;
+ }
+
+ // BPF programs can only ever operate on 32bit values. So, we have
+ // generated additional BPF instructions that inspect the MSB. Verify
+ // that they behave as intended.
+ if (code.width() == ErrorCode::TP_32BIT) {
+ if (code.value() >> 32) {
+ SANDBOX_DIE("Invalid comparison of a 32bit system call argument "
+ "against a 64bit constant; this test is always false.");
+ }
+
+ // If the system call argument was intended to be a 32bit parameter,
+ // verify that it is a fatal error if a 64bit value is ever passed
+ // here.
+ data->args[code.argno()] = 0x100000000ull;
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ sandbox->Unexpected64bitArgument(),
+ err)) {
+ return false;
+ }
+ } else {
+ // If the system call argument was intended to be a 64bit parameter,
+ // verify that we can handle (in-)equality for the MSB. This is
+ // essentially the same test that we did earlier for the LSB.
+ // We only need to verify the behavior of the inequality test. We
+ // know that the equality test already passed, as unlike the kernel
+ // the Verifier does operate on 64bit quantities.
+ data->args[code.argno()] = code.value() ^ 0x55AA55AA00000000ull;
+ if (!VerifyErrorCode(sandbox, program, data, root_code,
+ *code.failed(), err)) {
+ return false;
+ }
+ }
+ break;
+ case ErrorCode::OP_HAS_ALL_BITS:
+ case ErrorCode::OP_HAS_ANY_BITS:
+ // A comprehensive test of bit values is difficult and potentially rather
+ // time-expensive. We avoid doing so at run-time and instead rely on the
+ // unittest for full testing. The test that we have here covers just the
+ // common cases. We test against the bitmask itself, all zeros and all
+ // ones.
+ {
+ // Testing "any" bits against a zero mask is always false. So, there
+ // are some cases, where we expect tests to take the "failed()" branch
+ // even though this is a test that normally should take "passed()".
+ const ErrorCode& passed =
+ (!code.value() && code.op() == ErrorCode::OP_HAS_ANY_BITS) ||
+
+ // On a 32bit system, it is impossible to pass a 64bit value as a
+ // system call argument. So, some additional tests always evaluate
+ // as false.
+ ((code.value() & ~uint64_t(uintptr_t(-1))) &&
+ code.op() == ErrorCode::OP_HAS_ALL_BITS) ||
+ (code.value() && !(code.value() & uintptr_t(-1)) &&
+ code.op() == ErrorCode::OP_HAS_ANY_BITS)
+
+ ? *code.failed() : *code.passed();
+
+ // Similary, testing for "all" bits in a zero mask is always true. So,
+ // some cases pass despite them normally failing.
+ const ErrorCode& failed =
+ !code.value() && code.op() == ErrorCode::OP_HAS_ALL_BITS
+ ? *code.passed() : *code.failed();
+
+ data->args[code.argno()] = code.value() & uintptr_t(-1);
+ if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
+ return false;
+ }
+ data->args[code.argno()] = uintptr_t(-1);
+ if (!VerifyErrorCode(sandbox, program, data, root_code, passed, err)) {
+ return false;
+ }
+ data->args[code.argno()] = 0;
+ if (!VerifyErrorCode(sandbox, program, data, root_code, failed, err)) {
+ return false;
+ }
+ }
+ break;
+ default: // TODO(markus): Need to add support for OP_GREATER
+ *err = "Unsupported operation in conditional error code";
+ return false;
+ }
+ } else {
+ *err = "Attempting to return invalid error code from BPF program";
+ return false;
+ }
+ return true;
+}
+
+void Ld(State *state, const struct sock_filter& insn, const char **err) {
+ if (BPF_SIZE(insn.code) != BPF_W ||
+ BPF_MODE(insn.code) != BPF_ABS) {
+ *err = "Invalid BPF_LD instruction";
+ return;
+ }
+ if (insn.k < sizeof(struct arch_seccomp_data) && (insn.k & 3) == 0) {
+ // We only allow loading of properly aligned 32bit quantities.
+ memcpy(&state->accumulator,
+ reinterpret_cast<const char *>(&state->data) + insn.k,
+ 4);
+ } else {
+ *err = "Invalid operand in BPF_LD instruction";
+ return;
+ }
+ state->acc_is_valid = true;
+ return;
+}
+
+void Jmp(State *state, const struct sock_filter& insn, const char **err) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ if (state->ip + insn.k + 1 >= state->program.size() ||
+ state->ip + insn.k + 1 <= state->ip) {
+ compilation_failure:
+ *err = "Invalid BPF_JMP instruction";
+ return;
+ }
+ state->ip += insn.k;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K ||
+ !state->acc_is_valid ||
+ state->ip + insn.jt + 1 >= state->program.size() ||
+ state->ip + insn.jf + 1 >= state->program.size()) {
+ goto compilation_failure;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_JEQ:
+ if (state->accumulator == insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGT:
+ if (state->accumulator > insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JGE:
+ if (state->accumulator >= insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ case BPF_JSET:
+ if (state->accumulator & insn.k) {
+ state->ip += insn.jt;
+ } else {
+ state->ip += insn.jf;
+ }
+ break;
+ default:
+ goto compilation_failure;
+ }
+ }
+}
+
+uint32_t Ret(State *, const struct sock_filter& insn, const char **err) {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Invalid BPF_RET instruction";
+ return 0;
+ }
+ return insn.k;
+}
+
+void Alu(State *state, const struct sock_filter& insn, const char **err) {
+ if (BPF_OP(insn.code) == BPF_NEG) {
+ state->accumulator = -state->accumulator;
+ return;
+ } else {
+ if (BPF_SRC(insn.code) != BPF_K) {
+ *err = "Unexpected source operand in arithmetic operation";
+ return;
+ }
+ switch (BPF_OP(insn.code)) {
+ case BPF_ADD:
+ state->accumulator += insn.k;
+ break;
+ case BPF_SUB:
+ state->accumulator -= insn.k;
+ break;
+ case BPF_MUL:
+ state->accumulator *= insn.k;
+ break;
+ case BPF_DIV:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator /= insn.k;
+ break;
+ case BPF_MOD:
+ if (!insn.k) {
+ *err = "Illegal division by zero";
+ break;
+ }
+ state->accumulator %= insn.k;
+ break;
+ case BPF_OR:
+ state->accumulator |= insn.k;
+ break;
+ case BPF_XOR:
+ state->accumulator ^= insn.k;
+ break;
+ case BPF_AND:
+ state->accumulator &= insn.k;
+ break;
+ case BPF_LSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator <<= insn.k;
+ break;
+ case BPF_RSH:
+ if (insn.k > 32) {
+ *err = "Illegal shift operation";
+ break;
+ }
+ state->accumulator >>= insn.k;
+ break;
+ default:
+ *err = "Invalid operator in arithmetic operation";
+ break;
+ }
+ }
+}
+
+} // namespace
+
+namespace playground2 {
+
+bool Verifier::VerifyBPF(Sandbox *sandbox,
+ const std::vector<struct sock_filter>& program,
+ const Sandbox::Evaluators& evaluators,
+ const char **err) {
+ *err = NULL;
+ if (evaluators.size() != 1) {
+ *err = "Not implemented";
+ return false;
+ }
+ Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first;
+ void *aux = evaluators.begin()->second;
+ for (SyscallIterator iter(false); !iter.Done(); ) {
+ uint32_t sysnum = iter.Next();
+ // We ideally want to iterate over the full system call range and values
+ // just above and just below this range. This gives us the full result set
+ // of the "evaluators".
+ // On Intel systems, this can fail in a surprising way, as a cleared bit 30
+ // indicates either i386 or x86-64; and a set bit 30 indicates x32. And
+ // unless we pay attention to setting this bit correctly, an early check in
+ // our BPF program will make us fail with a misleading error code.
+ struct arch_seccomp_data data = { static_cast<int>(sysnum),
+ static_cast<uint32_t>(SECCOMP_ARCH) };
+#if defined(__i386__) || defined(__x86_64__)
+#if defined(__x86_64__) && defined(__ILP32__)
+ if (!(sysnum & 0x40000000u)) {
+ continue;
+ }
+#else
+ if (sysnum & 0x40000000u) {
+ continue;
+ }
+#endif
+#endif
+ ErrorCode code = evaluate_syscall(sandbox, sysnum, aux);
+ if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t Verifier::EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char **err) {
+ *err = NULL;
+ if (program.size() < 1 || program.size() >= SECCOMP_MAX_PROGRAM_SIZE) {
+ *err = "Invalid program length";
+ return 0;
+ }
+ for (State state(program, data); !*err; ++state.ip) {
+ if (state.ip >= program.size()) {
+ *err = "Invalid instruction pointer in BPF program";
+ break;
+ }
+ const struct sock_filter& insn = program[state.ip];
+ switch (BPF_CLASS(insn.code)) {
+ case BPF_LD:
+ Ld(&state, insn, err);
+ break;
+ case BPF_JMP:
+ Jmp(&state, insn, err);
+ break;
+ case BPF_RET: {
+ uint32_t r = Ret(&state, insn, err);
+ switch (r & SECCOMP_RET_ACTION) {
+ case SECCOMP_RET_TRAP:
+ case SECCOMP_RET_ERRNO:
+ case SECCOMP_RET_ALLOW:
+ break;
+ case SECCOMP_RET_KILL: // We don't ever generate this
+ case SECCOMP_RET_TRACE: // We don't ever generate this
+ case SECCOMP_RET_INVALID: // Should never show up in BPF program
+ default:
+ *err = "Unexpected return code found in BPF program";
+ return 0;
+ }
+ return r; }
+ case BPF_ALU:
+ Alu(&state, insn, err);
+ break;
+ default:
+ *err = "Unexpected instruction in BPF program";
+ break;
+ }
+ }
+ return 0;
+}
+
+} // namespace
diff --git a/chromium/sandbox/linux/seccomp-bpf/verifier.h b/chromium/sandbox/linux/seccomp-bpf/verifier.h
new file mode 100644
index 00000000000..3e99a0818b9
--- /dev/null
+++ b/chromium/sandbox/linux/seccomp-bpf/verifier.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
+
+#include <linux/filter.h>
+
+#include <utility>
+#include <vector>
+
+
+namespace playground2 {
+
+class Verifier {
+ public:
+ // Evaluate the BPF program for all possible inputs and verify that it
+ // computes the correct result. We use the "evaluators" to determine
+ // the full set of possible inputs that we have to iterate over.
+ // Returns success, if the BPF filter accurately reflects the rules
+ // set by the "evaluators".
+ // Upon success, "err" is set to NULL. Upon failure, it contains a static
+ // error message that does not need to be free()'d.
+ static bool VerifyBPF(Sandbox *sandbox,
+ const std::vector<struct sock_filter>& program,
+ const Sandbox::Evaluators& evaluators,
+ const char **err);
+
+ // Evaluate a given BPF program for a particular set of system call
+ // parameters. If evaluation failed for any reason, "err" will be set to
+ // a non-NULL error string. Otherwise, the BPF program's result will be
+ // returned by the function and "err" is NULL.
+ // We do not actually implement the full BPF state machine, but only the
+ // parts that can actually be generated by our BPF compiler. If this code
+ // is used for purposes other than verifying the output of the sandbox's
+ // BPF compiler, we might have to extend this BPF interpreter.
+ static uint32_t EvaluateBPF(const std::vector<struct sock_filter>& program,
+ const struct arch_seccomp_data& data,
+ const char **err);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__
diff --git a/chromium/sandbox/linux/services/android_arm_ucontext.h b/chromium/sandbox/linux/services/android_arm_ucontext.h
new file mode 100644
index 00000000000..d1446c67bd7
--- /dev/null
+++ b/chromium/sandbox/linux/services/android_arm_ucontext.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef unsigned long greg_t;
+
+//typedef unsigned long sigset_t;
+typedef struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ sigset_t uc_sigmask;
+ /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */
+ int __not_used[32 - (sizeof (sigset_t) / sizeof (int))];
+ /* Last for extensibility. Eight byte aligned because some
+ coprocessors require eight byte alignment. */
+ unsigned long uc_regspace[128] __attribute__((__aligned__(8)));
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_
diff --git a/chromium/sandbox/linux/services/android_i386_ucontext.h b/chromium/sandbox/linux/services/android_i386_ucontext.h
new file mode 100644
index 00000000000..580ac70a06a
--- /dev/null
+++ b/chromium/sandbox/linux/services/android_i386_ucontext.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_I386_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.
+// This is mostly copied from breakpad (common/android/include/sys/ucontext.h),
+// except we do use sigset_t for uc_sigmask instead of a custom type.
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+/* 80-bit floating-point register */
+struct _libc_fpreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+};
+
+/* Simple floating-point state, see FNSTENV instruction */
+struct _libc_fpstate {
+ unsigned long cw;
+ unsigned long sw;
+ unsigned long tag;
+ unsigned long ipoff;
+ unsigned long cssel;
+ unsigned long dataoff;
+ unsigned long datasel;
+ struct _libc_fpreg _st[8];
+ unsigned long status;
+};
+
+typedef uint32_t greg_t;
+
+typedef struct {
+ uint32_t gregs[19];
+ struct _libc_fpstate* fpregs;
+ uint32_t oldmask;
+ uint32_t cr2;
+} mcontext_t;
+
+enum {
+ REG_GS = 0,
+ REG_FS,
+ REG_ES,
+ REG_DS,
+ REG_EDI,
+ REG_ESI,
+ REG_EBP,
+ REG_ESP,
+ REG_EBX,
+ REG_EDX,
+ REG_ECX,
+ REG_EAX,
+ REG_TRAPNO,
+ REG_ERR,
+ REG_EIP,
+ REG_CS,
+ REG_EFL,
+ REG_UESP,
+ REG_SS,
+};
+
+typedef struct ucontext {
+ uint32_t 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_I386_UCONTEXT_H_
diff --git a/chromium/sandbox/linux/services/android_ucontext.h b/chromium/sandbox/linux/services/android_ucontext.h
new file mode 100644
index 00000000000..437bbab7bab
--- /dev/null
+++ b/chromium/sandbox/linux/services/android_ucontext.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
+#define SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
+
+#if defined(__ANDROID__)
+
+#if defined(__arm__)
+#include "sandbox/linux/services/android_arm_ucontext.h"
+#elif defined(__i386__)
+#include "sandbox/linux/services/android_i386_ucontext.h"
+#else
+#error "No support for your architecture in Android header"
+#endif
+
+#else // __ANDROID__
+#error "Android header file included on non Android."
+#endif // __ANDROID__
+
+#endif // SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_
diff --git a/chromium/sandbox/linux/services/arm_linux_syscalls.h b/chromium/sandbox/linux/services/arm_linux_syscalls.h
new file mode 100644
index 00000000000..dd0826e54eb
--- /dev/null
+++ b/chromium/sandbox/linux/services/arm_linux_syscalls.h
@@ -0,0 +1,1377 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's calls.S.
+#ifndef SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_
+
+#if !defined(__arm__) || !defined(__ARM_EABI__)
+#error "Including header on wrong architecture"
+#endif
+
+// __NR_SYSCALL_BASE, __ARM_NR_BASE are defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+
+// This syscall list has holes, because ARM EABI makes some syscalls obsolete.
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_SYSCALL_BASE+0)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_SYSCALL_BASE+1)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_SYSCALL_BASE+2)
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_SYSCALL_BASE+3)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_SYSCALL_BASE+4)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_SYSCALL_BASE+5)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_SYSCALL_BASE+6)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_SYSCALL_BASE+8)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_SYSCALL_BASE+9)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_SYSCALL_BASE+10)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_SYSCALL_BASE+11)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_SYSCALL_BASE+12)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_SYSCALL_BASE+14)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_SYSCALL_BASE+15)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_SYSCALL_BASE+16)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_SYSCALL_BASE+19)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_SYSCALL_BASE+20)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_SYSCALL_BASE+21)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_SYSCALL_BASE+23)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_SYSCALL_BASE+24)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_SYSCALL_BASE+26)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_SYSCALL_BASE+29)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_SYSCALL_BASE+33)
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice (__NR_SYSCALL_BASE+34)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_SYSCALL_BASE+36)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_SYSCALL_BASE+37)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_SYSCALL_BASE+38)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_SYSCALL_BASE+39)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_SYSCALL_BASE+40)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_SYSCALL_BASE+41)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_SYSCALL_BASE+42)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_SYSCALL_BASE+43)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_SYSCALL_BASE+45)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_SYSCALL_BASE+46)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_SYSCALL_BASE+47)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_SYSCALL_BASE+49)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_SYSCALL_BASE+50)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_SYSCALL_BASE+51)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_SYSCALL_BASE+52)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_SYSCALL_BASE+54)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_SYSCALL_BASE+55)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_SYSCALL_BASE+57)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_SYSCALL_BASE+60)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_SYSCALL_BASE+61)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_SYSCALL_BASE+62)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_SYSCALL_BASE+63)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_SYSCALL_BASE+64)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_SYSCALL_BASE+65)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_SYSCALL_BASE+66)
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction (__NR_SYSCALL_BASE+67)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_SYSCALL_BASE+70)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_SYSCALL_BASE+71)
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend (__NR_SYSCALL_BASE+72)
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending (__NR_SYSCALL_BASE+73)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_SYSCALL_BASE+74)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_SYSCALL_BASE+75)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_SYSCALL_BASE+77)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_SYSCALL_BASE+78)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_SYSCALL_BASE+79)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_SYSCALL_BASE+80)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_SYSCALL_BASE+81)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_SYSCALL_BASE+83)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_SYSCALL_BASE+85)
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib (__NR_SYSCALL_BASE+86)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_SYSCALL_BASE+87)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_SYSCALL_BASE+88)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_SYSCALL_BASE+91)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_SYSCALL_BASE+92)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_SYSCALL_BASE+93)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_SYSCALL_BASE+94)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_SYSCALL_BASE+95)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_SYSCALL_BASE+96)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_SYSCALL_BASE+97)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_SYSCALL_BASE+99)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_SYSCALL_BASE+100)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_SYSCALL_BASE+103)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_SYSCALL_BASE+104)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_SYSCALL_BASE+105)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_SYSCALL_BASE+106)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_SYSCALL_BASE+107)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_SYSCALL_BASE+108)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_SYSCALL_BASE+111)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_SYSCALL_BASE+114)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_SYSCALL_BASE+115)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_SYSCALL_BASE+116)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_SYSCALL_BASE+118)
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn (__NR_SYSCALL_BASE+119)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_SYSCALL_BASE+120)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_SYSCALL_BASE+121)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_SYSCALL_BASE+122)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_SYSCALL_BASE+124)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_SYSCALL_BASE+125)
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_SYSCALL_BASE+128)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_SYSCALL_BASE+129)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_SYSCALL_BASE+131)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_SYSCALL_BASE+132)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_SYSCALL_BASE+133)
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush (__NR_SYSCALL_BASE+134)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_SYSCALL_BASE+135)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_SYSCALL_BASE+136)
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_SYSCALL_BASE+138)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_SYSCALL_BASE+139)
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek (__NR_SYSCALL_BASE+140)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_SYSCALL_BASE+141)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_SYSCALL_BASE+142)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_SYSCALL_BASE+143)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_SYSCALL_BASE+144)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_SYSCALL_BASE+145)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_SYSCALL_BASE+146)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_SYSCALL_BASE+147)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_SYSCALL_BASE+148)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_SYSCALL_BASE+149)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_SYSCALL_BASE+150)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_SYSCALL_BASE+151)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_SYSCALL_BASE+152)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_SYSCALL_BASE+153)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_SYSCALL_BASE+154)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_SYSCALL_BASE+155)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_SYSCALL_BASE+158)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_SYSCALL_BASE+162)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_SYSCALL_BASE+163)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_SYSCALL_BASE+164)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_SYSCALL_BASE+165)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_SYSCALL_BASE+168)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_SYSCALL_BASE+170)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_SYSCALL_BASE+171)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_SYSCALL_BASE+172)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_SYSCALL_BASE+180)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_SYSCALL_BASE+181)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_SYSCALL_BASE+182)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_SYSCALL_BASE+183)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_SYSCALL_BASE+184)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_SYSCALL_BASE+185)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_SYSCALL_BASE+186)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_SYSCALL_BASE+187)
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork (__NR_SYSCALL_BASE+190)
+#endif
+
+#if !defined(__NR_ugetrlimit)
+#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191)
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 (__NR_SYSCALL_BASE+193)
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194)
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 (__NR_SYSCALL_BASE+195)
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 (__NR_SYSCALL_BASE+196)
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 (__NR_SYSCALL_BASE+197)
+#endif
+
+#if !defined(__NR_lchown32)
+#define __NR_lchown32 (__NR_SYSCALL_BASE+198)
+#endif
+
+#if !defined(__NR_getuid32)
+#define __NR_getuid32 (__NR_SYSCALL_BASE+199)
+#endif
+
+#if !defined(__NR_getgid32)
+#define __NR_getgid32 (__NR_SYSCALL_BASE+200)
+#endif
+
+#if !defined(__NR_geteuid32)
+#define __NR_geteuid32 (__NR_SYSCALL_BASE+201)
+#endif
+
+#if !defined(__NR_getegid32)
+#define __NR_getegid32 (__NR_SYSCALL_BASE+202)
+#endif
+
+#if !defined(__NR_setreuid32)
+#define __NR_setreuid32 (__NR_SYSCALL_BASE+203)
+#endif
+
+#if !defined(__NR_setregid32)
+#define __NR_setregid32 (__NR_SYSCALL_BASE+204)
+#endif
+
+#if !defined(__NR_getgroups32)
+#define __NR_getgroups32 (__NR_SYSCALL_BASE+205)
+#endif
+
+#if !defined(__NR_setgroups32)
+#define __NR_setgroups32 (__NR_SYSCALL_BASE+206)
+#endif
+
+#if !defined(__NR_fchown32)
+#define __NR_fchown32 (__NR_SYSCALL_BASE+207)
+#endif
+
+#if !defined(__NR_setresuid32)
+#define __NR_setresuid32 (__NR_SYSCALL_BASE+208)
+#endif
+
+#if !defined(__NR_getresuid32)
+#define __NR_getresuid32 (__NR_SYSCALL_BASE+209)
+#endif
+
+#if !defined(__NR_setresgid32)
+#define __NR_setresgid32 (__NR_SYSCALL_BASE+210)
+#endif
+
+#if !defined(__NR_getresgid32)
+#define __NR_getresgid32 (__NR_SYSCALL_BASE+211)
+#endif
+
+#if !defined(__NR_chown32)
+#define __NR_chown32 (__NR_SYSCALL_BASE+212)
+#endif
+
+#if !defined(__NR_setuid32)
+#define __NR_setuid32 (__NR_SYSCALL_BASE+213)
+#endif
+
+#if !defined(__NR_setgid32)
+#define __NR_setgid32 (__NR_SYSCALL_BASE+214)
+#endif
+
+#if !defined(__NR_setfsuid32)
+#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215)
+#endif
+
+#if !defined(__NR_setfsgid32)
+#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_SYSCALL_BASE+217)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_SYSCALL_BASE+218)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_SYSCALL_BASE+219)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_SYSCALL_BASE+220)
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 (__NR_SYSCALL_BASE+221)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_SYSCALL_BASE+224)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_SYSCALL_BASE+225)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_SYSCALL_BASE+226)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_SYSCALL_BASE+229)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_SYSCALL_BASE+231)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_SYSCALL_BASE+232)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_SYSCALL_BASE+233)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_SYSCALL_BASE+234)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_SYSCALL_BASE+235)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_SYSCALL_BASE+238)
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_SYSCALL_BASE+240)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_SYSCALL_BASE+243)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_SYSCALL_BASE+244)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_SYSCALL_BASE+245)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_SYSCALL_BASE+246)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_SYSCALL_BASE+247)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_SYSCALL_BASE+248)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_SYSCALL_BASE+250)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_SYSCALL_BASE+252)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_SYSCALL_BASE+256)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_SYSCALL_BASE+257)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_SYSCALL_BASE+258)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_SYSCALL_BASE+259)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_SYSCALL_BASE+261)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_SYSCALL_BASE+262)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_SYSCALL_BASE+263)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_SYSCALL_BASE+264)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265)
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 (__NR_SYSCALL_BASE+266)
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_SYSCALL_BASE+268)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_SYSCALL_BASE+269)
+#endif
+
+#if !defined(__NR_arm_fadvise64_64)
+#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270)
+#endif
+
+#if !defined(__NR_pciconfig_iobase)
+#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271)
+#endif
+
+#if !defined(__NR_pciconfig_read)
+#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272)
+#endif
+
+#if !defined(__NR_pciconfig_write)
+#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_SYSCALL_BASE+274)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_SYSCALL_BASE+275)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_SYSCALL_BASE+278)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_SYSCALL_BASE+280)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_SYSCALL_BASE+281)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_SYSCALL_BASE+282)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_SYSCALL_BASE+283)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_SYSCALL_BASE+284)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_SYSCALL_BASE+285)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_SYSCALL_BASE+286)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_SYSCALL_BASE+287)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_SYSCALL_BASE+288)
+#endif
+
+#if !defined(__NR_send)
+#define __NR_send (__NR_SYSCALL_BASE+289)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_SYSCALL_BASE+290)
+#endif
+
+#if !defined(__NR_recv)
+#define __NR_recv (__NR_SYSCALL_BASE+291)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_SYSCALL_BASE+292)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_SYSCALL_BASE+293)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_SYSCALL_BASE+294)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_SYSCALL_BASE+295)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_SYSCALL_BASE+296)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_SYSCALL_BASE+297)
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop (__NR_SYSCALL_BASE+298)
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget (__NR_SYSCALL_BASE+299)
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl (__NR_SYSCALL_BASE+300)
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd (__NR_SYSCALL_BASE+301)
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv (__NR_SYSCALL_BASE+302)
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget (__NR_SYSCALL_BASE+303)
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl (__NR_SYSCALL_BASE+304)
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat (__NR_SYSCALL_BASE+305)
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt (__NR_SYSCALL_BASE+306)
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget (__NR_SYSCALL_BASE+307)
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl (__NR_SYSCALL_BASE+308)
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_SYSCALL_BASE+309)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_SYSCALL_BASE+310)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_SYSCALL_BASE+311)
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop (__NR_SYSCALL_BASE+312)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_SYSCALL_BASE+313)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_SYSCALL_BASE+314)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_SYSCALL_BASE+315)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_SYSCALL_BASE+316)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_SYSCALL_BASE+319)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_SYSCALL_BASE+322)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_SYSCALL_BASE+323)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_SYSCALL_BASE+324)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_SYSCALL_BASE+325)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_SYSCALL_BASE+326)
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 (__NR_SYSCALL_BASE+327)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_SYSCALL_BASE+328)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_SYSCALL_BASE+329)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_SYSCALL_BASE+330)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_SYSCALL_BASE+331)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_SYSCALL_BASE+334)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_SYSCALL_BASE+336)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_SYSCALL_BASE+337)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_SYSCALL_BASE+340)
+#endif
+
+#if !defined(__NR_arm_sync_file_range)
+#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_SYSCALL_BASE+342)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_SYSCALL_BASE+344)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_SYSCALL_BASE+345)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_SYSCALL_BASE+348)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_SYSCALL_BASE+349)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate (__NR_SYSCALL_BASE+352)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_SYSCALL_BASE+355)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_SYSCALL_BASE+356)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_SYSCALL_BASE+358)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_SYSCALL_BASE+361)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_SYSCALL_BASE+362)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_SYSCALL_BASE+366)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_SYSCALL_BASE+367)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_SYSCALL_BASE+369)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_SYSCALL_BASE+372)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_SYSCALL_BASE+373)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_SYSCALL_BASE+374)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_SYSCALL_BASE+375)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
+#endif
+
+// ARM private syscalls.
+#if !defined(__ARM_NR_breakpoint)
+#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
+#endif
+
+#if !defined(__ARM_NR_cacheflush)
+#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
+#endif
+
+#if !defined(__ARM_NR_usr26)
+#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
+#endif
+
+#if !defined(__ARM_NR_usr32)
+#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
+#endif
+
+#if !defined(__ARM_NR_set_tls)
+#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
+#endif
+
+// ARM kernel private syscall.
+#if !defined(__ARM_NR_cmpxchg)
+#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_
+
diff --git a/chromium/sandbox/linux/services/broker_process.cc b/chromium/sandbox/linux/services/broker_process.cc
new file mode 100644
index 00000000000..7999e77fa34
--- /dev/null
+++ b/chromium/sandbox/linux/services/broker_process.cc
@@ -0,0 +1,522 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/broker_process.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/linux_syscalls.h"
+
+#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
+#define MSG_CMSG_CLOEXEC 0x40000000
+#endif
+
+namespace {
+
+static const size_t kMaxMessageLength = 4096;
+
+// Some flags are local to the current process and cannot be sent over a Unix
+// socket. They need special treatment from the client.
+// O_CLOEXEC is tricky because in theory another thread could call execve()
+// before special treatment is made on the client, so a client needs to call
+// recvmsg(2) with MSG_CMSG_CLOEXEC.
+// To make things worse, there are two CLOEXEC related flags, FD_CLOEXEC (see
+// F_GETFD in fcntl(2)) and O_CLOEXEC (see F_GETFL in fcntl(2)). O_CLOEXEC
+// doesn't affect the semantics on execve(), it's merely a note that the
+// descriptor was originally opened with O_CLOEXEC as a flag. And it is sent
+// over unix sockets just fine, so a receiver that would (incorrectly) look at
+// O_CLOEXEC instead of FD_CLOEXEC may be tricked in thinking that the file
+// descriptor will or won't be closed on execve().
+// Since we have to account for buggy userland (see crbug.com/237283), we will
+// open(2) the file with O_CLOEXEC in the broker process if necessary, in
+// addition to calling recvmsg(2) with MSG_CMSG_CLOEXEC.
+static const int kCurrentProcessOpenFlagsMask = O_CLOEXEC;
+
+// Check whether |requested_filename| is in |allowed_file_names|.
+// See GetFileNameIfAllowedToOpen() for an explanation of |file_to_open|.
+// async signal safe if |file_to_open| is NULL.
+// TODO(jln): assert signal safety.
+bool GetFileNameInWhitelist(const std::vector<std::string>& allowed_file_names,
+ const char* requested_filename,
+ const char** file_to_open) {
+ if (file_to_open && *file_to_open) {
+ // Make sure that callers never pass a non-empty string. In case callers
+ // wrongly forget to check the return value and look at the string
+ // instead, this could catch bugs.
+ RAW_LOG(FATAL, "*file_to_open should be NULL");
+ return false;
+ }
+
+ // Look for |requested_filename| in |allowed_file_names|.
+ // We don't use ::find() because it takes a std::string and
+ // the conversion allocates memory.
+ std::vector<std::string>::const_iterator it;
+ for (it = allowed_file_names.begin(); it != allowed_file_names.end(); it++) {
+ if (strcmp(requested_filename, it->c_str()) == 0) {
+ if (file_to_open)
+ *file_to_open = it->c_str();
+ return true;
+ }
+ }
+ return false;
+}
+
+// We maintain a list of flags that have been reviewed for "sanity" and that
+// we're ok to allow in the broker.
+// I.e. here is where we wouldn't add O_RESET_FILE_SYSTEM.
+bool IsAllowedOpenFlags(int flags) {
+ // First, check the access mode
+ const int access_mode = flags & O_ACCMODE;
+ if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
+ access_mode != O_RDWR) {
+ return false;
+ }
+
+ // We only support a 2-parameters open, so we forbid O_CREAT.
+ if (flags & O_CREAT) {
+ return false;
+ }
+
+ // Some flags affect the behavior of the current process. We don't support
+ // them and don't allow them for now.
+ if (flags & kCurrentProcessOpenFlagsMask) {
+ // We make an exception for O_CLOEXEC. Buggy userland could check for
+ // O_CLOEXEC and the only way to set it is to originally open with this
+ // flag. See the comment around kCurrentProcessOpenFlagsMask.
+ if (!(flags & O_CLOEXEC))
+ return false;
+ }
+
+ // Now check that all the flags are known to us.
+ const int creation_and_status_flags = flags & ~O_ACCMODE;
+
+ const int known_flags =
+ O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT |
+ O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME | O_NOCTTY |
+ O_NOFOLLOW | O_NONBLOCK | O_NDELAY | O_SYNC | O_TRUNC;
+
+ const int unknown_flags = ~known_flags;
+ const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
+ return !has_unknown_flags;
+}
+
+} // namespace
+
+namespace sandbox {
+
+BrokerProcess::BrokerProcess(const std::vector<std::string>& allowed_r_files,
+ const std::vector<std::string>& allowed_w_files,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : initialized_(false),
+ is_child_(false),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests),
+ broker_pid_(-1),
+ allowed_r_files_(allowed_r_files),
+ allowed_w_files_(allowed_w_files),
+ ipc_socketpair_(-1) {
+}
+
+BrokerProcess::~BrokerProcess() {
+ if (initialized_ && ipc_socketpair_ != -1) {
+ void (HANDLE_EINTR(close(ipc_socketpair_)));
+ }
+}
+
+bool BrokerProcess::Init(bool (*sandbox_callback)(void)) {
+ CHECK(!initialized_);
+ int socket_pair[2];
+ // Use SOCK_SEQPACKET, because we need to preserve message boundaries
+ // but we also want to be notified (recvmsg should return and not block)
+ // when the connection has been broken (one of the processes died).
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair)) {
+ LOG(ERROR) << "Failed to create socketpair";
+ return false;
+ }
+
+ int child_pid = fork();
+ if (child_pid == -1) {
+ (void) HANDLE_EINTR(close(socket_pair[0]));
+ (void) HANDLE_EINTR(close(socket_pair[1]));
+ return false;
+ }
+ if (child_pid) {
+ // We are the parent and we have just forked our broker process.
+ (void) HANDLE_EINTR(close(socket_pair[0]));
+ // We should only be able to write to the IPC channel. We'll always send
+ // a new file descriptor to receive the reply on.
+ shutdown(socket_pair[1], SHUT_RD);
+ ipc_socketpair_ = socket_pair[1];
+ is_child_ = false;
+ broker_pid_ = child_pid;
+ initialized_ = true;
+ return true;
+ } else {
+ // We are the broker.
+ (void) HANDLE_EINTR(close(socket_pair[1]));
+ // We should only be able to read from this IPC channel. We will send our
+ // replies on a new file descriptor attached to the requests.
+ shutdown(socket_pair[0], SHUT_WR);
+ ipc_socketpair_ = socket_pair[0];
+ is_child_ = true;
+ // Enable the sandbox if provided.
+ if (sandbox_callback) {
+ CHECK(sandbox_callback());
+ }
+ initialized_ = true;
+ for (;;) {
+ HandleRequest();
+ }
+ _exit(1);
+ }
+ NOTREACHED();
+}
+
+int BrokerProcess::Access(const char* pathname, int mode) const {
+ return PathAndFlagsSyscall(kCommandAccess, pathname, mode);
+}
+
+int BrokerProcess::Open(const char* pathname, int flags) const {
+ return PathAndFlagsSyscall(kCommandOpen, pathname, flags);
+}
+
+// Make a remote system call over IPC for syscalls that take a path and flags
+// as arguments, currently open() and access().
+// Will return -errno like a real system call.
+// This function needs to be async signal safe.
+int BrokerProcess::PathAndFlagsSyscall(enum IPCCommands syscall_type,
+ const char* pathname, int flags) const {
+ int recvmsg_flags = 0;
+ RAW_CHECK(initialized_); // async signal safe CHECK().
+ RAW_CHECK(syscall_type == kCommandOpen || syscall_type == kCommandAccess);
+ if (!pathname)
+ return -EFAULT;
+
+ // For this "remote system call" to work, we need to handle any flag that
+ // cannot be sent over a Unix socket in a special way.
+ // See the comments around kCurrentProcessOpenFlagsMask.
+ if (syscall_type == kCommandOpen && (flags & kCurrentProcessOpenFlagsMask)) {
+ // This implementation only knows about O_CLOEXEC, someone needs to look at
+ // this code if other flags are added.
+ RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
+ recvmsg_flags |= MSG_CMSG_CLOEXEC;
+ }
+
+ // There is no point in forwarding a request that we know will be denied.
+ // Of course, the real security check needs to be on the other side of the
+ // IPC.
+ if (fast_check_in_client_) {
+ if (syscall_type == kCommandOpen &&
+ !GetFileNameIfAllowedToOpen(pathname, flags, NULL)) {
+ return -EPERM;
+ }
+ if (syscall_type == kCommandAccess &&
+ !GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
+ return -EPERM;
+ }
+ }
+
+ Pickle write_pickle;
+ write_pickle.WriteInt(syscall_type);
+ write_pickle.WriteString(pathname);
+ write_pickle.WriteInt(flags);
+ RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+ int returned_fd = -1;
+ uint8_t reply_buf[kMaxMessageLength];
+
+ // Send a request (in write_pickle) as well that will include a new
+ // temporary socketpair (created internally by SendRecvMsg()).
+ // Then read the reply on this new socketpair in reply_buf and put an
+ // eventual attached file descriptor in |returned_fd|.
+ ssize_t msg_len = UnixDomainSocket::SendRecvMsgWithFlags(ipc_socketpair_,
+ reply_buf,
+ sizeof(reply_buf),
+ recvmsg_flags,
+ &returned_fd,
+ write_pickle);
+ if (msg_len <= 0) {
+ if (!quiet_failures_for_tests_)
+ RAW_LOG(ERROR, "Could not make request to broker process");
+ return -ENOMEM;
+ }
+
+ Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ PickleIterator iter(read_pickle);
+ int return_value = -1;
+ // Now deserialize the return value and eventually return the file
+ // descriptor.
+ if (read_pickle.ReadInt(&iter, &return_value)) {
+ switch (syscall_type) {
+ case kCommandAccess:
+ // We should never have a fd to return.
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ case kCommandOpen:
+ if (return_value < 0) {
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ } else {
+ // We have a real file descriptor to return.
+ RAW_CHECK(returned_fd >= 0);
+ return returned_fd;
+ }
+ default:
+ RAW_LOG(ERROR, "Unsupported command");
+ return -ENOSYS;
+ }
+ } else {
+ RAW_LOG(ERROR, "Could not read pickle");
+ NOTREACHED();
+ return -EPERM;
+ }
+}
+
+// Handle a request on the IPC channel ipc_socketpair_.
+// A request should have a file descriptor attached on which we will reply and
+// 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;
+ char buf[kMaxMessageLength];
+ errno = 0;
+ const ssize_t msg_len = UnixDomainSocket::RecvMsg(ipc_socketpair_, buf,
+ sizeof(buf), &fds);
+
+ if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
+ // EOF from our parent, or our parent died, we should die.
+ _exit(0);
+ }
+
+ // 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) {
+ PLOG(ERROR) << "Error reading message from the client";
+ return false;
+ }
+
+ const int temporary_ipc = fds.at(0);
+
+ Pickle pickle(buf, msg_len);
+ PickleIterator iter(pickle);
+ int command_type;
+ if (pickle.ReadInt(&iter, &command_type)) {
+ bool r = false;
+ // Go through all the possible IPC messages.
+ switch (command_type) {
+ case kCommandAccess:
+ 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);
+ break;
+ default:
+ NOTREACHED();
+ r = false;
+ break;
+ }
+ int ret = HANDLE_EINTR(close(temporary_ipc));
+ DCHECK(!ret) << "Could not close temporary IPC channel";
+ return r;
+ }
+
+ LOG(ERROR) << "Error parsing IPC request";
+ return false;
+}
+
+// Handle a |command_type| request contained in |read_pickle| and send the reply
+// on |reply_ipc|.
+// Currently kCommandOpen and kCommandAccess are supported.
+bool BrokerProcess::HandleRemoteCommand(IPCCommands command_type, int reply_ipc,
+ const Pickle& read_pickle,
+ PickleIterator iter) const {
+ // Currently all commands have two arguments: filename and flags.
+ std::string requested_filename;
+ int flags = 0;
+ if (!read_pickle.ReadString(&iter, &requested_filename) ||
+ !read_pickle.ReadInt(&iter, &flags)) {
+ return -1;
+ }
+
+ Pickle write_pickle;
+ std::vector<int> opened_files;
+
+ switch (command_type) {
+ case kCommandAccess:
+ AccessFileForIPC(requested_filename, flags, &write_pickle);
+ break;
+ case kCommandOpen:
+ OpenFileForIPC(requested_filename, flags, &write_pickle, &opened_files);
+ break;
+ default:
+ LOG(ERROR) << "Invalid IPC command";
+ break;
+ }
+
+ CHECK_LE(write_pickle.size(), kMaxMessageLength);
+ ssize_t sent = UnixDomainSocket::SendMsg(reply_ipc, write_pickle.data(),
+ write_pickle.size(), opened_files);
+
+ // Close anything we have opened in this process.
+ for (std::vector<int>::iterator it = opened_files.begin();
+ it < opened_files.end(); ++it) {
+ int ret = HANDLE_EINTR(close(*it));
+ DCHECK(!ret) << "Could not close file descriptor";
+ }
+
+ if (sent <= 0) {
+ LOG(ERROR) << "Could not send IPC reply";
+ return false;
+ }
+ return true;
+}
+
+// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
+// policy. Write the syscall return value (-errno) to |write_pickle|.
+void BrokerProcess::AccessFileForIPC(const std::string& requested_filename,
+ int mode, Pickle* write_pickle) const {
+ DCHECK(write_pickle);
+ const char* file_to_access = NULL;
+ const bool safe_to_access_file = GetFileNameIfAllowedToAccess(
+ requested_filename.c_str(), mode, &file_to_access);
+
+ if (safe_to_access_file) {
+ CHECK(file_to_access);
+ int access_ret = access(file_to_access, mode);
+ int access_errno = errno;
+ if (!access_ret)
+ write_pickle->WriteInt(0);
+ else
+ write_pickle->WriteInt(-access_errno);
+ } else {
+ write_pickle->WriteInt(-EPERM);
+ }
+}
+
+// Open |requested_filename| with |flags| if allowed by our policy.
+// Write the syscall return value (-errno) to |write_pickle| and append
+// a file descriptor to |opened_files| if relevant.
+void BrokerProcess::OpenFileForIPC(const std::string& requested_filename,
+ int flags, Pickle* write_pickle,
+ std::vector<int>* opened_files) const {
+ DCHECK(write_pickle);
+ DCHECK(opened_files);
+ const char* file_to_open = NULL;
+ const bool safe_to_open_file = GetFileNameIfAllowedToOpen(
+ requested_filename.c_str(), flags, &file_to_open);
+
+ 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);
+ if (opened_fd < 0) {
+ write_pickle->WriteInt(-errno);
+ } else {
+ // Success.
+ opened_files->push_back(opened_fd);
+ write_pickle->WriteInt(0);
+ }
+ } else {
+ write_pickle->WriteInt(-EPERM);
+ }
+}
+
+
+// Check if calling access() should be allowed on |requested_filename| with
+// mode |requested_mode|.
+// Note: access() being a system call to check permissions, this can get a bit
+// confusing. We're checking if calling access() should even be allowed with
+// the same policy we would use for open().
+// If |file_to_access| is not NULL, we will return the matching pointer from
+// the whitelist. For paranoia a caller should then use |file_to_access|. See
+// GetFileNameIfAllowedToOpen() fore more explanation.
+// return true if calling access() on this file should be allowed, false
+// otherwise.
+// Async signal safe if and only if |file_to_access| is NULL.
+bool BrokerProcess::GetFileNameIfAllowedToAccess(const char* requested_filename,
+ int requested_mode, const char** file_to_access) const {
+ // First, check if |requested_mode| is existence, ability to read or ability
+ // to write. We do not support X_OK.
+ if (requested_mode != F_OK &&
+ requested_mode & ~(R_OK | W_OK)) {
+ return false;
+ }
+ switch (requested_mode) {
+ case F_OK:
+ // We allow to check for file existence if we can either read or write.
+ return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+ file_to_access) ||
+ GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_access);
+ case R_OK:
+ return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+ file_to_access);
+ case W_OK:
+ return GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_access);
+ case R_OK | W_OK:
+ {
+ bool allowed_for_read_and_write =
+ GetFileNameInWhitelist(allowed_r_files_, requested_filename, NULL) &&
+ GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_access);
+ return allowed_for_read_and_write;
+ }
+ default:
+ return false;
+ }
+}
+
+// Check if |requested_filename| can be opened with flags |requested_flags|.
+// If |file_to_open| is not NULL, we will return the matching pointer from the
+// whitelist. For paranoia, a caller should then use |file_to_open| rather
+// than |requested_filename|, so that it never attempts to open an
+// attacker-controlled file name, even if an attacker managed to fool the
+// string comparison mechanism.
+// Return true if opening should be allowed, false otherwise.
+// Async signal safe if and only if |file_to_open| is NULL.
+bool BrokerProcess::GetFileNameIfAllowedToOpen(const char* requested_filename,
+ int requested_flags, const char** file_to_open) const {
+ if (!IsAllowedOpenFlags(requested_flags)) {
+ return false;
+ }
+ switch (requested_flags & O_ACCMODE) {
+ case O_RDONLY:
+ return GetFileNameInWhitelist(allowed_r_files_, requested_filename,
+ file_to_open);
+ case O_WRONLY:
+ return GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_open);
+ case O_RDWR:
+ {
+ bool allowed_for_read_and_write =
+ GetFileNameInWhitelist(allowed_r_files_, requested_filename, NULL) &&
+ GetFileNameInWhitelist(allowed_w_files_, requested_filename,
+ file_to_open);
+ return allowed_for_read_and_write;
+ }
+ default:
+ return false;
+ }
+}
+
+} // namespace sandbox.
diff --git a/chromium/sandbox/linux/services/broker_process.h b/chromium/sandbox/linux/services/broker_process.h
new file mode 100644
index 00000000000..901ae5077c0
--- /dev/null
+++ b/chromium/sandbox/linux/services/broker_process.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+#include "base/process/process.h"
+
+namespace sandbox {
+
+// Create a new "broker" process to which we can send requests via an IPC
+// channel.
+// This is a low level IPC mechanism that is suitable to be called from a
+// signal handler.
+// A process would typically create a broker process before entering
+// sandboxing.
+// 1. BrokerProcess open_broker(read_whitelist, write_whitelist);
+// 2. CHECK(open_broker.Init(NULL));
+// 3. Enable sandbox.
+// 4. Use open_broker.Open() to open files.
+class BrokerProcess {
+ public:
+ // |allowed_file_names| is a white list of files that can be opened later via
+ // the Open() API.
+ // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
+ // unit tests, don't use it.
+ explicit BrokerProcess(const std::vector<std::string>& allowed_r_files_,
+ const std::vector<std::string>& allowed_w_files_,
+ bool fast_check_in_client = true,
+ bool quiet_failures_for_tests = false);
+ ~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));
+
+ // Can be used in place of access(). Will be async signal safe.
+ // X_OK will always EPERM in practice since the broker process doesn't support
+ // execute permissions.
+ // It's similar to the access() system call and will return -errno on errors.
+ int Access(const char* pathname, int mode) const;
+ // Can be used in place of open(). Will be async signal safe.
+ // The implementation only supports certain white listed flags and will
+ // return -EPERM on other flags.
+ // It's similar to the open() system call and will return -errno on errors.
+ int Open(const char* pathname, int flags) const;
+
+ int broker_pid() const { return broker_pid_; }
+
+ private:
+ enum IPCCommands {
+ kCommandInvalid = 0,
+ kCommandOpen,
+ kCommandAccess,
+ };
+ int PathAndFlagsSyscall(enum IPCCommands command_type,
+ const char* pathname, int flags) const;
+ bool HandleRequest() const;
+ bool HandleRemoteCommand(IPCCommands command_type, int reply_ipc,
+ const Pickle& read_pickle, PickleIterator iter) const;
+
+ void AccessFileForIPC(const std::string& requested_filename,
+ int mode, Pickle* write_pickle) const;
+ void OpenFileForIPC(const std::string& requested_filename,
+ int flags, Pickle* write_pickle,
+ std::vector<int>* opened_files) const;
+ bool GetFileNameIfAllowedToAccess(const char*, int, const char**) const;
+ bool GetFileNameIfAllowedToOpen(const char*, int, const char**) const;
+ bool initialized_; // Whether we've been through Init() yet.
+ bool is_child_; // Whether we're the child (broker process).
+ bool fast_check_in_client_; // Whether to forward a request that we know
+ // will be denied to the broker.
+ bool quiet_failures_for_tests_; // Disable certain error message when
+ // testing for failures.
+ pid_t broker_pid_; // The PID of the broker (child).
+ const std::vector<std::string> allowed_r_files_; // Files allowed for read.
+ const std::vector<std::string> allowed_w_files_; // Files allowed for write.
+ int ipc_socketpair_; // Our communication channel to parent or child.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerProcess);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
diff --git a/chromium/sandbox/linux/services/broker_process_unittest.cc b/chromium/sandbox/linux/services/broker_process_unittest.cc
new file mode 100644
index 00000000000..c0393ed2f50
--- /dev/null
+++ b/chromium/sandbox/linux/services/broker_process_unittest.cc
@@ -0,0 +1,438 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/broker_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+// Creates and open a temporary file on creation and closes
+// and removes it on destruction.
+// Unlike base/ helpers, this does not require JNI on Android.
+class ScopedTemporaryFile {
+ public:
+ ScopedTemporaryFile()
+ : fd_(-1) {
+#if defined(OS_ANDROID)
+ static const char file_template[] = "/data/local/tmp/ScopedTempFileXXXXXX";
+#else
+ static const char file_template[] = "/tmp/ScopedTempFileXXXXXX";
+#endif // defined(OS_ANDROID)
+ COMPILE_ASSERT(sizeof(full_file_name_) >= sizeof(file_template),
+ full_file_name_is_large_enough);
+ memcpy(full_file_name_, file_template, sizeof(file_template));
+ fd_ = mkstemp(full_file_name_);
+ CHECK_LE(0, fd_);
+ }
+ ~ScopedTemporaryFile() {
+ CHECK_EQ(0, unlink(full_file_name_));
+ CHECK_EQ(0, HANDLE_EINTR(close(fd_)));
+ }
+
+ int fd() const { return fd_; }
+ const char* full_file_name() const { return full_file_name_; }
+
+ private:
+ int fd_;
+ char full_file_name_[128];
+ DISALLOW_COPY_AND_ASSIGN(ScopedTemporaryFile);
+};
+
+} // namespace
+
+#if defined(OS_ANDROID)
+ #define DISABLE_ON_ANDROID(function) DISABLED_##function
+#else
+ #define DISABLE_ON_ANDROID(function) function
+#endif
+
+TEST(BrokerProcess, CreateAndDestroy) {
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back("/proc/cpuinfo");
+
+ BrokerProcess* open_broker = new BrokerProcess(read_whitelist,
+ std::vector<std::string>());
+ ASSERT_TRUE(open_broker->Init(NULL));
+ pid_t broker_pid = open_broker->broker_pid();
+ delete(open_broker);
+
+ // Now we check that the broker has exited properly.
+ int status = 0;
+ ASSERT_EQ(waitpid(broker_pid, &status, 0), broker_pid);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 0);
+}
+
+TEST(BrokerProcess, TestOpenAccessNull) {
+ const std::vector<std::string> empty;
+ BrokerProcess open_broker(empty, empty);
+ ASSERT_TRUE(open_broker.Init(NULL));
+
+ int fd = open_broker.Open(NULL, O_RDONLY);
+ ASSERT_EQ(fd, -EFAULT);
+
+ int ret = open_broker.Access(NULL, F_OK);
+ ASSERT_EQ(ret, -EFAULT);
+}
+
+void TestOpenFilePerms(bool fast_check_in_client) {
+ const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1";
+ // We can't debug the init process, and shouldn't be able to access
+ // its auxv file.
+ const char kR_WhiteListedButDenied[] = "/proc/1/auxv";
+ const char kW_WhiteListed[] = "/proc/DOESNOTEXIST2";
+ const char kRW_WhiteListed[] = "/proc/DOESNOTEXIST3";
+ const char k_NotWhitelisted[] = "/proc/DOESNOTEXIST4";
+
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back(kR_WhiteListed);
+ read_whitelist.push_back(kR_WhiteListedButDenied);
+ read_whitelist.push_back(kRW_WhiteListed);
+
+ std::vector<std::string> write_whitelist;
+ write_whitelist.push_back(kW_WhiteListed);
+ write_whitelist.push_back(kRW_WhiteListed);
+
+ BrokerProcess open_broker(read_whitelist,
+ write_whitelist,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(NULL));
+
+ int fd = -1;
+ fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kR_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -EPERM);
+ fd = open_broker.Open(kR_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -EPERM);
+ int ret = -1;
+ ret = open_broker.Access(kR_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kR_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -EPERM);
+
+ // Android sometimes runs tests as root.
+ // This part of the test requires a process that doesn't have
+ // CAP_DAC_OVERRIDE. We check against a root euid as a proxy for that.
+ if (geteuid()) {
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDONLY);
+ // The broker process will allow this, but the normal permission system
+ // won't.
+ ASSERT_EQ(fd, -EACCES);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_WRONLY);
+ ASSERT_EQ(fd, -EPERM);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDWR);
+ ASSERT_EQ(fd, -EPERM);
+ ret = open_broker.Access(kR_WhiteListedButDenied, F_OK);
+ // The normal permission system will let us check that the file exists.
+ ASSERT_EQ(ret, 0);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK);
+ ASSERT_EQ(ret, -EACCES);
+ ret = open_broker.Access(kR_WhiteListedButDenied, W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kR_WhiteListedButDenied, X_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | X_OK);
+ ASSERT_EQ(ret, -EPERM);
+ }
+
+ fd = open_broker.Open(kW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+ fd = open_broker.Open(kW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -EPERM);
+ ret = open_broker.Access(kW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -EPERM);
+
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kRW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kRW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -EPERM);
+
+ fd = open_broker.Open(k_NotWhitelisted, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+ fd = open_broker.Open(k_NotWhitelisted, O_WRONLY);
+ ASSERT_EQ(fd, -EPERM);
+ fd = open_broker.Open(k_NotWhitelisted, O_RDWR);
+ ASSERT_EQ(fd, -EPERM);
+ ret = open_broker.Access(k_NotWhitelisted, F_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(k_NotWhitelisted, W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | W_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(k_NotWhitelisted, X_OK);
+ ASSERT_EQ(ret, -EPERM);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | X_OK);
+ ASSERT_EQ(ret, -EPERM);
+
+
+ // We have some extra sanity check for clearly wrong values.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY|O_WRONLY|O_RDWR);
+ ASSERT_EQ(fd, -EPERM);
+
+ // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
+ // is denied.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR|O_CREAT);
+ ASSERT_EQ(fd, -EPERM);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenFilePermsWithClientCheck) {
+ TestOpenFilePerms(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) {
+ TestOpenFilePerms(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+
+void TestOpenCpuinfo(bool fast_check_in_client) {
+ const char kFileCpuInfo[] = "/proc/cpuinfo";
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back(kFileCpuInfo);
+
+ BrokerProcess* open_broker = new BrokerProcess(read_whitelist,
+ std::vector<std::string>(),
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker->Init(NULL));
+ pid_t broker_pid = open_broker->broker_pid();
+
+ int fd = -1;
+ fd = open_broker->Open(kFileCpuInfo, O_RDWR);
+ ASSERT_EQ(fd, -EPERM);
+
+ // Check we can read /proc/cpuinfo.
+ int can_access = open_broker->Access(kFileCpuInfo, R_OK);
+ ASSERT_EQ(can_access, 0);
+ can_access = open_broker->Access(kFileCpuInfo, W_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ // Check we can not write /proc/cpuinfo.
+
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ ASSERT_GE(cpuinfo_fd, 0);
+ char buf[3];
+ memset(buf, 0, sizeof(buf));
+ int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
+ ASSERT_GT(read_len1, 0);
+
+ // Open cpuinfo directly.
+ int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+ ASSERT_GE(cpuinfo_fd2, 0);
+ char buf2[3];
+ memset(buf2, 1, sizeof(buf2));
+ int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
+ ASSERT_GT(read_len1, 0);
+
+ // The following is not guaranteed true, but will be in practice.
+ ASSERT_EQ(read_len1, read_len2);
+ // Compare the cpuinfo as returned by the broker with the one we opened
+ // ourselves.
+ ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
+
+ if (fd >= 0)
+ close(fd);
+ if (cpuinfo_fd >= 0)
+ close(cpuinfo_fd);
+ if (cpuinfo_fd2 >= 0)
+ close(cpuinfo_fd);
+
+ delete(open_broker);
+
+ // 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);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
+ TestOpenCpuinfo(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
+ TestOpenCpuinfo(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenFileRW) {
+ ScopedTemporaryFile tempfile;
+ const char* tempfile_name = tempfile.full_file_name();
+
+ std::vector<std::string> whitelist;
+ whitelist.push_back(tempfile_name);
+
+ BrokerProcess open_broker(whitelist, whitelist);
+ ASSERT_TRUE(open_broker.Init(NULL));
+
+ // Check we can access that file with read or write.
+ int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
+ ASSERT_EQ(can_access, 0);
+
+ int tempfile2 = -1;
+ tempfile2 = open_broker.Open(tempfile_name, O_RDWR);
+ ASSERT_GE(tempfile2, 0);
+
+ // Write to the descriptor opened by the broker.
+ char test_text[] = "TESTTESTTEST";
+ ssize_t len = write(tempfile2, test_text, sizeof(test_text));
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+
+ // Read back from the original file descriptor what we wrote through
+ // the descriptor provided by the broker.
+ char buf[1024];
+ len = read(tempfile.fd(), buf, sizeof(buf));
+
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(test_text)));
+ ASSERT_EQ(memcmp(test_text, buf, sizeof(test_text)), 0);
+
+ ASSERT_EQ(close(tempfile2), 0);
+}
+
+// Sandbox test because we could get a SIGPIPE.
+SANDBOX_TEST(BrokerProcess, BrokerDied) {
+ std::vector<std::string> read_whitelist;
+ read_whitelist.push_back("/proc/cpuinfo");
+
+ BrokerProcess open_broker(read_whitelist,
+ 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(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);
+ // Hopefully 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);
+}
+
+void TestOpenComplexFlags(bool fast_check_in_client) {
+ const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<std::string> whitelist;
+ whitelist.push_back(kCpuInfo);
+
+ BrokerProcess open_broker(whitelist,
+ whitelist,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(NULL));
+ // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
+ int fd = -1;
+ int ret = 0;
+ fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ // The descriptor shouldn't have the O_CLOEXEC attribute, nor O_NONBLOCK.
+ ASSERT_EQ(0, ret & (O_CLOEXEC | O_NONBLOCK));
+ ASSERT_EQ(0, close(fd));
+
+ fd = open_broker.Open(kCpuInfo, O_RDONLY | O_CLOEXEC);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFD);
+ ASSERT_NE(-1, ret);
+ // Important: use F_GETFD, not F_GETFL. The O_CLOEXEC flag in F_GETFL
+ // is actually not used by the kernel.
+ ASSERT_TRUE(FD_CLOEXEC & ret);
+
+ // There is buggy userland code that can check for O_CLOEXEC with fcntl(2)
+ // even though it doesn't mean anything. We need to support this case.
+ // See crbug.com/237283.
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ ASSERT_TRUE(O_CLOEXEC & ret);
+
+ ASSERT_EQ(0, close(fd));
+
+ fd = open_broker.Open(kCpuInfo, O_RDONLY | O_NONBLOCK);
+ ASSERT_GE(fd, 0);
+ ret = fcntl(fd, F_GETFL);
+ ASSERT_NE(-1, ret);
+ ASSERT_TRUE(O_NONBLOCK & ret);
+ ASSERT_EQ(0, close(fd));
+}
+
+TEST(BrokerProcess, OpenComplexFlagsWithClientCheck) {
+ TestOpenComplexFlags(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenComplexFlagsNoClientCheck) {
+ TestOpenComplexFlags(false /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/services/libc_urandom_override.cc b/chromium/sandbox/linux/services/libc_urandom_override.cc
new file mode 100644
index 00000000000..c5c49bac8ca
--- /dev/null
+++ b/chromium/sandbox/linux/services/libc_urandom_override.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/libc_urandom_override.h"
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/rand_util.h"
+
+// Note: this file is used by the zygote and nacl_helper.
+
+#if !defined(HAVE_XSTAT) && defined(LIBC_GLIBC)
+#define HAVE_XSTAT 1
+#endif
+
+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)
+static void InitLibcFileIOFunctions();
+static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
+#endif
+
+void InitLibcUrandomOverrides() {
+ // Make sure /dev/urandom is open.
+ base::GetUrandomFD();
+ g_override_urandom = true;
+
+#if !defined(ADDRESS_SANITIZER)
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+#endif
+}
+
+#if !defined(ADDRESS_SANITIZER)
+
+static const char kUrandomDevPath[] = "/dev/urandom";
+
+typedef FILE* (*FopenFunction)(const char* path, const char* mode);
+
+static FopenFunction g_libc_fopen = NULL;
+static FopenFunction g_libc_fopen64 = NULL;
+
+#if HAVE_XSTAT
+typedef int (*XstatFunction)(int version, const char *path, struct stat *buf);
+typedef int (*Xstat64Function)(int version, const char *path,
+ struct stat64 *buf);
+
+static XstatFunction g_libc_xstat = NULL;
+static Xstat64Function g_libc_xstat64 = NULL;
+#else
+typedef int (*StatFunction)(const char *path, struct stat *buf);
+typedef int (*Stat64Function)(const char *path, struct stat64 *buf);
+
+static StatFunction g_libc_stat = NULL;
+static Stat64Function g_libc_stat64 = NULL;
+#endif // HAVE_XSTAT
+
+// Find the libc's real fopen* and *stat* functions. This should only be
+// called once, and should be guarded by g_libc_file_io_funcs_guard.
+static void InitLibcFileIOFunctions() {
+ g_libc_fopen = reinterpret_cast<FopenFunction>(
+ dlsym(RTLD_NEXT, "fopen"));
+ g_libc_fopen64 = reinterpret_cast<FopenFunction>(
+ dlsym(RTLD_NEXT, "fopen64"));
+
+ if (!g_libc_fopen) {
+ LOG(FATAL) << "Failed to get fopen() from libc.";
+ } else if (!g_libc_fopen64) {
+#if !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
+ LOG(WARNING) << "Failed to get fopen64() from libc. Using fopen() instead.";
+#endif // !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
+ g_libc_fopen64 = g_libc_fopen;
+ }
+
+#if HAVE_XSTAT
+ g_libc_xstat = reinterpret_cast<XstatFunction>(
+ dlsym(RTLD_NEXT, "__xstat"));
+ g_libc_xstat64 = reinterpret_cast<Xstat64Function>(
+ dlsym(RTLD_NEXT, "__xstat64"));
+
+ if (!g_libc_xstat) {
+ LOG(FATAL) << "Failed to get __xstat() from libc.";
+ }
+ if (!g_libc_xstat64) {
+ LOG(FATAL) << "Failed to get __xstat64() from libc.";
+ }
+#else
+ g_libc_stat = reinterpret_cast<StatFunction>(
+ dlsym(RTLD_NEXT, "stat"));
+ g_libc_stat64 = reinterpret_cast<Stat64Function>(
+ dlsym(RTLD_NEXT, "stat64"));
+
+ if (!g_libc_stat) {
+ LOG(FATAL) << "Failed to get stat() from libc.";
+ }
+ if (!g_libc_stat64) {
+ LOG(FATAL) << "Failed to get stat64() from libc.";
+ }
+#endif // HAVE_XSTAT
+}
+
+// fopen() and fopen64() are intercepted here so that NSS can open
+// /dev/urandom to seed its random number generator. NSS is used by
+// remoting in the sendbox.
+
+// fopen() call may be redirected to fopen64() in stdio.h using
+// __REDIRECT(), which sets asm name for fopen() to "fopen64". This
+// means that we cannot override fopen() directly here. Instead the
+// the code below defines fopen_override() function with asm name
+// "fopen", so that all references to fopen() will resolve to this
+// function.
+__attribute__ ((__visibility__("default")))
+FILE* fopen_override(const char* path, const char* mode) __asm__ ("fopen");
+
+__attribute__ ((__visibility__("default")))
+FILE* fopen_override(const char* path, const char* mode) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
+ if (fd < 0) {
+ PLOG(ERROR) << "dup() failed.";
+ return NULL;
+ }
+ return fdopen(fd, mode);
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_fopen(path, mode);
+ }
+}
+
+__attribute__ ((__visibility__("default")))
+FILE* fopen64(const char* path, const char* mode) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int fd = HANDLE_EINTR(dup(base::GetUrandomFD()));
+ if (fd < 0) {
+ PLOG(ERROR) << "dup() failed.";
+ return NULL;
+ }
+ return fdopen(fd, mode);
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_fopen64(path, mode);
+ }
+}
+
+// The stat() family of functions are subject to the same problem as
+// fopen(), so we have to use the same trick to override them.
+
+#if HAVE_XSTAT
+
+__attribute__ ((__visibility__("default")))
+int xstat_override(int version,
+ const char *path,
+ struct stat *buf) __asm__ ("__xstat");
+
+__attribute__ ((__visibility__("default")))
+int xstat_override(int version, const char *path, struct stat *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = __fxstat(version, base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_xstat(version, path, buf);
+ }
+}
+
+__attribute__ ((__visibility__("default")))
+int xstat64_override(int version,
+ const char *path,
+ struct stat64 *buf) __asm__ ("__xstat64");
+
+__attribute__ ((__visibility__("default")))
+int xstat64_override(int version, const char *path, struct stat64 *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = __fxstat64(version, base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_xstat64(version, path, buf);
+ }
+}
+
+#else
+
+__attribute__ ((__visibility__("default")))
+int stat_override(const char *path,
+ struct stat *buf) __asm__ ("stat");
+
+__attribute__ ((__visibility__("default")))
+int stat_override(const char *path, struct stat *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = fstat(base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_stat(path, buf);
+ }
+}
+
+__attribute__ ((__visibility__("default")))
+int stat64_override(const char *path,
+ struct stat64 *buf) __asm__ ("stat64");
+
+__attribute__ ((__visibility__("default")))
+int stat64_override(const char *path, struct stat64 *buf) {
+ if (g_override_urandom && strcmp(path, kUrandomDevPath) == 0) {
+ int result = fstat64(base::GetUrandomFD(), buf);
+ return result;
+ } else {
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+ return g_libc_stat64(path, buf);
+ }
+}
+
+#endif // HAVE_XSTAT
+
+#endif // !defined(ADDRESS_SANITIZER)
+
+} // namespace content
diff --git a/chromium/sandbox/linux/services/libc_urandom_override.h b/chromium/sandbox/linux/services/libc_urandom_override.h
new file mode 100644
index 00000000000..86212f8bc43
--- /dev/null
+++ b/chromium/sandbox/linux/services/libc_urandom_override.h
@@ -0,0 +1,14 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_LIBC_URANDOM_OVERRIDE_H_
+#define SANDBOX_LINUX_SERVICES_LIBC_URANDOM_OVERRIDE_H_
+
+namespace sandbox {
+
+void InitLibcUrandomOverrides();
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_LIBC_URANDOM_OVERRIDE_H_
diff --git a/chromium/sandbox/linux/services/linux_syscalls.h b/chromium/sandbox/linux/services/linux_syscalls.h
new file mode 100644
index 00000000000..77c1be8b82a
--- /dev/null
+++ b/chromium/sandbox/linux/services/linux_syscalls.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This header will be kept up to date so that we can compile system-call
+// policies even when system headers are old.
+// System call numbers are accessible through __NR_syscall_name.
+
+#ifndef SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_
+
+#if defined(__x86_64__)
+#include "sandbox/linux/services/x86_64_linux_syscalls.h"
+#endif
+
+#if defined(__i386__)
+#include "sandbox/linux/services/x86_32_linux_syscalls.h"
+#endif
+
+#if defined(__arm__) && defined(__ARM_EABI__)
+#include "sandbox/linux/services/arm_linux_syscalls.h"
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_
+
diff --git a/chromium/sandbox/linux/services/x86_32_linux_syscalls.h b/chromium/sandbox/linux/services/x86_32_linux_syscalls.h
new file mode 100644
index 00000000000..ca8ccbdc6d3
--- /dev/null
+++ b/chromium/sandbox/linux/services/x86_32_linux_syscalls.h
@@ -0,0 +1,1398 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's syscall_32.tbl.
+#ifndef SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
+
+#if !defined(__i386__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 0
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 1
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork 2
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 3
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 4
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open 5
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 6
+#endif
+
+#if !defined(__NR_waitpid)
+#define __NR_waitpid 7
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat 8
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link 9
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink 10
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 11
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 12
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time 13
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod 14
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod 15
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown 16
+#endif
+
+#if !defined(__NR_break)
+#define __NR_break 17
+#endif
+
+#if !defined(__NR_oldstat)
+#define __NR_oldstat 18
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 19
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 20
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 21
+#endif
+
+#if !defined(__NR_umount)
+#define __NR_umount 22
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 23
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 24
+#endif
+
+#if !defined(__NR_stime)
+#define __NR_stime 25
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 26
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm 27
+#endif
+
+#if !defined(__NR_oldfstat)
+#define __NR_oldfstat 28
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause 29
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime 30
+#endif
+
+#if !defined(__NR_stty)
+#define __NR_stty 31
+#endif
+
+#if !defined(__NR_gtty)
+#define __NR_gtty 32
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access 33
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice 34
+#endif
+
+#if !defined(__NR_ftime)
+#define __NR_ftime 35
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 36
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 37
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename 38
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir 39
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir 40
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 41
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe 42
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 43
+#endif
+
+#if !defined(__NR_prof)
+#define __NR_prof 44
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 45
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 46
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 47
+#endif
+
+#if !defined(__NR_signal)
+#define __NR_signal 48
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 49
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 50
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 51
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 52
+#endif
+
+#if !defined(__NR_lock)
+#define __NR_lock 53
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 54
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 55
+#endif
+
+#if !defined(__NR_mpx)
+#define __NR_mpx 56
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 57
+#endif
+
+#if !defined(__NR_ulimit)
+#define __NR_ulimit 58
+#endif
+
+#if !defined(__NR_oldolduname)
+#define __NR_oldolduname 59
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 60
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 61
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat 62
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 63
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 64
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp 65
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 66
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction 67
+#endif
+
+#if !defined(__NR_sgetmask)
+#define __NR_sgetmask 68
+#endif
+
+#if !defined(__NR_ssetmask)
+#define __NR_ssetmask 69
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 70
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 71
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend 72
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending 73
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 74
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 75
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 76
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 77
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 78
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 79
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 80
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 81
+#endif
+
+#if !defined(__NR_select)
+#define __NR_select 82
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink 83
+#endif
+
+#if !defined(__NR_oldlstat)
+#define __NR_oldlstat 84
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink 85
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib 86
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 87
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 88
+#endif
+
+#if !defined(__NR_readdir)
+#define __NR_readdir 89
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 90
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 91
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 92
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 93
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 94
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 95
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 96
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 97
+#endif
+
+#if !defined(__NR_profil)
+#define __NR_profil 98
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 99
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 100
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm 101
+#endif
+
+#if !defined(__NR_socketcall)
+#define __NR_socketcall 102
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 103
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 104
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 105
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat 106
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat 107
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 108
+#endif
+
+#if !defined(__NR_olduname)
+#define __NR_olduname 109
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl 110
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 111
+#endif
+
+#if !defined(__NR_idle)
+#define __NR_idle 112
+#endif
+
+#if !defined(__NR_vm86old)
+#define __NR_vm86old 113
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 114
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 115
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 116
+#endif
+
+#if !defined(__NR_ipc)
+#define __NR_ipc 117
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 118
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn 119
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 120
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 121
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 122
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt 123
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 124
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 125
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask 126
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module 127
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 128
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 129
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms 130
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 131
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 132
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 133
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush 134
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs 135
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 136
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall 137
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 138
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 139
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek 140
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents 141
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect 142
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 143
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 144
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 145
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 146
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 147
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 148
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl 149
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 150
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 151
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 152
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 153
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 154
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 155
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 156
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 157
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 158
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 159
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 160
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 161
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 162
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 163
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 164
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 165
+#endif
+
+#if !defined(__NR_vm86)
+#define __NR_vm86 166
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module 167
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll 168
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 169
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 170
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 171
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 172
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 173
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 174
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 175
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 176
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 177
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 178
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 179
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 180
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 181
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown 182
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 183
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 184
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 185
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 186
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 187
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg 188
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg 189
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork 190
+#endif
+
+#if !defined(__NR_ugetrlimit)
+#define __NR_ugetrlimit 191
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 192
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 193
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 194
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 195
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 196
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 197
+#endif
+
+#if !defined(__NR_lchown32)
+#define __NR_lchown32 198
+#endif
+
+#if !defined(__NR_getuid32)
+#define __NR_getuid32 199
+#endif
+
+#if !defined(__NR_getgid32)
+#define __NR_getgid32 200
+#endif
+
+#if !defined(__NR_geteuid32)
+#define __NR_geteuid32 201
+#endif
+
+#if !defined(__NR_getegid32)
+#define __NR_getegid32 202
+#endif
+
+#if !defined(__NR_setreuid32)
+#define __NR_setreuid32 203
+#endif
+
+#if !defined(__NR_setregid32)
+#define __NR_setregid32 204
+#endif
+
+#if !defined(__NR_getgroups32)
+#define __NR_getgroups32 205
+#endif
+
+#if !defined(__NR_setgroups32)
+#define __NR_setgroups32 206
+#endif
+
+#if !defined(__NR_fchown32)
+#define __NR_fchown32 207
+#endif
+
+#if !defined(__NR_setresuid32)
+#define __NR_setresuid32 208
+#endif
+
+#if !defined(__NR_getresuid32)
+#define __NR_getresuid32 209
+#endif
+
+#if !defined(__NR_setresgid32)
+#define __NR_setresgid32 210
+#endif
+
+#if !defined(__NR_getresgid32)
+#define __NR_getresgid32 211
+#endif
+
+#if !defined(__NR_chown32)
+#define __NR_chown32 212
+#endif
+
+#if !defined(__NR_setuid32)
+#define __NR_setuid32 213
+#endif
+
+#if !defined(__NR_setgid32)
+#define __NR_setgid32 214
+#endif
+
+#if !defined(__NR_setfsuid32)
+#define __NR_setfsuid32 215
+#endif
+
+#if !defined(__NR_setfsgid32)
+#define __NR_setfsgid32 216
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 217
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 218
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 219
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 220
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 221
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 224
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 225
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 226
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 227
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 228
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 229
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 230
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 231
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 232
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 233
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 234
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 235
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 236
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 237
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 238
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 239
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 240
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 241
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 242
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area 243
+#endif
+
+#if !defined(__NR_get_thread_area)
+#define __NR_get_thread_area 244
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 245
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 246
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 247
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 248
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 249
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 250
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 252
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 253
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create 254
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 255
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait 256
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 257
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 258
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 259
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 260
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 261
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 262
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 263
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 264
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 265
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 266
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 267
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 268
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 269
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 270
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes 271
+#endif
+
+#if !defined(__NR_fadvise64_64)
+#define __NR_fadvise64_64 272
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver 273
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 274
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 275
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 276
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 277
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 278
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 279
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 280
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 281
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 282
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 283
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 284
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 286
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 287
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 288
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 289
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 290
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init 291
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 292
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 293
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 294
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 295
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 296
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 297
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 298
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat 299
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 300
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 301
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 302
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 303
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 304
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 305
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 306
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 307
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 308
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 309
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 310
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 311
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 312
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 313
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 314
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 315
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 316
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 317
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 318
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 319
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 320
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd 321
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 322
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd 323
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 324
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 325
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 326
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 327
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 328
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 329
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 330
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 331
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 332
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 333
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 334
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 335
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 336
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 337
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 338
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 339
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 340
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 341
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 342
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 343
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 344
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 345
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 346
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 347
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 348
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 349
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_
+
diff --git a/chromium/sandbox/linux/services/x86_64_linux_syscalls.h b/chromium/sandbox/linux/services/x86_64_linux_syscalls.h
new file mode 100644
index 00000000000..bd30c55b1d8
--- /dev/null
+++ b/chromium/sandbox/linux/services/x86_64_linux_syscalls.h
@@ -0,0 +1,1266 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's syscall_64.tbl.
+#ifndef SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_
+
+#if !defined(__x86_64__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 0
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 1
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open 2
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 3
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat 4
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 5
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat 6
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll 7
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 8
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 9
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 10
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 11
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 12
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 13
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 14
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 15
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 16
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 17
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 18
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 19
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 20
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access 21
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe 22
+#endif
+
+#if !defined(__NR_select)
+#define __NR_select 23
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 24
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 25
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 26
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 27
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 28
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 29
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 30
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 31
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 32
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 33
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause 34
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 35
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 36
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm 37
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 38
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 39
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 40
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 41
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 42
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept 43
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 44
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 45
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 46
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 47
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 48
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 49
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 50
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 51
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 52
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 53
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 54
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 55
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 56
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork 57
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork 58
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 59
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 60
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 61
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 62
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 63
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 64
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop 65
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 66
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 67
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 68
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 69
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 70
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 71
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 72
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 73
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 74
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 75
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 76
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 77
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents 78
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 79
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 80
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 81
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename 82
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir 83
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir 84
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat 85
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link 86
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink 87
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink 88
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink 89
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod 90
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 91
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown 92
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 93
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown 94
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 95
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 96
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 97
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 98
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 99
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 100
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 101
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 102
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 103
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 104
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 105
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 106
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 107
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 108
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 109
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 110
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp 111
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 112
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 113
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 114
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 115
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 116
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 117
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 118
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 119
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 120
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 121
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 122
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 123
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 124
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 125
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 126
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 127
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 128
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 129
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 130
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 131
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime 132
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod 133
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib 134
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 135
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat 136
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 137
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 138
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs 139
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 140
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 141
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 142
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 143
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 144
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 145
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 146
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 147
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 148
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 149
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 150
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 151
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 152
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 153
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt 154
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 155
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl 156
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 157
+#endif
+
+#if !defined(__NR_arch_prctl)
+#define __NR_arch_prctl 158
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 159
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 160
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 161
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 162
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 163
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 164
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 165
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 166
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 167
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 168
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 169
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 170
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 171
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl 172
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm 173
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module 174
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 175
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 176
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms 177
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module 178
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 179
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 180
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg 181
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg 182
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall 183
+#endif
+
+#if !defined(__NR_tuxcall)
+#define __NR_tuxcall 184
+#endif
+
+#if !defined(__NR_security)
+#define __NR_security 185
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 186
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 187
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 188
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 189
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 190
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 191
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 192
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 193
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 194
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 195
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 196
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 197
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 198
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 199
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 200
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time 201
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 202
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 203
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 204
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area 205
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 206
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 207
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 208
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 209
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 210
+#endif
+
+#if !defined(__NR_get_thread_area)
+#define __NR_get_thread_area 211
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 212
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create 213
+#endif
+
+#if !defined(__NR_epoll_ctl_old)
+#define __NR_epoll_ctl_old 214
+#endif
+
+#if !defined(__NR_epoll_wait_old)
+#define __NR_epoll_wait_old 215
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 216
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 217
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 218
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 219
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop 220
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 221
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 222
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 223
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 224
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 225
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 226
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 227
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 228
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 229
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 230
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 231
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait 232
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 233
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 234
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes 235
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver 236
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 237
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 238
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 239
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 240
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 241
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 242
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 243
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 244
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 245
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 246
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 247
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 248
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 249
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 250
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 251
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 252
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init 253
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 254
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 255
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 256
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 257
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 258
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 259
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 260
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat 261
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat 262
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 263
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 264
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 265
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 266
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 267
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 268
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 269
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 270
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 271
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 272
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 273
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 274
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 275
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 276
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 277
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 278
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 279
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 280
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 281
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd 282
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 283
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd 284
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 285
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 286
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 287
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 288
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 289
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 290
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 291
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 292
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 293
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 294
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 295
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 296
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 297
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 298
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 299
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 300
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 301
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 302
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 303
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 304
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 305
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 306
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 307
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 308
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 309
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 310
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 311
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 312
+#endif
+
+#endif // SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_
+
diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc
new file mode 100644
index 00000000000..34231d47a1b
--- /dev/null
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
+#include "setuid_sandbox_client.h"
+
+namespace {
+
+// 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) {
+ env->SetVar(sandbox::kSandboxEnvironmentApiRequest,
+ base::IntToString(sandbox::kSUIDSandboxApiNumber));
+}
+
+// Wrapper around a shared C function.
+// Returns the "saved" environment variable name corresponding to |envvar|
+// in a new string or NULL.
+std::string* CreateSavedVariableName(const char* env_var) {
+ char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var);
+ if (!saved_env_var)
+ return NULL;
+ std::string* saved_env_var_copy = new std::string(saved_env_var);
+ // SandboxSavedEnvironmentVariable is the C function that we wrap and uses
+ // malloc() to allocate memory.
+ free(saved_env_var);
+ return saved_env_var_copy;
+}
+
+// The ELF loader will clear many environment variables so we save them to
+// different names here so that the SUID sandbox can resolve them for the
+// renderer.
+void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) {
+ for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+ const char* env_var = kSUIDUnsafeEnvironmentVariables[i];
+ // Get the saved environment variable corresponding to envvar.
+ scoped_ptr<std::string> saved_env_var(CreateSavedVariableName(env_var));
+ if (saved_env_var == NULL)
+ continue;
+
+ std::string value;
+ if (env->GetVar(env_var, &value))
+ env->SetVar(saved_env_var->c_str(), value);
+ else
+ env->UnSetVar(saved_env_var->c_str());
+ }
+}
+
+int GetHelperApi(base::Environment* env) {
+ std::string api_string;
+ int api_number = 0; // Assume API version 0 if no environment was found.
+ if (env->GetVar(sandbox::kSandboxEnvironmentApiProvides, &api_string) &&
+ !base::StringToInt(api_string, &api_number)) {
+ // It's an error if we could not convert the API number.
+ api_number = -1;
+ }
+ return api_number;
+}
+
+// Convert |var_name| from the environment |env| to an int.
+// Return -1 if the variable does not exist or the value cannot be converted.
+int EnvToInt(base::Environment* env, const char* var_name) {
+ std::string var_string;
+ int var_value = -1;
+ if (env->GetVar(var_name, &var_string) &&
+ !base::StringToInt(var_string, &var_value)) {
+ var_value = -1;
+ }
+ return var_value;
+}
+
+pid_t GetHelperPID(base::Environment* env) {
+ return EnvToInt(env, sandbox::kSandboxHelperPidEnvironmentVarName);
+}
+
+// Get the IPC file descriptor used to communicate with the setuid helper.
+int GetIPCDescriptor(base::Environment* env) {
+ return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName);
+}
+
+} // namespace
+
+namespace sandbox {
+
+SetuidSandboxClient* SetuidSandboxClient::Create() {
+ base::Environment* environment(base::Environment::Create());
+ SetuidSandboxClient* sandbox_client(new(SetuidSandboxClient));
+
+ CHECK(environment);
+ sandbox_client->env_ = environment;
+ return sandbox_client;
+}
+
+SetuidSandboxClient::SetuidSandboxClient()
+ : env_(NULL),
+ sandboxed_(false) {
+}
+
+SetuidSandboxClient::~SetuidSandboxClient() {
+ delete env_;
+}
+
+bool SetuidSandboxClient::ChrootMe() {
+ int ipc_fd = GetIPCDescriptor(env_);
+
+ if (ipc_fd < 0) {
+ LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor";
+ return false;
+ }
+
+ if (HANDLE_EINTR(write(ipc_fd, &kMsgChrootMe, 1)) != 1) {
+ PLOG(ERROR) << "Failed to write to chroot pipe";
+ return false;
+ }
+
+ // 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) {
+ PLOG(ERROR) << "Failed to wait for setuid helper to die";
+ return false;
+ }
+
+ char reply;
+ if (HANDLE_EINTR(read(ipc_fd, &reply, 1)) != 1) {
+ PLOG(ERROR) << "Failed to read from chroot pipe";
+ return false;
+ }
+
+ if (reply != kMsgChrootSuccessful) {
+ LOG(ERROR) << "Error code reply from chroot helper";
+ return false;
+ }
+
+ // We now consider ourselves "fully sandboxed" as far as the
+ // setuid sandbox is concerned.
+ sandboxed_ = true;
+ return true;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxUpToDate() const {
+ return GetHelperApi(env_) == kSUIDSandboxApiNumber;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxChild() const {
+ return GetIPCDescriptor(env_) >= 0;
+}
+
+bool SetuidSandboxClient::IsInNewPIDNamespace() const {
+ return env_->HasVar(kSandboxPIDNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsInNewNETNamespace() const {
+ return env_->HasVar(kSandboxNETNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsSandboxed() const {
+ return sandboxed_;
+}
+
+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
new file mode 100644
index 00000000000..a9f6536cc15
--- /dev/null
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+
+#include "base/basictypes.h"
+
+namespace base { class Environment; }
+
+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.
+//
+// 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
+// requests other sandboxing status via the status functions.
+class SetuidSandboxClient {
+ public:
+ // All instantation should go through this factory method.
+ static class SetuidSandboxClient* Create();
+ ~SetuidSandboxClient();
+
+ // 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.
+ bool ChrootMe();
+
+ // Did we get launched through an up to date setuid binary ?
+ bool IsSuidSandboxUpToDate() const;
+ // Did we get launched through the setuid helper ?
+ bool IsSuidSandboxChild() const;
+ // Did the setuid helper create a new PID namespace ?
+ bool IsInNewPIDNamespace() const;
+ // Did the setuid helper create a new network namespace ?
+ bool IsInNewNETNamespace() const;
+ // Are we done and fully sandboxed ?
+ bool IsSandboxed() const;
+
+ // Set-up the environment. This should be done prior to launching the setuid
+ // helper.
+ void SetupLaunchEnvironment();
+
+ private:
+ // Holds the environment. Will never be NULL.
+ base::Environment* env_;
+ bool sandboxed_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(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
new file mode 100644
index 00000000000..552dc4b9d9c
--- /dev/null
+++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/environment.h"
+#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/common/sandbox.h"
+#include "setuid_sandbox_client.h"
+
+namespace sandbox {
+
+TEST(SetuidSandboxClient, SetupLaunchEnvironment) {
+ const char kTestValue[] = "This is a test";
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ EXPECT_TRUE(env != NULL);
+
+ std::string saved_ld_preload;
+ bool environment_had_ld_preload;
+ // First, back-up the real LD_PRELOAD if any.
+ environment_had_ld_preload = env->GetVar("LD_PRELOAD", &saved_ld_preload);
+ // Setup environment variables to save or not save.
+ EXPECT_TRUE(env->SetVar("LD_PRELOAD", kTestValue));
+ EXPECT_TRUE(env->UnSetVar("LD_ORIGIN_PATH"));
+
+ scoped_ptr<SetuidSandboxClient>
+ sandbox_client(SetuidSandboxClient::Create());
+ EXPECT_TRUE(sandbox_client != NULL);
+
+ // Make sure the environment is clean.
+ EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiRequest));
+ EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiProvides));
+
+ sandbox_client->SetupLaunchEnvironment();
+
+ // Check if the requested API environment was set.
+ std::string api_request;
+ EXPECT_TRUE(env->GetVar(kSandboxEnvironmentApiRequest, &api_request));
+ int api_request_num;
+ EXPECT_TRUE(base::StringToInt(api_request, &api_request_num));
+ EXPECT_EQ(api_request_num, kSUIDSandboxApiNumber);
+
+ // Now check if LD_PRELOAD was saved to SANDBOX_LD_PRELOAD.
+ std::string sandbox_ld_preload;
+ EXPECT_TRUE(env->GetVar("SANDBOX_LD_PRELOAD", &sandbox_ld_preload));
+ EXPECT_EQ(sandbox_ld_preload, kTestValue);
+
+ // Check that LD_ORIGIN_PATH was not saved.
+ EXPECT_FALSE(env->HasVar("SANDBOX_LD_ORIGIN_PATH"));
+
+ // We should not forget to restore LD_PRELOAD at the end, or this environment
+ // variable will affect the next running tests!
+ if (environment_had_ld_preload) {
+ EXPECT_TRUE(env->SetVar("LD_PRELOAD", saved_ld_preload));
+ } else {
+ EXPECT_TRUE(env->UnSetVar("LD_PRELOAD"));
+ }
+}
+
+TEST(SetuidSandboxClient, SandboxedClientAPI) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ EXPECT_TRUE(env != NULL);
+
+ scoped_ptr<SetuidSandboxClient>
+ sandbox_client(SetuidSandboxClient::Create());
+ EXPECT_TRUE(sandbox_client != NULL);
+
+ // Set-up a fake environment as if we went through the setuid sandbox.
+ EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
+ base::IntToString(kSUIDSandboxApiNumber)));
+ EXPECT_TRUE(env->SetVar(kSandboxDescriptorEnvironmentVarName, "1"));
+ EXPECT_TRUE(env->SetVar(kSandboxPIDNSEnvironmentVarName, "1"));
+ EXPECT_TRUE(env->UnSetVar(kSandboxNETNSEnvironmentVarName));
+
+ // Check the API.
+ EXPECT_TRUE(sandbox_client->IsSuidSandboxUpToDate());
+ EXPECT_TRUE(sandbox_client->IsSuidSandboxChild());
+ EXPECT_TRUE(sandbox_client->IsInNewPIDNamespace());
+ EXPECT_FALSE(sandbox_client->IsInNewNETNamespace());
+
+ // Forge an incorrect API version and check.
+ EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
+ base::IntToString(kSUIDSandboxApiNumber + 1)));
+ EXPECT_FALSE(sandbox_client->IsSuidSandboxUpToDate());
+ // We didn't go through the actual sandboxing mechanism as it is
+ // very hard in a unit test.
+ EXPECT_FALSE(sandbox_client->IsSandboxed());
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/linux/suid/common/sandbox.h b/chromium/sandbox/linux/suid/common/sandbox.h
new file mode 100644
index 00000000000..aad4ff8bd30
--- /dev/null
+++ b/chromium/sandbox/linux/suid/common/sandbox.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SUID_SANDBOX_H_
+#define SANDBOX_LINUX_SUID_SANDBOX_H_
+
+#if defined(__cplusplus)
+namespace sandbox {
+#endif
+
+// These are command line switches that may be used by other programs
+// (e.g. Chrome) to construct a command line for the sandbox.
+static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score";
+#if defined(OS_CHROMEOS)
+static const char kAdjustLowMemMarginSwitch[] = "--adjust-low-mem";
+#endif
+
+static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D";
+static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID";
+
+static const long kSUIDSandboxApiNumber = 1;
+static const char kSandboxEnvironmentApiRequest[] = "SBX_CHROME_API_RQ";
+static const char kSandboxEnvironmentApiProvides[] = "SBX_CHROME_API_PRV";
+
+// This number must be kept in sync with common/zygote_commands_linux.h
+static const int kZygoteIdFd = 7;
+
+// These are the magic byte values which the sandboxed process uses to request
+// that it be chrooted.
+static const char kMsgChrootMe = 'C';
+static const char kMsgChrootSuccessful = 'O';
+
+// These are set if we have respectively switched to a new PID or NET namespace
+// by going through the setuid binary helper.
+static const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS";
+static const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
+
+#if defined(__cplusplus)
+} // namespace sandbox
+#endif
+
+#endif // SANDBOX_LINUX_SUID_SANDBOX_H_
diff --git a/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h b/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
new file mode 100644
index 00000000000..1132a7a7161
--- /dev/null
+++ b/chromium/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a list of environment variables which the ELF loader unsets when
+// loading a SUID binary. Because they are unset rather than just ignored, they
+// aren't passed to child processes of SUID processes either.
+//
+// We need to save these environment variables before running a SUID sandbox
+// and restore them before running child processes (but after dropping root).
+//
+// List gathered from glibc sources (00ebd7ed58df389a78e41dece058048725cb585e):
+// sysdeps/unix/sysv/linux/i386/dl-librecon.h
+// sysdeps/generic/unsecvars.h
+
+#ifndef SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
+#define SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
+
+#include <stdint.h>
+#include <stdlib.h> // malloc
+#include <string.h> // memcpy
+
+static const char* kSUIDUnsafeEnvironmentVariables[] = {
+ "LD_AOUT_LIBRARY_PATH",
+ "LD_AOUT_PRELOAD",
+ "GCONV_PATH",
+ "GETCONF_DIR",
+ "HOSTALIASES",
+ "LD_AUDIT",
+ "LD_DEBUG",
+ "LD_DEBUG_OUTPUT",
+ "LD_DYNAMIC_WEAK",
+ "LD_LIBRARY_PATH",
+ "LD_ORIGIN_PATH",
+ "LD_PRELOAD",
+ "LD_PROFILE",
+ "LD_SHOW_AUXV",
+ "LD_USE_LOAD_BIAS",
+ "LOCALDOMAIN",
+ "LOCPATH",
+ "MALLOC_TRACE",
+ "NIS_PATH",
+ "NLSPATH",
+ "RESOLV_HOST_CONF",
+ "RES_OPTIONS",
+ "TMPDIR",
+ "TZDIR",
+ NULL,
+};
+
+// Return a malloc allocated string containing the 'saved' environment variable
+// name for a given environment variable.
+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)
+ return NULL;
+
+ const size_t saved_envvarlen = envvar_len + 1 /* NUL terminator */ +
+ 8 /* strlen("SANDBOX_") */;
+ char* const saved_envvar = (char*) malloc(saved_envvarlen);
+ if (!saved_envvar)
+ return NULL;
+
+ memcpy(saved_envvar, "SANDBOX_", 8);
+ memcpy(saved_envvar + 8, envvar, envvar_len);
+ saved_envvar[8 + envvar_len] = 0;
+
+ return saved_envvar;
+}
+
+#endif // SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_
diff --git a/chromium/sandbox/linux/suid/linux_util.c b/chromium/sandbox/linux/suid/linux_util.c
new file mode 100644
index 00000000000..256468ff4ea
--- /dev/null
+++ b/chromium/sandbox/linux/suid/linux_util.c
@@ -0,0 +1,135 @@
+// Copyright (c) 2009 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.
+
+// 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"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// expected prefix of the target of the /proc/self/fd/%d link for a socket
+static const char kSocketLinkPrefix[] = "socket:[";
+
+// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
+// 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,
+ const char* path) {
+ // We also check that the path is relative.
+ if (!inode_out || !path || *path == '/')
+ return false;
+ char buf[256];
+ const ssize_t n = readlinkat(base_dir_fd, path, buf, sizeof(buf) - 1);
+ if (n < 0)
+ return false;
+ buf[n] = 0;
+
+ if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1))
+ return false;
+
+ char *endptr = NULL;
+ errno = 0;
+ const unsigned long long int inode_ull =
+ strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
+ if (inode_ull == ULLONG_MAX || !endptr || *endptr != ']' || errno != 0)
+ return false;
+
+ *inode_out = inode_ull;
+ return true;
+}
+
+static DIR* opendirat(int base_dir_fd, const char* name) {
+ // Also check that |name| is relative.
+ if (base_dir_fd < 0 || !name || *name == '/')
+ return NULL;
+ int new_dir_fd = openat(base_dir_fd, name, O_RDONLY | O_DIRECTORY);
+ if (new_dir_fd < 0)
+ return NULL;
+
+ return fdopendir(new_dir_fd);
+}
+
+bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) {
+ bool already_found = false;
+
+ DIR* proc = opendir("/proc");
+ if (!proc)
+ return false;
+
+ const uid_t uid = getuid();
+ struct dirent* dent;
+ while ((dent = readdir(proc))) {
+ 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)
+ continue;
+
+ // We have this setuid code here because the zygote and its children have
+ // /proc/$pid/fd owned by root. While scanning through /proc, we add this
+ // extra check so users cannot accidentally gain information about other
+ // users' processes. To determine process ownership, we use the property
+ // that if user foo owns process N, then /proc/N is owned by foo.
+ int proc_pid_fd = -1;
+ {
+ char buf[256];
+ struct stat statbuf;
+ snprintf(buf, sizeof(buf), "/proc/%lu", pid_ul);
+ proc_pid_fd = open(buf, O_RDONLY | O_DIRECTORY);
+ if (proc_pid_fd < 0)
+ continue;
+ if (fstat(proc_pid_fd, &statbuf) < 0 || uid != statbuf.st_uid) {
+ close(proc_pid_fd);
+ continue;
+ }
+ }
+
+ DIR* fd = opendirat(proc_pid_fd, "fd");
+ if (!fd) {
+ close(proc_pid_fd);
+ continue;
+ }
+
+ while ((dent = readdir(fd))) {
+ char buf[256];
+ int printed = snprintf(buf, sizeof(buf), "fd/%s", dent->d_name);
+ if (printed < 0 || printed >= (int)(sizeof(buf) - 1)) {
+ continue;
+ }
+
+ ino_t fd_inode;
+ if (ProcPathGetInodeAt(&fd_inode, proc_pid_fd, buf)) {
+ if (fd_inode == socket_inode) {
+ if (already_found) {
+ closedir(fd);
+ close(proc_pid_fd);
+ closedir(proc);
+ return false;
+ }
+
+ already_found = true;
+ *pid_out = pid_ul;
+ break;
+ }
+ }
+ }
+ closedir(fd);
+ close(proc_pid_fd);
+ }
+ closedir(proc);
+
+ return already_found;
+}
diff --git a/chromium/sandbox/linux/suid/linux_util.h b/chromium/sandbox/linux/suid/linux_util.h
new file mode 100644
index 00000000000..d06425227a9
--- /dev/null
+++ b/chromium/sandbox/linux/suid/linux_util.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is duplicated from base/linux_utils.h.
+// We shouldn't link against C++ code in a setuid binary.
+
+#ifndef SANDBOX_LINUX_SUID_LINUX_UTIL_H_
+#define SANDBOX_LINUX_SUID_LINUX_UTIL_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+static const char kFindInodeSwitch[] = "--find-inode";
+static const char kSuidSandboxGetApiSwitch[] = "--get-api";
+
+// Find the process which holds the given socket, named by inode number. If
+// multiple processes hold the socket, this function returns false.
+bool FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode);
+
+#endif // SANDBOX_LINUX_SUID_LINUX_UTIL_H_
diff --git a/chromium/sandbox/linux/suid/process_util.h b/chromium/sandbox/linux/suid/process_util.h
new file mode 100644
index 00000000000..f6b4c314e63
--- /dev/null
+++ b/chromium/sandbox/linux/suid/process_util.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following is duplicated from base/process_utils.h.
+// We shouldn't link against C++ code in a setuid binary.
+
+#ifndef SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
+#define SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
+
+#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.
+//
+// If the Linux system isn't new enough to use oom_score_adj, then we
+// 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);
+
+// 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);
+
+#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
new file mode 100644
index 00000000000..5e6b33b60f9
--- /dev/null
+++ b/chromium/sandbox/linux/suid/process_util_linux.c
@@ -0,0 +1,106 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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
+
+#include "process_util.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Ranges for the current (oom_score_adj) and previous (oom_adj)
+// flavors of OOM score.
+static const int kMaxOomScore = 1000;
+static const int kMaxOldOomScore = 15;
+
+// Kernel pseudo-file that allows setting of the low memory margin.
+static const char kLowMemMarginFile[] =
+ "/sys/kernel/mm/chromeos-low_mem/margin";
+
+// NOTE: This is not the only version of this function in the source:
+// the base library (in process_util_linux.cc) also has its own C++ version.
+bool AdjustOOMScore(pid_t process, int score) {
+ if (score < 0 || score > kMaxOomScore)
+ return false;
+
+ char oom_adj[27]; // "/proc/" + log_10(2**64) + "\0"
+ // 6 + 20 + 1 = 27
+ snprintf(oom_adj, sizeof(oom_adj), "/proc/%" PRIdMAX, (intmax_t)process);
+
+ const int dirfd = open(oom_adj, O_RDONLY | O_DIRECTORY);
+ if (dirfd < 0)
+ return false;
+
+ struct stat statbuf;
+ if (fstat(dirfd, &statbuf) < 0) {
+ close(dirfd);
+ return false;
+ }
+ if (getuid() != statbuf.st_uid) {
+ close(dirfd);
+ return false;
+ }
+
+ int fd = openat(dirfd, "oom_score_adj", O_WRONLY);
+ if (fd < 0) {
+ // We failed to open oom_score_adj, so let's try for the older
+ // oom_adj file instead.
+ fd = openat(dirfd, "oom_adj", O_WRONLY);
+ if (fd < 0) {
+ // Nope, that doesn't work either.
+ return false;
+ } else {
+ // If we're using the old oom_adj file, the allowed range is now
+ // [0, kMaxOldOomScore], so we scale the score. This may result in some
+ // aliasing of values, of course.
+ score = score * kMaxOldOomScore / kMaxOomScore;
+ }
+ }
+ close(dirfd);
+
+ char buf[11]; // 0 <= |score| <= kMaxOomScore; using log_10(2**32) + 1 size
+ snprintf(buf, sizeof(buf), "%d", score);
+ size_t len = strlen(buf);
+
+ ssize_t bytes_written = write(fd, buf, len);
+ close(fd);
+ return (bytes_written == len);
+}
+
+bool AdjustLowMemoryMargin(int64_t margin_mb) {
+ int file_descriptor = open(kLowMemMarginFile, O_WRONLY);
+ if (file_descriptor < 0)
+ return false;
+
+ // Only allow those values which are reasonable, to prevent mischief.
+ char value[21];
+ switch (margin_mb) {
+ case -1L:
+ snprintf(value, sizeof(value), "off");
+ break;
+ case 0L:
+ case 25L:
+ case 50L:
+ case 100L:
+ case 200L:
+ snprintf(value, sizeof(value), "%lld", (long long int)margin_mb);
+ break;
+ default:
+ return false;
+ }
+
+ bool success = (write(file_descriptor, value, strlen(value)) >= 0);
+ close(file_descriptor);
+ return success;
+}
diff --git a/chromium/sandbox/linux/suid/sandbox.c b/chromium/sandbox/linux/suid/sandbox.c
new file mode 100644
index 00000000000..32435a7ad89
--- /dev/null
+++ b/chromium/sandbox/linux/suid/sandbox.c
@@ -0,0 +1,482 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox
+
+#include "common/sandbox.h"
+
+#define _GNU_SOURCE
+#include <asm/unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "linux_util.h"
+#include "process_util.h"
+#include "common/suid_unsafe_environment_variables.h"
+
+#if !defined(CLONE_NEWPID)
+#define CLONE_NEWPID 0x20000000
+#endif
+#if !defined(CLONE_NEWNET)
+#define CLONE_NEWNET 0x40000000
+#endif
+
+static bool DropRoot();
+
+#define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x)
+
+static void FatalError(const char *msg, ...)
+ __attribute__((noreturn, format(printf, 1, 2)));
+
+static void FatalError(const char *msg, ...) {
+ va_list ap;
+ va_start(ap, msg);
+
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, ": %s\n", strerror(errno));
+ fflush(stderr);
+ va_end(ap);
+ _exit(1);
+}
+
+// We will chroot() to the helper's /proc/self directory. Anything there will
+// not exist anymore if we make sure to wait() for the helper.
+//
+// /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty
+// even if the helper survives as a zombie.
+//
+// There is very little reason to use fdinfo/ instead of fd/ but we are
+// paranoid. fdinfo/ only exists since 2.6.22 so we allow fallback to fd/
+#define SAFE_DIR "/proc/self/fdinfo"
+#define SAFE_DIR2 "/proc/self/fd"
+
+static bool SpawnChrootHelper() {
+ int sv[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+ perror("socketpair");
+ return false;
+ }
+
+ char *safedir = NULL;
+ struct stat sdir_stat;
+ 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;
+ }
+
+ const pid_t pid = syscall(
+ __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0);
+
+ if (pid == -1) {
+ perror("clone");
+ close(sv[0]);
+ close(sv[1]);
+ return false;
+ }
+
+ if (pid == 0) {
+ // We share our files structure with an untrusted process. As a security in
+ // depth measure, we make sure that we can't open anything by mistake.
+ // TODO(agl): drop CAP_SYS_RESOURCE / use SECURE_NOROOT
+
+ const struct rlimit nofile = {0, 0};
+ if (setrlimit(RLIMIT_NOFILE, &nofile))
+ FatalError("Setting RLIMIT_NOFILE");
+
+ if (close(sv[1]))
+ FatalError("close");
+
+ // wait for message
+ char msg;
+ ssize_t bytes;
+ do {
+ bytes = read(sv[0], &msg, 1);
+ } while (bytes == -1 && errno == EINTR);
+
+ if (bytes == 0)
+ _exit(0);
+ if (bytes != 1)
+ FatalError("read");
+
+ // do chrooting
+ if (msg != kMsgChrootMe)
+ FatalError("Unknown message from sandboxed process");
+
+ // sanity check
+ if (chdir(safedir))
+ FatalError("Cannot chdir into /proc/ directory");
+
+ if (chroot(safedir))
+ FatalError("Cannot chroot into /proc/ directory");
+
+ if (chdir("/"))
+ FatalError("Cannot chdir to / after chroot");
+
+ const char reply = kMsgChrootSuccessful;
+ do {
+ bytes = write(sv[0], &reply, 1);
+ } while (bytes == -1 && errno == EINTR);
+
+ if (bytes != 1)
+ FatalError("Writing reply");
+
+ _exit(0);
+ // We now become a zombie. /proc/self/fd(info) is now an empty dir and we
+ // are chrooted there.
+ // Our (unprivileged) parent should not even be able to open "." or "/"
+ // since they would need to pass the ptrace() check. If our parent wait()
+ // for us, our root directory will completely disappear.
+ }
+
+ if (close(sv[0])) {
+ close(sv[1]);
+ perror("close");
+ return false;
+ }
+
+ // In the parent process, we install an environment variable containing the
+ // number of the file descriptor.
+ char desc_str[64];
+ int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]);
+ if (printed < 0 || printed >= (int)sizeof(desc_str)) {
+ fprintf(stderr, "Failed to snprintf\n");
+ return false;
+ }
+
+ if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) {
+ perror("setenv");
+ close(sv[1]);
+ return false;
+ }
+
+ // We also install an environment variable containing the pid of the child
+ char helper_pid_str[64];
+ printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid);
+ if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) {
+ fprintf(stderr, "Failed to snprintf\n");
+ return false;
+ }
+
+ if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) {
+ perror("setenv");
+ close(sv[1]);
+ return false;
+ }
+
+ return true;
+}
+
+// Block until child_pid exits, then exit. Try to preserve the exit code.
+static void WaitForChildAndExit(pid_t child_pid) {
+ int exit_code = -1;
+ siginfo_t reaped_child_info;
+
+ int wait_ret =
+ 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) {
+ exit_code = reaped_child_info.si_status;
+ } else {
+ // Exit with code 0 if the child got signaled.
+ exit_code = 0;
+ }
+ }
+ _exit(exit_code);
+}
+
+static bool MoveToNewNamespaces() {
+ // These are the sets of flags which we'll try, in order.
+ 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;
+ int sync_fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+ FatalError("Failed to create a socketpair");
+ }
+
+ for (size_t i = 0;
+ i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]);
+ i++) {
+ pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0);
+
+ if (pid > 0) {
+ if (!DropRoot()) {
+ FatalError("Could not drop privileges");
+ } else {
+ if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD))
+ FatalError("Could not close socketpair");
+ // The kZygoteIdFd needs to be closed in the parent before
+ // Zygote gets started.
+ if (close(kZygoteIdFd))
+ FatalError("close");
+ // Tell our child to continue
+ if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1)
+ FatalError("send");
+ if (close(sync_fds[1]))
+ FatalError("close");
+ // We want to keep a full process tree and we don't want our childs to
+ // be reparented to (the outer PID namespace) init. So we wait for it.
+ WaitForChildAndExit(pid);
+ }
+ // NOTREACHED
+ FatalError("Not reached");
+ }
+
+ if (pid == 0) {
+ if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR))
+ FatalError("Could not close socketpair");
+
+ // Wait for the parent to confirm it closed kZygoteIdFd before we
+ // continue
+ char should_continue;
+ if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1)
+ FatalError("Read on socketpair");
+ if (close(sync_fds[0]))
+ FatalError("close");
+
+ if (kCloneExtraFlags[i] & CLONE_NEWPID) {
+ setenv(kSandboxPIDNSEnvironmentVarName, "", 1 /* overwrite */);
+ } else {
+ unsetenv(kSandboxPIDNSEnvironmentVarName);
+ }
+
+ if (kCloneExtraFlags[i] & CLONE_NEWNET) {
+ setenv(kSandboxNETNSEnvironmentVarName, "", 1 /* overwrite */);
+ } else {
+ unsetenv(kSandboxNETNSEnvironmentVarName);
+ }
+
+ break;
+ }
+
+ if (errno != EINVAL) {
+ perror("Failed to move to new PID namespace");
+ return false;
+ }
+ }
+
+ // If the system doesn't support NEWPID then we carry on anyway.
+ return true;
+}
+
+static bool DropRoot() {
+ if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) {
+ perror("prctl(PR_SET_DUMPABLE)");
+ return false;
+ }
+
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
+ perror("Still dumpable after prctl(PR_SET_DUMPABLE)");
+ return false;
+ }
+
+ gid_t rgid, egid, sgid;
+ if (getresgid(&rgid, &egid, &sgid)) {
+ perror("getresgid");
+ return false;
+ }
+
+ if (setresgid(rgid, rgid, rgid)) {
+ perror("setresgid");
+ return false;
+ }
+
+ uid_t ruid, euid, suid;
+ if (getresuid(&ruid, &euid, &suid)) {
+ perror("getresuid");
+ return false;
+ }
+
+ if (setresuid(ruid, ruid, ruid)) {
+ perror("setresuid");
+ return false;
+ }
+
+ return true;
+}
+
+static bool SetupChildEnvironment() {
+ unsigned i;
+
+ // ld.so may have cleared several environment variables because we are SUID.
+ // However, the child process might need them so zygote_host_linux.cc saves a
+ // copy in SANDBOX_$x. This is safe because we have dropped root by this
+ // point, so we can only exec a binary with the permissions of the user who
+ // ran us in the first place.
+
+ for (i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) {
+ const char* const envvar = kSUIDUnsafeEnvironmentVariables[i];
+ char* const saved_envvar = SandboxSavedEnvironmentVariable(envvar);
+ if (!saved_envvar)
+ return false;
+
+ const char* const value = getenv(saved_envvar);
+ if (value) {
+ setenv(envvar, value, 1 /* overwrite */);
+ unsetenv(saved_envvar);
+ }
+
+ free(saved_envvar);
+ }
+
+ return true;
+}
+
+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);
+ if (!api_string) {
+ api_number = 0;
+ } else {
+ errno = 0;
+ char* endptr = NULL;
+ api_number = strtol(api_string, &endptr, 10);
+ if (!endptr || *endptr || errno != 0)
+ return false;
+ }
+
+ // 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);
+ }
+
+ // 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);
+ if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) {
+ perror("setenv");
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ if (argc <= 0) {
+ return 1;
+ }
+
+ fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]);
+ return 1;
+ }
+
+ // Allow someone to query our API version
+ if (argc == 2 && 0 == strcmp(argv[1], kSuidSandboxGetApiSwitch)) {
+ printf("%ld\n", kSUIDSandboxApiNumber);
+ return 0;
+ }
+
+ // In the SUID sandbox, if we succeed in calling MoveToNewNamespaces()
+ // below, then the zygote and all the renderers are in an alternate PID
+ // namespace and do not know their real PIDs. As such, they report the wrong
+ // PIDs to the task manager.
+ //
+ // To fix this, when the zygote spawns a new renderer, it gives the renderer
+ // a dummy socket, which has a unique inode number. Then it asks the sandbox
+ // host to find the PID of the process holding that fd by searching /proc.
+ //
+ // Since the zygote and renderers are all spawned by this setuid executable,
+ // their entries in /proc are owned by root and only readable by root. In
+ // order to search /proc for the fd we want, this setuid executable has to
+ // double as a helper and perform the search. The code block below does this
+ // when you call it with --find-inode INODE_NUMBER.
+ if (argc == 3 && (0 == strcmp(argv[1], kFindInodeSwitch))) {
+ pid_t pid;
+ char* endptr = NULL;
+ errno = 0;
+ ino_t inode = strtoull(argv[2], &endptr, 10);
+ if (inode == ULLONG_MAX || !endptr || *endptr || errno != 0)
+ return 1;
+ if (!FindProcessHoldingSocket(&pid, inode))
+ return 1;
+ printf("%d\n", pid);
+ return 0;
+ }
+ // Likewise, we cannot adjust /proc/pid/oom_adj for sandboxed renderers
+ // because those files are owned by root. So we need another helper here.
+ if (argc == 4 && (0 == strcmp(argv[1], kAdjustOOMScoreSwitch))) {
+ char* endptr = NULL;
+ long score;
+ errno = 0;
+ unsigned long pid_ul = strtoul(argv[2], &endptr, 10);
+ if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0)
+ return 1;
+ pid_t pid = pid_ul;
+ endptr = NULL;
+ errno = 0;
+ score = strtol(argv[3], &endptr, 10);
+ if (score == LONG_MAX || score == LONG_MIN ||
+ !endptr || *endptr || errno != 0)
+ return 1;
+ return AdjustOOMScore(pid, score);
+ }
+#if defined(OS_CHROMEOS)
+ if (argc == 3 && (0 == strcmp(argv[1], kAdjustLowMemMarginSwitch))) {
+ char* endptr = NULL;
+ errno = 0;
+ unsigned long margin_mb = strtoul(argv[2], &endptr, 10);
+ if (!endptr || *endptr || errno != 0)
+ return 1;
+ return AdjustLowMemoryMargin(margin_mb);
+ }
+#endif
+
+ // Protect the core setuid sandbox functionality with an API version
+ if (!CheckAndExportApiVersion()) {
+ return 1;
+ }
+
+ if (!MoveToNewNamespaces())
+ return 1;
+ if (!SpawnChrootHelper())
+ return 1;
+ if (!DropRoot())
+ return 1;
+ if (!SetupChildEnvironment())
+ return 1;
+
+ execv(argv[1], &argv[1]);
+ FatalError("execv failed");
+
+ return 1;
+}
diff --git a/chromium/sandbox/sandbox.gyp b/chromium/sandbox/sandbox.gyp
new file mode 100644
index 00000000000..b48727a28ba
--- /dev/null
+++ b/chromium/sandbox/sandbox.gyp
@@ -0,0 +1,30 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'conditions': [
+ [ 'OS=="win"', {
+ 'includes': [
+ 'win/sandbox_win.gypi',
+ ],
+ }],
+ [ 'OS=="linux" or OS=="android"', {
+ 'includes': [
+ 'linux/sandbox_linux.gypi',
+ ],
+ }],
+ [ 'OS!="win" and OS!="mac" and OS!="linux" and OS!="android"', {
+ # A 'default' to accomodate the "sandbox" target.
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': 'none',
+ }
+ ]
+ }],
+ ],
+}
diff --git a/chromium/sandbox/win/sandbox_poc/main_ui_window.cc b/chromium/sandbox/win/sandbox_poc/main_ui_window.cc
new file mode 100644
index 00000000000..4d7c13a2663
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/main_ui_window.cc
@@ -0,0 +1,668 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <CommCtrl.h>
+#include <commdlg.h>
+#include <time.h>
+#include <windowsx.h>
+#include <atlbase.h>
+#include <atlsecurity.h>
+#include <algorithm>
+#include <sstream>
+
+#include "sandbox/win/sandbox_poc/main_ui_window.h"
+#include "base/logging.h"
+#include "sandbox/win/sandbox_poc/resource.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/win_utils.h"
+
+HWND MainUIWindow::list_view_ = NULL;
+
+const wchar_t MainUIWindow::kDefaultDll_[] = L"\\POCDLL.dll";
+const wchar_t MainUIWindow::kDefaultEntryPoint_[] = L"Run";
+const wchar_t MainUIWindow::kDefaultLogFile_[] = L"";
+
+MainUIWindow::MainUIWindow()
+ : instance_handle_(NULL),
+ spawn_target_(L""),
+ dll_path_(L""),
+ entry_point_(L""),
+ broker_(NULL) {
+}
+
+MainUIWindow::~MainUIWindow() {
+}
+
+unsigned int MainUIWindow::CreateMainWindowAndLoop(
+ HINSTANCE instance,
+ wchar_t* command_line,
+ int show_command,
+ sandbox::BrokerServices* broker) {
+ DCHECK(instance);
+ DCHECK(command_line);
+ DCHECK(broker);
+
+ instance_handle_ = instance;
+ spawn_target_ = command_line;
+ broker_ = broker;
+
+ // We'll use spawn_target_ later for creating a child process, but
+ // CreateProcess doesn't like double quotes, so we remove them along with
+ // tabs and spaces from the start and end of the string
+ const wchar_t *trim_removal = L" \r\t\"";
+ spawn_target_.erase(0, spawn_target_.find_first_not_of(trim_removal));
+ spawn_target_.erase(spawn_target_.find_last_not_of(trim_removal) + 1);
+
+ WNDCLASSEX window_class = {0};
+ window_class.cbSize = sizeof(WNDCLASSEX);
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.lpfnWndProc = MainUIWindow::WndProc;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = instance;
+ window_class.hIcon =
+ ::LoadIcon(instance, MAKEINTRESOURCE(IDI_SANDBOX));
+ window_class.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+ window_class.hbrBackground = GetStockBrush(WHITE_BRUSH);
+ window_class.lpszMenuName = MAKEINTRESOURCE(IDR_MENU_MAIN_UI);
+ window_class.lpszClassName = L"sandbox_ui_1";
+ window_class.hIconSm = NULL;
+
+ INITCOMMONCONTROLSEX controls = {
+ sizeof(INITCOMMONCONTROLSEX),
+ ICC_STANDARD_CLASSES | ICC_LISTVIEW_CLASSES
+ };
+ ::InitCommonControlsEx(&controls);
+
+ if (!::RegisterClassEx(&window_class))
+ return ::GetLastError();
+
+ // Create a main window of size 600x400
+ HWND window = ::CreateWindowW(window_class.lpszClassName,
+ L"", // window name
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, // x
+ CW_USEDEFAULT, // y
+ 600, // width
+ 400, // height
+ NULL, // parent
+ NULL, // NULL = use class menu
+ instance,
+ 0); // lpParam
+
+ if (NULL == window)
+ return ::GetLastError();
+
+ ::SetWindowLongPtr(window,
+ GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(this));
+
+ ::SetWindowText(window, L"Sandbox Proof of Concept");
+
+ ::ShowWindow(window, show_command);
+
+ MSG message;
+ // Now lets start the message pump retrieving messages for any window that
+ // belongs to the current thread
+ while (::GetMessage(&message, NULL, 0, 0)) {
+ ::TranslateMessage(&message);
+ ::DispatchMessage(&message);
+ }
+
+ return 0;
+}
+
+LRESULT CALLBACK MainUIWindow::WndProc(HWND window,
+ UINT message_id,
+ WPARAM wparam,
+ LPARAM lparam) {
+ MainUIWindow* host = FromWindow(window);
+
+ #define HANDLE_MSG(hwnd, message, fn) \
+ case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
+
+ switch (message_id) {
+ case WM_CREATE:
+ // 'host' is not yet available when we get the WM_CREATE message
+ return HANDLE_WM_CREATE(window, wparam, lparam, OnCreate);
+ case WM_DESTROY:
+ return HANDLE_WM_DESTROY(window, wparam, lparam, host->OnDestroy);
+ case WM_SIZE:
+ return HANDLE_WM_SIZE(window, wparam, lparam, host->OnSize);
+ case WM_COMMAND: {
+ // Look at which menu item was clicked on (or which accelerator)
+ int id = LOWORD(wparam);
+ switch (id) {
+ case ID_FILE_EXIT:
+ host->OnFileExit();
+ break;
+ case ID_COMMANDS_SPAWNTARGET:
+ host->OnCommandsLaunch(window);
+ break;
+ default:
+ // Some other menu item or accelerator
+ break;
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ default:
+ // Some other WM_message, let it pass to DefWndProc
+ break;
+ }
+
+ return DefWindowProc(window, message_id, wparam, lparam);
+}
+
+INT_PTR CALLBACK MainUIWindow::SpawnTargetWndProc(HWND dialog,
+ UINT message_id,
+ WPARAM wparam,
+ LPARAM lparam) {
+ UNREFERENCED_PARAMETER(lparam);
+
+ // Grab a reference to the main UI window (from the window handle)
+ MainUIWindow* host = FromWindow(GetParent(dialog));
+ DCHECK(host);
+
+ switch (message_id) {
+ case WM_INITDIALOG: {
+ // Initialize the window text for DLL name edit box
+ HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME);
+ wchar_t current_dir[MAX_PATH];
+ if (GetCurrentDirectory(MAX_PATH, current_dir)) {
+ std::wstring dll_path = std::wstring(current_dir) +
+ std::wstring(kDefaultDll_);
+ ::SetWindowText(edit_box_dll_name, dll_path.c_str());
+ }
+
+ // Initialize the window text for Entry Point edit box
+ HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT);
+ ::SetWindowText(edit_box_entry_point, kDefaultEntryPoint_);
+
+ // Initialize the window text for Log File edit box
+ HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE);
+ ::SetWindowText(edit_box_log_file, kDefaultLogFile_);
+
+ return static_cast<INT_PTR>(TRUE);
+ }
+ case WM_COMMAND:
+ // If the user presses the OK button (Launch)
+ if (LOWORD(wparam) == IDOK) {
+ if (host->OnLaunchDll(dialog)) {
+ if (host->SpawnTarget()) {
+ ::EndDialog(dialog, LOWORD(wparam));
+ }
+ }
+ return static_cast<INT_PTR>(TRUE);
+ } else if (LOWORD(wparam) == IDCANCEL) {
+ // If the user presses the Cancel button
+ ::EndDialog(dialog, LOWORD(wparam));
+ return static_cast<INT_PTR>(TRUE);
+ } else if (LOWORD(wparam) == IDC_BROWSE_DLL) {
+ // If the user presses the Browse button to look for a DLL
+ std::wstring dll_path = host->OnShowBrowseForDllDlg(dialog);
+ if (dll_path.length() > 0) {
+ // Initialize the window text for Log File edit box
+ HWND edit_box_dll_path = ::GetDlgItem(dialog, IDC_DLL_NAME);
+ ::SetWindowText(edit_box_dll_path, dll_path.c_str());
+ }
+ return static_cast<INT_PTR>(TRUE);
+ } else if (LOWORD(wparam) == IDC_BROWSE_LOG) {
+ // If the user presses the Browse button to look for a log file
+ std::wstring log_path = host->OnShowBrowseForLogFileDlg(dialog);
+ if (log_path.length() > 0) {
+ // Initialize the window text for Log File edit box
+ HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE);
+ ::SetWindowText(edit_box_log_file, log_path.c_str());
+ }
+ return static_cast<INT_PTR>(TRUE);
+ }
+
+ break;
+ }
+
+ return static_cast<INT_PTR>(FALSE);
+}
+
+MainUIWindow* MainUIWindow::FromWindow(HWND main_window) {
+ // We store a 'this' pointer using SetWindowLong in CreateMainWindowAndLoop
+ // so that we can retrieve it with this function later. This prevents us
+ // from having to define all the message handling functions (that we refer to
+ // in the window proc) as static
+ ::GetWindowLongPtr(main_window, GWLP_USERDATA);
+ return reinterpret_cast<MainUIWindow*>(
+ ::GetWindowLongPtr(main_window, GWLP_USERDATA));
+}
+
+BOOL MainUIWindow::OnCreate(HWND parent_window, LPCREATESTRUCT) {
+ // Create the listview that will the main app UI
+ list_view_ = ::CreateWindow(WC_LISTVIEW, // Class name
+ L"", // Window name
+ WS_CHILD | WS_VISIBLE | LVS_REPORT |
+ LVS_NOCOLUMNHEADER | WS_BORDER,
+ 0, // x
+ 0, // y
+ 0, // width
+ 0, // height
+ parent_window, // parent
+ NULL, // menu
+ ::GetModuleHandle(NULL),
+ 0); // lpParam
+
+ DCHECK(list_view_);
+ if (!list_view_)
+ return FALSE;
+
+ LVCOLUMN list_view_column = {0};
+ list_view_column.mask = LVCF_FMT | LVCF_WIDTH ;
+ list_view_column.fmt = LVCFMT_LEFT;
+ list_view_column.cx = 10000; // Maximum size of an entry in the list view.
+ ListView_InsertColumn(list_view_, 0, &list_view_column);
+
+ // Set list view to show green font on black background
+ ListView_SetBkColor(list_view_, CLR_NONE);
+ ListView_SetTextColor(list_view_, RGB(0x0, 0x0, 0x0));
+ ListView_SetTextBkColor(list_view_, CLR_NONE);
+
+ return TRUE;
+}
+
+void MainUIWindow::OnDestroy(HWND window) {
+ UNREFERENCED_PARAMETER(window);
+
+ // Post a quit message because our application is over when the
+ // user closes this window.
+ ::PostQuitMessage(0);
+}
+
+void MainUIWindow::OnSize(HWND window, UINT state, int cx, int cy) {
+ UNREFERENCED_PARAMETER(window);
+ UNREFERENCED_PARAMETER(state);
+
+ // If we have a valid inner child, resize it to cover the entire
+ // client area of the main UI window.
+ if (list_view_) {
+ ::MoveWindow(list_view_,
+ 0, // x
+ 0, // y
+ cx, // width
+ cy, // height
+ TRUE); // repaint
+ }
+}
+
+void MainUIWindow::OnPaint(HWND window) {
+ PAINTSTRUCT paintstruct;
+ ::BeginPaint(window, &paintstruct);
+ // add painting code here if required
+ ::EndPaint(window, &paintstruct);
+}
+
+void MainUIWindow::OnFileExit() {
+ ::PostQuitMessage(0);
+}
+
+void MainUIWindow::OnCommandsLaunch(HWND window) {
+ // User wants to see the Select DLL dialog box
+ ::DialogBox(instance_handle_,
+ MAKEINTRESOURCE(IDD_LAUNCH_DLL),
+ window,
+ SpawnTargetWndProc);
+}
+
+bool MainUIWindow::OnLaunchDll(HWND dialog) {
+ HWND edit_box_dll_name = ::GetDlgItem(dialog, IDC_DLL_NAME);
+ HWND edit_box_entry_point = ::GetDlgItem(dialog, IDC_ENTRY_POINT);
+ HWND edit_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE);
+
+ wchar_t dll_path[MAX_PATH];
+ wchar_t entry_point[MAX_PATH];
+ wchar_t log_file[MAX_PATH];
+
+ int dll_name_len = ::GetWindowText(edit_box_dll_name, dll_path, MAX_PATH);
+ int entry_point_len = ::GetWindowText(edit_box_entry_point,
+ entry_point, MAX_PATH);
+ // Log file is optional (can be blank)
+ ::GetWindowText(edit_log_file, log_file, MAX_PATH);
+
+ if (0 >= dll_name_len) {
+ ::MessageBox(dialog,
+ L"Please specify a DLL for the target to load",
+ L"No DLL specified",
+ MB_ICONERROR);
+ return false;
+ }
+
+ if (GetFileAttributes(dll_path) == INVALID_FILE_ATTRIBUTES) {
+ ::MessageBox(dialog,
+ L"DLL specified was not found",
+ L"DLL not found",
+ MB_ICONERROR);
+ return false;
+ }
+
+ if (0 >= entry_point_len) {
+ ::MessageBox(dialog,
+ L"Please specify an entry point for the DLL",
+ L"No entry point specified",
+ MB_ICONERROR);
+ return false;
+ }
+
+ // store these values in the member variables for use in SpawnTarget
+ log_file_ = std::wstring(L"\"") + log_file + std::wstring(L"\"");
+ dll_path_ = dll_path;
+ entry_point_ = entry_point;
+
+ return true;
+}
+
+DWORD WINAPI MainUIWindow::ListenPipeThunk(void *param) {
+ return reinterpret_cast<MainUIWindow*>(param)->ListenPipe();
+}
+
+DWORD WINAPI MainUIWindow::WaitForTargetThunk(void *param) {
+ return reinterpret_cast<MainUIWindow*>(param)->WaitForTarget();
+}
+
+// Thread waiting for the target application to die. It displays
+// a message in the list view when it happens.
+DWORD MainUIWindow::WaitForTarget() {
+ WaitForSingleObject(target_.hProcess, INFINITE);
+
+ DWORD exit_code = 0;
+ if (!GetExitCodeProcess(target_.hProcess, &exit_code)) {
+ exit_code = 0xFFFF; // Default exit code
+ }
+
+ ::CloseHandle(target_.hProcess);
+ ::CloseHandle(target_.hThread);
+
+ AddDebugMessage(L"Targed exited with return code %d", exit_code);
+ return 0;
+}
+
+// Thread waiting for messages on the log pipe. It displays the messages
+// in the listview.
+DWORD MainUIWindow::ListenPipe() {
+ HANDLE logfile_handle = NULL;
+ ATL::CString file_to_open = log_file_.c_str();
+ file_to_open.Remove(L'\"');
+ if (file_to_open.GetLength()) {
+ logfile_handle = ::CreateFile(file_to_open.GetBuffer(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // Default security attributes
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL); // No template
+ if (INVALID_HANDLE_VALUE == logfile_handle) {
+ AddDebugMessage(L"Failed to open \"%ls\" for logging. Error %d",
+ file_to_open.GetBuffer(), ::GetLastError());
+ logfile_handle = NULL;
+ }
+ }
+
+ const int kSizeBuffer = 1024;
+ BYTE read_buffer[kSizeBuffer] = {0};
+ ATL::CStringA read_buffer_global;
+ ATL::CStringA string_to_print;
+
+ DWORD last_error = 0;
+ while(last_error == ERROR_SUCCESS || last_error == ERROR_PIPE_LISTENING ||
+ last_error == ERROR_NO_DATA)
+ {
+ DWORD read_data_length;
+ if (::ReadFile(pipe_handle_,
+ read_buffer,
+ kSizeBuffer - 1, // Max read size
+ &read_data_length,
+ NULL)) { // Not overlapped
+ if (logfile_handle) {
+ DWORD write_data_length;
+ ::WriteFile(logfile_handle,
+ read_buffer,
+ read_data_length,
+ &write_data_length,
+ FALSE); // Not overlapped
+ }
+
+ // Append the new buffer to the current buffer
+ read_buffer[read_data_length] = NULL;
+ read_buffer_global += reinterpret_cast<char *>(read_buffer);
+ read_buffer_global.Remove(10); // Remove the CRs
+
+ // If we completed a new line, output it
+ int endline = read_buffer_global.Find(13); // search for LF
+ while (-1 != endline) {
+ string_to_print = read_buffer_global;
+ string_to_print.Delete(endline, string_to_print.GetLength());
+ read_buffer_global.Delete(0, endline);
+
+ // print the line (with the ending LF)
+ OutputDebugStringA(string_to_print.GetBuffer());
+
+ // Remove the ending LF
+ read_buffer_global.Delete(0, 1);
+
+ // Add the line to the log
+ AddDebugMessage(L"%S", string_to_print.GetBuffer());
+
+ endline = read_buffer_global.Find(13);
+ }
+ last_error = ERROR_SUCCESS;
+ } else {
+ last_error = GetLastError();
+ Sleep(100);
+ }
+ }
+
+ if (read_buffer_global.GetLength()) {
+ AddDebugMessage(L"%S", read_buffer_global.GetBuffer());
+ }
+
+ CloseHandle(pipe_handle_);
+
+ if (logfile_handle) {
+ CloseHandle(logfile_handle);
+ }
+
+ return 0;
+}
+
+bool MainUIWindow::SpawnTarget() {
+ // Generate the pipe name
+ GUID random_id;
+ CoCreateGuid(&random_id);
+
+ wchar_t log_pipe[MAX_PATH] = {0};
+ wnsprintf(log_pipe, MAX_PATH - 1,
+ L"\\\\.\\pipe\\sbox_pipe_log_%lu_%lu_%lu_%lu",
+ random_id.Data1,
+ random_id.Data2,
+ random_id.Data3,
+ random_id.Data4);
+
+ // We concatenate the four strings, add three spaces and a zero termination
+ // We use the resulting string as a param to CreateProcess (in SpawnTarget)
+ // Documented maximum for command line in CreateProcess is 32K (msdn)
+ size_t size_call = spawn_target_.length() + entry_point_.length() +
+ dll_path_.length() + wcslen(log_pipe) + 6;
+ if (32 * 1024 < (size_call * sizeof(wchar_t))) {
+ AddDebugMessage(L"The length of the arguments exceeded 32K. "
+ L"Aborting operation.");
+ return false;
+ }
+
+ wchar_t * arguments = new wchar_t[size_call];
+ wnsprintf(arguments, static_cast<int>(size_call), L"%ls %ls \"%ls\" %ls",
+ spawn_target_.c_str(), entry_point_.c_str(),
+ dll_path_.c_str(), log_pipe);
+
+ arguments[size_call - 1] = L'\0';
+
+ sandbox::TargetPolicy* policy = broker_->CreatePolicy();
+ policy->SetJobLevel(sandbox::JOB_LOCKDOWN, 0);
+ policy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LOCKDOWN);
+ policy->SetAlternateDesktop(true);
+ policy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+
+ // Set the rule to allow the POC dll to be loaded by the target. Note that
+ // the rule allows 'all access' to the DLL, which could mean that the target
+ // could modify the DLL on disk.
+ policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY, dll_path_.c_str());
+
+ sandbox::ResultCode result = broker_->SpawnTarget(spawn_target_.c_str(),
+ arguments, policy,
+ &target_);
+
+ policy->Release();
+ policy = NULL;
+
+ bool return_value = false;
+ if (sandbox::SBOX_ALL_OK != result) {
+ AddDebugMessage(
+ L"Failed to spawn target %ls w/args (%ls), sandbox error code: %d",
+ spawn_target_.c_str(), arguments, result);
+ return_value = false;
+ } else {
+
+ DWORD thread_id;
+ ::CreateThread(NULL, // Default security attributes
+ NULL, // Default stack size
+ &MainUIWindow::WaitForTargetThunk,
+ this,
+ 0, // No flags
+ &thread_id);
+
+ pipe_handle_ = ::CreateNamedPipe(log_pipe,
+ PIPE_ACCESS_INBOUND | WRITE_DAC,
+ PIPE_TYPE_MESSAGE | PIPE_NOWAIT,
+ 1, // Number of instances.
+ 512, // Out buffer size.
+ 512, // In buffer size.
+ NMPWAIT_USE_DEFAULT_WAIT,
+ NULL); // Default security descriptor
+
+ if (INVALID_HANDLE_VALUE == pipe_handle_)
+ AddDebugMessage(L"Failed to create pipe. Error %d", ::GetLastError());
+
+ if (!sandbox::AddKnownSidToKernelObject(pipe_handle_, WinWorldSid,
+ FILE_ALL_ACCESS))
+ AddDebugMessage(L"Failed to set security on pipe. Error %d",
+ ::GetLastError());
+
+ ::CreateThread(NULL, // Default security attributes
+ NULL, // Default stack size
+ &MainUIWindow::ListenPipeThunk,
+ this,
+ 0, // No flags
+ &thread_id);
+
+ ::ResumeThread(target_.hThread);
+
+ AddDebugMessage(L"Successfully spawned target w/args (%ls)", arguments);
+ return_value = true;
+ }
+
+ delete[] arguments;
+ return return_value;
+}
+
+std::wstring MainUIWindow::OnShowBrowseForDllDlg(HWND owner) {
+ wchar_t filename[MAX_PATH];
+ wcscpy_s(filename, MAX_PATH, L"");
+
+ OPENFILENAMEW file_info = {0};
+ file_info.lStructSize = sizeof(file_info);
+ file_info.hwndOwner = owner;
+ file_info.lpstrFile = filename;
+ file_info.nMaxFile = MAX_PATH;
+ file_info.lpstrFilter = L"DLL files (*.dll)\0*.dll\0All files\0*.*\0\0\0";
+
+ file_info.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
+
+ if (GetOpenFileName(&file_info)) {
+ return file_info.lpstrFile;
+ }
+
+ return L"";
+}
+
+std::wstring MainUIWindow::OnShowBrowseForLogFileDlg(HWND owner) {
+ wchar_t filename[MAX_PATH];
+ wcscpy_s(filename, MAX_PATH, L"");
+
+ OPENFILENAMEW file_info = {0};
+ file_info.lStructSize = sizeof(file_info);
+ file_info.hwndOwner = owner;
+ file_info.lpstrFile = filename;
+ file_info.nMaxFile = MAX_PATH;
+ file_info.lpstrFilter = L"Log file (*.txt)\0*.txt\0All files\0*.*\0\0\0";
+
+ file_info.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+
+ if (GetSaveFileName(&file_info)) {
+ return file_info.lpstrFile;
+ }
+
+ return L"";
+}
+
+void MainUIWindow::AddDebugMessage(const wchar_t* format, ...) {
+ DCHECK(format);
+ if (!format)
+ return;
+
+ const int kMaxDebugBuffSize = 1024;
+
+ va_list arg_list;
+ _crt_va_start(arg_list, format);
+
+ wchar_t text[kMaxDebugBuffSize + 1];
+ vswprintf_s(text, kMaxDebugBuffSize, format, arg_list);
+ text[kMaxDebugBuffSize] = L'\0';
+
+ InsertLineInListView(text);
+}
+
+
+void MainUIWindow::InsertLineInListView(wchar_t* debug_message) {
+ DCHECK(debug_message);
+ if (!debug_message)
+ return;
+
+ // Prepend the time to the message
+ const int kSizeTime = 100;
+ size_t size_message_with_time = wcslen(debug_message) + kSizeTime;
+ wchar_t * message_time = new wchar_t[size_message_with_time];
+
+ time_t time_temp;
+ time_temp = time(NULL);
+
+ struct tm time = {0};
+ localtime_s(&time, &time_temp);
+
+ size_t return_code;
+ return_code = wcsftime(message_time, kSizeTime, L"[%H:%M:%S] ", &time);
+
+ wcscat_s(message_time, size_message_with_time, debug_message);
+
+ // We add the debug message to the top of the listview
+ LVITEM item;
+ item.iItem = ListView_GetItemCount(list_view_);
+ item.iSubItem = 0;
+ item.mask = LVIF_TEXT | LVIF_PARAM;
+ item.pszText = message_time;
+ item.lParam = 0;
+
+ ListView_InsertItem(list_view_, &item);
+
+ delete[] message_time;
+}
diff --git a/chromium/sandbox/win/sandbox_poc/main_ui_window.h b/chromium/sandbox/win/sandbox_poc/main_ui_window.h
new file mode 100644
index 00000000000..c70189abfe7
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/main_ui_window.h
@@ -0,0 +1,193 @@
+// Copyright (c) 2011 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_POC_MAIN_UI_WINDOW_H__
+#define SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace sandbox {
+class BrokerServices;
+enum ResultCode;
+}
+
+// Header file for the MainUIWindow, a simple window with a menu bar that
+// can pop up a dialog (accessible through the menu bar), to specify a path and
+// filename of any DLL to load and choose an entry point of his/her choice
+// (note: only entry points with no parameters are expected to work).
+//
+// The purpose of this is to be able to spawn an EXE inside a SandBox, have it
+// load a DLL and call the entry point on it to test how it behaves inside the
+// sandbox. This is useful for developer debugging and for security testing.
+//
+// The MainUIWindow also has a listview that displays debugging information to
+// the user.
+//
+// Sample usage:
+//
+// MainUIWindow window;
+// unsigned int ret = window.CreateMainWindowAndLoop(
+// handle_to_current_instance,
+// ::GetCommandLineW(),
+// show_command,
+// broker);
+//
+// The CreateMainWindowAndLoop() contains a message loop that ends when the
+// user closes the MainUIWindow.
+
+// This class encapsulates the Main UI window for the broker application.
+// It simply shows a menu that gives the user the ability (through a dialog) to
+// specify a DLL and what entry point to call in the DLL.
+class MainUIWindow {
+ public:
+ MainUIWindow();
+ ~MainUIWindow();
+
+ // Creates the main window, displays it and starts the message pump. This
+ // call will not return until user closes the main UI window that appears
+ // as a result. Arguments 'instance', 'command_line' and 'show_cmd' can be
+ // passed in directly from winmain. The 'broker' argument is a pointer to a
+ // BrokerService that will launch a new EXE inside the sandbox and load the
+ // DLL of the user's choice.
+ unsigned int CreateMainWindowAndLoop(HINSTANCE instance,
+ wchar_t* command_line,
+ int show_command,
+ sandbox::BrokerServices* broker);
+
+ private:
+ // The default value DLL name to add to the edit box.
+ static const wchar_t kDefaultDll_[];
+
+ // The default value to show in the entry point.
+ static const wchar_t kDefaultEntryPoint_[];
+
+ // The default value to show in the log file.
+ static const wchar_t kDefaultLogFile_[];
+
+ // Handles the messages sent to the main UI window. The return value is the
+ // result of the message processing and depends on the message.
+ static LRESULT CALLBACK WndProc(HWND window,
+ UINT message_id,
+ WPARAM wparam,
+ LPARAM lparam);
+
+ // Handles the messages sent to the SpawnTarget dialog. The return value is
+ // the result of the message processing and depends on the message.
+ static INT_PTR CALLBACK SpawnTargetWndProc(HWND dialog,
+ UINT message_id,
+ WPARAM wparam,
+ LPARAM lparam);
+
+ // Retrieves a pointer to the MainWindow from a value stored along with the
+ // window handle (passed in as hwnd). Return value is a pointer to the
+ // MainUIWindow previously stored with SetWindowLong() during WM_CREATE.
+ static MainUIWindow* FromWindow(HWND main_window);
+
+ // Handles the WM_CREATE message for the main UI window. Returns TRUE on
+ // success.
+ static BOOL OnCreate(HWND parent_window, LPCREATESTRUCT);
+
+ // Handles the WM_DESTROY message for the main UI window.
+ void OnDestroy(HWND window);
+
+ // Handles the WM_SIZE message for the main UI window.
+ void OnSize(HWND window, UINT state, int cx, int cy);
+
+ // Handles the WM_PAINT message for the main UI window.
+ void OnPaint(HWND window);
+
+ // Handles the menu command File \ Exit for the main UI window.
+ void OnFileExit();
+
+ // Handles the menu command Commands \ Launch for the main UI window.
+ void OnCommandsLaunch(HWND window);
+
+ // Handles the Launch button in the SpawnTarget dialog (normally clicked
+ // after selecting DLL and entry point). OnLaunchDll will retrieve the
+ // values entered by the user and store it in the members of the class.
+ // Returns true if user selected a non-zero values for DLL filename
+ // (possibly including path also) and entry point.
+ bool OnLaunchDll(HWND dialog);
+
+ // Spawns a target EXE inside the sandbox (with the help of the
+ // BrokerServices passed in to CreateMainWindowAndLoop), and passes to it
+ // (as command line arguments) the DLL path and the entry point function
+ // name. The EXE is expected to parse the command line and load the DLL.
+ // NOTE: The broker does not know if the target EXE successfully loaded the
+ // DLL, for that you have to rely on the EXE providing a log.
+ // Returns true if the broker reports that it was successful in creating
+ // the target and false if not.
+ bool SpawnTarget();
+
+ // Shows a standard File Open dialog and returns the DLL filename selected or
+ // blank string if the user cancelled (or an error occurred).
+ std::wstring OnShowBrowseForDllDlg(HWND owner);
+
+ // Shows a standard Save As dialog and returns the log filename selected or
+ // blank string if the user cancelled (or an error occurred).
+ std::wstring OnShowBrowseForLogFileDlg(HWND owner);
+
+ // Formats a message using the supplied format string and prints it in the
+ // listview in the main UI window. Passing a NULL param in 'fmt' results in
+ // no action being performed. Maximum message length is 1K.
+ void AddDebugMessage(const wchar_t* format, ...);
+
+ // Assists AddDebugMessage in displaying a message in the ListView. It
+ // simply wraps ListView_InsertItem to insert a debugging message to the
+ // top of the list view. Passing a NULL param in 'fmt' results in no action
+ // being performed.
+ void InsertLineInListView(wchar_t* debug_message);
+
+ // Calls ListenPipe using the class instance received in parameter. This is
+ // used to create new threads executing ListenPipe
+ static DWORD WINAPI ListenPipeThunk(void *param);
+
+ // Calls WaitForTargetThunk using the class instance received in parameter
+ // This is used to create new threads executing WaitForTarget.
+ static DWORD WINAPI WaitForTargetThunk(void *param);
+
+ // Listens on a pipe and output the data received to a file and to the UI.
+ DWORD ListenPipe();
+
+ // Waits for the target to dies and display a message in the UI.
+ DWORD WaitForTarget();
+
+ // The BrokerServices will be used to spawn an EXE in a sandbox and ask
+ // it to load a DLL.
+ sandbox::BrokerServices* broker_;
+
+ // Contains the information about the running target.
+ PROCESS_INFORMATION target_;
+
+ // This is essentially a command line to a target executable that the
+ // broker will spawn and ask to load the DLL.
+ std::wstring spawn_target_;
+
+ // A handle to the current instance of the app. Passed in to this class
+ // through CreateMainWindowAndLoop.
+ HINSTANCE instance_handle_;
+
+ // A path to the DLL that the target should load once it executes.
+ std::wstring dll_path_;
+
+ // The name of the entry point the target should call after it loads the DLL.
+ std::wstring entry_point_;
+
+ // The name of the log file to use.
+ std::wstring log_file_;
+
+ // This is a static handle to the list view that fills up the entire main
+ // UI window. The list view is used to display debugging information to the
+ // user.
+ static HWND list_view_;
+
+ // Pipe used to communicate the logs between the target and the broker.
+ HANDLE pipe_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(MainUIWindow);
+};
+
+#endif // SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/exports.h b/chromium/sandbox/win/sandbox_poc/pocdll/exports.h
new file mode 100644
index 00000000000..66a07d6b78b
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/exports.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2006-2008 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_POC_POCDLL_EXPORTS_H__
+#define SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__
+
+#include <windows.h>
+
+#ifdef POCDLL_EXPORTS
+#define POCDLL_API __declspec(dllexport) __cdecl
+#else
+#define POCDLL_API __declspec(dllimport) __cdecl
+#endif
+
+extern "C" {
+// Tries to open several known system path and outputs
+// the result.
+// "log" is the handle of the log file.
+void POCDLL_API TestFileSystem(HANDLE log);
+
+// Tries to find all handles open in the process and prints the name of the
+// resource references by the handle along with the access right.
+// "log" is the handle of the log file.
+void POCDLL_API TestGetHandle(HANDLE log);
+
+// Creates a lot of threads until it cannot create more. The goal of this
+// function is to determine if it's possible to crash the machine when we
+// flood the machine with new threads
+// "log" is the handle of the log file.
+void POCDLL_API TestThreadBombing(HANDLE log);
+
+// Takes all cpu of the machine. For each processor on the machine we assign
+// a thread. This thread will compute a mathematical expression over and over
+// to take all cpu.
+// "log" is the handle of the log file.
+// Note: here we are using the affinity to find out how many processors are on
+// the machine and to force a thread to run only on a given processor.
+void POCDLL_API TestTakeAllCpu(HANDLE log);
+
+// Creates memory in the heap until it fails 5 times in a row and prints the
+// amount of memory created. This function is used to find out if it's possible
+// to take all memory on the machine and crash the system.
+// "log" is the handle of the log file.
+void POCDLL_API TestUseAllMemory(HANDLE log);
+
+// Creates millions of kernel objects. This function is used to find out if it's
+// possible to crash the system if we create too many kernel objects and if we
+// hold too many handles. All those kernel objects are unnamed.
+// "log" is the handle of the log file.
+void POCDLL_API TestCreateObjects(HANDLE log);
+
+// Receives a hwnd and tries to close it. This is the callback for EnumWindows.
+// It will be called for each window(hwnd) on the system.
+// "log" is the handle of the log file.
+// Always returns TRUE to tell the system that we want to continue the
+// enumeration.
+void POCDLL_API TestCloseHWND(HANDLE log);
+
+// Tries to listen on the port 88.
+// "log" is the handle of the log file.
+void POCDLL_API TestNetworkListen(HANDLE log);
+
+// Lists all processes on the system and tries to open them
+// "log" is the handle of the log file.
+void POCDLL_API TestProcesses(HANDLE log);
+
+// Lists all threads on the system and tries to open them
+// "log" is the handle of the log file.
+void POCDLL_API TestThreads(HANDLE log);
+
+// Tries to open some known system registry key and outputs the result.
+// "log" is the handle of the log file.
+void POCDLL_API TestRegistry(HANDLE log);
+
+// Records all keystrokes typed for 15 seconds and then display them.
+// "log" is the handle of the log file.
+void POCDLL_API TestSpyKeys(HANDLE log);
+
+// Tries to read pixels on the monitor and output if the operation
+// failes or succeeded.
+// "log" is the handle of the log file.
+void POCDLL_API TestSpyScreen(HANDLE log);
+
+// Runs all tests except those who are invasive
+void POCDLL_API Run(HANDLE log);
+}
+
+#endif // SANDBOX_SANDBOX_POC_POCDLL_EXPORTS_H__
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/fs.cc b/chromium/sandbox/win/sandbox_poc/pocdll/fs.cc
new file mode 100644
index 00000000000..9a5b2bc3ca5
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/fs.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2006-2008 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/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+// This file contains the tests used to verify the security of the file system.
+
+// Tries to open a file and outputs the result.
+// "path" can contain environment variables.
+// "output" is the stream for the logging.
+void TryOpenFile(wchar_t *path, FILE *output) {
+ wchar_t path_expanded[MAX_PATH] = {0};
+ DWORD size = ::ExpandEnvironmentStrings(path, path_expanded, MAX_PATH - 1);
+ if (!size) {
+ fprintf(output, "[ERROR] Cannot expand \"%S\". Error %d.\r\n", path,
+ ::GetLastError());
+ }
+
+ HANDLE file;
+ file = ::CreateFile(path_expanded,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, // No security attributes
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL); // No template
+
+ if (file && INVALID_HANDLE_VALUE != file) {
+ fprintf(output, "[GRANTED] Opening file \"%S\". Handle 0x%p\r\n", path,
+ file);
+ ::CloseHandle(file);
+ } else {
+ fprintf(output, "[BLOCKED] Opening file \"%S\". Error %d.\r\n", path,
+ ::GetLastError());
+ }
+}
+
+void POCDLL_API TestFileSystem(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ TryOpenFile(L"%SystemDrive%", output);
+ TryOpenFile(L"%SystemRoot%", output);
+ TryOpenFile(L"%ProgramFiles%", output);
+ TryOpenFile(L"%SystemRoot%\\System32", output);
+ TryOpenFile(L"%SystemRoot%\\explorer.exe", output);
+ TryOpenFile(L"%SystemRoot%\\Cursors\\arrow_i.cur", output);
+ TryOpenFile(L"%AllUsersProfile%", output);
+ TryOpenFile(L"%UserProfile%", output);
+ TryOpenFile(L"%Temp%", output);
+ TryOpenFile(L"%AppData%", output);
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/handles.cc b/chromium/sandbox/win/sandbox_poc/pocdll/handles.cc
new file mode 100644
index 00000000000..0568484f9e7
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/handles.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2006-2010 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/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+#include "sandbox/win/tools/finder/ntundoc.h"
+
+// This file contains the tests used to verify the security of handles in
+// the process
+
+NTQUERYOBJECT NtQueryObject;
+NTQUERYINFORMATIONFILE NtQueryInformationFile;
+NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;
+
+void POCDLL_API TestGetHandle(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ // Initialize the NTAPI functions we need
+ HMODULE ntdll_handle = ::GetModuleHandle(L"ntdll.dll");
+ if (!ntdll_handle) {
+ fprintf(output, "[ERROR] Cannot load ntdll.dll. Error %d\r\n",
+ ::GetLastError());
+ return;
+ }
+
+ NtQueryObject = reinterpret_cast<NTQUERYOBJECT>(
+ GetProcAddress(ntdll_handle, "NtQueryObject"));
+ NtQueryInformationFile = reinterpret_cast<NTQUERYINFORMATIONFILE>(
+ GetProcAddress(ntdll_handle, "NtQueryInformationFile"));
+ NtQuerySystemInformation = reinterpret_cast<NTQUERYSYSTEMINFORMATION>(
+ GetProcAddress(ntdll_handle, "NtQuerySystemInformation"));
+
+ if (!NtQueryObject || !NtQueryInformationFile || !NtQuerySystemInformation) {
+ fprintf(output, "[ERROR] Cannot load all NT functions. Error %d\r\n",
+ ::GetLastError());
+ return;
+ }
+
+ // Get the number of handles on the system
+ DWORD buffer_size = 0;
+ SYSTEM_HANDLE_INFORMATION_EX temp_info;
+ NTSTATUS status = NtQuerySystemInformation(
+ SystemHandleInformation, &temp_info, sizeof(temp_info),
+ &buffer_size);
+ if (!buffer_size) {
+ fprintf(output, "[ERROR] Get the number of handles. Error 0x%X\r\n",
+ status);
+ return;
+ }
+
+ SYSTEM_HANDLE_INFORMATION_EX *system_handles =
+ reinterpret_cast<SYSTEM_HANDLE_INFORMATION_EX*>(new BYTE[buffer_size]);
+
+ status = NtQuerySystemInformation(SystemHandleInformation, system_handles,
+ buffer_size, &buffer_size);
+ if (STATUS_SUCCESS != status) {
+ fprintf(output, "[ERROR] Failed to get the handle list. Error 0x%X\r\n",
+ status);
+ delete [] system_handles;
+ return;
+ }
+
+ for (ULONG i = 0; i < system_handles->NumberOfHandles; ++i) {
+ USHORT h = system_handles->Information[i].Handle;
+ if (system_handles->Information[i].ProcessId != ::GetCurrentProcessId())
+ continue;
+
+ OBJECT_NAME_INFORMATION *name = NULL;
+ ULONG name_size = 0;
+ // Query the name information a first time to get the size of the name.
+ status = NtQueryObject(reinterpret_cast<HANDLE>(h),
+ ObjectNameInformation,
+ name,
+ name_size,
+ &name_size);
+
+ if (name_size) {
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION *>(new BYTE[name_size]);
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = NtQueryObject(reinterpret_cast<HANDLE>(h),
+ ObjectNameInformation,
+ name,
+ name_size,
+ &name_size);
+ }
+
+ PUBLIC_OBJECT_TYPE_INFORMATION *type = NULL;
+ ULONG type_size = 0;
+
+ // Query the object to get the size of the object type name.
+ status = NtQueryObject(reinterpret_cast<HANDLE>(h),
+ ObjectTypeInformation,
+ type,
+ type_size,
+ &type_size);
+ if (type_size) {
+ type = reinterpret_cast<PUBLIC_OBJECT_TYPE_INFORMATION *>(
+ new BYTE[type_size]);
+
+ // Query the type information a second time to get the object type
+ // name.
+ status = NtQueryObject(reinterpret_cast<HANDLE>(h),
+ ObjectTypeInformation,
+ type,
+ type_size,
+ &type_size);
+ }
+
+ // NtQueryObject cannot return the name for a file. In this case we
+ // need to ask NtQueryInformationFile
+ FILE_NAME_INFORMATION *file_name = NULL;
+ if (type && wcsncmp(L"File", type->TypeName.Buffer,
+ (type->TypeName.Length /
+ sizeof(type->TypeName.Buffer[0]))) == 0) {
+ // This function does not return the size of the buffer. We need to
+ // iterate and always increase the buffer size until the function
+ // succeeds. (Or at least does not fail with STATUS_BUFFER_OVERFLOW)
+ ULONG size_file = MAX_PATH;
+ IO_STATUS_BLOCK status_block = {0};
+ do {
+ // Delete the previous buffer create. The buffer was too small
+ if (file_name) {
+ delete[] reinterpret_cast<BYTE*>(file_name);
+ file_name = NULL;
+ }
+
+ // Increase the buffer and do the call agan
+ size_file += MAX_PATH;
+ file_name = reinterpret_cast<FILE_NAME_INFORMATION *>(
+ new BYTE[size_file]);
+ status = NtQueryInformationFile(reinterpret_cast<HANDLE>(h),
+ &status_block,
+ file_name,
+ size_file,
+ FileNameInformation);
+ } while (status == STATUS_BUFFER_OVERFLOW);
+
+ if (STATUS_SUCCESS != status) {
+ if (file_name) {
+ delete[] file_name;
+ file_name = NULL;
+ }
+ }
+ }
+
+ if (file_name) {
+ UNICODE_STRING file_name_string;
+ file_name_string.Buffer = file_name->FileName;
+ file_name_string.Length = (USHORT)file_name->FileNameLength;
+ file_name_string.MaximumLength = (USHORT)file_name->FileNameLength;
+ fprintf(output, "[GRANTED] Handle 0x%4.4X Access: 0x%8.8X "
+ "Type: %-13.13wZ Path: %wZ\r\n",
+ h,
+ system_handles->Information[i].GrantedAccess,
+ type ? &type->TypeName : NULL,
+ &file_name_string);
+ } else {
+ fprintf(output, "[GRANTED] Handle 0x%4.4X Access: 0x%8.8X "
+ "Type: %-13.13wZ Path: %wZ\r\n",
+ h,
+ system_handles->Information[i].GrantedAccess,
+ type ? &type->TypeName : NULL,
+ name ? &name->ObjectName : NULL);
+ }
+
+ if (type) {
+ delete[] type;
+ }
+
+ if (file_name) {
+ delete[] file_name;
+ }
+
+ if (name) {
+ delete [] name;
+ }
+ }
+
+ if (system_handles) {
+ delete [] system_handles;
+ }
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/invasive.cc b/chromium/sandbox/win/sandbox_poc/pocdll/invasive.cc
new file mode 100644
index 00000000000..df3661243a8
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/invasive.cc
@@ -0,0 +1,196 @@
+// Copyright (c) 2006-2008 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 <malloc.h>
+#include "sandbox/win/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+// This file contains the tests used to verify if it's possible to DOS or crash
+// the machine. All tests that can impact the stability of the machine should
+// be in this file.
+
+// Sleeps forever. this function is used to be the
+// entry point for the threads created by the thread bombing function.
+// This function never returns.
+DWORD WINAPI MyThreadBombimgFunction(void *param) {
+ UNREFERENCED_PARAMETER(param);
+ Sleep(INFINITE);
+ return 0;
+}
+
+void POCDLL_API TestThreadBombing(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ // we stop after 5 errors in a row
+ int number_errors = 0;
+ for (int i = 0; i < 100000; ++i) {
+ DWORD tid;
+ // Create the thread and leak the handle.
+ HANDLE thread = ::CreateThread(NULL, // Default security attributes
+ NULL, // Stack size
+ MyThreadBombimgFunction,
+ NULL, // Parameter
+ 0, // No creation flags
+ &tid);
+ if (thread) {
+ fprintf(output, "[GRANTED] Creating thread with tid 0x%X\r\n", tid);
+ ::CloseHandle(thread);
+ number_errors = 0;
+ } else {
+ fprintf(output, "[BLOCKED] Creating thread. Error %d\r\n",
+ ::GetLastError());
+ number_errors++;
+ }
+
+ if (number_errors >= 5) {
+ break;
+ }
+ }
+}
+
+
+// Executes a complex mathematical operation forever in a loop. This function
+// is used as entry point for the threads created by TestTakeAllCpu. It it
+// designed to take all CPU on the processor where the thread is running.
+// The return value is always 0.
+DWORD WINAPI TakeAllCpu(void *param) {
+ UNREFERENCED_PARAMETER(param);
+ int cpt = 0;
+ for (;;) {
+ cpt += 2;
+ cpt /= 2;
+ cpt *= cpt;
+ cpt = cpt % 100;
+ cpt = cpt | (cpt * cpt);
+ }
+}
+
+void POCDLL_API TestTakeAllCpu(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ DWORD_PTR process_mask = 0;
+ DWORD_PTR system_mask = 0;
+ if (::GetProcessAffinityMask(::GetCurrentProcess(),
+ &process_mask,
+ &system_mask)) {
+ DWORD_PTR affinity_mask = 1;
+
+ while (system_mask) {
+ DWORD tid = 0;
+
+ HANDLE thread = ::CreateThread(NULL, // Default security attributes.
+ NULL, // Stack size.
+ TakeAllCpu,
+ NULL, // Parameter.
+ 0, // No creation flags.
+ &tid);
+ ::SetThreadAffinityMask(thread, affinity_mask);
+
+ if (::SetThreadPriority(thread, REALTIME_PRIORITY_CLASS)) {
+ fprintf(output, "[GRANTED] Set thread(%d) priority to Realtime\r\n",
+ tid);
+ } else {
+ fprintf(output, "[BLOCKED] Set thread(%d) priority to Realtime\r\n",
+ tid);
+ }
+
+ ::CloseHandle(thread);
+
+ affinity_mask = affinity_mask << 1;
+ system_mask = system_mask >> 1;
+ }
+ } else {
+ fprintf(output, "[ERROR] Cannot get affinity mask. Error %d\r\n",
+ ::GetLastError());
+ }
+}
+
+void POCDLL_API TestUseAllMemory(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ int number_errors = 0;
+ unsigned long memory_size = 0;
+ for (;;) {
+ DWORD *ptr_to_leak = reinterpret_cast<DWORD *>(malloc(1024*256));
+ if (ptr_to_leak) {
+ memory_size += (256);
+ number_errors = 0;
+ } else {
+ number_errors++;
+ }
+
+ // check if we have more than 5 errors in a row. If so, quit.
+ if (number_errors >= 5) {
+ fprintf(output, "[INFO] Created %lu kb of memory\r\n", memory_size);
+ return;
+ }
+
+ Sleep(5); // 5ms to be able to see the progression easily with taskmgr.
+ }
+}
+
+void POCDLL_API TestCreateObjects(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ int mutexes = 0;
+ int jobs = 0;
+ int events = 0;
+ for (int i = 0; i < 1000000; ++i) {
+ if (::CreateMutex(NULL, // Default security attributes.
+ TRUE, // We are the initial owner.
+ NULL)) { // No name.
+ mutexes++;
+ }
+
+ if (::CreateJobObject(NULL, // Default security attributes.
+ NULL)) { // No name.
+ jobs++;
+ }
+
+ if (::CreateEvent(NULL, // Default security attributes.
+ TRUE, // Manual Reset.
+ TRUE, // Object is signaled.
+ NULL)) { // No name.
+ events++;
+ }
+ }
+
+ fprintf(output, "[GRANTED] Created %d mutexes, %d jobs and %d events for "
+ "a total of %d objects out of 3 000 000\r\n", mutexes, jobs,
+ events, mutexes + jobs + events);
+}
+
+BOOL CALLBACK EnumWindowCallback(HWND hwnd, LPARAM output) {
+ DWORD pid;
+ ::GetWindowThreadProcessId(hwnd, &pid);
+ if (pid != ::GetCurrentProcessId()) {
+ wchar_t window_title[100 + 1] = {0};
+ ::GetWindowText(hwnd, window_title, 100);
+ fprintf(reinterpret_cast<FILE*>(output),
+ "[GRANTED] Found window 0x%p with title %S\r\n",
+ hwnd,
+ window_title);
+ ::CloseWindow(hwnd);
+ }
+
+ return TRUE;
+}
+
+// Enumerates all the windows on the system and call the function to try to
+// close them. The goal of this function is to try to kill the system by
+// closing all windows.
+// "output" is the stream used for logging.
+void POCDLL_API TestCloseHWND(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ ::EnumWindows(EnumWindowCallback, PtrToLong(output));
+ // TODO(nsylvain): find a way to know when the enum is finished
+ // before returning.
+ ::Sleep(3000);
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/network.cc b/chromium/sandbox/win/sandbox_poc/pocdll/network.cc
new file mode 100644
index 00000000000..56ab5aefb0c
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/network.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2006-2008 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/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+// This file contains the tests used to verify the security of the network.
+
+void POCDLL_API TestNetworkListen(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+#if DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK
+ // Initialize Winsock
+ WSADATA wsa_data;
+ int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data);
+ if (result != NO_ERROR) {
+ fprintf(output, "[ERROR] Cannot initialize winsock. Error%d\r\n", result);
+ return;
+ }
+
+ // Create a SOCKET for listening for
+ // incoming connection requests.
+ SOCKET listen_socket;
+ listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (listen_socket == INVALID_SOCKET) {
+ fprintf(output, "[ERROR] Failed to create socket. Error %ld\r\n",
+ ::WSAGetLastError());
+ ::WSACleanup();
+ return;
+ }
+
+ // The sockaddr_in structure specifies the address family,
+ // IP address, and port for the socket that is being bound.
+ sockaddr_in service;
+ service.sin_family = AF_INET;
+ service.sin_addr.s_addr = inet_addr("127.0.0.1");
+ service.sin_port = htons(88);
+
+ if (bind(listen_socket, reinterpret_cast<SOCKADDR*>(&service),
+ sizeof(service)) == SOCKET_ERROR) {
+ fprintf(output, "[BLOCKED] Bind socket on port 88. Error %ld\r\n",
+ ::WSAGetLastError());
+ closesocket(listen_socket);
+ ::WSACleanup();
+ return;
+ }
+
+ // Listen for incoming connection requests
+ // on the created socket
+ if (listen(listen_socket, SOMAXCONN) == SOCKET_ERROR) {
+ fprintf(output, "[BLOCKED] Listen socket on port 88. Error %ld\r\n",
+ ::WSAGetLastError());
+
+ } else {
+ fprintf(output, "[GRANTED] Listen socket on port 88.\r\n",
+ ::WSAGetLastError());
+ }
+
+ ::WSACleanup();
+ return;
+#else // DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK
+ // Just print out that this test is not running.
+ fprintf(output, "[ERROR] No network tests.\r\n");
+#endif // DONT_WANT_INTERCEPTIONS_JUST_WANT_NETWORK
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/pocdll.cc b/chromium/sandbox/win/sandbox_poc/pocdll/pocdll.cc
new file mode 100644
index 00000000000..e058f58b821
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/pocdll.cc
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2008 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/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+BOOL APIENTRY DllMain(HMODULE module,
+ DWORD reason_for_call,
+ LPVOID reserved) {
+ UNREFERENCED_PARAMETER(module);
+ UNREFERENCED_PARAMETER(reason_for_call);
+ UNREFERENCED_PARAMETER(reserved);
+ return TRUE;
+}
+
+void POCDLL_API Run(HANDLE log) {
+ TestFileSystem(log);
+ TestRegistry(log);
+ TestNetworkListen(log);
+ TestSpyScreen(log);
+ TestSpyKeys(log);
+ TestThreads(log);
+ TestProcesses(log);
+ TestGetHandle(log);
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj b/chromium/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj
new file mode 100644
index 00000000000..8e4e31fc6ad
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="pocdll"
+ ProjectGUID="{AE5BFB87-850E-4454-B01D-58E7D8BAC224}"
+ RootNamespace="pocdll"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="POCDLL_EXPORTS"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ ModuleDefinitionFile=""
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ EmbedManifest="false"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="POCDLL_EXPORTS"
+ UsePrecompiledHeader="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ ModuleDefinitionFile=""
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ EmbedManifest="false"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\exports.h"
+ >
+ </File>
+ <File
+ RelativePath=".\fs.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\handles.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\invasive.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\network.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\pocdll.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\processes_and_threads.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\spyware.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\utils.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc b/chromium/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc
new file mode 100644
index 00000000000..0a7503cf572
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <Tlhelp32.h>
+#include "sandbox/win/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+// This file contains the tests used to verify the security of threads and
+// processes.
+
+void POCDLL_API TestProcesses(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
+ if (INVALID_HANDLE_VALUE == snapshot) {
+ fprintf(output, "[BLOCKED] Cannot list all processes on the system. "
+ "Error %d\r\n", ::GetLastError());
+ return;
+ }
+
+ PROCESSENTRY32 process_entry = {0};
+ process_entry.dwSize = sizeof(PROCESSENTRY32);
+
+ BOOL result = ::Process32First(snapshot, &process_entry);
+
+ while (result) {
+ HANDLE process = ::OpenProcess(PROCESS_VM_READ,
+ FALSE, // Do not inherit handle.
+ process_entry.th32ProcessID);
+ if (NULL == process) {
+ fprintf(output, "[BLOCKED] Found process %S:%d but cannot open it. "
+ "Error %d\r\n",
+ process_entry.szExeFile,
+ process_entry.th32ProcessID,
+ ::GetLastError());
+ } else {
+ fprintf(output, "[GRANTED] Found process %S:%d and open succeeded.\r\n",
+ process_entry.szExeFile, process_entry.th32ProcessID);
+ ::CloseHandle(process);
+ }
+
+ result = ::Process32Next(snapshot, &process_entry);
+ }
+
+ DWORD err_code = ::GetLastError();
+ if (ERROR_NO_MORE_FILES != err_code) {
+ fprintf(output, "[ERROR] Error %d while looking at the processes on "
+ "the system\r\n", err_code);
+ }
+
+ ::CloseHandle(snapshot);
+}
+
+void POCDLL_API TestThreads(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
+ if (INVALID_HANDLE_VALUE == snapshot) {
+ fprintf(output, "[BLOCKED] Cannot list all threads on the system. "
+ "Error %d\r\n", ::GetLastError());
+ return;
+ }
+
+ THREADENTRY32 thread_entry = {0};
+ thread_entry.dwSize = sizeof(THREADENTRY32);
+
+ BOOL result = ::Thread32First(snapshot, &thread_entry);
+ int nb_success = 0;
+ int nb_failure = 0;
+
+ while (result) {
+ HANDLE thread = ::OpenThread(THREAD_QUERY_INFORMATION,
+ FALSE, // Do not inherit handles.
+ thread_entry.th32ThreadID);
+ if (NULL == thread) {
+ nb_failure++;
+ } else {
+ nb_success++;
+ fprintf(output, "[GRANTED] Found thread %d:%d and able to open it.\r\n",
+ thread_entry.th32OwnerProcessID,
+ thread_entry.th32ThreadID);
+ ::CloseHandle(thread);
+ }
+
+ result = Thread32Next(snapshot, &thread_entry);
+ }
+
+ DWORD err_code = ::GetLastError();
+ if (ERROR_NO_MORE_FILES != err_code) {
+ fprintf(output, "[ERROR] Error %d while looking at the processes on "
+ "the system\r\n", err_code);
+ }
+
+ fprintf(output, "[INFO] Found %d threads. Able to open %d of them\r\n",
+ nb_success + nb_failure, nb_success);
+
+ ::CloseHandle(snapshot);
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/registry.cc b/chromium/sandbox/win/sandbox_poc/pocdll/registry.cc
new file mode 100644
index 00000000000..7394ec4f18a
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/registry.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2006-2008 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/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+// This file contains the tests used to verify the security of the registry.
+
+// Converts an HKEY to a string. This is using the lazy way and works only
+// for the main hives.
+// "key" is the hive to convert to string.
+// The return value is the string corresponding to the hive or "unknown"
+const wchar_t *HKEYToString(const HKEY key) {
+ switch (reinterpret_cast<LONG_PTR>(key)) {
+ case HKEY_CLASSES_ROOT:
+ return L"HKEY_CLASSES_ROOT";
+ case HKEY_CURRENT_CONFIG:
+ return L"HKEY_CURRENT_CONFIG";
+ case HKEY_CURRENT_USER:
+ return L"HKEY_CURRENT_USER";
+ case HKEY_LOCAL_MACHINE:
+ return L"HKEY_LOCAL_MACHINE";
+ case HKEY_USERS:
+ return L"HKEY_USERS";
+ }
+ return L"unknown";
+}
+
+// Tries to open the key hive\path and outputs the result.
+// "output" is the stream used for logging.
+void TryOpenKey(const HKEY hive, const wchar_t *path, FILE *output) {
+ HKEY key;
+ LONG err_code = ::RegOpenKeyEx(hive,
+ path,
+ 0, // Reserved, must be 0.
+ MAXIMUM_ALLOWED,
+ &key);
+ if (ERROR_SUCCESS == err_code) {
+ fprintf(output, "[GRANTED] Opening key \"%S\\%S\". Handle 0x%p\r\n",
+ HKEYToString(hive),
+ path,
+ key);
+ ::RegCloseKey(key);
+ } else {
+ fprintf(output, "[BLOCKED] Opening key \"%S\\%S\". Error %d\r\n",
+ HKEYToString(hive),
+ path,
+ err_code);
+ }
+}
+
+void POCDLL_API TestRegistry(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ TryOpenKey(HKEY_LOCAL_MACHINE, NULL, output);
+ TryOpenKey(HKEY_CURRENT_USER, NULL, output);
+ TryOpenKey(HKEY_USERS, NULL, output);
+ TryOpenKey(HKEY_LOCAL_MACHINE,
+ L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon",
+ output);
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc b/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc
new file mode 100644
index 00000000000..a77fe0a5809
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/spyware.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2006-2009 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 <string>
+#include "sandbox/win/sandbox_poc/pocdll/exports.h"
+#include "sandbox/win/sandbox_poc/pocdll/utils.h"
+
+// This file contains the tests used to verify the security of the system by
+// using some spying techniques.
+
+void POCDLL_API TestSpyKeys(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ if (RegisterHotKey(NULL, 1, 0, 0x42)) {
+ fprintf(output, "[GRANTED] successfully registered hotkey\r\n");
+ UnregisterHotKey(NULL, 1);
+ } else {
+ fprintf(output, "[BLOCKED] Failed to register hotkey. Error = %d\r\n",
+ ::GetLastError());
+ }
+
+ fprintf(output, "[INFO] Logging keystrokes for 15 seconds\r\n");
+ fflush(output);
+ std::wstring logged;
+ DWORD tick = ::GetTickCount() + 15000;
+ while (tick > ::GetTickCount()) {
+ for (int i = 0; i < 256; ++i) {
+ if (::GetAsyncKeyState(i) & 1) {
+ if (i >= VK_SPACE && i <= 0x5A /*VK_Z*/) {
+ logged.append(1, static_cast<wchar_t>(i));
+ } else {
+ logged.append(1, '?');
+ }
+ }
+ }
+ }
+
+ if (logged.size()) {
+ fprintf(output, "[GRANTED] Spyed keystrokes \"%S\"\r\n",
+ logged.c_str());
+ } else {
+ fprintf(output, "[BLOCKED] Spyed keystrokes \"(null)\"\r\n");
+ }
+}
+
+void POCDLL_API TestSpyScreen(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ HDC screen_dc = ::GetDC(NULL);
+ COLORREF pixel_color = ::GetPixel(screen_dc, 0, 0);
+
+ for (int x = 0; x < 10; ++x) {
+ for (int y = 0; y < 10; ++y) {
+ if (::GetPixel(screen_dc, x, y) != pixel_color) {
+ fprintf(output, "[GRANTED] Read pixel on screen\r\n");
+ return;
+ }
+ }
+ }
+
+ fprintf(output, "[BLOCKED] Read pixel on screen. Error = %d\r\n",
+ ::GetLastError());
+}
diff --git a/chromium/sandbox/win/sandbox_poc/pocdll/utils.h b/chromium/sandbox/win/sandbox_poc/pocdll/utils.h
new file mode 100644
index 00000000000..ae4286147a4
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/pocdll/utils.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 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_POC_POCDLL_UTILS_H__
+#define SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__
+
+#include <stdio.h>
+#include <io.h>
+#include "base/basictypes.h"
+
+// Class to convert a HANDLE to a FILE *. The FILE * is closed when the
+// object goes out of scope
+class HandleToFile {
+ public:
+ HandleToFile() {
+ file_ = NULL;
+ };
+
+ // Note: c_file_handle_ does not need to be closed because fclose does it.
+ ~HandleToFile() {
+ if (file_) {
+ fflush(file_);
+ fclose(file_);
+ }
+ };
+
+ // Translates a HANDLE (handle) to a FILE * opened with the mode "mode".
+ // The return value is the FILE * or NULL if there is an error.
+ FILE* Translate(HANDLE handle, const char *mode) {
+ if (file_) {
+ return NULL;
+ }
+
+ HANDLE new_handle;
+ BOOL result = ::DuplicateHandle(::GetCurrentProcess(),
+ handle,
+ ::GetCurrentProcess(),
+ &new_handle,
+ 0, // Don't ask for a specific
+ // desired access.
+ FALSE, // Not inheritable.
+ DUPLICATE_SAME_ACCESS);
+
+ if (!result) {
+ return NULL;
+ }
+
+ int c_file_handle = _open_osfhandle(reinterpret_cast<LONG_PTR>(new_handle),
+ 0); // No flags
+ if (-1 == c_file_handle) {
+ return NULL;
+ }
+
+ file_ = _fdopen(c_file_handle, mode);
+ return file_;
+ };
+ private:
+ // the FILE* returned. We need to closed it at the end.
+ FILE* file_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleToFile);
+};
+
+#endif // SANDBOX_SANDBOX_POC_POCDLL_UTILS_H__
diff --git a/chromium/sandbox/win/sandbox_poc/resource.h b/chromium/sandbox/win/sandbox_poc/resource.h
new file mode 100644
index 00000000000..87ff920ca48
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/resource.h
@@ -0,0 +1,30 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by sandbox.rc
+//
+#define IDI_SANDBOX 107
+#define IDR_MENU_MAIN_UI 129
+#define IDD_LAUNCH_DLL 130
+#define IDC_RADIO_POCDLL 1000
+#define IDC_RADIO_CUSTOM_DLL 1001
+#define IDC_DLL_NAME 1002
+#define IDC_ENTRY_POINT 1003
+#define IDC_LOG_FILE 1004
+#define IDC_BROWSE_DLL 1005
+#define IDC_BROWSE_LOG 1006
+#define ID_FILE_EXIT 32771
+#define ID_COMMANDS_LAUNCHDLL 32772
+#define ID_COMMANDS_SPAWNTARGET 32773
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 131
+#define _APS_NEXT_COMMAND_VALUE 32774
+#define _APS_NEXT_CONTROL_VALUE 1007
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/chromium/sandbox/win/sandbox_poc/sandbox.cc b/chromium/sandbox/win/sandbox_poc/sandbox.cc
new file mode 100644
index 00000000000..31cf3083c84
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/sandbox.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <tchar.h>
+#include <shellapi.h>
+#include "sandbox/win/sandbox_poc/sandbox.h"
+#include "base/logging.h"
+#include "sandbox/win/sandbox_poc/main_ui_window.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+
+// Prototype allowed for functions to be called in the POC
+typedef void(__cdecl *lpfnInit)(HANDLE);
+
+bool ParseCommandLine(wchar_t * command_line,
+ std::string * dll_name,
+ std::string * entry_point,
+ std::wstring * log_file) {
+ DCHECK(dll_name);
+ DCHECK(entry_point);
+ DCHECK(log_file);
+ if (!dll_name || !entry_point || !log_file)
+ return false;
+
+ LPWSTR *arg_list;
+ int arg_count;
+
+ // We expect the command line to contain: EntryPointName "DLLPath" "LogPath"
+ // NOTE: Double quotes are required, even if long path name not used
+ // NOTE: LogPath can be blank, but still requires the double quotes
+ arg_list = CommandLineToArgvW(command_line, &arg_count);
+ if (NULL == arg_list || arg_count < 4) {
+ return false;
+ }
+
+ std::wstring entry_point_wide = arg_list[1];
+ std::wstring dll_name_wide = arg_list[2];
+ *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end());
+ *dll_name = std::string(dll_name_wide.begin(), dll_name_wide.end());
+ *log_file = arg_list[3];
+
+ // Free memory allocated for CommandLineToArgvW arguments.
+ LocalFree(arg_list);
+
+ return true;
+}
+
+int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE, wchar_t* command_line,
+ int show_command) {
+ UNREFERENCED_PARAMETER(command_line);
+
+ sandbox::BrokerServices* broker_service =
+ sandbox::SandboxFactory::GetBrokerServices();
+ sandbox::ResultCode result;
+
+ // This application starts as the broker; an application with a UI that
+ // spawns an instance of itself (called a 'target') inside the sandbox.
+ // Before spawning a hidden instance of itself, the application will have
+ // asked the user which DLL the spawned instance should load and passes
+ // that as command line argument to the spawned instance.
+ //
+ // We check here to see if we can retrieve a pointer to the BrokerServices,
+ // which is not possible if we are running inside the sandbox under a
+ // restricted token so it also tells us which mode we are in. If we can
+ // retrieve the pointer, then we are the broker, otherwise we are the target
+ // that the broker launched.
+ if (NULL != broker_service) {
+ // Yes, we are the broker so we need to initialize and show the UI
+ if (0 != (result = broker_service->Init())) {
+ ::MessageBox(NULL, L"Failed to initialize the BrokerServices object",
+ L"Error during initialization", MB_ICONERROR);
+ return 1;
+ }
+
+ wchar_t exe_name[MAX_PATH];
+ if (0 == GetModuleFileName(NULL, exe_name, MAX_PATH - 1)) {
+ ::MessageBox(NULL, L"Failed to get name of current EXE",
+ L"Error during initialization", MB_ICONERROR);
+ return 1;
+ }
+
+ // The CreateMainWindowAndLoop() call will not return until the user closes
+ // the application window (or selects File\Exit).
+ MainUIWindow window;
+ window.CreateMainWindowAndLoop(instance,
+ exe_name,
+ show_command,
+ broker_service);
+
+
+ // Cannot exit until we have cleaned up after all the targets we have
+ // created
+ broker_service->WaitForAllTargets();
+ } else {
+ // This is an instance that has been spawned inside the sandbox by the
+ // broker, so we need to parse the command line to figure out which DLL to
+ // load and what entry point to call
+ sandbox::TargetServices* target_service
+ = sandbox::SandboxFactory::GetTargetServices();
+
+ if (NULL == target_service) {
+ // TODO(finnur): write the failure to the log file
+ // We cannot display messageboxes inside the sandbox unless access to
+ // the desktop handle has been granted to us, and we don't have a
+ // console window to write to. Therefore we need to have the broker
+ // grant us access to a handle to a logfile and write the error that
+ // occurred into the log before continuing
+ return -1;
+ }
+
+ // Debugging the spawned application can be tricky, because DebugBreak()
+ // and _asm int 3 cause the app to terminate (due to a flag in the job
+ // object), MessageBoxes() will not be displayed unless we have been granted
+ // that privilege and the target finishes its business so quickly we cannot
+ // attach to it quickly enough. Therefore, you can uncomment the
+ // following line and attach (w. msdev or windbg) as the target is sleeping
+
+ // Sleep(10000);
+
+ if (sandbox::SBOX_ALL_OK != (result = target_service->Init())) {
+ // TODO(finnur): write the initialization error to the log file
+ return -2;
+ }
+
+ // Parse the command line to find out what we need to call
+ std::string dll_name, entry_point;
+ std::wstring log_file;
+ if (!ParseCommandLine(GetCommandLineW(),
+ &dll_name,
+ &entry_point,
+ &log_file)) {
+ // TODO(finnur): write the failure to the log file
+ return -3;
+ }
+
+ // Open the pipe to transfert the log output
+ HANDLE pipe = ::CreateFile(log_file.c_str(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, // Default security attributes.
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL); // No template
+
+ if (INVALID_HANDLE_VALUE == pipe) {
+ return -4;
+ }
+
+ // We now know what we should load, so load it
+ HMODULE dll_module = ::LoadLibraryA(dll_name.c_str());
+ if (dll_module == NULL) {
+ // TODO(finnur): write the failure to the log file
+ return -5;
+ }
+
+ // Initialization is finished, so we can enter lock-down mode
+ target_service->LowerToken();
+
+ lpfnInit init_function =
+ (lpfnInit) ::GetProcAddress(dll_module, entry_point.c_str());
+
+ if (!init_function) {
+ // TODO(finnur): write the failure to the log file
+ ::FreeLibrary(dll_module);
+ CloseHandle(pipe);
+ return -6;
+ }
+
+ // Transfer control to the entry point in the DLL requested
+ init_function(pipe);
+
+ CloseHandle(pipe);
+ Sleep(1000); // Give a change to the debug output to arrive before the
+ // end of the process
+
+ ::FreeLibrary(dll_module);
+ }
+
+ return 0;
+}
diff --git a/chromium/sandbox/win/sandbox_poc/sandbox.h b/chromium/sandbox/win/sandbox_poc/sandbox.h
new file mode 100644
index 00000000000..65c09a119fa
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/sandbox.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2006-2008 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_POC_SANDBOX_H__
+#define SANDBOX_SANDBOX_POC_SANDBOX_H__
+
+#include "sandbox/win/sandbox_poc/resource.h"
+
+#endif // SANDBOX_SANDBOX_POC_SANDBOX_H__
diff --git a/chromium/sandbox/win/sandbox_poc/sandbox.ico b/chromium/sandbox/win/sandbox_poc/sandbox.ico
new file mode 100644
index 00000000000..916fa12fc2b
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/sandbox.ico
Binary files differ
diff --git a/chromium/sandbox/win/sandbox_poc/sandbox.rc b/chromium/sandbox/win/sandbox_poc/sandbox.rc
new file mode 100644
index 00000000000..978c96f6f23
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/sandbox.rc
@@ -0,0 +1,136 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_MENU_MAIN_UI MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "E&xit", ID_FILE_EXIT
+ END
+ POPUP "&Commands"
+ BEGIN
+ MENUITEM "&Spawn target", ID_COMMANDS_SPAWNTARGET
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_LAUNCH_DLL DIALOGEX 0, 0, 269, 118
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "BrokerUI: Load an Attack DLL"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Call now",IDOK,212,70,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,212,95,50,14
+ EDITTEXT IDC_DLL_NAME,7,43,200,13,ES_AUTOHSCROLL
+ LTEXT "DLL to load in target:",IDC_STATIC,7,33,168,8
+ LTEXT "Function to call:",IDC_STATIC,7,61,139,8
+ EDITTEXT IDC_ENTRY_POINT,7,71,200,13,ES_AUTOHSCROLL
+ EDITTEXT IDC_LOG_FILE,7,17,200,13,ES_AUTOHSCROLL
+ LTEXT "File for Target logging (optional):",IDC_STATIC,7,7,139,8
+ PUSHBUTTON "Browse...",IDC_BROWSE_DLL,212,42,50,14
+ PUSHBUTTON "Browse...",IDC_BROWSE_LOG,212,16,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_LAUNCH_DLL, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 262
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 111
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_SANDBOX ICON "sandbox.ico"
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/chromium/sandbox/win/sandbox_poc/sandbox_poc.vcproj b/chromium/sandbox/win/sandbox_poc/sandbox_poc.vcproj
new file mode 100644
index 00000000000..5fde1cd4078
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_poc/sandbox_poc.vcproj
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sandbox_poc"
+ ProjectGUID="{CF757839-F2A1-417C-8F25-DCAE480020F1}"
+ RootNamespace="sandbox_poc"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\main_ui_window.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\main_ui_window.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.ico"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chromium/sandbox/win/sandbox_standalone.sln b/chromium/sandbox/win/sandbox_standalone.sln
new file mode 100644
index 00000000000..529d20e09e8
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_standalone.sln
@@ -0,0 +1,127 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox", "src\sandbox.vcproj", "{881F6A97-D539-4C48-B401-DF04385B2343}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_unittests", "tests\unit_tests\sbox_unittests.vcproj", "{883553BE-2A9D-418C-A121-61FE1DFBC562}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343}
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_validation_tests", "tests\validation_tests\sbox_validation_tests.vcproj", "{B9CC7B0D-145A-49C2-B887-84E43CFA0F27}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343}
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{BCE54389-D18D-48B9-977E-9D1998200F63}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_message", "..\base\debug_message.vcproj", "{F0F92189-193A-6607-C2BB-0F98BBD19ADF}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{7F36EE20-5016-4051-B0D7-42824CDA0291}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "proof_of_concept", "proof_of_concept", "{B607BE7B-3555-422C-A40B-28E73C0B5E24}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sandbox_poc", "sandbox_poc\sandbox_poc.vcproj", "{CF757839-F2A1-417C-8F25-DCAE480020F1}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343}
+ {AE5BFB87-850E-4454-B01D-58E7D8BAC224} = {AE5BFB87-850E-4454-B01D-58E7D8BAC224}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pocdll", "sandbox_poc\pocdll\pocdll.vcproj", "{AE5BFB87-850E-4454-B01D-58E7D8BAC224}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "finder", "tools\finder\finder.vcproj", "{ACDC2E06-0366-41A4-A646-C37E130A605D}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "launcher", "tools\launcher\launcher.vcproj", "{386FA217-FBC2-4461-882D-CDAD221ED800}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sbox_integration_tests", "tests\integration_tests\sbox_integration_tests.vcproj", "{542D4B3B-98D4-4233-B68D-0103891508C6}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {881F6A97-D539-4C48-B401-DF04385B2343} = {881F6A97-D539-4C48-B401-DF04385B2343}
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "..\base\base.vcproj", "{1832A374-8A74-4F9E-B536-69A699B3E165}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "..\testing\gtest.vcproj", "{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {881F6A97-D539-4C48-B401-DF04385B2343}.Debug|Win32.ActiveCfg = Debug|Win32
+ {881F6A97-D539-4C48-B401-DF04385B2343}.Debug|Win32.Build.0 = Debug|Win32
+ {881F6A97-D539-4C48-B401-DF04385B2343}.Release|Win32.ActiveCfg = Release|Win32
+ {881F6A97-D539-4C48-B401-DF04385B2343}.Release|Win32.Build.0 = Release|Win32
+ {883553BE-2A9D-418C-A121-61FE1DFBC562}.Debug|Win32.ActiveCfg = Debug|Win32
+ {883553BE-2A9D-418C-A121-61FE1DFBC562}.Debug|Win32.Build.0 = Debug|Win32
+ {883553BE-2A9D-418C-A121-61FE1DFBC562}.Release|Win32.ActiveCfg = Release|Win32
+ {883553BE-2A9D-418C-A121-61FE1DFBC562}.Release|Win32.Build.0 = Release|Win32
+ {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Debug|Win32.Build.0 = Debug|Win32
+ {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Release|Win32.ActiveCfg = Release|Win32
+ {B9CC7B0D-145A-49C2-B887-84E43CFA0F27}.Release|Win32.Build.0 = Release|Win32
+ {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Debug|Win32.ActiveCfg = Debug|Win32
+ {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Debug|Win32.Build.0 = Debug|Win32
+ {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Release|Win32.ActiveCfg = Release|Win32
+ {F0F92189-193A-6607-C2BB-0F98BBD19ADF}.Release|Win32.Build.0 = Release|Win32
+ {CF757839-F2A1-417C-8F25-DCAE480020F1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CF757839-F2A1-417C-8F25-DCAE480020F1}.Debug|Win32.Build.0 = Debug|Win32
+ {CF757839-F2A1-417C-8F25-DCAE480020F1}.Release|Win32.ActiveCfg = Release|Win32
+ {CF757839-F2A1-417C-8F25-DCAE480020F1}.Release|Win32.Build.0 = Release|Win32
+ {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Debug|Win32.ActiveCfg = Debug|Win32
+ {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Debug|Win32.Build.0 = Debug|Win32
+ {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Release|Win32.ActiveCfg = Release|Win32
+ {AE5BFB87-850E-4454-B01D-58E7D8BAC224}.Release|Win32.Build.0 = Release|Win32
+ {ACDC2E06-0366-41A4-A646-C37E130A605D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {ACDC2E06-0366-41A4-A646-C37E130A605D}.Debug|Win32.Build.0 = Debug|Win32
+ {ACDC2E06-0366-41A4-A646-C37E130A605D}.Release|Win32.ActiveCfg = Release|Win32
+ {ACDC2E06-0366-41A4-A646-C37E130A605D}.Release|Win32.Build.0 = Release|Win32
+ {386FA217-FBC2-4461-882D-CDAD221ED800}.Debug|Win32.ActiveCfg = Debug|Win32
+ {386FA217-FBC2-4461-882D-CDAD221ED800}.Debug|Win32.Build.0 = Debug|Win32
+ {386FA217-FBC2-4461-882D-CDAD221ED800}.Release|Win32.ActiveCfg = Release|Win32
+ {386FA217-FBC2-4461-882D-CDAD221ED800}.Release|Win32.Build.0 = Release|Win32
+ {542D4B3B-98D4-4233-B68D-0103891508C6}.Debug|Win32.ActiveCfg = Debug|Win32
+ {542D4B3B-98D4-4233-B68D-0103891508C6}.Debug|Win32.Build.0 = Debug|Win32
+ {542D4B3B-98D4-4233-B68D-0103891508C6}.Release|Win32.ActiveCfg = Release|Win32
+ {542D4B3B-98D4-4233-B68D-0103891508C6}.Release|Win32.Build.0 = Release|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.ActiveCfg = Debug|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.Build.0 = Debug|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.ActiveCfg = Release|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.Build.0 = Release|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.Build.0 = Debug|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.ActiveCfg = Release|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {883553BE-2A9D-418C-A121-61FE1DFBC562} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED}
+ {B9CC7B0D-145A-49C2-B887-84E43CFA0F27} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED}
+ {542D4B3B-98D4-4233-B68D-0103891508C6} = {F7A3B82E-B8B4-4FDF-BC8E-FEC9398F57ED}
+ {F0F92189-193A-6607-C2BB-0F98BBD19ADF} = {BCE54389-D18D-48B9-977E-9D1998200F63}
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {BCE54389-D18D-48B9-977E-9D1998200F63}
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BCE54389-D18D-48B9-977E-9D1998200F63}
+ {ACDC2E06-0366-41A4-A646-C37E130A605D} = {7F36EE20-5016-4051-B0D7-42824CDA0291}
+ {386FA217-FBC2-4461-882D-CDAD221ED800} = {7F36EE20-5016-4051-B0D7-42824CDA0291}
+ {CF757839-F2A1-417C-8F25-DCAE480020F1} = {B607BE7B-3555-422C-A40B-28E73C0B5E24}
+ {AE5BFB87-850E-4454-B01D-58E7D8BAC224} = {B607BE7B-3555-422C-A40B-28E73C0B5E24}
+ EndGlobalSection
+EndGlobal
diff --git a/chromium/sandbox/win/sandbox_win.gypi b/chromium/sandbox/win/sandbox_win.gypi
new file mode 100644
index 00000000000..ab2be495fd9
--- /dev/null
+++ b/chromium/sandbox/win/sandbox_win.gypi
@@ -0,0 +1,364 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'target_defaults': {
+ 'variables': {
+ 'sandbox_windows_target': 0,
+ 'target_arch%': 'ia32',
+ },
+ 'target_conditions': [
+ ['sandbox_windows_target==1', {
+ # Files that are shared between the 32-bit and the 64-bit versions
+ # of the Windows sandbox library.
+ '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_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',
+ ],
+ 'target_conditions': [
+ ['target_arch=="x64"', {
+ 'sources': [
+ 'src/interceptors_64.cc',
+ 'src/interceptors_64.h',
+ 'src/resolver_64.cc',
+ 'src/service_resolver_64.cc',
+ 'src/Wow64_64.cc',
+ ],
+ }],
+ ['target_arch=="ia32"', {
+ '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',
+ ],
+ }],
+ ],
+ }],
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': 'static_library',
+ 'variables': {
+ 'sandbox_windows_target': 1,
+ },
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_static',
+ ],
+ 'export_dependent_settings': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'src',
+ '../..',
+ ],
+ },
+ 'target_conditions': [
+ ['target_arch=="ia32"', {
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [
+ 'wow_helper/wow_helper.exe',
+ 'wow_helper/wow_helper.pdb',
+ ],
+ },
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sbox_integration_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ '../testing/gtest.gyp:gtest',
+ ],
+ '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',
+ ],
+ },
+ {
+ 'target_name': 'sbox_validation_tests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ '../testing/gtest.gyp:gtest',
+ ],
+ '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',
+ ],
+ },
+ {
+ 'target_name': 'sbox_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ '../testing/gtest.gyp:gtest',
+ ],
+ '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',
+ ],
+ },
+ {
+ 'target_name': 'sandbox_poc',
+ 'type': 'executable',
+ 'dependencies': [
+ 'sandbox',
+ 'pocdll',
+ ],
+ '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',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '-lcomctl32.lib',
+ ],
+ },
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'SubSystem': '2', # Set /SUBSYSTEM:WINDOWS
+ },
+ },
+ },
+ {
+ 'target_name': 'pocdll',
+ 'type': 'shared_library',
+ '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',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="win" and target_arch=="ia32"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_win64',
+ 'type': 'static_library',
+ 'variables': {
+ 'sandbox_windows_target': 1,
+ 'target_arch': 'x64',
+ },
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ '../base/base.gyp:base_nacl_win64',
+ '../base/base.gyp:base_static_win64',
+ ],
+ 'configurations': {
+ 'Common_Base': {
+ 'msvs_target_platform': 'x64',
+ },
+ },
+ 'include_dirs': [
+ '../..',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'src',
+ '../..',
+ ],
+ },
+ 'defines': [
+ '<@(nacl_win64_defines)',
+ ]
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/sandbox/win/src/Wow64.cc b/chromium/sandbox/win/src/Wow64.cc
new file mode 100644
index 00000000000..39108e50b36
--- /dev/null
+++ b/chromium/sandbox/win/src/Wow64.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/wow64.h"
+
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/target_process.h"
+
+namespace {
+
+// Holds the information needed for the interception of NtMapViewOfSection on
+// 64 bits.
+// Warning: do not modify this definition without changing also the code on the
+// 64 bit helper process.
+struct PatchInfo32 {
+ HANDLE dll_load; // Event to signal the broker.
+ ULONG pad1;
+ HANDLE continue_load; // Event to wait for the broker.
+ ULONG pad2;
+ HANDLE section; // First argument of the call.
+ ULONG pad3;
+ void* orig_MapViewOfSection;
+ ULONG original_high;
+ void* signal_and_wait;
+ ULONG pad4;
+ void* patch_location;
+ ULONG patch_high;
+};
+
+// Size of the 64 bit service entry.
+const SIZE_T kServiceEntry64Size = 0x10;
+
+// Removes the interception of ntdll64.
+bool Restore64Code(HANDLE child, PatchInfo32* patch_info) {
+ PatchInfo32 local_patch_info;
+ SIZE_T actual;
+ if (!::ReadProcessMemory(child, patch_info, &local_patch_info,
+ sizeof(local_patch_info), &actual))
+ return false;
+ if (sizeof(local_patch_info) != actual)
+ return false;
+
+ if (local_patch_info.original_high)
+ return false;
+ if (local_patch_info.patch_high)
+ return false;
+
+ char buffer[kServiceEntry64Size];
+
+ if (!::ReadProcessMemory(child, local_patch_info.orig_MapViewOfSection,
+ &buffer, kServiceEntry64Size, &actual))
+ return false;
+ if (kServiceEntry64Size != actual)
+ return false;
+
+ if (!::WriteProcessMemory(child, local_patch_info.patch_location, &buffer,
+ kServiceEntry64Size, &actual))
+ return false;
+ if (kServiceEntry64Size != actual)
+ return false;
+ return true;
+}
+
+typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64);
+
+} // namespace
+
+namespace sandbox {
+
+Wow64::~Wow64() {
+ if (dll_load_)
+ ::CloseHandle(dll_load_);
+
+ if (continue_load_)
+ ::CloseHandle(continue_load_);
+}
+
+// The basic idea is to allocate one page of memory on the child, and initialize
+// the first part of it with our version of PatchInfo32. Then launch the helper
+// process passing it that address on the child. The helper process will patch
+// the 64 bit version of NtMapViewOfFile, and the interception will signal the
+// first event on the buffer. We'll be waiting on that event and after the 32
+// bit version of ntdll is loaded, we'll remove the interception and return to
+// our caller.
+bool Wow64::WaitForNtdll() {
+ if (base::win::OSInfo::GetInstance()->wow64_status() !=
+ base::win::OSInfo::WOW64_ENABLED)
+ return true;
+
+ const size_t page_size = 4096;
+
+ // Create some default manual reset un-named events, not signaled.
+ dll_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ continue_load_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ HANDLE current_process = ::GetCurrentProcess();
+ HANDLE remote_load, remote_continue;
+ DWORD access = EVENT_MODIFY_STATE | SYNCHRONIZE;
+ if (!::DuplicateHandle(current_process, dll_load_, child_->Process(),
+ &remote_load, access, FALSE, 0))
+ return false;
+ if (!::DuplicateHandle(current_process, continue_load_, child_->Process(),
+ &remote_continue, access, FALSE, 0))
+ return false;
+
+ void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ DCHECK(buffer);
+ if (!buffer)
+ return false;
+
+ PatchInfo32* patch_info = reinterpret_cast<PatchInfo32*>(buffer);
+ PatchInfo32 local_patch_info = {0};
+ local_patch_info.dll_load = remote_load;
+ local_patch_info.continue_load = remote_continue;
+ SIZE_T written;
+ if (!::WriteProcessMemory(child_->Process(), patch_info, &local_patch_info,
+ offsetof(PatchInfo32, section), &written))
+ return false;
+ if (offsetof(PatchInfo32, section) != written)
+ return false;
+
+ if (!RunWowHelper(buffer))
+ return false;
+
+ // The child is intercepted on 64 bit, go on and wait for our event.
+ if (!DllMapped())
+ return false;
+
+ // The 32 bit version is available, cleanup the child.
+ return Restore64Code(child_->Process(), patch_info);
+}
+
+bool Wow64::RunWowHelper(void* buffer) {
+ COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits);
+
+ // Get the path to the helper (beside the exe).
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+ std::wstring path(prog_name);
+ size_t name_pos = path.find_last_of(L"\\");
+ if (std::wstring::npos == name_pos)
+ return false;
+ path.resize(name_pos + 1);
+
+ std::wstringstream command;
+ command << std::hex << std::showbase << L"\"" << path <<
+ L"wow_helper.exe\" " << child_->ProcessId() << " " <<
+ bit_cast<ULONG>(buffer);
+
+ scoped_ptr_malloc<wchar_t> writable_command(_wcsdup(command.str().c_str()));
+
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ base::win::ScopedProcessInformation process_info;
+ if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
+ NULL, &startup_info, process_info.Receive()))
+ return false;
+
+ DWORD reason = ::WaitForSingleObject(process_info.process_handle(), INFINITE);
+
+ DWORD code;
+ bool ok =
+ ::GetExitCodeProcess(process_info.process_handle(), &code) ? true : false;
+
+ if (WAIT_TIMEOUT == reason)
+ return false;
+
+ return ok && (0 == code);
+}
+
+// First we must wake up the child, then wait for dll loads on the child until
+// the one we care is loaded; at that point we must suspend the child again.
+bool Wow64::DllMapped() {
+ if (1 != ::ResumeThread(child_->MainThread())) {
+ NOTREACHED();
+ return false;
+ }
+
+ for (;;) {
+ DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE);
+ if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason)
+ return false;
+
+ if (!::ResetEvent(dll_load_))
+ return false;
+
+ bool found = NtdllPresent();
+ if (found) {
+ if (::SuspendThread(child_->MainThread()))
+ return false;
+ }
+
+ if (!::SetEvent(continue_load_))
+ return false;
+
+ if (found)
+ return true;
+ }
+}
+
+bool Wow64::NtdllPresent() {
+ const size_t kBufferSize = 512;
+ char buffer[kBufferSize];
+ SIZE_T read;
+ if (!::ReadProcessMemory(child_->Process(), ntdll_, &buffer, kBufferSize,
+ &read))
+ return false;
+ if (kBufferSize != read)
+ return false;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/Wow64.h b/chromium/sandbox/win/src/Wow64.h
new file mode 100644
index 00000000000..e9bbd53d9d0
--- /dev/null
+++ b/chromium/sandbox/win/src/Wow64.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 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_WOW64_H__
+#define SANDBOX_SRC_WOW64_H__
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+class TargetProcess;
+
+// This class wraps the code needed to interact with the Windows On Windows
+// subsystem on 64 bit OSes, from the point of view of interceptions.
+class Wow64 {
+ public:
+ Wow64(TargetProcess* child, HMODULE ntdll)
+ : child_(child), ntdll_(ntdll), dll_load_(NULL), continue_load_(NULL) {}
+ ~Wow64();
+
+ // Waits for the 32 bit DLL to get loaded on the child process. This function
+ // will return immediately if not running under WOW, or launch the helper
+ // process and wait until ntdll is ready.
+ bool WaitForNtdll();
+
+ private:
+ // Runs the WOW helper process, passing the address of a buffer allocated on
+ // the child (one page).
+ bool RunWowHelper(void* buffer);
+
+ // This method receives "notifications" whenever a DLL is mapped on the child.
+ bool DllMapped();
+
+ // Returns true if ntdll.dll is mapped on the child.
+ bool NtdllPresent();
+
+ TargetProcess* child_; // Child process.
+ HMODULE ntdll_; // ntdll on the parent.
+ HANDLE dll_load_; // Event that is signaled on dll load.
+ HANDLE continue_load_; // Event to signal to continue execution on the child.
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Wow64);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WOW64_H__
diff --git a/chromium/sandbox/win/src/Wow64_64.cc b/chromium/sandbox/win/src/Wow64_64.cc
new file mode 100644
index 00000000000..f03831bd115
--- /dev/null
+++ b/chromium/sandbox/win/src/Wow64_64.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2011 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.
+
+// Wow64 implementation for native 64-bit Windows (in other words, never WOW).
+
+#include "sandbox/win/src/wow64.h"
+
+namespace sandbox {
+
+Wow64::~Wow64() {
+}
+
+bool Wow64::WaitForNtdll() {
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/acl.cc b/chromium/sandbox/win/src/acl.cc
new file mode 100644
index 00000000000..70d2a8d3109
--- /dev/null
+++ b/chromium/sandbox/win/src/acl.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/acl.h"
+
+#include <aclapi.h>
+#include <sddl.h>
+
+#include "base/logging.h"
+
+namespace sandbox {
+
+bool GetDefaultDacl(HANDLE token,
+ scoped_ptr_malloc<TOKEN_DEFAULT_DACL>* default_dacl) {
+ if (token == NULL)
+ return false;
+
+ DCHECK(default_dacl != NULL);
+
+ unsigned long length = 0;
+ ::GetTokenInformation(token, TokenDefaultDacl, NULL, 0, &length);
+ if (length == 0) {
+ NOTREACHED();
+ return false;
+ }
+
+ TOKEN_DEFAULT_DACL* acl =
+ reinterpret_cast<TOKEN_DEFAULT_DACL*>(malloc(length));
+ default_dacl->reset(acl);
+
+ if (!::GetTokenInformation(token, TokenDefaultDacl, default_dacl->get(),
+ length, &length))
+ return false;
+
+ return true;
+}
+
+bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access,
+ ACL** new_dacl) {
+ EXPLICIT_ACCESS new_access = {0};
+ new_access.grfAccessMode = GRANT_ACCESS;
+ new_access.grfAccessPermissions = access;
+ new_access.grfInheritance = NO_INHERITANCE;
+
+ new_access.Trustee.pMultipleTrustee = NULL;
+ new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(
+ const_cast<SID*>(sid.GetPSID()));
+
+ if (ERROR_SUCCESS != ::SetEntriesInAcl(1, &new_access, old_dacl, new_dacl))
+ return false;
+
+ return true;
+}
+
+bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access) {
+ if (token == NULL)
+ return false;
+
+ scoped_ptr_malloc<TOKEN_DEFAULT_DACL> default_dacl;
+ if (!GetDefaultDacl(token, &default_dacl))
+ return false;
+
+ ACL* new_dacl = NULL;
+ if (!AddSidToDacl(sid, default_dacl->DefaultDacl, access, &new_dacl))
+ return false;
+
+ TOKEN_DEFAULT_DACL new_token_dacl = {0};
+ new_token_dacl.DefaultDacl = new_dacl;
+
+ BOOL ret = ::SetTokenInformation(token, TokenDefaultDacl, &new_token_dacl,
+ sizeof(new_token_dacl));
+ ::LocalFree(new_dacl);
+ return (TRUE == ret);
+}
+
+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);
+
+ if (!::GetTokenInformation(token, TokenUser, token_user, size, &size))
+ return false;
+
+ return AddSidToDefaultDacl(token,
+ reinterpret_cast<SID*>(token_user->User.Sid),
+ access);
+}
+
+bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid,
+ ACCESS_MASK access) {
+ PSECURITY_DESCRIPTOR descriptor = NULL;
+ PACL old_dacl = NULL;
+ PACL new_dacl = NULL;
+
+ if (ERROR_SUCCESS != ::GetSecurityInfo(object, SE_KERNEL_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL,
+ &old_dacl, NULL, &descriptor))
+ return false;
+
+ if (!AddSidToDacl(sid.GetPSID(), old_dacl, access, &new_dacl)) {
+ ::LocalFree(descriptor);
+ return false;
+ }
+
+ DWORD result = ::SetSecurityInfo(object, SE_KERNEL_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL,
+ new_dacl, NULL);
+
+ ::LocalFree(new_dacl);
+ ::LocalFree(descriptor);
+
+ if (ERROR_SUCCESS != result)
+ return false;
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/acl.h b/chromium/sandbox/win/src/acl.h
new file mode 100644
index 00000000000..25d5cdb1611
--- /dev/null
+++ b/chromium/sandbox/win/src/acl.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_ACL_H_
+#define SANDBOX_SRC_ACL_H_
+
+#include <windows.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+// Returns the default dacl from the token passed in.
+bool GetDefaultDacl(HANDLE token,
+ scoped_ptr_malloc<TOKEN_DEFAULT_DACL>* default_dacl);
+
+// Appends an ACE represented by |sid| and |access| to |old_dacl|. If the
+// function succeeds, new_dacl contains the new dacl and must be freed using
+// LocalFree.
+bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MASK access,
+ ACL** new_dacl);
+
+// Adds and ACE represented by |sid| and |access| to the default dacl present
+// in the token.
+bool AddSidToDefaultDacl(HANDLE token, const Sid& sid, ACCESS_MASK access);
+
+// Adds an ACE represented by the user sid and |access| to the default dacl
+// present in the token.
+bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access);
+
+// Adds an ACE represented by |known_sid| and |access| to the dacl of the kernel
+// object referenced by |object|.
+bool AddKnownSidToKernelObject(HANDLE object, const Sid& sid,
+ ACCESS_MASK access);
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_ACL_H_
diff --git a/chromium/sandbox/win/src/app_container.cc b/chromium/sandbox/win/src/app_container.cc
new file mode 100644
index 00000000000..ee978b9515d
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/app_container.h"
+
+#include <Sddl.h>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/startup_information.h"
+#include "sandbox/win/src/internal_types.h"
+
+namespace {
+
+// Converts the passed in sid string to a PSID that must be relased with
+// LocalFree.
+PSID ConvertSid(const string16& sid) {
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return NULL;
+ return local_sid;
+}
+
+} // namespace
+
+namespace sandbox {
+
+AppContainerAttributes::AppContainerAttributes() {
+ memset(&capabilities_, 0, sizeof(capabilities_));
+}
+
+AppContainerAttributes::~AppContainerAttributes() {
+ for (size_t i = 0; i < attributes_.size(); i++)
+ LocalFree(attributes_[i].Sid);
+ LocalFree(capabilities_.AppContainerSid);
+}
+
+ResultCode AppContainerAttributes::SetAppContainer(
+ const string16& app_container_sid,
+ const std::vector<string16>& capabilities) {
+ DCHECK(!capabilities_.AppContainerSid);
+ DCHECK(attributes_.empty());
+ capabilities_.AppContainerSid = ConvertSid(app_container_sid);
+ if (!capabilities_.AppContainerSid)
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ for (size_t i = 0; i < capabilities.size(); i++) {
+ SID_AND_ATTRIBUTES sid_and_attributes;
+ sid_and_attributes.Sid = ConvertSid(capabilities[i]);
+ if (!sid_and_attributes.Sid)
+ return SBOX_ERROR_INVALID_CAPABILITY;
+
+ sid_and_attributes.Attributes = SE_GROUP_ENABLED;
+ attributes_.push_back(sid_and_attributes);
+ }
+
+ if (capabilities.size()) {
+ capabilities_.CapabilityCount = static_cast<DWORD>(capabilities.size());
+ capabilities_.Capabilities = &attributes_[0];
+ }
+ return SBOX_ALL_OK;
+}
+
+ResultCode AppContainerAttributes::ShareForStartup(
+ base::win::StartupInformation* startup_information) const {
+ // The only thing we support so far is an AppContainer.
+ if (!capabilities_.AppContainerSid)
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ if (!startup_information->UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
+ const_cast<SECURITY_CAPABILITIES*>(&capabilities_),
+ sizeof(capabilities_))) {
+ DPLOG(ERROR) << "Failed UpdateProcThreadAttribute";
+ return SBOX_ERROR_CANNOT_INIT_APPCONTAINER;
+ }
+ return SBOX_ALL_OK;
+}
+
+bool AppContainerAttributes::HasAppContainer() const {
+ return (capabilities_.AppContainerSid != NULL);
+}
+
+ResultCode CreateAppContainer(const string16& sid, const string16& name) {
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ typedef HRESULT (WINAPI* AppContainerRegisterSidPtr)(PSID sid,
+ LPCWSTR moniker,
+ LPCWSTR display_name);
+ static AppContainerRegisterSidPtr AppContainerRegisterSid = NULL;
+
+ if (!AppContainerRegisterSid) {
+ HMODULE module = GetModuleHandle(kKerneldllName);
+ AppContainerRegisterSid = reinterpret_cast<AppContainerRegisterSidPtr>(
+ GetProcAddress(module, "AppContainerRegisterSid"));
+ }
+
+ ResultCode operation_result = SBOX_ERROR_GENERIC;
+ if (AppContainerRegisterSid) {
+ HRESULT rv = AppContainerRegisterSid(local_sid, name.c_str(), name.c_str());
+ if (SUCCEEDED(rv))
+ operation_result = SBOX_ALL_OK;
+ else
+ DLOG(ERROR) << "AppContainerRegisterSid error:" << std::hex << rv;
+ }
+ LocalFree(local_sid);
+ return operation_result;
+}
+
+ResultCode DeleteAppContainer(const string16& sid) {
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ typedef HRESULT (WINAPI* AppContainerUnregisterSidPtr)(PSID sid);
+ static AppContainerUnregisterSidPtr AppContainerUnregisterSid = NULL;
+
+ if (!AppContainerUnregisterSid) {
+ HMODULE module = GetModuleHandle(kKerneldllName);
+ AppContainerUnregisterSid = reinterpret_cast<AppContainerUnregisterSidPtr>(
+ GetProcAddress(module, "AppContainerUnregisterSid"));
+ }
+
+ ResultCode operation_result = SBOX_ERROR_GENERIC;
+ if (AppContainerUnregisterSid) {
+ HRESULT rv = AppContainerUnregisterSid(local_sid);
+ if (SUCCEEDED(rv))
+ operation_result = SBOX_ALL_OK;
+ else
+ DLOG(ERROR) << "AppContainerUnregisterSid error:" << std::hex << rv;
+ }
+ LocalFree(local_sid);
+ return operation_result;
+}
+
+string16 LookupAppContainer(const string16& sid) {
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return string16();
+
+ typedef HRESULT (WINAPI* AppContainerLookupMonikerPtr)(PSID sid,
+ LPWSTR* moniker);
+ typedef BOOLEAN (WINAPI* AppContainerFreeMemoryPtr)(void* ptr);
+
+ static AppContainerLookupMonikerPtr AppContainerLookupMoniker = NULL;
+ static AppContainerFreeMemoryPtr AppContainerFreeMemory = NULL;
+
+ if (!AppContainerLookupMoniker || !AppContainerFreeMemory) {
+ HMODULE module = GetModuleHandle(kKerneldllName);
+ AppContainerLookupMoniker = reinterpret_cast<AppContainerLookupMonikerPtr>(
+ GetProcAddress(module, "AppContainerLookupMoniker"));
+ AppContainerFreeMemory = reinterpret_cast<AppContainerFreeMemoryPtr>(
+ GetProcAddress(module, "AppContainerFreeMemory"));
+ }
+
+ if (!AppContainerLookupMoniker || !AppContainerFreeMemory)
+ return string16();
+
+ wchar_t* buffer = NULL;
+ HRESULT rv = AppContainerLookupMoniker(local_sid, &buffer);
+ if (FAILED(rv))
+ return string16();
+
+ string16 name(buffer);
+ if (!AppContainerFreeMemory(buffer))
+ NOTREACHED();
+ return name;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/app_container.h b/chromium/sandbox/win/src/app_container.h
new file mode 100644
index 00000000000..34b43e95f57
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_APP_CONTAINER_H_
+#define SANDBOX_WIN_SRC_APP_CONTAINER_H_
+
+#include <windows.h>
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace base {
+namespace win {
+class StartupInformation;
+}
+}
+
+namespace sandbox {
+
+// Maintains an attribute list to be used during creation of a new sandboxed
+// process.
+class AppContainerAttributes {
+ public:
+ 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);
+
+ // Updates the proc_thred attribute list of the provided startup_information
+ // with the app container related data.
+ // WARNING: startup_information just points back to our internal memory, so
+ // the lifetime of this object has to be greater than the lifetime of the
+ // provided startup_information.
+ ResultCode ShareForStartup(
+ base::win::StartupInformation* startup_information) const;
+
+ bool HasAppContainer() const;
+
+ private:
+ SECURITY_CAPABILITIES capabilities_;
+ std::vector<SID_AND_ATTRIBUTES> attributes_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppContainerAttributes);
+};
+
+// Creates a new AppContainer on the system. |sid| is the identifier of the new
+// 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);
+
+// Deletes an AppContainer previously created with a successfull call to
+// CreateAppContainer.
+ResultCode DeleteAppContainer(const 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);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_APP_CONTAINER_H_
diff --git a/chromium/sandbox/win/src/app_container_test.cc b/chromium/sandbox/win/src/app_container_test.cc
new file mode 100644
index 00000000000..3b33ca5c901
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container_test.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#define _ATL_NO_EXCEPTIONS
+#include <atlbase.h>
+#include <atlsecurity.h>
+
+#include "base/strings/string16.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sync_policy_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const wchar_t kAppContainerName[] = L"sbox_test";
+const wchar_t kAppContainerSid[] =
+ L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-"
+ L"924012148-2839372144";
+
+const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+
+HANDLE CreateTaggedEvent(const string16& name, const string16& sid) {
+ base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, name.c_str()));
+ if (!event.IsValid())
+ return NULL;
+
+ wchar_t file_name[MAX_PATH] = {};
+ wchar_t temp_directory[MAX_PATH] = {};
+ GetTempPath(MAX_PATH, temp_directory);
+ GetTempFileName(temp_directory, L"test", 0, file_name);
+
+ base::win::ScopedHandle file;
+ file.Set(CreateFile(file_name, GENERIC_READ | STANDARD_RIGHTS_READ, kSharing,
+ NULL, OPEN_EXISTING, 0, NULL));
+ DeleteFile(file_name);
+ if (!file.IsValid())
+ return NULL;
+
+ CSecurityDesc sd;
+ if (!AtlGetSecurityDescriptor(file.Get(), SE_FILE_OBJECT, &sd,
+ OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION)) {
+ return NULL;
+ }
+
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return NULL;
+
+ CDacl new_dacl;
+ sd.GetDacl(&new_dacl);
+ CSid csid(reinterpret_cast<SID*>(local_sid));
+ new_dacl.AddAllowedAce(csid, EVENT_ALL_ACCESS);
+ if (!AtlSetDacl(event.Get(), SE_KERNEL_OBJECT, new_dacl))
+ event.Close();
+
+ LocalFree(local_sid);
+ return event.IsValid() ? event.Take() : NULL;
+}
+
+} // namespace
+
+namespace sandbox {
+
+TEST(AppContainerTest, AllowOpenEvent) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
+
+ const wchar_t capability[] = L"S-1-15-3-12345678-87654321";
+ base::win::ScopedHandle handle(CreateTaggedEvent(L"test", capability));
+ ASSERT_TRUE(handle.IsValid());
+
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.broker()->InstallAppContainer(kAppContainerSid,
+ kAppContainerName));
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetCapability(capability));
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetAppContainer(kAppContainerSid));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open f test"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open f test"));
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.broker()->UninstallAppContainer(kAppContainerSid));
+}
+
+TEST(AppContainerTest, DenyOpenEvent) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
+
+ const wchar_t capability[] = L"S-1-15-3-12345678-87654321";
+ base::win::ScopedHandle handle(CreateTaggedEvent(L"test", capability));
+ ASSERT_TRUE(handle.IsValid());
+
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.broker()->InstallAppContainer(kAppContainerSid,
+ kAppContainerName));
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetAppContainer(kAppContainerSid));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.broker()->UninstallAppContainer(kAppContainerSid));
+}
+
+TEST(AppContainerTest, NoImpersonation) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_LIMITED, USER_LIMITED);
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetAppContainer(kAppContainerSid));
+}
+
+TEST(AppContainerTest, WantsImpersonation) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_NON_ADMIN);
+ EXPECT_EQ(SBOX_ERROR_CANNOT_INIT_APPCONTAINER,
+ runner.GetPolicy()->SetAppContainer(kAppContainerSid));
+}
+
+TEST(AppContainerTest, RequiresImpersonation) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_RESTRICTED, USER_RESTRICTED);
+ EXPECT_EQ(SBOX_ERROR_CANNOT_INIT_APPCONTAINER,
+ runner.GetPolicy()->SetAppContainer(kAppContainerSid));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/app_container_unittest.cc b/chromium/sandbox/win/src/app_container_unittest.cc
new file mode 100644
index 00000000000..936a9cbc7bf
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests the low level AppContainer interface.
+TEST(AppContainerTest, CreateAppContainer) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ const wchar_t kName[] = L"Test";
+ const wchar_t kValidSid[] = L"S-1-15-2-12345-234-567-890-123-456-789";
+
+ EXPECT_TRUE(LookupAppContainer(kValidSid).empty());
+ EXPECT_EQ(SBOX_ERROR_GENERIC, DeleteAppContainer(kValidSid));
+
+ EXPECT_EQ(SBOX_ALL_OK, CreateAppContainer(kValidSid, kName));
+ EXPECT_EQ(SBOX_ERROR_GENERIC, CreateAppContainer(kValidSid, kName));
+ EXPECT_EQ(kName, LookupAppContainer(kValidSid));
+ EXPECT_EQ(SBOX_ALL_OK, DeleteAppContainer(kValidSid));
+
+ EXPECT_TRUE(LookupAppContainer(kValidSid).empty());
+ EXPECT_EQ(SBOX_ERROR_GENERIC, DeleteAppContainer(kValidSid));
+
+ EXPECT_EQ(SBOX_ERROR_INVALID_APP_CONTAINER,
+ CreateAppContainer(L"Foo", kName));
+}
+
+// Tests handling of security capabilities on the attribute list.
+TEST(AppContainerTest, SecurityCapabilities) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ scoped_ptr<AppContainerAttributes> attributes(new AppContainerAttributes);
+ std::vector<string16> capabilities;
+ EXPECT_EQ(SBOX_ERROR_INVALID_APP_CONTAINER,
+ attributes->SetAppContainer(L"S-1-foo", capabilities));
+
+ EXPECT_EQ(SBOX_ALL_OK,
+ attributes->SetAppContainer(L"S-1-15-2-12345-234", capabilities));
+ EXPECT_TRUE(attributes->HasAppContainer());
+
+ attributes.reset(new AppContainerAttributes);
+ capabilities.push_back(L"S-1-15-3-12345678-87654321");
+ capabilities.push_back(L"S-1-15-3-1");
+ capabilities.push_back(L"S-1-15-3-2");
+ capabilities.push_back(L"S-1-15-3-3");
+ EXPECT_EQ(SBOX_ALL_OK,
+ attributes->SetAppContainer(L"S-1-15-2-1-2", capabilities));
+ EXPECT_TRUE(attributes->HasAppContainer());
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc
new file mode 100644
index 00000000000..921eb4f89e0
--- /dev/null
+++ b/chromium/sandbox/win/src/broker_services.cc
@@ -0,0 +1,510 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/broker_services.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Utility function to associate a completion port to a job object.
+bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) {
+ JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = { key, port };
+ return ::SetInformationJobObject(job,
+ JobObjectAssociateCompletionPortInformation,
+ &job_acp, sizeof(job_acp))? true : false;
+}
+
+// Utility function to do the cleanup necessary when something goes wrong
+// while in SpawnTarget and we must terminate the target process.
+sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target, DWORD error) {
+ if (0 == error)
+ error = ::GetLastError();
+
+ target->Terminate();
+ delete target;
+ ::SetLastError(error);
+ return sandbox::SBOX_ERROR_GENERIC;
+}
+
+// the different commands that you can send to the worker thread that
+// executes TargetEventsThread().
+enum {
+ THREAD_CTRL_NONE,
+ THREAD_CTRL_REMOVE_PEER,
+ THREAD_CTRL_QUIT,
+ THREAD_CTRL_LAST,
+};
+
+// Helper structure that allows the Broker to associate a job notification
+// with a job object and with a policy.
+struct JobTracker {
+ HANDLE job;
+ sandbox::PolicyBase* policy;
+ JobTracker(HANDLE cjob, sandbox::PolicyBase* cpolicy)
+ : job(cjob), policy(cpolicy) {
+ }
+};
+
+// Helper structure that allows the broker to track peer processes
+struct PeerTracker {
+ HANDLE wait_object;
+ base::win::ScopedHandle process;
+ DWORD id;
+ HANDLE job_port;
+ PeerTracker(DWORD process_id, HANDLE broker_job_port)
+ : wait_object(NULL), id(process_id), job_port(broker_job_port) {
+ }
+};
+
+void DeregisterPeerTracker(PeerTracker* peer) {
+ // Deregistration shouldn't fail, but we leak rather than crash if it does.
+ if (::UnregisterWaitEx(peer->wait_object, INVALID_HANDLE_VALUE)) {
+ delete peer;
+ } else {
+ NOTREACHED();
+ }
+}
+
+} // namespace
+
+namespace sandbox {
+
+BrokerServicesBase::BrokerServicesBase()
+ : thread_pool_(NULL), job_port_(NULL), no_targets_(NULL),
+ job_thread_(NULL) {
+}
+
+// The broker uses a dedicated worker thread that services the job completion
+// port to perform policy notifications and associated cleanup tasks.
+ResultCode BrokerServicesBase::Init() {
+ if ((NULL != job_port_) || (NULL != thread_pool_))
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ ::InitializeCriticalSection(&lock_);
+
+ job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (NULL == job_port_)
+ return SBOX_ERROR_GENERIC;
+
+ no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL);
+
+ job_thread_ = ::CreateThread(NULL, 0, // Default security and stack.
+ TargetEventsThread, this, NULL, NULL);
+ if (NULL == job_thread_)
+ return SBOX_ERROR_GENERIC;
+
+ return SBOX_ALL_OK;
+}
+
+// The destructor should only be called when the Broker process is terminating.
+// Since BrokerServicesBase is a singleton, this is called from the CRT
+// termination handlers, if this code lives on a DLL it is called during
+// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot
+// wait for threads here.
+BrokerServicesBase::~BrokerServicesBase() {
+ // If there is no port Init() was never called successfully.
+ if (!job_port_)
+ return;
+
+ // Closing the port causes, that no more Job notifications are delivered to
+ // the worker thread and also causes the thread to exit. This is what we
+ // want to do since we are going to close all outstanding Jobs and notifying
+ // the policy objects ourselves.
+ ::PostQueuedCompletionStatus(job_port_, 0, THREAD_CTRL_QUIT, FALSE);
+ ::CloseHandle(job_port_);
+
+ if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) {
+ // Cannot clean broker services.
+ NOTREACHED();
+ return;
+ }
+
+ JobTrackerList::iterator it;
+ for (it = tracker_list_.begin(); it != tracker_list_.end(); ++it) {
+ JobTracker* tracker = (*it);
+ FreeResources(tracker);
+ delete tracker;
+ }
+ ::CloseHandle(job_thread_);
+ delete thread_pool_;
+ ::CloseHandle(no_targets_);
+
+ // Cancel the wait events and delete remaining peer trackers.
+ for (PeerTrackerMap::iterator it = peer_map_.begin();
+ it != peer_map_.end(); ++it) {
+ DeregisterPeerTracker(it->second);
+ }
+
+ // If job_port_ isn't NULL, assumes that the lock has been initialized.
+ if (job_port_)
+ ::DeleteCriticalSection(&lock_);
+}
+
+TargetPolicy* BrokerServicesBase::CreatePolicy() {
+ // If you change the type of the object being created here you must also
+ // change the downcast to it in SpawnTarget().
+ return new PolicyBase;
+}
+
+void BrokerServicesBase::FreeResources(JobTracker* tracker) {
+ if (NULL != tracker->policy) {
+ BOOL res = ::TerminateJobObject(tracker->job, SBOX_ALL_OK);
+ DCHECK(res);
+ // Closing the job causes the target process to be destroyed so this
+ // needs to happen before calling OnJobEmpty().
+ res = ::CloseHandle(tracker->job);
+ DCHECK(res);
+ // In OnJobEmpty() we don't actually use the job handle directly.
+ tracker->policy->OnJobEmpty(tracker->job);
+ tracker->policy->Release();
+ tracker->policy = NULL;
+ }
+}
+
+// The worker thread stays in a loop waiting for asynchronous notifications
+// from the job objects. Right now we only care about knowing when the last
+// process on a job terminates, but in general this is the place to tell
+// the policy about events.
+DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
+ if (NULL == param)
+ return 1;
+
+ base::PlatformThread::SetName("BrokerEvent");
+
+ BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
+ HANDLE port = broker->job_port_;
+ HANDLE no_targets = broker->no_targets_;
+
+ int target_counter = 0;
+ ::ResetEvent(no_targets);
+
+ while (true) {
+ DWORD events = 0;
+ ULONG_PTR key = 0;
+ LPOVERLAPPED ovl = NULL;
+
+ if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE))
+ // this call fails if the port has been closed before we have a
+ // chance to service the last packet which is 'exit' anyway so
+ // this is not an error.
+ return 1;
+
+ if (key > THREAD_CTRL_LAST) {
+ // The notification comes from a job object. There are nine notifications
+ // that jobs can send and some of them depend on the job attributes set.
+ JobTracker* tracker = reinterpret_cast<JobTracker*>(key);
+
+ switch (events) {
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: {
+ // The job object has signaled that the last process associated
+ // with it has terminated. Assuming there is no way for a process
+ // to appear out of thin air in this job, it safe to assume that
+ // we can tell the policy to destroy the target object, and for
+ // us to release our reference to the policy object.
+ FreeResources(tracker);
+ break;
+ }
+
+ case JOB_OBJECT_MSG_NEW_PROCESS: {
+ ++target_counter;
+ if (1 == target_counter) {
+ ::ResetEvent(no_targets);
+ }
+ break;
+ }
+
+ case JOB_OBJECT_MSG_EXIT_PROCESS:
+ case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: {
+ {
+ AutoLock lock(&broker->lock_);
+ broker->child_process_ids_.erase(reinterpret_cast<DWORD>(ovl));
+ }
+ --target_counter;
+ if (0 == target_counter)
+ ::SetEvent(no_targets);
+
+ DCHECK(target_counter >= 0);
+ break;
+ }
+
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
+ break;
+ }
+
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ } else if (THREAD_CTRL_REMOVE_PEER == key) {
+ // Remove a process from our list of peers.
+ AutoLock lock(&broker->lock_);
+ PeerTrackerMap::iterator it =
+ broker->peer_map_.find(reinterpret_cast<DWORD>(ovl));
+ DeregisterPeerTracker(it->second);
+ broker->peer_map_.erase(it);
+ } else if (THREAD_CTRL_QUIT == key) {
+ // The broker object is being destroyed so the thread needs to exit.
+ return 0;
+ } else {
+ // We have not implemented more commands.
+ NOTREACHED();
+ }
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+// SpawnTarget does all the interesting sandbox setup and creates the target
+// process inside the sandbox.
+ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ TargetPolicy* policy,
+ PROCESS_INFORMATION* target_info) {
+ if (!exe_path)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!policy)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ // Even though the resources touched by SpawnTarget can be accessed in
+ // multiple threads, the method itself cannot be called from more than
+ // 1 thread. This is to protect the global variables used while setting up
+ // the child process.
+ static DWORD thread_id = ::GetCurrentThreadId();
+ DCHECK(thread_id == ::GetCurrentThreadId());
+
+ AutoLock lock(&lock_);
+
+ // This downcast is safe as long as we control CreatePolicy()
+ PolicyBase* policy_base = static_cast<PolicyBase*>(policy);
+
+ // Construct the tokens and the job object that we are going to associate
+ // 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;
+
+ base::win::ScopedHandle initial_token(initial_token_temp);
+ base::win::ScopedHandle lockdown_token(lockdown_token_temp);
+
+ HANDLE job_temp;
+ result = policy_base->MakeJobObject(&job_temp);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ base::win::ScopedHandle job(job_temp);
+
+ // Initialize the startup information from the policy.
+ base::win::StartupInformation startup_info;
+ string16 desktop = policy_base->GetAlternateDesktop();
+ if (!desktop.empty()) {
+ startup_info.startup_info()->lpDesktop =
+ const_cast<wchar_t*>(desktop.c_str());
+ }
+
+ bool inherit_handles = false;
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+ int attribute_count = 0;
+ const AppContainerAttributes* app_container =
+ policy_base->GetAppContainer();
+ if (app_container)
+ ++attribute_count;
+
+ DWORD64 mitigations;
+ size_t mitigations_size;
+ ConvertProcessMitigationsToPolicy(policy->GetProcessMitigations(),
+ &mitigations, &mitigations_size);
+ if (mitigations)
+ ++attribute_count;
+
+ HANDLE stdout_handle = policy_base->GetStdoutHandle();
+ HANDLE stderr_handle = policy_base->GetStderrHandle();
+ HANDLE inherit_handle_list[2];
+ int inherit_handle_count = 0;
+ if (stdout_handle != INVALID_HANDLE_VALUE)
+ inherit_handle_list[inherit_handle_count++] = stdout_handle;
+ // Handles in the list must be unique.
+ if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
+ inherit_handle_list[inherit_handle_count++] = stderr_handle;
+ if (inherit_handle_count)
+ ++attribute_count;
+
+ if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+
+ if (app_container) {
+ result = app_container->ShareForStartup(&startup_info);
+ if (SBOX_ALL_OK != result)
+ return result;
+ }
+
+ if (mitigations) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations,
+ mitigations_size)) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (inherit_handle_count) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ inherit_handle_list,
+ sizeof(inherit_handle_list[0]) * inherit_handle_count)) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
+ startup_info.startup_info()->hStdOutput = stdout_handle;
+ startup_info.startup_info()->hStdError = stderr_handle;
+ // Allowing inheritance of handles is only secure now that we
+ // have limited which handles will be inherited.
+ inherit_handles = true;
+ }
+ }
+
+ // Construct the thread pool here in case it is expensive.
+ // The thread pool is shared by all the targets
+ if (NULL == thread_pool_)
+ thread_pool_ = new Win2kThreadPool();
+
+ // Create the TargetProces object and spawn the target suspended. Note that
+ // Brokerservices does not own the target object. It is owned by the Policy.
+ base::win::ScopedProcessInformation process_info;
+ TargetProcess* target = new TargetProcess(initial_token.Take(),
+ lockdown_token.Take(),
+ job,
+ thread_pool_);
+
+ DWORD win_result = target->Create(exe_path, command_line, inherit_handles,
+ startup_info, &process_info);
+ if (ERROR_SUCCESS != win_result)
+ return SpawnCleanup(target, win_result);
+
+ // Now the policy is the owner of the target.
+ if (!policy_base->AddTarget(target)) {
+ return SpawnCleanup(target, 0);
+ }
+
+ // We are going to keep a pointer to the policy because we'll call it when
+ // the job object generates notifications using the completion port.
+ policy_base->AddRef();
+ if (job.IsValid()) {
+ scoped_ptr<JobTracker> tracker(new JobTracker(job.Take(), policy_base));
+ if (!AssociateCompletionPort(tracker->job, job_port_, tracker.get()))
+ return SpawnCleanup(target, 0);
+ // Save the tracker because in cleanup we might need to force closing
+ // the Jobs.
+ tracker_list_.push_back(tracker.release());
+ child_process_ids_.insert(process_info.process_id());
+ } else {
+ // We have to signal the event once here because the completion port will
+ // never get a message that this target is being terminated thus we should
+ // not block WaitForAllTargets until we have at least one target with job.
+ if (child_process_ids_.empty())
+ ::SetEvent(no_targets_);
+ // We can not track the life time of such processes and it is responsibility
+ // of the host application to make sure that spawned targets without jobs
+ // are terminated when the main application don't need them anymore.
+ }
+
+ *target_info = process_info.Take();
+ return SBOX_ALL_OK;
+}
+
+
+ResultCode BrokerServicesBase::WaitForAllTargets() {
+ ::WaitForSingleObject(no_targets_, INFINITE);
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::IsActiveTarget(DWORD process_id) {
+ AutoLock lock(&lock_);
+ return child_process_ids_.find(process_id) != child_process_ids_.end() ||
+ peer_map_.find(process_id) != peer_map_.end();
+}
+
+VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) {
+ PeerTracker* peer = reinterpret_cast<PeerTracker*>(parameter);
+ // Don't check the return code because we this may fail (safely) at shutdown.
+ ::PostQueuedCompletionStatus(peer->job_port, 0, THREAD_CTRL_REMOVE_PEER,
+ reinterpret_cast<LPOVERLAPPED>(peer->id));
+}
+
+ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
+ scoped_ptr<PeerTracker> peer(new PeerTracker(::GetProcessId(peer_process),
+ job_port_));
+ if (!peer->id)
+ return SBOX_ERROR_GENERIC;
+
+ HANDLE process_handle;
+ if (!::DuplicateHandle(::GetCurrentProcess(), peer_process,
+ ::GetCurrentProcess(), &process_handle,
+ SYNCHRONIZE, FALSE, 0)) {
+ return SBOX_ERROR_GENERIC;
+ }
+ peer->process.Set(process_handle);
+
+ AutoLock lock(&lock_);
+ if (!peer_map_.insert(std::make_pair(peer->id, peer.get())).second)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!::RegisterWaitForSingleObject(
+ &peer->wait_object, peer->process, RemovePeer, peer.get(), INFINITE,
+ WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD)) {
+ peer_map_.erase(peer->id);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ // Release the pointer since it will be cleaned up by the callback.
+ peer.release();
+ return SBOX_ALL_OK;
+}
+
+ResultCode BrokerServicesBase::InstallAppContainer(const wchar_t* sid,
+ const wchar_t* name) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ string16 old_name = LookupAppContainer(sid);
+ if (old_name.empty())
+ return CreateAppContainer(sid, name);
+
+ if (old_name != name)
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ return SBOX_ALL_OK;
+}
+
+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);
+ if (name.empty())
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ return DeleteAppContainer(sid);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/broker_services.h b/chromium/sandbox/win/src/broker_services.h
new file mode 100644
index 00000000000..11c10e0f884
--- /dev/null
+++ b/chromium/sandbox/win/src/broker_services.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+#define SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+
+#include <list>
+#include <map>
+#include <set>
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+struct JobTracker;
+struct PeerTracker;
+
+} // namespace
+
+namespace sandbox {
+
+class PolicyBase;
+
+// BrokerServicesBase ---------------------------------------------------------
+// Broker implementation version 0
+//
+// This is an implementation of the interface BrokerServices and
+// 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> {
+ public:
+ BrokerServicesBase();
+
+ ~BrokerServicesBase();
+
+ // BrokerServices interface.
+ virtual ResultCode Init() OVERRIDE;
+ virtual TargetPolicy* CreatePolicy() OVERRIDE;
+ virtual ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ TargetPolicy* policy,
+ PROCESS_INFORMATION* target) OVERRIDE;
+ virtual ResultCode WaitForAllTargets() OVERRIDE;
+ virtual ResultCode AddTargetPeer(HANDLE peer_process) OVERRIDE;
+ virtual ResultCode InstallAppContainer(const wchar_t* sid,
+ const wchar_t* name) OVERRIDE;
+ virtual ResultCode UninstallAppContainer(const wchar_t* sid) OVERRIDE;
+
+ // Checks if the supplied process ID matches one of the broker's active
+ // target processes
+ // Returns:
+ // true if there is an active target process for this ID, otherwise false.
+ bool IsActiveTarget(DWORD process_id);
+
+ private:
+ // Releases the Job and notifies the associated Policy object to its
+ // resources as well.
+ static void FreeResources(JobTracker* tracker);
+
+ // The routine that the worker thread executes. It is in charge of
+ // notifications and cleanup-related tasks.
+ static DWORD WINAPI TargetEventsThread(PVOID param);
+
+ // Removes a target peer from the process list if it expires.
+ static VOID CALLBACK RemovePeer(PVOID parameter, BOOLEAN timeout);
+
+ // The completion port used by the job objects to communicate events to
+ // the worker thread.
+ HANDLE job_port_;
+
+ // Handle to a manual-reset event that is signaled when the total target
+ // process count reaches zero.
+ HANDLE no_targets_;
+
+ // Handle to the worker thread that reacts to job notifications.
+ HANDLE job_thread_;
+
+ // Lock used to protect the list of targets from being modified by 2
+ // threads at the same time.
+ CRITICAL_SECTION lock_;
+
+ // provides a pool of threads that are used to wait on the IPC calls.
+ ThreadProvider* thread_pool_;
+
+ // List of the trackers for closing and cleanup purposes.
+ typedef std::list<JobTracker*> JobTrackerList;
+ JobTrackerList tracker_list_;
+
+ // Maps peer process IDs to the saved handle and wait event.
+ // Prevents peer callbacks from accessing the broker after destruction.
+ typedef std::map<DWORD, PeerTracker*> PeerTrackerMap;
+ PeerTrackerMap peer_map_;
+
+ // Provides a fast lookup to identify sandboxed processes that belong to a
+ // job. Consult |jobless_process_handles_| for handles of pocess without job.
+ std::set<DWORD> child_process_ids_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_WIN_SRC_BROKER_SERVICES_H_
diff --git a/chromium/sandbox/win/src/crosscall_client.h b/chromium/sandbox/win/src/crosscall_client.h
new file mode 100644
index 00000000000..2715f961639
--- /dev/null
+++ b/chromium/sandbox/win/src/crosscall_client.h
@@ -0,0 +1,483 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_
+#define SANDBOX_SRC_CROSSCALL_CLIENT_H_
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+// This header defines the CrossCall(..) family of templated functions
+// Their purpose is to simulate the syntax of regular call but to generate
+// and IPC from the client-side.
+//
+// The basic pattern is to
+// 1) use template argument deduction to compute the size of each
+// parameter and the appropriate copy method
+// 2) pack the parameters in the appropriate ActualCallParams< > object
+// 3) call the IPC interface IPCProvider::DoCall( )
+//
+// The general interface of CrossCall is:
+// ResultCode CrossCall(IPCProvider& ipc_provider,
+// uint32 tag,
+// const Par1& p1, const Par2& p2,...pn
+// CrossCallReturn* answer)
+//
+// where:
+// ipc_provider: is a specific implementation of the ipc transport see
+// sharedmem_ipc_server.h for an example.
+// tag : is the unique id for this IPC call. Is used to route the call to
+// the appropriate service.
+// p1, p2,.. pn : The input parameters of the IPC. Use only simple types
+// and wide strings (can add support for others).
+// answer : If the IPC was successful. The server-side answer is here. The
+// interpretation of the answer is private to client and server.
+//
+// The return value is ALL_OK if the IPC was delivered to the server, other
+// return codes indicate that the IPC transport failed to deliver it.
+namespace sandbox {
+
+// this is the assumed channel size. This can be overridden in a given
+// IPC implementation.
+const uint32 kIPCChannelSize = 1024;
+
+// The copy helper uses templates to deduce the appropriate copy function to
+// copy the input parameters in the buffer that is going to be send across the
+// IPC. These template facility can be made more sophisticated as need arises.
+
+// The default copy helper. It catches the general case where no other
+// specialized template matches better. We set the type to ULONG_TYPE, so this
+// only works with objects whose size is 32 bits.
+template<typename T>
+class CopyHelper {
+ public:
+ CopyHelper(const T& t) : t_(t) {}
+
+ // Returns the pointer to the start of the input.
+ const void* GetStart() const {
+ return &t_;
+ }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the input in bytes.
+ uint32 GetSize() const {
+ return sizeof(T);
+ }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() {
+ return false;
+ }
+
+ // Returns this object's type.
+ ArgType GetType() {
+ COMPILE_ASSERT(sizeof(T) == sizeof(uint32), need_specialization);
+ return ULONG_TYPE;
+ }
+
+ private:
+ const T& t_;
+};
+
+// This copy helper template specialization if for the void pointer
+// case both 32 and 64 bit.
+template<>
+class CopyHelper<void*> {
+ public:
+ CopyHelper(void* t) : t_(t) {}
+
+ // Returns the pointer to the start of the input.
+ const void* GetStart() const {
+ return &t_;
+ }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the input in bytes.
+ uint32 GetSize() const {
+ return sizeof(t_);
+ }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() {
+ return false;
+ }
+
+ // Returns this object's type.
+ ArgType GetType() {
+ return VOIDPTR_TYPE;
+ }
+
+ private:
+ const void* t_;
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a pointer to a string.
+template<>
+class CopyHelper<const wchar_t*> {
+ public:
+ CopyHelper(const wchar_t* t)
+ : t_(t) {
+ }
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const {
+ return t_;
+ }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the string in bytes. We define a NULL string to
+ // be of zero length.
+ uint32 GetSize() const {
+ __try {
+ return (!t_) ? 0 : static_cast<uint32>(StringLength(t_) * sizeof(t_[0]));
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {
+ return kuint32max;
+ }
+ }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() {
+ return false;
+ }
+
+ ArgType GetType() {
+ return WCHAR_TYPE;
+ }
+
+ private:
+ // We provide our not very optimized version of wcslen(), since we don't
+ // want to risk having the linker use the version in the CRT since the CRT
+ // might not be present when we do an early IPC call.
+ static size_t __cdecl StringLength(const wchar_t* wcs) {
+ const wchar_t *eos = wcs;
+ while (*eos++);
+ return static_cast<size_t>(eos - wcs - 1);
+ }
+
+ const wchar_t* t_;
+};
+
+// Specialization for non-const strings. We just reuse the implementation of the
+// const string specialization.
+template<>
+class CopyHelper<wchar_t*> : public CopyHelper<const wchar_t*> {
+ public:
+ typedef CopyHelper<const wchar_t*> Base;
+ CopyHelper(wchar_t* t) : Base(t) {}
+
+ const void* GetStart() const {
+ return Base::GetStart();
+ }
+
+ bool Update(void* buffer) {
+ return Base::Update(buffer);
+ }
+
+ uint32 GetSize() const {
+ return Base::GetSize();
+ }
+
+ bool IsInOut() {
+ return Base::IsInOut();
+ }
+
+ ArgType GetType() {
+ return Base::GetType();
+ }
+};
+
+// Specialization for wchar_t arrays strings. We just reuse the implementation
+// of the const string specialization.
+template<size_t n>
+class CopyHelper<const wchar_t[n]> : public CopyHelper<const wchar_t*> {
+ public:
+ typedef const wchar_t array[n];
+ typedef CopyHelper<const wchar_t*> Base;
+ CopyHelper(array t) : Base(t) {}
+
+ const void* GetStart() const {
+ return Base::GetStart();
+ }
+
+ bool Update(void* buffer) {
+ return Base::Update(buffer);
+ }
+
+ uint32 GetSize() const {
+ return Base::GetSize();
+ }
+
+ bool IsInOut() {
+ return Base::IsInOut();
+ }
+
+ ArgType GetType() {
+ return Base::GetType();
+ }
+};
+
+// Generic encapsulation class containing a pointer to a buffer and the
+// size of the buffer. It is used by the IPC to be able to pass in/out
+// parameters.
+class InOutCountedBuffer : public CountedBuffer {
+ public:
+ InOutCountedBuffer(void* buffer, uint32 size) : CountedBuffer(buffer, size) {}
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a an input/output buffer.
+template<>
+class CopyHelper<InOutCountedBuffer> {
+ public:
+ CopyHelper(const InOutCountedBuffer t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const {
+ return t_.Buffer();
+ }
+
+ // Updates the buffer with the value from the new buffer in parameter.
+ bool Update(void* buffer) {
+ // We are touching user memory, this has to be done from inside a try
+ // except.
+ __try {
+ memcpy(t_.Buffer(), buffer, t_.Size());
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+ }
+
+ // Returns the size of the string in bytes. We define a NULL string to
+ // be of zero length.
+ uint32 GetSize() const {
+ return t_.Size();
+ }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() {
+ return true;
+ }
+
+ ArgType GetType() {
+ return INOUTPTR_TYPE;
+ }
+
+ private:
+ const InOutCountedBuffer t_;
+};
+
+// The following two macros make it less error prone the generation
+// of CrossCall functions with ever more input parameters.
+
+#define XCALL_GEN_PARAMS_OBJ(num, params) \
+ typedef ActualCallParams<num, kIPCChannelSize> ActualParams; \
+ void* raw_mem = ipc_provider.GetBuffer(); \
+ if (NULL == raw_mem) \
+ return SBOX_ERROR_NO_SPACE; \
+ ActualParams* params = new(raw_mem) ActualParams(tag);
+
+#define XCALL_GEN_COPY_PARAM(num, params) \
+ COMPILE_ASSERT(kMaxIpcParams >= num, too_many_parameters); \
+ CopyHelper<Par##num> ch##num(p##num); \
+ if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \
+ ch##num.IsInOut(), ch##num.GetType())) \
+ return SBOX_ERROR_NO_SPACE;
+
+#define XCALL_GEN_UPDATE_PARAM(num, params) \
+ if (!ch##num.Update(params->GetParamPtr(num-1))) {\
+ ipc_provider.FreeBuffer(raw_mem); \
+ return SBOX_ERROR_BAD_PARAMS; \
+ }
+
+#define XCALL_GEN_FREE_CHANNEL() \
+ ipc_provider.FreeBuffer(raw_mem);
+
+// CrossCall template with one input parameter
+template <typename IPCProvider, typename Par1>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(1, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+
+ return result;
+}
+
+// CrossCall template with two input parameters.
+template <typename IPCProvider, typename Par1, typename Par2>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ const Par2& p2, CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(2, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with three input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ const Par2& p2, const Par3& p3, CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(3, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with four input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
+ typename Par4>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ const Par2& p2, const Par3& p3, const Par4& p4,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(4, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with five input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
+ typename Par4, typename Par5>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ const Par2& p2, const Par3& p3, const Par4& p4,
+ const Par5& p5, CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(5, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with six input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
+ typename Par4, typename Par5, typename Par6>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ const Par2& p2, const Par3& p3, const Par4& p4,
+ const Par5& p5, const Par6& p6, CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(6, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+ XCALL_GEN_COPY_PARAM(6, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_UPDATE_PARAM(6, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with seven input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3,
+ typename Par4, typename Par5, typename Par6, typename Par7>
+ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1,
+ const Par2& p2, const Par3& p3, const Par4& p4,
+ const Par5& p5, const Par6& p6, const Par7& p7,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(7, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+ XCALL_GEN_COPY_PARAM(6, call_params);
+ XCALL_GEN_COPY_PARAM(7, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_UPDATE_PARAM(6, call_params);
+ XCALL_GEN_UPDATE_PARAM(7, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_CLIENT_H__
diff --git a/chromium/sandbox/win/src/crosscall_params.h b/chromium/sandbox/win/src/crosscall_params.h
new file mode 100644
index 00000000000..c5298ba7bec
--- /dev/null
+++ b/chromium/sandbox/win/src/crosscall_params.h
@@ -0,0 +1,293 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__
+#define SANDBOX_SRC_CROSSCALL_PARAMS_H__
+
+#include <windows.h>
+#include <lmaccess.h>
+
+#include <memory>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+
+// Increases |value| until there is no need for padding given an int64
+// alignment. Returns the increased value.
+uint32 Align(uint32 value) {
+ uint32 alignment = sizeof(int64);
+ return ((value + alignment - 1) / alignment) * alignment;
+}
+
+}
+// This header is part of CrossCall: the sandbox inter-process communication.
+// This header defines the basic types used both in the client IPC and in the
+// server IPC code. CrossCallParams and ActualCallParams model the input
+// parameters of an IPC call and CrossCallReturn models the output params and
+// the return value.
+//
+// An IPC call is defined by its 'tag' which is a (uint32) unique identifier
+// that is used to route the IPC call to the proper server. Every tag implies
+// a complete call signature including the order and type of each parameter.
+//
+// Like most IPC systems. CrossCall is designed to take as inputs 'simple'
+// types such as integers and strings. Classes, generic arrays or pointers to
+// them are not supported.
+//
+// Another limitation of CrossCall is that the return value and output
+// parameters can only be uint32 integers. Returning complex structures or
+// strings is not supported.
+
+namespace sandbox {
+
+// max number of extended return parameters. See CrossCallReturn
+const size_t kExtendedReturnCount = 8;
+
+// Union of multiple types to be used as extended results
+// in the CrossCallReturn.
+union MultiType {
+ uint32 unsigned_int;
+ void* pointer;
+ HANDLE handle;
+ ULONG_PTR ulong_ptr;
+};
+
+// Maximum number of IPC parameters currently supported.
+// To increase this value, we have to:
+// - Add another Callback typedef to Dispatcher.
+// - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
+// - Add another case to the switch in GetActualAndMaxBufferSize
+const int kMaxIpcParams = 9;
+
+// Contains the information about a parameter in the ipc buffer.
+struct ParamInfo {
+ ArgType type_;
+ uint32 offset_;
+ uint32 size_;
+};
+
+// Models the return value and the return parameters of an IPC call
+// currently limited to one status code and eight generic return values
+// which cannot be pointers to other data. For x64 ports this structure
+// might have to use other integer types.
+struct CrossCallReturn {
+ // the IPC tag. It should match the original IPC tag.
+ uint32 tag;
+ // The result of the IPC operation itself.
+ ResultCode call_outcome;
+ // the result of the IPC call as executed in the server. The interpretation
+ // of this value depends on the specific service.
+ union {
+ NTSTATUS nt_status;
+ DWORD win32_result;
+ };
+ // Number of extended return values.
+ uint32 extended_count;
+ // for calls that should return a windows handle. It is found here.
+ HANDLE handle;
+ // The array of extended values.
+ MultiType extended[kExtendedReturnCount];
+};
+
+// CrossCallParams base class that models the input params all packed in a
+// single compact memory blob. The representation can vary but in general a
+// given child of this class is meant to represent all input parameters
+// necessary to make a IPC call.
+//
+// This class cannot have virtual members because its assumed the IPC
+// parameters start from the 'this' pointer to the end, which is defined by
+// one of the subclasses
+//
+// Objects of this class cannot be constructed directly. Only derived
+// classes have the proper knowledge to construct it.
+class CrossCallParams {
+ public:
+ // Returns the tag (ipc unique id) associated with this IPC.
+ uint32 GetTag() const {
+ return tag_;
+ }
+
+ // Returns the beggining of the buffer where the IPC params can be stored.
+ // prior to an IPC call
+ const void* GetBuffer() const {
+ return this;
+ }
+
+ // Returns how many parameter this IPC call should have.
+ const uint32 GetParamsCount() const {
+ return params_count_;
+ }
+
+ // Returns a pointer to the CrossCallReturn structure.
+ CrossCallReturn* GetCallReturn() {
+ return &call_return;
+ }
+
+ // Returns TRUE if this call contains InOut parameters.
+ const bool IsInOut() const {
+ return (1 == is_in_out_);
+ }
+
+ // Tells the CrossCall object if it contains InOut parameters.
+ void SetIsInOut(bool value) {
+ if (value)
+ is_in_out_ = 1;
+ else
+ is_in_out_ = 0;
+ }
+
+ protected:
+ // constructs the IPC call params. Called only from the derived classes
+ CrossCallParams(uint32 tag, uint32 params_count)
+ : tag_(tag),
+ params_count_(params_count),
+ is_in_out_(0) {
+ }
+
+ private:
+ uint32 tag_;
+ uint32 is_in_out_;
+ CrossCallReturn call_return;
+ const uint32 params_count_;
+ DISALLOW_COPY_AND_ASSIGN(CrossCallParams);
+};
+
+// ActualCallParams models an specific IPC call parameters with respect to the
+// storage allocation that the packed parameters should need.
+// NUMBER_PARAMS: the number of parameters, valid from 1 to N
+// BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take,
+// typically the block size is defined by the channel size of the underlying
+// ipc mechanism.
+// In practice this class is used to levergage C++ capacity to properly
+// calculate sizes and displacements given the possibility of the packed params
+// blob to be complex.
+//
+// As is, this class assumes that the layout of the blob is as follows. Assume
+// that NUMBER_PARAMS = 2 and a 32-bit build:
+//
+// [ tag 4 bytes]
+// [ IsOnOut 4 bytes]
+// [ call return 52 bytes]
+// [ params count 4 bytes]
+// [ parameter 0 type 4 bytes]
+// [ parameter 0 offset 4 bytes] ---delta to ---\
+// [ parameter 0 size 4 bytes] |
+// [ parameter 1 type 4 bytes] |
+// [ parameter 1 offset 4 bytes] ---------------|--\
+// [ parameter 1 size 4 bytes] | |
+// [ parameter 2 type 4 bytes] | |
+// [ parameter 2 offset 4 bytes] ----------------------\
+// [ parameter 2 size 4 bytes] | | |
+// |---------------------------| | | |
+// | value 0 (x bytes) | <--------------/ | |
+// | value 1 (y bytes) | <-----------------/ |
+// | | |
+// | end of buffer | <---------------------/
+// |---------------------------|
+//
+// Note that the actual number of params is NUMBER_PARAMS + 1
+// so that the size of each actual param can be computed from the difference
+// between one parameter and the next down. The offset of the last param
+// points to the end of the buffer and the type and size are undefined.
+//
+template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE>
+class ActualCallParams : public CrossCallParams {
+ public:
+ // constructor. Pass the ipc unique tag as input
+ explicit ActualCallParams(uint32 tag)
+ : CrossCallParams(tag, NUMBER_PARAMS) {
+ param_info_[0].offset_ = parameters_ - reinterpret_cast<char*>(this);
+ }
+
+ // Testing-only constructor. Allows setting the |number_params| to a
+ // wrong value.
+ ActualCallParams(uint32 tag, uint32 number_params)
+ : CrossCallParams(tag, number_params) {
+ param_info_[0].offset_ = parameters_ - reinterpret_cast<char*>(this);
+ }
+
+ // Testing-only method. Allows setting the apparent size to a wrong value.
+ // returns the previous size.
+ uint32 OverrideSize(uint32 new_size) {
+ uint32 previous_size = param_info_[NUMBER_PARAMS].offset_;
+ param_info_[NUMBER_PARAMS].offset_ = new_size;
+ return previous_size;
+ }
+
+ // Copies each paramter into the internal buffer. For each you must supply:
+ // index: 0 for the first param, 1 for the next an so on
+ bool CopyParamIn(uint32 index, const void* parameter_address, uint32 size,
+ bool is_in_out, ArgType type) {
+ if (index >= NUMBER_PARAMS) {
+ return false;
+ }
+
+ if (kuint32max == size) {
+ // Memory error while getting the size.
+ return false;
+ }
+
+ if (size && !parameter_address) {
+ return false;
+ }
+
+ if (param_info_[index].offset_ > sizeof(*this)) {
+ // It does not fit, abort copy.
+ return false;
+ }
+
+ char* dest = reinterpret_cast<char*>(this) + param_info_[index].offset_;
+
+ // We might be touching user memory, this has to be done from inside a try
+ // except.
+ __try {
+ memcpy(dest, parameter_address, size);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+
+ // Set the flag to tell the broker to update the buffer once the call is
+ // made.
+ if (is_in_out)
+ SetIsInOut(true);
+
+ param_info_[index + 1].offset_ = Align(param_info_[index].offset_ +
+ size);
+ param_info_[index].size_ = size;
+ param_info_[index].type_ = type;
+ return true;
+ }
+
+ // Returns a pointer to a parameter in the memory section.
+ void* GetParamPtr(size_t index) {
+ return reinterpret_cast<char*>(this) + param_info_[index].offset_;
+ }
+
+ // Returns the total size of the buffer. Only valid once all the paramters
+ // have been copied in with CopyParamIn.
+ uint32 GetSize() const {
+ return param_info_[NUMBER_PARAMS].offset_;
+ }
+
+ protected:
+ ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { }
+
+ private:
+ ParamInfo param_info_[NUMBER_PARAMS + 1];
+ char parameters_[BLOCK_SIZE - sizeof(CrossCallParams)
+ - sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
+ DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
+};
+
+COMPILE_ASSERT(sizeof(ActualCallParams<1, 1024>) == 1024, bad_size_buffer);
+COMPILE_ASSERT(sizeof(ActualCallParams<2, 1024>) == 1024, bad_size_buffer);
+COMPILE_ASSERT(sizeof(ActualCallParams<3, 1024>) == 1024, bad_size_buffer);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__
diff --git a/chromium/sandbox/win/src/crosscall_server.cc b/chromium/sandbox/win/src/crosscall_server.cc
new file mode 100644
index 00000000000..fea855337d2
--- /dev/null
+++ b/chromium/sandbox/win/src/crosscall_server.cc
@@ -0,0 +1,301 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "base/logging.h"
+
+// This code performs the ipc message validation. Potential security flaws
+// on the ipc are likelier to be found in this code than in the rest of
+// the ipc code.
+
+namespace {
+
+// The buffer for a message must match the max channel size.
+const size_t kMaxBufferSize = sandbox::kIPCChannelSize;
+
+}
+
+namespace sandbox {
+
+// Returns the actual size for the parameters in an IPC buffer. Returns
+// zero if the |param_count| is zero or too big.
+uint32 GetActualBufferSize(uint32 param_count, void* buffer_base) {
+ // The template types are used to calculate the maximum expected size.
+ typedef ActualCallParams<1, kMaxBufferSize> ActualCP1;
+ typedef ActualCallParams<2, kMaxBufferSize> ActualCP2;
+ typedef ActualCallParams<3, kMaxBufferSize> ActualCP3;
+ typedef ActualCallParams<4, kMaxBufferSize> ActualCP4;
+ typedef ActualCallParams<5, kMaxBufferSize> ActualCP5;
+ typedef ActualCallParams<6, kMaxBufferSize> ActualCP6;
+ typedef ActualCallParams<7, kMaxBufferSize> ActualCP7;
+ typedef ActualCallParams<8, kMaxBufferSize> ActualCP8;
+ typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;
+
+ // Retrieve the actual size and the maximum size of the params buffer.
+ switch (param_count) {
+ case 0:
+ return 0;
+ case 1:
+ return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize();
+ case 2:
+ return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize();
+ case 3:
+ return reinterpret_cast<ActualCP3*>(buffer_base)->GetSize();
+ case 4:
+ return reinterpret_cast<ActualCP4*>(buffer_base)->GetSize();
+ case 5:
+ return reinterpret_cast<ActualCP5*>(buffer_base)->GetSize();
+ case 6:
+ return reinterpret_cast<ActualCP6*>(buffer_base)->GetSize();
+ case 7:
+ return reinterpret_cast<ActualCP7*>(buffer_base)->GetSize();
+ case 8:
+ return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize();
+ case 9:
+ return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize();
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// Verifies that the declared sizes of an IPC buffer are within range.
+bool IsSizeWithinRange(uint32 buffer_size, uint32 min_declared_size,
+ uint32 declared_size) {
+ if ((buffer_size < min_declared_size) ||
+ (sizeof(CrossCallParamsEx) > min_declared_size)) {
+ // Minimal computed size bigger than existing buffer or param_count
+ // integer overflow.
+ return false;
+ }
+
+ if ((declared_size > buffer_size) || (declared_size < min_declared_size)) {
+ // Declared size is bigger than buffer or smaller than computed size
+ // or param_count is equal to 0 or bigger than 9.
+ return false;
+ }
+
+ return true;
+}
+
+CrossCallParamsEx::CrossCallParamsEx()
+ :CrossCallParams(0, 0) {
+}
+
+// We override the delete operator because the object's backing memory
+// is hand allocated in CreateFromBuffer. We don't override the new operator
+// because the constructors are private so there is no way to mismatch
+// new & delete.
+void CrossCallParamsEx::operator delete(void* raw_memory) throw() {
+ if (NULL == raw_memory) {
+ // C++ standard allows 'delete 0' behavior.
+ return;
+ }
+ delete[] reinterpret_cast<char*>(raw_memory);
+}
+
+// This function uses a SEH try block so cannot use C++ objects that
+// have destructors or else you get Compiler Error C2712. So no DCHECKs
+// inside this function.
+CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base,
+ uint32 buffer_size,
+ uint32* output_size) {
+ // IMPORTANT: Everything inside buffer_base and derived from it such
+ // as param_count and declared_size is untrusted.
+ if (NULL == buffer_base) {
+ return NULL;
+ }
+ if (buffer_size < sizeof(CrossCallParams)) {
+ return NULL;
+ }
+ if (buffer_size > kMaxBufferSize) {
+ return NULL;
+ }
+
+ char* backing_mem = NULL;
+ uint32 param_count = 0;
+ uint32 declared_size;
+ uint32 min_declared_size;
+ CrossCallParamsEx* copied_params = NULL;
+
+ // Touching the untrusted buffer is done under a SEH try block. This
+ // will catch memory access violations so we don't crash.
+ __try {
+ CrossCallParams* call_params =
+ reinterpret_cast<CrossCallParams*>(buffer_base);
+
+ // Check against the minimum size given the number of stated params
+ // if too small we bail out.
+ param_count = call_params->GetParamsCount();
+ min_declared_size = sizeof(CrossCallParams) +
+ ((param_count + 1) * sizeof(ParamInfo));
+
+ // Retrieve the declared size which if it fails returns 0.
+ declared_size = GetActualBufferSize(param_count, buffer_base);
+
+ if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size))
+ return NULL;
+
+ // Now we copy the actual amount of the message.
+ *output_size = declared_size;
+ backing_mem = new char[declared_size];
+ copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem);
+ memcpy(backing_mem, call_params, declared_size);
+
+ // Avoid compiler optimizations across this point. Any value stored in
+ // memory should be stored for real, and values previously read from memory
+ // should be actually read.
+ _ReadWriteBarrier();
+
+ min_declared_size = sizeof(CrossCallParams) +
+ ((param_count + 1) * sizeof(ParamInfo));
+
+ // Check that the copied buffer is still valid.
+ if (copied_params->GetParamsCount() != param_count ||
+ GetActualBufferSize(param_count, backing_mem) != declared_size ||
+ !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) {
+ delete [] backing_mem;
+ return NULL;
+ }
+
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ // In case of a windows exception we know it occurred while touching the
+ // untrusted buffer so we bail out as is.
+ delete [] backing_mem;
+ return NULL;
+ }
+
+ const char* last_byte = &backing_mem[declared_size];
+ const char* first_byte = &backing_mem[min_declared_size];
+
+ // Verify here that all and each parameters make sense. This is done in the
+ // local copy.
+ for (uint32 ix =0; ix != param_count; ++ix) {
+ uint32 size = 0;
+ ArgType type;
+ char* address = reinterpret_cast<char*>(
+ copied_params->GetRawParameter(ix, &size, &type));
+ if ((NULL == address) || // No null params.
+ (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type.
+ (address < backing_mem) || // Start cannot point before buffer.
+ (address < first_byte) || // Start cannot point too low.
+ (address > last_byte) || // Start cannot point past buffer.
+ ((address + size) < address) || // Invalid size.
+ ((address + size) > last_byte)) { // End cannot point past buffer.
+ // Malformed.
+ delete[] backing_mem;
+ return NULL;
+ }
+ }
+ // The parameter buffer looks good.
+ return copied_params;
+}
+
+// Accessors to the parameters in the raw buffer.
+void* CrossCallParamsEx::GetRawParameter(uint32 index, uint32* size,
+ ArgType* type) {
+ if (index >= GetParamsCount()) {
+ return NULL;
+ }
+ // The size is always computed from the parameter minus the next
+ // parameter, this works because the message has an extra parameter slot
+ *size = param_info_[index].size_;
+ *type = param_info_[index].type_;
+
+ return param_info_[index].offset_ + reinterpret_cast<char*>(this);
+}
+
+// Covers common case for 32 bit integers.
+bool CrossCallParamsEx::GetParameter32(uint32 index, uint32* param) {
+ uint32 size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if ((NULL == start) || (4 != size) || (ULONG_TYPE != type)) {
+ return false;
+ }
+ // Copy the 4 bytes.
+ *(reinterpret_cast<uint32*>(param)) = *(reinterpret_cast<uint32*>(start));
+ return true;
+}
+
+bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) {
+ uint32 size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if ((NULL == start) || (sizeof(void*) != size) || (VOIDPTR_TYPE != type)) {
+ return false;
+ }
+ *param = *(reinterpret_cast<void**>(start));
+ return true;
+}
+
+// Covers the common case of reading a string. Note that the string is not
+// scanned for invalid characters.
+bool CrossCallParamsEx::GetParameterStr(uint32 index, std::wstring* string) {
+ uint32 size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (WCHAR_TYPE != type) {
+ return false;
+ }
+
+ // Check if this is an empty string.
+ if (size == 0) {
+ *string = L"";
+ return true;
+ }
+
+ if ((NULL == start) || ((size % sizeof(wchar_t)) != 0)) {
+ return false;
+ }
+ string->append(reinterpret_cast<wchar_t*>(start), size/(sizeof(wchar_t)));
+ return true;
+}
+
+bool CrossCallParamsEx::GetParameterPtr(uint32 index, uint32 expected_size,
+ void** pointer) {
+ uint32 size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+
+ if ((size != expected_size) || (INOUTPTR_TYPE != type)) {
+ return false;
+ }
+
+ if (NULL == start) {
+ return false;
+ }
+
+ *pointer = start;
+ return true;
+}
+
+void SetCallError(ResultCode error, CrossCallReturn* call_return) {
+ call_return->call_outcome = error;
+ call_return->extended_count = 0;
+}
+
+void SetCallSuccess(CrossCallReturn* call_return) {
+ call_return->call_outcome = SBOX_ALL_OK;
+}
+
+Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) {
+ DCHECK(callback);
+ std::vector<IPCCall>::iterator it = ipc_calls_.begin();
+ for (; it != ipc_calls_.end(); ++it) {
+ if (it->params.Matches(ipc)) {
+ *callback = it->callback;
+ return this;
+ }
+ }
+ return NULL;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/crosscall_server.h b/chromium/sandbox/win/src/crosscall_server.h
new file mode 100644
index 00000000000..2a395077db7
--- /dev/null
+++ b/chromium/sandbox/win/src/crosscall_server.h
@@ -0,0 +1,224 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_SERVER_H_
+#define SANDBOX_SRC_CROSSCALL_SERVER_H_
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "sandbox/win/src/crosscall_params.h"
+
+// This is the IPC server interface for CrossCall: The IPC for the Sandbox
+// On the server, CrossCall needs two things:
+// 1) threads: Or better said, someone to provide them, that is what the
+// ThreadProvider interface is defined for. These thread(s) are
+// the ones that will actually execute the IPC data retrieval.
+//
+// 2) a dispatcher: This interface represents the way to route and process
+// an IPC call given the IPC tag.
+//
+// The other class included here CrossCallParamsEx is the server side version
+// of the CrossCallParams class of /sandbox/crosscall_params.h The difference
+// is that the sever version is paranoid about the correctness of the IPC
+// message and will do all sorts of verifications.
+//
+// A general diagram of the interaction is as follows:
+//
+// ------------
+// | |
+// ThreadProvider <--(1)Register--| IPC |
+// | | Implemen |
+// | | -tation |
+// (2) | | OnMessage
+// IPC fired --callback ------>| |--(3)---> Dispatcher
+// | |
+// ------------
+//
+// The IPC implementation sits as a middleman between the handling of the
+// specifics of scheduling a thread to service the IPC and the multiple
+// entities that can potentially serve each particular IPC.
+namespace sandbox {
+
+class InterceptionManager;
+
+// This function signature is required as the callback when an IPC call fires.
+// context: a user-defined pointer that was set using ThreadProvider
+// reason: 0 if the callback was fired because of a timeout.
+// 1 if the callback was fired because of an event.
+typedef void (__stdcall * CrossCallIPCCallback)(void* context,
+ unsigned char reason);
+
+// ThreadProvider models a thread factory. The idea is to decouple thread
+// creation and lifetime from the inner guts of the IPC. The contract is
+// simple:
+// - the IPC implementation calls RegisterWait with a waitable object that
+// becomes signaled when an IPC arrives and needs to be serviced.
+// - when the waitable object becomes signaled, the thread provider conjures
+// a thread that calls the callback (CrossCallIPCCallback) function
+// - the callback function tries its best not to block and return quickly
+// and should not assume that the next callback will use the same thread
+// - when the callback returns the ThreadProvider owns again the thread
+// and can destroy it or keep it around.
+class ThreadProvider {
+ public:
+ // Registers a waitable object with the thread provider.
+ // client: A number to associate with all the RegisterWait calls, typically
+ // this is the address of the caller object. This parameter cannot
+ // be zero.
+ // waitable_object : a kernel object that can be waited on
+ // callback: a function pointer which is the function that will be called
+ // when the waitable object fires
+ // context: a user-provider pointer that is passed back to the callback
+ // when its called
+ virtual bool RegisterWait(const void* client, HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) = 0;
+
+ // Removes all the registrations done with the same cookie parameter.
+ // This frees internal thread pool resources.
+ virtual bool UnRegisterWaits(void* cookie) = 0;
+ virtual ~ThreadProvider() {}
+};
+
+// Models the server-side of the original input parameters.
+// Provides IPC buffer validation and it is capable of reading the parameters
+// out of the IPC buffer.
+class CrossCallParamsEx : public CrossCallParams {
+ public:
+ // Factory constructor. Pass an IPCbuffer (and buffer size) that contains a
+ // pending IPCcall. This constructor will:
+ // 1) validate the IPC buffer. returns NULL is the IPCbuffer is malformed.
+ // 2) make a copy of the IPCbuffer (parameter capture)
+ static CrossCallParamsEx* CreateFromBuffer(void* buffer_base,
+ uint32 buffer_size,
+ uint32* output_size);
+
+ // Provides IPCinput parameter raw access:
+ // index : the parameter to read; 0 is the first parameter
+ // returns NULL if the parameter is non-existent. If it exists it also
+ // returns the size in *size
+ void* GetRawParameter(uint32 index, uint32* size, ArgType* type);
+
+ // Gets a parameter that is four bytes in size.
+ // Returns false if the parameter does not exist or is not 32 bits wide.
+ bool GetParameter32(uint32 index, uint32* param);
+
+ // Gets a parameter that is void pointer in size.
+ // Returns false if the parameter does not exist or is not void pointer sized.
+ bool GetParameterVoidPtr(uint32 index, void** param);
+
+ // Gets a parameter that is a string. Returns false if the parameter does not
+ // exist.
+ bool GetParameterStr(uint32 index, std::wstring* string);
+
+ // Gets a parameter that is an in/out buffer. Returns false is the parameter
+ // does not exist or if the size of the actual parameter is not equal to the
+ // expected size.
+ bool GetParameterPtr(uint32 index, uint32 expected_size, void** pointer);
+
+ // Frees the memory associated with the IPC parameters.
+ static void operator delete(void* raw_memory) throw();
+
+ private:
+ // Only the factory method CreateFromBuffer can construct these objects.
+ CrossCallParamsEx();
+
+ ParamInfo param_info_[1];
+ DISALLOW_COPY_AND_ASSIGN(CrossCallParamsEx);
+};
+
+// Simple helper function that sets the members of CrossCallReturn
+// to the proper state to signal a basic error.
+void SetCallError(ResultCode error, CrossCallReturn* call_return);
+
+// Sets the internal status of call_return to signify the that IPC call
+// completed successfully.
+void SetCallSuccess(CrossCallReturn* call_return);
+
+// Represents the client process that initiated the IPC which boils down to the
+// process handle and the job object handle that contains the client process.
+struct ClientInfo {
+ HANDLE process;
+ HANDLE job_object;
+ DWORD process_id;
+};
+
+// All IPC-related information to be passed to the IPC handler.
+struct IPCInfo {
+ int ipc_tag;
+ const ClientInfo* client_info;
+ CrossCallReturn return_info;
+};
+
+// This structure identifies IPC signatures.
+struct IPCParams {
+ int ipc_tag;
+ ArgType args[kMaxIpcParams];
+
+ bool Matches(IPCParams* other) const {
+ return !memcmp(this, other, sizeof(*other));
+ }
+};
+
+// Models an entity that can process an IPC message or it can route to another
+// one that could handle it. When an IPC arrives the IPC implementation will:
+// 1) call OnMessageReady() with the tag of the pending IPC. If the dispatcher
+// returns NULL it means that it cannot handle this IPC but if it returns
+// non-null, it must be the pointer to a dispatcher that can handle it.
+// 2) When the IPC finally obtains a valid Dispatcher the IPC
+// implementation creates a CrossCallParamsEx from the raw IPC buffer.
+// 3) It calls the returned callback, with the IPC info and arguments.
+class Dispatcher {
+ public:
+ // Called from the IPC implementation to handle a specific IPC message.
+ typedef bool (Dispatcher::*CallbackGeneric)();
+ typedef bool (Dispatcher::*Callback0)(IPCInfo* ipc);
+ typedef bool (Dispatcher::*Callback1)(IPCInfo* ipc, void* p1);
+ typedef bool (Dispatcher::*Callback2)(IPCInfo* ipc, void* p1, void* p2);
+ typedef bool (Dispatcher::*Callback3)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3);
+ typedef bool (Dispatcher::*Callback4)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3, void* p4);
+ typedef bool (Dispatcher::*Callback5)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3, void* p4, void* p5);
+ typedef bool (Dispatcher::*Callback6)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3, void* p4, void* p5, void* p6);
+ typedef bool (Dispatcher::*Callback7)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3, void* p4, void* p5, void* p6,
+ void* p7);
+ typedef bool (Dispatcher::*Callback8)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3, void* p4, void* p5, void* p6,
+ void* p7, void* p8);
+ typedef bool (Dispatcher::*Callback9)(IPCInfo* ipc, void* p1, void* p2,
+ void* p3, void* p4, void* p5, void* p6,
+ void* p7, void* p8, void* p9);
+
+ // Called from the IPC implementation when an IPC message is ready override
+ // on a derived class to handle a set of IPC messages. Return NULL if your
+ // subclass does not handle the message or return the pointer to the subclass
+ // that can handle it.
+ virtual Dispatcher* OnMessageReady(IPCParams* ipc, CallbackGeneric* callback);
+
+ // Called when a target proces is created, to setup the interceptions related
+ // with the given service (IPC).
+ virtual bool SetupService(InterceptionManager* manager, int service) = 0;
+
+ virtual ~Dispatcher() {}
+
+ protected:
+ // Structure that defines an IPC Call with all the parameters and the handler.
+ struct IPCCall {
+ IPCParams params;
+ CallbackGeneric callback;
+ };
+
+ // List of IPC Calls supported by the class.
+ std::vector<IPCCall> ipc_calls_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_SERVER_H_
diff --git a/chromium/sandbox/win/src/eat_resolver.cc b/chromium/sandbox/win/src/eat_resolver.cc
new file mode 100644
index 00000000000..154bfcfbddf
--- /dev/null
+++ b/chromium/sandbox/win/src/eat_resolver.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2006-2010 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/eat_resolver.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS EatResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point,
+ thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ if (!eat_entry_)
+ return STATUS_INVALID_PARAMETER;
+
+ size_t thunk_bytes = GetInternalThunkSize();
+
+#if defined(_WIN64)
+ // We have two thunks, in order: the return path and the forward path.
+ if (!SetInternalThunk(thunk_storage, storage_bytes, NULL, target_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ storage_bytes -= thunk_bytes;
+ thunk_storage = reinterpret_cast<char*>(thunk_storage) + thunk_bytes;
+#endif
+
+ if (!SetInternalThunk(thunk_storage, storage_bytes, target_, interceptor_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ AutoProtectMemory memory;
+ ret = memory.ChangeProtection(eat_entry_, sizeof(DWORD), PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ // Perform the patch.
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ *eat_entry_ = reinterpret_cast<DWORD>(thunk_storage) -
+ reinterpret_cast<DWORD>(target_module);
+#pragma warning(pop)
+
+ if (NULL != storage_used)
+ *storage_used = GetThunkSize();
+
+ return ret;
+}
+
+NTSTATUS EatResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ DCHECK_NT(address);
+ if (!module)
+ return STATUS_INVALID_PARAMETER;
+
+ base::win::PEImage pe(module);
+ if (!pe.VerifyMagic())
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ eat_entry_ = pe.GetExportEntry(function_name);
+
+ if (!eat_entry_)
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ *address = pe.RVAToAddr(*eat_entry_);
+
+ return STATUS_SUCCESS;
+}
+
+size_t EatResolverThunk::GetThunkSize() const {
+#if defined(_WIN64)
+ return GetInternalThunkSize() * 2;
+#else
+ return GetInternalThunkSize();
+#endif
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/eat_resolver.h b/chromium/sandbox/win/src/eat_resolver.h
new file mode 100644
index 00000000000..5e4b6e83cc5
--- /dev/null
+++ b/chromium/sandbox/win/src/eat_resolver.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 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_EAT_RESOLVER_H__
+#define SANDBOX_SRC_EAT_RESOLVER_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform exports table interceptions.
+class EatResolverThunk : public ResolverThunk {
+ public:
+ EatResolverThunk() : eat_entry_(NULL) {}
+ virtual ~EatResolverThunk() {}
+
+ // Implementation of Resolver::Setup.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ // Implementation of Resolver::ResolveTarget.
+ virtual NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address);
+
+ // Implementation of Resolver::GetThunkSize.
+ virtual size_t GetThunkSize() const;
+
+ private:
+ // The entry to patch.
+ DWORD* eat_entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(EatResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_EAT_RESOLVER_H__
diff --git a/chromium/sandbox/win/src/file_policy_test.cc b/chromium/sandbox/win/src/file_policy_test.cc
new file mode 100644
index 00000000000..85aea0b7071
--- /dev/null
+++ b/chromium/sandbox/win/src/file_policy_test.cc
@@ -0,0 +1,599 @@
+// Copyright (c) 2011 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 <algorithm>
+#include <cctype>
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define BINDNTDLL(name) \
+ name ## Function name = reinterpret_cast<name ## Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+namespace sandbox {
+
+const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+
+// Creates a file using different desired access. Returns if the call succeeded
+// or not. The first argument in argv is the filename. If the second argument
+// is "read", we try read only access. Otherwise we try read-write access.
+SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t **argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ bool read = (_wcsicmp(argv[0], L"Read") == 0);
+
+ if (read) {
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_READ, kSharing, NULL, OPEN_EXISTING, 0, NULL));
+ base::win::ScopedHandle file2(CreateFile(
+ argv[1], FILE_EXECUTE, kSharing, NULL, OPEN_EXISTING, 0, NULL));
+
+ if (file1.Get() && file2.Get())
+ return SBOX_TEST_SUCCEEDED;
+ return SBOX_TEST_DENIED;
+ } else {
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_ALL, kSharing, NULL, OPEN_EXISTING, 0, NULL));
+ base::win::ScopedHandle file2(CreateFile(
+ argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, NULL, OPEN_EXISTING,
+ 0, NULL));
+
+ if (file1.Get() && file2.Get())
+ return SBOX_TEST_SUCCEEDED;
+ return SBOX_TEST_DENIED;
+ }
+}
+
+SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ std::wstring full_path = MakePathToSys(argv[0], false);
+ if (full_path.empty()) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ HANDLE file = ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (INVALID_HANDLE_VALUE != file) {
+ ::CloseHandle(file);
+ return SBOX_TEST_SUCCEEDED;
+ } else {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ } else {
+ return SBOX_TEST_FAILED;
+ }
+ }
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Creates the file in parameter using the NtCreateFile api and returns if the
+// call succeeded or not.
+SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) {
+ BINDNTDLL(NtCreateFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtCreateFile || !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring file(argv[0]);
+ if (0 != _wcsnicmp(file.c_str(), kNTObjManPrefix, kNTObjManPrefixLen))
+ file = MakePathToSys(argv[0], true);
+
+ UNICODE_STRING object_name;
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ HANDLE handle;
+ IO_STATUS_BLOCK io_block = {0};
+ NTSTATUS status = NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes,
+ &io_block, NULL, 0, kSharing, FILE_OPEN,
+ 0, NULL, 0);
+ if (NT_SUCCESS(status)) {
+ ::CloseHandle(handle);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+ return SBOX_TEST_FAILED;
+}
+
+// Opens the file in parameter using the NtOpenFile api and returns if the
+// call succeeded or not.
+SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) {
+ BINDNTDLL(NtOpenFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtOpenFile || !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring file = MakePathToSys(argv[0], true);
+ UNICODE_STRING object_name;
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ HANDLE handle;
+ IO_STATUS_BLOCK io_block = {0};
+ NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes,
+ &io_block, kSharing, 0);
+ if (NT_SUCCESS(status)) {
+ ::CloseHandle(handle);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) {
+ std::wstring sys_path = MakePathToSys(L"", false);
+ if (sys_path.empty()) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ ULARGE_INTEGER free_user = {0};
+ ULARGE_INTEGER total = {0};
+ ULARGE_INTEGER free_total = {0};
+ if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total,
+ &free_total)) {
+ if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) {
+ return SBOX_TEST_SUCCEEDED;
+ }
+ } else {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ } else {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ }
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Move a file using the MoveFileEx api and returns if the call succeeded or
+// not.
+SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t **argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (::MoveFileEx(argv[0], argv[1], 0))
+ return SBOX_TEST_SUCCEEDED;
+
+ if (::GetLastError() != ERROR_ACCESS_DENIED)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_DENIED;
+}
+
+// Query the attributes of file in parameter using the NtQueryAttributesFile api
+// and NtQueryFullAttributesFile and returns if the call succeeded or not. The
+// second argument in argv is "d" or "f" telling if we expect the attributes to
+// specify a file or a directory. The expected attribute has to match the real
+// attributes for the call to be successful.
+SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) {
+ BINDNTDLL(NtQueryAttributesFile);
+ BINDNTDLL(NtQueryFullAttributesFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtQueryAttributesFile || !NtQueryFullAttributesFile ||
+ !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ bool expect_directory = (L'd' == argv[1][0]);
+
+ UNICODE_STRING object_name;
+ std::wstring file = MakePathToSys(argv[0], true);
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ FILE_BASIC_INFORMATION info = {0};
+ FILE_NETWORK_OPEN_INFORMATION full_info = {0};
+ NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info);
+ NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info);
+
+ if (status1 != status2)
+ return SBOX_TEST_FAILED;
+
+ if (NT_SUCCESS(status1)) {
+ if (info.FileAttributes != full_info.FileAttributes)
+ return SBOX_TEST_FAILED;
+
+ bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ if (expect_directory == is_directory1)
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status1) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+TEST(FilePolicyTest, DenyNtCreateCalc) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY,
+ L"calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+}
+
+TEST(FilePolicyTest, AllowNtCreateCalc) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+}
+
+TEST(FilePolicyTest, AllowNtCreateWithNativePath) {
+ std::wstring calc = MakePathToSys(L"calc.exe", false);
+ std::wstring nt_path;
+ ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path));
+ TestRunner runner;
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str());
+
+ wchar_t buff[MAX_PATH];
+ ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
+
+ std::transform(nt_path.begin(), nt_path.end(), nt_path.begin(), std::tolower);
+ ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
+}
+
+TEST(FilePolicyTest, AllowReadOnly) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY,
+ temp_file_name));
+
+ wchar_t command_read[MAX_PATH + 20] = {0};
+ wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name);
+ wchar_t command_write[MAX_PATH + 20] = {0};
+ wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
+
+ // Verify that we have read access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read));
+
+ // Verify that we don't have write access after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write));
+
+ // Verify that we really have write access to the file.
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
+
+ DeleteFile(temp_file_name);
+}
+
+TEST(FilePolicyTest, AllowWildcard) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ wcscat_s(temp_directory, MAX_PATH, L"*");
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory));
+
+ wchar_t command_write[MAX_PATH + 20] = {0};
+ wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
+
+ // Verify that we have write access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
+
+ DeleteFile(temp_file_name);
+}
+
+TEST(FilePolicyTest, AllowNtCreatePatternRule) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_OpenSys32 appmgmts.dll"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_OpenSys32 appmgmts.dll"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
+}
+
+TEST(FilePolicyTest, CheckNotFound) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll"));
+
+ EXPECT_EQ(SBOX_TEST_NOT_FOUND,
+ runner.RunTest(L"File_OpenSys32 notfound.dll"));
+}
+
+TEST(FilePolicyTest, CheckNoLeak) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe"));
+}
+
+TEST(FilePolicyTest, TestQueryAttributesFile) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY,
+ L"appmgmts.dll"));
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY,
+ L"notfound.exe"));
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers"));
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY,
+ L"ipconfig.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes drivers d"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes appmgmts.dll f"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes ipconfig.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes ftp.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_NOT_FOUND,
+ runner.RunTest(L"File_QueryAttributes notfound.exe f"));
+}
+
+// Makes sure that we don't leak information when there is not policy to allow
+// a path.
+TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes ftp.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes notfound.exe f"));
+}
+
+TEST(FilePolicyTest, TestRename) {
+ TestRunner runner;
+
+ // Give access to the temp directory.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name1[MAX_PATH];
+ wchar_t temp_file_name2[MAX_PATH];
+ wchar_t temp_file_name3[MAX_PATH];
+ wchar_t temp_file_name4[MAX_PATH];
+ wchar_t temp_file_name5[MAX_PATH];
+ wchar_t temp_file_name6[MAX_PATH];
+ wchar_t temp_file_name7[MAX_PATH];
+ wchar_t temp_file_name8[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u);
+
+
+ // Add rules to make file1->file2 succeed.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2));
+
+ // Add rules to make file3->file4 fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY,
+ temp_file_name4));
+
+ // Add rules to make file5->file6 fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY,
+ temp_file_name5));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6));
+
+ // Add rules to make file7->no_pol_file fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7));
+
+ // Delete the files where the files are going to be renamed to.
+ ::DeleteFile(temp_file_name2);
+ ::DeleteFile(temp_file_name4);
+ ::DeleteFile(temp_file_name6);
+ ::DeleteFile(temp_file_name8);
+
+
+ wchar_t command[MAX_PATH*2 + 20] = {0};
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1,
+ temp_file_name2);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3,
+ temp_file_name4);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5,
+ temp_file_name6);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7,
+ temp_file_name8);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+
+ // Delete all the files in case they are still there.
+ ::DeleteFile(temp_file_name1);
+ ::DeleteFile(temp_file_name2);
+ ::DeleteFile(temp_file_name3);
+ ::DeleteFile(temp_file_name4);
+ ::DeleteFile(temp_file_name5);
+ ::DeleteFile(temp_file_name6);
+ ::DeleteFile(temp_file_name7);
+ ::DeleteFile(temp_file_name8);
+}
+
+TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY,
+ L"notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+}
+
+TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY,
+ L"notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe"));
+}
+
+TEST(FilePolicyTest, FileGetDiskSpace) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace"));
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+
+ // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx
+ // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is
+ // denied since there is no wild card in the rule.
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L""));
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
+}
+
+// http://crbug.com/146944
+TEST(FilePolicyTest, DISABLED_TestReparsePoint) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(temp_file_name));
+ ASSERT_TRUE(::CreateDirectory(temp_file_name, NULL));
+
+ // Create a temporary file in the subfolder.
+ std::wstring subfolder = temp_file_name;
+ std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1);
+ std::wstring temp_file = subfolder + L"\\file_" + temp_file_title;
+
+ HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, NULL);
+ ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Create a temporary file in the temp directory.
+ std::wstring temp_dir = temp_directory;
+ std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title;
+ file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, NULL);
+ ASSERT_TRUE(file != NULL);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Give write access to the temp directory.
+ std::wstring temp_dir_wildcard = temp_dir + L"*";
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY,
+ temp_dir_wildcard.c_str()));
+
+ // Prepare the command to execute.
+ std::wstring command_write;
+ command_write += L"File_Create Write \"";
+ command_write += temp_file;
+ command_write += L"\"";
+
+ // Verify that we have write access to the original file
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str()));
+
+ // Replace the subfolder by a reparse point to %temp%.
+ ::DeleteFile(temp_file.c_str());
+ HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
+
+ std::wstring temp_dir_nt;
+ temp_dir_nt += L"\\??\\";
+ temp_dir_nt += temp_dir;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+ EXPECT_TRUE(::CloseHandle(dir));
+
+ // Try to open the file again.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str()));
+
+ // Remove the reparse point.
+ dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+ EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
+ EXPECT_TRUE(DeleteReparsePoint(dir));
+ EXPECT_TRUE(::CloseHandle(dir));
+
+ // Cleanup.
+ EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str()));
+ EXPECT_TRUE(::RemoveDirectory(subfolder.c_str()));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/filesystem_dispatcher.cc b/chromium/sandbox/win/src/filesystem_dispatcher.cc
new file mode 100644
index 00000000000..22240ff0e55
--- /dev/null
+++ b/chromium/sandbox/win/src/filesystem_dispatcher.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2006-2010 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/filesystem_dispatcher.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/filesystem_interception.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+FilesystemDispatcher::FilesystemDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IPC_NTCREATEFILE_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE,
+ ULONG_TYPE, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtCreateFile)
+ };
+
+ static const IPCCall open_file = {
+ {IPC_NTOPENFILE_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE,
+ ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtOpenFile)
+ };
+
+ static const IPCCall attribs = {
+ {IPC_NTQUERYATTRIBUTESFILE_TAG, WCHAR_TYPE, ULONG_TYPE, INOUTPTR_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryAttributesFile)
+ };
+
+ static const IPCCall full_attribs = {
+ {IPC_NTQUERYFULLATTRIBUTESFILE_TAG, WCHAR_TYPE, ULONG_TYPE, INOUTPTR_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryFullAttributesFile)
+ };
+
+ static const IPCCall set_info = {
+ {IPC_NTSETINFO_RENAME_TAG, VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE,
+ ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtSetInformationFile)
+ };
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_file);
+ ipc_calls_.push_back(attribs);
+ ipc_calls_.push_back(full_attribs);
+ ipc_calls_.push_back(set_info);
+}
+
+bool FilesystemDispatcher::SetupService(InterceptionManager* manager,
+ int service) {
+ switch (service) {
+ case IPC_NTCREATEFILE_TAG:
+ return INTERCEPT_NT(manager, NtCreateFile, CREATE_FILE_ID, 48);
+
+ case IPC_NTOPENFILE_TAG:
+ return INTERCEPT_NT(manager, NtOpenFile, OPEN_FILE_ID, 28);
+
+ case IPC_NTQUERYATTRIBUTESFILE_TAG:
+ return INTERCEPT_NT(manager, NtQueryAttributesFile, QUERY_ATTRIB_FILE_ID,
+ 12);
+
+ case IPC_NTQUERYFULLATTRIBUTESFILE_TAG:
+ return INTERCEPT_NT(manager, NtQueryFullAttributesFile,
+ QUERY_FULL_ATTRIB_FILE_ID, 12);
+
+ case IPC_NTSETINFO_RENAME_TAG:
+ return INTERCEPT_NT(manager, NtSetInformationFile, SET_INFO_FILE_ID, 24);
+
+ default:
+ return false;
+ }
+}
+
+bool FilesystemDispatcher::NtCreateFile(
+ IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access,
+ DWORD file_attributes, DWORD share_access, DWORD create_disposition,
+ DWORD create_options) {
+ if (!PreProcessName(*name, name)) {
+ // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ ULONG broker = TRUE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::OPTIONS] = ParamPickerMake(create_options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEFILE_TAG,
+ params.GetBase());
+ HANDLE handle;
+ ULONG_PTR io_information = 0;
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::CreateFileAction(result, *ipc->client_info, *name,
+ attributes, desired_access,
+ file_attributes, share_access,
+ create_disposition, create_options,
+ &handle, &nt_status,
+ &io_information)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].ulong_ptr = io_information;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool FilesystemDispatcher::NtOpenFile(
+ IPCInfo* ipc, std::wstring* name, DWORD attributes, DWORD desired_access,
+ DWORD share_access, DWORD open_options) {
+ if (!PreProcessName(*name, name)) {
+ // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ ULONG broker = TRUE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::OPTIONS] = ParamPickerMake(open_options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENFILE_TAG,
+ params.GetBase());
+ HANDLE handle;
+ ULONG_PTR io_information = 0;
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::OpenFileAction(result, *ipc->client_info, *name,
+ attributes, desired_access,
+ share_access, open_options, &handle,
+ &nt_status, &io_information)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].ulong_ptr = io_information;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool FilesystemDispatcher::NtQueryAttributesFile(
+ IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) {
+ if (sizeof(FILE_BASIC_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(*name, name)) {
+ // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ ULONG broker = TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(IPC_NTQUERYATTRIBUTESFILE_TAG,
+ params.GetBase());
+
+ FILE_BASIC_INFORMATION* information =
+ reinterpret_cast<FILE_BASIC_INFORMATION*>(info->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::QueryAttributesFileAction(result, *ipc->client_info,
+ *name, attributes,
+ information, &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+bool FilesystemDispatcher::NtQueryFullAttributesFile(
+ IPCInfo* ipc, std::wstring* name, DWORD attributes, CountedBuffer* info) {
+ if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(*name, name)) {
+ // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ ULONG broker = TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(
+ IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase());
+
+ FILE_NETWORK_OPEN_INFORMATION* information =
+ reinterpret_cast<FILE_NETWORK_OPEN_INFORMATION*>(info->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::QueryFullAttributesFileAction(result,
+ *ipc->client_info,
+ *name, attributes,
+ information,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+bool FilesystemDispatcher::NtSetInformationFile(
+ IPCInfo* ipc, HANDLE handle, CountedBuffer* status, CountedBuffer* info,
+ DWORD length, DWORD info_class) {
+ if (sizeof(IO_STATUS_BLOCK) != status->Size())
+ return false;
+ if (length != info->Size())
+ return false;
+
+ FILE_RENAME_INFORMATION* rename_info =
+ reinterpret_cast<FILE_RENAME_INFORMATION*>(info->Buffer());
+
+ if (!IsSupportedRenameCall(rename_info, length, info_class))
+ return false;
+
+ std::wstring name;
+ name.assign(rename_info->FileName, rename_info->FileNameLength /
+ sizeof(rename_info->FileName[0]));
+ if (!PreProcessName(name, &name)) {
+ // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ ULONG broker = TRUE;
+ const wchar_t* filename = name.c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(IPC_NTSETINFO_RENAME_TAG,
+ params.GetBase());
+
+ IO_STATUS_BLOCK* io_status =
+ reinterpret_cast<IO_STATUS_BLOCK*>(status->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::SetInformationFileAction(result, *ipc->client_info,
+ handle, rename_info, length,
+ info_class, io_status,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/filesystem_dispatcher.h b/chromium/sandbox/win/src/filesystem_dispatcher.h
new file mode 100644
index 00000000000..b0d9a7a0a95
--- /dev/null
+++ b/chromium/sandbox/win/src/filesystem_dispatcher.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2010 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_FILESYSTEM_DISPATCHER_H__
+#define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles file system-related IPC calls.
+class FilesystemDispatcher : public Dispatcher {
+ public:
+ explicit FilesystemDispatcher(PolicyBase* policy_base);
+ ~FilesystemDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateFile in the target.
+ bool NtCreateFile(IPCInfo* ipc, std::wstring* name, DWORD attributes,
+ DWORD desired_access, DWORD file_attributes,
+ DWORD share_access, DWORD create_disposition,
+ DWORD create_options);
+
+ // Processes IPC requests coming from calls to NtOpenFile in the target.
+ bool NtOpenFile(IPCInfo* ipc, std::wstring* name, DWORD attributes,
+ DWORD desired_access, DWORD share_access,
+ DWORD create_options);
+
+ // Processes IPC requests coming from calls to NtQueryAttributesFile in the
+ // target.
+ bool NtQueryAttributesFile(IPCInfo* ipc, std::wstring* name, DWORD attributes,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtQueryFullAttributesFile in
+ // the target.
+ bool NtQueryFullAttributesFile(IPCInfo* ipc, std::wstring* name,
+ DWORD attributes, CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtSetInformationFile with the
+ // rename information class.
+ bool NtSetInformationFile(IPCInfo* ipc, HANDLE handle, CountedBuffer* status,
+ CountedBuffer* info, DWORD length,
+ DWORD info_class);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
diff --git a/chromium/sandbox/win/src/filesystem_interception.cc b/chromium/sandbox/win/src/filesystem_interception.cc
new file mode 100644
index 00000000000..33688f0f649
--- /dev/null
+++ b/chromium/sandbox/win/src/filesystem_interception.cc
@@ -0,0 +1,351 @@
+// Copyright (c) 2006-2008 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/filesystem_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+ PHANDLE file, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes, ULONG sharing,
+ ULONG disposition, ULONG options,
+ PVOID ea_buffer, ULONG ea_length) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_CreateFile(file, desired_access, object_attributes,
+ io_status, allocation_size,
+ file_attributes, sharing, disposition,
+ options, ea_buffer, ea_length);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ wchar_t* name;
+ uint32 attributes = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ ULONG broker = FALSE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(name);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IPC_NTCREATEFILE_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ // The following call must match in the parameters with
+ // FilesystemDispatcher::ProcessNtCreateFile.
+ ResultCode code = CrossCall(ipc, IPC_NTCREATEFILE_TAG, name, attributes,
+ desired_access, file_attributes, sharing,
+ disposition, options, &answer);
+
+ operator delete(name, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ status = io_status->Status;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status, ULONG sharing,
+ ULONG options) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes,
+ io_status, sharing, options);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ wchar_t* name;
+ uint32 attributes;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ ULONG broker = FALSE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(name);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IPC_NTOPENFILE_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTOPENFILE_TAG, name, attributes,
+ desired_access, sharing, options, &answer);
+
+ operator delete(name, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ status = io_status->Status;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtQueryAttributesFile(
+ NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ wchar_t* name = NULL;
+ uint32 attributes = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ InOutCountedBuffer file_info(file_attributes,
+ sizeof(FILE_BASIC_INFORMATION));
+
+ ULONG broker = FALSE;
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(name);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IPC_NTQUERYATTRIBUTESFILE_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTQUERYATTRIBUTESFILE_TAG, name,
+ attributes, file_info, &answer);
+
+ operator delete(name, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ return answer.nt_status;
+
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryFullAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status = orig_QueryFullAttributes(object_attributes,
+ file_attributes);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ wchar_t* name = NULL;
+ uint32 attributes = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ InOutCountedBuffer file_info(file_attributes,
+ sizeof(FILE_NETWORK_OPEN_INFORMATION));
+
+ ULONG broker = FALSE;
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(name);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTQUERYFULLATTRIBUTESFILE_TAG, name,
+ attributes, file_info, &answer);
+
+ operator delete(name, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtSetInformationFile(
+ NtSetInformationFileFunction orig_SetInformationFile, HANDLE file,
+ PIO_STATUS_BLOCK io_status, PVOID file_info, ULONG length,
+ FILE_INFORMATION_CLASS file_info_class) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_SetInformationFile(file, io_status, file_info, length,
+ file_info_class);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ if (!ValidParameter(file_info, length, READ))
+ break;
+
+ FILE_RENAME_INFORMATION* file_rename_info =
+ reinterpret_cast<FILE_RENAME_INFORMATION*>(file_info);
+ OBJECT_ATTRIBUTES object_attributes;
+ UNICODE_STRING object_name;
+ InitializeObjectAttributes(&object_attributes, &object_name, 0, NULL, NULL);
+
+ __try {
+ if (!IsSupportedRenameCall(file_rename_info, length, file_info_class))
+ break;
+
+ object_attributes.RootDirectory = file_rename_info->RootDirectory;
+ object_name.Buffer = file_rename_info->FileName;
+ object_name.Length = object_name.MaximumLength =
+ static_cast<USHORT>(file_rename_info->FileNameLength);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ wchar_t* name;
+ NTSTATUS ret = AllocAndCopyName(&object_attributes, &name, NULL, NULL);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ ULONG broker = FALSE;
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(name);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IPC_NTSETINFO_RENAME_TAG, params.GetBase()))
+ break;
+
+ InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK));
+ // This is actually not an InOut buffer, only In, but using InOut facility
+ // really helps to simplify the code.
+ InOutCountedBuffer file_info_buffer(file_info, length);
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTSETINFO_RENAME_TAG, file,
+ io_status_buffer, file_info_buffer, length,
+ file_info_class, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/filesystem_interception.h b/chromium/sandbox/win/src/filesystem_interception.h
new file mode 100644
index 00000000000..2fafb4499b1
--- /dev/null
+++ b/chromium/sandbox/win/src/filesystem_interception.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2006-2008 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__
+#define SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtCreateFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile(
+ NtCreateFileFunction orig_CreateFile, PHANDLE file,
+ ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status, PLARGE_INTEGER allocation_size,
+ ULONG file_attributes, ULONG sharing, ULONG disposition, ULONG options,
+ PVOID ea_buffer, ULONG ea_length);
+
+// Interception of NtOpenFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile(
+ NtOpenFileFunction orig_OpenFile, PHANDLE file, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status,
+ ULONG sharing, ULONG options);
+
+// Interception of NtQueryAtttributesFile on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile(
+ NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes);
+
+// Interception of NtQueryFullAtttributesFile on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes);
+
+// Interception of NtSetInformationFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile(
+ NtSetInformationFileFunction orig_SetInformationFile, HANDLE file,
+ PIO_STATUS_BLOCK io_status, PVOID file_information, ULONG length,
+ FILE_INFORMATION_CLASS file_information_class);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_INTERCEPTION_H__
diff --git a/chromium/sandbox/win/src/filesystem_policy.cc b/chromium/sandbox/win/src/filesystem_policy.cc
new file mode 100644
index 00000000000..02707b0a847
--- /dev/null
+++ b/chromium/sandbox/win/src/filesystem_policy.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2011 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 <string>
+
+#include "sandbox/win/src/filesystem_policy.h"
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ IO_STATUS_BLOCK* io_status_block,
+ ULONG file_attributes,
+ ULONG share_access,
+ ULONG create_disposition,
+ ULONG create_options,
+ PVOID ea_buffer,
+ ULONG ea_lenght,
+ HANDLE target_process) {
+ NtCreateFileFunction NtCreateFile = NULL;
+ ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile);
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status = NtCreateFile(&local_handle, desired_access, obj_attributes,
+ io_status_block, NULL, file_attributes,
+ share_access, create_disposition,
+ create_options, ea_buffer, ea_lenght);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) {
+ // The handle points somewhere else. Fail the operation.
+ ::CloseHandle(local_handle);
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ target_process, target_file_handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+} // namespace.
+
+namespace sandbox {
+
+bool FileSystemPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ // Don't do any pre-processing if the name starts like the the native
+ // object manager style.
+ if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) {
+ // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
+ // infrastructure to normalize names. In any case we need to escape the
+ // question marks.
+ if (!PreProcessName(mod_name, &mod_name)) {
+ // The path to be added might contain a reparse point.
+ NOTREACHED();
+ return false;
+ }
+ if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) {
+ // TODO(nsylvain): Find a better way to do name resolution. Right now we
+ // take the name and we expand it.
+ mod_name.insert(0, L"\\/?/?\\");
+ name = mod_name.c_str();
+ }
+ }
+
+ EvalResult result = ASK_BROKER;
+
+ // List of supported calls for the filesystem.
+ const unsigned kCallNtCreateFile = 0x1;
+ const unsigned kCallNtOpenFile = 0x2;
+ const unsigned kCallNtQueryAttributesFile = 0x4;
+ const unsigned kCallNtQueryFullAttributesFile = 0x8;
+ const unsigned kCallNtSetInfoRename = 0x10;
+
+ DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile |
+ kCallNtQueryAttributesFile |
+ kCallNtQueryFullAttributesFile | kCallNtSetInfoRename;
+
+ PolicyRule create(result);
+ PolicyRule open(result);
+ PolicyRule query(result);
+ PolicyRule query_full(result);
+ PolicyRule rename(result);
+
+ switch (semantics) {
+ case TargetPolicy::FILES_ALLOW_DIR_ANY: {
+ open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
+ create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_READONLY: {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write.
+ DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES |
+ FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE |
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
+ DWORD restricted_flags = ~allowed_flags;
+ open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+ create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+
+ // Read only access don't work for rename.
+ rule_to_add &= ~kCallNtSetInfoRename;
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_QUERY: {
+ // Here we don't want to add policy for the open or the create.
+ rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile |
+ kCallNtSetInfoRename);
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_ANY: {
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if ((rule_to_add & kCallNtCreateFile) &&
+ (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtOpenFile) &&
+ (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtQueryAttributesFile) &&
+ (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtQueryFullAttributesFile) &&
+ (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE)
+ || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG,
+ &query_full))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtSetInfoRename) &&
+ (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) {
+ return false;
+ }
+
+ return true;
+}
+
+// Right now we insert two rules, to be evaluated before any user supplied rule:
+// - go to the broker if the path doesn't look like the paths that we push on
+// the policy (namely \??\something).
+// - go to the broker if it looks like this is a short-name path.
+//
+// It is possible to add a rule to go to the broker in any case; it would look
+// something like:
+// rule = new PolicyRule(ASK_BROKER);
+// rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
+// policy->AddRule(service, rule);
+bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) {
+ PolicyRule format(ASK_BROKER);
+ PolicyRule short_name(ASK_BROKER);
+
+ bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
+ rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*",
+ CASE_SENSITIVE);
+
+ rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
+ rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE);
+
+ if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format))
+ return false;
+
+ if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name))
+ return false;
+
+ if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format))
+ return false;
+
+ if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name))
+ return false;
+
+ if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format))
+ return false;
+
+ if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name))
+ return false;
+
+ if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format))
+ return false;
+
+ if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name))
+ return false;
+
+ if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format))
+ return false;
+
+ if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name))
+ return false;
+
+ return true;
+}
+
+bool FileSystemPolicy::CreateFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 file_attributes,
+ uint32 share_access,
+ uint32 create_disposition,
+ uint32 create_options,
+ HANDLE *handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR *io_information) {
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+ IO_STATUS_BLOCK io_block = {0};
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
+ *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes,
+ &io_block, file_attributes, share_access,
+ create_disposition, create_options, NULL,
+ 0, client_info.process);
+
+ *io_information = io_block.Information;
+ return true;
+}
+
+bool FileSystemPolicy::OpenFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 share_access,
+ uint32 open_options,
+ HANDLE *handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR *io_information) {
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
+ // CreateDisposition = FILE_OPEN.
+ IO_STATUS_BLOCK io_block = {0};
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
+ *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes,
+ &io_block, 0, share_access, FILE_OPEN,
+ open_options, NULL, 0,
+ client_info.process);
+
+ *io_information = io_block.Information;
+ return true;
+}
+
+bool FileSystemPolicy::QueryAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ FILE_BASIC_INFORMATION* file_info,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means query the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ NtQueryAttributesFileFunction NtQueryAttributesFile = NULL;
+ ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
+ *nt_status = NtQueryAttributesFile(&obj_attributes, file_info);
+
+ return true;
+}
+
+bool FileSystemPolicy::QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ FILE_NETWORK_OPEN_INFORMATION* file_info,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means query the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL;
+ ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile);
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
+ *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info);
+
+ return true;
+}
+
+bool FileSystemPolicy::SetInformationFileAction(
+ EvalResult eval_result, const ClientInfo& client_info,
+ HANDLE target_file_handle, void* file_info, uint32 length,
+ uint32 info_class, IO_STATUS_BLOCK* io_block,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ NtSetInformationFileFunction NtSetInformationFile = NULL;
+ ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile);
+
+ HANDLE local_handle = NULL;
+ if (!::DuplicateHandle(client_info.process, target_file_handle,
+ ::GetCurrentProcess(), &local_handle, 0, FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ base::win::ScopedHandle handle(local_handle);
+
+ FILE_INFORMATION_CLASS file_info_class =
+ static_cast<FILE_INFORMATION_CLASS>(info_class);
+ *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length,
+ file_info_class);
+
+ return true;
+}
+
+bool PreProcessName(const std::wstring& path, std::wstring* new_path) {
+ ConvertToLongPath(path, new_path);
+
+ bool reparsed = false;
+ if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed))
+ return false;
+
+ // We can't process reparsed file.
+ return !reparsed;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/filesystem_policy.h b/chromium/sandbox/win/src/filesystem_policy.h
new file mode 100644
index 00000000000..bcedb6356c8
--- /dev/null
+++ b/chromium/sandbox/win/src/filesystem_policy.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2006-2008 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_FILESYSTEM_POLICY_H__
+#define SANDBOX_SRC_FILESYSTEM_POLICY_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.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 file system policy
+class FileSystemPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for File IO, in particular open or create actions.
+ // 'name' is the file or directory name.
+ // 'semantics' is the desired semantics for the open or create.
+ // '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);
+
+ // Add basic file system rules.
+ static bool SetInitialRules(LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a create request with an
+ // API that is compatible with the IPC-received parameters.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'file' : The target file or directory.
+ static bool CreateFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 file_attributes,
+ uint32 share_access,
+ uint32 create_disposition,
+ uint32 create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information);
+
+ // Performs the desired policy action on an open request with an
+ // API that is compatible with the IPC-received parameters.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'file' : The target file or directory.
+ static bool OpenFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 share_access,
+ uint32 open_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information);
+
+ // Performs the desired policy action on a query request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool QueryAttributesFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ FILE_BASIC_INFORMATION* file_info,
+ NTSTATUS* nt_status);
+
+ // Performs the desired policy action on a query request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &file,
+ uint32 attributes,
+ FILE_NETWORK_OPEN_INFORMATION* file_info,
+ NTSTATUS* nt_status);
+
+ // Performs the desired policy action on a set_info request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool SetInformationFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ HANDLE target_file_handle,
+ void* file_info,
+ uint32 length,
+ uint32 info_class,
+ IO_STATUS_BLOCK* io_block,
+ NTSTATUS* nt_status);
+};
+
+// Expands the path and check if it's a reparse point. Returns false if
+// we cannot determine or if there is an unexpected error. In that case
+// the path cannot be trusted.
+bool PreProcessName(const std::wstring& path, std::wstring* new_path);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_POLICY_H__
diff --git a/chromium/sandbox/win/src/handle_closer.cc b/chromium/sandbox/win/src/handle_closer.cc
new file mode 100644
index 00000000000..39915a9b0a1
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_closer.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2011 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/handle_closer.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+template<typename T> T RoundUpToWordSize(T v) {
+ if (size_t mod = v % sizeof(size_t))
+ v += sizeof(size_t) - mod;
+ return v;
+}
+
+template<typename T> T* RoundUpToWordSize(T* v) {
+ return reinterpret_cast<T*>(RoundUpToWordSize(reinterpret_cast<size_t>(v)));
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
+
+HandleCloser::HandleCloser() {}
+
+ResultCode HandleCloser::AddHandle(const char16* handle_type,
+ const char16* handle_name) {
+ if (!handle_type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ HandleMap::iterator names = handles_to_close_.find(handle_type);
+ if (names == handles_to_close_.end()) { // We have no entries for this type.
+ std::pair<HandleMap::iterator, bool> result = handles_to_close_.insert(
+ HandleMap::value_type(handle_type, HandleMap::mapped_type()));
+ names = result.first;
+ if (handle_name)
+ names->second.insert(handle_name);
+ } else if (!handle_name) { // Now we need to close all handles of this type.
+ names->second.clear();
+ } else if (!names->second.empty()) { // Add another name for this type.
+ names->second.insert(handle_name);
+ } // If we're already closing all handles of type then we're done.
+
+ return SBOX_ALL_OK;
+}
+
+size_t HandleCloser::GetBufferSize() {
+ size_t bytes_total = offsetof(HandleCloserInfo, handle_entries);
+
+ 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);
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ bytes_entry += ((*j).size() + 1) * sizeof(char16);
+ }
+
+ // Round up to the nearest multiple of word size.
+ bytes_entry = RoundUpToWordSize(bytes_entry);
+ bytes_total += bytes_entry;
+ }
+
+ return bytes_total;
+}
+
+bool HandleCloser::InitializeTargetHandles(TargetProcess* target) {
+ // Do nothing on an empty list (global pointer already initialized to NULL).
+ if (handles_to_close_.empty())
+ return true;
+
+ size_t bytes_needed = GetBufferSize();
+ scoped_ptr<size_t[]> local_buffer(
+ new size_t[bytes_needed / sizeof(size_t)]);
+
+ if (!SetupHandleList(local_buffer.get(), bytes_needed))
+ return false;
+
+ HANDLE child = target->Process();
+
+ // Allocate memory in the target process without specifying the address
+ void* remote_data = ::VirtualAllocEx(child, NULL, bytes_needed,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (NULL == remote_data)
+ return false;
+
+ // Copy the handle buffer over.
+ SIZE_T bytes_written;
+ BOOL result = ::WriteProcessMemory(child, remote_data, local_buffer.get(),
+ bytes_needed, &bytes_written);
+ if (!result || bytes_written != bytes_needed) {
+ ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
+ return false;
+ }
+
+ g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data);
+
+ ResultCode rc = target->TransferVariable("g_handles_to_close",
+ &g_handles_to_close,
+ sizeof(g_handles_to_close));
+
+ return (SBOX_ALL_OK == rc);
+}
+
+bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
+ ::ZeroMemory(buffer, buffer_bytes);
+ HandleCloserInfo* handle_info = reinterpret_cast<HandleCloserInfo*>(buffer);
+ 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*>(
+ reinterpret_cast<char*>(buffer) + buffer_bytes);
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ if (output >= end)
+ return false;
+ HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output);
+ output = &list_entry->handle_type[0];
+
+ // Copy the typename and set the offset and count.
+ i->first._Copy_s(output, i->first.size(), i->first.size());
+ *(output += i->first.size()) = L'\0';
+ output++;
+ list_entry->offset_to_names = reinterpret_cast<char*>(output) -
+ reinterpret_cast<char*>(list_entry);
+ list_entry->name_count = i->second.size();
+
+ // Copy the handle names.
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ output = std::copy((*j).begin(), (*j).end(), output) + 1;
+ }
+
+ // Round up to the nearest multiple of sizeof(size_t).
+ output = RoundUpToWordSize(output);
+ list_entry->record_bytes = reinterpret_cast<char*>(output) -
+ reinterpret_cast<char*>(list_entry);
+ }
+
+ DCHECK_EQ(reinterpret_cast<size_t>(output), reinterpret_cast<size_t>(end));
+ 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) {
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ ULONG size = MAX_PATH;
+ scoped_ptr<UNICODE_STRING, base::FreeDeleter> name;
+ NTSTATUS result;
+
+ do {
+ name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
+ DCHECK(name.get());
+ result = QueryObject(handle, ObjectNameInformation, name.get(),
+ size, &size);
+ } while (result == STATUS_INFO_LENGTH_MISMATCH ||
+ result == STATUS_BUFFER_OVERFLOW);
+
+ if (NT_SUCCESS(result) && name->Buffer && name->Length)
+ handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
+ else
+ handle_name->clear();
+
+ return NT_SUCCESS(result);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/handle_closer.h b/chromium/sandbox/win/src/handle_closer.h
new file mode 100644
index 00000000000..a6f81d5b290
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_closer.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_CLOSER_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_process.h"
+
+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;
+
+// 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];
+};
+
+// Global parameters and a pointer to the list of entries.
+struct HandleCloserInfo {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t num_handle_types;
+ struct HandleListEntry handle_entries[1];
+};
+
+SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info;
+
+// Adds handles to close after lockdown.
+class HandleCloser {
+ public:
+ 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);
+
+ // 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).
+ size_t GetBufferSize();
+
+ // Serializes the handle list into the target process.
+ bool SetupHandleList(void* buffer, size_t buffer_bytes);
+
+ HandleMap handles_to_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloser);
+};
+
+// Returns the object manager's name associated with a handle
+bool GetHandleName(HANDLE handle, string16* handle_name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_H_
diff --git a/chromium/sandbox/win/src/handle_closer_agent.cc b/chromium/sandbox/win/src/handle_closer_agent.cc
new file mode 100644
index 00000000000..bc75e7322b2
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_closer_agent.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_closer_agent.h"
+
+#include "base/logging.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Returns type infomation for an NT object. This routine is expected to be
+// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
+// that can be generated when handle tracing is enabled.
+NTSTATUS QueryObjectTypeInformation(HANDLE handle,
+ void* buffer,
+ ULONG* size) {
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ __try {
+ status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
+ } __except(GetExceptionCode() == STATUS_INVALID_HANDLE ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ status = STATUS_INVALID_HANDLE;
+ }
+ return status;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = NULL;
+
+bool HandleCloserAgent::NeedsHandlesClosed() {
+ return g_handles_to_close != NULL;
+}
+
+// Reads g_handles_to_close and creates the lookup map.
+void HandleCloserAgent::InitializeHandlesToClose() {
+ CHECK(g_handles_to_close != NULL);
+
+ // Grab the header.
+ 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;
+ HandleMap::mapped_type& handle_names = handles_to_close_[input];
+ input = reinterpret_cast<char16*>(reinterpret_cast<char*>(entry)
+ + entry->offset_to_names);
+ // Grab all the handle names.
+ for (size_t j = 0; j < entry->name_count; ++j) {
+ std::pair<HandleMap::mapped_type::iterator, bool> name
+ = handle_names.insert(input);
+ CHECK(name.second);
+ input += name.first->size() + 1;
+ }
+
+ // Move on to the next entry.
+ 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));
+ }
+
+ // Clean up the memory we copied over.
+ ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE);
+ g_handles_to_close = NULL;
+}
+
+bool HandleCloserAgent::CloseHandles() {
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = sizeof(HANDLE);
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return false;
+
+ // Set up buffers for the type info and the name.
+ std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+ 32 * sizeof(wchar_t));
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ string16 handle_name;
+ HANDLE handle = NULL;
+ int invalid_count = 0;
+
+ // Keep incrementing until we hit the number of handles reported by
+ // GetProcessHandleCount(). If we hit a very long sequence of invalid
+ // handles we assume that we've run past the end of the table.
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ NTSTATUS rc;
+
+ // Get the type name, reusing the buffer.
+ ULONG size = static_cast<ULONG>(type_info_buffer.size());
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ while (rc == STATUS_INFO_LENGTH_MISMATCH ||
+ rc == STATUS_BUFFER_OVERFLOW) {
+ type_info_buffer.resize(size + sizeof(wchar_t));
+ type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
+ &(type_info_buffer[0]));
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ // Leave padding for the nul terminator.
+ if (NT_SUCCESS(rc) && size == type_info_buffer.size())
+ rc = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) {
+ ++invalid_count;
+ continue;
+ }
+
+ --handle_count;
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ // Check if we're looking for this type of handle.
+ HandleMap::iterator result =
+ handles_to_close_.find(type_info->Name.Buffer);
+ if (result != handles_to_close_.end()) {
+ HandleMap::mapped_type& names = result->second;
+ // Empty set means close all handles of this type; otherwise check name.
+ if (!names.empty()) {
+ // Move on to the next handle if this name doesn't match.
+ if (!GetHandleName(handle, &handle_name) || !names.count(handle_name))
+ continue;
+ }
+
+ if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0))
+ return false;
+ if (!::CloseHandle(handle))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/handle_closer_agent.h b/chromium/sandbox/win/src/handle_closer_agent.h
new file mode 100644
index 00000000000..50ebf8959d3
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_closer_agent.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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_HANDLE_CLOSER_AGENT_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Target process code to close the handle list copied over from the broker.
+class HandleCloserAgent {
+ public:
+ HandleCloserAgent() {}
+
+ // Reads the serialized list from the broker and creates the lookup map.
+ void InitializeHandlesToClose();
+
+ // Closes any handles matching those in the lookup map.
+ bool CloseHandles();
+
+ // True if we have handles waiting to be closed
+ static bool NeedsHandlesClosed();
+
+ private:
+ HandleMap handles_to_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
diff --git a/chromium/sandbox/win/src/handle_closer_test.cc b/chromium/sandbox/win/src/handle_closer_test.cc
new file mode 100644
index 00000000000..ba0e33a2204
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_closer_test.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2011 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 "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const wchar_t *kFileExtensions[] = { L".1", L".2", L".3", L".4" };
+
+// Returns a handle to a unique marker file that can be retrieved between runs.
+HANDLE GetMarkerFile(const wchar_t *extension) {
+ wchar_t path_buffer[MAX_PATH + 1];
+ CHECK(::GetTempPath(MAX_PATH, path_buffer));
+ string16 marker_path = path_buffer;
+ marker_path += L"\\sbox_marker_";
+
+ // Generate a unique value from the exe's size and timestamp.
+ CHECK(::GetModuleFileName(NULL, path_buffer, MAX_PATH));
+ base::win::ScopedHandle module(::CreateFile(path_buffer,
+ FILE_READ_ATTRIBUTES, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ CHECK(module.IsValid());
+ FILETIME timestamp;
+ CHECK(::GetFileTime(module, &timestamp, NULL, NULL));
+ marker_path += base::StringPrintf(L"%08x%08x%08x",
+ ::GetFileSize(module, NULL),
+ timestamp.dwLowDateTime,
+ timestamp.dwHighDateTime);
+ marker_path += extension;
+
+ // Make the file delete-on-close so cleanup is automatic.
+ return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL);
+}
+
+// Used by the thread pool tests.
+HANDLE finish_event;
+const int kWaitCount = 20;
+
+} // namespace
+
+namespace sandbox {
+
+// Checks for the presence of a list of files (in object path form).
+// Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...]
+// - Y or N depending if the file should exist or not.
+SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) {
+ if (argc < 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ bool should_find = argv[0][0] == L'Y';
+ if (argv[0][1] != L'\0' || !should_find && argv[0][0] != L'N')
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ static int state = BEFORE_INIT;
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ // The handles leak, but it will be closed by the test or on exit.
+ for (int i = 0; i < arraysize(kFileExtensions); ++i)
+ EXPECT_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE);
+ return SBOX_TEST_SUCCEEDED;
+
+ case AFTER_REVERT: {
+ // 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);
+ HANDLE handle = NULL;
+ int invalid_count = 0;
+ string16 handle_name;
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ if (GetHandleName(handle, &handle_name)) {
+ for (int i = 1; i < argc; ++i) {
+ if (handle_name == argv[i])
+ return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ }
+ --handle_count;
+ } else {
+ ++invalid_count;
+ }
+ }
+
+ return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+
+ default: // Do nothing.
+ break;
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, CheckForMarkerFiles) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ string16 command = string16(L"CheckForFileHandles Y");
+ for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+ string16 handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker, &handle_name));
+ command += (L" ");
+ command += handle_name;
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) <<
+ "Failed: " << command;
+}
+
+TEST(HandleCloserTest, CloseMarkerFiles) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ string16 command = string16(L"CheckForFileHandles N");
+ for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+ string16 handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker, &handle_name));
+ CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
+ SBOX_ALL_OK);
+ command += (L" ");
+ command += handle_name;
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str())) <<
+ "Failed: " << command;
+}
+
+void WINAPI ThreadPoolTask(void* event, BOOLEAN timeout) {
+ static volatile LONG waiters_remaining = kWaitCount;
+ CHECK(!timeout);
+ CHECK(::CloseHandle(event));
+ if (::InterlockedDecrement(&waiters_remaining) == 0)
+ CHECK(::SetEvent(finish_event));
+}
+
+// Run a thread pool inside a sandbox without a CSRSS connection.
+SBOX_TESTS_COMMAND int RunThreadPool(int argc, wchar_t **argv) {
+ HANDLE wait_list[20];
+ CHECK(finish_event = ::CreateEvent(NULL, TRUE, FALSE, NULL));
+
+ // Set up a bunch of waiters.
+ HANDLE pool = NULL;
+ for (int i = 0; i < kWaitCount; ++i) {
+ HANDLE event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ CHECK(event);
+ CHECK(::RegisterWaitForSingleObject(&pool, event, ThreadPoolTask, event,
+ INFINITE, WT_EXECUTEONLYONCE));
+ wait_list[i] = event;
+ }
+
+ // Signal all the waiters.
+ for (int i = 0; i < kWaitCount; ++i)
+ CHECK(::SetEvent(wait_list[i]));
+
+ CHECK_EQ(::WaitForSingleObject(finish_event, INFINITE), WAIT_OBJECT_0);
+ CHECK(::CloseHandle(finish_event));
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, RunThreadPool) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(AFTER_REVERT);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ // Sever the CSRSS connection by closing ALPC ports inside the sandbox.
+ CHECK_EQ(policy->AddKernelObjectToClose(L"ALPC Port", NULL), SBOX_ALL_OK);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"RunThreadPool"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/handle_dispatcher.cc b/chromium/sandbox/win/src/handle_dispatcher.cc
new file mode 100644
index 00000000000..26b8fc365ad
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_dispatcher.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_dispatcher.h"
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_interception.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+HandleDispatcher::HandleDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall duplicate_handle_proxy = {
+ {IPC_DUPLICATEHANDLEPROXY_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE,
+ ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&HandleDispatcher::DuplicateHandleProxy)
+ };
+
+ ipc_calls_.push_back(duplicate_handle_proxy);
+}
+
+bool HandleDispatcher::SetupService(InterceptionManager* manager,
+ int service) {
+ // We perform no interceptions for handles right now.
+ switch (service) {
+ case IPC_DUPLICATEHANDLEPROXY_TAG:
+ return true;
+ }
+
+ return false;
+}
+
+bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc,
+ HANDLE source_handle,
+ DWORD target_process_id,
+ DWORD desired_access,
+ DWORD options) {
+ NTSTATUS error;
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ // Get a copy of the handle for use in the broker process.
+ HANDLE handle_temp;
+ if (!::DuplicateHandle(ipc->client_info->process, source_handle,
+ ::GetCurrentProcess(), &handle_temp,
+ 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ ipc->return_info.win32_result = ::GetLastError();
+ return false;
+ }
+ base::win::ScopedHandle handle(handle_temp);
+
+ // Get the object type (32 characters is safe; current max is 14).
+ BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)];
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer);
+ ULONG size = sizeof(buffer) - sizeof(wchar_t);
+ error = QueryObject(handle, ObjectTypeInformation, type_info, size, &size);
+ if (!NT_SUCCESS(error)) {
+ ipc->return_info.win32_result = error;
+ return false;
+ }
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ CountedParameterSet<HandleTarget> params;
+ params[HandleTarget::NAME] = ParamPickerMake(type_info->Name.Buffer);
+ params[HandleTarget::TARGET] = ParamPickerMake(target_process_id);
+
+ EvalResult eval = policy_base_->EvalPolicy(IPC_DUPLICATEHANDLEPROXY_TAG,
+ params.GetBase());
+ ipc->return_info.win32_result =
+ HandlePolicy::DuplicateHandleProxyAction(eval, *ipc->client_info,
+ source_handle,
+ target_process_id,
+ &ipc->return_info.handle,
+ desired_access, options);
+ return true;
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/handle_dispatcher.h b/chromium/sandbox/win/src/handle_dispatcher.h
new file mode 100644
index 00000000000..a3dc6cf662a
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_
+#define SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles handle-related IPC calls.
+class HandleDispatcher : public Dispatcher {
+ public:
+ explicit HandleDispatcher(PolicyBase* policy_base);
+ ~HandleDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+ private:
+ // Processes IPC requests coming from calls to
+ // TargetServices::DuplicateHandle() in the target.
+ bool DuplicateHandleProxy(IPCInfo* ipc, HANDLE source_handle,
+ DWORD target_process_id, DWORD desired_access,
+ DWORD options);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(HandleDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
diff --git a/chromium/sandbox/win/src/handle_inheritance_test.cc b/chromium/sandbox/win/src/handle_inheritance_test.cc
new file mode 100644
index 00000000000..5523a6c64a6
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_inheritance_test.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "base/file_util.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int HandleInheritanceTests_PrintToStdout(int argc,
+ wchar_t** argv) {
+ printf("Example output to stdout\n");
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleInheritanceTests, TestStdoutInheritance) {
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ SECURITY_ATTRIBUTES attrs = {};
+ attrs.nLength = sizeof(attrs);
+ attrs.lpSecurityDescriptor = NULL;
+ attrs.bInheritHandle = TRUE;
+ HANDLE file_handle = CreateFile(
+ temp_file_name, GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
+ &attrs, OPEN_EXISTING, 0, NULL);
+ EXPECT_NE(file_handle, INVALID_HANDLE_VALUE);
+
+ TestRunner runner;
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStdoutHandle(file_handle));
+ int result = runner.RunTest(L"HandleInheritanceTests_PrintToStdout");
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, result);
+ EXPECT_TRUE(::CloseHandle(file_handle));
+
+ std::string data;
+ EXPECT_TRUE(file_util::ReadFileToString(base::FilePath(temp_file_name),
+ &data));
+ // Redirection uses a feature that was added in Windows Vista.
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
+ EXPECT_EQ("Example output to stdout\r\n", data);
+ } else {
+ EXPECT_EQ("", data);
+ }
+
+ EXPECT_TRUE(::DeleteFile(temp_file_name));
+}
+
+}
diff --git a/chromium/sandbox/win/src/handle_interception.cc b/chromium/sandbox/win/src/handle_interception.cc
new file mode 100644
index 00000000000..af0bebca045
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_interception.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ *target_handle = NULL;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ return SBOX_ERROR_NO_SPACE;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_DUPLICATEHANDLEPROXY_TAG,
+ source_handle, target_process_id,
+ desired_access, options, &answer);
+ if (SBOX_ALL_OK != code)
+ return code;
+
+ if (answer.win32_result) {
+ ::SetLastError(answer.nt_status);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ *target_handle = answer.handle;
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/handle_interception.h b/chromium/sandbox/win/src/handle_interception.h
new file mode 100644
index 00000000000..6f60811f17e
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_interception.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+#define SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+
+namespace sandbox {
+
+// TODO(jschuh) Add an interception to catch dangerous DuplicateHandle calls.
+
+ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+
diff --git a/chromium/sandbox/win/src/handle_policy.cc b/chromium/sandbox/win/src/handle_policy.cc
new file mode 100644
index 00000000000..718376ecee1
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_policy.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_policy.h"
+
+#include <string>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/broker_services.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+bool HandlePolicy::GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule duplicate_rule(ASK_BROKER);
+
+ switch (semantics) {
+ case TargetPolicy::HANDLES_DUP_ANY: {
+ if (!duplicate_rule.AddNumberMatch(IF_NOT, HandleTarget::TARGET,
+ ::GetCurrentProcessId(), EQUAL)) {
+ return false;
+ }
+ break;
+ }
+
+ case TargetPolicy::HANDLES_DUP_BROKER: {
+ if (!duplicate_rule.AddNumberMatch(IF, HandleTarget::TARGET,
+ ::GetCurrentProcessId(), EQUAL)) {
+ return false;
+ }
+ break;
+ }
+
+ default:
+ return false;
+ }
+ if (!duplicate_rule.AddStringMatch(IF, HandleTarget::NAME, type_name,
+ CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IPC_DUPLICATEHANDLEPROXY_TAG, &duplicate_rule)) {
+ return false;
+ }
+ return true;
+}
+
+DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ // The only action supported is ASK_BROKER which means duplicate the handle.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ base::win::ScopedHandle remote_target_process;
+ if (target_process_id != ::GetCurrentProcessId()) {
+ // Sandboxed children are dynamic, so we check that manually.
+ if (!BrokerServicesBase::GetInstance()->IsActiveTarget(target_process_id)) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ remote_target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE,
+ target_process_id));
+ if (!remote_target_process.IsValid())
+ return ::GetLastError();
+ }
+
+ // If the policy didn't block us and we have no valid target, then the broker
+ // (this process) is the valid target.
+ 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,
+ target_handle, desired_access, FALSE,
+ options)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/handle_policy.h b/chromium/sandbox/win/src/handle_policy.h
new file mode 100644
index 00000000000..d91a0393aef
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_policy.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_POLICY_H_
+#define SANDBOX_SRC_HANDLE_POLICY_H_
+
+#include <string>
+
+#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 handle policy.
+class HandlePolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for handles, in particular duplicate action.
+ static bool GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // 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,
+ DWORD desired_access,
+ DWORD options);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_POLICY_H_
+
diff --git a/chromium/sandbox/win/src/handle_policy_test.cc b/chromium/sandbox/win/src/handle_policy_test.cc
new file mode 100644
index 00000000000..11e888abe72
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_policy_test.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/stringprintf.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Just waits for the supplied number of milliseconds.
+SBOX_TESTS_COMMAND int Handle_WaitProcess(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ ::Sleep(::wcstoul(argv[0], NULL, 10));
+ return SBOX_TEST_TIMED_OUT;
+}
+
+// Attempts to duplicate an event handle into the target process.
+SBOX_TESTS_COMMAND int Handle_DuplicateEvent(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // Create a test event to use as a handle.
+ base::win::ScopedHandle test_event;
+ test_event.Set(::CreateEvent(NULL, TRUE, TRUE, NULL));
+ if (!test_event.IsValid())
+ return SBOX_TEST_FIRST_ERROR;
+
+ // Get the target process ID.
+ DWORD target_process_id = ::wcstoul(argv[0], NULL, 10);
+
+ HANDLE handle = NULL;
+ ResultCode result = SandboxFactory::GetTargetServices()->DuplicateHandle(
+ test_event, target_process_id, &handle, 0, DUPLICATE_SAME_ACCESS);
+
+ return (result == SBOX_ALL_OK) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicateHandle) {
+ TestRunner target;
+ TestRunner runner;
+
+ // Kick off an asynchronous target process for testing.
+ target.SetAsynchronous(true);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
+
+ // First test that we fail to open the event.
+ std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicatePeerHandle) {
+ TestRunner target;
+ TestRunner runner;
+
+ // Kick off an asynchronous target process for testing.
+ target.SetAsynchronous(true);
+ target.SetUnsandboxed(true);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
+
+ // First test that we fail to open the event.
+ std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicateBrokerHandle) {
+ TestRunner runner;
+
+ // First test that we fail to open the event.
+ std::wstring cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ ::GetCurrentProcessId());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Add the peer rule and make sure we fail again.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+
+ // Now successfully open the event after adding a broker handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_BROKER,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/handle_table.cc b/chromium/sandbox/win/src/handle_table.cc
new file mode 100644
index 00000000000..7230dff87b5
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_table.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2011 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/handle_table.h"
+
+#include <algorithm>
+#include <cstdlib>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+bool CompareHandleEntries(const SYSTEM_HANDLE_INFORMATION& a,
+ const SYSTEM_HANDLE_INFORMATION& b) {
+ return a.ProcessId < b.ProcessId;
+}
+
+} // namespace
+
+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";
+
+HandleTable::HandleTable() {
+ static NtQuerySystemInformation QuerySystemInformation = NULL;
+ if (!QuerySystemInformation)
+ ResolveNTFunctionPtr("NtQuerySystemInformation", &QuerySystemInformation);
+
+ ULONG size = 0x15000;
+ NTSTATUS result;
+ do {
+ handle_info_buffer_.resize(size);
+ result = QuerySystemInformation(SystemHandleInformation,
+ handle_info_internal(), size, &size);
+ } while (result == STATUS_INFO_LENGTH_MISMATCH);
+
+ // We failed, so make an empty table.
+ if (!NT_SUCCESS(result)) {
+ handle_info_buffer_.resize(0);
+ return;
+ }
+
+ // Sort it to make process lookups faster.
+ std::sort(handle_info_internal()->Information,
+ handle_info_internal()->Information +
+ handle_info_internal()->NumberOfHandles, CompareHandleEntries);
+}
+
+HandleTable::Iterator HandleTable::HandlesForProcess(ULONG process_id) const {
+ SYSTEM_HANDLE_INFORMATION key;
+ key.ProcessId = process_id;
+
+ const SYSTEM_HANDLE_INFORMATION* start = handle_info()->Information;
+ const SYSTEM_HANDLE_INFORMATION* finish =
+ &handle_info()->Information[handle_info()->NumberOfHandles];
+
+ start = std::lower_bound(start, finish, key, CompareHandleEntries);
+ if (start->ProcessId != process_id)
+ return Iterator(*this, finish, finish);
+ finish = std::upper_bound(start, finish, key, CompareHandleEntries);
+ return Iterator(*this, start, finish);
+}
+
+HandleTable::HandleEntry::HandleEntry(
+ const SYSTEM_HANDLE_INFORMATION* handle_info_entry)
+ : handle_entry_(handle_info_entry), last_entry_(0) {
+}
+
+void HandleTable::HandleEntry::UpdateInfo(UpdateType flag) {
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ NTSTATUS result;
+
+ // Always update the basic type info, but grab the names as needed.
+ if (needs_info_update()) {
+ handle_name_.clear();
+ type_name_.clear();
+ last_entry_ = handle_entry_;
+
+ // Most handle names are very short, so start small and reuse this buffer.
+ if (type_info_buffer_.empty())
+ type_info_buffer_.resize(sizeof(OBJECT_TYPE_INFORMATION)
+ + (32 * sizeof(wchar_t)));
+ ULONG size = static_cast<ULONG>(type_info_buffer_.size());
+ result = QueryObject(reinterpret_cast<HANDLE>(handle_entry_->Handle),
+ ObjectTypeInformation, type_info_internal(), size, &size);
+ while (result == STATUS_INFO_LENGTH_MISMATCH) {
+ type_info_buffer_.resize(size);
+ result = QueryObject(reinterpret_cast<HANDLE>(handle_entry_->Handle),
+ ObjectTypeInformation, type_info_internal(), size, &size);
+ }
+
+ if (!NT_SUCCESS(result)) {
+ type_info_buffer_.clear();
+ return;
+ }
+ }
+
+ // Don't bother copying out names until we ask for them, and then cache them.
+ switch (flag) {
+ case UPDATE_INFO_AND_NAME:
+ if (type_info_buffer_.size() && handle_name_.empty()) {
+ ULONG size = MAX_PATH;
+ scoped_ptr<UNICODE_STRING, base::FreeDeleter> name;
+ do {
+ name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
+ DCHECK(name.get());
+ result = QueryObject(reinterpret_cast<HANDLE>(
+ handle_entry_->Handle), ObjectNameInformation, name.get(),
+ size, &size);
+ } while (result == STATUS_INFO_LENGTH_MISMATCH);
+
+ if (NT_SUCCESS(result)) {
+ handle_name_.assign(name->Buffer, name->Length / sizeof(wchar_t));
+ }
+ }
+ break;
+
+ case UPDATE_INFO_AND_TYPE_NAME:
+ if (!type_info_buffer_.empty() && type_info_internal()->Name.Buffer &&
+ type_name_.empty()) {
+ type_name_.assign(type_info_internal()->Name.Buffer,
+ type_info_internal()->Name.Length / sizeof(wchar_t));
+ }
+ break;
+ }
+}
+
+const OBJECT_TYPE_INFORMATION* HandleTable::HandleEntry::TypeInfo() {
+ UpdateInfo(UPDATE_INFO_ONLY);
+ return type_info_buffer_.empty() ? NULL : type_info_internal();
+}
+
+const string16& HandleTable::HandleEntry::Name() {
+ UpdateInfo(UPDATE_INFO_AND_NAME);
+ return handle_name_;
+}
+
+const string16& HandleTable::HandleEntry::Type() {
+ UpdateInfo(UPDATE_INFO_AND_TYPE_NAME);
+ return type_name_;
+}
+
+bool HandleTable::HandleEntry::IsType(const string16& type_string) {
+ UpdateInfo(UPDATE_INFO_ONLY);
+ if (type_info_buffer_.empty())
+ return false;
+ return type_string.compare(0,
+ type_info_internal()->Name.Length / sizeof(wchar_t),
+ type_info_internal()->Name.Buffer) == 0;
+}
+
+HandleTable::Iterator::Iterator(const HandleTable& table,
+ const SYSTEM_HANDLE_INFORMATION* start,
+ const SYSTEM_HANDLE_INFORMATION* end)
+ : table_(table), current_(start), end_(end) {
+}
+
+HandleTable::Iterator::Iterator(const Iterator& it)
+ : table_(it.table_), current_(it.current_.handle_entry_), end_(it.end_) {
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/handle_table.h b/chromium/sandbox/win/src/handle_table.h
new file mode 100644
index 00000000000..21ff80ff481
--- /dev/null
+++ b/chromium/sandbox/win/src/handle_table.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2011 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_HANDLE_TABLE_H_
+#define SANDBOX_SRC_HANDLE_TABLE_H_
+
+#include <windows.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+// HandleTable retrieves the global handle table and provides helper classes
+// 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;
+
+ class Iterator;
+
+ // Used by the iterator to provide simple caching accessors to handle data.
+ class HandleEntry {
+ public:
+ bool operator==(const HandleEntry& rhs) const {
+ return handle_entry_ == rhs.handle_entry_;
+ }
+
+ bool operator!=(const HandleEntry& rhs) const {
+ return handle_entry_ != rhs.handle_entry_;
+ }
+
+ const SYSTEM_HANDLE_INFORMATION* handle_entry() const {
+ return handle_entry_;
+ }
+
+ const OBJECT_TYPE_INFORMATION* TypeInfo();
+
+ const string16& Name();
+
+ const string16& Type();
+
+ bool IsType(const string16& type_string);
+
+ private:
+ friend class Iterator;
+ friend class HandleTable;
+
+ enum UpdateType {
+ UPDATE_INFO_ONLY,
+ UPDATE_INFO_AND_NAME,
+ UPDATE_INFO_AND_TYPE_NAME,
+ };
+
+ explicit HandleEntry(const SYSTEM_HANDLE_INFORMATION* handle_info_entry);
+
+ bool needs_info_update() { return handle_entry_ != last_entry_; }
+
+ void UpdateInfo(UpdateType flag);
+
+ OBJECT_TYPE_INFORMATION* type_info_internal() {
+ return reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
+ &(type_info_buffer_[0]));
+ }
+
+ const SYSTEM_HANDLE_INFORMATION* handle_entry_;
+ const SYSTEM_HANDLE_INFORMATION* last_entry_;
+ std::vector<BYTE> type_info_buffer_;
+ string16 handle_name_;
+ string16 type_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleEntry);
+ };
+
+ class Iterator {
+ public:
+ Iterator(const HandleTable& table, const SYSTEM_HANDLE_INFORMATION* start,
+ const SYSTEM_HANDLE_INFORMATION* stop);
+
+ Iterator(const Iterator& it);
+
+ Iterator& operator++() {
+ if (++(current_.handle_entry_) == end_)
+ current_.handle_entry_ = table_.end();
+ return *this;
+ }
+
+ bool operator==(const Iterator& rhs) const {
+ return current_ == rhs.current_;
+ }
+
+ bool operator!=(const Iterator& rhs) const {
+ return current_ != rhs.current_;
+ }
+
+ HandleEntry& operator*() { return current_; }
+
+ operator const SYSTEM_HANDLE_INFORMATION*() {
+ return current_.handle_entry_;
+ }
+
+ HandleEntry* operator->() { return &current_; }
+
+ private:
+ const HandleTable& table_;
+ HandleEntry current_;
+ const SYSTEM_HANDLE_INFORMATION* end_;
+ };
+
+ HandleTable();
+
+ Iterator begin() const {
+ return Iterator(*this, handle_info()->Information,
+ &handle_info()->Information[handle_info()->NumberOfHandles]);
+ }
+
+ const SYSTEM_HANDLE_INFORMATION_EX* handle_info() const {
+ return reinterpret_cast<const SYSTEM_HANDLE_INFORMATION_EX*>(
+ &(handle_info_buffer_[0]));
+ }
+
+ // Returns an iterator to the handles for only the supplied process ID.
+ Iterator HandlesForProcess(ULONG process_id) const;
+ const SYSTEM_HANDLE_INFORMATION* end() const {
+ return &handle_info()->Information[handle_info()->NumberOfHandles];
+ }
+
+ private:
+ SYSTEM_HANDLE_INFORMATION_EX* handle_info_internal() {
+ return reinterpret_cast<SYSTEM_HANDLE_INFORMATION_EX*>(
+ &(handle_info_buffer_[0]));
+ }
+
+ std::vector<BYTE> handle_info_buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleTable);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_TABLE_H_
diff --git a/chromium/sandbox/win/src/integrity_level_test.cc b/chromium/sandbox/win/src/integrity_level_test.cc
new file mode 100644
index 00000000000..67ea9de252f
--- /dev/null
+++ b/chromium/sandbox/win/src/integrity_level_test.cc
@@ -0,0 +1,90 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <atlsecurity.h>
+
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+
+SBOX_TESTS_COMMAND int CheckIntegrityLevel(int argc, wchar_t **argv) {
+ ATL::CAccessToken token;
+ if (!token.GetEffectiveToken(TOKEN_READ))
+ return SBOX_TEST_FAILED;
+
+ char* buffer[100];
+ DWORD buf_size = 100;
+ if (!::GetTokenInformation(token.GetHandle(), TokenIntegrityLevel,
+ reinterpret_cast<void*>(buffer), buf_size,
+ &buf_size))
+ return SBOX_TEST_FAILED;
+
+ TOKEN_MANDATORY_LABEL* label =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buffer);
+
+ PSID sid_low = NULL;
+ if (!::ConvertStringSidToSid(L"S-1-16-4096", &sid_low))
+ return SBOX_TEST_FAILED;
+
+ BOOL is_low_sid = ::EqualSid(label->Label.Sid, sid_low);
+
+ ::LocalFree(sid_low);
+
+ if (is_low_sid)
+ return SBOX_TEST_SUCCEEDED;
+
+ return SBOX_TEST_DENIED;
+}
+
+TEST(IntegrityLevelTest, TestLowILReal) {
+ if (base::win::GetVersion() != base::win::VERSION_VISTA)
+ return;
+
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(DelayedIntegrityLevelTest, TestLowILDelayed) {
+ if (base::win::GetVersion() != base::win::VERSION_VISTA)
+ return;
+
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestNoILChange) {
+ if (base::win::GetVersion() != base::win::VERSION_VISTA)
+ return;
+
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/interception.cc b/chromium/sandbox/win/src/interception.cc
new file mode 100644
index 00000000000..8c897a1a977
--- /dev/null
+++ b/chromium/sandbox/win/src/interception.cc
@@ -0,0 +1,550 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include <set>
+
+#include "sandbox/win/src/interception.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#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"
+#include "sandbox/win/src/wow64.h"
+
+namespace {
+
+const char kMapViewOfSectionName[] = "NtMapViewOfSection";
+const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection";
+
+// Standard allocation granularity and page size for Windows.
+const size_t kAllocGranularity = 65536;
+const size_t kPageSize = 4096;
+
+// Find a random offset within 64k and aligned to ceil(log2(size)).
+size_t GetGranularAlignedRandomOffset(size_t size) {
+ CHECK_LE(size, kAllocGranularity);
+ unsigned int offset;
+
+ do {
+ rand_s(&offset);
+ offset &= (kAllocGranularity - 1);
+ } while (offset > (kAllocGranularity - size));
+
+ // Find an alignment between 64 and the page size (4096).
+ size_t align_size = kPageSize;
+ for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) {
+ align_size = new_size;
+ }
+ return offset & ~(align_size - 1);
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT SharedMemory* g_interceptions;
+
+// Table of the unpatched functions that we intercept. Mapped from the parent.
+SANDBOX_INTERCEPT OriginalFunctions g_originals = { NULL };
+
+// Magic constant that identifies that this function is not to be patched.
+const char kUnloadDLLDummyFunction[] = "@";
+
+InterceptionManager::InterceptionManager(TargetProcess* child_process,
+ bool relaxed)
+ : child_(child_process), names_used_(false), relaxed_(relaxed) {
+ child_->AddRef();
+}
+InterceptionManager::~InterceptionManager() {
+ child_->Release();
+}
+
+bool InterceptionManager::AddToPatchedFunctions(
+ const wchar_t* dll_name, const char* function_name,
+ InterceptionType interception_type, const void* replacement_code_address,
+ InterceptorId id) {
+ InterceptionData function;
+ function.type = interception_type;
+ function.id = id;
+ function.dll = dll_name;
+ function.function = function_name;
+ function.interceptor_address = replacement_code_address;
+
+ interceptions_.push_back(function);
+ return true;
+}
+
+bool InterceptionManager::AddToPatchedFunctions(
+ const wchar_t* dll_name, const char* function_name,
+ InterceptionType interception_type, const char* replacement_function_name,
+ InterceptorId id) {
+ InterceptionData function;
+ function.type = interception_type;
+ function.id = id;
+ function.dll = dll_name;
+ function.function = function_name;
+ function.interceptor = replacement_function_name;
+ function.interceptor_address = NULL;
+
+ interceptions_.push_back(function);
+ names_used_ = true;
+ return true;
+}
+
+bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) {
+ InterceptionData module_to_unload;
+ module_to_unload.type = INTERCEPTION_UNLOAD_MODULE;
+ module_to_unload.dll = dll_name;
+ // The next two are dummy values that make the structures regular, instead
+ // of having special cases. They should not be used.
+ module_to_unload.function = kUnloadDLLDummyFunction;
+ module_to_unload.interceptor_address = reinterpret_cast<void*>(1);
+
+ interceptions_.push_back(module_to_unload);
+ return true;
+}
+
+bool InterceptionManager::InitializeInterceptions() {
+ if (interceptions_.empty())
+ return true; // Nothing to do here
+
+ size_t buffer_bytes = GetBufferSize();
+ scoped_ptr<char[]> local_buffer(new char[buffer_bytes]);
+
+ if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
+ return false;
+
+ void* remote_buffer;
+ if (!CopyDataToChild(local_buffer.get(), buffer_bytes, &remote_buffer))
+ return false;
+
+ bool hot_patch_needed = (0 != buffer_bytes);
+ if (!PatchNtdll(hot_patch_needed))
+ return false;
+
+ g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
+ ResultCode rc = child_->TransferVariable("g_interceptions",
+ &g_interceptions,
+ sizeof(g_interceptions));
+ return (SBOX_ALL_OK == rc);
+}
+
+size_t InterceptionManager::GetBufferSize() const {
+ std::set<std::wstring> dlls;
+ size_t buffer_bytes = 0;
+
+ std::list<InterceptionData>::const_iterator it = interceptions_.begin();
+ for (; it != interceptions_.end(); ++it) {
+ // skip interceptions that are performed from the parent
+ if (!IsInterceptionPerformedByChild(*it))
+ continue;
+
+ if (!dlls.count(it->dll)) {
+ // NULL terminate the dll name on the structure
+ size_t dll_name_bytes = (it->dll.size() + 1) * sizeof(wchar_t);
+
+ // include the dll related size
+ buffer_bytes += RoundUpToMultiple(offsetof(DllPatchInfo, dll_name) +
+ dll_name_bytes, sizeof(size_t));
+ dlls.insert(it->dll);
+ }
+
+ // we have to NULL terminate the strings on the structure
+ size_t strings_chars = it->function.size() + it->interceptor.size() + 2;
+
+ // a new FunctionInfo is required per function
+ size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
+ record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
+ buffer_bytes += record_bytes;
+ }
+
+ if (0 != buffer_bytes)
+ // add the part of SharedMemory that we have not counted yet
+ buffer_bytes += offsetof(SharedMemory, dll_list);
+
+ return buffer_bytes;
+}
+
+// Basically, walk the list of interceptions moving them to the config buffer,
+// but keeping together all interceptions that belong to the same dll.
+// The config buffer is a local buffer, not the one allocated on the child.
+bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
+ if (0 == buffer_bytes)
+ return true;
+
+ DCHECK(buffer_bytes > sizeof(SharedMemory));
+
+ SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
+ DllPatchInfo* dll_info = shared_memory->dll_list;
+ int num_dlls = 0;
+
+ shared_memory->interceptor_base = names_used_ ? child_->MainModule() : NULL;
+
+ buffer_bytes -= offsetof(SharedMemory, dll_list);
+ buffer = dll_info;
+
+ std::list<InterceptionData>::iterator it = interceptions_.begin();
+ for (; it != interceptions_.end();) {
+ // skip interceptions that are performed from the parent
+ if (!IsInterceptionPerformedByChild(*it)) {
+ ++it;
+ continue;
+ }
+
+ const std::wstring dll = it->dll;
+ if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
+ return false;
+
+ // walk the interceptions from this point, saving the ones that are
+ // performed on this dll, and removing the entry from the list.
+ // advance the iterator before removing the element from the list
+ std::list<InterceptionData>::iterator rest = it;
+ for (; rest != interceptions_.end();) {
+ if (rest->dll == dll) {
+ if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
+ return false;
+ if (it == rest)
+ ++it;
+ rest = interceptions_.erase(rest);
+ } else {
+ ++rest;
+ }
+ }
+ dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
+ ++num_dlls;
+ }
+
+ shared_memory->num_intercepted_dlls = num_dlls;
+ return true;
+}
+
+// Fills up just the part that depends on the dll, not the info that depends on
+// the actual interception.
+bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes) const {
+ DCHECK(buffer_bytes);
+ DCHECK(buffer);
+ DCHECK(*buffer);
+
+ DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);
+
+ // the strings have to be zero terminated
+ size_t required = offsetof(DllPatchInfo, dll_name) +
+ (data.dll.size() + 1) * sizeof(wchar_t);
+ required = RoundUpToMultiple(required, sizeof(size_t));
+ if (*buffer_bytes < required)
+ return false;
+
+ *buffer_bytes -= required;
+ *buffer = reinterpret_cast<char*>(*buffer) + required;
+
+ // set up the dll info to be what we know about it at this time
+ dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE);
+ dll_info->record_bytes = required;
+ dll_info->offset_to_functions = required;
+ dll_info->num_functions = 0;
+ data.dll._Copy_s(dll_info->dll_name, data.dll.size(), data.dll.size());
+ dll_info->dll_name[data.dll.size()] = L'\0';
+
+ return true;
+}
+
+bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes,
+ DllPatchInfo* dll_info) const {
+ DCHECK(buffer_bytes);
+ DCHECK(buffer);
+ DCHECK(*buffer);
+
+ if ((dll_info->unload_module) &&
+ (data.function != kUnloadDLLDummyFunction)) {
+ // Can't specify a dll for both patch and unload.
+ NOTREACHED();
+ }
+
+ FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);
+
+ size_t name_bytes = data.function.size();
+ size_t interceptor_bytes = data.interceptor.size();
+
+ // the strings at the end of the structure are zero terminated
+ size_t required = offsetof(FunctionInfo, function) +
+ name_bytes + interceptor_bytes + 2;
+ required = RoundUpToMultiple(required, sizeof(size_t));
+ if (*buffer_bytes < required)
+ return false;
+
+ // update the caller's values
+ *buffer_bytes -= required;
+ *buffer = reinterpret_cast<char*>(*buffer) + required;
+
+ function->record_bytes = required;
+ function->type = data.type;
+ function->id = data.id;
+ function->interceptor_address = data.interceptor_address;
+ char* names = function->function;
+
+ data.function._Copy_s(names, name_bytes, name_bytes);
+ names += name_bytes;
+ *names++ = '\0';
+
+ // interceptor follows the function_name
+ data.interceptor._Copy_s(names, interceptor_bytes, interceptor_bytes);
+ names += interceptor_bytes;
+ *names++ = '\0';
+
+ // update the dll table
+ dll_info->num_functions++;
+ dll_info->record_bytes += required;
+
+ return true;
+}
+
+bool InterceptionManager::CopyDataToChild(const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer) const {
+ DCHECK(NULL != remote_buffer);
+ if (0 == buffer_bytes) {
+ *remote_buffer = NULL;
+ return true;
+ }
+
+ HANDLE child = child_->Process();
+
+ // Allocate memory on the target process without specifying the address
+ void* remote_data = ::VirtualAllocEx(child, NULL, buffer_bytes,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (NULL == remote_data)
+ return false;
+
+ SIZE_T bytes_written;
+ BOOL success = ::WriteProcessMemory(child, remote_data, local_buffer,
+ buffer_bytes, &bytes_written);
+ if (FALSE == success || bytes_written != buffer_bytes) {
+ ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
+ return false;
+ }
+
+ *remote_buffer = remote_data;
+
+ return true;
+}
+
+// Only return true if the child should be able to perform this interception.
+bool InterceptionManager::IsInterceptionPerformedByChild(
+ const InterceptionData& data) const {
+ if (INTERCEPTION_INVALID == data.type)
+ return false;
+
+ if (INTERCEPTION_SERVICE_CALL == data.type)
+ return false;
+
+ if (data.type >= INTERCEPTION_LAST)
+ return false;
+
+ std::wstring ntdll(kNtdllName);
+ if (ntdll == data.dll)
+ return false; // ntdll has to be intercepted from the parent
+
+ return true;
+}
+
+bool InterceptionManager::PatchNtdll(bool hot_patch_needed) {
+ // Maybe there is nothing to do
+ if (!hot_patch_needed && interceptions_.empty())
+ return true;
+
+ if (hot_patch_needed) {
+#if SANDBOX_EXPORTS
+ // Make sure the functions are not excluded by the linker.
+#if defined(_WIN64)
+ #pragma comment(linker, "/include:TargetNtMapViewOfSection64")
+ #pragma comment(linker, "/include:TargetNtUnmapViewOfSection64")
+#else
+ #pragma comment(linker, "/include:_TargetNtMapViewOfSection@44")
+ #pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12")
+#endif
+#endif
+ ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44);
+ ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12);
+ }
+
+ // Reserve a full 64k memory range in the child process.
+ HANDLE child = child_->Process();
+ BYTE* thunk_base = reinterpret_cast<BYTE*>(
+ ::VirtualAllocEx(child, NULL, kAllocGranularity,
+ MEM_RESERVE, PAGE_NOACCESS));
+
+ // Find an aligned, random location within the reserved range.
+ size_t thunk_bytes = interceptions_.size() * sizeof(ThunkData) +
+ sizeof(DllInterceptionData);
+ size_t thunk_offset = GetGranularAlignedRandomOffset(thunk_bytes);
+
+ // Split the base and offset along page boundaries.
+ thunk_base += thunk_offset & ~(kPageSize - 1);
+ 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;
+ thunk_base = reinterpret_cast<BYTE*>(
+ ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded,
+ MEM_COMMIT, PAGE_EXECUTE_READWRITE));
+ CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access.
+ DllInterceptionData* thunks = reinterpret_cast<DllInterceptionData*>(
+ thunk_base + thunk_offset);
+
+ DllInterceptionData dll_data;
+ dll_data.data_bytes = thunk_bytes;
+ dll_data.num_thunks = 0;
+ dll_data.used_bytes = offsetof(DllInterceptionData, thunks);
+
+ // Reset all helpers for a new child.
+ memset(g_originals, 0, sizeof(g_originals));
+
+ // this should write all the individual thunks to the child's memory
+ if (!PatchClientFunctions(thunks, thunk_bytes, &dll_data))
+ return false;
+
+ // and now write the first part of the table to the child's memory
+ SIZE_T written;
+ bool ok = FALSE != ::WriteProcessMemory(child, thunks, &dll_data,
+ offsetof(DllInterceptionData, thunks),
+ &written);
+
+ if (!ok || (offsetof(DllInterceptionData, thunks) != written))
+ return false;
+
+ // Attempt to protect all the thunks, but ignore failure
+ DWORD old_protection;
+ ::VirtualProtectEx(child, thunks, thunk_bytes,
+ PAGE_EXECUTE_READ, &old_protection);
+
+ ResultCode ret = child_->TransferVariable("g_originals", g_originals,
+ sizeof(g_originals));
+ return (SBOX_ALL_OK == ret);
+}
+
+bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks,
+ size_t thunk_bytes,
+ DllInterceptionData* dll_data) {
+ DCHECK(NULL != thunks);
+ DCHECK(NULL != dll_data);
+
+ HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
+ if (!ntdll_base)
+ return false;
+
+ base::win::PEImage ntdll_image(ntdll_base);
+
+ // Bypass purify's interception.
+ wchar_t* loader_get = reinterpret_cast<wchar_t*>(
+ ntdll_image.GetProcAddress("LdrGetDllHandle"));
+ if (loader_get) {
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ loader_get, &ntdll_base))
+ return false;
+ }
+
+ if (base::win::GetVersion() <= base::win::VERSION_VISTA) {
+ Wow64 WowHelper(child_, ntdll_base);
+ if (!WowHelper.WaitForNtdll())
+ return false;
+ }
+
+ char* interceptor_base = NULL;
+
+#if SANDBOX_EXPORTS
+ interceptor_base = reinterpret_cast<char*>(child_->MainModule());
+ HMODULE local_interceptor = ::LoadLibrary(child_->Name());
+#endif
+
+ ServiceResolverThunk* thunk;
+#if defined(_WIN64)
+ thunk = new ServiceResolverThunk(child_->Process(), relaxed_);
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::VERSION_WIN8)
+ 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 {
+ thunk = new ServiceResolverThunk(child_->Process(), relaxed_);
+ }
+#endif
+
+ std::list<InterceptionData>::iterator it = interceptions_.begin();
+ for (; it != interceptions_.end(); ++it) {
+ const std::wstring ntdll(kNtdllName);
+ if (it->dll != ntdll)
+ break;
+
+ if (INTERCEPTION_SERVICE_CALL != it->type)
+ break;
+
+#if SANDBOX_EXPORTS
+ // We may be trying to patch by function name.
+ if (NULL == it->interceptor_address) {
+ const char* address;
+ NTSTATUS ret = thunk->ResolveInterceptor(local_interceptor,
+ it->interceptor.c_str(),
+ reinterpret_cast<const void**>(
+ &address));
+ if (!NT_SUCCESS(ret))
+ break;
+
+ // Translate the local address to an address on the child.
+ it->interceptor_address = interceptor_base + (address -
+ reinterpret_cast<char*>(local_interceptor));
+ }
+#endif
+ NTSTATUS ret = thunk->Setup(ntdll_base,
+ interceptor_base,
+ it->function.c_str(),
+ it->interceptor.c_str(),
+ it->interceptor_address,
+ &thunks->thunks[dll_data->num_thunks],
+ thunk_bytes - dll_data->used_bytes,
+ NULL);
+ if (!NT_SUCCESS(ret))
+ break;
+
+ DCHECK(!g_originals[it->id]);
+ g_originals[it->id] = &thunks->thunks[dll_data->num_thunks];
+
+ dll_data->num_thunks++;
+ dll_data->used_bytes += sizeof(ThunkData);
+ }
+
+ delete(thunk);
+
+#if SANDBOX_EXPORTS
+ if (NULL != local_interceptor)
+ ::FreeLibrary(local_interceptor);
+#endif
+
+ if (it != interceptions_.end())
+ return false;
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/interception.h b/chromium/sandbox/win/src/interception.h
new file mode 100644
index 00000000000..02fc592a067
--- /dev/null
+++ b/chromium/sandbox/win/src/interception.h
@@ -0,0 +1,272 @@
+// Copyright (c) 2011 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.
+
+// Defines InterceptionManager, the class in charge of setting up interceptions
+// for the sandboxed process. For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_H_
+#define SANDBOX_SRC_INTERCEPTION_H_
+
+#include <list>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+class TargetProcess;
+enum InterceptorId;
+
+// Internal structures used for communication between the broker and the target.
+struct DllPatchInfo;
+struct DllInterceptionData;
+
+// The InterceptionManager executes on the parent application, and it is in
+// charge of setting up the desired interceptions, and placing the Interception
+// Agent into the child application.
+//
+// The exposed API consists of two methods: AddToPatchedFunctions to set up a
+// particular interception, and InitializeInterceptions to actually go ahead and
+// perform all interceptions and transfer data to the child application.
+//
+// The typical usage is something like this:
+//
+// InterceptionManager interception_manager(child);
+// if (!interception_manager.AddToPatchedFunctions(
+// L"ntdll.dll", "NtCreateFile",
+// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1))
+// return false;
+//
+// if (!interception_manager.AddToPatchedFunctions(
+// L"kernel32.dll", "CreateDirectoryW",
+// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2))
+// return false;
+//
+// if (!interception_manager.InitializeInterceptions()) {
+// DWORD error = ::GetLastError();
+// return false;
+// }
+//
+// Any required syncronization must be performed outside this class. Also, it is
+// not possible to perform further interceptions after InitializeInterceptions
+// is called.
+//
+class InterceptionManager {
+ // The unit test will access private members.
+ // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
+ // do not work with sandbox tests.
+ FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1);
+ FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2);
+
+ public:
+ // An interception manager performs interceptions on a given child process.
+ // If we are allowed to intercept functions that have been patched by somebody
+ // else, relaxed should be set to true.
+ // Note: We increase the child's reference count internally.
+ InterceptionManager(TargetProcess* child_process, bool relaxed);
+ ~InterceptionManager();
+
+ // Patches function_name inside dll_name to point to replacement_code_address.
+ // function_name has to be an exported symbol of dll_name.
+ // Returns true on success.
+ //
+ // The new function should match the prototype and calling convention of the
+ // function to intercept except for one extra argument (the first one) that
+ // contains a pointer to the original function, to simplify the development
+ // of interceptors (for IA32). In x64, there is no extra argument to the
+ // interceptor, so the provided InterceptorId is used to keep a table of
+ // intercepted functions so that the interceptor can index that table to get
+ // the pointer that would have been the first argument (g_originals[id]).
+ //
+ // For example, to intercept NtClose, the following code could be used:
+ //
+ // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
+ // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose,
+ // IN HANDLE Handle) {
+ // // do something
+ // // call the original function
+ // return OriginalClose(Handle);
+ // }
+ //
+ // And in x64:
+ //
+ // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
+ // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) {
+ // // do something
+ // // call the original function
+ // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID];
+ // return OriginalClose(Handle);
+ // }
+ bool AddToPatchedFunctions(const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const void* replacement_code_address,
+ InterceptorId id);
+
+ // Patches function_name inside dll_name to point to
+ // replacement_function_name.
+ bool AddToPatchedFunctions(const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const char* replacement_function_name,
+ InterceptorId id);
+
+ // The interception agent will unload the dll with dll_name.
+ bool AddToUnloadModules(const wchar_t* dll_name);
+
+ // Initializes all interceptions on the client.
+ // Returns true on success.
+ //
+ // The child process must be created suspended, and cannot be resumed until
+ // after this method returns. In addition, no action should be performed on
+ // the child that may cause it to resume momentarily, such as injecting
+ // threads or APCs.
+ //
+ // This function must be called only once, after all interceptions have been
+ // set up using AddToPatchedFunctions.
+ bool InitializeInterceptions();
+
+ private:
+ // Used to store the interception information until the actual set-up.
+ struct InterceptionData {
+ InterceptionType type; // Interception type.
+ InterceptorId id; // Interceptor id.
+ std::wstring dll; // Name of dll to intercept.
+ std::string function; // Name of function to intercept.
+ std::string interceptor; // Name of interceptor function.
+ const void* interceptor_address; // Interceptor's entry point.
+ };
+
+ // Calculates the size of the required configuration buffer.
+ size_t GetBufferSize() const;
+
+ // Rounds up the size of a given buffer, considering alignment (padding).
+ // value is the current size of the buffer, and alignment is specified in
+ // bytes.
+ static inline size_t RoundUpToMultiple(size_t value, size_t alignment) {
+ return ((value + alignment -1) / alignment) * alignment;
+ }
+
+ // Sets up a given buffer with all the information that has to be transfered
+ // to the child.
+ // Returns true on success.
+ //
+ // The buffer size should be at least the value returned by GetBufferSize
+ bool SetupConfigBuffer(void* buffer, size_t buffer_bytes);
+
+ // Fills up the part of the transfer buffer that corresponds to information
+ // about one dll to patch.
+ // data is the first recorded interception for this dll.
+ // Returns true on success.
+ //
+ // On successful return, buffer will be advanced from it's current position
+ // to the point where the next block of configuration data should be written
+ // (the actual interception info), and the current size of the buffer will
+ // decrease to account the space used by this method.
+ bool SetupDllInfo(const InterceptionData& data,
+ void** buffer, size_t* buffer_bytes) const;
+
+ // Fills up the part of the transfer buffer that corresponds to a single
+ // function to patch.
+ // dll_info points to the dll being updated with the interception stored on
+ // data. The buffer pointer and remaining size are updated by this call.
+ // Returns true on success.
+ bool SetupInterceptionInfo(const InterceptionData& data, void** buffer,
+ size_t* buffer_bytes,
+ DllPatchInfo* dll_info) const;
+
+ // Returns true if this interception is to be performed by the child
+ // as opposed to from the parent.
+ bool IsInterceptionPerformedByChild(const InterceptionData& data) const;
+
+ // Allocates a buffer on the child's address space (returned on
+ // remote_buffer), and fills it with the contents of a local buffer.
+ // Returns true on success.
+ bool CopyDataToChild(const void* local_buffer, size_t buffer_bytes,
+ void** remote_buffer) const;
+
+ // Performs the cold patch (from the parent) of ntdll.
+ // Returns true on success.
+ //
+ // This method will insert additional interceptions to launch the interceptor
+ // agent on the child process, if there are additional interceptions to do.
+ bool PatchNtdll(bool hot_patch_needed);
+
+ // Peforms the actual interceptions on ntdll.
+ // thunks is the memory to store all the thunks for this dll (on the child),
+ // and dll_data is a local buffer to hold global dll interception info.
+ // Returns true on success.
+ bool PatchClientFunctions(DllInterceptionData* thunks,
+ size_t thunk_bytes,
+ DllInterceptionData* dll_data);
+
+ // The process to intercept.
+ TargetProcess* child_;
+ // Holds all interception info until the call to initialize (perform the
+ // actual patch).
+ std::list<InterceptionData> interceptions_;
+
+ // Keep track of patches added by name.
+ bool names_used_;
+
+ // true if we are allowed to patch already-patched functions.
+ bool relaxed_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
+};
+
+// This macro simply calls interception_manager.AddToPatchedFunctions with
+// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that
+// the interceptor is called "TargetXXX", where XXX is the name of the service.
+// Note that num_params is the number of bytes to pop out of the stack for
+// the exported interceptor, following the calling convention of a service call
+// (WINAPI = with the "C" underscore).
+#if SANDBOX_EXPORTS
+#if defined(_WIN64)
+#define MAKE_SERVICE_NAME(service, params) "Target" # service "64"
+#else
+#define MAKE_SERVICE_NAME(service, params) "_Target" # service "@" # params
+#endif
+
+#define ADD_NT_INTERCEPTION(service, id, num_params) \
+ AddToPatchedFunctions(kNtdllName, #service, \
+ sandbox::INTERCEPTION_SERVICE_CALL, \
+ MAKE_SERVICE_NAME(service, num_params), id)
+
+#define INTERCEPT_NT(manager, service, id, num_params) \
+ ((&Target##service) ? \
+ manager->ADD_NT_INTERCEPTION(service, id, num_params) : false)
+
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ ((&Target##function) ? \
+ manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
+ MAKE_SERVICE_NAME(function, num_params), \
+ id) : \
+ false)
+#else // SANDBOX_EXPORTS
+#if defined(_WIN64)
+#define MAKE_SERVICE_NAME(service) &Target##service##64
+#else
+#define MAKE_SERVICE_NAME(service) &Target##service
+#endif
+
+#define ADD_NT_INTERCEPTION(service, id, num_params) \
+ AddToPatchedFunctions(kNtdllName, #service, \
+ sandbox::INTERCEPTION_SERVICE_CALL, \
+ MAKE_SERVICE_NAME(service), id)
+
+#define INTERCEPT_NT(manager, service, id, num_params) \
+ manager->ADD_NT_INTERCEPTION(service, id, num_params)
+
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
+ MAKE_SERVICE_NAME(function), id)
+#endif // SANDBOX_EXPORTS
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_H_
diff --git a/chromium/sandbox/win/src/interception_agent.cc b/chromium/sandbox/win/src/interception_agent.cc
new file mode 100644
index 00000000000..b2a66c471f0
--- /dev/null
+++ b/chromium/sandbox/win/src/interception_agent.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2006-2010 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.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "sandbox/win/src/interception_agent.h"
+
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/eat_resolver.h"
+#include "sandbox/win/src/sidestep_resolver.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+// Returns true if target lies between base and base + range.
+bool IsWithinRange(const void* base, size_t range, const void* target) {
+ const char* end = reinterpret_cast<const char*>(base) + range;
+ return reinterpret_cast<const char*>(target) < end;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// The list of intercepted functions back-pointers.
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+// Memory buffer mapped from the parent, with the list of interceptions.
+SANDBOX_INTERCEPT SharedMemory* g_interceptions = NULL;
+
+InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
+ static InterceptionAgent* s_singleton = NULL;
+ if (!s_singleton) {
+ if (!g_interceptions)
+ return NULL;
+
+ size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
+ s_singleton = reinterpret_cast<InterceptionAgent*>(
+ new(NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]);
+
+ bool success = s_singleton->Init(g_interceptions);
+ if (!success) {
+ operator delete(s_singleton, NT_ALLOC);
+ s_singleton = NULL;
+ }
+ }
+ return s_singleton;
+}
+
+bool InterceptionAgent::Init(SharedMemory* shared_memory) {
+ interceptions_ = shared_memory;
+ for (int i = 0 ; i < shared_memory->num_intercepted_dlls; i++)
+ dlls_[i] = NULL;
+ return true;
+}
+
+bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info) {
+ UNICODE_STRING current_name;
+ current_name.Length = static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) *
+ sizeof(wchar_t));
+ current_name.MaximumLength = current_name.Length;
+ current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);
+
+ BOOLEAN case_insensitive = TRUE;
+ if (full_path &&
+ !g_nt.RtlCompareUnicodeString(&current_name, full_path, case_insensitive))
+ return true;
+
+ if (name &&
+ !g_nt.RtlCompareUnicodeString(&current_name, name, case_insensitive))
+ return true;
+
+ return false;
+}
+
+bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ void* base_address) {
+ DllPatchInfo* dll_info = interceptions_->dll_list;
+ int i = 0;
+ for (; i < interceptions_->num_intercepted_dlls; i++) {
+ if (DllMatch(full_path, name, dll_info))
+ break;
+
+ dll_info = reinterpret_cast<DllPatchInfo*>(
+ reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
+ }
+
+ // Return now if the dll is not in our list of interest.
+ if (i == interceptions_->num_intercepted_dlls)
+ return true;
+
+ // The dll must be unloaded.
+ if (dll_info->unload_module)
+ return false;
+
+ // Purify causes this condition to trigger.
+ if (dlls_[i])
+ return true;
+
+ size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
+ dll_info->num_functions * sizeof(ThunkData);
+ dlls_[i] = reinterpret_cast<DllInterceptionData*>(
+ new(NT_PAGE, base_address) char[buffer_bytes]);
+
+ DCHECK_NT(dlls_[i]);
+ if (!dlls_[i])
+ return true;
+
+ dlls_[i]->data_bytes = buffer_bytes;
+ dlls_[i]->num_thunks = 0;
+ dlls_[i]->base = base_address;
+ dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);
+
+ VERIFY(PatchDll(dll_info, dlls_[i]));
+
+ ULONG old_protect;
+ SIZE_T real_size = buffer_bytes;
+ void* to_protect = dlls_[i];
+ VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect,
+ &real_size, PAGE_EXECUTE_READ,
+ &old_protect));
+ return true;
+}
+
+void InterceptionAgent::OnDllUnload(void* base_address) {
+ for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) {
+ if (dlls_[i] && dlls_[i]->base == base_address) {
+ operator delete(dlls_[i], NT_PAGE);
+ dlls_[i] = NULL;
+ break;
+ }
+ }
+}
+
+// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
+// the timestamp of the patched dll, or modify the info on the prebinded dll.
+// the first approach messes matching of debug symbols, the second one is more
+// complicated.
+bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
+ DllInterceptionData* thunks) {
+ DCHECK_NT(NULL != thunks);
+ DCHECK_NT(NULL != dll_info);
+
+ const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);
+
+ for (int i = 0; i < dll_info->num_functions; i++) {
+ if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ ResolverThunk* resolver = GetResolver(function->type);
+ if (!resolver)
+ return false;
+
+ const char* interceptor = function->function +
+ g_nt.strlen(function->function) + 1;
+
+ if (!IsWithinRange(function, function->record_bytes, interceptor) ||
+ !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ NTSTATUS ret = resolver->Setup(thunks->base,
+ interceptions_->interceptor_base,
+ function->function,
+ interceptor,
+ function->interceptor_address,
+ &thunks->thunks[i],
+ sizeof(ThunkData),
+ NULL);
+ if (!NT_SUCCESS(ret)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ DCHECK_NT(!g_originals[function->id]);
+ g_originals[function->id] = &thunks->thunks[i];
+
+ thunks->num_thunks++;
+ thunks->used_bytes += sizeof(ThunkData);
+
+ function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(function) + function->record_bytes);
+ }
+
+ return true;
+}
+
+// This method is called from within the loader lock
+ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
+ static EatResolverThunk* eat_resolver = NULL;
+ static SidestepResolverThunk* sidestep_resolver = NULL;
+ static SmartSidestepResolverThunk* smart_sidestep_resolver = NULL;
+
+ if (!eat_resolver)
+ eat_resolver = new(NT_ALLOC) EatResolverThunk;
+
+#if !defined(_WIN64)
+ // Sidestep is not supported for x64.
+ if (!sidestep_resolver)
+ sidestep_resolver = new(NT_ALLOC) SidestepResolverThunk;
+
+ if (!smart_sidestep_resolver)
+ smart_sidestep_resolver = new(NT_ALLOC) SmartSidestepResolverThunk;
+#endif
+
+ switch (type) {
+ case INTERCEPTION_EAT:
+ return eat_resolver;
+ case INTERCEPTION_SIDESTEP:
+ return sidestep_resolver;
+ case INTERCEPTION_SMART_SIDESTEP:
+ return smart_sidestep_resolver;
+ default:
+ NOTREACHED_NT();
+ }
+
+ return NULL;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/interception_agent.h b/chromium/sandbox/win/src/interception_agent.h
new file mode 100644
index 00000000000..2762c611fd9
--- /dev/null
+++ b/chromium/sandbox/win/src/interception_agent.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2006-2008 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.
+
+// Defines InterceptionAgent, the class in charge of setting up interceptions
+// from the inside of the sandboxed process. For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_AGENT_H__
+#define SANDBOX_SRC_INTERCEPTION_AGENT_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Internal structures used for communication between the broker and the target.
+struct DllInterceptionData;
+struct SharedMemory;
+struct DllPatchInfo;
+
+class ResolverThunk;
+
+// The InterceptionAgent executes on the target application, and it is in charge
+// of setting up the desired interceptions or indicating what module needs to
+// be unloaded.
+//
+// The exposed API consists of three methods: GetInterceptionAgent to retrieve
+// the single class instance, OnDllLoad and OnDllUnload to process a dll being
+// loaded and unloaded respectively.
+//
+// This class assumes that it will get called for every dll being loaded,
+// starting with kernel32, so the singleton will be instantiated from within the
+// loader lock.
+class InterceptionAgent {
+ public:
+ // Returns the single InterceptionAgent object for this process.
+ static InterceptionAgent* GetInterceptionAgent();
+
+ // This method should be invoked whenever a new dll is loaded to perform the
+ // required patches. If the return value is false, this dll should not be
+ // allowed to load.
+ //
+ // full_path is the (optional) full name of the module being loaded and name
+ // is the internal module name. If full_path is provided, it will be used
+ // before the internal name to determine if we care about this dll.
+ bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+ void* base_address);
+
+ // Performs cleanup when a dll is unloaded.
+ void OnDllUnload(void* base_address);
+
+ private:
+ ~InterceptionAgent() {}
+
+ // Performs initialization of the singleton.
+ bool Init(SharedMemory* shared_memory);
+
+ // Returns true if we are interested on this dll. dll_info is an entry of the
+ // list of intercepted dlls.
+ bool DllMatch(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info);
+
+ // Performs the patching of the dll loaded at base_address.
+ // The patches to perform are described on dll_info, and thunks is the thunk
+ // storage for the whole dll.
+ // Returns true on success.
+ bool PatchDll(const DllPatchInfo* dll_info, DllInterceptionData* thunks);
+
+ // Returns a resolver for a given interception type.
+ ResolverThunk* GetResolver(InterceptionType type);
+
+ // Shared memory containing the list of functions to intercept.
+ SharedMemory* interceptions_;
+
+ // Array of thunk data buffers for the intercepted dlls. This object singleton
+ // is allocated with a placement new with enough space to hold the complete
+ // array of pointers, not just the first element.
+ DllInterceptionData* dlls_[1];
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptionAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_AGENT_H__
diff --git a/chromium/sandbox/win/src/interception_internal.h b/chromium/sandbox/win/src/interception_internal.h
new file mode 100644
index 00000000000..810478addb6
--- /dev/null
+++ b/chromium/sandbox/win/src/interception_internal.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2006-2010 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.
+
+// Defines InterceptionManager, the class in charge of setting up interceptions
+// for the sandboxed process. For more details see:
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
+#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
+
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+const int kMaxThunkDataBytes = 64;
+
+enum InterceptorId;
+
+// The following structures contain variable size fields at the end, and will be
+// used to transfer information between two processes. In order to guarantee
+// our ability to follow the chain of structures, the alignment should be fixed,
+// hence this pragma.
+#pragma pack(push, 4)
+
+// Structures for the shared memory that contains patching information
+// for the InterceptionAgent.
+// A single interception:
+struct FunctionInfo {
+ size_t record_bytes; // rounded to sizeof(size_t) bytes
+ InterceptionType type;
+ InterceptorId id;
+ const void* interceptor_address;
+ char function[1]; // placeholder for null terminated name
+ // char interceptor[] // followed by the interceptor function
+};
+
+// A single dll:
+struct DllPatchInfo {
+ size_t record_bytes; // rounded to sizeof(size_t) bytes
+ size_t offset_to_functions;
+ int num_functions;
+ bool unload_module;
+ wchar_t dll_name[1]; // placeholder for null terminated name
+ // FunctionInfo function_info[] // followed by the functions to intercept
+};
+
+// All interceptions:
+struct SharedMemory {
+ int num_intercepted_dlls;
+ void* interceptor_base;
+ DllPatchInfo dll_list[1]; // placeholder for the list of dlls
+};
+
+// Dummy single thunk:
+struct ThunkData {
+ char data[kMaxThunkDataBytes];
+};
+
+// In-memory representation of the interceptions for a given dll:
+struct DllInterceptionData {
+ size_t data_bytes;
+ size_t used_bytes;
+ void* base;
+ int num_thunks;
+#if defined(_WIN64)
+ int dummy; // Improve alignment.
+#endif
+ ThunkData thunks[1];
+};
+
+#pragma pack(pop)
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
diff --git a/chromium/sandbox/win/src/interception_unittest.cc b/chromium/sandbox/win/src/interception_unittest.cc
new file mode 100644
index 00000000000..0fc9b7cbe2e
--- /dev/null
+++ b/chromium/sandbox/win/src/interception_unittest.cc
@@ -0,0 +1,212 @@
+// Copyright (c) 2011 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 unit tests for InterceptionManager.
+// The tests require private information so the whole interception.cc file is
+// included from this file.
+
+#include <windows.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/target_process.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Walks the settings buffer, verifying that the values make sense and counting
+// objects.
+// Arguments:
+// buffer (in): the buffer to walk.
+// size (in): buffer size
+// num_dlls (out): count of the dlls on the buffer.
+// num_function (out): count of intercepted functions.
+// num_names (out): count of named interceptor functions.
+void WalkBuffer(void* buffer, size_t size, int* num_dlls, int* num_functions,
+ int* num_names) {
+ ASSERT_TRUE(NULL != buffer);
+ ASSERT_TRUE(NULL != num_functions);
+ ASSERT_TRUE(NULL != num_names);
+ *num_dlls = *num_functions = *num_names = 0;
+ SharedMemory *memory = reinterpret_cast<SharedMemory*>(buffer);
+
+ ASSERT_GT(size, sizeof(SharedMemory));
+ DllPatchInfo *dll = &memory->dll_list[0];
+
+ for (int i = 0; i < memory->num_intercepted_dlls; i++) {
+ ASSERT_NE(0u, wcslen(dll->dll_name));
+ ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t));
+ ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t));
+ ASSERT_NE(0, dll->num_functions);
+
+ FunctionInfo *function = reinterpret_cast<FunctionInfo*>(
+ reinterpret_cast<char*>(dll) + dll->offset_to_functions);
+
+ for (int j = 0; j < dll->num_functions; j++) {
+ ASSERT_EQ(0u, function->record_bytes % sizeof(size_t));
+
+ char* name = function->function;
+ size_t length = strlen(name);
+ ASSERT_NE(0u, length);
+ name += length + 1;
+
+ // look for overflows
+ ASSERT_GT(reinterpret_cast<char*>(buffer) + size, name + strlen(name));
+
+ // look for a named interceptor
+ if (strlen(name)) {
+ (*num_names)++;
+ EXPECT_TRUE(NULL == function->interceptor_address);
+ } else {
+ EXPECT_TRUE(NULL != function->interceptor_address);
+ }
+
+ (*num_functions)++;
+ function = reinterpret_cast<FunctionInfo*>(
+ reinterpret_cast<char*>(function) + function->record_bytes);
+ }
+
+ (*num_dlls)++;
+ dll = reinterpret_cast<DllPatchInfo*>(reinterpret_cast<char*>(dll) +
+ dll->record_bytes);
+ }
+}
+
+TEST(InterceptionManagerTest, BufferLayout1) {
+ wchar_t exe_name[MAX_PATH];
+ ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1));
+
+ TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(),
+ ::GetModuleHandle(exe_name));
+
+ InterceptionManager interceptions(target, true);
+
+ // Any pointer will do for a function pointer.
+ void* function = &interceptions;
+
+ // We don't care about the interceptor id.
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
+ INTERCEPTION_SMART_SIDESTEP, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "FindWindow",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateMutex",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+ INTERCEPTION_EAT, "replacement",
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtOpenFile",
+ INTERCEPTION_SIDESTEP, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"some.dll", "Superfn",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, "a", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_SIDESTEP, "ab", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, "abc", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "p",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"b.dll",
+ "TheIncredibleCallToSaveTheWorld",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "ARules",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+
+ // Verify that all interceptions were added
+ ASSERT_EQ(18, interceptions.interceptions_.size());
+
+ size_t buffer_size = interceptions.GetBufferSize();
+ scoped_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]);
+
+ ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(),
+ buffer_size));
+
+ // At this point, the interceptions should have been separated into two
+ // groups: one group with the local ("cold") interceptions, consisting of
+ // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and
+ // another group with the interceptions belonging to dlls that will be "hot"
+ // patched on the client. The second group lives on local_buffer, and the
+ // first group remains on the list of interceptions (inside the object
+ // "interceptions"). There are 3 local interceptions (of ntdll); the
+ // other 15 have to be sent to the child to be performed "hot".
+ EXPECT_EQ(3, interceptions.interceptions_.size());
+
+ int num_dlls, num_functions, num_names;
+ WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions,
+ &num_names);
+
+ // The 15 interceptions on the buffer (to the child) should be grouped on 6
+ // dlls. Only four interceptions are using an explicit name for the
+ // interceptor function.
+ EXPECT_EQ(6, num_dlls);
+ EXPECT_EQ(15, num_functions);
+ EXPECT_EQ(4, num_names);
+}
+
+TEST(InterceptionManagerTest, BufferLayout2) {
+ wchar_t exe_name[MAX_PATH];
+ ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1));
+
+ TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(),
+ ::GetModuleHandle(exe_name));
+
+ InterceptionManager interceptions(target, true);
+
+ // Any pointer will do for a function pointer.
+ void* function = &interceptions;
+ interceptions.AddToUnloadModules(L"some01.dll");
+ // We don't care about the interceptor id.
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_FILE_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx",
+ INTERCEPTION_EAT, function, OPEN_FILE_ID);
+ interceptions.AddToUnloadModules(L"some02.dll");
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
+ INTERCEPTION_SMART_SIDESTEP, function,
+ OPEN_FILE_ID);
+ // Verify that all interceptions were added
+ ASSERT_EQ(5, interceptions.interceptions_.size());
+
+ size_t buffer_size = interceptions.GetBufferSize();
+ scoped_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]);
+
+ ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(),
+ buffer_size));
+
+ // At this point, the interceptions should have been separated into two
+ // groups: one group with the local ("cold") interceptions, and another
+ // group with the interceptions belonging to dlls that will be "hot"
+ // patched on the client. The second group lives on local_buffer, and the
+ // first group remains on the list of interceptions, in this case just one.
+ EXPECT_EQ(1, interceptions.interceptions_.size());
+
+ int num_dlls, num_functions, num_names;
+ WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions,
+ &num_names);
+
+ EXPECT_EQ(3, num_dlls);
+ EXPECT_EQ(4, num_functions);
+ EXPECT_EQ(0, num_names);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/interceptors.h b/chromium/sandbox/win/src/interceptors.h
new file mode 100644
index 00000000000..43126d005ac
--- /dev/null
+++ b/chromium/sandbox/win/src/interceptors.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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_INTERCEPTORS_H_
+#define SANDBOX_SRC_INTERCEPTORS_H_
+
+#if defined(_WIN64)
+#include "sandbox/win/src/interceptors_64.h"
+#endif
+
+namespace sandbox {
+
+enum InterceptorId {
+ // Internal use:
+ MAP_VIEW_OF_SECTION_ID = 0,
+ UNMAP_VIEW_OF_SECTION_ID,
+ // Policy broker:
+ SET_INFORMATION_THREAD_ID,
+ OPEN_THREAD_TOKEN_ID,
+ OPEN_THREAD_TOKEN_EX_ID,
+ OPEN_TREAD_ID,
+ OPEN_PROCESS_ID,
+ OPEN_PROCESS_TOKEN_ID,
+ OPEN_PROCESS_TOKEN_EX_ID,
+ // Filesystem dispatcher:
+ CREATE_FILE_ID,
+ OPEN_FILE_ID,
+ QUERY_ATTRIB_FILE_ID,
+ QUERY_FULL_ATTRIB_FILE_ID,
+ SET_INFO_FILE_ID,
+ // Named pipe dispatcher:
+ CREATE_NAMED_PIPE_ID,
+ // Process-thread dispatcher:
+ CREATE_PROCESSW_ID,
+ CREATE_PROCESSA_ID,
+ // Registry dispatcher:
+ CREATE_KEY_ID,
+ OPEN_KEY_ID,
+ OPEN_KEY_EX_ID,
+ // Sync dispatcher:
+ CREATE_EVENT_ID,
+ OPEN_EVENT_ID,
+ // CSRSS bypasses for HandleCloser:
+ CREATE_THREAD_ID,
+ GET_USER_DEFAULT_LCID_ID,
+ INTERCEPTOR_MAX_ID
+};
+
+typedef void* OriginalFunctions[INTERCEPTOR_MAX_ID];
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTORS_H_
diff --git a/chromium/sandbox/win/src/interceptors_64.cc b/chromium/sandbox/win/src/interceptors_64.cc
new file mode 100644
index 00000000000..30843fdd889
--- /dev/null
+++ b/chromium/sandbox/win/src/interceptors_64.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2011 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/interceptors_64.h"
+
+#include "sandbox/win/src/interceptors.h"
+#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_thread_interception.h"
+#include "sandbox/win/src/registry_interception.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/target_interceptions.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+NTSTATUS WINAPI TargetNtMapViewOfSection64(
+ HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits,
+ SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size,
+ SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect) {
+ NtMapViewOfSectionFunction orig_fn = reinterpret_cast<
+ NtMapViewOfSectionFunction>(g_originals[MAP_VIEW_OF_SECTION_ID]);
+
+ return TargetNtMapViewOfSection(orig_fn, section, process, base, zero_bits,
+ commit_size, offset, view_size, inherit,
+ allocation_type, protect);
+}
+
+NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, PVOID base) {
+ NtUnmapViewOfSectionFunction orig_fn = reinterpret_cast<
+ NtUnmapViewOfSectionFunction>(g_originals[UNMAP_VIEW_OF_SECTION_ID]);
+ return TargetNtUnmapViewOfSection(orig_fn, process, base);
+}
+
+// -----------------------------------------------------------------------
+
+NTSTATUS WINAPI TargetNtSetInformationThread64(
+ HANDLE thread, NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information, ULONG thread_information_bytes) {
+ NtSetInformationThreadFunction orig_fn = reinterpret_cast<
+ NtSetInformationThreadFunction>(g_originals[SET_INFORMATION_THREAD_ID]);
+ return TargetNtSetInformationThread(orig_fn, thread, thread_info_class,
+ thread_information,
+ thread_information_bytes);
+}
+
+NTSTATUS WINAPI TargetNtOpenThreadToken64(
+ HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self,
+ PHANDLE token) {
+ NtOpenThreadTokenFunction orig_fn = reinterpret_cast<
+ NtOpenThreadTokenFunction>(g_originals[OPEN_THREAD_TOKEN_ID]);
+ return TargetNtOpenThreadToken(orig_fn, thread, desired_access, open_as_self,
+ token);
+}
+
+NTSTATUS WINAPI TargetNtOpenThreadTokenEx64(
+ HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self,
+ ULONG handle_attributes, PHANDLE token) {
+ NtOpenThreadTokenExFunction orig_fn = reinterpret_cast<
+ NtOpenThreadTokenExFunction>(g_originals[OPEN_THREAD_TOKEN_EX_ID]);
+ return TargetNtOpenThreadTokenEx(orig_fn, thread, desired_access,
+ 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(
+ PHANDLE file, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing,
+ ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length) {
+ NtCreateFileFunction orig_fn = reinterpret_cast<
+ NtCreateFileFunction>(g_originals[CREATE_FILE_ID]);
+ return TargetNtCreateFile(orig_fn, file, desired_access, object_attributes,
+ io_status, allocation_size, file_attributes,
+ sharing, disposition, options, ea_buffer,
+ ea_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile64(
+ PHANDLE file, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status,
+ ULONG sharing, ULONG options) {
+ NtOpenFileFunction orig_fn = reinterpret_cast<
+ NtOpenFileFunction>(g_originals[OPEN_FILE_ID]);
+ return TargetNtOpenFile(orig_fn, file, desired_access, object_attributes,
+ io_status, sharing, options);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ NtQueryAttributesFileFunction orig_fn = reinterpret_cast<
+ NtQueryAttributesFileFunction>(g_originals[QUERY_ATTRIB_FILE_ID]);
+ return TargetNtQueryAttributesFile(orig_fn, object_attributes,
+ file_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ NtQueryFullAttributesFileFunction orig_fn = reinterpret_cast<
+ NtQueryFullAttributesFileFunction>(
+ g_originals[QUERY_FULL_ATTRIB_FILE_ID]);
+ return TargetNtQueryFullAttributesFile(orig_fn, object_attributes,
+ file_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile64(
+ HANDLE file, PIO_STATUS_BLOCK io_status, PVOID file_information,
+ ULONG length, FILE_INFORMATION_CLASS file_information_class) {
+ NtSetInformationFileFunction orig_fn = reinterpret_cast<
+ NtSetInformationFileFunction>(g_originals[SET_INFO_FILE_ID]);
+ return TargetNtSetInformationFile(orig_fn, file, io_status, file_information,
+ length, file_information_class);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW64(
+ LPCWSTR pipe_name, DWORD open_mode, DWORD pipe_mode, DWORD max_instance,
+ DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ CreateNamedPipeWFunction orig_fn = reinterpret_cast<
+ CreateNamedPipeWFunction>(g_originals[CREATE_NAMED_PIPE_ID]);
+ return TargetCreateNamedPipeW(orig_fn, pipe_name, open_mode, pipe_mode,
+ max_instance, out_buffer_size, in_buffer_size,
+ default_timeout, security_attributes);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread64(
+ PHANDLE thread, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id) {
+ NtOpenThreadFunction orig_fn = reinterpret_cast<
+ NtOpenThreadFunction>(g_originals[OPEN_TREAD_ID]);
+ return TargetNtOpenThread(orig_fn, thread, desired_access, object_attributes,
+ client_id);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess64(
+ PHANDLE process, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id) {
+ NtOpenProcessFunction orig_fn = reinterpret_cast<
+ NtOpenProcessFunction>(g_originals[OPEN_PROCESS_ID]);
+ return TargetNtOpenProcess(orig_fn, process, desired_access,
+ object_attributes, client_id);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken64(
+ HANDLE process, ACCESS_MASK desired_access, PHANDLE token) {
+ NtOpenProcessTokenFunction orig_fn = reinterpret_cast<
+ NtOpenProcessTokenFunction>(g_originals[OPEN_PROCESS_TOKEN_ID]);
+ return TargetNtOpenProcessToken(orig_fn, process, desired_access, token);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx64(
+ HANDLE process, ACCESS_MASK desired_access, ULONG handle_attributes,
+ PHANDLE token) {
+ NtOpenProcessTokenExFunction orig_fn = reinterpret_cast<
+ NtOpenProcessTokenExFunction>(g_originals[OPEN_PROCESS_TOKEN_EX_ID]);
+ return TargetNtOpenProcessTokenEx(orig_fn, process, desired_access,
+ handle_attributes, token);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW64(
+ LPCWSTR application_name, LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ CreateProcessWFunction orig_fn = reinterpret_cast<
+ CreateProcessWFunction>(g_originals[CREATE_PROCESSW_ID]);
+ return TargetCreateProcessW(orig_fn, application_name, command_line,
+ process_attributes, thread_attributes,
+ inherit_handles, flags, environment,
+ current_directory, startup_info,
+ process_information);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA64(
+ LPCSTR application_name, LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ CreateProcessAFunction orig_fn = reinterpret_cast<
+ CreateProcessAFunction>(g_originals[CREATE_PROCESSA_ID]);
+ return TargetCreateProcessA(orig_fn, application_name, command_line,
+ process_attributes, thread_attributes,
+ inherit_handles, flags, environment,
+ current_directory, startup_info,
+ process_information);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey64(
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG title_index,
+ PUNICODE_STRING class_name, ULONG create_options, PULONG disposition) {
+ NtCreateKeyFunction orig_fn = reinterpret_cast<
+ NtCreateKeyFunction>(g_originals[CREATE_KEY_ID]);
+ return TargetNtCreateKey(orig_fn, key, desired_access, object_attributes,
+ title_index, class_name, create_options,
+ disposition);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey64(
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NtOpenKeyFunction orig_fn = reinterpret_cast<
+ NtOpenKeyFunction>(g_originals[OPEN_KEY_ID]);
+ return TargetNtOpenKey(orig_fn, key, desired_access, object_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64(
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG open_options) {
+ NtOpenKeyExFunction orig_fn = reinterpret_cast<
+ NtOpenKeyExFunction>(g_originals[OPEN_KEY_EX_ID]);
+ return TargetNtOpenKeyEx(orig_fn, key, desired_access, object_attributes,
+ open_options);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64(
+ LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset,
+ BOOL initial_state, LPCWSTR name) {
+ CreateEventWFunction orig_fn = reinterpret_cast<
+ CreateEventWFunction>(g_originals[CREATE_EVENT_ID]);
+ return TargetCreateEventW(orig_fn, security_attributes, manual_reset,
+ initial_state, name);
+}
+
+SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64(
+ ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name) {
+ OpenEventWFunction orig_fn = reinterpret_cast<
+ OpenEventWFunction>(g_originals[OPEN_EVENT_ID]);
+ return TargetOpenEventW(orig_fn, desired_access, inherit_handle, name);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/interceptors_64.h b/chromium/sandbox/win/src/interceptors_64.h
new file mode 100644
index 00000000000..87c1c507598
--- /dev/null
+++ b/chromium/sandbox/win/src/interceptors_64.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2011 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_INTERCEPTORS_64_H_
+#define SANDBOX_SRC_INTERCEPTORS_64_H_
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtMapViewOfSection64(
+ HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits,
+ SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size,
+ SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect);
+
+// Interception of NtUnmapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being unloaded, so we can clean up our interceptions.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process,
+ PVOID base);
+
+// -----------------------------------------------------------------------
+// Interceptors without IPC.
+
+// Interception of NtSetInformationThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread64(
+ HANDLE thread, NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information, ULONG thread_information_bytes);
+
+// Interception of NtOpenThreadToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken64(
+ HANDLE thread, ACCESS_MASK desired_access, BOOLEAN open_as_self,
+ PHANDLE token);
+
+// Interception of NtOpenThreadTokenEx on the child process.
+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.
+
+// Interception of NtCreateFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateFile64(
+ PHANDLE file, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size, ULONG file_attributes, ULONG sharing,
+ ULONG disposition, ULONG options, PVOID ea_buffer, ULONG ea_length);
+
+// Interception of NtOpenFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenFile64(
+ PHANDLE file, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PIO_STATUS_BLOCK io_status,
+ ULONG sharing, ULONG options);
+
+// Interception of NtQueryAtttributesFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes);
+
+// Interception of NtQueryFullAtttributesFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes);
+
+// Interception of NtSetInformationFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationFile64(
+ HANDLE file, PIO_STATUS_BLOCK io_status, PVOID file_information,
+ ULONG length, FILE_INFORMATION_CLASS file_information_class);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the named pipe dispatcher.
+
+// Interception of CreateNamedPipeW in kernel32.dll
+SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW64(
+ LPCWSTR pipe_name, DWORD open_mode, DWORD pipe_mode, DWORD max_instance,
+ DWORD out_buffer_size, DWORD in_buffer_size, DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the process-thread dispatcher.
+
+// Interception of NtOpenThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread64(
+ PHANDLE thread, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id);
+
+// Interception of NtOpenProcess on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess64(
+ PHANDLE process, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, PCLIENT_ID client_id);
+
+// Interception of NtOpenProcessToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken64(
+ HANDLE process, ACCESS_MASK desired_access, PHANDLE token);
+
+// Interception of NtOpenProcessTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx64(
+ HANDLE process, ACCESS_MASK desired_access, ULONG handle_attributes,
+ PHANDLE token);
+
+// Interception of CreateProcessW in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW64(
+ LPCWSTR application_name, LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateProcessA in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA64(
+ LPCSTR application_name, LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCSTR current_directory, LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the registry dispatcher.
+
+// Interception of NtCreateKey on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey64(
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG title_index,
+ PUNICODE_STRING class_name, ULONG create_options, PULONG disposition);
+
+// Interception of NtOpenKey on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey64(
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// Interception of NtOpenKeyEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx64(
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG open_options);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the sync dispatcher.
+
+// Interception of CreateEventW on the child process.
+SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW64(
+ LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset,
+ BOOL initial_state, LPCWSTR name);
+
+// Interception of OpenEventW on the child process.
+SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW64(
+ ACCESS_MASK desired_access, BOOL inherit_handle, LPCWSTR name);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTORS_64_H_
diff --git a/chromium/sandbox/win/src/internal_types.h b/chromium/sandbox/win/src/internal_types.h
new file mode 100644
index 00000000000..db969aadd59
--- /dev/null
+++ b/chromium/sandbox/win/src/internal_types.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_INTERNAL_TYPES_H_
+#define SANDBOX_SRC_INTERNAL_TYPES_H_
+
+namespace sandbox {
+
+const wchar_t kNtdllName[] = L"ntdll.dll";
+const wchar_t kKerneldllName[] = L"kernel32.dll";
+
+// Defines the supported C++ types encoding to numeric id. Like a poor's man
+// RTTI. Note that true C++ RTTI will not work because the types are not
+// polymorphic anyway.
+enum ArgType {
+ INVALID_TYPE = 0,
+ WCHAR_TYPE,
+ ULONG_TYPE,
+ UNISTR_TYPE,
+ VOIDPTR_TYPE,
+ INPTR_TYPE,
+ INOUTPTR_TYPE,
+ LAST_TYPE
+};
+
+// Encapsulates a pointer to a buffer and the size of the buffer.
+class CountedBuffer {
+ public:
+ CountedBuffer(void* buffer, uint32 size) : size_(size), buffer_(buffer) {}
+
+ uint32 Size() const {
+ return size_;
+ }
+
+ void* Buffer() const {
+ return buffer_;
+ }
+
+ private:
+ uint32 size_;
+ void* buffer_;
+};
+
+// Helper class to convert void-pointer packed ints for both
+// 32 and 64 bit builds. This construct is non-portable.
+class IPCInt {
+ public:
+ explicit IPCInt(void* buffer) {
+ buffer_.vp = buffer;
+ }
+
+ explicit IPCInt(unsigned __int32 i32) {
+ buffer_.vp = NULL;
+ buffer_.i32 = i32;
+ }
+
+ unsigned __int32 As32Bit() const {
+ return buffer_.i32;
+ }
+
+ void* AsVoidPtr() const {
+ return buffer_.vp;
+ }
+
+ private:
+ union U {
+ void* vp;
+ unsigned __int32 i32;
+ } buffer_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERNAL_TYPES_H_
diff --git a/chromium/sandbox/win/src/ipc_ping_test.cc b/chromium/sandbox/win/src/ipc_ping_test.cc
new file mode 100644
index 00000000000..64e3de6c543
--- /dev/null
+++ b/chromium/sandbox/win/src/ipc_ping_test.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+// Tests that the IPC is working by issuing a special IPC that is not exposed
+// in the public API.
+SBOX_TESTS_COMMAND int IPC_Ping(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED;
+
+ TargetServices* ts = SandboxFactory::GetTargetServices();
+ if (NULL == ts)
+ return SBOX_TEST_FAILED;
+
+ // Downcast because we have internal knowledge of the object returned.
+ TargetServicesBase* ts_base = reinterpret_cast<TargetServicesBase*>(ts);
+
+ int version = 0;
+ if (L'1' == argv[0][0])
+ version = 1;
+ else
+ version = 2;
+
+ if (!ts_base->TestIPCPing(version))
+ return SBOX_TEST_FAILED;
+
+ ::Sleep(1);
+ if (!ts_base->TestIPCPing(version))
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// The IPC ping test should work before and after the token drop.
+TEST(IPCTest, IPCPingTestSimple) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 1"));
+}
+
+TEST(IPCTest, IPCPingTestWithOutput) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/ipc_tags.h b/chromium/sandbox/win/src/ipc_tags.h
new file mode 100644
index 00000000000..4e3a8061d86
--- /dev/null
+++ b/chromium/sandbox/win/src/ipc_tags.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_IPC_TAGS_H__
+#define SANDBOX_SRC_IPC_TAGS_H__
+
+namespace sandbox {
+
+enum {
+ IPC_UNUSED_TAG = 0,
+ IPC_PING1_TAG, // Takes a cookie in parameters and returns the cookie
+ // multiplied by 2 and the tick_count. Used for testing only.
+ IPC_PING2_TAG, // Takes an in/out cookie in parameters and modify the cookie
+ // to be multiplied by 3. Used for testing only.
+ IPC_NTCREATEFILE_TAG,
+ IPC_NTOPENFILE_TAG,
+ IPC_NTQUERYATTRIBUTESFILE_TAG,
+ IPC_NTQUERYFULLATTRIBUTESFILE_TAG,
+ IPC_NTSETINFO_RENAME_TAG,
+ IPC_CREATENAMEDPIPEW_TAG,
+ IPC_NTOPENTHREAD_TAG,
+ IPC_NTOPENPROCESS_TAG,
+ IPC_NTOPENPROCESSTOKEN_TAG,
+ IPC_NTOPENPROCESSTOKENEX_TAG,
+ IPC_CREATEPROCESSW_TAG,
+ IPC_CREATEEVENT_TAG,
+ IPC_OPENEVENT_TAG,
+ IPC_NTCREATEKEY_TAG,
+ IPC_NTOPENKEY_TAG,
+ IPC_DUPLICATEHANDLEPROXY_TAG,
+ IPC_LAST_TAG
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_IPC_TAGS_H__
diff --git a/chromium/sandbox/win/src/ipc_unittest.cc b/chromium/sandbox/win/src/ipc_unittest.cc
new file mode 100644
index 00000000000..daca7bbe4f1
--- /dev/null
+++ b/chromium/sandbox/win/src/ipc_unittest.cc
@@ -0,0 +1,641 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Helper function to make the fake shared memory with some
+// basic elements initialized.
+IPCControl* MakeChannels(size_t channel_size, size_t total_shared_size,
+ size_t* base_start) {
+ // Allocate memory
+ char* mem = new char[total_shared_size];
+ memset(mem, 0, total_shared_size);
+ // Calculate how many channels we can fit in the shared memory.
+ total_shared_size -= offsetof(IPCControl, channels);
+ size_t channel_count =
+ total_shared_size / (sizeof(ChannelControl) + channel_size);
+ // Calculate the start of the first channel.
+ *base_start = (sizeof(ChannelControl)* channel_count) +
+ offsetof(IPCControl, channels);
+ // Setup client structure.
+ IPCControl* client_control = reinterpret_cast<IPCControl*>(mem);
+ client_control->channels_count = channel_count;
+ return client_control;
+}
+
+enum TestFixMode {
+ FIX_NO_EVENTS,
+ FIX_PONG_READY,
+ FIX_PONG_NOT_READY
+};
+
+void FixChannels(IPCControl* client_control, size_t base_start,
+ size_t channel_size, TestFixMode mode) {
+ for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
+ ChannelControl& channel = client_control->channels[ix];
+ channel.channel_base = base_start;
+ channel.state = kFreeChannel;
+ if (mode != FIX_NO_EVENTS) {
+ BOOL signaled = (FIX_PONG_READY == mode)? TRUE : FALSE;
+ channel.ping_event = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+ channel.pong_event = ::CreateEventW(NULL, FALSE, signaled, NULL);
+ }
+ base_start += channel_size;
+ }
+}
+
+void CloseChannelEvents(IPCControl* client_control) {
+ for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
+ ChannelControl& channel = client_control->channels[ix];
+ ::CloseHandle(channel.ping_event);
+ ::CloseHandle(channel.pong_event);
+ }
+}
+
+TEST(IPCTest, ChannelMaker) {
+ // Test that our testing rig is computing offsets properly. We should have
+ // 5 channnels and the offset to the first channel is 108 bytes in 32 bits
+ // and 216 in 64 bits.
+ size_t channel_start = 0;
+ IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start);
+ ASSERT_TRUE(NULL != client_control);
+ EXPECT_EQ(5, client_control->channels_count);
+#if defined(_WIN64)
+ EXPECT_EQ(216, channel_start);
+#else
+ EXPECT_EQ(108, channel_start);
+#endif
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, ClientLockUnlock) {
+ // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and
+ // unlock channels properly.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ // Test that we lock the first 3 channels in sequence.
+ void* buff0 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff1 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff2 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ // Test that we unlock and re-lock the right channel.
+ client.FreeBuffer(buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff2b = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ client.FreeBuffer(buff0);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallStrPacking) {
+ // This test tries the CrossCall object with null and non-null string
+ // combination of parameters, integer types and verifies that the unpacker
+ // can read them properly.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ uint32 tag1 = 666;
+ const wchar_t text[] = L"98765 - 43210";
+ std::wstring copied_text;
+ CrossCallParamsEx* actual_params;
+
+ CrossCall(client, tag1, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1, actual_params->GetParamsCount());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_STREQ(text, copied_text.c_str());
+
+ // Check with an empty string.
+ uint32 tag2 = 777;
+ const wchar_t* null_text = NULL;
+ CrossCall(client, tag2, null_text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ uint32 param_size = 1;
+ ArgType type = INVALID_TYPE;
+ void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ EXPECT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+
+ uint32 tag3 = 888;
+ param_size = 1;
+ copied_text.clear();
+
+ // Check with an empty string and a non-empty string.
+ CrossCall(client, tag3, null_text, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(2, actual_params->GetParamsCount());
+ EXPECT_EQ(tag3, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ EXPECT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text));
+ EXPECT_STREQ(text, copied_text.c_str());
+
+ param_size = 1;
+ std::wstring copied_text_p0, copied_text_p2;
+
+ 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());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0));
+ EXPECT_STREQ(text2, copied_text_p0.c_str());
+ EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2));
+ EXPECT_STREQ(text, copied_text_p2.c_str());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ EXPECT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallIntPacking) {
+ // Check handling for regular 32 bit integers used in Windows.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ uint32 tag1 = 999;
+ uint32 tag2 = 111;
+ const wchar_t text[] = L"godzilla";
+ CrossCallParamsEx* actual_params;
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ DWORD dw = 0xE6578;
+ CrossCall(client, tag2, dw, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ ArgType type = INVALID_TYPE;
+ uint32 param_size = 1;
+ void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ ASSERT_EQ(sizeof(dw), param_size);
+ EXPECT_EQ(ULONG_TYPE, type);
+ ASSERT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
+
+ // Check handling for windows HANDLES.
+ HANDLE h = HANDLE(0x70000500);
+ CrossCall(client, tag1, text, h, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(2, actual_params->GetParamsCount());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+
+ // Check combination of 32 and 64 bits.
+ CrossCall(client, tag2, h, dw, h, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(3, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ ASSERT_EQ(sizeof(dw), param_size);
+ EXPECT_EQ(ULONG_TYPE, type);
+ ASSERT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(2, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(NULL != param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallValidation) {
+ // First a sanity test with a well formed parameter object.
+ unsigned long value = 124816;
+ const uint32 kTag = 33;
+ const uint32 kBufferSize = 256;
+ ActualCallParams<1, kBufferSize> params_1(kTag);
+ params_1.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
+ void* buffer = const_cast<void*>(params_1.GetBuffer());
+
+ uint32 out_size = 0;
+ CrossCallParamsEx* ccp = 0;
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
+ &out_size);
+ ASSERT_TRUE(NULL != ccp);
+ EXPECT_TRUE(ccp->GetBuffer() != buffer);
+ EXPECT_EQ(kTag, ccp->GetTag());
+ EXPECT_EQ(1, ccp->GetParamsCount());
+ delete[] (reinterpret_cast<char*>(ccp));
+
+#if defined(NDEBUG)
+ // Test hat 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);
+ for (int32 ix = -1; ix != 3; ++ix) {
+ uint32 fake_num_params = (kuint32max / kPtrDiffSz) + ix;
+ ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params);
+ params_2.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
+ buffer = const_cast<void*>(params_2.GetBuffer());
+
+ EXPECT_TRUE(NULL != buffer);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
+ &out_size);
+ // 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);
+ buffer = const_cast<void*>(params_3.GetBuffer());
+ EXPECT_TRUE(NULL != buffer);
+
+ uint32 correct_size = params_3.OverrideSize(1);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(NULL == ccp);
+
+ // The correct_size is 8 bytes aligned.
+ params_3.OverrideSize(correct_size - 7);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(NULL == ccp);
+
+ params_3.OverrideSize(correct_size);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(NULL != ccp);
+
+ // Make sure that two parameters work as expected.
+ ActualCallParams<2, kBufferSize> params_4(kTag, 2);
+ params_4.CopyParamIn(0, &value, sizeof(value), false, ULONG_TYPE);
+ params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE);
+ buffer = const_cast<void*>(params_4.GetBuffer());
+ EXPECT_TRUE(NULL != buffer);
+
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(NULL != ccp);
+
+#if defined(_WIN64)
+ correct_size = params_4.OverrideSize(1);
+ params_4.OverrideSize(correct_size - 1);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(NULL == ccp);
+#endif
+}
+
+// This structure is passed to the mock server threads to simulate
+// the server side IPC so it has the required kernel objects.
+struct ServerEvents {
+ HANDLE ping;
+ HANDLE pong;
+ volatile LONG* state;
+ HANDLE mutex;
+};
+
+// This is the server thread that quicky answers an IPC and exits.
+DWORD WINAPI QuickResponseServer(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->ping, INFINITE);
+ ::InterlockedExchange(events->state, kAckChannel);
+ ::SetEvent(events->pong);
+ return wait_result;
+}
+
+class CrossCallParamsMock : public CrossCallParams {
+ public:
+ CrossCallParamsMock(uint32 tag, uint32 params_count)
+ : CrossCallParams(tag, params_count) {
+ }
+ private:
+ void* params[4];
+};
+
+void FakeOkAnswerInChannel(void* channel) {
+ CrossCallReturn* answer = reinterpret_cast<CrossCallReturn*>(channel);
+ answer->call_outcome = SBOX_ALL_OK;
+}
+
+// Create two threads that will quickly answer IPCs; the first one
+// using channel 1 (channel 0 is busy) and one using channel 0. No time-out
+// should occur.
+TEST(IPCTest, ClientFastServer) {
+ const size_t channel_size = kIPCChannelSize;
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(channel_size, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
+ client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ ServerEvents events = {0};
+ events.ping = client_control->channels[1].ping_event;
+ events.pong = client_control->channels[1].pong_event;
+ events.state = &client_control->channels[1].state;
+
+ HANDLE t1 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL);
+ ASSERT_TRUE(NULL != t1);
+ ::CloseHandle(t1);
+
+ void* buff0 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ void* buff1 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ EXPECT_EQ(0, client_control->channels[1].ipc_tag);
+
+ uint32 tag = 7654;
+ CrossCallReturn answer;
+ CrossCallParamsMock* params1 = new(buff1) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff1);
+
+ ResultCode result = client.DoCall(params1, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff1);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[1].ipc_tag);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ HANDLE t2 = ::CreateThread(NULL, 0, QuickResponseServer, &events, 0, NULL);
+ ASSERT_TRUE(NULL != t2);
+ ::CloseHandle(t2);
+
+ client.FreeBuffer(buff0);
+ events.ping = client_control->channels[0].ping_event;
+ events.pong = client_control->channels[0].pong_event;
+ events.state = &client_control->channels[0].state;
+
+ tag = 4567;
+ CrossCallParamsMock* params2 = new(buff0) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff0);
+
+ result = client.DoCall(params2, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff0);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ CloseChannelEvents(client_control);
+ ::CloseHandle(client_control->server_alive);
+
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+// This is the server thread that very slowly answers an IPC and exits. Note
+// that the pong event needs to be signaled twice.
+DWORD WINAPI SlowResponseServer(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->ping, INFINITE);
+ ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200);
+ ::InterlockedExchange(events->state, kAckChannel);
+ ::SetEvent(events->pong);
+ return wait_result;
+}
+
+// This thread's job is to keep the mutex locked.
+DWORD WINAPI MainServerThread(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->mutex, INFINITE);
+ Sleep(kIPCWaitTimeOut1 * 20);
+ return wait_result;
+}
+
+// Creates a server thread that answers the IPC so slow that is guaranteed to
+// trigger the time-out code path in the client. A second thread is created
+// to hold locked the server_alive mutex: this signals the client that the
+// server is not dead and it retries the wait.
+TEST(IPCTest, ClientSlowServer) {
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096*2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
+ client_control->server_alive = ::CreateMutex(NULL, FALSE, NULL);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ ServerEvents events = {0};
+ events.ping = client_control->channels[0].ping_event;
+ events.pong = client_control->channels[0].pong_event;
+ events.state = &client_control->channels[0].state;
+
+ HANDLE t1 = ::CreateThread(NULL, 0, SlowResponseServer, &events, 0, NULL);
+ ASSERT_TRUE(NULL != t1);
+ ::CloseHandle(t1);
+
+ ServerEvents events2 = {0};
+ events2.pong = events.pong;
+ events2.mutex = client_control->server_alive;
+
+ HANDLE t2 = ::CreateThread(NULL, 0, MainServerThread, &events2, 0, NULL);
+ ASSERT_TRUE(NULL != t2);
+ ::CloseHandle(t2);
+
+ ::Sleep(1);
+
+ void* buff0 = client.GetBuffer();
+ uint32 tag = 4321;
+ CrossCallReturn answer;
+ CrossCallParamsMock* params1 = new(buff0) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff0);
+
+ ResultCode result = client.DoCall(params1, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff0);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+
+ CloseChannelEvents(client_control);
+ ::CloseHandle(client_control->server_alive);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+// This test-only IPC dispatcher has two handlers with the same signature
+// but only CallOneHandler should be used.
+class UnitTestIPCDispatcher : public Dispatcher {
+ public:
+ enum {
+ CALL_ONE_TAG = 78,
+ CALL_TWO_TAG = 87
+ };
+
+ UnitTestIPCDispatcher();
+ ~UnitTestIPCDispatcher() {};
+
+ virtual bool SetupService(InterceptionManager* manager, int service) {
+ return true;
+ }
+
+ private:
+ bool CallOneHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) {
+ ipc->return_info.extended[0].handle = p1;
+ ipc->return_info.extended[1].unsigned_int = p2;
+ return true;
+ }
+
+ bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, DWORD p2) {
+ return true;
+ }
+};
+
+UnitTestIPCDispatcher::UnitTestIPCDispatcher() {
+ static const IPCCall call_one = {
+ {CALL_ONE_TAG, VOIDPTR_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallOneHandler)
+ };
+ static const IPCCall call_two = {
+ {CALL_TWO_TAG, VOIDPTR_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallTwoHandler)
+ };
+ ipc_calls_.push_back(call_one);
+ ipc_calls_.push_back(call_two);
+}
+
+// This test does most of the shared memory IPC client-server roundtrip
+// and tests the packing, unpacking and call dispatching.
+TEST(IPCTest, SharedMemServerTests) {
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ HANDLE bar = HANDLE(191919);
+ DWORD foo = 6767676;
+ CrossCall(client, UnitTestIPCDispatcher::CALL_ONE_TAG, bar, foo, &answer);
+ void* buff = client.GetBuffer();
+ ASSERT_TRUE(NULL != buff);
+
+ UnitTestIPCDispatcher dispatcher;
+ // Since we are directly calling InvokeCallback, most of this structure
+ // can be set to NULL.
+ sandbox::SharedMemIPCServer::ServerControl srv_control = {
+ NULL, NULL, kIPCChannelSize, NULL,
+ reinterpret_cast<char*>(client_control),
+ NULL, &dispatcher, {0} };
+
+ sandbox::CrossCallReturn call_return = {0};
+ EXPECT_TRUE(SharedMemIPCServer::InvokeCallback(&srv_control, buff,
+ &call_return));
+ EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome);
+ EXPECT_TRUE(bar == call_return.extended[0].handle);
+ EXPECT_EQ(foo, call_return.extended[1].unsigned_int);
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/job.cc b/chromium/sandbox/win/src/job.cc
new file mode 100644
index 00000000000..060ffa52438
--- /dev/null
+++ b/chromium/sandbox/win/src/job.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 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/job.h"
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/restricted_token.h"
+
+namespace sandbox {
+
+Job::~Job() {
+ if (job_handle_)
+ ::CloseHandle(job_handle_);
+};
+
+DWORD Job::Init(JobLevel security_level, wchar_t *job_name,
+ DWORD ui_exceptions) {
+ if (job_handle_)
+ return ERROR_ALREADY_INITIALIZED;
+
+ job_handle_ = ::CreateJobObject(NULL, // No security attribute
+ job_name);
+ if (!job_handle_)
+ return ::GetLastError();
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+
+ // Set the settings for the different security levels. Note: The higher levels
+ // inherit from the lower levels.
+ switch (security_level) {
+ case JOB_LOCKDOWN: {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
+ }
+ case JOB_RESTRICTED: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS;
+ }
+ case JOB_LIMITED_USER: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS;
+ jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
+ jeli.BasicLimitInformation.ActiveProcessLimit = 1;
+ }
+ case JOB_INTERACTIVE: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP;
+ 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;
+
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ break;
+ }
+ default: {
+ return ERROR_BAD_ARGUMENTS;
+ }
+ }
+
+ if (FALSE == ::SetInformationJobObject(job_handle_,
+ JobObjectExtendedLimitInformation,
+ &jeli,
+ sizeof(jeli))) {
+ return ::GetLastError();
+ }
+
+ jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions);
+ if (FALSE == ::SetInformationJobObject(job_handle_,
+ JobObjectBasicUIRestrictions,
+ &jbur,
+ sizeof(jbur))) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD Job::UserHandleGrantAccess(HANDLE handle) {
+ if (!job_handle_)
+ return ERROR_NO_DATA;
+
+ if (!::UserHandleGrantAccess(handle,
+ job_handle_,
+ TRUE)) { // Access allowed.
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+HANDLE Job::Detach() {
+ HANDLE handle_temp = job_handle_;
+ job_handle_ = NULL;
+ return handle_temp;
+}
+
+DWORD Job::AssignProcessToJob(HANDLE process_handle) {
+ if (!job_handle_)
+ return ERROR_NO_DATA;
+
+ if (FALSE == ::AssignProcessToJobObject(job_handle_, process_handle))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/job.h b/chromium/sandbox/win/src/job.h
new file mode 100644
index 00000000000..487722f0bb2
--- /dev/null
+++ b/chromium/sandbox/win/src/job.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2010 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_JOB_H_
+#define SANDBOX_SRC_JOB_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+
+namespace sandbox {
+
+// Handles the creation of job objects based on a security profile.
+// Sample usage:
+// Job job;
+// job.Init(JOB_LOCKDOWN, NULL); //no job name
+// job.AssignProcessToJob(process_handle);
+class Job {
+ public:
+ Job() : job_handle_(NULL) { }
+
+ ~Job();
+
+ // Initializes and creates the job object. The security of the job is based
+ // on the security_level parameter.
+ // job_name can be NULL if the job is unnamed.
+ // If the chosen profile has too many ui restrictions, you can disable some
+ // by specifying them in the ui_exceptions parameters.
+ // 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);
+
+ // Assigns the process referenced by process_handle to the 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 AssignProcessToJob(HANDLE process_handle);
+
+ // Grants access to "handle" to the job. All processes in the job can
+ // subsequently recognize and use the handle.
+ // 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 UserHandleGrantAccess(HANDLE handle);
+
+ // Revokes ownership to the job handle and returns it. The destructor of the
+ // class won't close the handle when called.
+ // If the object is not yet initialized, it returns 0.
+ HANDLE Detach();
+
+ private:
+ // Handle to the job referenced by the object.
+ HANDLE job_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_JOB_H_
diff --git a/chromium/sandbox/win/src/job_unittest.cc b/chromium/sandbox/win/src/job_unittest.cc
new file mode 100644
index 00000000000..597c86c1ef3
--- /dev/null
+++ b/chromium/sandbox/win/src/job_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the job object.
+
+#include "base/win/scoped_process_information.h"
+#include "sandbox/win/src/job.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests the creation and destruction of the job.
+TEST(JobTest, TestCreation) {
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
+
+ // check if the job exists.
+ HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE,
+ L"my_test_job_name");
+ ASSERT_TRUE(job_handle != NULL);
+
+ if (job_handle)
+ CloseHandle(job_handle);
+ }
+
+ // Check if the job is destroyed when the object goes out of scope.
+ HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name");
+ ASSERT_TRUE(job_handle == NULL);
+ ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
+}
+
+// Tests the method "Detach".
+TEST(JobTest, TestDetach) {
+ HANDLE job_handle;
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
+
+ job_handle = job.Detach();
+ ASSERT_TRUE(job_handle != NULL);
+ }
+
+ // Check to be sure that the job is still alive even after the object is gone
+ // out of scope.
+ HANDLE job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, FALSE,
+ L"my_test_job_name");
+ ASSERT_TRUE(job_handle_dup != NULL);
+
+ // Remove all references.
+ if (job_handle_dup)
+ ::CloseHandle(job_handle_dup);
+
+ if (job_handle)
+ ::CloseHandle(job_handle);
+
+ // Check if the jbo is really dead.
+ job_handle = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name");
+ ASSERT_TRUE(job_handle == NULL);
+ ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
+}
+
+// Tests the ui exceptions
+TEST(JobTest, TestExceptions) {
+ HANDLE job_handle;
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name",
+ JOB_OBJECT_UILIMIT_READCLIPBOARD));
+
+ job_handle = job.Detach();
+ ASSERT_TRUE(job_handle != NULL);
+
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+ DWORD size = sizeof(jbur);
+ BOOL result = ::QueryInformationJobObject(job_handle,
+ JobObjectBasicUIRestrictions,
+ &jbur, size, &size);
+ ASSERT_TRUE(result);
+
+ ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, 0);
+ ::CloseHandle(job_handle);
+ }
+
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0));
+
+ job_handle = job.Detach();
+ ASSERT_TRUE(job_handle != NULL);
+
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+ DWORD size = sizeof(jbur);
+ BOOL result = ::QueryInformationJobObject(job_handle,
+ JobObjectBasicUIRestrictions,
+ &jbur, size, &size);
+ ASSERT_TRUE(result);
+
+ ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD,
+ JOB_OBJECT_UILIMIT_READCLIPBOARD);
+ ::CloseHandle(job_handle);
+ }
+}
+
+// Tests the error case when the job is initialized twice.
+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));
+}
+
+// Tests the error case when we use a method and the object is not yet
+// initialized.
+TEST(JobTest, NoInit) {
+ Job job;
+ ASSERT_EQ(ERROR_NO_DATA, job.UserHandleGrantAccess(NULL));
+ ASSERT_EQ(ERROR_NO_DATA, job.AssignProcessToJob(NULL));
+ ASSERT_TRUE(job.Detach() == NULL);
+}
+
+// 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));
+
+ Job job2;
+ ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0));
+
+ Job job3;
+ ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0));
+
+ Job job4;
+ ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0));
+
+ Job job5;
+ ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 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));
+
+ Job job7;
+ ASSERT_EQ(ERROR_BAD_ARGUMENTS, job7.Init(
+ static_cast<JobLevel>(JOB_NONE+1), L"job7", 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));
+
+ BOOL result = FALSE;
+
+ wchar_t notepad[] = L"notepad";
+ STARTUPINFO si = { sizeof(si) };
+ base::win::ScopedProcessInformation pi;
+ result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si,
+ pi.Receive());
+ ASSERT_TRUE(result);
+ ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle()));
+
+ // Get the job handle.
+ HANDLE job_handle = job.Detach();
+
+ // Check if the process is in the job.
+ JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0};
+ DWORD size = sizeof(jbpidl);
+ result = ::QueryInformationJobObject(job_handle,
+ JobObjectBasicProcessIdList,
+ &jbpidl, size, &size);
+ EXPECT_TRUE(result);
+
+ EXPECT_EQ(1, jbpidl.NumberOfAssignedProcesses);
+ EXPECT_EQ(1, jbpidl.NumberOfProcessIdsInList);
+ EXPECT_EQ(pi.process_id(), jbpidl.ProcessIdList[0]);
+
+ EXPECT_TRUE(::TerminateProcess(pi.process_handle(), 0));
+
+ EXPECT_TRUE(::CloseHandle(job_handle));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/named_pipe_dispatcher.cc b/chromium/sandbox/win/src/named_pipe_dispatcher.cc
new file mode 100644
index 00000000000..aae1096b27d
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_dispatcher.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_dispatcher.h"
+
+#include "base/basictypes.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/named_pipe_interception.h"
+#include "sandbox/win/src/named_pipe_policy.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+
+namespace sandbox {
+
+NamedPipeDispatcher::NamedPipeDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IPC_CREATENAMEDPIPEW_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE, ULONG_TYPE,
+ ULONG_TYPE, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&NamedPipeDispatcher::CreateNamedPipe)
+ };
+
+ ipc_calls_.push_back(create_params);
+}
+
+bool NamedPipeDispatcher::SetupService(InterceptionManager* manager,
+ int service) {
+ if (IPC_CREATENAMEDPIPEW_TAG == service)
+ return INTERCEPT_EAT(manager, L"kernel32.dll", CreateNamedPipeW,
+ CREATE_NAMED_PIPE_ID, 36);
+
+ return false;
+}
+
+bool NamedPipeDispatcher::CreateNamedPipe(
+ IPCInfo* ipc, std::wstring* name, DWORD open_mode, DWORD pipe_mode,
+ DWORD max_instances, DWORD out_buffer_size, DWORD in_buffer_size,
+ DWORD default_timeout) {
+ const wchar_t* pipe_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ EvalResult eval = policy_base_->EvalPolicy(IPC_CREATENAMEDPIPEW_TAG,
+ params.GetBase());
+
+ HANDLE pipe;
+ DWORD ret = NamedPipePolicy::CreateNamedPipeAction(eval, *ipc->client_info,
+ *name, open_mode,
+ pipe_mode, max_instances,
+ out_buffer_size,
+ in_buffer_size,
+ default_timeout, &pipe);
+
+ ipc->return_info.win32_result = ret;
+ ipc->return_info.handle = pipe;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/named_pipe_dispatcher.h b/chromium/sandbox/win/src/named_pipe_dispatcher.h
new file mode 100644
index 00000000000..0d03b2e9db5
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2010 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_NAMED_PIPE_DISPATCHER_H__
+#define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles named pipe related IPC calls.
+class NamedPipeDispatcher : public Dispatcher {
+ public:
+ explicit NamedPipeDispatcher(PolicyBase* policy_base);
+ ~NamedPipeDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+ private:
+ // Processes IPC requests coming from calls to CreateNamedPipeW() in the
+ // target.
+ bool CreateNamedPipe(IPCInfo* ipc, std::wstring* name, DWORD open_mode,
+ DWORD pipe_mode, DWORD max_instances,
+ DWORD out_buffer_size, DWORD in_buffer_size,
+ DWORD default_timeout);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(NamedPipeDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
diff --git a/chromium/sandbox/win/src/named_pipe_interception.cc b/chromium/sandbox/win/src/named_pipe_interception.cc
new file mode 100644
index 00000000000..f437b1c2e85
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_interception.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+HANDLE WINAPI TargetCreateNamedPipeW(
+ CreateNamedPipeWFunction orig_CreateNamedPipeW, LPCWSTR pipe_name,
+ DWORD open_mode, DWORD pipe_mode, DWORD max_instance, DWORD out_buffer_size,
+ DWORD in_buffer_size, DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = orig_CreateNamedPipeW(pipe_name, open_mode, pipe_mode,
+ max_instance, out_buffer_size,
+ in_buffer_size, default_timeout,
+ security_attributes);
+ if (INVALID_HANDLE_VALUE != pipe)
+ return pipe;
+
+ DWORD original_error = ::GetLastError();
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return INVALID_HANDLE_VALUE;
+
+ // We don't support specific Security Attributes.
+ if (security_attributes)
+ return INVALID_HANDLE_VALUE;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ if (!QueryBroker(IPC_CREATENAMEDPIPEW_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_CREATENAMEDPIPEW_TAG, pipe_name,
+ open_mode, pipe_mode, max_instance,
+ out_buffer_size, in_buffer_size,
+ default_timeout, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+
+ if (ERROR_SUCCESS != answer.win32_result)
+ return INVALID_HANDLE_VALUE;
+
+ return answer.handle;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return INVALID_HANDLE_VALUE;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/named_pipe_interception.h b/chromium/sandbox/win/src/named_pipe_interception.h
new file mode 100644
index 00000000000..fdbee19766c
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_interception.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2006-2008 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__
+#define SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__
+
+namespace sandbox {
+
+extern "C" {
+
+typedef HANDLE (WINAPI *CreateNamedPipeWFunction) (
+ LPCWSTR lpName,
+ DWORD dwOpenMode,
+ DWORD dwPipeMode,
+ DWORD nMaxInstances,
+ DWORD nOutBufferSize,
+ DWORD nInBufferSize,
+ DWORD nDefaultTimeOut,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes);
+
+// Interception of CreateNamedPipeW in kernel32.dll
+SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateNamedPipeW(
+ CreateNamedPipeWFunction orig_CreateNamedPipeW, LPCWSTR pipe_name,
+ DWORD open_mode, DWORD pipe_mode, DWORD max_instance, DWORD out_buffer_size,
+ DWORD in_buffer_size, DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_INTERCEPTION_H__
diff --git a/chromium/sandbox/win/src/named_pipe_policy.cc b/chromium/sandbox/win/src/named_pipe_policy.cc
new file mode 100644
index 00000000000..0f620b18110
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_policy.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_policy.h"
+
+#include <string>
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+
+// Creates a named pipe and duplicates the handle to 'target_process'. The
+// remaining parameters are the same as CreateNamedPipeW().
+HANDLE CreateNamedPipeHelper(HANDLE target_process, LPCWSTR pipe_name,
+ DWORD open_mode, DWORD pipe_mode,
+ DWORD max_instances, DWORD out_buffer_size,
+ DWORD in_buffer_size, DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = ::CreateNamedPipeW(pipe_name, open_mode, pipe_mode,
+ max_instances, out_buffer_size,
+ in_buffer_size, default_timeout,
+ security_attributes);
+ if (INVALID_HANDLE_VALUE == pipe)
+ return pipe;
+
+ HANDLE new_pipe;
+ if (!::DuplicateHandle(::GetCurrentProcess(), pipe,
+ target_process, &new_pipe,
+ 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ return new_pipe;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool NamedPipePolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ if (TargetPolicy::NAMEDPIPES_ALLOW_ANY != semantics) {
+ return false;
+ }
+ PolicyRule pipe(ASK_BROKER);
+ if (!pipe.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IPC_CREATENAMEDPIPEW_TAG, &pipe)) {
+ return false;
+ }
+ return true;
+}
+
+DWORD NamedPipePolicy::CreateNamedPipeAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &name,
+ DWORD open_mode, DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ HANDLE* pipe) {
+ // The only action supported is ASK_BROKER which means create the pipe.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ *pipe = CreateNamedPipeHelper(client_info.process, name.c_str(),
+ open_mode, pipe_mode, max_instances,
+ out_buffer_size, in_buffer_size,
+ default_timeout, NULL);
+
+ if (INVALID_HANDLE_VALUE == *pipe)
+ return ERROR_ACCESS_DENIED;
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/named_pipe_policy.h b/chromium/sandbox/win/src/named_pipe_policy.h
new file mode 100644
index 00000000000..1ba07b8ba30
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_policy.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2006-2008 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_NAMED_PIPE_POLICY_H__
+#define SANDBOX_SRC_NAMED_PIPE_POLICY_H__
+
+#include <string>
+
+#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 named pipe creation.
+class NamedPipePolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level.
+ // policy rule for named pipe creation
+ // 'name' is the named pipe to be created
+ // 'semantics' is the desired semantics.
+ // '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);
+
+ // Processes a 'CreateNamedPipeW()' request from the target.
+ static DWORD CreateNamedPipeAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &name,
+ DWORD open_mode, DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout, HANDLE* pipe);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_NAMED_PIPE_POLICY_H__
diff --git a/chromium/sandbox/win/src/named_pipe_policy_test.cc b/chromium/sandbox/win/src/named_pipe_policy_test.cc
new file mode 100644
index 00000000000..b89a1913d06
--- /dev/null
+++ b/chromium/sandbox/win/src/named_pipe_policy_test.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+
+SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ HANDLE pipe = ::CreateNamedPipeW(argv[0],
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096,
+ 4096, 2000, NULL);
+ if (INVALID_HANDLE_VALUE == pipe)
+ return SBOX_TEST_DENIED;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
+ BOOL result = ::ConnectNamedPipe(pipe, &overlapped);
+
+ if (!result) {
+ DWORD error = ::GetLastError();
+ if (ERROR_PIPE_CONNECTED != error &&
+ ERROR_IO_PENDING != error) {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ if (!::CloseHandle(pipe))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(overlapped.hEvent);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests if we can create a pipe in the sandbox. On XP, the sandbox can create
+// a pipe without any help but it fails on Vista, this is why we do not test
+// the "denied" case.
+TEST(NamedPipePolicyTest, CreatePipe) {
+ TestRunner runner;
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh"));
+}
+
+// The same test as CreatePipe but this time using strict interceptions.
+TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) {
+ TestRunner runner;
+ runner.GetPolicy()->SetStrictInterceptions();
+
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/nt_internals.h b/chromium/sandbox/win/src/nt_internals.h
new file mode 100644
index 00000000000..c9aaf927328
--- /dev/null
+++ b/chromium/sandbox/win/src/nt_internals.h
@@ -0,0 +1,619 @@
+// Copyright (c) 2006-2010 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 holds definitions related to the ntdll API.
+
+#ifndef SANDBOX_WIN_SRC_NT_INTERNALS_H__
+#define SANDBOX_WIN_SRC_NT_INTERNALS_H__
+
+#include <windows.h>
+
+typedef LONG NTSTATUS;
+#define NT_SUCCESS(st) (st >= 0)
+
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
+#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
+#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L)
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+#ifndef STATUS_INVALID_PARAMETER
+// It is now defined in Windows 2008 SDK.
+#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
+#endif
+#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
+#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
+#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
+#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL)
+#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
+
+#define CURRENT_PROCESS ((HANDLE) -1)
+#define CURRENT_THREAD ((HANDLE) -2)
+#define NtCurrentProcess CURRENT_PROCESS
+
+typedef struct _UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING;
+typedef UNICODE_STRING *PUNICODE_STRING;
+typedef const UNICODE_STRING *PCUNICODE_STRING;
+
+typedef struct _STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PCHAR Buffer;
+} STRING;
+typedef STRING *PSTRING;
+
+typedef STRING ANSI_STRING;
+typedef PSTRING PANSI_STRING;
+typedef CONST PSTRING PCANSI_STRING;
+
+typedef STRING OEM_STRING;
+typedef PSTRING POEM_STRING;
+typedef CONST STRING* PCOEM_STRING;
+
+#define OBJ_CASE_INSENSITIVE 0x00000040L
+
+typedef struct _OBJECT_ATTRIBUTES {
+ ULONG Length;
+ HANDLE RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+} OBJECT_ATTRIBUTES;
+typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
+
+#define InitializeObjectAttributes(p, n, a, r, s) { \
+ (p)->Length = sizeof(OBJECT_ATTRIBUTES);\
+ (p)->RootDirectory = r;\
+ (p)->Attributes = a;\
+ (p)->ObjectName = n;\
+ (p)->SecurityDescriptor = s;\
+ (p)->SecurityQualityOfService = NULL;\
+}
+
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+// -----------------------------------------------------------------------
+// File IO
+
+// Create disposition values.
+
+#define FILE_SUPERSEDE 0x00000000
+#define FILE_OPEN 0x00000001
+#define FILE_CREATE 0x00000002
+#define FILE_OPEN_IF 0x00000003
+#define FILE_OVERWRITE 0x00000004
+#define FILE_OVERWRITE_IF 0x00000005
+#define FILE_MAXIMUM_DISPOSITION 0x00000005
+
+// Create/open option flags.
+
+#define FILE_DIRECTORY_FILE 0x00000001
+#define FILE_WRITE_THROUGH 0x00000002
+#define FILE_SEQUENTIAL_ONLY 0x00000004
+#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
+
+#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
+#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
+#define FILE_NON_DIRECTORY_FILE 0x00000040
+#define FILE_CREATE_TREE_CONNECTION 0x00000080
+
+#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
+#define FILE_NO_EA_KNOWLEDGE 0x00000200
+#define FILE_OPEN_REMOTE_INSTANCE 0x00000400
+#define FILE_RANDOM_ACCESS 0x00000800
+
+#define FILE_DELETE_ON_CLOSE 0x00001000
+#define FILE_OPEN_BY_FILE_ID 0x00002000
+#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
+#define FILE_NO_COMPRESSION 0x00008000
+
+#define FILE_RESERVE_OPFILTER 0x00100000
+#define FILE_OPEN_REPARSE_POINT 0x00200000
+#define FILE_OPEN_NO_RECALL 0x00400000
+#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
+
+typedef NTSTATUS (WINAPI *NtCreateFileFunction)(
+ OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PLARGE_INTEGER AllocationSize OPTIONAL,
+ IN ULONG FileAttributes,
+ IN ULONG ShareAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength);
+
+typedef NTSTATUS (WINAPI *NtOpenFileFunction)(
+ OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG ShareAccess,
+ IN ULONG OpenOptions);
+
+typedef NTSTATUS (WINAPI *NtCloseFunction)(
+ IN HANDLE Handle);
+
+typedef enum _FILE_INFORMATION_CLASS {
+ FileRenameInformation = 10
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef struct _FILE_RENAME_INFORMATION {
+ BOOLEAN ReplaceIfExists;
+ HANDLE RootDirectory;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
+
+typedef NTSTATUS (WINAPI *NtSetInformationFileFunction)(
+ IN HANDLE FileHandle,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PVOID FileInformation,
+ IN ULONG Length,
+ IN FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef struct FILE_BASIC_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ ULONG FileAttributes;
+} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+typedef NTSTATUS (WINAPI *NtQueryAttributesFileFunction)(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PFILE_BASIC_INFORMATION FileAttributes);
+
+typedef struct _FILE_NETWORK_OPEN_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ ULONG FileAttributes;
+} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION;
+
+typedef NTSTATUS (WINAPI *NtQueryFullAttributesFileFunction)(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PFILE_NETWORK_OPEN_INFORMATION FileAttributes);
+
+// -----------------------------------------------------------------------
+// Sections
+
+typedef NTSTATUS (WINAPI *NtCreateSectionFunction)(
+ OUT PHANDLE SectionHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN PLARGE_INTEGER MaximumSize OPTIONAL,
+ IN ULONG SectionPageProtection,
+ IN ULONG AllocationAttributes,
+ IN HANDLE FileHandle OPTIONAL);
+
+typedef ULONG SECTION_INHERIT;
+#define ViewShare 1
+#define ViewUnmap 2
+
+typedef NTSTATUS (WINAPI *NtMapViewOfSectionFunction)(
+ IN HANDLE SectionHandle,
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN ULONG_PTR ZeroBits,
+ IN SIZE_T CommitSize,
+ IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
+ IN OUT PSIZE_T ViewSize,
+ IN SECTION_INHERIT InheritDisposition,
+ IN ULONG AllocationType,
+ IN ULONG Win32Protect);
+
+typedef NTSTATUS (WINAPI *NtUnmapViewOfSectionFunction)(
+ IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress);
+
+typedef enum _SECTION_INFORMATION_CLASS {
+ SectionBasicInformation = 0,
+ SectionImageInformation
+} SECTION_INFORMATION_CLASS;
+
+typedef struct _SECTION_BASIC_INFORMATION {
+ PVOID BaseAddress;
+ ULONG Attributes;
+ LARGE_INTEGER Size;
+} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
+
+typedef NTSTATUS (WINAPI *NtQuerySectionFunction)(
+ IN HANDLE SectionHandle,
+ IN SECTION_INFORMATION_CLASS SectionInformationClass,
+ OUT PVOID SectionInformation,
+ IN SIZE_T SectionInformationLength,
+ OUT PSIZE_T ReturnLength OPTIONAL);
+
+// -----------------------------------------------------------------------
+// Process and Thread
+
+typedef struct _CLIENT_ID {
+ PVOID UniqueProcess;
+ PVOID UniqueThread;
+} CLIENT_ID, *PCLIENT_ID;
+
+typedef NTSTATUS (WINAPI *NtOpenThreadFunction) (
+ OUT PHANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PCLIENT_ID ClientId);
+
+typedef NTSTATUS (WINAPI *NtOpenProcessFunction) (
+ OUT PHANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN PCLIENT_ID ClientId);
+
+typedef enum _NT_THREAD_INFORMATION_CLASS {
+ ThreadBasicInformation,
+ ThreadTimes,
+ ThreadPriority,
+ ThreadBasePriority,
+ ThreadAffinityMask,
+ ThreadImpersonationToken,
+ ThreadDescriptorTableEntry,
+ ThreadEnableAlignmentFaultFixup,
+ ThreadEventPair,
+ ThreadQuerySetWin32StartAddress,
+ ThreadZeroTlsCell,
+ ThreadPerformanceCount,
+ ThreadAmILastThread,
+ ThreadIdealProcessor,
+ ThreadPriorityBoost,
+ ThreadSetTlsArrayAddress,
+ ThreadIsIoPending,
+ ThreadHideFromDebugger
+} NT_THREAD_INFORMATION_CLASS, *PNT_THREAD_INFORMATION_CLASS;
+
+typedef NTSTATUS (WINAPI *NtSetInformationThreadFunction) (
+ IN HANDLE ThreadHandle,
+ IN NT_THREAD_INFORMATION_CLASS ThreadInformationClass,
+ IN PVOID ThreadInformation,
+ IN ULONG ThreadInformationLength);
+
+// Partial definition only:
+typedef enum _PROCESSINFOCLASS {
+ ProcessBasicInformation = 0,
+ ProcessExecuteFlags = 0x22
+} PROCESSINFOCLASS;
+
+typedef PVOID PPEB;
+typedef PVOID KPRIORITY;
+
+typedef struct _PROCESS_BASIC_INFORMATION {
+ NTSTATUS ExitStatus;
+ PPEB PebBaseAddress;
+ KAFFINITY AffinityMask;
+ KPRIORITY BasePriority;
+ ULONG UniqueProcessId;
+ ULONG InheritedFromUniqueProcessId;
+} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
+
+typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)(
+ IN HANDLE ProcessHandle,
+ IN PROCESSINFOCLASS ProcessInformationClass,
+ OUT PVOID ProcessInformation,
+ IN ULONG ProcessInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS (WINAPI *NtSetInformationProcessFunction)(
+ HANDLE ProcessHandle,
+ IN PROCESSINFOCLASS ProcessInformationClass,
+ IN PVOID ProcessInformation,
+ IN ULONG ProcessInformationLength);
+
+typedef NTSTATUS (WINAPI *NtOpenThreadTokenFunction) (
+ IN HANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS (WINAPI *NtOpenThreadTokenExFunction) (
+ IN HANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ IN ULONG HandleAttributes,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS (WINAPI *NtOpenProcessTokenFunction) (
+ IN HANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS (WINAPI *NtOpenProcessTokenExFunction) (
+ IN HANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG HandleAttributes,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS (WINAPI * RtlCreateUserThreadFunction)(
+ IN HANDLE Process,
+ IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
+ IN BOOLEAN CreateSuspended,
+ IN ULONG ZeroBits,
+ IN SIZE_T MaximumStackSize,
+ IN SIZE_T CommittedStackSize,
+ IN LPTHREAD_START_ROUTINE StartAddress,
+ IN PVOID Parameter,
+ OUT PHANDLE Thread,
+ OUT PCLIENT_ID ClientId);
+
+// -----------------------------------------------------------------------
+// Registry
+
+typedef NTSTATUS (WINAPI *NtCreateKeyFunction)(
+ OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN ULONG TitleIndex,
+ IN PUNICODE_STRING Class OPTIONAL,
+ IN ULONG CreateOptions,
+ OUT PULONG Disposition OPTIONAL);
+
+typedef NTSTATUS (WINAPI *NtOpenKeyFunction)(
+ OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI *NtOpenKeyExFunction)(
+ OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN DWORD open_options);
+
+typedef NTSTATUS (WINAPI *NtDeleteKeyFunction)(
+ IN HANDLE KeyHandle);
+
+// -----------------------------------------------------------------------
+// Memory
+
+// Don't really need this structure right now.
+typedef PVOID PRTL_HEAP_PARAMETERS;
+
+typedef PVOID (WINAPI *RtlCreateHeapFunction)(
+ IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN SIZE_T ReserveSize OPTIONAL,
+ IN SIZE_T CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL);
+
+typedef PVOID (WINAPI *RtlDestroyHeapFunction)(
+ IN PVOID HeapHandle);
+
+typedef PVOID (WINAPI *RtlAllocateHeapFunction)(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN SIZE_T Size);
+
+typedef BOOLEAN (WINAPI *RtlFreeHeapFunction)(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID HeapBase);
+
+typedef NTSTATUS (WINAPI *NtAllocateVirtualMemoryFunction) (
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN ULONG_PTR ZeroBits,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG AllocationType,
+ IN ULONG Protect);
+
+typedef NTSTATUS (WINAPI *NtFreeVirtualMemoryFunction) (
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG FreeType);
+
+typedef enum _MEMORY_INFORMATION_CLASS {
+ MemoryBasicInformation = 0,
+ MemoryWorkingSetList,
+ MemorySectionName,
+ MemoryBasicVlmInformation
+} MEMORY_INFORMATION_CLASS;
+
+typedef struct _MEMORY_SECTION_NAME { // Information Class 2
+ UNICODE_STRING SectionFileName;
+} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
+
+typedef NTSTATUS (WINAPI *NtQueryVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress,
+ IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
+ OUT PVOID MemoryInformation,
+ IN ULONG MemoryInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS (WINAPI *NtProtectVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN OUT PSIZE_T ProtectSize,
+ IN ULONG NewProtect,
+ OUT PULONG OldProtect);
+
+// -----------------------------------------------------------------------
+// Objects
+
+typedef enum _OBJECT_INFORMATION_CLASS {
+ ObjectBasicInformation,
+ ObjectNameInformation,
+ ObjectTypeInformation,
+ ObjectAllInformation,
+ ObjectDataInformation
+} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
+
+typedef struct _OBJDIR_INFORMATION {
+ UNICODE_STRING ObjectName;
+ UNICODE_STRING ObjectTypeName;
+ BYTE Data[1];
+} OBJDIR_INFORMATION;
+
+typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG Reserved[10]; // reserved for internal use
+} PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION;
+
+typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING TypeName;
+ ULONG Reserved[22]; // reserved for internal use
+} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;
+
+typedef enum _POOL_TYPE {
+ NonPagedPool,
+ PagedPool,
+ NonPagedPoolMustSucceed,
+ ReservedType,
+ NonPagedPoolCacheAligned,
+ PagedPoolCacheAligned,
+ NonPagedPoolCacheAlignedMustS
+} POOL_TYPE;
+
+typedef struct _OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+ ULONG Reserved[3];
+ ULONG NameInformationLength;
+ ULONG TypeInformationLength;
+ ULONG SecurityDescriptorLength;
+ LARGE_INTEGER CreateTime;
+} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
+
+typedef struct _OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING Name;
+ ULONG TotalNumberOfObjects;
+ ULONG TotalNumberOfHandles;
+ ULONG TotalPagedPoolUsage;
+ ULONG TotalNonPagedPoolUsage;
+ ULONG TotalNamePoolUsage;
+ ULONG TotalHandleTableUsage;
+ ULONG HighWaterNumberOfObjects;
+ ULONG HighWaterNumberOfHandles;
+ ULONG HighWaterPagedPoolUsage;
+ ULONG HighWaterNonPagedPoolUsage;
+ ULONG HighWaterNamePoolUsage;
+ ULONG HighWaterHandleTableUsage;
+ ULONG InvalidAttributes;
+ GENERIC_MAPPING GenericMapping;
+ ULONG ValidAccess;
+ BOOLEAN SecurityRequired;
+ BOOLEAN MaintainHandleCount;
+ USHORT MaintainTypeList;
+ POOL_TYPE PoolType;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemHandleInformation = 16
+} SYSTEM_INFORMATION_CLASS;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION {
+ USHORT ProcessId;
+ USHORT CreatorBackTraceIndex;
+ UCHAR ObjectTypeNumber;
+ UCHAR Flags;
+ USHORT Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
+ ULONG NumberOfHandles;
+ SYSTEM_HANDLE_INFORMATION Information[1];
+} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
+
+typedef struct _OBJECT_NAME_INFORMATION {
+ UNICODE_STRING ObjectName;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+typedef NTSTATUS (WINAPI *NtQueryObjectFunction)(
+ IN HANDLE Handle,
+ IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ OUT PVOID ObjectInformation OPTIONAL,
+ IN ULONG ObjectInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS (WINAPI *NtDuplicateObjectFunction)(
+ IN HANDLE SourceProcess,
+ IN HANDLE SourceHandle,
+ IN HANDLE TargetProcess,
+ OUT PHANDLE TargetHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG Attributes,
+ IN ULONG Options);
+
+typedef NTSTATUS (WINAPI *NtSignalAndWaitForSingleObjectFunction)(
+ IN HANDLE HandleToSignal,
+ IN HANDLE HandleToWait,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout OPTIONAL);
+
+typedef NTSTATUS (WINAPI *NtQuerySystemInformation)(
+ IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ OUT PVOID SystemInformation,
+ IN ULONG SystemInformationLength,
+ OUT PULONG ReturnLength);
+
+typedef NTSTATUS (WINAPI *NtQueryObject)(
+ IN HANDLE Handle,
+ IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ OUT PVOID ObjectInformation,
+ IN ULONG ObjectInformationLength,
+ OUT PULONG ReturnLength);
+
+// -----------------------------------------------------------------------
+// Strings
+
+typedef int (__cdecl *_strnicmpFunction)(
+ IN const char* _Str1,
+ IN const char* _Str2,
+ IN size_t _MaxCount);
+
+typedef size_t (__cdecl *strlenFunction)(
+ IN const char * _Str);
+
+typedef size_t (__cdecl *wcslenFunction)(
+ IN const wchar_t* _Str);
+
+typedef NTSTATUS (WINAPI *RtlAnsiStringToUnicodeStringFunction)(
+ IN OUT PUNICODE_STRING DestinationString,
+ IN PANSI_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString);
+
+typedef LONG (WINAPI *RtlCompareUnicodeStringFunction)(
+ IN PCUNICODE_STRING String1,
+ IN PCUNICODE_STRING String2,
+ IN BOOLEAN CaseInSensitive);
+
+typedef VOID (WINAPI *RtlInitUnicodeStringFunction) (
+ IN OUT PUNICODE_STRING DestinationString,
+ IN PCWSTR SourceString);
+
+#endif // SANDBOX_WIN_SRC_NT_INTERNALS_H__
+
diff --git a/chromium/sandbox/win/src/policy_broker.cc b/chromium/sandbox/win/src/policy_broker.cc
new file mode 100644
index 00000000000..fbe46190770
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_broker.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <map>
+
+#include "sandbox/win/src/policy_broker.h"
+
+#include "base/logging.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_process.h"
+
+// This code executes on the broker side, as a callback from the policy on the
+// target side (the child).
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+#define INIT_GLOBAL_NT(member) \
+ g_nt.member = reinterpret_cast<Nt##member##Function>( \
+ ntdll_image.GetProcAddress("Nt" #member)); \
+ if (NULL == g_nt.member) \
+ return false
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.member = reinterpret_cast<member##Function>( \
+ ntdll_image.GetProcAddress(#member)); \
+ if (NULL == g_nt.member) \
+ return false
+
+bool SetupNtdllImports(TargetProcess *child) {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ base::win::PEImage ntdll_image(ntdll);
+
+ // Bypass purify's interception.
+ wchar_t* loader_get = reinterpret_cast<wchar_t*>(
+ ntdll_image.GetProcAddress("LdrGetDllHandle"));
+ if (loader_get) {
+ GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ loader_get, &ntdll);
+ }
+
+ INIT_GLOBAL_NT(AllocateVirtualMemory);
+ INIT_GLOBAL_NT(Close);
+ INIT_GLOBAL_NT(DuplicateObject);
+ INIT_GLOBAL_NT(FreeVirtualMemory);
+ INIT_GLOBAL_NT(MapViewOfSection);
+ INIT_GLOBAL_NT(ProtectVirtualMemory);
+ INIT_GLOBAL_NT(QueryInformationProcess);
+ INIT_GLOBAL_NT(QueryObject);
+ INIT_GLOBAL_NT(QuerySection);
+ INIT_GLOBAL_NT(QueryVirtualMemory);
+ INIT_GLOBAL_NT(UnmapViewOfSection);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlCreateUserThread);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+
+#ifndef NDEBUG
+ // Verify that the structure is fully initialized.
+ for (size_t i = 0; i < sizeof(g_nt)/sizeof(void*); i++)
+ DCHECK(reinterpret_cast<char**>(&g_nt)[i]);
+#endif
+ return (SBOX_ALL_OK == child->TransferVariable("g_nt", &g_nt, sizeof(g_nt)));
+}
+
+#undef INIT_GLOBAL_NT
+#undef INIT_GLOBAL_RTL
+
+bool SetupBasicInterceptions(InterceptionManager* manager) {
+ // Interceptions provided by process_thread_policy, without actual policy.
+ if (!INTERCEPT_NT(manager, NtOpenThread, OPEN_TREAD_ID, 20) ||
+ !INTERCEPT_NT(manager, NtOpenProcess, OPEN_PROCESS_ID, 20) ||
+ !INTERCEPT_NT(manager, NtOpenProcessToken, OPEN_PROCESS_TOKEN_ID, 16))
+ return false;
+
+ // Interceptions with neither policy nor IPC.
+ if (!INTERCEPT_NT(manager, NtSetInformationThread, SET_INFORMATION_THREAD_ID,
+ 20) ||
+ !INTERCEPT_NT(manager, NtOpenThreadToken, OPEN_THREAD_TOKEN_ID, 20))
+ return false;
+
+ if (base::win::GetVersion() >= base::win::VERSION_XP) {
+ // Bug 27218: We don't have dispatch for some x64 syscalls.
+ // This one is also provided by process_thread_policy.
+ if (!INTERCEPT_NT(manager, NtOpenProcessTokenEx, OPEN_PROCESS_TOKEN_EX_ID,
+ 20))
+ return false;
+
+ return INTERCEPT_NT(manager, NtOpenThreadTokenEx, OPEN_THREAD_TOKEN_EX_ID,
+ 24);
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_broker.h b/chromium/sandbox/win/src/policy_broker.h
new file mode 100644
index 00000000000..1c5cc26c236
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_broker.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2006-2010 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_POLICY_BROKER_H_
+#define SANDBOX_SRC_POLICY_BROKER_H_
+
+#include "sandbox/win/src/interception.h"
+
+namespace sandbox {
+
+class TargetProcess;
+
+// Sets up interceptions not controlled by explicit policies.
+bool SetupBasicInterceptions(InterceptionManager* manager);
+
+// Sets up imports from NTDLL for the given target process so the interceptions
+// can work.
+bool SetupNtdllImports(TargetProcess *child);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_BROKER_H_
diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.cc b/chromium/sandbox/win/src/policy_engine_opcodes.cc
new file mode 100644
index 00000000000..e8a39ed6122
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_engine_opcodes.cc
@@ -0,0 +1,454 @@
+// Copyright (c) 2006-2008 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/policy_engine_opcodes.h"
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+const unsigned short kMaxUniStrSize = 0xfffc;
+
+bool InitStringUnicode(const wchar_t* source, size_t length,
+ UNICODE_STRING* ustring) {
+ ustring->Buffer = const_cast<wchar_t*>(source);
+ ustring->Length = static_cast<USHORT>(length) * sizeof(wchar_t);
+ if (length > kMaxUniStrSize) {
+ return false;
+ }
+ ustring->MaximumLength = (NULL != source) ?
+ ustring->Length + sizeof(wchar_t) : 0;
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Note: The opcodes are implemented as functions (as opposed to classes derived
+// from PolicyOpcode) because you should not add more member variables to the
+// PolicyOpcode class since it would cause object slicing on the target. So to
+// enforce that (instead of just trusting the developer) the opcodes became
+// just functions.
+//
+// In the code that follows I have keep the evaluation function and the factory
+// function together to stress the close relationship between both. For example,
+// only the factory method and the evaluation function know the stored argument
+// order and meaning.
+
+template <int>
+EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp,
+ MatchContext* match);
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAlwaysFalse:
+// Does not require input parameter.
+
+PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) {
+ return MakeBase(OP_ALWAYS_FALSE, options, -1);
+}
+
+template <>
+EvalResult OpcodeEval<OP_ALWAYS_FALSE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(opcode);
+ UNREFERENCED_PARAMETER(param);
+ UNREFERENCED_PARAMETER(context);
+ return EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAlwaysTrue:
+// Does not require input parameter.
+
+PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) {
+ return MakeBase(OP_ALWAYS_TRUE, options, -1);
+}
+
+template <>
+EvalResult OpcodeEval<OP_ALWAYS_TRUE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(opcode);
+ UNREFERENCED_PARAMETER(param);
+ UNREFERENCED_PARAMETER(context);
+ return EVAL_TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAction:
+// Does not require input parameter.
+// Argument 0 contains the actual action to return.
+
+PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action,
+ uint32 options) {
+ PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, action);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_ACTION>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(param);
+ UNREFERENCED_PARAMETER(context);
+ int action = 0;
+ opcode->GetArgument(0, &action);
+ return static_cast<EvalResult>(action);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberMatch:
+// Requires a unsigned long or void* in selected_param
+// Argument 0 is the stored number to match.
+// Argument 1 is the C++ type of the 0th argument.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param,
+ unsigned long match,
+ uint32 options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, ULONG_TYPE);
+ return opcode;
+}
+
+PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param,
+ const void* match,
+ uint32 options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, VOIDPTR_TYPE);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(context);
+ unsigned long value_ulong = 0;
+ if (param->Get(&value_ulong)) {
+ unsigned long match_ulong = 0;
+ opcode->GetArgument(0, &match_ulong);
+ return (match_ulong != value_ulong)? EVAL_FALSE : EVAL_TRUE;
+ } else {
+ const void* value_ptr = NULL;
+ if (param->Get(&value_ptr)) {
+ const void* match_ptr = NULL;
+ opcode->GetArgument(0, &match_ptr);
+ return (match_ptr != value_ptr)? EVAL_FALSE : EVAL_TRUE;
+ }
+ }
+ return EVAL_ERROR;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpUlongMatchRange
+// Requires a unsigned long in selected_param.
+// Argument 0 is the stored lower bound to match.
+// Argument 1 is the stored upper bound to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpUlongMatchRange(int16 selected_param,
+ unsigned long lower_bound,
+ unsigned long upper_bound,
+ uint32 options) {
+ if (lower_bound > upper_bound) {
+ return false;
+ }
+ PolicyOpcode* opcode = MakeBase(OP_ULONG_MATCH_RANGE, options,
+ selected_param);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, lower_bound);
+ opcode->SetArgument(1, upper_bound);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_ULONG_MATCH_RANGE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(context);
+ unsigned long value = 0;
+ if (!param->Get(&value)) return EVAL_ERROR;
+
+ unsigned long lower_bound = 0;
+ unsigned long upper_bound = 0;
+ opcode->GetArgument(0, &lower_bound);
+ opcode->GetArgument(1, &upper_bound);
+ return((lower_bound <= value) && (upper_bound >= value))?
+ EVAL_TRUE : EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpUlongAndMatch:
+// Requires a unsigned long in selected_param.
+// Argument 0 is the stored number to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpUlongAndMatch(int16 selected_param,
+ unsigned long match,
+ uint32 options) {
+ PolicyOpcode* opcode = MakeBase(OP_ULONG_AND_MATCH, options, selected_param);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, match);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_ULONG_AND_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(context);
+ unsigned long value = 0;
+ if (!param->Get(&value)) return EVAL_ERROR;
+
+ unsigned long number = 0;
+ opcode->GetArgument(0, &number);
+ return (number & value)? EVAL_TRUE : EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpWStringMatch:
+// Requires a wchar_t* in selected_param.
+// Argument 0 is the byte displacement of the stored string.
+// Argument 1 is the lenght in chars of the stored string.
+// Argument 2 is the offset to apply on the input string. It has special values.
+// as noted in the header file.
+// Argument 3 is the string matching options.
+
+PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param,
+ const wchar_t* match_str,
+ int start_position,
+ StringMatchOptions match_opts,
+ uint32 options) {
+ if (NULL == match_str) {
+ return NULL;
+ }
+ if ('\0' == match_str[0]) {
+ return NULL;
+ }
+
+ int lenght = lstrlenW(match_str);
+
+ PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param);
+ if (NULL == opcode) {
+ return NULL;
+ }
+ ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str)+1);
+ if (0 == delta_str) {
+ return NULL;
+ }
+ opcode->SetArgument(0, delta_str);
+ opcode->SetArgument(1, lenght);
+ opcode->SetArgument(2, start_position);
+ opcode->SetArgument(3, match_opts);
+ return opcode;
+}
+
+template<>
+EvalResult OpcodeEval<OP_WSTRING_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ if (NULL == context) {
+ return EVAL_ERROR;
+ }
+ const wchar_t* source_str = NULL;
+ if (!param->Get(&source_str)) return EVAL_ERROR;
+
+ int start_position = 0;
+ int match_len = 0;
+ unsigned int match_opts = 0;
+ opcode->GetArgument(1, &match_len);
+ opcode->GetArgument(2, &start_position);
+ opcode->GetArgument(3, &match_opts);
+
+ const wchar_t* match_str = opcode->GetRelativeString(0);
+ // Advance the source string to the last successfully evaluated position
+ // according to the match context.
+ source_str = &source_str[context->position];
+ int source_len = static_cast<int>(g_nt.wcslen(source_str));
+
+ if (0 == source_len) {
+ // If we reached the end of the source string there is nothing we can
+ // match against.
+ return EVAL_FALSE;
+ }
+ if (match_len > source_len) {
+ // There can't be a positive match when the target string is bigger than
+ // the source string
+ return EVAL_FALSE;
+ }
+
+ BOOL case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE;
+
+ // We have three cases, depending on the value of start_pos:
+ // Case 1. We skip N characters and compare once.
+ // Case 2: We skip to the end and compare once.
+ // Case 3: We match the first substring (if we find any).
+ if (start_position >= 0) {
+ if (kSeekToEnd == start_position) {
+ start_position = source_len - match_len;
+ } else if (match_opts & EXACT_LENGHT) {
+ // A sub-case of case 3 is when the EXACT_LENGHT flag is on
+ // the match needs to be not just substring but full match.
+ if ((match_len + start_position) != source_len) {
+ return EVAL_FALSE;
+ }
+ }
+
+ // Advance start_pos characters. Warning! this does not consider
+ // utf16 encodings (surrogate pairs) or other Unicode 'features'.
+ source_str += start_position;
+
+ // Since we skipped, lets reevaluate just the lengths again.
+ if ((match_len + start_position) > source_len) {
+ return EVAL_FALSE;
+ }
+
+ UNICODE_STRING match_ustr;
+ InitStringUnicode(match_str, match_len, &match_ustr);
+ UNICODE_STRING source_ustr;
+ InitStringUnicode(source_str, match_len, &source_ustr);
+
+ if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr,
+ case_sensitive)) {
+ // Match! update the match context.
+ context->position += start_position + match_len;
+ return EVAL_TRUE;
+ } else {
+ return EVAL_FALSE;
+ }
+ } else if (start_position < 0) {
+ UNICODE_STRING match_ustr;
+ InitStringUnicode(match_str, match_len, &match_ustr);
+ UNICODE_STRING source_ustr;
+ InitStringUnicode(source_str, match_len, &source_ustr);
+
+ do {
+ if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr,
+ case_sensitive)) {
+ // Match! update the match context.
+ context->position += (source_ustr.Buffer - source_str) + match_len;
+ return EVAL_TRUE;
+ }
+ ++source_ustr.Buffer;
+ --source_len;
+ } while (source_len >= match_len);
+ }
+ return EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// OpcodeMaker (other member functions).
+
+PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id,
+ uint32 options,
+ int16 selected_param) {
+ if (memory_size() < sizeof(PolicyOpcode)) {
+ return NULL;
+ }
+
+ // Create opcode using placement-new on the buffer memory.
+ PolicyOpcode* opcode = new(memory_top_) PolicyOpcode();
+
+ // Fill in the standard fields, that every opcode has.
+ memory_top_ += sizeof(PolicyOpcode);
+ opcode->opcode_id_ = opcode_id;
+ opcode->options_ = static_cast<int16>(options);
+ opcode->parameter_ = selected_param;
+ return opcode;
+}
+
+ptrdiff_t OpcodeFactory::AllocRelative(void* start, const wchar_t* str,
+ size_t lenght) {
+ size_t bytes = lenght * sizeof(wchar_t);
+ if (memory_size() < bytes) {
+ return 0;
+ }
+ memory_bottom_ -= bytes;
+ if (reinterpret_cast<UINT_PTR>(memory_bottom_) & 1) {
+ // TODO(cpu) replace this for something better.
+ ::DebugBreak();
+ }
+ memcpy(memory_bottom_, str, bytes);
+ ptrdiff_t delta = memory_bottom_ - reinterpret_cast<char*>(start);
+ return delta;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode evaluation dispatchers.
+
+// This function is the one and only entry for evaluating any opcode. It is
+// in charge of applying any relevant opcode options and calling EvaluateInner
+// were the actual dispatch-by-id is made. It would seem at first glance that
+// the dispatch should be done by virtual function (vtable) calls but you have
+// to remember that the opcodes are made in the broker process and copied as
+// raw memory to the target process.
+
+EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params,
+ size_t param_count, MatchContext* match) {
+ if (NULL == call_params) {
+ return EVAL_ERROR;
+ }
+ const ParameterSet* selected_param = NULL;
+ if (parameter_ >= 0) {
+ if (static_cast<size_t>(parameter_) >= param_count) {
+ return EVAL_ERROR;
+ }
+ selected_param = &call_params[parameter_];
+ }
+ EvalResult result = EvaluateHelper(selected_param, match);
+
+ // Apply the general options regardless of the particular type of opcode.
+ if (kPolNone == options_) {
+ return result;
+ }
+
+ if (options_ & kPolNegateEval) {
+ if (EVAL_TRUE == result) {
+ result = EVAL_FALSE;
+ } else if (EVAL_FALSE == result) {
+ result = EVAL_TRUE;
+ } else if (EVAL_ERROR != result) {
+ result = EVAL_ERROR;
+ }
+ }
+ if (NULL != match) {
+ if (options_ & kPolClearContext) {
+ match->Clear();
+ }
+ if (options_ & kPolUseOREval) {
+ match->options = kPolUseOREval;
+ }
+ }
+ return result;
+}
+
+#define OPCODE_EVAL(op, x, y, z) case op: return OpcodeEval<op>(x, y, z)
+
+EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters,
+ MatchContext* match) {
+ switch (opcode_id_) {
+ OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match);
+ OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_ULONG_MATCH_RANGE, this, parameters, match);
+ OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_ULONG_AND_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_ACTION, this, parameters, match);
+ default:
+ return EVAL_ERROR;
+ }
+}
+
+#undef OPCODE_EVAL
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.h b/chromium/sandbox/win/src/policy_engine_opcodes.h
new file mode 100644
index 00000000000..306e67761c1
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_engine_opcodes.h
@@ -0,0 +1,380 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
+#define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
+
+#include "sandbox/win/src/policy_engine_params.h"
+#include "base/basictypes.h"
+
+// The low-level policy is implemented using the concept of policy 'opcodes'.
+// An opcode is a structure that contains enough information to perform one
+// comparison against one single input parameter. For example, an opcode can
+// encode just one of the following comparison:
+//
+// - Is input parameter 3 not equal to NULL?
+// - Does input parameter 2 start with L"c:\\"?
+// - Is input parameter 5, bit 3 is equal 1?
+//
+// Each opcode is in fact equivalent to a function invocation where all
+// the parameters are known by the opcode except one. So say you have a
+// function of this form:
+// bool fn(a, b, c, d) with 4 arguments
+//
+// Then an opcode is:
+// op(fn, b, c, d)
+// Which stores the function to call and its 3 last arguments
+//
+// Then and opcode evaluation is:
+// op.eval(a) ------------------------> fn(a,b,c,d)
+// internally calls
+//
+// The idea is that complex policy rules can be split into streams of
+// opcodes which are evaluated in sequence. The evaluation is done in
+// groups of opcodes that have N comparison opcodes plus 1 action opcode:
+//
+// [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
+// ----- evaluation order----------->
+//
+// Each opcode group encodes one high-level policy rule. The rule applies
+// only if all the conditions on the group evaluate to true. The action
+// opcode contains the policy outcome for that particular rule.
+//
+// Note that this header contains the main building blocks of low-level policy
+// but not the low level policy class.
+namespace sandbox {
+
+// These are the possible policy outcomes. Note that some of them might
+// not apply and can be removed. Also note that The following values only
+// specify what to do, not how to do it and it is acceptable given specific
+// cases to ignore the policy outcome.
+enum EvalResult {
+ // Comparison opcode values:
+ EVAL_TRUE, // Opcode condition evaluated true.
+ EVAL_FALSE, // Opcode condition evaluated false.
+ EVAL_ERROR, // Opcode condition generated an error while evaluating.
+ // Action opcode values:
+ ASK_BROKER, // The target must generate an IPC to the broker. On the broker
+ // side, this means grant access to the resource.
+ DENY_ACCESS, // No access granted to the resource.
+ GIVE_READONLY, // Give readonly access to the resource.
+ GIVE_ALLACCESS, // Give full access to the resource.
+ GIVE_CACHED, // IPC is not required. Target can return a cached handle.
+ GIVE_FIRST, // TODO(cpu)
+ SIGNAL_ALARM, // Unusual activity. Generate an alarm.
+ FAKE_SUCCESS, // Do not call original function. Just return 'success'.
+ FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied'
+ // and do not do IPC.
+ TERMINATE_PROCESS, // Destroy target process. Do IPC as well.
+};
+
+// The following are the implemented opcodes.
+enum OpcodeID {
+ OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE).
+ OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE).
+ OP_NUMBER_MATCH, // Match a 32-bit integer as n == a.
+ OP_ULONG_MATCH_RANGE, // Match an ulong integer as a <= n <= b.
+ OP_ULONG_AND_MATCH, // Match using bitwise AND; as in: n & a != 0.
+ OP_WSTRING_MATCH, // Match a string for equality.
+ OP_ACTION // Evaluates to an action opcode.
+};
+
+// Options that apply to every opcode. They are specified when creating
+// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
+// Do nothing special.
+const uint32 kPolNone = 0;
+
+// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
+// negated conditions such as if ( a && !b).
+const uint32 kPolNegateEval = 1;
+
+// Zero the MatchContext context structure. This happens after the opcode
+// is evaluated.
+const uint32 kPolClearContext = 2;
+
+// Use OR when evaluating this set of opcodes. The policy evaluator by default
+// uses AND when evaluating. Very helpful when
+// used with kPolNegateEval. For example if you have a condition best expressed
+// as if(! (a && b && c)), the use of this flags allows it to be expressed as
+// if ((!a) || (!b) || (!c)).
+const uint32 kPolUseOREval = 4;
+
+// Keeps the evaluation state between opcode evaluations. This is used
+// for string matching where the next opcode needs to continue matching
+// from the last character position from the current opcode. The match
+// context is preserved across opcode evaluation unless an opcode specifies
+// as an option kPolClearContext.
+struct MatchContext {
+ size_t position;
+ uint32 options;
+
+ MatchContext() {
+ Clear();
+ }
+
+ void Clear() {
+ position = 0;
+ options = 0;
+ }
+};
+
+// Models a policy opcode; that is a condition evaluation were all the
+// arguments but one are stored in objects of this class. Use OpcodeFactory
+// to create objects of this type.
+// This class is just an implementation artifact and not exposed to the
+// API clients or visible in the intercepted service. Internally, an
+// opcode is just:
+// - An integer that identifies the actual opcode.
+// - An index to indicate which one is the input argument
+// - An array of arguments.
+// While an OO hierarchy of objects would have been a natural choice, the fact
+// that 1) this code can execute before the CRT is loaded, presents serious
+// problems in terms of guarantees about the actual state of the vtables and
+// 2) because the opcode objects are generated in the broker process, we need to
+// use plain objects. To preserve some minimal type safety templates are used
+// when possible.
+class PolicyOpcode {
+ friend class OpcodeFactory;
+ public:
+ // Evaluates the opcode. For a typical comparison opcode the return value
+ // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the
+ // the return is EVAL_ERROR. If the opcode is an action opcode then the
+ // return can take other values such as ASK_BROKER.
+ // parameters: An array of all input parameters. This argument is normally
+ // created by the macros POLPARAMS_BEGIN() POLPARAMS_END.
+ // count: The number of parameters passed as first argument.
+ // match: The match context that is persisted across the opcode evaluation
+ // sequence.
+ EvalResult Evaluate(const ParameterSet* parameters, size_t count,
+ MatchContext* match);
+
+ // Retrieves a stored argument by index. Valid index values are
+ // from 0 to < kArgumentCount.
+ template <typename T>
+ void GetArgument(size_t index, T* argument) const {
+ COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size);
+ *argument = *reinterpret_cast<const T*>(&arguments_[index].mem);
+ }
+
+ // Sets a stored argument by index. Valid index values are
+ // from 0 to < kArgumentCount.
+ template <typename T>
+ void SetArgument(size_t index, const T& argument) {
+ COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size);
+ *reinterpret_cast<T*>(&arguments_[index].mem) = argument;
+ }
+
+ // Retrieves the actual address of an string argument. When using
+ // GetArgument() to retrieve an index that contains a string, the returned
+ // value is just an offset to the actual string.
+ // index: the stored string index. Valid values are from 0
+ // to < kArgumentCount.
+ const wchar_t* GetRelativeString(size_t index) const {
+ ptrdiff_t str_delta = 0;
+ GetArgument(index, &str_delta);
+ const char* delta = reinterpret_cast<const char*>(this) + str_delta;
+ return reinterpret_cast<const wchar_t*>(delta);
+ }
+
+ // Returns true if this opcode is an action opcode without actually
+ // evaluating it. Used to do a quick scan forward to the next opcode group.
+ bool IsAction() const {
+ return (OP_ACTION == opcode_id_);
+ };
+
+ // Returns the opcode type.
+ OpcodeID GetID() const {
+ return opcode_id_;
+ }
+
+ // Returns the stored options such as kPolNegateEval and others.
+ uint32 GetOptions() const {
+ return options_;
+ }
+
+ // Sets the stored options such as kPolNegateEval.
+ void SetOptions(int16 options) {
+ options_ = options;
+ }
+
+ private:
+
+ static const size_t kArgumentCount = 4; // The number of supported argument.
+
+ struct OpcodeArgument {
+ UINT_PTR mem;
+ };
+
+ // Better define placement new in the class instead of relying on the
+ // global definition which seems to be fubared.
+ void* operator new(size_t, void* location) {
+ return location;
+ }
+
+ // Helper function to evaluate the opcode. The parameters have the same
+ // meaning that in Evaluate().
+ EvalResult EvaluateHelper(const ParameterSet* parameters,
+ MatchContext* match);
+ OpcodeID opcode_id_;
+ int16 parameter_;
+ int16 options_;
+ OpcodeArgument arguments_[PolicyOpcode::kArgumentCount];
+};
+
+enum StringMatchOptions {
+ CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
+ CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
+ EXACT_LENGHT = 2 // Don't do substring match. Do full string match.
+};
+
+// Opcodes that do string comparisons take a parameter that is the starting
+// position to perform the comparison so we can do substring matching. There
+// are two special values:
+//
+// Start from the current position and compare strings advancing forward until
+// a match is found if any. Similar to CRT strstr().
+const int kSeekForward = -1;
+// Perform a match with the end of the string. It only does a single comparison.
+const int kSeekToEnd = 0xfffff;
+
+
+// A PolicyBuffer is a variable size structure that contains all the opcodes
+// that are to be created or evaluated in sequence.
+struct PolicyBuffer {
+ size_t opcode_count;
+ PolicyOpcode opcodes[1];
+};
+
+// Helper class to create any opcode sequence. This class is normally invoked
+// only by the high level policy module or when you need to handcraft a special
+// policy.
+// The factory works by creating the opcodes using a chunk of memory given
+// in the constructor. The opcodes themselves are allocated from the beginning
+// (top) of the memory, while any string that an opcode needs is allocated from
+// the end (bottom) of the memory.
+//
+// In essence:
+//
+// low address ---> [opcode 1]
+// [opcode 2]
+// [opcode 3]
+// | | <--- memory_top_
+// | free |
+// | |
+// | | <--- memory_bottom_
+// [string 1]
+// high address --> [string 2]
+//
+// Note that this class does not keep track of the number of opcodes made and
+// it is designed to be a building block for low-level policy.
+//
+// Note that any of the MakeOpXXXXX member functions below can return NULL on
+// failure. When that happens opcode sequence creation must be aborted.
+class OpcodeFactory {
+ public:
+ // memory: base pointer to a chunk of memory where the opcodes are created.
+ // memory_size: the size in bytes of the memory chunk.
+ OpcodeFactory(char* memory, size_t memory_size)
+ : memory_top_(memory) {
+ memory_bottom_ = &memory_top_[memory_size];
+ }
+
+ // policy: contains the raw memory where the opcodes are created.
+ // memory_size: contains the actual size of the policy argument.
+ OpcodeFactory(PolicyBuffer* policy, size_t memory_size) {
+ memory_top_ = reinterpret_cast<char*>(&policy->opcodes[0]);
+ memory_bottom_ = &memory_top_[memory_size];
+ }
+
+ // Returns the available memory to make opcodes.
+ size_t memory_size() const {
+ return memory_bottom_ - memory_top_;
+ }
+
+ // Creates an OpAlwaysFalse opcode.
+ PolicyOpcode* MakeOpAlwaysFalse(uint32 options);
+
+ // Creates an OpAlwaysFalse opcode.
+ PolicyOpcode* MakeOpAlwaysTrue(uint32 options);
+
+ // Creates an OpAction opcode.
+ // action: The action to return when Evaluate() is called.
+ PolicyOpcode* MakeOpAction(EvalResult action, uint32 options);
+
+ // Creates an OpNumberMatch opcode.
+ // selected_param: index of the input argument. It must be a ulong or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the number to compare against the selected_param.
+ PolicyOpcode* MakeOpNumberMatch(int16 selected_param, unsigned long match,
+ uint32 options);
+
+ // Creates an OpNumberMatch opcode (void pointers are cast to numbers).
+ // selected_param: index of the input argument. It must be an void* or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the pointer numeric value to compare against selected_param.
+ PolicyOpcode* MakeOpVoidPtrMatch(int16 selected_param, const void* match,
+ uint32 options);
+
+ // Creates an OpUlongMatchRange opcode using the memory passed in the ctor.
+ // selected_param: index of the input argument. It must be a ulong or the
+ // evaluation result will generate a EVAL_ERROR.
+ // lower_bound, upper_bound: the range to compare against selected_param.
+ PolicyOpcode* MakeOpUlongMatchRange(int16 selected_param,
+ unsigned long lower_bound,
+ unsigned long upper_bound,
+ uint32 options);
+
+ // Creates an OpWStringMatch opcode using the raw memory passed in the ctor.
+ // selected_param: index of the input argument. It must be a wide string
+ // pointer or the evaluation result will generate a EVAL_ERROR.
+ // match_str: string to compare against selected_param.
+ // start_position: when its value is from 0 to < 0x7fff it indicates an
+ // offset from the selected_param string where to perform the comparison. If
+ // the value is SeekForward then a substring search is performed. If the
+ // value is SeekToEnd the comparison is performed against the last part of
+ // the selected_param string.
+ // Note that the range in the position (0 to 0x7fff) is dictated by the
+ // current implementation.
+ // match_opts: Indicates additional matching flags. Currently CaseInsensitive
+ // is supported.
+ PolicyOpcode* MakeOpWStringMatch(int16 selected_param,
+ const wchar_t* match_str,
+ int start_position,
+ StringMatchOptions match_opts,
+ uint32 options);
+
+ // Creates an OpUlongAndMatch opcode using the raw memory passed in the ctor.
+ // selected_param: index of the input argument. It must be ulong or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the value to bitwise AND against selected_param.
+ PolicyOpcode* MakeOpUlongAndMatch(int16 selected_param,
+ unsigned long match,
+ uint32 options);
+
+ private:
+ // Constructs the common part of every opcode. selected_param is the index
+ // of the input param to use when evaluating the opcode. Pass -1 in
+ // selected_param to indicate that no input parameter is required.
+ PolicyOpcode* MakeBase(OpcodeID opcode_id, uint32 options,
+ int16 selected_param);
+
+ // Allocates (and copies) a string (of size length) inside the buffer and
+ // returns the displacement with respect to start.
+ ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght);
+
+ // Points to the lowest currently available address of the memory
+ // used to make the opcodes. This pointer increments as opcodes are made.
+ char* memory_top_;
+
+ // Points to the highest currently available address of the memory
+ // used to make the opcodes. This pointer decrements as opcode strings are
+ // allocated.
+ char* memory_bottom_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpcodeFactory);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
diff --git a/chromium/sandbox/win/src/policy_engine_params.h b/chromium/sandbox/win/src/policy_engine_params.h
new file mode 100644
index 00000000000..a1566f3afab
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_engine_params.h
@@ -0,0 +1,202 @@
+// Copyright (c) 2006-2008 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_POLICY_ENGINE_PARAMS_H__
+#define SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+// This header defines the classes that allow the low level policy to select
+// the input parameters. In order to better make sense of this header is
+// recommended that you check policy_engine_opcodes.h first.
+
+namespace sandbox {
+
+// Models the set of interesting parameters of an intercepted system call
+// normally you don't create objects of this class directly, instead you
+// use the POLPARAMS_XXX macros.
+// For example, if an intercepted function has the following signature:
+//
+// NTSTATUS NtOpenFileFunction (PHANDLE FileHandle,
+// ACCESS_MASK DesiredAccess,
+// POBJECT_ATTRIBUTES ObjectAttributes,
+// PIO_STATUS_BLOCK IoStatusBlock,
+// ULONG ShareAccess,
+// ULONG OpenOptions);
+//
+// You could say that the following parameters are of interest to policy:
+//
+// POLPARAMS_BEGIN(open_params)
+// POLPARAM(DESIRED_ACCESS)
+// POLPARAM(OBJECT_NAME)
+// POLPARAM(SECURITY_DESCRIPTOR)
+// POLPARAM(IO_STATUS)
+// POLPARAM(OPEN_OPTIONS)
+// POLPARAMS_END;
+//
+// and the actual code will use this for defining the parameters:
+//
+// CountedParameterSet<open_params> p;
+// p[open_params::DESIRED_ACCESS] = ParamPickerMake(DesiredAccess);
+// p[open_params::OBJECT_NAME] =
+// ParamPickerMake(ObjectAttributes->ObjectName);
+// p[open_params::SECURITY_DESCRIPTOR] =
+// ParamPickerMake(ObjectAttributes->SecurityDescriptor);
+// p[open_params::IO_STATUS] = ParamPickerMake(IoStatusBlock);
+// p[open_params::OPEN_OPTIONS] = ParamPickerMake(OpenOptions);
+//
+// These will create an stack-allocated array of ParameterSet objects which
+// have each 1) the address of the parameter 2) a numeric id that encodes the
+// original C++ type. This allows the policy to treat any set of supported
+// argument types uniformily and with some type safety.
+//
+// TODO(cpu): support not fully implemented yet for unicode string and will
+// probably add other types as well.
+class ParameterSet {
+ public:
+ ParameterSet() : real_type_(INVALID_TYPE), address_(NULL) {}
+
+ // Retrieve the stored parameter. If the type does not match ulong fail.
+ bool Get(unsigned long* destination) const {
+ if (ULONG_TYPE != real_type_) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<unsigned long>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match void* fail.
+ bool Get(const void** destination) const {
+ if (VOIDPTR_TYPE != real_type_) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<void*>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match wchar_t* fail.
+ bool Get(const wchar_t** destination) const {
+ if (WCHAR_TYPE != real_type_) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<const wchar_t*>();
+ return true;
+ }
+
+ // False if the parameter is not properly initialized.
+ bool IsValid() const {
+ return INVALID_TYPE != real_type_;
+ }
+
+ protected:
+ // The constructor can only be called by derived types, which should
+ // safely provide the real_type and the address of the argument.
+ ParameterSet(ArgType real_type, const void* address)
+ : real_type_(real_type), address_(address) {
+ }
+
+ private:
+ // This template provides the same functionality as bits_cast but
+ // it works with pointer while the former works only with references.
+ template <typename T>
+ T Void2TypePointerCopy() const {
+ return *(reinterpret_cast<const T*>(address_));
+ }
+
+ ArgType real_type_;
+ const void* address_;
+};
+
+// To safely infer the type, we use a set of template specializations
+// in ParameterSetEx with a template function ParamPickerMake to do the
+// parameter type deduction.
+
+// Base template class. Not implemented so using unsupported types should
+// fail to compile.
+template <typename T>
+class ParameterSetEx : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address);
+};
+
+template<>
+class ParameterSetEx<void const*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(VOIDPTR_TYPE, address) {}
+};
+
+template<>
+class ParameterSetEx<void*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(VOIDPTR_TYPE, address) {}
+};
+
+
+template<>
+class ParameterSetEx<wchar_t*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(WCHAR_TYPE, address) {}
+};
+
+template<>
+class ParameterSetEx<wchar_t const*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(WCHAR_TYPE, address) {}
+};
+
+
+template<>
+class ParameterSetEx<unsigned long> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(ULONG_TYPE, address) {}
+};
+
+template<>
+class ParameterSetEx<UNICODE_STRING> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(UNISTR_TYPE, address) {}
+};
+
+template <typename T>
+ParameterSet ParamPickerMake(T& parameter) {
+ return ParameterSetEx<T>(&parameter);
+};
+
+struct CountedParameterSetBase {
+ int count;
+ ParameterSet parameters[1];
+};
+
+// This template defines the actual list of policy parameters for a given
+// interception.
+// Warning: This template stores the address to the actual variables, in
+// other words, the values are not copied.
+template <typename T>
+struct CountedParameterSet {
+ CountedParameterSet() : count(T::PolParamLast) {}
+
+ ParameterSet& operator[](typename T::Args n) {
+ return parameters[n];
+ }
+
+ CountedParameterSetBase* GetBase() {
+ return reinterpret_cast<CountedParameterSetBase*>(this);
+ }
+
+ int count;
+ ParameterSet parameters[T::PolParamLast];
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
diff --git a/chromium/sandbox/win/src/policy_engine_processor.cc b/chromium/sandbox/win/src/policy_engine_processor.cc
new file mode 100644
index 00000000000..7ca25b2ed2e
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_engine_processor.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2006-2008 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/policy_engine_processor.h"
+
+namespace sandbox {
+
+void PolicyProcessor::SetInternalState(size_t index, EvalResult result) {
+ state_.current_index_ = index;
+ state_.current_result_ = result;
+}
+
+EvalResult PolicyProcessor::GetAction() const {
+ return state_.current_result_;
+}
+
+// Decides if an opcode can be skipped (not evaluated) or not. The function
+// takes as inputs the opcode and the current evaluation context and returns
+// true if the opcode should be skipped or not and also can set keep_skipping
+// to false to signal that the current instruction should be skipped but not
+// the next after the current one.
+bool SkipOpcode(const PolicyOpcode& opcode, MatchContext* context,
+ bool* keep_skipping) {
+ if (opcode.IsAction()) {
+ uint32 options = context->options;
+ context->Clear();
+ *keep_skipping = false;
+ return (kPolUseOREval != options);
+ }
+ *keep_skipping = true;
+ return true;
+}
+
+PolicyResult PolicyProcessor::Evaluate(uint32 options,
+ ParameterSet* parameters,
+ size_t param_count) {
+ if (NULL == policy_) {
+ return NO_POLICY_MATCH;
+ }
+ if (0 == policy_->opcode_count) {
+ return NO_POLICY_MATCH;
+ }
+ if (!(kShortEval & options)) {
+ return POLICY_ERROR;
+ }
+
+ MatchContext context;
+ bool evaluation = false;
+ bool skip_group = false;
+ SetInternalState(0, EVAL_FALSE);
+ size_t count = policy_->opcode_count;
+
+ // Loop over all the opcodes Evaluating in sequence. Since we only support
+ // short circuit evaluation, we stop as soon as we find an 'action' opcode
+ // and the current evaluation is true.
+ //
+ // Skipping opcodes can happen when we are in AND mode (!kPolUseOREval) and
+ // have got EVAL_FALSE or when we are in OR mode (kPolUseOREval) and got
+ // EVAL_TRUE. Skipping will stop at the next action opcode or at the opcode
+ // after the action depending on kPolUseOREval.
+
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode& opcode = policy_->opcodes[ix];
+ // Skipping block.
+ if (skip_group) {
+ if (SkipOpcode(opcode, &context, &skip_group)) {
+ continue;
+ }
+ }
+ // Evaluation block.
+ EvalResult result = opcode.Evaluate(parameters, param_count, &context);
+ switch (result) {
+ case EVAL_FALSE:
+ evaluation = false;
+ if (kPolUseOREval != context.options) {
+ skip_group = true;
+ }
+ break;
+ case EVAL_ERROR:
+ if (kStopOnErrors & options) {
+ return POLICY_ERROR;
+ }
+ break;
+ case EVAL_TRUE:
+ evaluation = true;
+ if (kPolUseOREval == context.options) {
+ skip_group = true;
+ }
+ break;
+ default:
+ // We have evaluated an action.
+ SetInternalState(ix, result);
+ return POLICY_MATCH;
+ }
+ }
+
+ if (evaluation) {
+ // Reaching the end of the policy with a positive evaluation is probably
+ // an error: we did not find a final action opcode?
+ return POLICY_ERROR;
+ }
+ return NO_POLICY_MATCH;
+}
+
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_engine_processor.h b/chromium/sandbox/win/src/policy_engine_processor.h
new file mode 100644
index 00000000000..85c999e27e9
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_engine_processor.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2010 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_POLICY_ENGINE_PROCESSOR_H__
+#define SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+
+namespace sandbox {
+
+// This header contains the core policy evaluator. In its simplest form
+// it evaluates a stream of opcodes assuming that they are laid out in
+// memory as opcode groups.
+//
+// An opcode group has N comparison opcodes plus 1 action opcode. For
+// example here we have 3 opcode groups (A, B,C):
+//
+// [comparison 1] <-- group A start
+// [comparison 2]
+// [comparison 3]
+// [action A ]
+// [comparison 1] <-- group B start
+// [action B ]
+// [comparison 1] <-- group C start
+// [comparison 2]
+// [action C ]
+//
+// The opcode evaluator proceeds from the top, evaluating each opcode in
+// sequence. An opcode group is evaluated until the first comparison that
+// returns false. At that point the rest of the group is skipped and evaluation
+// resumes with the first comparison of the next group. When all the comparisons
+// in a group have evaluated to true and the action is reached. The group is
+// considered a matching group.
+//
+// In the 'ShortEval' mode evaluation stops when it reaches the end or the first
+// matching group. The action opcode from this group is the resulting policy
+// action.
+//
+// In the 'RankedEval' mode evaluation stops only when it reaches the end of the
+// the opcode stream. In the process all matching groups are saved and at the
+// end the 'best' group is selected (what makes the best is TBD) and the action
+// from this group is the resulting policy action.
+//
+// As explained above, the policy evaluation of a group is a logical AND of
+// the evaluation of each opcode. However an opcode can request kPolUseOREval
+// which makes the evaluation to use logical OR. Given that each opcode can
+// request its evaluation result to be negated with kPolNegateEval you can
+// achieve the negation of the total group evaluation. This means that if you
+// need to express:
+// if (!(c1 && c2 && c3))
+// You can do it by:
+// if ((!c1) || (!c2) || (!c3))
+//
+
+// Possible outcomes of policy evaluation.
+enum PolicyResult {
+ NO_POLICY_MATCH,
+ POLICY_MATCH,
+ POLICY_ERROR
+};
+
+// Policy evaluation flags
+// TODO(cpu): implement the options 0 & 4.
+//
+// Stop evaluating as soon as an error is encountered.
+const uint32 kStopOnErrors = 0;
+// Ignore all non fatal opcode evaluation errors.
+const uint32 kIgnoreErrors = 1;
+// Short-circuit evaluation: Only evaluate until opcode group that
+// evaluated to true has been found.
+const uint32 kShortEval = 2;
+// Discussed briefly at the policy design meeting. It will evaluate
+// all rules and then return the 'best' rule that evaluated true.
+const uint32 kRankedEval = 4;
+
+// This class evaluates a policy-opcode stream given the memory where the
+// opcodes are and an input 'parameter set'.
+//
+// This class is designed to be callable from interception points
+// as low as the NtXXXX service level (it is not currently safe, but
+// it is designed to be made safe).
+//
+// Its usage in an interception is:
+//
+// POLPARAMS_BEGIN(eval_params)
+// POLPARAM(param1)
+// POLPARAM(param2)
+// POLPARAM(param3)
+// POLPARAM(param4)
+// POLPARAM(param5)
+// POLPARAMS_END;
+//
+// PolicyProcessor pol_evaluator(policy_memory);
+// PolicyResult pr = pol_evaluator.Evaluate(ShortEval, eval_params,
+// _countof(eval_params));
+// if (NO_POLICY_MATCH == pr) {
+// EvalResult policy_action = pol_evaluator.GetAction();
+// // apply policy here...
+// }
+//
+// Where the POLPARAM() arguments are derived from the intercepted function
+// arguments, and represent all the 'interesting' policy inputs, and
+// policy_memory is a memory buffer containing the opcode stream that is the
+// relevant policy for this intercept.
+class PolicyProcessor {
+ public:
+ // policy_buffer contains opcodes made with OpcodeFactory. They are usually
+ // created in the broker process and evaluated in the target process.
+
+ // This constructor is just a variant of the previous constructor.
+ explicit PolicyProcessor(PolicyBuffer* policy)
+ : policy_(policy) {
+ SetInternalState(0, EVAL_FALSE);
+ }
+
+ // Evaluates a policy-opcode stream. See the comments at the top of this
+ // class for more info. Returns POLICY_MATCH if a rule set was found that
+ // matches an active policy.
+ PolicyResult Evaluate(uint32 options,
+ ParameterSet* parameters,
+ size_t parameter_count);
+
+ // If the result of Evaluate() was POLICY_MATCH, calling this function returns
+ // the recommended policy action.
+ EvalResult GetAction() const;
+
+ private:
+ struct {
+ size_t current_index_;
+ EvalResult current_result_;
+ } state_;
+
+ // Sets the currently matching action result.
+ void SetInternalState(size_t index, EvalResult result);
+
+ PolicyBuffer* policy_;
+ DISALLOW_COPY_AND_ASSIGN(PolicyProcessor);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
diff --git a/chromium/sandbox/win/src/policy_engine_unittest.cc b/chromium/sandbox/win/src/policy_engine_unittest.cc
new file mode 100644
index 00000000000..e6c343500fe
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_engine_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2006-2008 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/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = {
+#define POLPARAM(p) sandbox::ParamPickerMake(p),
+#define POLPARAMS_END }
+
+namespace sandbox {
+
+bool SetupNtdllImports();
+
+TEST(PolicyEngineTest, Rules1) {
+ SetupNtdllImports();
+
+ // Construct two policy rules that say:
+ //
+ // #1
+ // If the path is c:\\documents and settings\\* AND
+ // If the creation mode is 'open existing' AND
+ // If the security descriptor is null THEN
+ // Ask the broker.
+ //
+ // #2
+ // If the security descriptor is null AND
+ // If the path ends with *.txt AND
+ // If the creation mode is not 'create new' THEN
+ // return Access Denied.
+
+ enum FileCreateArgs {
+ FileNameArg,
+ CreationDispositionArg,
+ FlagsAndAttributesArg,
+ SecurityAttributes
+ };
+
+ const size_t policy_sz = 1024;
+ PolicyBuffer* policy = reinterpret_cast<PolicyBuffer*>(new char[policy_sz]);
+ OpcodeFactory opcode_maker(policy, policy_sz - 0x40);
+
+ // Add rule set #1
+ opcode_maker.MakeOpWStringMatch(FileNameArg,
+ L"c:\\documents and settings\\",
+ 0, CASE_INSENSITIVE, kPolNone);
+ opcode_maker.MakeOpNumberMatch(CreationDispositionArg, OPEN_EXISTING,
+ kPolNone);
+ opcode_maker.MakeOpVoidPtrMatch(SecurityAttributes, (void*)NULL,
+ kPolNone);
+ opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+
+ // Add rule set #2
+ opcode_maker.MakeOpWStringMatch(FileNameArg, L".TXT",
+ kSeekToEnd, CASE_INSENSITIVE, kPolNone);
+ opcode_maker.MakeOpNumberMatch(CreationDispositionArg, CREATE_NEW,
+ kPolNegateEval);
+ opcode_maker.MakeOpAction(FAKE_ACCESS_DENIED, kPolNone);
+ policy->opcode_count = 7;
+
+ 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;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename)
+ POLPARAM(creation_mode)
+ POLPARAM(flags)
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult pr;
+ PolicyProcessor pol_ev(policy);
+
+ // Test should match the first rule set.
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ // Test should still match the first rule set.
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ // Changing creation_mode such that evaluation should not match any rule.
+ creation_mode = CREATE_NEW;
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, pr);
+
+ // Changing creation_mode such that evaluation should match rule #2.
+ creation_mode = OPEN_ALWAYS;
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(FAKE_ACCESS_DENIED, pol_ev.GetAction());
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_low_level.cc b/chromium/sandbox/win/src/policy_low_level.cc
new file mode 100644
index 00000000000..686caa197b0
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_low_level.cc
@@ -0,0 +1,348 @@
+// Copyright (c) 2006-2008 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 <string>
+#include <map>
+
+#include "sandbox/win/src/policy_low_level.h"
+#include "base/basictypes.h"
+
+namespace {
+
+ // A single rule can use at most this amount of memory.
+ const size_t kRuleBufferSize = 1024*4;
+
+ // The possible states of the string matching opcode generator.
+ enum {
+ PENDING_NONE,
+ PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode.
+ PENDING_QMARK, // Have seen an '?' but have not generated an opcode.
+ };
+
+ // The category of the last character seen by the string matching opcode
+ // generator.
+ const uint32 kLastCharIsNone = 0;
+ const uint32 kLastCharIsAlpha = 1;
+ const uint32 kLastCharIsWild = 2;
+ const uint32 kLastCharIsAsterisk = kLastCharIsWild + 4;
+ const uint32 kLastCharIsQuestionM = kLastCharIsWild + 8;
+}
+
+namespace sandbox {
+
+// Adding a rule is nothing more than pushing it into an stl container. Done()
+// is called for the rule in case the code that made the rule in the first
+// place has not done it.
+bool LowLevelPolicy::AddRule(int service, PolicyRule* rule) {
+ if (!rule->Done()) {
+ return false;
+ }
+
+ PolicyRule* local_rule = new PolicyRule(*rule);
+ RuleNode node = {local_rule, service};
+ rules_.push_back(node);
+ return true;
+}
+
+LowLevelPolicy::~LowLevelPolicy() {
+ // Delete all the rules.
+ typedef std::list<RuleNode> RuleNodes;
+ for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
+ delete it->rule;
+ }
+}
+
+// Here is where the heavy byte shuffling is done. We take all the rules and
+// 'compile' them into a single memory region. Now, the rules are in random
+// order so the first step is to reorganize them into a stl map that is keyed
+// by the service id and as a value contains a list with all the rules that
+// belong to that service. Then we enter the big for-loop where we carve a
+// memory zone for the opcodes and the data and call RebindCopy on each rule
+// so they all end up nicely packed in the policy_store_.
+bool LowLevelPolicy::Done() {
+ typedef std::list<RuleNode> RuleNodes;
+ typedef std::list<const PolicyRule*> RuleList;
+ typedef std::map<uint32, RuleList> Mmap;
+ Mmap mmap;
+
+ for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
+ mmap[it->service].push_back(it->rule);
+ }
+
+ PolicyBuffer* current_buffer = &policy_store_->data[0];
+ char* buffer_end = reinterpret_cast<char*>(current_buffer) +
+ policy_store_->data_size;
+ size_t avail_size = policy_store_->data_size;
+
+ for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) {
+ uint32 service = (*it).first;
+ if (service >= kMaxServiceCount) {
+ return false;
+ }
+ policy_store_->entry[service] = current_buffer;
+
+ RuleList::iterator rules_it = (*it).second.begin();
+ RuleList::iterator rules_it_end = (*it).second.end();
+
+ size_t svc_opcode_count = 0;
+
+ for (; rules_it != rules_it_end; ++rules_it) {
+ const PolicyRule* rule = (*rules_it);
+ size_t op_count = rule->GetOpcodeCount();
+
+ size_t opcodes_size = op_count * sizeof(PolicyOpcode);
+ if (avail_size < opcodes_size) {
+ return false;
+ }
+ size_t data_size = avail_size - opcodes_size;
+ PolicyOpcode* opcodes_start = &current_buffer->opcodes[svc_opcode_count];
+ if (!rule->RebindCopy(opcodes_start, opcodes_size,
+ buffer_end, &data_size)) {
+ return false;
+ }
+ size_t used = avail_size - data_size;
+ buffer_end -= used;
+ avail_size -= used;
+ svc_opcode_count += op_count;
+ }
+
+ current_buffer->opcode_count += svc_opcode_count;
+ size_t policy_byte_count = (svc_opcode_count * sizeof(PolicyOpcode))
+ / sizeof(current_buffer[0]);
+ current_buffer = &current_buffer[policy_byte_count + 1];
+ }
+
+ return true;
+}
+
+PolicyRule::PolicyRule(EvalResult action)
+ : action_(action), done_(false) {
+ char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize];
+ buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
+ buffer_->opcode_count = 0;
+ opcode_factory_ = new OpcodeFactory(buffer_,
+ kRuleBufferSize + sizeof(PolicyOpcode));
+}
+
+PolicyRule::PolicyRule(const PolicyRule& other) {
+ if (this == &other)
+ return;
+ action_ = other.action_;
+ done_ = other.done_;
+ size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize;
+ char* memory = new char[buffer_size];
+ buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
+ memcpy(buffer_, other.buffer_, buffer_size);
+
+ char* opcode_buffer = reinterpret_cast<char*>(&buffer_->opcodes[0]);
+ char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)];
+ opcode_factory_ =
+ new OpcodeFactory(next_opcode, other.opcode_factory_->memory_size());
+}
+
+// This function get called from a simple state machine implemented in
+// AddStringMatch() which passes the current state (in state) and it passes
+// true in last_call if AddStringMatch() has finished processing the input
+// pattern string and this would be the last call to generate any pending
+// opcode. The skip_count is the currently accumulated number of '?' seen so
+// far and once the associated opcode is generated this function sets it back
+// to zero.
+bool PolicyRule::GenStringOpcode(RuleType rule_type,
+ StringMatchOptions match_opts,
+ uint16 parameter, int state, bool last_call,
+ int* skip_count, std::wstring* fragment) {
+
+ // The last opcode must:
+ // 1) Always clear the context.
+ // 2) Preserve the negation.
+ // 3) Remove the 'OR' mode flag.
+ uint32 options = kPolNone;
+ if (last_call) {
+ if (IF_NOT == rule_type) {
+ options = kPolClearContext | kPolNegateEval;
+ } else {
+ options = kPolClearContext;
+ }
+ } else if (IF_NOT == rule_type) {
+ options = kPolUseOREval | kPolNegateEval;
+ }
+
+ PolicyOpcode* op = NULL;
+
+ // The fragment string contains the accumulated characters to match with, it
+ // never contains wildcards (unless they have been escaped) and while there
+ // is no fragment there is no new string match opcode to generate.
+ if (fragment->empty()) {
+ // There is no new opcode to generate but in the last call we have to fix
+ // the previous opcode because it was really the last but we did not know
+ // it at that time.
+ if (last_call && (buffer_->opcode_count > 0)) {
+ op = &buffer_->opcodes[buffer_->opcode_count - 1];
+ op->SetOptions(options);
+ }
+ return true;
+ }
+
+ if (PENDING_ASTERISK == state) {
+ if (last_call) {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ kSeekToEnd, match_opts,
+ options);
+ } else {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ kSeekForward, match_opts,
+ options);
+ }
+
+ } else if (PENDING_QMARK == state) {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ *skip_count, match_opts, options);
+ *skip_count = 0;
+ } else {
+ if (last_call) {
+ match_opts = static_cast<StringMatchOptions>(EXACT_LENGHT | match_opts);
+ }
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0,
+ match_opts, options);
+ }
+ if (NULL == op) {
+ return false;
+ }
+ ++buffer_->opcode_count;
+ fragment->clear();
+ return true;
+}
+
+bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter,
+ const wchar_t* string,
+ StringMatchOptions match_opts) {
+ if (done_) {
+ // Do not allow to add more rules after generating the action opcode.
+ return false;
+ }
+
+ const wchar_t* current_char = string;
+ uint32 last_char = kLastCharIsNone;
+ int state = PENDING_NONE;
+ int skip_count = 0; // counts how many '?' we have seen in a row.
+ std::wstring fragment; // accumulates the non-wildcard part of the string.
+
+ while (L'\0' != *current_char) {
+ switch (*current_char) {
+ case L'*':
+ if (kLastCharIsWild & last_char) {
+ // '**' and '&*' is an error.
+ return false;
+ }
+ if (!GenStringOpcode(rule_type, match_opts, parameter,
+ state, false, &skip_count, &fragment)) {
+ return false;
+ }
+ last_char = kLastCharIsAsterisk;
+ state = PENDING_ASTERISK;
+ break;
+ case L'?':
+ if (kLastCharIsAsterisk == last_char) {
+ // '*?' is an error.
+ return false;
+ }
+ if (!GenStringOpcode(rule_type, match_opts, parameter,
+ state, false, &skip_count, &fragment)) {
+ return false;
+ }
+ ++skip_count;
+ last_char = kLastCharIsQuestionM;
+ state = PENDING_QMARK;
+ break;
+ case L'/':
+ // Note: "/?" is an escaped '?'. Eat the slash and fall through.
+ if (L'?' == current_char[1]) {
+ ++current_char;
+ }
+ default:
+ fragment += *current_char;
+ last_char = kLastCharIsAlpha;
+ }
+ ++current_char;
+ }
+
+ if (!GenStringOpcode(rule_type, match_opts, parameter,
+ state, true, &skip_count, &fragment)) {
+ return false;
+ }
+ return true;
+}
+
+bool PolicyRule::AddNumberMatch(RuleType rule_type, int16 parameter,
+ unsigned long number, RuleOp comparison_op) {
+ if (done_) {
+ // Do not allow to add more rules after generating the action opcode.
+ return false;
+ }
+ uint32 opts = (rule_type == IF_NOT)? kPolNegateEval : kPolNone;
+
+ if (EQUAL == comparison_op) {
+ if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) {
+ return false;
+ }
+ } else if (AND == comparison_op) {
+ if (NULL == opcode_factory_->MakeOpUlongAndMatch(parameter, number, opts)) {
+ return false;
+ }
+ }
+ ++buffer_->opcode_count;
+ return true;
+}
+
+bool PolicyRule::Done() {
+ if (done_) {
+ return true;
+ }
+ if (NULL == opcode_factory_->MakeOpAction(action_, kPolNone)) {
+ return false;
+ }
+ ++buffer_->opcode_count;
+ done_ = true;
+ return true;
+}
+
+bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size,
+ char* data_start, size_t* data_size) const {
+ size_t count = buffer_->opcode_count;
+ for (size_t ix = 0; ix != count; ++ix) {
+ if (opcode_size < sizeof(PolicyOpcode)) {
+ return false;
+ }
+ PolicyOpcode& opcode = buffer_->opcodes[ix];
+ *opcode_start = opcode;
+ if (OP_WSTRING_MATCH == opcode.GetID()) {
+ // For this opcode argument 0 is a delta to the string and argument 1
+ // is the length (in chars) of the string.
+ const wchar_t* str = opcode.GetRelativeString(0);
+ size_t str_len;
+ opcode.GetArgument(1, &str_len);
+ str_len = str_len * sizeof(wchar_t);
+ if ((*data_size) < str_len) {
+ return false;
+ }
+ *data_size -= str_len;
+ data_start -= str_len;
+ memcpy(data_start, str, str_len);
+ // Recompute the string displacement
+ ptrdiff_t delta = data_start - reinterpret_cast<char*>(opcode_start);
+ opcode_start->SetArgument(0, delta);
+ }
+ ++opcode_start;
+ opcode_size -= sizeof(PolicyOpcode);
+ }
+
+ return true;
+}
+
+PolicyRule::~PolicyRule() {
+ delete [] reinterpret_cast<char*>(buffer_);
+ delete opcode_factory_;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_low_level.h b/chromium/sandbox/win/src/policy_low_level.h
new file mode 100644
index 00000000000..025a133ec6a
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_low_level.h
@@ -0,0 +1,182 @@
+// Copyright (c) 2006-2008 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_POLICY_LOW_LEVEL_H__
+#define SANDBOX_SRC_POLICY_LOW_LEVEL_H__
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+
+// Low level policy classes.
+// Built on top of the PolicyOpcode and OpcodeFatory, the low level policy
+// provides a way to define rules on strings and numbers but it is unaware
+// of Windows specific details or how the Interceptions must be set up.
+// To use these classes you construct one or more rules and add them to the
+// LowLevelPolicy object like this:
+//
+// PolicyRule rule1(ASK_BROKER);
+// rule1.AddStringMatch(IF, 0, L"\\\\/?/?\\c:\\*Microsoft*\\*.exe", true);
+// rule1.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL);
+// rule1.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL);
+//
+// PolicyRule rule2(FAKE_SUCCESS);
+// rule2.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", false));
+// rule2.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL));
+//
+// LowLevelPolicy policyGen(*policy_memory);
+// policyGen.AddRule(kNtCreateFileSvc, &rule1);
+// policyGen.AddRule(kNtCreateFileSvc, &rule2);
+// policyGen.Done();
+//
+// At this point (error checking omitted) the policy_memory can be copied
+// to the target process where it can be evaluated.
+
+namespace sandbox {
+
+// TODO(cpu): Move this constant to crosscall_client.h.
+const size_t kMaxServiceCount = 32;
+COMPILE_ASSERT(IPC_LAST_TAG <= kMaxServiceCount, kMaxServiceCount_is_too_low);
+
+// Defines the memory layout of the policy. This memory is filled by
+// LowLevelPolicy object.
+// For example:
+//
+// [Service 0] --points to---\
+// [Service 1] --------------|-----\
+// ...... | |
+// [Service N] | |
+// [data_size] | |
+// [Policy Buffer 0] <-------/ |
+// [opcodes of] |
+// ....... |
+// [Policy Buffer 1] <-------------/
+// [opcodes]
+// .......
+// .......
+// [Policy Buffer N]
+// [opcodes]
+// .......
+// <possibly unused space here>
+// .......
+// [opcode string ]
+// [opcode string ]
+// .......
+// [opcode string ]
+struct PolicyGlobal {
+ PolicyBuffer* entry[kMaxServiceCount];
+ size_t data_size;
+ PolicyBuffer data[1];
+};
+
+class PolicyRule;
+
+// Provides the means to collect rules into a policy store (memory)
+class LowLevelPolicy {
+ public:
+ // policy_store: must contain allocated memory and the internal
+ // size fields set to correct values.
+ explicit LowLevelPolicy(PolicyGlobal* policy_store)
+ : policy_store_(policy_store) {
+ }
+
+ // Destroys all the policy rules.
+ ~LowLevelPolicy();
+
+ // Adds a rule to be generated when Done() is called.
+ // service: The id of the service that this rule is associated with,
+ // for example the 'Open Thread' service or the "Create File" service.
+ // returns false on error.
+ bool AddRule(int service, PolicyRule* rule);
+
+ // Generates all the rules added with AddRule() into the memory area
+ // passed on the constructor. Returns false on error.
+ bool Done();
+
+ private:
+ struct RuleNode {
+ const PolicyRule* rule;
+ int service;
+ };
+ std::list<RuleNode> rules_;
+ PolicyGlobal* policy_store_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LowLevelPolicy);
+};
+
+// There are 'if' rules and 'if not' comparisons
+enum RuleType {
+ IF = 0,
+ IF_NOT = 1,
+};
+
+// Possible comparisons for numbers
+enum RuleOp {
+ EQUAL,
+ AND,
+ RANGE // TODO(cpu): Implement this option.
+};
+
+// Provides the means to collect a set of comparisons into a single
+// rule and its associated action.
+class PolicyRule {
+ friend class LowLevelPolicy;
+
+ public:
+ explicit PolicyRule(EvalResult action);
+ PolicyRule(const PolicyRule& other);
+ ~PolicyRule();
+
+ // Adds a string comparison to the rule.
+ // rule_type: possible values are IF and IF_NOT.
+ // parameter: the expected index of the argument for this rule. For example
+ // in a 'create file' service the file name argument can be at index 0.
+ // string: is the desired matching pattern.
+ // match_opts: if the pattern matching is case sensitive or not.
+ bool AddStringMatch(RuleType rule_type, int16 parameter,
+ const wchar_t* string, StringMatchOptions match_opts);
+
+ // Adds a number match comparison to the rule.
+ // rule_type: possible values are IF and IF_NOT.
+ // parameter: the expected index of the argument for this rule.
+ // number: the value to compare the input to.
+ // comparison_op: the comparison kind (equal, logical and, etc).
+ bool AddNumberMatch(RuleType rule_type, int16 parameter,
+ unsigned long number, RuleOp comparison_op);
+
+ // Returns the number of opcodes generated so far.
+ size_t GetOpcodeCount() const {
+ return buffer_->opcode_count;
+ }
+
+ // Called when there is no more comparisons to add. Internally it generates
+ // the last opcode (the action opcode). Returns false if this operation fails.
+ bool Done();
+
+ private:
+ void operator=(const PolicyRule&);
+ // Called in a loop from AddStringMatch to generate the required string
+ // match opcodes. rule_type, match_opts and parameter are the same as
+ // in AddStringMatch.
+ bool GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts,
+ uint16 parameter, int state, bool last_call,
+ int* skip_count, std::wstring* fragment);
+
+ // Loop over all generated opcodes and copy them to increasing memory
+ // addresses from opcode_start and copy the extra data (strings usually) into
+ // decreasing addresses from data_start. Extra data is only present in the
+ // string evaluation opcodes.
+ bool RebindCopy(PolicyOpcode* opcode_start, size_t opcode_size,
+ char* data_start, size_t* data_size) const;
+ PolicyBuffer* buffer_;
+ OpcodeFactory* opcode_factory_;
+ EvalResult action_;
+ bool done_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_LOW_LEVEL_H__
diff --git a/chromium/sandbox/win/src/policy_low_level_unittest.cc b/chromium/sandbox/win/src/policy_low_level_unittest.cc
new file mode 100644
index 00000000000..2b5d0f7a6ec
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_low_level_unittest.cc
@@ -0,0 +1,617 @@
+// Copyright (c) 2006-2008 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/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = {
+#define POLPARAM(p) sandbox::ParamPickerMake(p),
+#define POLPARAMS_END }
+
+namespace sandbox {
+
+bool SetupNtdllImports();
+
+// Testing that we allow opcode generation on valid string patterns.
+TEST(PolicyEngineTest, StringPatternsOK) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\adobe\\ver??\\", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"*.tmp", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*.doc", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\windows\\*", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"d:\\adobe\\acrobat.exe",
+ CASE_SENSITIVE));
+}
+
+// Testing that we signal invalid string patterns.
+TEST(PolicyEngineTest, StringPatternsBAD) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"one**two", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"**three", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"five?six*?seven", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"eight?*nine", CASE_SENSITIVE));
+}
+
+// Helper function to allocate space (on the heap) for policy.
+PolicyGlobal* MakePolicyMemory() {
+ const size_t kTotalPolicySz = 4096*8;
+ char* mem = new char[kTotalPolicySz];
+ memset(mem, 0, kTotalPolicySz);
+ PolicyGlobal* policy = reinterpret_cast<PolicyGlobal*>(mem);
+ policy->data_size = kTotalPolicySz - sizeof(PolicyGlobal);
+ return policy;
+}
+
+// The simplest test using LowLevelPolicy it should test a single opcode which
+// does a exact string comparison.
+TEST(PolicyEngineTest, SimpleStrMatch) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"z:\\Directory\\domo.txt",
+ CASE_INSENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const uint32 kFakeService = 2;
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* filename = L"Z:\\Directory\\domo.txt";
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kFakeService]);
+
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"Z:\\Directory\\domo.txt.tmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatch) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\",
+ CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const uint32 kFakeService = 2;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* filename = NULL;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kFakeService]);
+
+ filename = L"c:\\Microsoft\\";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*",
+ CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const uint32 kFakeService = 3;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* filename = NULL;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kFakeService]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*.txt",
+ CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const uint32 kFakeService = 3;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* filename = NULL;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kFakeService]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\domo.bmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*",
+ CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const uint32 kFakeService = 3;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* filename = NULL;
+ unsigned long access = 0;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(access) // Argument 1
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kFakeService]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Micronesia\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL));
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\GoogleV?\\*.txt",
+ CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 2, 66, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const uint32 kFakeService = 3;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* filename = NULL;
+ unsigned long access = 0;
+ unsigned long sharing = 66;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(access) // Argument 1
+ POLPARAM(sharing) // Argument 2
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kFakeService]);
+
+ filename = L"c:\\GoogleV2\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\GoogleV2\\domo.bmp";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\GoogleV23\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+
+ filename = L"c:\\GoogleV2\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Google\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Micronesia\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\GoogleV2\\domo.bmp";
+ access = 24;
+ sharing = 0;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+// Testing one single rule in one single service. The service is made to
+// resemble NtCreateFile.
+TEST(PolicyEngineTest, OneRuleTest) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*Microsoft*\\*.txt",
+ CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+
+ const uint32 kNtFakeCreateFile = 7;
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ 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;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(creation_mode) // Argument 1
+ POLPARAM(flags) // Argument 2
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[kNtFakeCreateFile]);
+
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ creation_mode = CREATE_ALWAYS;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ creation_mode = OPEN_EXISTING;
+ filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt.tmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags = FILE_ATTRIBUTE_DEVICE;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Other\\Macrosoft\\Another file.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Microsoft\\1.txt";
+ flags = FILE_ATTRIBUTE_NORMAL;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\1.ttt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+// Testing 3 rules in 3 services. Two of the services resemble File services.
+TEST(PolicyEngineTest, ThreeRulesTest) {
+ SetupNtdllImports();
+ PolicyRule pr_pipe(FAKE_SUCCESS);
+ EXPECT_TRUE(pr_pipe.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL));
+ EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc1 = pr_pipe.GetOpcodeCount();
+ EXPECT_EQ(3, opc1);
+
+ PolicyRule pr_dump(ASK_BROKER);
+ EXPECT_TRUE(pr_dump.AddStringMatch(IF, 0, L"\\\\/?/?\\*\\Crash Reports\\*",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 1, CREATE_ALWAYS, EQUAL));
+ EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc2 = pr_dump.GetOpcodeCount();
+ EXPECT_EQ(4, opc2);
+
+ PolicyRule pr_winexe(SIGNAL_ALARM);
+ EXPECT_TRUE(pr_winexe.AddStringMatch(IF, 0, L"\\\\/?/?\\C:\\Windows\\*.exe",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_winexe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc3 = pr_winexe.GetOpcodeCount();
+ EXPECT_EQ(3, opc3);
+
+ PolicyRule pr_adobe(GIVE_CACHED);
+ EXPECT_TRUE(pr_adobe.AddStringMatch(IF, 0, L"c:\\adobe\\ver?.?\\",
+ CASE_SENSITIVE));
+ EXPECT_TRUE(pr_adobe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc4 = pr_adobe.GetOpcodeCount();
+ EXPECT_EQ(4, opc4);
+
+ PolicyRule pr_none(GIVE_FIRST);
+ EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_READONLY, AND));
+ EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_SYSTEM, AND));
+
+ size_t opc5 = pr_none.GetOpcodeCount();
+ EXPECT_EQ(2, opc5);
+
+ PolicyGlobal* policy = MakePolicyMemory();
+
+ const uint32 kNtFakeNone = 4;
+ const uint32 kNtFakeCreateFile = 5;
+ const uint32 kNtFakeOpenFile = 6;
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_pipe));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_dump));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_winexe));
+
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_adobe));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_pipe));
+
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeNone, &pr_none));
+
+ EXPECT_TRUE(policyGen.Done());
+
+ // Inspect the policy structure manually.
+ EXPECT_TRUE(NULL == policy->entry[0]);
+ EXPECT_TRUE(NULL == policy->entry[1]);
+ EXPECT_TRUE(NULL == policy->entry[2]);
+ EXPECT_TRUE(NULL == policy->entry[3]);
+ EXPECT_TRUE(NULL != policy->entry[4]); // kNtFakeNone.
+ EXPECT_TRUE(NULL != policy->entry[5]); // kNtFakeCreateFile.
+ EXPECT_TRUE(NULL != policy->entry[6]); // kNtFakeOpenFile.
+ EXPECT_TRUE(NULL == policy->entry[7]);
+
+ // The total per service opcode counts now must take in account one
+ // extra opcode (action opcode) per rule.
+ ++opc1;
+ ++opc2;
+ ++opc3;
+ ++opc4;
+ ++opc5;
+
+ size_t tc1 = policy->entry[kNtFakeNone]->opcode_count;
+ size_t tc2 = policy->entry[kNtFakeCreateFile]->opcode_count;
+ size_t tc3 = policy->entry[kNtFakeOpenFile]->opcode_count;
+
+ EXPECT_EQ(opc5, tc1);
+ EXPECT_EQ((opc1 + opc2 + opc3), tc2);
+ EXPECT_EQ((opc1 + opc4), tc3);
+
+ // Check the type of the first and last opcode of each service.
+
+ EXPECT_EQ(OP_ULONG_AND_MATCH, policy->entry[kNtFakeNone]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[kNtFakeNone]->opcodes[tc1-1].GetID());
+ EXPECT_EQ(OP_WSTRING_MATCH,
+ policy->entry[kNtFakeCreateFile]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION,
+ policy->entry[kNtFakeCreateFile]->opcodes[tc2-1].GetID());
+ EXPECT_EQ(OP_WSTRING_MATCH,
+ policy->entry[kNtFakeOpenFile]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[kNtFakeOpenFile]->opcodes[tc3-1].GetID());
+
+ // Test the policy evaluation.
+
+ wchar_t* filename = L"";
+ unsigned long creation_mode = OPEN_EXISTING;
+ unsigned long flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = NULL;
+
+ POLPARAMS_BEGIN(params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(creation_mode) // Argument 1
+ POLPARAM(flags) // Argument 2
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor eval_CreateFile(policy->entry[kNtFakeCreateFile]);
+ PolicyProcessor eval_OpenFile(policy->entry[kNtFakeOpenFile]);
+ PolicyProcessor eval_None(policy->entry[kNtFakeNone]);
+
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\c:\\Windows\\System32\\calc.exe";
+ flags = FILE_ATTRIBUTE_SYSTEM;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags += FILE_ATTRIBUTE_READONLY;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(GIVE_FIRST, eval_None.GetAction());
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags = FILE_ATTRIBUTE_NORMAL;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(SIGNAL_ALARM, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\adobe\\ver3.2\\temp";
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(GIVE_CACHED, eval_OpenFile.GetAction());
+
+ filename = L"c:\\adobe\\ver3.22\\temp";
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\c:\\some path\\other path\\crash reports\\some path";
+ creation_mode = CREATE_ALWAYS;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\Pipe\\Chrome.12345";
+ creation_mode = OPEN_EXISTING;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(FAKE_SUCCESS, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(FAKE_SUCCESS, eval_OpenFile.GetAction());
+
+ delete [] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, PolicyRuleCopyConstructorTwoStrings) {
+ SetupNtdllImports();
+ // Both pr_orig and pr_copy should allow hello.* but not *.txt files.
+ PolicyRule pr_orig(ASK_BROKER);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF, 0, L"hello.*", CASE_SENSITIVE));
+
+ PolicyRule pr_copy(pr_orig);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_copy.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(1, &pr_orig));
+ EXPECT_TRUE(policyGen.AddRule(2, &pr_copy));
+ EXPECT_TRUE(policyGen.Done());
+
+ wchar_t* name = NULL;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(name)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev_orig(policy->entry[1]);
+ name = L"domo.txt";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_orig.GetAction());
+
+ PolicyProcessor pol_ev_copy(policy->entry[2]);
+ name = L"domo.txt";
+ result = pol_ev_copy.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_copy.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_copy.GetAction());
+}
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_opcodes_unittest.cc b/chromium/sandbox/win/src/policy_opcodes_unittest.cc
new file mode 100644
index 00000000000..e1a7ad63d20
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_opcodes_unittest.cc
@@ -0,0 +1,344 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.##member = reinterpret_cast<##member##Function>( \
+ ::GetProcAddress(ntdll, #member)); \
+ if (NULL == g_nt.##member) \
+ return false
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+bool SetupNtdllImports() {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+
+ return true;
+}
+
+TEST(PolicyEngineTest, ParameterSetTest) {
+ void* pv1 = reinterpret_cast<void*>(0x477EAA5);
+ const void* pv2 = reinterpret_cast<void*>(0x987654);
+ ParameterSet pset1 = ParamPickerMake(pv1);
+ ParameterSet pset2 = ParamPickerMake(pv2);
+
+ // Test that we can store and retrieve a void pointer:
+ const void* result1 =0;
+ unsigned long result2 = 0;
+ EXPECT_TRUE(pset1.Get(&result1));
+ EXPECT_TRUE(pv1 == result1);
+ EXPECT_FALSE(pset1.Get(&result2));
+ EXPECT_TRUE(pset2.Get(&result1));
+ EXPECT_TRUE(pv2 == result1);
+ EXPECT_FALSE(pset2.Get(&result2));
+
+ // Test that we can store and retrieve a ulong:
+ unsigned long number = 12747;
+ ParameterSet pset3 = ParamPickerMake(number);
+ EXPECT_FALSE(pset3.Get(&result1));
+ EXPECT_TRUE(pset3.Get(&result2));
+ EXPECT_EQ(number, result2);
+
+ // Test that we can store and retrieve a string:
+ const wchar_t* txt = L"S231L";
+ ParameterSet pset4 = ParamPickerMake(txt);
+ const wchar_t* result3 = NULL;
+ EXPECT_TRUE(pset4.Get(&result3));
+ EXPECT_EQ(0, wcscmp(txt, result3));
+}
+
+TEST(PolicyEngineTest, OpcodeConstraints) {
+ // Test that PolicyOpcode has no virtual functions
+ // because these objects are copied over to other processes
+ // so they cannot have vtables.
+ EXPECT_FALSE(__is_polymorphic(PolicyOpcode));
+ // Keep developers from adding smarts to the opcodes which should
+ // be pretty much a bag of bytes with a OO interface.
+ EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_copy(PolicyOpcode));
+}
+
+TEST(PolicyEngineTest, TrueFalseOpcodes) {
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+ char memory[1024];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // This opcode always evaluates to true.
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, NULL));
+ EXPECT_FALSE(op1->IsAction());
+
+ // This opcode always evaluates to false.
+ PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL));
+
+ // Nulls not allowed on the params.
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 0, NULL));
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 1, NULL));
+
+ // True and False opcodes do not 'require' a number of parameters
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, NULL));
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL));
+
+ // Test Inverting the logic. Note that inversion is done outside
+ // any particular opcode evaluation so no need to repeat for all
+ // opcodes.
+ PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL));
+ PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval);
+ EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL));
+
+ // Test that we clear the match context
+ PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext);
+ MatchContext context;
+ context.position = 1;
+ context.options = kPolUseOREval;
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context));
+ EXPECT_EQ(0, context.position);
+ MatchContext context2;
+ EXPECT_EQ(context2.options, context.options);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase1) {
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / sizeof(PolicyOpcode);
+
+ for (size_t ix =0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_TRUE(NULL != op);
+ EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, NULL));
+ }
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_TRUE(NULL == op1);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase2) {
+ SetupNtdllImports();
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ // The difference with the previous test is that this opcodes allocate
+ // the string 'txt2' inside the same buffer.
+ const wchar_t* txt1 = L"1234";
+ const wchar_t txt2[] = L"123";
+
+ ParameterSet ppb1 = ParamPickerMake(txt1);
+ MatchContext mc1;
+
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2));
+
+ // Test that it does not overrun the buffer.
+ for (size_t ix =0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolClearContext);
+ ASSERT_TRUE(NULL != op);
+ EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1));
+ }
+
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+ ASSERT_TRUE(NULL == op1);
+}
+
+TEST(PolicyEngineTest, IntegerOpcodes) {
+ const wchar_t* txt = L"abcdef";
+ unsigned long num1 = 42;
+ unsigned long num2 = 113377;
+
+ ParameterSet pp_wrong1 = ParamPickerMake(txt);
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+ ParameterSet pp_num2 = ParamPickerMake(num2);
+
+ char memory[128];
+ 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);
+ 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));
+
+ // Test basic match for void pointers.
+ const void* vp = NULL;
+ ParameterSet pp_num3 = ParamPickerMake(vp);
+ PolicyOpcode* op_vp_null = opcode_maker.MakeOpVoidPtrMatch(0, NULL,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, NULL));
+ EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, NULL));
+ EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, NULL));
+
+ // Basic range test [41 43] (inclusive).
+ PolicyOpcode* op_range1 = opcode_maker.MakeOpUlongMatchRange(0, 41, 43,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, NULL));
+ EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, NULL));
+ EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, NULL));
+}
+
+TEST(PolicyEngineTest, LogicalOpcodes) {
+ char memory[128];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ unsigned long num1 = 0x10100702;
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+
+ PolicyOpcode* op_and1 = opcode_maker.MakeOpUlongAndMatch(0, 0x00100000,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL));
+ PolicyOpcode* op_and2 = opcode_maker.MakeOpUlongAndMatch(0, 0x00000001,
+ kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, NULL));
+}
+
+TEST(PolicyEngineTest, WCharOpcodes1) {
+ SetupNtdllImports();
+
+ const wchar_t* txt1 = L"the quick fox jumps over the lazy dog";
+ const wchar_t txt2[] = L"the quick";
+ const wchar_t txt3[] = L" fox jumps";
+ const wchar_t txt4[] = L"the lazy dog";
+ const wchar_t txt5[] = L"jumps over";
+ const wchar_t txt6[] = L"g";
+
+ ParameterSet pp_tc1 = ParamPickerMake(txt1);
+ char memory[512];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+
+ // Simplest substring match from pos 0. It should be a successful match
+ // and the match context should be updated.
+ MatchContext mc1;
+ EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Matching again should fail and the context should be unmodified.
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Using the same match context we should continue where we left
+ // in the previous successful match,
+ PolicyOpcode* op3 = opcode_maker.MakeOpWStringMatch(0, txt3, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2);
+
+ // We now keep on matching but now we skip 6 characters which means
+ // we skip the string ' over '. And we zero the match context. This is
+ // the primitive that we use to build '??'.
+ PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(0, txt4, 6,
+ CASE_SENSITIVE,
+ kPolClearContext);
+ EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0, mc1.position);
+
+ // Test that we can properly match the last part of the string
+ PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd,
+ CASE_SENSITIVE,
+ kPolClearContext);
+ EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0, mc1.position);
+
+ // Test matching 'jumps over' over the entire string. This is the
+ // primitive we build '*' from.
+ PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(24, mc1.position);
+
+ // Test that we don't match because it is not at the end of the string
+ PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekToEnd,
+ CASE_SENSITIVE,
+ kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1));
+
+ // Test that we function if the string does not fit. In this case we
+ // try to match 'the lazy dog' against 'he lazy dog'.
+ PolicyOpcode* op6 = opcode_maker.MakeOpWStringMatch(0, txt4, 2,
+ CASE_SENSITIVE, kPolNone);
+ EXPECT_EQ(24, mc1.position);
+
+ // Testing matching against 'g' which should be the last char.
+ MatchContext mc2;
+ PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2));
+
+ // Trying to match again should fail since we are in the last char.
+ // This also covers a couple of boundary conditions.
+ EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2));
+}
+
+TEST(PolicyEngineTest, WCharOpcodes2) {
+ SetupNtdllImports();
+
+ const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt";
+ const wchar_t txt1[] = L"Settings\\microsoft";
+ ParameterSet pp_tc1 = ParamPickerMake(path1);
+
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+
+ // Testing case-insensitive does not buy us much since it this option
+ // is just passed to the Microsoft API that we use normally, but just for
+ // coverage, here it is:
+ PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward,
+ CASE_INSENSITIVE,
+ kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(35, mc1.position);
+}
+
+TEST(PolicyEngineTest, ActionOpcodes) {
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+ EXPECT_TRUE(op1->IsAction());
+ EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_params.h b/chromium/sandbox/win/src/policy_params.h
new file mode 100644
index 00000000000..7ded1fcfc47
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_params.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2006-2008 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_POLICY_PARAMS_H__
+#define SANDBOX_SRC_POLICY_PARAMS_H__
+
+#include "sandbox/win/src/policy_engine_params.h"
+
+namespace sandbox {
+
+class ParameterSet;
+
+// Warning: The following macros store the address to the actual variables, in
+// other words, the values are not copied.
+#define POLPARAMS_BEGIN(type) class type { public: enum Args {
+#define POLPARAM(arg) arg,
+#define POLPARAMS_END(type) PolParamLast }; }; \
+ typedef sandbox::ParameterSet type##Array [type::PolParamLast];
+
+// Policy parameters for file open / create.
+POLPARAMS_BEGIN(OpenFile)
+ POLPARAM(NAME)
+ POLPARAM(BROKER) // TRUE if called from the broker.
+ POLPARAM(ACCESS)
+ POLPARAM(OPTIONS)
+POLPARAMS_END(OpenFile)
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(FileName)
+ POLPARAM(NAME)
+ POLPARAM(BROKER) // TRUE if called from the broker.
+POLPARAMS_END(FileName)
+
+COMPILE_ASSERT(OpenFile::NAME == static_cast<int>(FileName::NAME),
+ to_simplify_fs_policies);
+COMPILE_ASSERT(OpenFile::BROKER == static_cast<int>(FileName::BROKER),
+ to_simplify_fs_policies);
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(NameBased)
+ POLPARAM(NAME)
+POLPARAMS_END(NameBased)
+
+// Policy parameters for open event.
+POLPARAMS_BEGIN(OpenEventParams)
+ POLPARAM(NAME)
+ POLPARAM(ACCESS)
+POLPARAMS_END(OpenEventParams)
+
+// Policy Parameters for reg open / create.
+POLPARAMS_BEGIN(OpenKey)
+ POLPARAM(NAME)
+ POLPARAM(ACCESS)
+POLPARAMS_END(OpenKey)
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(HandleTarget)
+ POLPARAM(NAME)
+ POLPARAM(TARGET)
+POLPARAMS_END(HandleTarget)
+
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_PARAMS_H__
diff --git a/chromium/sandbox/win/src/policy_target.cc b/chromium/sandbox/win/src/policy_target.cc
new file mode 100644
index 00000000000..84b72037db6
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_target.cc
@@ -0,0 +1,127 @@
+// Copyright (c) 2006-2008 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/policy_target.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// Handle for our private heap.
+extern void* g_heap;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Policy data.
+extern void* volatile g_shared_policy_memory;
+SANDBOX_INTERCEPT size_t g_shared_policy_size;
+
+bool QueryBroker(int ipc_id, CountedParameterSetBase* params) {
+ DCHECK_NT(static_cast<size_t>(ipc_id) < kMaxServiceCount);
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+
+ if (static_cast<size_t>(ipc_id) >= kMaxServiceCount)
+ return false;
+
+ PolicyGlobal* global_policy =
+ reinterpret_cast<PolicyGlobal*>(g_shared_policy_memory);
+
+ if (!global_policy->entry[ipc_id])
+ return false;
+
+ PolicyBuffer* policy = reinterpret_cast<PolicyBuffer*>(
+ reinterpret_cast<char*>(g_shared_policy_memory) +
+ reinterpret_cast<size_t>(global_policy->entry[ipc_id]));
+
+ if ((reinterpret_cast<size_t>(global_policy->entry[ipc_id]) >
+ global_policy->data_size) ||
+ (g_shared_policy_size < global_policy->data_size)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ for (int i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED_NT();
+ return false;
+ }
+ }
+
+ PolicyProcessor processor(policy);
+ PolicyResult result = processor.Evaluate(kShortEval, params->parameters,
+ params->count);
+ DCHECK_NT(POLICY_ERROR != result);
+
+ return POLICY_MATCH == result && ASK_BROKER == processor.GetAction();
+}
+
+// -----------------------------------------------------------------------
+
+// Hooks NtSetInformationThread to block RevertToSelf from being
+// called before the actual call to LowerToken.
+NTSTATUS WINAPI TargetNtSetInformationThread(
+ NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information,
+ ULONG thread_information_bytes) {
+ do {
+ if (SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ break;
+ if (ThreadImpersonationToken != thread_info_class)
+ break;
+ if (!thread_information)
+ break;
+ HANDLE token;
+ if (sizeof(token) > thread_information_bytes)
+ break;
+
+ NTSTATUS ret = CopyData(&token, thread_information, sizeof(token));
+ if (!NT_SUCCESS(ret) || NULL != token)
+ break;
+
+ // This is a revert to self.
+ return STATUS_SUCCESS;
+ } while (false);
+
+ return orig_SetInformationThread(thread, thread_info_class,
+ thread_information,
+ thread_information_bytes);
+}
+
+// Hooks NtOpenThreadToken to force the open_as_self parameter to be set to
+// FALSE if we are still running with the impersonation token. open_as_self set
+// to TRUE means that the token will be open using the process token instead of
+// the impersonation token. This is bad because the process token does not have
+// access to open the thread token.
+NTSTATUS WINAPI TargetNtOpenThreadToken(
+ NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ open_as_self = FALSE;
+
+ return orig_OpenThreadToken(thread, desired_access, open_as_self, token);
+}
+
+// See comment for TargetNtOpenThreadToken
+NTSTATUS WINAPI TargetNtOpenThreadTokenEx(
+ NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes,
+ PHANDLE token) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ open_as_self = FALSE;
+
+ return orig_OpenThreadTokenEx(thread, desired_access, open_as_self,
+ handle_attributes, token);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/policy_target.h b/chromium/sandbox/win/src/policy_target.h
new file mode 100644
index 00000000000..8a2b437095e
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_target.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2006-2008 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_POLICY_TARGET_H__
+#define SANDBOX_SRC_POLICY_TARGET_H__
+
+namespace sandbox {
+
+struct CountedParameterSetBase;
+
+// Performs a policy lookup and returns true if the request should be passed to
+// the broker process.
+bool QueryBroker(int ipc_id, CountedParameterSetBase* params);
+
+extern "C" {
+
+// Interception of NtSetInformationThread on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread(
+ NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information,
+ ULONG thread_information_bytes);
+
+// Interception of NtOpenThreadToken on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken(
+ NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token);
+
+// Interception of NtOpenThreadTokenEx on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx(
+ NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes,
+ PHANDLE token);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_TARGET_H__
diff --git a/chromium/sandbox/win/src/policy_target_test.cc b/chromium/sandbox/win/src/policy_target_test.cc
new file mode 100644
index 00000000000..ee28260b084
--- /dev/null
+++ b/chromium/sandbox/win/src/policy_target_test.cc
@@ -0,0 +1,337 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.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/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+#define BINDNTDLL(name) \
+ name ## Function name = reinterpret_cast<name ## Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+// Reverts to self and verify that SetInformationToken was faked. Returns
+// SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t **argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE |
+ TOKEN_DUPLICATE, FALSE, &thread_token))
+ return ::GetLastError();
+
+ ::RevertToSelf();
+ ::CloseHandle(thread_token);
+
+ int ret = SBOX_TEST_FAILED;
+ if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ FALSE, &thread_token)) {
+ ret = SBOX_TEST_SUCCEEDED;
+ ::CloseHandle(thread_token);
+ }
+ return ret;
+}
+
+// Stores the high privilege token on a static variable, change impersonation
+// again to that one and verify that we are not interfering anymore with
+// RevertToSelf.
+SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t **argv) {
+ static HANDLE thread_token;
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
+ if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE |
+ TOKEN_DUPLICATE, FALSE, &thread_token))
+ return ::GetLastError();
+ } else {
+ if (!::SetThreadToken(NULL, thread_token))
+ return ::GetLastError();
+
+ // See if we fake the call again.
+ int ret = PolicyTargetTest_token(argc, argv);
+ ::CloseHandle(thread_token);
+ return ret;
+ }
+ return 0;
+}
+
+// Opens the thread token with and without impersonation.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t **argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE |
+ TOKEN_DUPLICATE, FALSE, &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ TRUE, &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Opens the thread token with and without impersonation, using
+// NtOpenThreadTokenEX.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t **argv) {
+ BINDNTDLL(NtOpenThreadTokenEx);
+ if (!NtOpenThreadTokenEx)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ FALSE, 0, &thread_token);
+ if (status == STATUS_NO_TOKEN)
+ return ERROR_NO_TOKEN;
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, TRUE, 0,
+ &thread_token);
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can open the current thread.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t **argv) {
+ DWORD thread_id = ::GetCurrentThreadId();
+ HANDLE thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// New thread entry point: do nothing.
+DWORD WINAPI PolicyTargetTest_thread_main(void* param) {
+ ::Sleep(INFINITE);
+ return 0;
+}
+
+// Tests that we can create a new thread, and open it.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t **argv) {
+ // Use default values to create a new thread.
+ DWORD thread_id;
+ HANDLE thread = ::CreateThread(NULL, 0, &PolicyTargetTest_thread_main, 0, 0,
+ &thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ thread = ::OpenThread(SYNCHRONIZE, FALSE, thread_id);
+ if (!thread)
+ return ::GetLastError();
+
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can call CreateProcess.
+SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t **argv) {
+ // Use default values to create a new process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ base::win::ScopedProcessInformation process_info;
+ if (!::CreateProcessW(L"foo.exe", L"foo.exe", NULL, NULL, FALSE, 0,
+ NULL, NULL, &startup_info, process_info.Receive()))
+ return SBOX_TEST_SUCCEEDED;
+ return SBOX_TEST_FAILED;
+}
+
+TEST(PolicyTargetTest, SetInformationThread) {
+ TestRunner runner;
+ if (base::win::GetVersion() >= base::win::VERSION_XP) {
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token"));
+ }
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(EVERY_STATE);
+ if (base::win::GetVersion() >= base::win::VERSION_XP)
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal"));
+}
+
+TEST(PolicyTargetTest, OpenThreadToken) {
+ TestRunner runner;
+ if (base::win::GetVersion() >= base::win::VERSION_XP) {
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2"));
+ }
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2"));
+}
+
+TEST(PolicyTargetTest, OpenThreadTokenEx) {
+ TestRunner runner;
+ if (base::win::GetVersion() < base::win::VERSION_XP)
+ return;
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3"));
+}
+
+TEST(PolicyTargetTest, OpenThread) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) <<
+ "Opens the current thread";
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2")) <<
+ "Creates a new thread and opens it";
+}
+
+TEST(PolicyTargetTest, OpenProcess) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process")) <<
+ "Opens a process";
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// desktop associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, DesktopPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ TargetPolicy* temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(false);
+ temp_policy->Release();
+
+ ASSERT_TRUE(broker != NULL);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ TargetPolicy* policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(false);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
+ target.Receive());
+ policy->Release();
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+
+ EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE);
+ EXPECT_TRUE(NULL != desk);
+ EXPECT_TRUE(::CloseDesktop(desk));
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy->Release();
+
+ // Make sure the desktop does not exist anymore.
+ desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE);
+ EXPECT_TRUE(NULL == desk);
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// winstation associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, WinstaPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ TargetPolicy* temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(true);
+ temp_policy->Release();
+
+ ASSERT_TRUE(broker != NULL);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ TargetPolicy* policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(true);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
+ target.Receive());
+ policy->Release();
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+
+ EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ ASSERT_FALSE(desktop_name.empty());
+
+ // Make sure there is a backslash, for the window station name.
+ EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos);
+
+ // Isolate the desktop name.
+ desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, FALSE, DESKTOP_ENUMERATE);
+ // This should fail if the desktop is really on another window station.
+ EXPECT_FALSE(NULL != desk);
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy->Release();
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_mitigations.cc b/chromium/sandbox/win/src/process_mitigations.cc
new file mode 100644
index 00000000000..b9148549f95
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations.cc
@@ -0,0 +1,332 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations.h"
+
+#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 {
+
+// Functions for enabling policies.
+typedef BOOL (WINAPI *SetProcessDEPPolicyFunction)(DWORD dwFlags);
+
+typedef BOOL (WINAPI *SetProcessMitigationPolicyFunction)(
+ PROCESS_MITIGATION_POLICY mitigation_policy,
+ PVOID buffer,
+ SIZE_T length);
+
+typedef BOOL (WINAPI *SetDefaultDllDirectoriesFunction)(
+ DWORD DirectoryFlags);
+
+} // namespace
+
+namespace sandbox {
+
+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");
+
+ if (version >= base::win::VERSION_VISTA &&
+ (flags & MITIGATION_DLL_SEARCH_ORDER)) {
+ SetDefaultDllDirectoriesFunction set_default_dll_directories =
+ reinterpret_cast<SetDefaultDllDirectoriesFunction>(
+ ::GetProcAddress(module, "SetDefaultDllDirectories"));
+
+ // Check for SetDefaultDllDirectories since it requires KB2533623.
+ if (set_default_dll_directories) {
+ if (!set_default_dll_directories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+ }
+
+ // Set the heap to terminate on corruption
+ if (version >= base::win::VERSION_VISTA &&
+ (flags & MITIGATION_HEAP_TERMINATE)) {
+ if (!::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption,
+ NULL, 0) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+#if !defined(_WIN64) // DEP is always enabled on 64-bit.
+ if (flags & MITIGATION_DEP) {
+ DWORD dep_flags = PROCESS_DEP_ENABLE;
+ // DEP support is quirky on XP, so don't force a failure in that case.
+ const bool return_on_fail = version >= base::win::VERSION_VISTA;
+
+ if (flags & MITIGATION_DEP_NO_ATL_THUNK)
+ dep_flags |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION;
+
+ SetProcessDEPPolicyFunction set_process_dep_policy =
+ reinterpret_cast<SetProcessDEPPolicyFunction>(
+ ::GetProcAddress(module, "SetProcessDEPPolicy"));
+ if (set_process_dep_policy) {
+ if (!set_process_dep_policy(dep_flags) &&
+ ERROR_ACCESS_DENIED != ::GetLastError() && return_on_fail) {
+ return false;
+ }
+ } else {
+ // We're on XP sp2, so use the less standard approach.
+ // For reference: http://www.uninformed.org/?v=2&a=4
+ const int MEM_EXECUTE_OPTION_ENABLE = 1;
+ const int MEM_EXECUTE_OPTION_DISABLE = 2;
+ const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4;
+ const int MEM_EXECUTE_OPTION_PERMANENT = 8;
+
+ NtSetInformationProcessFunction set_information_process = NULL;
+ ResolveNTFunctionPtr("NtSetInformationProcess",
+ &set_information_process);
+ if (!set_information_process)
+ return false;
+ ULONG dep = MEM_EXECUTE_OPTION_DISABLE | MEM_EXECUTE_OPTION_PERMANENT;
+ if (!(dep_flags & PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION))
+ dep |= MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION;
+ if (!SUCCEEDED(set_information_process(GetCurrentProcess(),
+ ProcessExecuteFlags,
+ &dep, sizeof(dep))) &&
+ ERROR_ACCESS_DENIED != ::GetLastError() && return_on_fail) {
+ return false;
+ }
+ }
+ }
+#endif
+
+ // This is all we can do in Win7 and below.
+ if (version < base::win::VERSION_WIN8)
+ return true;
+
+ SetProcessMitigationPolicyFunction set_process_mitigation_policy =
+ reinterpret_cast<SetProcessMitigationPolicyFunction>(
+ ::GetProcAddress(module, "SetProcessMitigationPolicy"));
+ if (!set_process_mitigation_policy)
+ return false;
+
+ // Enable ASLR policies.
+ if (flags & MITIGATION_RELOCATE_IMAGE) {
+ PROCESS_MITIGATION_ASLR_POLICY policy = { 0 };
+ policy.EnableForceRelocateImages = true;
+ policy.DisallowStrippedImages = (flags &
+ MITIGATION_RELOCATE_IMAGE_REQUIRED) ==
+ MITIGATION_RELOCATE_IMAGE_REQUIRED;
+
+ if (!set_process_mitigation_policy(ProcessASLRPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable strict handle policies.
+ if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
+ PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = { 0 };
+ policy.HandleExceptionsPermanentlyEnabled =
+ policy.RaiseExceptionOnInvalidHandleReference = true;
+
+ if (!set_process_mitigation_policy(ProcessStrictHandleCheckPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable system call policies.
+ if (flags & MITIGATION_WIN32K_DISABLE) {
+ PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = { 0 };
+ policy.DisallowWin32kSystemCalls = true;
+
+ if (!set_process_mitigation_policy(ProcessSystemCallDisablePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable system call policies.
+ if (flags & MITIGATION_EXTENSION_DLL_DISABLE) {
+ PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = { 0 };
+ policy.DisableExtensionPoints = true;
+
+ if (!set_process_mitigation_policy(ProcessExtensionPointDisablePolicy,
+ &policy, sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
+ DWORD64* policy_flags, size_t* size) {
+ base::win::Version version = base::win::GetVersion();
+
+ *policy_flags = 0;
+#if defined(_WIN64)
+ *size = sizeof(*policy_flags);
+#elif defined(_M_IX86)
+ // A 64-bit flags attribute is illegal on 32-bit Win 7 and below.
+ if (version < base::win::VERSION_WIN8)
+ *size = sizeof(DWORD);
+ else
+ *size = sizeof(*policy_flags);
+#else
+#error This platform is not supported.
+#endif
+
+ // Nothing for Win XP or Vista.
+ if (version <= base::win::VERSION_VISTA)
+ return;
+
+ // DEP and SEHOP are not valid for 64-bit Windows
+#if !defined(_WIN64)
+ if (flags & MITIGATION_DEP) {
+ *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE;
+ if (!(flags & MITIGATION_DEP_NO_ATL_THUNK))
+ *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE;
+ }
+
+ if (flags & MITIGATION_SEHOP)
+ *policy_flags |= PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE;
+#endif
+
+ // Win 7
+ if (version < base::win::VERSION_WIN8)
+ return;
+
+ if (flags & MITIGATION_RELOCATE_IMAGE) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON;
+ if (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS;
+ }
+ }
+
+ if (flags & MITIGATION_HEAP_TERMINATE) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_BOTTOM_UP_ASLR) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_HIGH_ENTROPY_ASLR) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_WIN32K_DISABLE) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_EXTENSION_DLL_DISABLE) {
+ *policy_flags |=
+ PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON;
+ }
+}
+
+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 |
+ MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HEAP_TERMINATE);
+ }
+
+ // Windows 7 and Vista.
+ } else if (version < base::win::VERSION_WIN8) {
+ return flags & (MITIGATION_BOTTOM_UP_ASLR |
+ MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HEAP_TERMINATE);
+ }
+
+ // Windows 8 and above.
+ return flags & (MITIGATION_BOTTOM_UP_ASLR |
+ MITIGATION_DLL_SEARCH_ORDER);
+}
+
+bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
+ MitigationFlags flags) {
+// This is a hack to fake a weak bottom-up ASLR on 32-bit Windows.
+#if !defined(_WIN64)
+ if (flags & MITIGATION_BOTTOM_UP_ASLR) {
+ unsigned int limit;
+ rand_s(&limit);
+ char* ptr = 0;
+ const size_t kMask64k = 0xFFFF;
+ // Random range (512k-16.5mb) in 64k steps.
+ const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k);
+ while (ptr < end) {
+ MEMORY_BASIC_INFORMATION memory_info;
+ if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info)))
+ break;
+ size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k,
+ static_cast<SIZE_T>(end - ptr));
+ if (ptr && memory_info.State == MEM_FREE)
+ ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS);
+ ptr += size;
+ }
+ }
+#endif
+
+ return true;
+}
+
+bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) {
+ // All of these mitigations can be enabled after startup.
+ return !(flags & ~(MITIGATION_HEAP_TERMINATE |
+ MITIGATION_DEP |
+ MITIGATION_DEP_NO_ATL_THUNK |
+ MITIGATION_RELOCATE_IMAGE |
+ MITIGATION_RELOCATE_IMAGE_REQUIRED |
+ MITIGATION_BOTTOM_UP_ASLR |
+ MITIGATION_STRICT_HANDLE_CHECKS |
+ MITIGATION_WIN32K_DISABLE |
+ MITIGATION_EXTENSION_DLL_DISABLE |
+ MITIGATION_DLL_SEARCH_ORDER));
+}
+
+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));
+}
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/process_mitigations.h b/chromium/sandbox/win/src/process_mitigations.h
new file mode 100644
index 00000000000..9039ad677b2
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+#define SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/security_level.h"
+
+namespace sandbox {
+
+// Sets the mitigation policy for the current process, ignoring any settings
+// that are invalid for the current version of Windows.
+bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags);
+
+// Returns the flags that must be enforced after startup for the current OS
+// version.
+MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags);
+
+// Converts sandbox flags to the PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES
+// policy flags used by UpdateProcThreadAttribute(). The size field varies
+// between a 32-bit and a 64-bit type based on the exact build and version of
+// Windows, so the returned size must be passed to UpdateProcThreadAttribute().
+void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
+ DWORD64* policy_flags, size_t* size);
+
+// Adds mitigations that need to be performed on the suspended target process
+// before execution begins.
+bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
+ MitigationFlags flags);
+
+// Returns true if all the supplied flags can be set after a process starts.
+bool CanSetProcessMitigationsPostStartup(MitigationFlags flags);
+
+// Returns true if all the supplied flags can be set before a process starts.
+bool CanSetProcessMitigationsPreStartup(MitigationFlags flags);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+
diff --git a/chromium/sandbox/win/src/process_mitigations_test.cc b/chromium/sandbox/win/src/process_mitigations_test.cc
new file mode 100644
index 00000000000..f1274c54406
--- /dev/null
+++ b/chromium/sandbox/win/src/process_mitigations_test.cc
@@ -0,0 +1,207 @@
+// Copyright (c) 2011 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 "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/nt_internals.h"
+#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"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+typedef BOOL (WINAPI *GetProcessDEPPolicyFunction)(
+ HANDLE process,
+ LPDWORD flags,
+ PBOOL permanent);
+
+typedef BOOL (WINAPI *GetProcessMitigationPolicyFunction)(
+ HANDLE process,
+ PROCESS_MITIGATION_POLICY mitigation_policy,
+ PVOID buffer,
+ SIZE_T length);
+
+GetProcessMitigationPolicyFunction get_process_mitigation_policy;
+
+bool CheckWin8DepPolicy() {
+ PROCESS_MITIGATION_DEP_POLICY policy;
+ if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessDEPPolicy,
+ &policy, sizeof(policy))) {
+ return false;
+ }
+ return policy.Enable && policy.Permanent;
+}
+
+bool CheckWin8AslrPolicy() {
+ PROCESS_MITIGATION_ASLR_POLICY policy;
+ if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessASLRPolicy,
+ &policy, sizeof(policy))) {
+ return false;
+ }
+ return policy.EnableForceRelocateImages && policy.DisallowStrippedImages;
+}
+
+bool CheckWin8StrictHandlePolicy() {
+ PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy;
+ if (!get_process_mitigation_policy(::GetCurrentProcess(),
+ ProcessStrictHandleCheckPolicy,
+ &policy, sizeof(policy))) {
+ return false;
+ }
+ return policy.RaiseExceptionOnInvalidHandleReference &&
+ policy.HandleExceptionsPermanentlyEnabled;
+}
+
+bool CheckWin8Win32CallPolicy() {
+ PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy;
+ if (!get_process_mitigation_policy(::GetCurrentProcess(),
+ ProcessSystemCallDisablePolicy,
+ &policy, sizeof(policy))) {
+ return false;
+ }
+ return policy.DisallowWin32kSystemCalls;
+}
+
+bool CheckWin8DllExtensionPolicy() {
+ PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy;
+ if (!get_process_mitigation_policy(::GetCurrentProcess(),
+ ProcessExtensionPointDisablePolicy,
+ &policy, sizeof(policy))) {
+ return false;
+ }
+ return policy.DisableExtensionPoints;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int CheckWin8(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 (!CheckWin8DepPolicy())
+ return SBOX_TEST_FIRST_ERROR;
+
+#if defined(NDEBUG) // ASLR cannot be forced in debug builds.
+ if (!CheckWin8AslrPolicy())
+ return SBOX_TEST_SECOND_ERROR;
+#endif
+
+ if (!CheckWin8StrictHandlePolicy())
+ return SBOX_TEST_THIRD_ERROR;
+
+ if (!CheckWin8Win32CallPolicy())
+ return SBOX_TEST_FOURTH_ERROR;
+
+ if (!CheckWin8DllExtensionPolicy())
+ return SBOX_TEST_FIFTH_ERROR;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(ProcessMitigationsTest, CheckWin8) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ sandbox::MitigationFlags mitigations = MITIGATION_DEP |
+ MITIGATION_DEP_NO_ATL_THUNK |
+ MITIGATION_EXTENSION_DLL_DISABLE;
+#if defined(NDEBUG) // ASLR cannot be forced in debug builds.
+ mitigations |= MITIGATION_RELOCATE_IMAGE |
+ MITIGATION_RELOCATE_IMAGE_REQUIRED;
+#endif
+
+ EXPECT_EQ(policy->SetProcessMitigations(mitigations), SBOX_ALL_OK);
+
+ mitigations |= MITIGATION_STRICT_HANDLE_CHECKS |
+ MITIGATION_WIN32K_DISABLE;
+
+ EXPECT_EQ(policy->SetDelayedProcessMitigations(mitigations), SBOX_ALL_OK);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8"));
+}
+
+
+SBOX_TESTS_COMMAND int CheckDep(int argc, wchar_t **argv) {
+ GetProcessDEPPolicyFunction get_process_dep_policy =
+ reinterpret_cast<GetProcessDEPPolicyFunction>(
+ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
+ "GetProcessDEPPolicy"));
+ if (get_process_dep_policy) {
+ BOOL is_permanent = FALSE;
+ DWORD dep_flags = 0;
+
+ if (!get_process_dep_policy(::GetCurrentProcess(), &dep_flags,
+ &is_permanent)) {
+ return SBOX_TEST_FIRST_ERROR;
+ }
+
+ if (!(dep_flags & PROCESS_DEP_ENABLE) || !is_permanent)
+ return SBOX_TEST_SECOND_ERROR;
+
+ } else {
+ NtQueryInformationProcessFunction query_information_process = NULL;
+ ResolveNTFunctionPtr("NtQueryInformationProcess",
+ &query_information_process);
+ if (!query_information_process)
+ return SBOX_TEST_NOT_FOUND;
+
+ ULONG size = 0;
+ ULONG dep_flags = 0;
+ if (!SUCCEEDED(query_information_process(::GetCurrentProcess(),
+ ProcessExecuteFlags, &dep_flags,
+ sizeof(dep_flags), &size))) {
+ return SBOX_TEST_THIRD_ERROR;
+ }
+
+ const int MEM_EXECUTE_OPTION_ENABLE = 1;
+ const int MEM_EXECUTE_OPTION_DISABLE = 2;
+ const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4;
+ const int MEM_EXECUTE_OPTION_PERMANENT = 8;
+ dep_flags &= 0xff;
+
+ if (dep_flags != (MEM_EXECUTE_OPTION_DISABLE |
+ MEM_EXECUTE_OPTION_PERMANENT)) {
+ return SBOX_TEST_FOURTH_ERROR;
+ }
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+#if !defined(_WIN64) // DEP is always enabled on 64-bit.
+TEST(ProcessMitigationsTest, CheckDep) {
+ if (!IsXPSP2OrLater() || base::win::GetVersion() > base::win::VERSION_WIN7)
+ return;
+
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ EXPECT_EQ(policy->SetProcessMitigations(
+ MITIGATION_DEP |
+ MITIGATION_DEP_NO_ATL_THUNK |
+ MITIGATION_SEHOP),
+ SBOX_ALL_OK);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckDep"));
+}
+#endif
+
+} // namespace sandbox
+
diff --git a/chromium/sandbox/win/src/process_policy_test.cc b/chromium/sandbox/win/src/process_policy_test.cc
new file mode 100644
index 00000000000..babe3214b0c
--- /dev/null
+++ b/chromium/sandbox/win/src/process_policy_test.cc
@@ -0,0 +1,384 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+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) {
+ wchar_t windows_path[MAX_PATH] = {0};
+ ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH);
+ string16 full_path(windows_path);
+ if (full_path.empty()) {
+ return full_path;
+ }
+ full_path += L"\\system32\\";
+ full_path += name;
+ return full_path;
+}
+
+// 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) {
+ base::win::ScopedProcessInformation pi;
+ STARTUPINFOW si = {sizeof(si)};
+
+ const wchar_t *exe_name = NULL;
+ if (!exe.empty())
+ exe_name = exe.c_str();
+
+ const wchar_t *cmd_line = NULL;
+ if (!command.empty())
+ cmd_line = command.c_str();
+
+ // Create the process with the unicode version of the API.
+ sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED;
+ if (!::CreateProcessW(exe_name, const_cast<wchar_t*>(cmd_line), NULL, NULL,
+ FALSE, 0, NULL, NULL, &si, pi.Receive())) {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret1 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret1 = sandbox::SBOX_TEST_FAILED;
+ }
+ } else {
+ ret1 = sandbox::SBOX_TEST_SUCCEEDED;
+ }
+
+ pi.Close();
+
+ // Do the same with the ansi version of the api
+ STARTUPINFOA sia = {sizeof(sia)};
+ sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED;
+
+ std::string narrow_cmd_line;
+ if (cmd_line)
+ narrow_cmd_line = base::SysWideToMultiByte(cmd_line, CP_UTF8);
+ if (!::CreateProcessA(
+ exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL,
+ cmd_line ? const_cast<char*>(narrow_cmd_line.c_str()) : NULL,
+ NULL, NULL, FALSE, 0, NULL, NULL, &sia, pi.Receive())) {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret2 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret2 = sandbox::SBOX_TEST_FAILED;
+ }
+ } else {
+ ret2 = sandbox::SBOX_TEST_SUCCEEDED;
+ }
+
+ if (ret1 == ret2)
+ return ret1;
+
+ return sandbox::SBOX_TEST_FAILED;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Process_RunApp1(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ string16 path = MakeFullPathToSystem32(argv[0]);
+
+ // TEST 1: Try with the path in the app_name.
+ return CreateProcessHelper(path, string16());
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ string16 path = MakeFullPathToSystem32(argv[0]);
+
+ // TEST 2: Try with the path in the cmd_line.
+ string16 cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\"";
+ return CreateProcessHelper(string16(), cmd_line);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ // TEST 3: Try file name in the cmd_line.
+ return CreateProcessHelper(string16(), argv[0]);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ // TEST 4: Try file name in the app_name and current directory sets correctly.
+ string16 system32 = MakeFullPathToSystem32(L"");
+ wchar_t current_directory[MAX_PATH + 1];
+ int result4;
+ bool test_succeeded = false;
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ if (!ret)
+ return SBOX_TEST_FIRST_ERROR;
+
+ if (ret < MAX_PATH) {
+ current_directory[ret] = L'\\';
+ current_directory[ret+1] = L'\0';
+ if (::SetCurrentDirectory(system32.c_str())) {
+ result4 = CreateProcessHelper(argv[0], string16());
+ if (::SetCurrentDirectory(current_directory)) {
+ test_succeeded = true;
+ }
+ } else {
+ return SBOX_TEST_SECOND_ERROR;
+ }
+ }
+ if (!test_succeeded)
+ result4 = SBOX_TEST_FAILED;
+
+ return result4;
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp5(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ string16 path = MakeFullPathToSystem32(argv[0]);
+
+ // TEST 5: Try with the path in the cmd_line and arguments.
+ string16 cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\" /I";
+ return CreateProcessHelper(string16(), cmd_line);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ if ((NULL == argv) || (NULL == argv[0])) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ // TEST 6: Try with the file_name in the cmd_line and arguments.
+ string16 cmd_line = argv[0];
+ cmd_line += L" /I";
+ return CreateProcessHelper(string16(), cmd_line);
+}
+
+// Creates a process and checks if it's possible to get a handle to it's token.
+SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if ((NULL == argv) || (NULL == argv[0]))
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ string16 path = MakeFullPathToSystem32(argv[0]);
+
+ base::win::ScopedProcessInformation pi;
+ STARTUPINFOW si = {sizeof(si)};
+
+ if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED,
+ NULL, NULL, &si, pi.Receive())) {
+ return SBOX_TEST_FAILED;
+ }
+
+ HANDLE token = NULL;
+ BOOL result =
+ ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token);
+ DWORD error = ::GetLastError();
+
+ base::win::ScopedHandle token_handle(token);
+
+ if (!::TerminateProcess(pi.process_handle(), 0))
+ return SBOX_TEST_FAILED;
+
+ if (result && token)
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+
+SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t **argv) {
+ HANDLE token;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ }
+ } else {
+ ::CloseHandle(token);
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+TEST(ProcessPolicyTest, TestAllAccess) {
+ // Check if the "all access" rule fails to be added when the token is too
+ // powerful.
+ TestRunner runner;
+
+ // Check the failing case.
+ runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ EXPECT_EQ(SBOX_ERROR_UNSUPPORTED,
+ runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ L"this is not important"));
+
+ // Check the working case.
+ runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE);
+
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ L"this is not important"));
+}
+
+TEST(ProcessPolicyTest, CreateProcessAW) {
+ TestRunner runner;
+ string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ string16 system32 = MakeFullPathToSystem32(L"");
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ exe_path.c_str()));
+
+ // Need to add directory rules for the directories that we use in
+ // SetCurrentDirectory.
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY,
+ system32.c_str()));
+
+ wchar_t current_directory[MAX_PATH];
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ ASSERT_TRUE(0 != ret && ret < MAX_PATH);
+
+ wcscat_s(current_directory, MAX_PATH, L"\\");
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY,
+ current_directory));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp1 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp2 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp3 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp5 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp6 calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp1 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp2 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp3 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp5 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp6 findstr.exe"));
+
+#if !defined(_WIN64)
+ if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) {
+ // WinXP results are not reliable.
+ EXPECT_EQ(SBOX_TEST_SECOND_ERROR,
+ runner.RunTest(L"Process_RunApp4 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_SECOND_ERROR,
+ runner.RunTest(L"Process_RunApp4 findstr.exe"));
+ }
+#endif
+}
+
+TEST(ProcessPolicyTest, OpenToken) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) {
+ TestRunner runner;
+ string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) {
+ TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE);
+ string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccessNoJob) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_thread_dispatcher.cc b/chromium/sandbox/win/src/process_thread_dispatcher.cc
new file mode 100644
index 00000000000..b2331b7f525
--- /dev/null
+++ b/chromium/sandbox/win/src/process_thread_dispatcher.cc
@@ -0,0 +1,245 @@
+// Copyright (c) 2006-2010 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_thread_dispatcher.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/sandbox.h"
+
+namespace {
+
+// Extracts the application name from a command line.
+//
+// The application name is the first element of the command line. If
+// there is no quotes, the first element is delimited by the first space.
+// If there are quotes, the first element is delimited by the quotes.
+//
+// The create process call is smarter than us. It tries really hard to launch
+// the process even if the command line is wrong. For example:
+// "c:\program files\test param" will first try to launch c:\program.exe then
+// c:\program files\test.exe. We don't do that, we stop after at the first
+// space when there is no quotes.
+std::wstring GetPathFromCmdLine(const std::wstring &cmd_line) {
+ std::wstring exe_name;
+ // Check if it starts with '"'.
+ if (cmd_line[0] == L'\"') {
+ // Find the position of the second '"', this terminates the path.
+ std::wstring::size_type pos = cmd_line.find(L'\"', 1);
+ if (std::wstring::npos == pos)
+ return cmd_line;
+ exe_name = cmd_line.substr(1, pos - 1);
+ } else {
+ // There is no '"', that means that the appname is terminated at the
+ // first space.
+ std::wstring::size_type pos = cmd_line.find(L' ');
+ if (std::wstring::npos == pos) {
+ // There is no space, the cmd_line contains only the app_name
+ exe_name = cmd_line;
+ } else {
+ exe_name = cmd_line.substr(0, pos);
+ }
+ }
+
+ return exe_name;
+}
+
+// Returns true is the path in parameter is relative. False if it's
+// absolute.
+bool IsPathRelative(const std::wstring &path) {
+ // A path is Relative if it's not a UNC path beginnning with \\ or a
+ // path beginning with a drive. (i.e. X:\)
+ if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
+ return false;
+ return true;
+}
+
+// Converts a relative path to an absolute path.
+bool ConvertToAbsolutePath(const std::wstring& child_current_directory,
+ bool use_env_path, std::wstring *path) {
+ wchar_t file_buffer[MAX_PATH];
+ wchar_t *file_part = NULL;
+
+ // Here we should start by looking at the path where the child application was
+ // started. We don't have this information yet.
+ DWORD result = 0;
+ if (use_env_path) {
+ // Try with the complete path
+ result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer,
+ &file_part);
+ }
+
+ if (0 == result) {
+ // Try with the current directory of the child
+ result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL,
+ MAX_PATH, file_buffer, &file_part);
+ }
+
+ if (0 == result || result >= MAX_PATH)
+ return false;
+
+ *path = file_buffer;
+ return true;
+}
+
+} // namespace
+namespace sandbox {
+
+ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall open_thread = {
+ {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenThread)
+ };
+
+ static const IPCCall open_process = {
+ {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcess)
+ };
+
+ static const IPCCall process_token = {
+ {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessToken)
+ };
+
+ static const IPCCall process_tokenex = {
+ {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessTokenEx)
+ };
+
+ static const IPCCall create_params = {
+ {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::CreateProcessW)
+ };
+
+ ipc_calls_.push_back(open_thread);
+ ipc_calls_.push_back(open_process);
+ ipc_calls_.push_back(process_token);
+ ipc_calls_.push_back(process_tokenex);
+ ipc_calls_.push_back(create_params);
+}
+
+bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager,
+ int service) {
+ switch (service) {
+ case IPC_NTOPENTHREAD_TAG:
+ case IPC_NTOPENPROCESS_TAG:
+ case IPC_NTOPENPROCESSTOKEN_TAG:
+ case IPC_NTOPENPROCESSTOKENEX_TAG:
+ // There is no explicit policy for these services.
+ NOTREACHED();
+ return false;
+
+ case IPC_CREATEPROCESSW_TAG:
+ return INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessW,
+ CREATE_PROCESSW_ID, 44) &&
+ INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
+ CREATE_PROCESSA_ID, 44);
+
+ default:
+ return false;
+ }
+}
+
+bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access,
+ DWORD thread_id) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info,
+ desired_access, thread_id,
+ &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access,
+ DWORD process_id) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info,
+ desired_access, process_id,
+ &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process,
+ DWORD desired_access) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info,
+ process, desired_access,
+ &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process,
+ DWORD desired_access,
+ DWORD attributes) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info,
+ process,
+ desired_access,
+ attributes, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, std::wstring* name,
+ std::wstring* cmd_line,
+ std::wstring* cur_dir,
+ CountedBuffer* info) {
+ if (sizeof(PROCESS_INFORMATION) != info->Size())
+ return false;
+
+ // Check if there is an application name.
+ std::wstring exe_name;
+ if (!name->empty())
+ exe_name = *name;
+ else
+ exe_name = GetPathFromCmdLine(*cmd_line);
+
+ if (IsPathRelative(exe_name)) {
+ if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
+ // Cannot find the path. Maybe the file does not exist.
+ ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
+ return true;
+ }
+ }
+
+ const wchar_t* const_exe_name = exe_name.c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(const_exe_name);
+
+ EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG,
+ params.GetBase());
+
+ PROCESS_INFORMATION* proc_info =
+ reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
+ // Here we force the app_name to be the one we used for the policy lookup.
+ // If our logic was wrong, at least we wont allow create a random process.
+ DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info,
+ exe_name, *cmd_line,
+ proc_info);
+
+ ipc->return_info.win32_result = ret;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_thread_dispatcher.h b/chromium/sandbox/win/src/process_thread_dispatcher.h
new file mode 100644
index 00000000000..1cc5743998f
--- /dev/null
+++ b/chromium/sandbox/win/src/process_thread_dispatcher.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 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_THREAD_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles process and thread-related IPC calls.
+class ThreadProcessDispatcher : public Dispatcher {
+ public:
+ explicit ThreadProcessDispatcher(PolicyBase* policy_base);
+ ~ThreadProcessDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+ private:
+ // Processes IPC requests coming from calls to NtOpenThread() in the target.
+ bool NtOpenThread(IPCInfo* ipc, DWORD desired_access, DWORD thread_id);
+
+ // Processes IPC requests coming from calls to NtOpenProcess() in the target.
+ bool NtOpenProcess(IPCInfo* ipc, DWORD desired_access, DWORD process_id);
+
+ // Processes IPC requests from calls to NtOpenProcessToken() in the target.
+ bool NtOpenProcessToken(IPCInfo* ipc, HANDLE process, DWORD desired_access);
+
+ // Processes IPC requests from calls to NtOpenProcessTokenEx() in the target.
+ bool NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, DWORD desired_access,
+ DWORD attributes);
+
+ // Processes IPC requests coming from calls to CreateProcessW() in the target.
+ bool CreateProcessW(IPCInfo* ipc, std::wstring* name, std::wstring* cmd_line,
+ std::wstring* cur_dir, CountedBuffer* info);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
diff --git a/chromium/sandbox/win/src/process_thread_interception.cc b/chromium/sandbox/win/src/process_thread_interception.cc
new file mode 100644
index 00000000000..cb1017b07b6
--- /dev/null
+++ b/chromium/sandbox/win/src/process_thread_interception.cc
@@ -0,0 +1,447 @@
+// Copyright (c) 2006-2011 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_thread_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Hooks NtOpenThread and proxy the call to the broker if it's trying to
+// open a thread in the same process.
+NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
+ PHANDLE thread, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes,
+ client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32 thread_id = 0;
+ bool should_break = false;
+ __try {
+ // We support only the calls for the current process
+ if (NULL != client_id->UniqueProcess)
+ should_break = true;
+
+ // Object attributes should be NULL or empty.
+ if (!should_break && NULL != object_attributes) {
+ if (0 != object_attributes->Attributes ||
+ NULL != object_attributes->ObjectName ||
+ NULL != object_attributes->RootDirectory ||
+ NULL != object_attributes->SecurityDescriptor ||
+ NULL != object_attributes->SecurityQualityOfService) {
+ should_break = true;
+ }
+ }
+
+ thread_id = static_cast<uint32>(
+ reinterpret_cast<ULONG_PTR>(client_id->UniqueThread));
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ if (should_break)
+ break;
+
+ if (!ValidParameter(thread, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access,
+ thread_id, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // The nt_status here is most likely STATUS_INVALID_CID because
+ // in the broker we set the process id in the CID (client ID) param
+ // to be the current process. If you try to open a thread from another
+ // process you will get this INVALID_CID error. On the other hand, if you
+ // try to open a thread in your own process, it should return success.
+ // We don't want to return STATUS_INVALID_CID here, so we return the
+ // return of the original open thread status, which is most likely
+ // STATUS_ACCESS_DENIED.
+ break;
+
+ __try {
+ // Write the output parameters.
+ *thread = answer.handle;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+// Hooks NtOpenProcess and proxy the call to the broker if it's trying to
+// open the current process.
+NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
+ PHANDLE process, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes,
+ client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32 process_id = 0;
+ bool should_break = false;
+ __try {
+ // Object attributes should be NULL or empty.
+ if (!should_break && NULL != object_attributes) {
+ if (0 != object_attributes->Attributes ||
+ NULL != object_attributes->ObjectName ||
+ NULL != object_attributes->RootDirectory ||
+ NULL != object_attributes->SecurityDescriptor ||
+ NULL != object_attributes->SecurityQualityOfService) {
+ should_break = true;
+ }
+ }
+
+ process_id = static_cast<uint32>(
+ reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess));
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ if (should_break)
+ break;
+
+ if (!ValidParameter(process, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access,
+ process_id, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *process = answer.handle;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+
+NTSTATUS WINAPI TargetNtOpenProcessToken(
+ NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process,
+ ACCESS_MASK desired_access, PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessToken(process, desired_access, token);
+ if (NT_SUCCESS(status))
+ return status;
+
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process,
+ desired_access, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenProcessTokenEx(
+ NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process,
+ ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access,
+ handle_attributes, token);
+ if (NT_SUCCESS(status))
+ return status;
+
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process,
+ desired_access, handle_attributes, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
+ LPCWSTR application_name, LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ if (orig_CreateProcessW(application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags,
+ environment, current_directory, startup_info,
+ process_information)) {
+ return TRUE;
+ }
+ DWORD original_error = ::GetLastError();
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return FALSE;
+
+ do {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ const wchar_t* cur_dir = NULL;
+
+ wchar_t current_directory[MAX_PATH];
+ DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ if (0 != result && result < MAX_PATH)
+ cur_dir = current_directory;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ InOutCountedBuffer proc_info(process_information,
+ sizeof(PROCESS_INFORMATION));
+
+ ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name,
+ command_line, cur_dir, proc_info, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return FALSE;
+
+ return TRUE;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return FALSE;
+}
+
+BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
+ LPCSTR application_name, LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ if (orig_CreateProcessA(application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags,
+ environment, current_directory, startup_info,
+ process_information)) {
+ return TRUE;
+ }
+ DWORD original_error = ::GetLastError();
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return FALSE;
+
+ do {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ // Convert the input params to unicode.
+ UNICODE_STRING *cmd_unicode = NULL;
+ UNICODE_STRING *app_unicode = NULL;
+ if (command_line) {
+ cmd_unicode = AnsiToUnicode(command_line);
+ if (!cmd_unicode)
+ break;
+ }
+
+ if (application_name) {
+ app_unicode = AnsiToUnicode(application_name);
+ if (!app_unicode) {
+ operator delete(cmd_unicode, NT_ALLOC);
+ break;
+ }
+ }
+
+ const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL;
+ const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL;
+ const wchar_t* cur_dir = NULL;
+
+ wchar_t current_directory[MAX_PATH];
+ DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ if (0 != result && result < MAX_PATH)
+ cur_dir = current_directory;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ InOutCountedBuffer proc_info(process_information,
+ sizeof(PROCESS_INFORMATION));
+
+ ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name,
+ cmd_line, cur_dir, proc_info, &answer);
+
+ operator delete(cmd_unicode, NT_ALLOC);
+ operator delete(app_unicode, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return FALSE;
+
+ return TRUE;
+ } while (false);
+
+ ::SetLastError(original_error);
+ 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
new file mode 100644
index 00000000000..7d2d533dafd
--- /dev/null
+++ b/chromium/sandbox/win/src/process_thread_interception.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2006-2011 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__
+#define SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__
+
+namespace sandbox {
+
+extern "C" {
+
+typedef BOOL (WINAPI *CreateProcessWFunction)(
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation);
+
+typedef BOOL (WINAPI *CreateProcessAFunction)(
+ LPCSTR lpApplicationName,
+ LPSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCSTR lpCurrentDirectory,
+ LPSTARTUPINFOA lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation);
+
+typedef HANDLE (WINAPI *CreateThreadFunction)(
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ SIZE_T dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress,
+ PVOID lpParameter,
+ DWORD dwCreationFlags,
+ LPDWORD lpThreadId);
+
+typedef LCID (WINAPI *GetUserDefaultLCIDFunction)();
+
+// Interception of NtOpenThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThread(
+ NtOpenThreadFunction orig_OpenThread, PHANDLE thread,
+ ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcess on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcess(
+ NtOpenProcessFunction orig_OpenProcess, PHANDLE process,
+ ACCESS_MASK desired_access, POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcessToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessToken(
+ NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process,
+ ACCESS_MASK desired_access, PHANDLE token);
+
+// Interception of NtOpenProcessTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenProcessTokenEx(
+ NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process,
+ ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token);
+
+// Interception of CreateProcessW and A in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessW(
+ CreateProcessWFunction orig_CreateProcessW, LPCWSTR application_name,
+ LPWSTR command_line, LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags,
+ LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetCreateProcessA(
+ CreateProcessAFunction orig_CreateProcessA, LPCSTR application_name,
+ LPSTR command_line, LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes, BOOL inherit_handles, DWORD flags,
+ 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
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__
diff --git a/chromium/sandbox/win/src/process_thread_policy.cc b/chromium/sandbox/win/src/process_thread_policy.cc
new file mode 100644
index 00000000000..9493b9ea199
--- /dev/null
+++ b/chromium/sandbox/win/src/process_thread_policy.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2011 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_thread_policy.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// These are the only safe rights that can be given to a sandboxed
+// process for the process created by the broker. All others are potential
+// vectors of privilege elevation.
+const DWORD kProcessRights = SYNCHRONIZE |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_QUERY_LIMITED_INFORMATION |
+ PROCESS_TERMINATE |
+ PROCESS_SUSPEND_RESUME;
+
+const DWORD kThreadRights = SYNCHRONIZE |
+ THREAD_TERMINATE |
+ THREAD_SUSPEND_RESUME |
+ THREAD_QUERY_INFORMATION |
+ THREAD_QUERY_LIMITED_INFORMATION |
+ THREAD_SET_LIMITED_INFORMATION;
+
+// Creates a child process and duplicates the handles to 'target_process'. The
+// remaining parameters are the same as CreateProcess().
+BOOL CreateProcessExWHelper(HANDLE target_process, BOOL give_full_access,
+ LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles, DWORD dwCreationFlags,
+ LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation) {
+ if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
+ lpThreadAttributes, bInheritHandles, dwCreationFlags,
+ lpEnvironment, lpCurrentDirectory, lpStartupInfo,
+ lpProcessInformation)) {
+ return FALSE;
+ }
+
+ DWORD process_access = kProcessRights;
+ DWORD thread_access = kThreadRights;
+ if (give_full_access) {
+ process_access = PROCESS_ALL_ACCESS;
+ thread_access = THREAD_ALL_ACCESS;
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess,
+ target_process, &lpProcessInformation->hProcess,
+ process_access, FALSE, DUPLICATE_CLOSE_SOURCE)) {
+ ::CloseHandle(lpProcessInformation->hThread);
+ return FALSE;
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread,
+ target_process, &lpProcessInformation->hThread,
+ thread_access, FALSE, DUPLICATE_CLOSE_SOURCE)) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+}
+
+namespace sandbox {
+
+bool ProcessPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ scoped_ptr<PolicyRule> process;
+ switch (semantics) {
+ case TargetPolicy::PROCESS_MIN_EXEC: {
+ process.reset(new PolicyRule(GIVE_READONLY));
+ break;
+ };
+ case TargetPolicy::PROCESS_ALL_EXEC: {
+ process.reset(new PolicyRule(GIVE_ALLACCESS));
+ break;
+ };
+ default: {
+ return false;
+ };
+ }
+
+ if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IPC_CREATEPROCESSW_TAG, process.get())) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info,
+ uint32 desired_access,
+ uint32 thread_id,
+ HANDLE* handle) {
+ *handle = NULL;
+
+ NtOpenThreadFunction NtOpenThread = NULL;
+ ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread);
+
+ OBJECT_ATTRIBUTES attributes = {0};
+ attributes.Length = sizeof(attributes);
+ CLIENT_ID client_id = {0};
+ client_id.UniqueProcess = reinterpret_cast<PVOID>(
+ static_cast<ULONG_PTR>(client_info.process_id));
+ client_id.UniqueThread =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(thread_id));
+
+ HANDLE local_handle;
+ NTSTATUS status = NtOpenThread(&local_handle, desired_access, &attributes,
+ &client_id);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ ::CloseHandle(local_handle);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info,
+ uint32 desired_access,
+ uint32 process_id,
+ HANDLE* handle) {
+ *handle = NULL;
+
+ NtOpenProcessFunction NtOpenProcess = NULL;
+ ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess);
+
+ if (client_info.process_id != process_id)
+ return STATUS_ACCESS_DENIED;
+
+ OBJECT_ATTRIBUTES attributes = {0};
+ attributes.Length = sizeof(attributes);
+ CLIENT_ID client_id = {0};
+ client_id.UniqueProcess = reinterpret_cast<PVOID>(
+ static_cast<ULONG_PTR>(client_info.process_id));
+ HANDLE local_handle;
+ NTSTATUS status = NtOpenProcess(&local_handle, desired_access, &attributes,
+ &client_id);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ ::CloseHandle(local_handle);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32 desired_access,
+ HANDLE* handle) {
+ *handle = NULL;
+ NtOpenProcessTokenFunction NtOpenProcessToken = NULL;
+ ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken);
+
+ if (CURRENT_PROCESS != process)
+ return STATUS_ACCESS_DENIED;
+
+ HANDLE local_handle;
+ NTSTATUS status = NtOpenProcessToken(client_info.process, desired_access,
+ &local_handle);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ ::CloseHandle(local_handle);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32 desired_access,
+ uint32 attributes,
+ HANDLE* handle) {
+ *handle = NULL;
+ NtOpenProcessTokenExFunction NtOpenProcessTokenEx = NULL;
+ ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx);
+
+ if (CURRENT_PROCESS != process)
+ return STATUS_ACCESS_DENIED;
+
+ HANDLE local_handle;
+ NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access,
+ attributes, &local_handle);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ ::CloseHandle(local_handle);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &app_name,
+ const std::wstring &command_line,
+ PROCESS_INFORMATION* process_info) {
+ // The only action supported is ASK_BROKER which means create the process.
+ if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ scoped_ptr_malloc<wchar_t> 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,
+ app_name.c_str(), cmd_line.get(), NULL, NULL,
+ FALSE, 0, NULL, NULL, &startup_info,
+ process_info)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/process_thread_policy.h b/chromium/sandbox/win/src/process_thread_policy.h
new file mode 100644
index 00000000000..c35c52b5e6d
--- /dev/null
+++ b/chromium/sandbox/win/src/process_thread_policy.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2006-2010 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_THREAD_POLICY_H_
+#define SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
+
+#include <string>
+
+#include "sandbox/win/src/policy_low_level.h"
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum EvalResult;
+
+// This class centralizes most of the knowledge related to process execution.
+class ProcessPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level.
+ // policy rule for process creation
+ // 'name' is the executable to be spawn.
+ // 'semantics' is the desired semantics.
+ // '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);
+
+ // Opens a thread from the child process and returns the handle.
+ // client_info contains the information about the child process,
+ // desired_access is the access requested by the child and thread_id
+ // is the thread_id to be opened.
+ // The function returns the return value of NtOpenThread.
+ static NTSTATUS OpenThreadAction(const ClientInfo& client_info,
+ uint32 desired_access,
+ uint32 thread_id,
+ HANDLE* handle);
+
+ // Opens the process id passed in and returns the duplicated handle to
+ // the child. We only allow the child processes to open themselves. Any other
+ // pid open is denied.
+ static NTSTATUS OpenProcessAction(const ClientInfo& client_info,
+ uint32 desired_access,
+ uint32 process_id,
+ HANDLE* handle);
+
+ // Opens the token associated with the process and returns the duplicated
+ // handle to the child. We only allow the child processes to open his own
+ // token (using ::GetCurrentProcess()).
+ static NTSTATUS OpenProcessTokenAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32 desired_access,
+ HANDLE* handle);
+
+ // Opens the token associated with the process and returns the duplicated
+ // handle to the child. We only allow the child processes to open his own
+ // token (using ::GetCurrentProcess()).
+ static NTSTATUS OpenProcessTokenExAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32 desired_access,
+ uint32 attributes,
+ HANDLE* handle);
+
+ // Processes a 'CreateProcessW()' request from the target.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'app_name' : The full path of the process to be created.
+ // 'command_line' : The command line passed to the created process.
+ static DWORD CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &app_name,
+ const std::wstring &command_line,
+ PROCESS_INFORMATION* process_info);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
diff --git a/chromium/sandbox/win/src/registry_dispatcher.cc b/chromium/sandbox/win/src/registry_dispatcher.cc
new file mode 100644
index 00000000000..f4dc5f5288b
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_dispatcher.cc
@@ -0,0 +1,161 @@
+// Copyright (c) 2011 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/registry_dispatcher.h"
+
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/registry_interception.h"
+#include "sandbox/win/src/registry_policy.h"
+
+namespace {
+
+// Builds a path using the root directory and the name.
+bool GetCompletePath(HANDLE root, const std::wstring& name,
+ std::wstring* complete_name) {
+ if (root) {
+ if (!sandbox::GetPathFromHandle(root, complete_name))
+ return false;
+
+ *complete_name += L"\\";
+ *complete_name += name;
+ } else {
+ *complete_name = name;
+ }
+
+ return true;
+}
+
+}
+
+namespace sandbox {
+
+RegistryDispatcher::RegistryDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IPC_NTCREATEKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE,
+ ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtCreateKey)
+ };
+
+ static const IPCCall open_params = {
+ {IPC_NTOPENKEY_TAG, WCHAR_TYPE, ULONG_TYPE, VOIDPTR_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtOpenKey)
+ };
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_params);
+}
+
+bool RegistryDispatcher::SetupService(InterceptionManager* manager,
+ int service) {
+ if (IPC_NTCREATEKEY_TAG == service)
+ return INTERCEPT_NT(manager, NtCreateKey, CREATE_KEY_ID, 32);
+
+ if (IPC_NTOPENKEY_TAG == service) {
+ bool result = INTERCEPT_NT(manager, NtOpenKey, OPEN_KEY_ID, 16);
+ if (base::win::GetVersion() >= base::win::VERSION_WIN7)
+ result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20);
+ return result;
+ }
+
+ return false;
+}
+
+bool RegistryDispatcher::NtCreateKey(
+ IPCInfo* ipc, std::wstring* name, DWORD attributes, HANDLE root,
+ DWORD desired_access, DWORD title_index, DWORD create_options) {
+ base::win::ScopedHandle root_handle;
+ std::wstring real_path = *name;
+
+ // If there is a root directory, we need to duplicate the handle to make
+ // it valid in this process.
+ if (root) {
+ if (!::DuplicateHandle(ipc->client_info->process, root,
+ ::GetCurrentProcess(), &root, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ return false;
+
+ root_handle.Set(root);
+ }
+
+ if (!GetCompletePath(root, *name, &real_path))
+ return false;
+
+ const wchar_t* regname = real_path.c_str();
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::NAME] = ParamPickerMake(regname);
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result = policy_base_->EvalPolicy(IPC_NTCREATEKEY_TAG,
+ params.GetBase());
+
+ HANDLE handle;
+ NTSTATUS nt_status;
+ ULONG disposition = 0;
+ if (!RegistryPolicy::CreateKeyAction(result, *ipc->client_info, *name,
+ attributes, root, desired_access,
+ title_index, create_options, &handle,
+ &nt_status, &disposition)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].unsigned_int = disposition;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, std::wstring* name,
+ DWORD attributes, HANDLE root,
+ DWORD desired_access) {
+ base::win::ScopedHandle root_handle;
+ std::wstring real_path = *name;
+
+ // If there is a root directory, we need to duplicate the handle to make
+ // it valid in this process.
+ if (root) {
+ if (!::DuplicateHandle(ipc->client_info->process, root,
+ ::GetCurrentProcess(), &root, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ return false;
+ root_handle.Set(root);
+ }
+
+ if (!GetCompletePath(root, *name, &real_path))
+ return false;
+
+ const wchar_t* regname = real_path.c_str();
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::NAME] = ParamPickerMake(regname);
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result = policy_base_->EvalPolicy(IPC_NTOPENKEY_TAG,
+ params.GetBase());
+ HANDLE handle;
+ NTSTATUS nt_status;
+ if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name,
+ attributes, root, desired_access, &handle,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/registry_dispatcher.h b/chromium/sandbox/win/src/registry_dispatcher.h
new file mode 100644
index 00000000000..782a070e4e2
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_dispatcher.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 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_REGISTRY_DISPATCHER_H_
+#define SANDBOX_SRC_REGISTRY_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles registry-related IPC calls.
+class RegistryDispatcher : public Dispatcher {
+ public:
+ explicit RegistryDispatcher(PolicyBase* policy_base);
+ ~RegistryDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateKey in the target.
+ bool NtCreateKey(IPCInfo* ipc, std::wstring* name, DWORD attributes,
+ HANDLE root, DWORD desired_access,
+ DWORD title_index, DWORD create_options);
+
+ // Processes IPC requests coming from calls to NtOpenKey in the target.
+ bool NtOpenKey(IPCInfo* ipc, std::wstring* name, DWORD attributes,
+ HANDLE root, DWORD desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_DISPATCHER_H_
diff --git a/chromium/sandbox/win/src/registry_interception.cc b/chromium/sandbox/win/src/registry_interception.cc
new file mode 100644
index 00000000000..936d4acc713
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_interception.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2006-2008 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/registry_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey,
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index, PUNICODE_STRING class_name,
+ ULONG create_options, PULONG disposition) {
+ // Check if the process can create it first.
+ NTSTATUS status = orig_CreateKey(key, desired_access, object_attributes,
+ title_index, class_name, create_options,
+ disposition);
+ if (NT_SUCCESS(status))
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+ if (disposition && !ValidParameter(disposition, sizeof(ULONG), WRITE))
+ break;
+
+ // At this point we don't support class_name.
+ if (class_name && class_name->Buffer && class_name->Length)
+ break;
+
+ // We don't support creating link keys, volatile keys and backup/restore.
+ if (create_options)
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ wchar_t* name;
+ uint32 attributes = 0;
+ HANDLE root_directory = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ &root_directory);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ ResultCode code = CrossCall(ipc, IPC_NTCREATEKEY_TAG, name, attributes,
+ root_directory, desired_access, title_index,
+ create_options, &answer);
+
+ operator delete(name, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // TODO(nsylvain): We should return answer.nt_status here instead
+ // of status. We can do this only after we checked the policy.
+ // otherwise we will returns ACCESS_DENIED for all paths
+ // that are not specified by a policy, even though your token allows
+ // access to that path, and the original call had a more meaningful
+ // error. Bug 4369
+ break;
+
+ __try {
+ *key = answer.handle;
+
+ if (disposition)
+ *disposition = answer.extended[0].unsigned_int;
+
+ status = answer.nt_status;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status, PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ wchar_t* name;
+ uint32 attributes;
+ HANDLE root_directory;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ &root_directory);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_NTOPENKEY_TAG, name, attributes,
+ root_directory, desired_access, &answer);
+
+ operator delete(name, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // TODO(nsylvain): We should return answer.nt_status here instead
+ // of status. We can do this only after we checked the policy.
+ // otherwise we will returns ACCESS_DENIED for all paths
+ // that are not specified by a policy, even though your token allows
+ // access to that path, and the original call had a more meaningful
+ // error. Bug 4369
+ break;
+
+ __try {
+ *key = answer.handle;
+ status = answer.nt_status;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey, PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenKey(key, desired_access, object_attributes);
+ if (NT_SUCCESS(status))
+ return status;
+
+ return CommonNtOpenKey(status, key, desired_access, object_attributes);
+}
+
+NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx,
+ PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenKeyEx(key, desired_access, object_attributes,
+ open_options);
+
+ // We do not support open_options at this time. The 2 current known values
+ // are REG_OPTION_CREATE_LINK, to open a symbolic link, and
+ // REG_OPTION_BACKUP_RESTORE to open the key with special privileges.
+ if (NT_SUCCESS(status) || open_options != 0)
+ return status;
+
+ return CommonNtOpenKey(status, key, desired_access, object_attributes);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/registry_interception.h b/chromium/sandbox/win/src/registry_interception.h
new file mode 100644
index 00000000000..c3cbde02e72
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_interception.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2008 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_REGISTRY_INTERCEPTION_H__
+#define SANDBOX_SRC_REGISTRY_INTERCEPTION_H__
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtCreateKey on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey(
+ NtCreateKeyFunction orig_CreateKey, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG title_index,
+ PUNICODE_STRING class_name, ULONG create_options, PULONG disposition);
+
+// Interception of NtOpenKey on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey(
+ NtOpenKeyFunction orig_OpenKey, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// Interception of NtOpenKeyEx on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx(
+ NtOpenKeyExFunction orig_OpenKeyEx, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG open_options);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_INTERCEPTION_H__
diff --git a/chromium/sandbox/win/src/registry_policy.cc b/chromium/sandbox/win/src/registry_policy.cc
new file mode 100644
index 00000000000..37e6ddbebd9
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_policy.cc
@@ -0,0 +1,225 @@
+// Copyright (c) 2006-2008 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 <string>
+
+#include "sandbox/win/src/registry_policy.h"
+
+#include "base/logging.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS |
+ KEY_NOTIFY | KEY_READ | GENERIC_READ |
+ GENERIC_EXECUTE | READ_CONTROL;
+
+// Opens the key referenced by |obj_attributes| with |access| and
+// checks what permission was given. Remove the WRITE flags and update
+// |access| with the new value.
+NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes,
+ DWORD* access) {
+ NtOpenKeyFunction NtOpenKey = NULL;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
+
+ NtCloseFunction NtClose = NULL;
+ ResolveNTFunctionPtr("NtClose", &NtClose);
+
+ NtQueryObjectFunction NtQueryObject = NULL;
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ // Open the key.
+ HANDLE handle;
+ NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ OBJECT_BASIC_INFORMATION info = {0};
+ status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info),
+ NULL);
+ NtClose(handle);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ *access = info.GrantedAccess & kAllowedRegFlags;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ ULONG title_index,
+ UNICODE_STRING* class_name,
+ ULONG create_options,
+ ULONG* disposition,
+ HANDLE target_process) {
+ NtCreateKeyFunction NtCreateKey = NULL;
+ ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey);
+
+ if (MAXIMUM_ALLOWED & desired_access) {
+ NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access);
+ if (!NT_SUCCESS(status))
+ return STATUS_ACCESS_DENIED;
+ }
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status = NtCreateKey(&local_handle, desired_access, obj_attributes,
+ title_index, class_name, create_options,
+ disposition);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ target_process, target_key_handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ HANDLE target_process) {
+ NtOpenKeyFunction NtOpenKey = NULL;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
+
+ if (MAXIMUM_ALLOWED & desired_access) {
+ NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access);
+ if (!NT_SUCCESS(status))
+ return STATUS_ACCESS_DENIED;
+ }
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ target_process, target_key_handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+}
+
+namespace sandbox {
+
+bool RegistryPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring resovled_name(name);
+ if (resovled_name.empty()) {
+ return false;
+ }
+
+ if (!ResolveRegistryName(resovled_name, &resovled_name))
+ return false;
+
+ name = resovled_name.c_str();
+
+ EvalResult result = ASK_BROKER;
+
+ PolicyRule open(result);
+ PolicyRule create(result);
+
+ switch (semantics) {
+ case TargetPolicy::REG_ALLOW_READONLY: {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write. Here we also support MAXIMUM_ALLOWED, but we are going
+ // to expand it to read-only before the call.
+ DWORD restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED);
+ open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND);
+ create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND);
+ break;
+ }
+ case TargetPolicy::REG_ALLOW_ANY: {
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IPC_NTCREATEKEY_TAG, &create)) {
+ return false;
+ }
+
+ if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IPC_NTOPENKEY_TAG, &open)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool RegistryPolicy::CreateKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &key,
+ uint32 attributes,
+ HANDLE root_directory,
+ uint32 desired_access,
+ uint32 title_index,
+ uint32 create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG* disposition) {
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ // We don't support creating link keys, volatile keys or backup/restore.
+ if (create_options) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(key, attributes, root_directory, &obj_attributes,
+ &uni_name);
+ *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes,
+ title_index, NULL, create_options,
+ disposition, client_info.process);
+ return true;
+}
+
+bool RegistryPolicy::OpenKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &key,
+ uint32 attributes,
+ HANDLE root_directory,
+ uint32 desired_access,
+ HANDLE* handle,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(key, attributes, root_directory, &obj_attributes,
+ &uni_name);
+ *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes,
+ client_info.process);
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/registry_policy.h b/chromium/sandbox/win/src/registry_policy.h
new file mode 100644
index 00000000000..8badde227d5
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_policy.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2006-2008 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_REGISTRY_POLICY_H__
+#define SANDBOX_SRC_REGISTRY_POLICY_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.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 registry policy
+class RegistryPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for registry IO, in particular open or create actions.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a create request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool CreateKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &key,
+ uint32 attributes,
+ HANDLE root_directory,
+ uint32 desired_access,
+ uint32 title_index,
+ uint32 create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG* disposition);
+
+ // Performs the desired policy action on an open request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool OpenKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &key,
+ uint32 attributes,
+ HANDLE root_directory,
+ uint32 desired_access,
+ HANDLE* handle,
+ NTSTATUS* nt_status);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_POLICY_H__
diff --git a/chromium/sandbox/win/src/registry_policy_test.cc b/chromium/sandbox/win/src/registry_policy_test.cc
new file mode 100644
index 00000000000..d8ee34b06dc
--- /dev/null
+++ b/chromium/sandbox/win/src/registry_policy_test.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2006-2010 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 <shlobj.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/src/registry_policy.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+
+namespace {
+
+static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS |
+ KEY_NOTIFY | KEY_READ | GENERIC_READ |
+ GENERIC_EXECUTE | READ_CONTROL;
+
+#define BINDNTDLL(name) \
+ name ## Function name = reinterpret_cast<name ## Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+bool IsKeyOpenForRead(HKEY handle) {
+ BINDNTDLL(NtQueryObject);
+
+ OBJECT_BASIC_INFORMATION info = {0};
+ NTSTATUS status = NtQueryObject(handle, ObjectBasicInformation, &info,
+ sizeof(info), NULL);
+
+ if (!NT_SUCCESS(status))
+ return false;
+
+ if ((info.GrantedAccess & (~kAllowedRegFlags)) != 0)
+ return false;
+ return true;
+}
+
+}
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Reg_OpenKey(int argc, wchar_t **argv) {
+ if (argc != 4)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ REGSAM desired_access = 0;
+ ULONG options = 0;
+ if (wcscmp(argv[1], L"read") == 0) {
+ desired_access = KEY_READ;
+ } else if (wcscmp(argv[1], L"write") == 0) {
+ desired_access = KEY_ALL_ACCESS;
+ } else if (wcscmp(argv[1], L"link") == 0) {
+ options = REG_OPTION_CREATE_LINK;
+ desired_access = KEY_ALL_ACCESS;
+ } else {
+ desired_access = MAXIMUM_ALLOWED;
+ }
+
+ HKEY root = GetReservedKeyFromName(argv[2]);
+ HKEY key;
+ LRESULT result = 0;
+
+ if (wcscmp(argv[0], L"create") == 0)
+ result = ::RegCreateKeyEx(root, argv[3], 0, NULL, options, desired_access,
+ NULL, &key, NULL);
+ else
+ result = ::RegOpenKeyEx(root, argv[3], 0, desired_access, &key);
+
+ if (ERROR_SUCCESS == result) {
+ if (MAXIMUM_ALLOWED == desired_access) {
+ if (!IsKeyOpenForRead(key)) {
+ ::RegCloseKey(key);
+ return SBOX_TEST_FAILED;
+ }
+ }
+ ::RegCloseKey(key);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (ERROR_ACCESS_DENIED == result) {
+ return SBOX_TEST_DENIED;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+TEST(RegistryPolicyTest, TestKeyAnyAccess) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
+
+ // Tests read access on key allowed for read-write.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ if (::IsUserAnAdmin()) {
+ // Tests write access on key allowed for read-write.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey create write HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey open write HKEY_LOCAL_MACHINE software\\microsoft"));
+ }
+
+ // Tests subdirectory access on keys where we don't have subdirectory acess.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create read "
+ L"HKEY_LOCAL_MACHINE software\\microsoft\\Windows"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey open read "
+ L"HKEY_LOCAL_MACHINE software\\microsoft\\windows"));
+
+ // Tests to see if we can create keys where we dont have subdirectory access.
+ // This is denied.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Microsoft\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Microsoft\\google_unit_tests");
+
+ // Tests if we need to handle differently the "\\" at the end.
+ // This is denied. We need to add both rules.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft\\"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft\\"));
+}
+
+TEST(RegistryPolicyTest, TestKeyNoAccess) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ // Tests read access where we don't have access at all.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software"));
+}
+
+TEST(RegistryPolicyTest, TestKeyReadOnlyAccess) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ // Tests subdirectory acess on keys where we have subdirectory acess.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create read "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey open read "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
+
+ // Tests to see if we can create keys where we have subdirectory access.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
+}
+
+TEST(RegistryPolicyTest, TestKeyAllAccessSubDir) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ if (::IsUserAnAdmin()) {
+ // Tests to see if we can create keys where we have subdirectory access.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
+ }
+}
+
+TEST(RegistryPolicyTest, TestKeyCreateLink) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ // Tests to see if we can create a registry link key.
+ // NOTE: In theory here we should make sure to check for SBOX_TEST_DENIED
+ // instead of !SBOX_TEST_SUCCEEDED, but unfortunately the result is not
+ // access denied. Internally RegCreateKeyEx (At least on Vista 64) tries to
+ // create the link, and we return successfully access denied, then, it
+ // decides to try to break the path in multiple chunks, and create the links
+ // one by one. In this scenario, it tries to create "HKLM\Software" as a
+ // link key, which obviously fail with STATUS_OBJECT_NAME_COLLISION, and
+ // this is what is returned to the user.
+ EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Reg_OpenKey create link "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ // In case our code fails, and the call works, we need to delete the new
+ // link. There is no api for this, so we need to use the NT call.
+ HKEY key = NULL;
+ LRESULT result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"software\\Policies\\google_unit_tests",
+ REG_OPTION_OPEN_LINK, MAXIMUM_ALLOWED,
+ &key);
+
+ if (!result) {
+ HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
+ NtDeleteKeyFunction NtDeleteKey =
+ reinterpret_cast<NtDeleteKeyFunction>(GetProcAddress(ntdll,
+ "NtDeleteKey"));
+ NtDeleteKey(key);
+ }
+}
+
+TEST(RegistryPolicyTest, TestKeyReadOnlyHKCU) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Software"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_USERS\\.default"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_USERS\\.default\\software"));
+
+ // Tests read access where we only have read-only access.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey create read HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey open read HKEY_CURRENT_USER software"));
+
+ // Tests write access where we only have read-only acess.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
+ L"Reg_OpenKey create write HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(
+ L"Reg_OpenKey open write HKEY_CURRENT_USER software"));
+
+ // Tests maximum allowed access where we only have read-only access.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey create maximum_allowed HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(
+ L"Reg_OpenKey open maximum_allowed HKEY_CURRENT_USER software"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/resolver.cc b/chromium/sandbox/win/src/resolver.cc
new file mode 100644
index 00000000000..6616fa52c36
--- /dev/null
+++ b/chromium/sandbox/win/src/resolver.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2006-2010 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/resolver.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS ResolverThunk::Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ if (NULL == thunk_storage || 0 == storage_bytes ||
+ NULL == target_module || NULL == target_name)
+ return STATUS_INVALID_PARAMETER;
+
+ if (storage_bytes < GetThunkSize())
+ return STATUS_BUFFER_TOO_SMALL;
+
+ NTSTATUS ret = STATUS_SUCCESS;
+ if (NULL == interceptor_entry_point) {
+ ret = ResolveInterceptor(interceptor_module, interceptor_name,
+ &interceptor_entry_point);
+ if (!NT_SUCCESS(ret))
+ return ret;
+ }
+
+ ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ interceptor_ = interceptor_entry_point;
+
+ return ret;
+}
+
+NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ DCHECK_NT(address);
+ if (!interceptor_module)
+ return STATUS_INVALID_PARAMETER;
+
+ base::win::PEImage pe(interceptor_module);
+ if (!pe.VerifyMagic())
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ *address = pe.GetProcAddress(interceptor_name);
+
+ if (!(*address))
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ return STATUS_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/resolver.h b/chromium/sandbox/win/src/resolver.h
new file mode 100644
index 00000000000..85f1e919908
--- /dev/null
+++ b/chromium/sandbox/win/src/resolver.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2010 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.
+
+// Defines ResolverThunk, the interface for classes that perform interceptions.
+// For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/nt_internals.h"
+
+#ifndef SANDBOX_SRC_RESOLVER_H__
+#define SANDBOX_SRC_RESOLVER_H__
+
+namespace sandbox {
+
+// A resolver is the object in charge of performing the actual interception of
+// a function. There should be a concrete implementation of a resolver roughly
+// per type of interception.
+class ResolverThunk {
+ public:
+ ResolverThunk() {}
+ virtual ~ResolverThunk() {}
+
+ // Performs the actual interception of a function.
+ // target_name is an exported function from the module loaded at
+ // target_module, and must be replaced by interceptor_name, exported from
+ // interceptor_module. interceptor_entry_point can be provided instead of
+ // interceptor_name / interceptor_module.
+ // thunk_storage must point to a buffer on the child's address space, to hold
+ // the patch thunk, and related data. If provided, storage_used will receive
+ // the number of bytes used from thunk_storage.
+ //
+ // Example: (without error checking)
+ //
+ // size_t size = resolver.GetThunkSize();
+ // char* buffer = ::VirtualAllocEx(child_process, NULL, size,
+ // MEM_COMMIT, PAGE_READWRITE);
+ // resolver.Setup(ntdll_module, NULL, L"NtCreateFile", NULL,
+ // &MyReplacementFunction, buffer, size, NULL);
+ //
+ // In general, the idea is to allocate a single big buffer for all
+ // interceptions on the same dll, and call Setup n times.
+ // WARNING: This means that any data member that is specific to a single
+ // interception must be reset within this method.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) = 0;
+
+ // Gets the address of function_name inside module (main exe).
+ virtual NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address);
+
+ // Gets the address of an exported function_name inside module.
+ virtual NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address);
+
+ // Gets the required buffer size for this type of thunk.
+ virtual size_t GetThunkSize() const = 0;
+
+ protected:
+ // Performs basic initialization on behalf of a concrete instance of a
+ // resolver. That is, parameter validation and resolution of the target
+ // and the interceptor into the member variables.
+ //
+ // target_name is an exported function from the module loaded at
+ // target_module, and must be replaced by interceptor_name, exported from
+ // interceptor_module. interceptor_entry_point can be provided instead of
+ // interceptor_name / interceptor_module.
+ // thunk_storage must point to a buffer on the child's address space, to hold
+ // the patch thunk, and related data.
+ virtual NTSTATUS Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes);
+
+ // Gets the required buffer size for the internal part of the thunk.
+ size_t GetInternalThunkSize() const;
+
+ // Initializes the internal part of the thunk.
+ // interceptor is the function to be called instead of original_function.
+ bool SetInternalThunk(void* storage, size_t storage_bytes,
+ const void* original_function, const void* interceptor);
+
+ // Holds the resolved interception target.
+ void* target_;
+ // Holds the resolved interception interceptor.
+ const void* interceptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunk);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESOLVER_H__
diff --git a/chromium/sandbox/win/src/resolver_32.cc b/chromium/sandbox/win/src/resolver_32.cc
new file mode 100644
index 00000000000..e5d7be374aa
--- /dev/null
+++ b/chromium/sandbox/win/src/resolver_32.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2006-2010 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/resolver.h"
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // sub esp, 8 // Create working space
+ // push edx // Save register
+ // mov edx, [esp + 0xc] // Get return adddress
+ // mov [esp + 8], edx // Store return address
+ // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument
+ // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to
+ // pop edx // Restore register
+ // ret // Jump to interceptor
+ //
+ // This code only modifies esp and eip so it must work with to normal calling
+ // convention. It is assembled as:
+ //
+ // 00 83ec08 sub esp,8
+ // 03 52 push edx
+ // 04 8b54240c mov edx,dword ptr [esp + 0Ch]
+ // 08 89542408 mov dword ptr [esp + 8], edx
+ // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h
+ // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h
+ // 1c 5a pop edx
+ // 1d c3 ret
+ InternalThunk() {
+ opcodes_1 = 0x5208ec83;
+ opcodes_2 = 0x0c24548b;
+ opcodes_3 = 0x08245489;
+ opcodes_4 = 0x0c2444c7;
+ opcodes_5 = 0x042444c7;
+ opcodes_6 = 0xc35a;
+ extra_argument = 0;
+ interceptor_function = 0;
+ };
+ ULONG opcodes_1; // = 0x5208ec83
+ ULONG opcodes_2; // = 0x0c24548b
+ ULONG opcodes_3; // = 0x08245489
+ ULONG opcodes_4; // = 0x0c2444c7
+ ULONG extra_argument;
+ ULONG opcodes_5; // = 0x042444c7
+ ULONG interceptor_function;
+ USHORT opcodes_6; // = 0xc35a
+};
+#pragma pack(pop)
+
+}; // namespace
+
+namespace sandbox {
+
+bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk;
+
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ thunk->interceptor_function = reinterpret_cast<ULONG>(interceptor);
+ thunk->extra_argument = reinterpret_cast<ULONG>(original_function);
+#pragma warning(pop)
+
+ return true;
+}
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ const void** casted = const_cast<const void**>(address);
+ return ResolverThunk::ResolveInterceptor(module, function_name, casted);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/resolver_64.cc b/chromium/sandbox/win/src/resolver_64.cc
new file mode 100644
index 00000000000..ca58b202993
--- /dev/null
+++ b/chromium/sandbox/win/src/resolver_64.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2006-2010 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/resolver.h"
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+const BYTE kPushRax = 0x50;
+const USHORT kMovRax = 0xB848;
+const ULONG kMovRspRax = 0x24048948;
+const BYTE kRetNp = 0xC3;
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // 00 50 push rax
+ // 01 48b8f0debc9a78563412 mov rax,123456789ABCDEF0h
+ // 0b 48890424 mov qword ptr [rsp],rax
+ // 0f c3 ret
+ //
+ // The code modifies rax, but that should not be an issue for the common
+ // calling conventions.
+
+ InternalThunk() {
+ push_rax = kPushRax;
+ mov_rax = kMovRax;
+ interceptor_function = 0;
+ mov_rsp_rax = kMovRspRax;
+ ret = kRetNp;
+ };
+ BYTE push_rax; // = 50
+ USHORT mov_rax; // = 48 B8
+ ULONG_PTR interceptor_function;
+ ULONG mov_rsp_rax; // = 48 89 04 24
+ BYTE ret; // = C3
+};
+#pragma pack(pop)
+
+} // namespace.
+
+namespace sandbox {
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+bool ResolverThunk::SetInternalThunk(void* storage, size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new(storage, NT_PLACE) InternalThunk;
+ thunk->interceptor_function = reinterpret_cast<ULONG_PTR>(interceptor);
+
+ return true;
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ // We don't support sidestep & co.
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/restricted_token.cc b/chromium/sandbox/win/src/restricted_token.cc
new file mode 100644
index 00000000000..6948d8ada40
--- /dev/null
+++ b/chromium/sandbox/win/src/restricted_token.cc
@@ -0,0 +1,472 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/restricted_token.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/win_utils.h"
+
+
+namespace sandbox {
+
+unsigned RestrictedToken::Init(const HANDLE effective_token) {
+ DCHECK(!init_);
+ if (init_)
+ return ERROR_ALREADY_INITIALIZED;
+
+ if (effective_token) {
+ // We duplicate the handle to be able to use it even if the original handle
+ // is closed.
+ HANDLE effective_token_dup;
+ if (::DuplicateHandle(::GetCurrentProcess(),
+ effective_token,
+ ::GetCurrentProcess(),
+ &effective_token_dup,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ effective_token_ = effective_token_dup;
+ } else {
+ return ::GetLastError();
+ }
+ } else {
+ if (!::OpenProcessToken(::GetCurrentProcess(),
+ TOKEN_ALL_ACCESS,
+ &effective_token_))
+ return ::GetLastError();
+ }
+
+ init_ = true;
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ size_t deny_size = sids_for_deny_only_.size();
+ size_t restrict_size = sids_to_restrict_.size();
+ size_t privileges_size = privileges_to_disable_.size();
+
+ SID_AND_ATTRIBUTES *deny_only_array = NULL;
+ if (deny_size) {
+ deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
+
+ for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) {
+ deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
+ deny_only_array[i].Sid =
+ const_cast<SID*>(sids_for_deny_only_[i].GetPSID());
+ }
+ }
+
+ SID_AND_ATTRIBUTES *sids_to_restrict_array = NULL;
+ if (restrict_size) {
+ sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size];
+
+ for (unsigned int i = 0; i < restrict_size; ++i) {
+ sids_to_restrict_array[i].Attributes = 0;
+ sids_to_restrict_array[i].Sid =
+ const_cast<SID*>(sids_to_restrict_[i].GetPSID());
+ }
+ }
+
+ LUID_AND_ATTRIBUTES *privileges_to_disable_array = NULL;
+ if (privileges_size) {
+ privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size];
+
+ for (unsigned int i = 0; i < privileges_size; ++i) {
+ privileges_to_disable_array[i].Attributes = 0;
+ privileges_to_disable_array[i].Luid = privileges_to_disable_[i];
+ }
+ }
+
+ BOOL result = TRUE;
+ HANDLE new_token = NULL;
+ // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell
+ // if a token has ben restricted given the limiations of IsTokenRestricted()
+ // but it appears that in Windows 7 it hints the AppLocker subsystem to
+ // leave us alone.
+ if (deny_size || restrict_size || privileges_size) {
+ result = ::CreateRestrictedToken(effective_token_,
+ SANDBOX_INERT,
+ static_cast<DWORD>(deny_size),
+ deny_only_array,
+ static_cast<DWORD>(privileges_size),
+ privileges_to_disable_array,
+ static_cast<DWORD>(restrict_size),
+ sids_to_restrict_array,
+ &new_token);
+ } else {
+ // Duplicate the token even if it's not modified at this point
+ // because any subsequent changes to this token would also affect the
+ // current process.
+ result = ::DuplicateTokenEx(effective_token_, TOKEN_ALL_ACCESS, NULL,
+ SecurityIdentification, TokenPrimary,
+ &new_token);
+ }
+
+ if (deny_only_array)
+ delete[] deny_only_array;
+
+ if (sids_to_restrict_array)
+ delete[] sids_to_restrict_array;
+
+ if (privileges_to_disable_array)
+ delete[] privileges_to_disable_array;
+
+ if (!result)
+ return ::GetLastError();
+
+ // Modify the default dacl on the token to contain Restricted and the user.
+ if (!AddSidToDefaultDacl(new_token, WinRestrictedCodeSid, GENERIC_ALL))
+ return ::GetLastError();
+
+ if (!AddUserSidToDefaultDacl(new_token, GENERIC_ALL))
+ return ::GetLastError();
+
+ DWORD error = SetTokenIntegrityLevel(new_token, integrity_level_);
+ if (ERROR_SUCCESS != error)
+ return error;
+
+ BOOL status = ::DuplicateHandle(::GetCurrentProcess(),
+ new_token,
+ ::GetCurrentProcess(),
+ token_handle,
+ TOKEN_ALL_ACCESS,
+ FALSE, // Don't inherit.
+ 0);
+
+ if (new_token != effective_token_)
+ ::CloseHandle(new_token);
+
+ if (!status)
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::GetRestrictedTokenHandleForImpersonation(
+ HANDLE *token_handle) const {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ HANDLE restricted_token_handle;
+ unsigned err_code = GetRestrictedTokenHandle(&restricted_token_handle);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+
+ HANDLE impersonation_token;
+ if (!::DuplicateToken(restricted_token_handle,
+ SecurityImpersonation,
+ &impersonation_token)) {
+ ::CloseHandle(restricted_token_handle);
+ return ::GetLastError();
+ }
+
+ ::CloseHandle(restricted_token_handle);
+
+ BOOL status = ::DuplicateHandle(::GetCurrentProcess(),
+ impersonation_token,
+ ::GetCurrentProcess(),
+ token_handle,
+ TOKEN_ALL_ACCESS,
+ FALSE, // Don't inherit.
+ 0);
+
+ ::CloseHandle(impersonation_token);
+
+ if (!status)
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ TOKEN_GROUPS *token_groups = NULL;
+ DWORD size = 0;
+
+ BOOL result = ::GetTokenInformation(effective_token_,
+ TokenGroups,
+ NULL, // No buffer.
+ 0, // Size is 0.
+ &size);
+ if (!size)
+ return ::GetLastError();
+
+ token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
+ result = ::GetTokenInformation(effective_token_,
+ TokenGroups,
+ token_groups,
+ size,
+ &size);
+ if (!result) {
+ delete[] reinterpret_cast<BYTE*>(token_groups);
+ return ::GetLastError();
+ }
+
+ // Build the list of the deny only group SIDs
+ for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
+ (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) {
+ bool should_ignore = false;
+ if (exceptions) {
+ for (unsigned int j = 0; j < exceptions->size(); ++j) {
+ if (::EqualSid(const_cast<SID*>((*exceptions)[j].GetPSID()),
+ token_groups->Groups[i].Sid)) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+ if (!should_ignore) {
+ sids_for_deny_only_.push_back(
+ reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
+ }
+ }
+ }
+
+ delete[] reinterpret_cast<BYTE*>(token_groups);
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_for_deny_only_.push_back(sid);
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddUserSidForDenyOnly() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
+
+ BOOL result = ::GetTokenInformation(effective_token_,
+ TokenUser,
+ token_user,
+ size,
+ &size);
+
+ if (!result) {
+ delete[] reinterpret_cast<BYTE*>(token_user);
+ return ::GetLastError();
+ }
+
+ Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
+ sids_for_deny_only_.push_back(user);
+
+ delete[] reinterpret_cast<BYTE*>(token_user);
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::DeleteAllPrivileges(
+ const std::vector<std::wstring> *exceptions) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ // Get the list of privileges in the token
+ TOKEN_PRIVILEGES *token_privileges = NULL;
+ DWORD size = 0;
+
+ BOOL result = ::GetTokenInformation(effective_token_,
+ TokenPrivileges,
+ NULL, // No buffer.
+ 0, // Size is 0.
+ &size);
+ if (!size)
+ return ::GetLastError();
+
+ token_privileges = reinterpret_cast<TOKEN_PRIVILEGES*>(new BYTE[size]);
+ result = ::GetTokenInformation(effective_token_,
+ TokenPrivileges,
+ token_privileges,
+ size,
+ &size);
+ if (!result) {
+ delete[] reinterpret_cast<BYTE *>(token_privileges);
+ return ::GetLastError();
+ }
+
+
+ // Build the list of privileges to disable
+ for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) {
+ bool should_ignore = false;
+ if (exceptions) {
+ for (unsigned int j = 0; j < exceptions->size(); ++j) {
+ LUID luid = {0};
+ ::LookupPrivilegeValue(NULL, (*exceptions)[j].c_str(), &luid);
+ if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart &&
+ token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+ if (!should_ignore) {
+ privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid);
+ }
+ }
+
+ delete[] reinterpret_cast<BYTE *>(token_privileges);
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ LUID luid = {0};
+ if (LookupPrivilegeValue(NULL, privilege, &luid))
+ privileges_to_disable_.push_back(luid);
+ else
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_to_restrict_.push_back(sid); // No attributes
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddRestrictingSidLogonSession() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ TOKEN_GROUPS *token_groups = NULL;
+ DWORD size = 0;
+
+ BOOL result = ::GetTokenInformation(effective_token_,
+ TokenGroups,
+ NULL, // No buffer.
+ 0, // Size is 0.
+ &size);
+ if (!size)
+ return ::GetLastError();
+
+ token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
+ result = ::GetTokenInformation(effective_token_,
+ TokenGroups,
+ token_groups,
+ size,
+ &size);
+ if (!result) {
+ delete[] reinterpret_cast<BYTE*>(token_groups);
+ return ::GetLastError();
+ }
+
+ SID *logon_sid = NULL;
+ for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
+ logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
+ break;
+ }
+ }
+
+ if (logon_sid)
+ sids_to_restrict_.push_back(logon_sid);
+
+ delete[] reinterpret_cast<BYTE*>(token_groups);
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddRestrictingSidCurrentUser() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
+
+ BOOL result = ::GetTokenInformation(effective_token_,
+ TokenUser,
+ token_user,
+ size,
+ &size);
+
+ if (!result) {
+ delete[] reinterpret_cast<BYTE*>(token_user);
+ return ::GetLastError();
+ }
+
+ Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
+ sids_to_restrict_.push_back(user);
+
+ delete[] reinterpret_cast<BYTE*>(token_user);
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::AddRestrictingSidAllSids() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ // Add the current user to the list.
+ unsigned error = AddRestrictingSidCurrentUser();
+ if (ERROR_SUCCESS != error)
+ return error;
+
+ TOKEN_GROUPS *token_groups = NULL;
+ DWORD size = 0;
+
+ // Get the buffer size required.
+ BOOL result = ::GetTokenInformation(effective_token_, TokenGroups, NULL, 0,
+ &size);
+ if (!size)
+ return ::GetLastError();
+
+ token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
+ result = ::GetTokenInformation(effective_token_,
+ TokenGroups,
+ token_groups,
+ size,
+ &size);
+ if (!result) {
+ delete[] reinterpret_cast<BYTE*>(token_groups);
+ return ::GetLastError();
+ }
+
+ // Build the list of restricting sids from all groups.
+ for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0)
+ AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
+ }
+
+ delete[] reinterpret_cast<BYTE*>(token_groups);
+
+ return ERROR_SUCCESS;
+}
+
+unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ integrity_level_ = integrity_level;
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/restricted_token.h b/chromium/sandbox/win/src/restricted_token.h
new file mode 100644
index 00000000000..4327856f364
--- /dev/null
+++ b/chromium/sandbox/win/src/restricted_token.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2010 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_RESTRICTED_TOKEN_H_
+#define SANDBOX_SRC_RESTRICTED_TOKEN_H_
+
+#include <windows.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/security_level.h"
+#include "sandbox/win/src/sid.h"
+
+// Flags present in the Group SID list. These 2 flags are new in Windows Vista
+#ifndef SE_GROUP_INTEGRITY
+#define SE_GROUP_INTEGRITY (0x00000020L)
+#endif
+#ifndef SE_GROUP_INTEGRITY_ENABLED
+#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L)
+#endif
+
+namespace sandbox {
+
+// Handles the creation of a restricted token using the effective token or
+// any token handle.
+// Sample usage:
+// RestrictedToken restricted_token;
+// unsigned err_code = restricted_token.Init(NULL); // Use the current
+// // effective token
+// if (ERROR_SUCCESS != err_code) {
+// // handle error.
+// }
+//
+// restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID());
+// HANDLE token_handle;
+// err_code = restricted_token.GetRestrictedTokenHandle(&token_handle);
+// if (ERROR_SUCCESS != err_code) {
+// // handle error.
+// }
+// [...]
+// CloseHandle(token_handle);
+class RestrictedToken {
+ public:
+ // Init() has to be called before calling any other method in the class.
+ RestrictedToken()
+ : init_(false), effective_token_(NULL),
+ integrity_level_(INTEGRITY_LEVEL_LAST) { }
+
+ ~RestrictedToken() {
+ if (effective_token_)
+ CloseHandle(effective_token_);
+ }
+
+ // Initializes the RestrictedToken object with effective_token.
+ // If effective_token is NULL, it initializes the RestrictedToken object with
+ // the effective token of the current process.
+ unsigned Init(HANDLE effective_token);
+
+ // Creates a restricted token and returns its handle using the token_handle
+ // output parameter. This handle has to be closed by the caller.
+ // 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.
+ unsigned GetRestrictedTokenHandle(HANDLE *token_handle) const;
+
+ // Creates a restricted token and uses this new token to create a new token
+ // for impersonation. Returns the handle of this impersonation token using
+ // the token_handle output parameter. This handle has to be closed by
+ // the caller.
+ //
+ // 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.
+ //
+ // The sample usage is the same as the GetRestrictedTokenHandle function.
+ unsigned GetRestrictedTokenHandleForImpersonation(HANDLE *token_handle) const;
+
+ // Lists all sids in the token and mark them as Deny Only except for those
+ // present in the exceptions parameter. If there is no exception needed,
+ // the caller can pass an empty list or NULL for the exceptions
+ // parameter.
+ //
+ // 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.
+ //
+ // Sample usage:
+ // std::vector<Sid> sid_exceptions;
+ // sid_exceptions.push_back(ATL::Sids::Users().GetPSID());
+ // sid_exceptions.push_back(ATL::Sids::World().GetPSID());
+ // restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+ // Note: A Sid marked for Deny Only in a token cannot be used to grant
+ // access to any resource. It can only be used to deny access.
+ unsigned AddAllSidsForDenyOnly(std::vector<Sid> *exceptions);
+
+ // Adds a user or group SID for Deny Only in the restricted token.
+ // Parameter: sid is the SID to add in the Deny Only list.
+ // The return value is always ERROR_SUCCESS.
+ //
+ // Sample Usage:
+ // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID());
+ unsigned AddSidForDenyOnly(const Sid &sid);
+
+ // Adds the user sid of the token for Deny Only in the restricted token.
+ // 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.
+ unsigned AddUserSidForDenyOnly();
+
+ // Lists all privileges in the token and add them to the list of privileges
+ // to remove except for those present in the exceptions parameter. If
+ // there is no exception needed, the caller can pass an empty list or NULL
+ // for the exceptions parameter.
+ //
+ // 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.
+ //
+ // Sample usage:
+ // std::vector<std::wstring> privilege_exceptions;
+ // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ // restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ unsigned DeleteAllPrivileges(
+ const std::vector<std::wstring> *exceptions);
+
+ // Adds a privilege to the list of privileges to remove in the restricted
+ // token.
+ // Parameter: privilege is the privilege name to remove. This is the string
+ // representing the privilege. (e.g. "SeChangeNotifyPrivilege").
+ // 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.
+ //
+ // Sample usage:
+ // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME);
+ unsigned DeletePrivilege(const wchar_t *privilege);
+
+ // Adds a SID to the list of restricting sids in the restricted token.
+ // Parameter: sid is the sid to add to the list restricting sids.
+ // The return value is always ERROR_SUCCESS.
+ //
+ // Sample usage:
+ // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID());
+ // Note: The list of restricting is used to force Windows to perform all
+ // access checks twice. The first time using your user SID and your groups,
+ // and the second time using your list of restricting sids. The access has
+ // to be granted in both places to get access to the resource requested.
+ unsigned AddRestrictingSid(const Sid &sid);
+
+ // Adds the logon sid of the token in the list of restricting sids for the
+ // restricted token.
+ //
+ // 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.
+ unsigned AddRestrictingSidLogonSession();
+
+ // Adds the owner sid of the token in the list of restricting sids for the
+ // restricted token.
+ //
+ // 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.
+ unsigned AddRestrictingSidCurrentUser();
+
+ // Adds all group sids and the user sid to the restricting sids list.
+ //
+ // 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.
+ unsigned AddRestrictingSidAllSids();
+
+ // Sets the token integrity level. This is only valid on Vista. The integrity
+ // level cannot be higher than your current integrity level.
+ unsigned SetIntegrityLevel(IntegrityLevel integrity_level);
+
+ private:
+ // The list of restricting sids in the restricted token.
+ std::vector<Sid> sids_to_restrict_;
+ // The list of privileges to remove in the restricted token.
+ std::vector<LUID> privileges_to_disable_;
+ // The list of sids to mark as Deny Only in the restricted token.
+ std::vector<Sid> sids_for_deny_only_;
+ // The token to restrict. Can only be set in a constructor.
+ HANDLE effective_token_;
+ // The token integrity level. Only valid on Vista.
+ IntegrityLevel integrity_level_;
+ // Tells if the object is initialized or not (if Init() has been called)
+ bool init_;
+
+ DISALLOW_COPY_AND_ASSIGN(RestrictedToken);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESTRICTED_TOKEN_H_
diff --git a/chromium/sandbox/win/src/restricted_token_unittest.cc b/chromium/sandbox/win/src/restricted_token_unittest.cc
new file mode 100644
index 00000000000..4948ad1c82f
--- /dev/null
+++ b/chromium/sandbox/win/src/restricted_token_unittest.cc
@@ -0,0 +1,593 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the RestrictedToken.
+
+#define _ATL_NO_EXCEPTIONS
+#include <atlbase.h>
+#include <atlsecurity.h>
+#include <vector>
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/sid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests the initializatioin with an invalid token handle.
+TEST(RestrictedTokenTest, InvalidHandle) {
+ RestrictedToken token;
+ ASSERT_EQ(ERROR_INVALID_HANDLE, token.Init(reinterpret_cast<HANDLE>(0x5555)));
+}
+
+// Tests the initialization with NULL as parameter.
+TEST(RestrictedTokenTest, DefaultInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Create the token using the current token.
+ RestrictedToken token_default;
+ ASSERT_EQ(ERROR_SUCCESS, token_default.Init(NULL));
+
+ // Get the handle to the restricted token.
+
+ HANDLE restricted_token_handle = NULL;
+ ASSERT_EQ(ERROR_SUCCESS,
+ token_default.GetRestrictedTokenHandle(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle);
+
+ ATL::CSid sid_user_restricted;
+ ATL::CSid sid_user_default;
+ ATL::CSid sid_owner_restricted;
+ ATL::CSid sid_owner_default;
+ ASSERT_TRUE(restricted_token.GetUser(&sid_user_restricted));
+ ASSERT_TRUE(access_token.GetUser(&sid_user_default));
+ ASSERT_TRUE(restricted_token.GetOwner(&sid_owner_restricted));
+ ASSERT_TRUE(access_token.GetOwner(&sid_owner_default));
+
+ // Check if both token have the same owner and user.
+ ASSERT_EQ(sid_user_restricted, sid_user_default);
+ ASSERT_EQ(sid_owner_restricted, sid_owner_default);
+}
+
+// Tests the initialization with a custom token as parameter.
+TEST(RestrictedTokenTest, CustomInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Change the primary group.
+ access_token.SetPrimaryGroup(ATL::Sids::World());
+
+ // Create the token using the current token.
+ RestrictedToken token;
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(access_token.GetHandle()));
+
+ // Get the handle to the restricted token.
+
+ HANDLE restricted_token_handle = NULL;
+ ASSERT_EQ(ERROR_SUCCESS,
+ token.GetRestrictedTokenHandle(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle);
+
+ ATL::CSid sid_restricted;
+ ATL::CSid sid_default;
+ ASSERT_TRUE(restricted_token.GetPrimaryGroup(&sid_restricted));
+ ASSERT_TRUE(access_token.GetPrimaryGroup(&sid_default));
+
+ // Check if both token have the same owner.
+ ASSERT_EQ(sid_restricted, sid_default);
+}
+
+// Verifies that the token created by the object are valid.
+TEST(RestrictedTokenTest, ResultToken) {
+ RestrictedToken token;
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+
+ ASSERT_EQ(ERROR_SUCCESS,
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ HANDLE restricted_token;
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&restricted_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(restricted_token));
+
+ DWORD length = 0;
+ TOKEN_TYPE type;
+ ASSERT_TRUE(::GetTokenInformation(restricted_token,
+ ::TokenType,
+ &type,
+ sizeof(type),
+ &length));
+
+ ASSERT_EQ(type, TokenPrimary);
+
+ HANDLE impersonation_token;
+ ASSERT_EQ(ERROR_SUCCESS,
+ token.GetRestrictedTokenHandleForImpersonation(&impersonation_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(impersonation_token));
+
+ ASSERT_TRUE(::GetTokenInformation(impersonation_token,
+ ::TokenType,
+ &type,
+ sizeof(type),
+ &length));
+
+ ASSERT_EQ(type, TokenImpersonation);
+
+ ::CloseHandle(impersonation_token);
+ ::CloseHandle(restricted_token);
+}
+
+// Verifies that the token created has "Restricted" in its default dacl.
+TEST(RestrictedTokenTest, DefaultDacl) {
+ RestrictedToken token;
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+
+ ASSERT_EQ(ERROR_SUCCESS,
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ HANDLE handle;
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(handle);
+
+ ATL::CDacl dacl;
+ ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl));
+
+ bool restricted_found = false;
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count ; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ dacl.GetAclEntry(i, &sid, &mask);
+ if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) {
+ restricted_found = true;
+ break;
+ }
+ }
+
+ ASSERT_TRUE(restricted_found);
+}
+
+// Tests the method "AddSidForDenyOnly".
+TEST(RestrictedTokenTest, DenySid) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddSidForDenyOnly(Sid(WinWorldSid)));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY,
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly".
+TEST(RestrictedTokenTest, DenySids) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY,
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly" using an exception list.
+TEST(RestrictedTokenTest, DenySidsException) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ std::vector<Sid> sids_exception;
+ sids_exception.push_back(Sid(WinWorldSid));
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(&sids_exception));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(NULL, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ } else {
+ ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY,
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly.
+TEST(RestrictedTokenTest, DenyOwnerSid) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddUserSidForDenyOnly());
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY,
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly with a custom effective token.
+TEST(RestrictedTokenTest, DenyOwnerSidCustom) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ RestrictedToken token;
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(access_token.GetHandle()));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddUserSidForDenyOnly());
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY,
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method DeleteAllPrivileges.
+TEST(RestrictedTokenTest, DeleteAllPrivileges) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ASSERT_EQ(0, privileges.GetCount());
+}
+
+// Tests the method DeleteAllPrivileges with an exception list.
+TEST(RestrictedTokenTest, DeleteAllPrivilegesException) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ std::vector<std::wstring> exceptions;
+ exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(&exceptions));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ ASSERT_EQ(1, privileges.GetCount());
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method DeletePrivilege.
+TEST(RestrictedTokenTest, DeletePrivilege) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_NE(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Checks if a sid is in the restricting list of the restricted token.
+// Asserts if it's not the case. If count is a positive number, the number of
+// 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];
+ TOKEN_GROUPS *groups = reinterpret_cast<TOKEN_GROUPS*>(memory);
+ ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(),
+ TokenRestrictedSids,
+ groups,
+ length,
+ &length));
+
+ ATL::CTokenGroups atl_groups(*groups);
+ delete[] memory;
+
+ if (count >= 0)
+ ASSERT_EQ(count, atl_groups.GetCount());
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ atl_groups.GetSidsAndAttributes(&sids, &attributes);
+
+ bool present = false;
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (sids[i] == sid) {
+ present = true;
+ break;
+ }
+ }
+
+ ASSERT_TRUE(present);
+}
+
+// Tests the method AddRestrictingSid.
+TEST(RestrictedTokenTest, AddRestrictingSid) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS,
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser with a custom effective token.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUserCustom) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ RestrictedToken token;
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(access_token.GetHandle()));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidLogonSession.
+TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ CheckRestrictingSid(restricted_token, session, 1);
+}
+
+// Tests adding a lot of restricting sids.
+TEST(RestrictedTokenTest, AddMultipleRestrictingSids) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(ERROR_SUCCESS,
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ DWORD length = 1000;
+ BYTE *memory = new BYTE[1000];
+ TOKEN_GROUPS *groups = reinterpret_cast<TOKEN_GROUPS*>(memory);
+ ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(),
+ TokenRestrictedSids,
+ groups,
+ length,
+ &length));
+
+ ATL::CTokenGroups atl_groups(*groups);
+ delete[] memory;
+
+ ASSERT_EQ(3, atl_groups.GetCount());
+}
+
+// Tests the method "AddRestrictingSidAllSids".
+TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) {
+ RestrictedToken token;
+ HANDLE token_handle = NULL;
+
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+ ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidAllSids());
+ ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle);
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all group sids are in the restricting sid list.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ CheckRestrictingSid(restricted_token, sids[i], -1);
+ }
+ }
+
+ // Verify that the user is in the restricting sid list.
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+ 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;
+ ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL));
+
+ 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
new file mode 100644
index 00000000000..5f1a246689c
--- /dev/null
+++ b/chromium/sandbox/win/src/restricted_token_utils.cc
@@ -0,0 +1,344 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <aclapi.h>
+#include <sddl.h>
+#include <vector>
+
+#include "sandbox/win/src/restricted_token_utils.h"
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/security_level.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+DWORD CreateRestrictedToken(HANDLE *token_handle,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type) {
+ if (!token_handle)
+ return ERROR_BAD_ARGUMENTS;
+
+ RestrictedToken restricted_token;
+ restricted_token.Init(NULL); // Initialized with the current process token
+
+ std::vector<std::wstring> privilege_exceptions;
+ std::vector<Sid> sid_exceptions;
+
+ bool deny_sids = true;
+ bool remove_privileges = true;
+
+ switch (security_level) {
+ case USER_UNPROTECTED: {
+ deny_sids = false;
+ remove_privileges = false;
+ break;
+ }
+ case USER_RESTRICTED_SAME_ACCESS: {
+ deny_sids = false;
+ remove_privileges = false;
+
+ unsigned err_code = restricted_token.AddRestrictingSidAllSids();
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+
+ break;
+ }
+ case USER_NON_ADMIN: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ break;
+ }
+ case USER_INTERACTIVE: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ break;
+ }
+ case USER_LIMITED: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+
+ // This token has to be able to create objects in BNO.
+ // Unfortunately, on vista, it needs the current logon sid
+ // in the token to achieve this. You should also set the process to be
+ // low integrity level so it can't access object created by other
+ // processes.
+ if (base::win::GetVersion() >= base::win::VERSION_VISTA)
+ restricted_token.AddRestrictingSidLogonSession();
+ break;
+ }
+ case USER_RESTRICTED: {
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddUserSidForDenyOnly();
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ break;
+ }
+ case USER_LOCKDOWN: {
+ restricted_token.AddUserSidForDenyOnly();
+ restricted_token.AddRestrictingSid(WinNullSid);
+ break;
+ }
+ default: {
+ return ERROR_BAD_ARGUMENTS;
+ }
+ }
+
+ DWORD err_code = ERROR_SUCCESS;
+ if (deny_sids) {
+ err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ if (remove_privileges) {
+ err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ restricted_token.SetIntegrityLevel(integrity_level);
+
+ switch (token_type) {
+ case PRIMARY: {
+ err_code = restricted_token.GetRestrictedTokenHandle(token_handle);
+ break;
+ }
+ case IMPERSONATION: {
+ err_code = restricted_token.GetRestrictedTokenHandleForImpersonation(
+ token_handle);
+ break;
+ }
+ default: {
+ err_code = ERROR_BAD_ARGUMENTS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+DWORD StartRestrictedProcessInJob(wchar_t *command_line,
+ TokenLevel primary_level,
+ TokenLevel impersonation_level,
+ JobLevel job_level,
+ HANDLE *const job_handle_ret) {
+ Job job;
+ DWORD err_code = job.Init(job_level, NULL, 0);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+
+ if (JOB_UNPROTECTED != job_level) {
+ // Share the Desktop handle to be able to use MessageBox() in the sandboxed
+ // application.
+ err_code = job.UserHandleGrantAccess(GetDesktopWindow());
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ // Create the primary (restricted) token for the process
+ HANDLE primary_token_handle = NULL;
+ err_code = CreateRestrictedToken(&primary_token_handle,
+ primary_level,
+ INTEGRITY_LEVEL_LAST,
+ PRIMARY);
+ if (ERROR_SUCCESS != err_code) {
+ return err_code;
+ }
+ base::win::ScopedHandle primary_token(primary_token_handle);
+
+ // Create the impersonation token (restricted) to be able to start the
+ // process.
+ HANDLE impersonation_token_handle;
+ err_code = CreateRestrictedToken(&impersonation_token_handle,
+ impersonation_level,
+ INTEGRITY_LEVEL_LAST,
+ IMPERSONATION);
+ if (ERROR_SUCCESS != err_code) {
+ return err_code;
+ }
+ base::win::ScopedHandle impersonation_token(impersonation_token_handle);
+
+ // Start the process
+ STARTUPINFO startup_info = {0};
+ base::win::ScopedProcessInformation process_info;
+ DWORD flags = CREATE_SUSPENDED;
+
+ if (base::win::GetVersion() < base::win::VERSION_WIN8) {
+ // Windows 8 implements nested jobs, but for older systems we need to
+ // break out of any job we're in to enforce our restrictions.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ if (!::CreateProcessAsUser(primary_token.Get(),
+ NULL, // No application name.
+ command_line,
+ NULL, // No security attribute.
+ NULL, // No thread attribute.
+ FALSE, // Do not inherit handles.
+ flags,
+ NULL, // Use the environment of the caller.
+ NULL, // Use current directory of the caller.
+ &startup_info,
+ process_info.Receive())) {
+ return ::GetLastError();
+ }
+
+ // Change the token of the main thread of the new process for the
+ // impersonation token with more rights.
+ {
+ HANDLE temp_thread = process_info.thread_handle();
+ if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) {
+ ::TerminateProcess(process_info.process_handle(),
+ 0); // exit code
+ return ::GetLastError();
+ }
+ }
+
+ err_code = job.AssignProcessToJob(process_info.process_handle());
+ if (ERROR_SUCCESS != err_code) {
+ ::TerminateProcess(process_info.process_handle(),
+ 0); // exit code
+ return ::GetLastError();
+ }
+
+ // Start the application
+ ::ResumeThread(process_info.thread_handle());
+
+ (*job_handle_ret) = job.Detach();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid) {
+ // Build the SDDL string for the label.
+ std::wstring sddl = L"S:("; // SDDL for a SACL.
+ sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
+ sddl += L";;"; // No Ace Flags.
+ sddl += ace_access; // Add the ACE access.
+ sddl += L";;;"; // No ObjectType and Inherited Object Type.
+ sddl += integrity_level_sid; // Trustee Sid.
+ sddl += L")";
+
+ DWORD error = ERROR_SUCCESS;
+ PSECURITY_DESCRIPTOR sec_desc = NULL;
+
+ PACL sacl = NULL;
+ BOOL sacl_present = FALSE;
+ BOOL sacl_defaulted = FALSE;
+
+ if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
+ SDDL_REVISION,
+ &sec_desc, NULL)) {
+ if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ error = ::SetSecurityInfo(handle, type,
+ LABEL_SECURITY_INFORMATION, NULL, NULL, NULL,
+ sacl);
+ } else {
+ error = ::GetLastError();
+ }
+
+ ::LocalFree(sec_desc);
+ } else {
+ return::GetLastError();
+ }
+
+ return error;
+}
+
+const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) {
+ switch (integrity_level) {
+ case INTEGRITY_LEVEL_SYSTEM:
+ return L"S-1-16-16384";
+ case INTEGRITY_LEVEL_HIGH:
+ return L"S-1-16-12288";
+ case INTEGRITY_LEVEL_MEDIUM:
+ return L"S-1-16-8192";
+ case INTEGRITY_LEVEL_MEDIUM_LOW:
+ return L"S-1-16-6144";
+ case INTEGRITY_LEVEL_LOW:
+ return L"S-1-16-4096";
+ case INTEGRITY_LEVEL_BELOW_LOW:
+ return L"S-1-16-2048";
+ case INTEGRITY_LEVEL_UNTRUSTED:
+ return L"S-1-16-0";
+ case INTEGRITY_LEVEL_LAST:
+ return NULL;
+ }
+
+ NOTREACHED();
+ return NULL;
+}
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return ERROR_SUCCESS;
+
+ const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level);
+ if (!integrity_level_str) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ PSID integrity_sid = NULL;
+ if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
+ return ::GetLastError();
+
+ TOKEN_MANDATORY_LABEL label = {0};
+ label.Label.Attributes = SE_GROUP_INTEGRITY;
+ label.Label.Sid = integrity_sid;
+
+ DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid);
+ BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label,
+ size);
+ ::LocalFree(integrity_sid);
+
+ return result ? ERROR_SUCCESS : ::GetLastError();
+}
+
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return ERROR_SUCCESS;
+
+ // We don't check for an invalid level here because we'll just let it
+ // fail on the SetTokenIntegrityLevel call later on.
+ if (integrity_level == INTEGRITY_LEVEL_LAST) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return SetTokenIntegrityLevel(token.Get(), integrity_level);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/restricted_token_utils.h b/chromium/sandbox/win/src/restricted_token_utils.h
new file mode 100644
index 00000000000..f2a280a1cb6
--- /dev/null
+++ b/chromium/sandbox/win/src/restricted_token_utils.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2006-2008 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_RESTRICTED_TOKEN_UTILS_H__
+#define SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
+
+#include <accctrl.h>
+#include <windows.h>
+
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/security_level.h"
+
+// Contains the utility functions to be able to create restricted tokens based
+// on a security profiles.
+
+namespace sandbox {
+
+// The type of the token returned by the CreateNakedToken.
+enum TokenType {
+ IMPERSONATION = 0,
+ PRIMARY
+};
+
+// Creates a restricted token based on the effective token of the current
+// process. The parameter security_level determines how much the token is
+// restricted. The token_type determines if the token will be used as a primary
+// token or impersonation token. The integrity level of the token is set to
+// |integrity level| on Vista only.
+// token_handle is the output value containing the handle of the
+// newly created restricted token.
+// 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 CreateRestrictedToken(HANDLE *token_handle,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type);
+
+// Starts the process described by the input parameter command_line in a job
+// with a restricted token. Also set the main thread of this newly created
+// process to impersonate a user with more rights so it can initialize
+// correctly.
+//
+// Parameters: primary_level is the security level of the primary token.
+// impersonation_level is the security level of the impersonation token used
+// to initialize the process. job_level is the security level of the job
+// object used to encapsulate the process.
+//
+// The output parameter job_handle is the handle to the job object. It has
+// to be closed with CloseHandle() when not needed. Closing this handle will
+// kill the process started.
+//
+// Note: The process started with this function has to call RevertToSelf() as
+// soon as possible to stop using the impersonation token and start being
+// secure.
+//
+// Note: The Unicode version of this function will fail if the command_line
+// parameter is a const string.
+DWORD StartRestrictedProcessInJob(wchar_t *command_line,
+ TokenLevel primary_level,
+ TokenLevel impersonation_level,
+ JobLevel job_level,
+ HANDLE *job_handle);
+
+// Sets the integrity label on a object handle.
+DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid);
+
+// Sets the integrity level on a token. This is only valid on Vista. It returns
+// without failing on XP. If the integrity level that you specify is greater
+// than the current integrity level, the function will fail.
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level);
+
+// Sets the integrity level on the current process on Vista. It returns without
+// failing on XP. If the integrity level that you specify is greater than the
+// current integrity level, the function will fail.
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
diff --git a/chromium/sandbox/win/src/sandbox.cc b/chromium/sandbox/win/src/sandbox.cc
new file mode 100644
index 00000000000..9e4ab08a097
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2006-2008 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 <stdio.h>
+#include <windows.h>
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/broker_services.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+// The section for IPC and policy.
+SANDBOX_INTERCEPT HANDLE g_shared_section = NULL;
+
+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.
+BrokerServices* SandboxFactory::GetBrokerServices() {
+ // Can't be the broker if the shared section is open.
+ if (NULL != g_shared_section) {
+ return NULL;
+ }
+ // If the shared section does not exist we are the broker, then create
+ // the broker object.
+ s_is_broker = true;
+ return BrokerServicesBase::GetInstance();
+}
+
+// GetTargetServices implementation must follow the same technique as the
+// GetBrokerServices, but in this case the logic is the opposite.
+TargetServices* SandboxFactory::GetTargetServices() {
+ // Can't be the target if the section handle is not valid.
+ if (NULL == g_shared_section) {
+ return NULL;
+ }
+ // We are the target
+ s_is_broker = false;
+ // Creates and returns the target services implementation.
+ return TargetServicesBase::GetInstance();
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sandbox.h b/chromium/sandbox/win/src/sandbox.h
new file mode 100644
index 00000000000..e3261944030
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox.h
@@ -0,0 +1,165 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Sandbox is a sandbox library for windows processes. Use when you want a
+// 'privileged' process and a 'locked down process' to interact with.
+// The privileged process is called the broker and it is started by external
+// means (such as the user starting it). The 'sandboxed' process is called the
+// target and it is started by the broker. There can be many target processes
+// started by a single broker process. This library provides facilities
+// for both the broker and the target.
+//
+// The design rationale and relevant documents can be found at http://go/sbox.
+//
+// Note: this header does not include the SandboxFactory definitions because
+// there are cases where the Sandbox library is linked against the main .exe
+// while its API needs to be used in a DLL.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_H_
+#define SANDBOX_WIN_SRC_SANDBOX_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+// sandbox: Google User-Land Application Sandbox
+namespace sandbox {
+
+class BrokerServices;
+class ProcessState;
+class TargetPolicy;
+class TargetServices;
+
+// BrokerServices exposes all the broker API.
+// The basic use is to start the target(s) and wait for them to end.
+//
+// This API is intended to be called in the following order
+// (error checking omitted):
+// BrokerServices* broker = SandboxFactory::GetBrokerServices();
+// broker->Init();
+// PROCESS_INFORMATION target;
+// broker->SpawnTarget(target_exe_path, target_args, &target);
+// ::ResumeThread(target->hThread);
+// // -- later you can call:
+// broker->WaitForAllTargets(option);
+//
+class BrokerServices {
+ public:
+ // Initializes the broker. Must be called before any other on this class.
+ // returns ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode Init() = 0;
+
+ // Returns the interface pointer to a new, empty policy object. Use this
+ // interface to specify the sandbox policy for new processes created by
+ // SpawnTarget()
+ virtual TargetPolicy* CreatePolicy() = 0;
+
+ // Creates a new target (child process) in a suspended state.
+ // Parameters:
+ // exe_path: This is the full path to the target binary. This parameter
+ // can be null and in this case the exe path must be the first argument
+ // of the command_line.
+ // command_line: The arguments to be passed as command line to the new
+ // process. This can be null if the exe_path parameter is not null.
+ // policy: This is the pointer to the policy object for the sandbox to
+ // be created.
+ // target: returns the resulting target process information such as process
+ // handle and PID just as if CreateProcess() had been called. The caller is
+ // responsible for closing the handles returned in this structure.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ virtual ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ TargetPolicy* policy,
+ PROCESS_INFORMATION* target) = 0;
+
+ // This call blocks (waits) for all the targets to terminate.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode WaitForAllTargets() = 0;
+
+ // Adds an unsandboxed process as a peer for policy decisions (e.g.
+ // HANDLES_DUP_ANY policy).
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode AddTargetPeer(HANDLE peer_process) = 0;
+
+ // Install the AppContainer with the specified sid an name. Returns ALL_OK if
+ // successful or an error code if the AppContainer cannot be installed.
+ virtual ResultCode InstallAppContainer(const wchar_t* sid,
+ const wchar_t* name) = 0;
+
+ // Removes from the system the AppContainer with the specified sid.
+ // Returns ALL_OK if successful or an error code otherwise.
+ virtual ResultCode UninstallAppContainer(const wchar_t* sid) = 0;
+};
+
+// TargetServices models the current process from the perspective
+// of a target process. To obtain a pointer to it use
+// Sandbox::GetTargetServices(). Note that this call returns a non-null
+// pointer only if this process is in fact a target. A process is a target
+// only if the process was spawned by a call to BrokerServices::SpawnTarget().
+//
+// This API allows the target to gain access to resources with a high
+// privilege token and then when it is ready to perform dangerous activities
+// (such as download content from the web) it can lower its token and
+// enter into locked-down (sandbox) mode.
+// The typical usage is as follows:
+//
+// TargetServices* target_services = Sandbox::GetTargetServices();
+// if (NULL != target_services) {
+// // We are the target.
+// target_services->Init();
+// // Do work that requires high privileges here.
+// // ....
+// // When ready to enter lock-down mode call LowerToken:
+// target_services->LowerToken();
+// }
+//
+// For more information see the BrokerServices API documentation.
+class TargetServices {
+ public:
+ // Initializes the target. Must call this function before any other.
+ // returns ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode Init() = 0;
+
+ // Discards the impersonation token and uses the lower token, call before
+ // processing any untrusted data or running third-party code. If this call
+ // fails the current process could be terminated immediately.
+ virtual void LowerToken() = 0;
+
+ // Returns the ProcessState object. Through that object it's possible to have
+ // information about the current state of the process, such as whether
+ // LowerToken has been called or not.
+ virtual ProcessState* GetState() = 0;
+
+ // Requests the broker to duplicate the supplied handle into the target
+ // process. The target process must be an active sandbox child process
+ // and the source process must have a corresponding policy allowing
+ // handle duplication for this object type.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) = 0;
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_H_
diff --git a/chromium/sandbox/win/src/sandbox.vcproj b/chromium/sandbox/win/src/sandbox.vcproj
new file mode 100644
index 00000000000..f206e01a1f2
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox.vcproj
@@ -0,0 +1,658 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sandbox"
+ ProjectGUID="{881F6A97-D539-4C48-B401-DF04385B2343}"
+ RootNamespace="sandbox"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="Copy wow_helper to output directory"
+ CommandLine="copy $(ProjectDir)\..\wow_helper\wow_helper.exe $(OutDir) &amp;&amp; copy $(ProjectDir)\..\wow_helper\wow_helper.pdb $(OutDir)"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ Description="Copy wow_helper to output directory"
+ CommandLine="copy $(ProjectDir)\..\wow_helper\wow_helper.exe $(OutDir) &amp;&amp; copy $(ProjectDir)\..\wow_helper\wow_helper.pdb $(OutDir)"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="security"
+ >
+ <File
+ RelativePath=".\acl.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\acl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\dep.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dep.h"
+ >
+ </File>
+ <File
+ RelativePath=".\job.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\job.h"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token.h"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\security_level.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sid.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\window.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\window.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Interception"
+ >
+ <File
+ RelativePath=".\eat_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\eat_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_agent.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_agent.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_internal.h"
+ >
+ </File>
+ <File
+ RelativePath=".\pe_image.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\pe_image.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\service_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\service_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_interceptions.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_interceptions.h"
+ >
+ </File>
+ <File
+ RelativePath=".\Wow64.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\Wow64.h"
+ >
+ </File>
+ <Filter
+ Name="sidestep"
+ >
+ <File
+ RelativePath=".\sidestep\ia32_modrm_map.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\ia32_opcode_map.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\preamble_patcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\preamble_patcher_with_stub.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="nt_level"
+ >
+ <File
+ RelativePath=".\nt_internals.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_target.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_target.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_util.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_util.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy_handlers"
+ >
+ <File
+ RelativePath=".\filesystem_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_policy.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="IPC"
+ >
+ <File
+ RelativePath=".\crosscall_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_server.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_server.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_tags.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_client.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_server.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_server.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy_base"
+ >
+ <File
+ RelativePath=".\policy_engine_opcodes.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_opcodes.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_processor.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_processor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_low_level.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_low_level.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy_base.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy_base.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\broker_services.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\broker_services.h"
+ >
+ </File>
+ <File
+ RelativePath=".\internal_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_broker.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_broker.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_factory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\shared_handles.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\shared_handles.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_process.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_process.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_services.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_services.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win2k_threadpool.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\win2k_threadpool.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\win_utils.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chromium/sandbox/win/src/sandbox_factory.h b/chromium/sandbox/win/src/sandbox_factory.h
new file mode 100644
index 00000000000..7a0280f9080
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_factory.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2006-2008 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_SANDBOX_FACTORY_H__
+#define SANDBOX_SRC_SANDBOX_FACTORY_H__
+
+#include "sandbox/win/src/sandbox.h"
+
+// SandboxFactory is a set of static methods to get access to the broker
+// or target services object. Only one of the two methods (GetBrokerServices,
+// GetTargetServices) will return a non-null pointer and that should be used
+// as the indication that the process is the broker or the target:
+//
+// BrokerServices* broker_services = SandboxFactory::GetBrokerServices();
+// if (NULL != broker_services) {
+// //we are the broker, call broker api here
+// broker_services->Init();
+// } else {
+// TargetServices* target_services = SandboxFactory::GetTargetServices();
+// if (NULL != target_services) {
+// //we are the target, call target api here
+// target_services->Init();
+// }
+//
+// The methods in this class are expected to be called from a single thread
+//
+// The Sandbox library needs to be linked against the main executable, but
+// sometimes the API calls are issued from a DLL that loads into the exe
+// process. These factory methods then need to be called from the main
+// exe and the interface pointers then can be safely passed to the DLL where
+// the Sandbox API calls are made.
+namespace sandbox {
+
+class SandboxFactory {
+ public:
+ // Returns the Broker API interface, returns NULL if this process is the
+ // target.
+ static BrokerServices* GetBrokerServices();
+
+ // Returns the Target API interface, returns NULL if this process is the
+ // broker.
+ static TargetServices* GetTargetServices();
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxFactory);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_FACTORY_H__
diff --git a/chromium/sandbox/win/src/sandbox_nt_types.h b/chromium/sandbox/win/src/sandbox_nt_types.h
new file mode 100644
index 00000000000..1303ac2627c
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_nt_types.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2006-2008 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_SANDBOX_NT_TYPES_H__
+#define SANDBOX_SRC_SANDBOX_NT_TYPES_H__
+
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+struct NtExports {
+ NtAllocateVirtualMemoryFunction AllocateVirtualMemory;
+ NtCloseFunction Close;
+ NtDuplicateObjectFunction DuplicateObject;
+ NtFreeVirtualMemoryFunction FreeVirtualMemory;
+ NtMapViewOfSectionFunction MapViewOfSection;
+ NtProtectVirtualMemoryFunction ProtectVirtualMemory;
+ NtQueryInformationProcessFunction QueryInformationProcess;
+ NtQueryObjectFunction QueryObject;
+ NtQuerySectionFunction QuerySection;
+ NtQueryVirtualMemoryFunction QueryVirtualMemory;
+ NtUnmapViewOfSectionFunction UnmapViewOfSection;
+ RtlAllocateHeapFunction RtlAllocateHeap;
+ RtlAnsiStringToUnicodeStringFunction RtlAnsiStringToUnicodeString;
+ RtlCompareUnicodeStringFunction RtlCompareUnicodeString;
+ RtlCreateHeapFunction RtlCreateHeap;
+ RtlCreateUserThreadFunction RtlCreateUserThread;
+ RtlDestroyHeapFunction RtlDestroyHeap;
+ RtlFreeHeapFunction RtlFreeHeap;
+ _strnicmpFunction _strnicmp;
+ strlenFunction strlen;
+ wcslenFunction wcslen;
+};
+
+// This is the value used for the ntdll level allocator.
+enum AllocationType {
+ NT_ALLOC,
+ NT_PLACE,
+ NT_PAGE
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SANDBOX_NT_TYPES_H__
diff --git a/chromium/sandbox/win/src/sandbox_nt_util.cc b/chromium/sandbox/win/src/sandbox_nt_util.cc
new file mode 100644
index 00000000000..123a26e36b4
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_nt_util.cc
@@ -0,0 +1,599 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt = { NULL };
+
+} // namespace
+
+namespace {
+
+#if defined(_WIN64)
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+
+ // Start with 1 GB above the source.
+ const unsigned int kOneGB = 0x40000000;
+ void* base = reinterpret_cast<char*>(source) + kOneGB;
+ SIZE_T actual_size = size;
+ ULONG_PTR zero_bits = 0; // Not the correct type if used.
+ ULONG type = MEM_RESERVE;
+
+ if (reinterpret_cast<SIZE_T>(source) > 0x7ff80000000) {
+ // We are at the top of the address space. Let's try the highest available
+ // address.
+ base = NULL;
+ type |= MEM_TOP_DOWN;
+ }
+
+ NTSTATUS ret;
+ int attempts = 0;
+ for (; attempts < 20; attempts++) {
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
+ &actual_size, type, PAGE_READWRITE);
+ if (NT_SUCCESS(ret)) {
+ if (base < source) {
+ // We won't be able to patch this dll.
+ VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
+ MEM_RELEASE));
+ return NULL;
+ }
+ break;
+ }
+
+ // Try 100 MB higher.
+ base = reinterpret_cast<char*>(base) + 100 * 0x100000;
+ };
+
+ if (attempts == 20)
+ return NULL;
+
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
+ &actual_size, MEM_COMMIT, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(ret)) {
+ VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
+ MEM_RELEASE));
+ base = NULL;
+ }
+
+ return base;
+}
+#else // defined(_WIN64).
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+ UNREFERENCED_PARAMETER(source);
+
+ // In 32-bit processes allocations below 512k are predictable, so mark
+ // anything in that range as reserved and retry until we get a good address.
+ const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024);
+ NTSTATUS ret;
+ SIZE_T actual_size;
+ void* base;
+ do {
+ base = NULL;
+ actual_size = 64 * 1024;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_RESERVE, PAGE_NOACCESS);
+ if (!NT_SUCCESS(ret))
+ return NULL;
+ } while (base < kMinAddress);
+
+ actual_size = size;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return NULL;
+ return base;
+}
+#endif // defined(_WIN64).
+
+} // namespace.
+
+namespace sandbox {
+
+// Handle for our private heap.
+void* g_heap = NULL;
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0;
+SANDBOX_INTERCEPT size_t g_shared_policy_size = 0;
+
+void* volatile g_shared_policy_memory = NULL;
+void* volatile g_shared_IPC_memory = NULL;
+
+// Both the IPC and the policy share a single region of memory in which the IPC
+// memory is first and the policy memory is last.
+bool MapGlobalMemory() {
+ if (NULL == g_shared_IPC_memory) {
+ void* memory = NULL;
+ SIZE_T size = 0;
+ // Map the entire shared section from the start.
+ NTSTATUS ret = g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess,
+ &memory, 0, 0, NULL, &size, ViewUnmap,
+ 0, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(ret) || NULL == memory) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ if (NULL != _InterlockedCompareExchangePointer(&g_shared_IPC_memory,
+ memory, NULL)) {
+ // Somebody beat us to the memory setup.
+ ret = g_nt.UnmapViewOfSection(NtCurrentProcess, memory);
+ VERIFY_SUCCESS(ret);
+ }
+ DCHECK_NT(g_shared_IPC_size > 0);
+ g_shared_policy_memory = reinterpret_cast<char*>(g_shared_IPC_memory)
+ + g_shared_IPC_size;
+ }
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+ return true;
+}
+
+void* GetGlobalIPCMemory() {
+ if (!MapGlobalMemory())
+ return NULL;
+ return g_shared_IPC_memory;
+}
+
+void* GetGlobalPolicyMemory() {
+ if (!MapGlobalMemory())
+ return NULL;
+ return g_shared_policy_memory;
+}
+
+bool InitHeap() {
+ if (!g_heap) {
+ // Create a new heap using default values for everything.
+ void* heap = g_nt.RtlCreateHeap(HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
+ if (!heap)
+ return false;
+
+ if (NULL != _InterlockedCompareExchangePointer(&g_heap, heap, NULL)) {
+ // Somebody beat us to the memory setup.
+ g_nt.RtlDestroyHeap(heap);
+ }
+ }
+ return (g_heap != NULL);
+}
+
+// Physically reads or writes from memory to verify that (at this time), it is
+// valid. Returns a dummy value.
+int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
+ const int kPageSize = 4096;
+ int dummy = 0;
+ char* start = reinterpret_cast<char*>(buffer);
+ char* end = start + size_bytes - 1;
+
+ if (WRITE == intent) {
+ for (; start < end; start += kPageSize) {
+ *start = 0;
+ }
+ *end = 0;
+ } else {
+ for (; start < end; start += kPageSize) {
+ dummy += *start;
+ }
+ dummy += *end;
+ }
+
+ return dummy;
+}
+
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
+ DCHECK_NT(size);
+ __try {
+ TouchMemory(buffer, size, intent);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ __try {
+ if (SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
+ memcpy(destination, source, bytes);
+ } else {
+ const char* from = reinterpret_cast<const char*>(source);
+ char* to = reinterpret_cast<char*>(destination);
+ for (size_t i = 0; i < bytes; i++) {
+ to[i] = from[i];
+ }
+ }
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+ return ret;
+}
+
+// Hacky code... replace with AllocAndCopyObjectAttributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ wchar_t** out_name, uint32* attributes,
+ HANDLE* root) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(out_name);
+ *out_name = NULL;
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
+ break;
+ if (NULL == in_object->ObjectName)
+ break;
+ if (NULL == in_object->ObjectName->Buffer)
+ break;
+
+ size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
+ *out_name = new(NT_ALLOC) wchar_t[size/sizeof(wchar_t)];
+ if (NULL == *out_name)
+ break;
+
+ ret = CopyData(*out_name, in_object->ObjectName->Buffer,
+ size - sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+
+ (*out_name)[size / sizeof(wchar_t) - 1] = L'\0';
+
+ if (attributes)
+ *attributes = in_object->Attributes;
+
+ if (root)
+ *root = in_object->RootDirectory;
+ ret = STATUS_SUCCESS;
+ } while (false);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret) && *out_name) {
+ operator delete(*out_name, NT_ALLOC);
+ *out_name = NULL;
+ }
+
+ return ret;
+}
+
+NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) {
+ PROCESS_BASIC_INFORMATION proc_info;
+ ULONG bytes_returned;
+
+ NTSTATUS ret = g_nt.QueryInformationProcess(process, ProcessBasicInformation,
+ &proc_info, sizeof(proc_info),
+ &bytes_returned);
+ if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
+ return ret;
+
+ *process_id = proc_info.UniqueProcessId;
+ return STATUS_SUCCESS;
+}
+
+bool IsSameProcess(HANDLE process) {
+ if (NtCurrentProcess == process)
+ return true;
+
+ static ULONG s_process_id = 0;
+
+ if (!s_process_id) {
+ NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+ }
+
+ ULONG process_id;
+ NTSTATUS ret = GetProcessId(process, &process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ return (process_id == s_process_id);
+}
+
+bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset,
+ PSIZE_T view_size) {
+ if (!section || !base || !view_size || offset)
+ return false;
+
+ HANDLE query_section;
+
+ NTSTATUS ret = g_nt.DuplicateObject(NtCurrentProcess, section,
+ NtCurrentProcess, &query_section,
+ SECTION_QUERY, 0, 0);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ SECTION_BASIC_INFORMATION basic_info;
+ SIZE_T bytes_returned;
+ ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
+ sizeof(basic_info), &bytes_returned);
+
+ VERIFY_SUCCESS(g_nt.Close(query_section));
+
+ if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
+ return false;
+
+ if (!(basic_info.Attributes & SEC_IMAGE))
+ return false;
+
+ return true;
+}
+
+UNICODE_STRING* AnsiToUnicode(const char* string) {
+ ANSI_STRING ansi_string;
+ ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
+ ansi_string.MaximumLength = ansi_string.Length + 1;
+ ansi_string.Buffer = const_cast<char*>(string);
+
+ if (ansi_string.Length > ansi_string.MaximumLength)
+ return NULL;
+
+ size_t name_bytes = ansi_string.MaximumLength * sizeof(wchar_t) +
+ sizeof(UNICODE_STRING);
+
+ UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(
+ new(NT_ALLOC) char[name_bytes]);
+ if (!out_string)
+ return NULL;
+
+ out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+
+ BOOLEAN alloc_destination = FALSE;
+ NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
+ alloc_destination);
+ DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return NULL;
+ }
+
+ return out_string;
+}
+
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) {
+ UNICODE_STRING* out_name = NULL;
+ __try {
+ do {
+ *flags = 0;
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+ *flags |= MODULE_IS_PE_IMAGE;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports) {
+ char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
+ out_name = AnsiToUnicode(name);
+ }
+
+ PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
+ if (headers) {
+ if (headers->OptionalHeader.AddressOfEntryPoint)
+ *flags |= MODULE_HAS_ENTRY_POINT;
+ if (headers->OptionalHeader.SizeOfCode)
+ *flags |= MODULE_HAS_CODE;
+ }
+ } while (false);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+}
+
+UNICODE_STRING* GetBackingFilePath(PVOID address) {
+ // We'll start with something close to max_path charactes for the name.
+ ULONG buffer_bytes = MAX_PATH * 2;
+
+ for (;;) {
+ MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
+ new(NT_ALLOC) char[buffer_bytes]);
+
+ if (!section_name)
+ return NULL;
+
+ ULONG returned_bytes;
+ NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address,
+ MemorySectionName, section_name,
+ buffer_bytes, &returned_bytes);
+
+ if (STATUS_BUFFER_OVERFLOW == ret) {
+ // Retry the call with the given buffer size.
+ operator delete(section_name, NT_ALLOC);
+ section_name = NULL;
+ buffer_bytes = returned_bytes;
+ continue;
+ }
+ if (!NT_SUCCESS(ret)) {
+ operator delete(section_name, NT_ALLOC);
+ return NULL;
+ }
+
+ return reinterpret_cast<UNICODE_STRING*>(section_name);
+ }
+}
+
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
+ if ((!module_path) || (!module_path->Buffer))
+ return NULL;
+
+ wchar_t* sep = NULL;
+ int start_pos = module_path->Length / sizeof(wchar_t) - 1;
+ int ix = start_pos;
+
+ for (; ix >= 0; --ix) {
+ if (module_path->Buffer[ix] == L'\\') {
+ sep = &module_path->Buffer[ix];
+ break;
+ }
+ }
+
+ // Ends with path separator. Not a valid module name.
+ if ((ix == start_pos) && sep)
+ return NULL;
+
+ // No path separator found. Use the entire name.
+ if (!sep) {
+ sep = &module_path->Buffer[-1];
+ }
+
+ // Add one to the size so we can null terminate the string.
+ size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
+
+ // Based on the code above, size_bytes should always be small enough
+ // to make the static_cast below safe.
+ DCHECK_NT(kuint16max > size_bytes);
+ char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
+ if (!str_buffer)
+ return NULL;
+
+ UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+ out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
+ out_string->MaximumLength = static_cast<USHORT>(size_bytes);
+
+ NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return NULL;
+ }
+
+ out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
+ return out_string;
+}
+
+NTSTATUS AutoProtectMemory::ChangeProtection(void* address, size_t bytes,
+ ULONG protect) {
+ DCHECK_NT(!changed_);
+ SIZE_T new_bytes = bytes;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
+ &new_bytes, protect, &old_protect_);
+ if (NT_SUCCESS(ret)) {
+ changed_ = true;
+ address_ = address;
+ bytes_ = new_bytes;
+ }
+
+ return ret;
+}
+
+NTSTATUS AutoProtectMemory::RevertProtection() {
+ if (!changed_)
+ return STATUS_SUCCESS;
+
+ DCHECK_NT(address_);
+ DCHECK_NT(bytes_);
+
+ SIZE_T new_bytes = bytes_;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address_,
+ &new_bytes, old_protect_,
+ &old_protect_);
+ DCHECK_NT(NT_SUCCESS(ret));
+
+ changed_ = false;
+ address_ = NULL;
+ bytes_ = 0;
+ old_protect_ = 0;
+
+ return ret;
+}
+
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length,
+ uint32 file_info_class) {
+ if (FileRenameInformation != file_info_class)
+ return false;
+
+ if (length < sizeof(FILE_RENAME_INFORMATION))
+ return false;
+
+ // Make sure file name length doesn't exceed the message length
+ if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
+ file_info->FileNameLength)
+ return false;
+
+ // We don't support a root directory.
+ if (file_info->RootDirectory)
+ return false;
+
+ // Check if it starts with \\??\\. We don't support relative paths.
+ if (file_info->FileNameLength < 4 || 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'\\')
+ return false;
+
+ return true;
+}
+
+} // namespace sandbox
+
+void* operator new(size_t size, sandbox::AllocationType type,
+ void* near_to) {
+ using namespace sandbox;
+
+ if (NT_ALLOC == type) {
+ if (!InitHeap())
+ return NULL;
+
+ // Use default flags for the allocation.
+ return g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
+ } else if (NT_PAGE == type) {
+ return AllocateNearTo(near_to, size);
+ }
+ NOTREACHED_NT();
+ return NULL;
+}
+
+void operator delete(void* memory, sandbox::AllocationType type) {
+ using namespace sandbox;
+
+ if (NT_ALLOC == type) {
+ // Use default flags.
+ VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory));
+ } else if (NT_PAGE == type) {
+ void* base = memory;
+ SIZE_T size = 0;
+ VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
+ MEM_RELEASE));
+ } else {
+ NOTREACHED_NT();
+ }
+}
+
+void operator delete(void* memory, sandbox::AllocationType type,
+ void* near_to) {
+ UNREFERENCED_PARAMETER(near_to);
+ operator delete(memory, type);
+}
+
+void* __cdecl operator new(size_t size, void* buffer,
+ sandbox::AllocationType type) {
+ UNREFERENCED_PARAMETER(size);
+ UNREFERENCED_PARAMETER(type);
+ return buffer;
+}
+
+void __cdecl operator delete(void* memory, void* buffer,
+ sandbox::AllocationType type) {
+ UNREFERENCED_PARAMETER(memory);
+ UNREFERENCED_PARAMETER(buffer);
+ UNREFERENCED_PARAMETER(type);
+}
diff --git a/chromium/sandbox/win/src/sandbox_nt_util.h b/chromium/sandbox/win/src/sandbox_nt_util.h
new file mode 100644
index 00000000000..40f261bc827
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_nt_util.h
@@ -0,0 +1,173 @@
+// Copyright (c) 2010 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_SANDBOX_NT_UTIL_H_
+#define SANDBOX_SRC_SANDBOX_NT_UTIL_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+
+// Placement new and delete to be used from ntdll interception code.
+void* __cdecl operator new(size_t size, sandbox::AllocationType type,
+ void* near_to = NULL);
+void __cdecl operator delete(void* memory, sandbox::AllocationType type);
+// Add operator delete that matches the placement form of the operator new
+// above. This is required by compiler to generate code to call operator delete
+// in case the object's constructor throws an exception.
+// See http://msdn.microsoft.com/en-us/library/cxdxz3x6.aspx
+void __cdecl operator delete(void* memory, sandbox::AllocationType type,
+ void* near_to);
+
+// Regular placement new and delete
+void* __cdecl operator new(size_t size, void* buffer,
+ sandbox::AllocationType type);
+void __cdecl operator delete(void* memory, void* buffer,
+ sandbox::AllocationType type);
+
+// DCHECK_NT is defined to be pretty much an assert at this time because we
+// don't have logging from the ntdll layer on the child.
+//
+// VERIFY_NT and VERIFY_SUCCESS_NT are the standard asserts on debug, but
+// execute the actual argument on release builds. VERIFY_NT expects an action
+// returning a bool, while VERIFY_SUCCESS_NT expects an action returning
+// NTSTATUS.
+#ifndef NDEBUG
+#define DCHECK_NT(condition) { (condition) ? (void)0 : __debugbreak(); }
+#define VERIFY(action) DCHECK_NT(action)
+#define VERIFY_SUCCESS(action) DCHECK_NT(NT_SUCCESS(action))
+#else
+#define DCHECK_NT(condition)
+#define VERIFY(action) (action)
+#define VERIFY_SUCCESS(action) (action)
+#endif
+
+#define NOTREACHED_NT() DCHECK_NT(false)
+
+namespace sandbox {
+
+extern "C" long _InterlockedCompareExchange(long volatile* destination,
+ long exchange, long comperand);
+
+#pragma intrinsic(_InterlockedCompareExchange)
+
+// We want to make sure that we use an intrinsic version of the function, not
+// the one provided by kernel32.
+__forceinline void* _InterlockedCompareExchangePointer(
+ void* volatile* destination, void* exchange, void* comperand) {
+ size_t ret = _InterlockedCompareExchange(
+ reinterpret_cast<long volatile*>(destination),
+ static_cast<long>(reinterpret_cast<size_t>(exchange)),
+ static_cast<long>(reinterpret_cast<size_t>(comperand)));
+
+ return reinterpret_cast<void*>(static_cast<size_t>(ret));
+}
+
+// Returns a pointer to the IPC shared memory.
+void* GetGlobalIPCMemory();
+
+// Returns a pointer to the Policy shared memory.
+void* GetGlobalPolicyMemory();
+
+enum RequiredAccess {
+ READ,
+ WRITE
+};
+
+// Performs basic user mode buffer validation. In any case, buffers access must
+// be protected by SEH. intent specifies if the buffer should be tested for read
+// or write.
+// Note that write intent implies destruction of the buffer content (we actually
+// write)
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent);
+
+
+// Copies data from a user buffer to our buffer. Returns the operation status.
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes);
+
+// Copies the name from an object attributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ wchar_t** out_name, uint32* attributes, HANDLE* root);
+
+// Initializes our ntdll level heap
+bool InitHeap();
+
+// Returns true if the provided handle refers to the current process.
+bool IsSameProcess(HANDLE process);
+
+enum MappedModuleFlags {
+ MODULE_IS_PE_IMAGE = 1, // Module is an executable.
+ MODULE_HAS_ENTRY_POINT = 2, // Execution entry point found.
+ MODULE_HAS_CODE = 4 // Non zero size of executable sections.
+};
+
+// Returns the name and characteristics for a given PE module. The return
+// value is the name as defined by the export table and the flags is any
+// combination of the MappedModuleFlags enumeration.
+//
+// The returned buffer must be freed with a placement delete from the ntdll
+// level allocator:
+//
+// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags);
+// if (!name) {
+// // probably not a valid dll
+// return;
+// }
+// InsertYourLogicHere(name);
+// operator delete(name, NT_ALLOC);
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags);
+
+// Returns the full path and filename for a given dll.
+// May return NULL if the provided address is not backed by a named section, or
+// if the current OS version doesn't support the call. The returned buffer must
+// be freed with a placement delete (see GetImageNameFromModule example).
+UNICODE_STRING* GetBackingFilePath(PVOID address);
+
+// Returns the last component of a path that contains the module name.
+// It will return NULL if the path ends with the path separator. The returned
+// buffer must be freed with a placement delete (see GetImageNameFromModule
+// example).
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path);
+
+// Returns true if the parameters correspond to a dll mapped as code.
+bool IsValidImageSection(HANDLE section, PVOID *base, PLARGE_INTEGER offset,
+ PSIZE_T view_size);
+
+// Converts an ansi string to an UNICODE_STRING.
+UNICODE_STRING* AnsiToUnicode(const char* string);
+
+// Provides a simple way to temporarily change the protection of a memory page.
+class AutoProtectMemory {
+ public:
+ AutoProtectMemory()
+ : changed_(false), address_(NULL), bytes_(0), old_protect_(0) {}
+
+ ~AutoProtectMemory() {
+ RevertProtection();
+ }
+
+ // Sets the desired protection of a given memory range.
+ NTSTATUS ChangeProtection(void* address, size_t bytes, ULONG protect);
+
+ // Restores the original page protection.
+ NTSTATUS RevertProtection();
+
+ private:
+ bool changed_;
+ void* address_;
+ size_t bytes_;
+ ULONG old_protect_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
+};
+
+// Returns true if the file_rename_information structure is supported by our
+// rename handler.
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length,
+ uint32 file_info_class);
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H__
diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h
new file mode 100644
index 00000000000..733356a39b8
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_policy.h
@@ -0,0 +1,227 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/security_level.h"
+
+namespace sandbox {
+
+class TargetPolicy {
+ public:
+ // Windows subsystems that can have specific rules.
+ // Note: The process subsystem(SUBSY_PROCESS) does not evaluate the request
+ // exactly like the CreateProcess API does. See the comment at the top of
+ // process_thread_dispatcher.cc for more details.
+ enum SubSystem {
+ SUBSYS_FILES, // Creation and opening of files and pipes.
+ SUBSYS_NAMED_PIPES, // Creation of named pipes.
+ 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.
+ };
+
+ // Allowable semantics when a rule is matched.
+ enum Semantics {
+ FILES_ALLOW_ANY, // Allows open or create for any kind of access that
+ // the file system supports.
+ FILES_ALLOW_READONLY, // Allows open or create with read access only.
+ FILES_ALLOW_QUERY, // Allows access to query the attributes of a file.
+ FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics
+ // only.
+ HANDLES_DUP_ANY, // Allows duplicating handles opened with any
+ // access permissions.
+ HANDLES_DUP_BROKER, // Allows duplicating handles to the broker process.
+ NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe.
+ PROCESS_MIN_EXEC, // Allows to create a process with minimal rights
+ // over the resulting process and thread handles.
+ // No other parameters besides the command line are
+ // passed to the child process.
+ PROCESS_ALL_EXEC, // Allows the creation of a process and return fill
+ // access on the returned handles.
+ // This flag can be used only when the main token of
+ // the sandboxed application is at least INTERACTIVE.
+ 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.
+ };
+
+ // Increments the reference count of this object. The reference count must
+ // be incremented if this interface is given to another component.
+ virtual void AddRef() = 0;
+
+ // Decrements the reference count of this object. When the reference count
+ // is zero the object is automatically destroyed.
+ // Indicates that the caller is done with this interface. After calling
+ // release no other method should be called.
+ virtual void Release() = 0;
+
+ // Sets the security level for the target process' two tokens.
+ // This setting is permanent and cannot be changed once the target process is
+ // spawned.
+ // initial: the security level for the initial token. This is the token that
+ // is used by the process from the creation of the process until the moment
+ // the process calls TargetServices::LowerToken() or the process calls
+ // win32's ReverToSelf(). Once this happens the initial token is no longer
+ // available and the lockdown token is in effect. Using an initial token is
+ // not compatible with AppContainer, see SetAppContainer.
+ // lockdown: the security level for the token that comes into force after the
+ // process calls TargetServices::LowerToken() or the process calls
+ // ReverToSelf(). See the explanation of each level in the TokenLevel
+ // definition.
+ // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise.
+ // Returns false if the lockdown value is more permissive than the initial
+ // value.
+ //
+ // Important: most of the sandbox-provided security relies on this single
+ // setting. The caller should strive to set the lockdown level as restricted
+ // as possible.
+ virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 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
+ // can not be specified in the token security profile.
+ // job_level: the security level for the job. See the explanation of each
+ // level in the JobLevel definition.
+ // ui_exceptions: specify what specific rights that are disabled in the
+ // chosen job_level that need to be granted. Use this parameter to avoid
+ // selecting the next permissive job level unless you need all the rights
+ // that are granted in such level.
+ // The exceptions can be specified as a combination of the following
+ // constants:
+ // JOB_OBJECT_UILIMIT_HANDLES : grant access to all user-mode handles. These
+ // include windows, icons, menus and various GDI objects. In addition the
+ // target process can set hooks, and broadcast messages to other processes
+ // that belong to the same desktop.
+ // JOB_OBJECT_UILIMIT_READCLIPBOARD : grant read-only access to the clipboard.
+ // JOB_OBJECT_UILIMIT_WRITECLIPBOARD : grant write access to the clipboard.
+ // JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS : allow changes to the system-wide
+ // parameters as defined by the Win32 call SystemParametersInfo().
+ // JOB_OBJECT_UILIMIT_DISPLAYSETTINGS : allow programmatic changes to the
+ // display settings.
+ // JOB_OBJECT_UILIMIT_GLOBALATOMS : allow access to the global atoms table.
+ // JOB_OBJECT_UILIMIT_DESKTOP : allow the creation of new desktops.
+ // JOB_OBJECT_UILIMIT_EXITWINDOWS : allow the call to ExitWindows().
+ //
+ // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise.
+ //
+ // Note: JOB_OBJECT_XXXX constants are defined in winnt.h and documented at
+ // length in:
+ // http://msdn2.microsoft.com/en-us/library/ms684152.aspx
+ //
+ // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN.
+ virtual ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) = 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.
+ virtual ResultCode SetAlternateDesktop(bool alternate_winstation) = 0;
+
+ // Returns the name of the alternate desktop used. If an alternate window
+ // station is specified, the name is prepended by the window station name,
+ // followed by a backslash.
+ virtual std::wstring GetAlternateDesktop() const = 0;
+
+ // Precreates the desktop and window station, if any.
+ virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) = 0;
+
+ // Destroys the desktop and windows station.
+ virtual void DestroyAlternateDesktop() = 0;
+
+ // Sets the integrity level of the process in the sandbox. Both the initial
+ // token and the main token will be affected by this. If the integrity level
+ // is set to a level higher than the current level, the sandbox will fail
+ // to start.
+ virtual ResultCode SetIntegrityLevel(IntegrityLevel level) = 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
+ // process in the sandbox. If the integrity level is set to a level higher
+ // than the current level, the sandbox will fail to start.
+ virtual ResultCode SetDelayedIntegrityLevel(IntegrityLevel level) = 0;
+
+ // Sets the AppContainer to be used for the sandboxed process. Any capability
+ // to be enabled for the process should be added before this method is invoked
+ // (by calling SetCapability() as many times as needed).
+ // The desired AppContainer must be already installed on the system, otherwise
+ // launching the sandboxed process will fail. See BrokerServices for details
+ // about installing an AppContainer.
+ // Note that currently Windows restricts the use of impersonation within
+ // AppContainers, so this function is incompatible with the use of an initial
+ // token.
+ virtual ResultCode SetAppContainer(const wchar_t* sid) = 0;
+
+ // Sets a capability to be enabled for the sandboxed process' AppContainer.
+ virtual ResultCode SetCapability(const wchar_t* sid) = 0;
+
+ // Sets the mitigations enabled when the process is created. Most of these
+ // are implemented as attributes passed via STARTUPINFOEX. So they take
+ // effect before any thread in the target executes. The declaration of
+ // MitigationFlags is followed by a detailed description of each flag.
+ virtual ResultCode SetProcessMitigations(MitigationFlags flags) = 0;
+
+ // Returns the currently set mitigation flags.
+ virtual MitigationFlags GetProcessMitigations() = 0;
+
+ // Sets process mitigation flags that don't take effect before the call to
+ // LowerToken().
+ virtual ResultCode SetDelayedProcessMitigations(MitigationFlags flags) = 0;
+
+ // Returns the currently set delayed mitigation flags.
+ virtual MitigationFlags GetDelayedProcessMitigations() = 0;
+
+ // Sets the interceptions to operate in strict mode. By default, interceptions
+ // are performed in "relaxed" mode, where if something inside NTDLL.DLL is
+ // already patched we attempt to intercept it anyway. Setting interceptions
+ // to strict mode means that when we detect that the function is patched we'll
+ // refuse to perform the interception.
+ virtual void SetStrictInterceptions() = 0;
+
+ // Set the handles the target process should inherit for stdout and
+ // stderr. The handles the caller passes must remain valid for the
+ // lifetime of the policy object. This only has an effect on
+ // Windows Vista and later versions. These methods accept pipe and
+ // file handles, but not console handles.
+ virtual ResultCode SetStdoutHandle(HANDLE handle) = 0;
+ virtual ResultCode SetStderrHandle(HANDLE handle) = 0;
+
+ // Adds a policy rule effective for processes spawned using this policy.
+ // subsystem: One of the above enumerated windows subsystems.
+ // semantics: One of the above enumerated FileSemantics.
+ // pattern: A specific full path or a full path with wildcard patterns.
+ // The valid wildcards are:
+ // '*' : Matches zero or more character. Only one in series allowed.
+ // '?' : Matches a single character. One or more in series are allowed.
+ // Examples:
+ // "c:\\documents and settings\\vince\\*.dmp"
+ // "c:\\documents and settings\\*\\crashdumps\\*.dmp"
+ // "c:\\temp\\app_log_?????_chrome.txt"
+ virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics,
+ const wchar_t* pattern) = 0;
+
+ // Adds a dll that will be unloaded in the target process before it gets
+ // a chance to initialize itself. Typically, dlls that cause the target
+ // to crash go here.
+ virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0;
+
+ // 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.
+ virtual ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) = 0;
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc
new file mode 100644
index 00000000000..220a0703024
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_policy_base.cc
@@ -0,0 +1,675 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container.h"
+#include "sandbox/win/src/filesystem_dispatcher.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/handle_dispatcher.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/named_pipe_dispatcher.h"
+#include "sandbox/win/src/named_pipe_policy.h"
+#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_thread_dispatcher.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/registry_dispatcher.h"
+#include "sandbox/win/src/registry_policy.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sync_dispatcher.h"
+#include "sandbox/win/src/sync_policy.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/window.h"
+
+namespace {
+
+// The standard windows size for one memory page.
+const size_t kOneMemPage = 4096;
+// The IPC and Policy shared memory sizes.
+const size_t kIPCMemSize = kOneMemPage * 2;
+const size_t kPolMemSize = kOneMemPage * 14;
+
+// Helper function to allocate space (on the heap) for policy.
+sandbox::PolicyGlobal* MakeBrokerPolicyMemory() {
+ const size_t kTotalPolicySz = kPolMemSize;
+ sandbox::PolicyGlobal* policy = static_cast<sandbox::PolicyGlobal*>
+ (::operator new(kTotalPolicySz));
+ DCHECK(policy);
+ memset(policy, 0, kTotalPolicySz);
+ policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal);
+ return policy;
+}
+
+bool IsInheritableHandle(HANDLE handle) {
+ if (!handle)
+ return false;
+ if (handle == INVALID_HANDLE_VALUE)
+ return false;
+ // File handles (FILE_TYPE_DISK) and pipe handles are known to be
+ // inheritable. Console handles (FILE_TYPE_CHAR) are not
+ // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ DWORD handle_type = GetFileType(handle);
+ return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
+}
+
+}
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level;
+SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations;
+
+// Initializes static members.
+HWINSTA PolicyBase::alternate_winstation_handle_ = NULL;
+HDESK PolicyBase::alternate_desktop_handle_ = NULL;
+
+PolicyBase::PolicyBase()
+ : ref_count(1),
+ lockdown_level_(USER_LOCKDOWN),
+ initial_level_(USER_LOCKDOWN),
+ job_level_(JOB_LOCKDOWN),
+ ui_exceptions_(0),
+ use_alternate_desktop_(false),
+ use_alternate_winstation_(false),
+ file_system_init_(false),
+ relaxed_interceptions_(true),
+ stdout_handle_(INVALID_HANDLE_VALUE),
+ stderr_handle_(INVALID_HANDLE_VALUE),
+ integrity_level_(INTEGRITY_LEVEL_LAST),
+ delayed_integrity_level_(INTEGRITY_LEVEL_LAST),
+ mitigations_(0),
+ delayed_mitigations_(0),
+ policy_maker_(NULL),
+ policy_(NULL) {
+ ::InitializeCriticalSection(&lock_);
+ // Initialize the IPC dispatcher array.
+ memset(&ipc_targets_, NULL, sizeof(ipc_targets_));
+ Dispatcher* dispatcher = NULL;
+
+ dispatcher = new FilesystemDispatcher(this);
+ ipc_targets_[IPC_NTCREATEFILE_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENFILE_TAG] = dispatcher;
+ ipc_targets_[IPC_NTSETINFO_RENAME_TAG] = dispatcher;
+ ipc_targets_[IPC_NTQUERYATTRIBUTESFILE_TAG] = dispatcher;
+ ipc_targets_[IPC_NTQUERYFULLATTRIBUTESFILE_TAG] = dispatcher;
+
+ dispatcher = new NamedPipeDispatcher(this);
+ ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher;
+
+ dispatcher = new ThreadProcessDispatcher(this);
+ ipc_targets_[IPC_NTOPENTHREAD_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENPROCESS_TAG] = dispatcher;
+ ipc_targets_[IPC_CREATEPROCESSW_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENPROCESSTOKEN_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENPROCESSTOKENEX_TAG] = dispatcher;
+
+ dispatcher = new SyncDispatcher(this);
+ ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher;
+ ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher;
+
+ dispatcher = new RegistryDispatcher(this);
+ ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher;
+ ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher;
+
+ dispatcher = new HandleDispatcher(this);
+ ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher;
+}
+
+PolicyBase::~PolicyBase() {
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ TargetProcess* target = (*it);
+ delete target;
+ }
+ delete ipc_targets_[IPC_NTCREATEFILE_TAG];
+ delete ipc_targets_[IPC_CREATENAMEDPIPEW_TAG];
+ delete ipc_targets_[IPC_NTOPENTHREAD_TAG];
+ delete ipc_targets_[IPC_CREATEEVENT_TAG];
+ delete ipc_targets_[IPC_NTCREATEKEY_TAG];
+ delete ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG];
+ delete policy_maker_;
+ delete policy_;
+ ::DeleteCriticalSection(&lock_);
+}
+
+void PolicyBase::AddRef() {
+ ::InterlockedIncrement(&ref_count);
+}
+
+void PolicyBase::Release() {
+ if (0 == ::InterlockedDecrement(&ref_count))
+ delete this;
+}
+
+ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
+ if (initial < lockdown) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ initial_level_ = initial;
+ lockdown_level_ = lockdown;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) {
+ job_level_ = job_level;
+ ui_exceptions_ = ui_exceptions;
+ 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 {
+ // No alternate desktop or winstation. Return an empty string.
+ if (!use_alternate_desktop_ && !use_alternate_winstation_) {
+ return 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();
+ }
+ if (use_alternate_winstation_ && (!alternate_desktop_handle_ ||
+ !alternate_winstation_handle_)) {
+ return string16();
+ }
+
+ return GetFullDesktopName(alternate_winstation_handle_,
+ alternate_desktop_handle_);
+}
+
+ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
+ if (alternate_winstation) {
+ // Previously called with alternate_winstation = false?
+ if (!alternate_winstation_handle_ && alternate_desktop_handle_)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ // Check if it's already created.
+ if (alternate_winstation_handle_ && alternate_desktop_handle_)
+ return SBOX_ALL_OK;
+
+ DCHECK(!alternate_winstation_handle_);
+ // Create the window station.
+ ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_winstation_handle_ ||
+ GetWindowObjectName(alternate_winstation_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+
+ // Create the destkop.
+ result = CreateAltDesktop(alternate_winstation_handle_,
+ &alternate_desktop_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_handle_ ||
+ GetWindowObjectName(alternate_desktop_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ } else {
+ // Previously called with alternate_winstation = true?
+ if (alternate_winstation_handle_)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ // Check if it already exists.
+ if (alternate_desktop_handle_)
+ return SBOX_ALL_OK;
+
+ // Create the destkop.
+ ResultCode result = CreateAltDesktop(NULL, &alternate_desktop_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_handle_ ||
+ GetWindowObjectName(alternate_desktop_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+
+ return SBOX_ALL_OK;
+}
+
+void PolicyBase::DestroyAlternateDesktop() {
+ if (alternate_desktop_handle_) {
+ ::CloseDesktop(alternate_desktop_handle_);
+ alternate_desktop_handle_ = NULL;
+ }
+
+ if (alternate_winstation_handle_) {
+ ::CloseWindowStation(alternate_winstation_handle_);
+ alternate_winstation_handle_ = NULL;
+ }
+}
+
+ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetDelayedIntegrityLevel(
+ IntegrityLevel integrity_level) {
+ delayed_integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetAppContainer(const wchar_t* sid) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return SBOX_ALL_OK;
+
+ // Windows refuses to work with an impersonation token for a process inside
+ // an AppContainer. If the caller wants to use a more privileged initial
+ // token, or if the lockdown level will prevent the process from starting,
+ // we have to fail the operation.
+ if (lockdown_level_ < USER_LIMITED || lockdown_level_ != initial_level_)
+ return SBOX_ERROR_CANNOT_INIT_APPCONTAINER;
+
+ DCHECK(!appcontainer_list_.get());
+ appcontainer_list_.reset(new AppContainerAttributes);
+ ResultCode rv = appcontainer_list_->SetAppContainer(sid, capabilities_);
+ if (rv != SBOX_ALL_OK)
+ return rv;
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetCapability(const wchar_t* sid) {
+ capabilities_.push_back(sid);
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetProcessMitigations(
+ MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPreStartup(flags))
+ return SBOX_ERROR_BAD_PARAMS;
+ mitigations_ = flags;
+ return SBOX_ALL_OK;
+}
+
+MitigationFlags PolicyBase::GetProcessMitigations() {
+ return mitigations_;
+}
+
+ResultCode PolicyBase::SetDelayedProcessMitigations(
+ MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPostStartup(flags))
+ return SBOX_ERROR_BAD_PARAMS;
+ delayed_mitigations_ = flags;
+ return SBOX_ALL_OK;
+}
+
+MitigationFlags PolicyBase::GetDelayedProcessMitigations() {
+ return delayed_mitigations_;
+}
+
+void PolicyBase::SetStrictInterceptions() {
+ relaxed_interceptions_ = false;
+}
+
+ResultCode PolicyBase::SetStdoutHandle(HANDLE handle) {
+ if (!IsInheritableHandle(handle))
+ return SBOX_ERROR_BAD_PARAMS;
+ stdout_handle_ = handle;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetStderrHandle(HANDLE handle) {
+ if (!IsInheritableHandle(handle))
+ return SBOX_ERROR_BAD_PARAMS;
+ stderr_handle_ = handle;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddRule(SubSystem subsystem, Semantics semantics,
+ const wchar_t* pattern) {
+ if (NULL == policy_) {
+ policy_ = MakeBrokerPolicyMemory();
+ DCHECK(policy_);
+ policy_maker_ = new LowLevelPolicy(policy_);
+ DCHECK(policy_maker_);
+ }
+
+ switch (subsystem) {
+ case SUBSYS_FILES: {
+ if (!file_system_init_) {
+ if (!FileSystemPolicy::SetInitialRules(policy_maker_))
+ return SBOX_ERROR_BAD_PARAMS;
+ file_system_init_ = true;
+ }
+ if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_SYNC: {
+ if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_PROCESS: {
+ if (lockdown_level_ < USER_INTERACTIVE &&
+ TargetPolicy::PROCESS_ALL_EXEC == semantics) {
+ // This is unsupported. This is a huge security risk to give full access
+ // to a process handle.
+ return SBOX_ERROR_UNSUPPORTED;
+ }
+ if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_NAMED_PIPES: {
+ if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_REGISTRY: {
+ if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_HANDLES: {
+ if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ default: {
+ return SBOX_ERROR_UNSUPPORTED;
+ }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
+ blacklisted_dlls_.push_back(dll_name);
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddKernelObjectToClose(const char16* handle_type,
+ const char16* handle_name) {
+ return handle_closer_.AddHandle(handle_type, handle_name);
+}
+
+// When an IPC is ready in any of the targets we get called. We manage an array
+// of IPC dispatchers which are keyed on the IPC tag so we normally delegate
+// to the appropriate dispatcher unless we can handle the IPC call ourselves.
+Dispatcher* PolicyBase::OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) {
+ DCHECK(callback);
+ static const IPCParams ping1 = {IPC_PING1_TAG, ULONG_TYPE};
+ static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE};
+
+ if (ping1.Matches(ipc) || ping2.Matches(ipc)) {
+ *callback = reinterpret_cast<CallbackGeneric>(
+ static_cast<Callback1>(&PolicyBase::Ping));
+ return this;
+ }
+
+ Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag);
+ if (!dispatch) {
+ NOTREACHED();
+ return NULL;
+ }
+ return dispatch->OnMessageReady(ipc, callback);
+}
+
+// Delegate to the appropriate dispatcher.
+bool PolicyBase::SetupService(InterceptionManager* manager, int service) {
+ if (IPC_PING1_TAG == service || IPC_PING2_TAG == service)
+ return true;
+
+ Dispatcher* dispatch = GetDispatcher(service);
+ if (!dispatch) {
+ NOTREACHED();
+ return false;
+ }
+ return dispatch->SetupService(manager, service);
+}
+
+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_);
+ if (ERROR_SUCCESS != result) {
+ return SBOX_ERROR_GENERIC;
+ }
+ *job = job_obj.Detach();
+ } else {
+ *job = NULL;
+ }
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) {
+ // Create the 'naked' token. This will be the permanent token associated
+ // with the process and therefore with any thread that is not impersonating.
+ DWORD result = CreateRestrictedToken(lockdown, lockdown_level_,
+ integrity_level_, PRIMARY);
+ if (ERROR_SUCCESS != result) {
+ return SBOX_ERROR_GENERIC;
+ }
+
+ if (appcontainer_list_.get() && appcontainer_list_->HasAppContainer()) {
+ // Windows refuses to work with an impersonation token. See SetAppContainer
+ // implementation for more details.
+ if (lockdown_level_ < USER_LIMITED || lockdown_level_ != initial_level_)
+ return SBOX_ERROR_CANNOT_INIT_APPCONTAINER;
+
+ *initial = INVALID_HANDLE_VALUE;
+ return SBOX_ALL_OK;
+ }
+
+ // Create the 'better' token. We use this token as the one that the main
+ // thread uses when booting up the process. It should contain most of
+ // what we need (before reaching main( ))
+ result = CreateRestrictedToken(initial, initial_level_,
+ integrity_level_, IMPERSONATION);
+ if (ERROR_SUCCESS != result) {
+ ::CloseHandle(*lockdown);
+ return SBOX_ERROR_GENERIC;
+ }
+ return SBOX_ALL_OK;
+}
+
+const AppContainerAttributes* PolicyBase::GetAppContainer() {
+ if (!appcontainer_list_.get() || !appcontainer_list_->HasAppContainer())
+ return NULL;
+
+ return appcontainer_list_.get();
+}
+
+bool PolicyBase::AddTarget(TargetProcess* target) {
+ if (NULL != policy_)
+ policy_maker_->Done();
+
+ if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(),
+ mitigations_)) {
+ return false;
+ }
+
+ if (!SetupAllInterceptions(target))
+ return false;
+
+ if (!SetupHandleCloser(target))
+ return false;
+
+ // Initialize the sandbox infrastructure for the target.
+ if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize))
+ return false;
+
+ g_shared_delayed_integrity_level = delayed_integrity_level_;
+ ResultCode ret = target->TransferVariable(
+ "g_shared_delayed_integrity_level",
+ &g_shared_delayed_integrity_level,
+ sizeof(g_shared_delayed_integrity_level));
+ g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST;
+ if (SBOX_ALL_OK != ret)
+ return false;
+
+ // Add in delayed mitigations and pseudo-mitigations enforced at startup.
+ g_shared_delayed_mitigations = delayed_mitigations_ |
+ FilterPostStartupProcessMitigations(mitigations_);
+ if (!CanSetProcessMitigationsPostStartup(g_shared_delayed_mitigations))
+ return false;
+
+ ret = target->TransferVariable("g_shared_delayed_mitigations",
+ &g_shared_delayed_mitigations,
+ sizeof(g_shared_delayed_mitigations));
+ g_shared_delayed_mitigations = 0;
+ if (SBOX_ALL_OK != ret)
+ return false;
+
+ AutoLock lock(&lock_);
+ targets_.push_back(target);
+ return true;
+}
+
+bool PolicyBase::OnJobEmpty(HANDLE job) {
+ AutoLock lock(&lock_);
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ if ((*it)->Job() == job)
+ break;
+ }
+ if (it == targets_.end()) {
+ return false;
+ }
+ TargetProcess* target = *it;
+ targets_.erase(it);
+ delete target;
+ return true;
+}
+
+EvalResult PolicyBase::EvalPolicy(int service,
+ CountedParameterSetBase* params) {
+ if (NULL != policy_) {
+ if (NULL == policy_->entry[service]) {
+ // There is no policy for this particular service. This is not a big
+ // deal.
+ return DENY_ACCESS;
+ }
+ for (int i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED();
+ return SIGNAL_ALARM;
+ }
+ }
+ PolicyProcessor pol_evaluator(policy_->entry[service]);
+ PolicyResult result = pol_evaluator.Evaluate(kShortEval,
+ params->parameters,
+ params->count);
+ if (POLICY_MATCH == result) {
+ return pol_evaluator.GetAction();
+ }
+ DCHECK(POLICY_ERROR != result);
+ }
+
+ return DENY_ACCESS;
+}
+
+HANDLE PolicyBase::GetStdoutHandle() {
+ return stdout_handle_;
+}
+
+HANDLE PolicyBase::GetStderrHandle() {
+ return stderr_handle_;
+}
+
+// We service IPC_PING_TAG message which is a way to test a round trip of the
+// IPC subsystem. We receive a integer cookie and we are expected to return the
+// cookie times two (or three) and the current tick count.
+bool PolicyBase::Ping(IPCInfo* ipc, void* arg1) {
+ switch (ipc->ipc_tag) {
+ case IPC_PING1_TAG: {
+ IPCInt ipc_int(arg1);
+ uint32 cookie = ipc_int.As32Bit();
+ ipc->return_info.extended_count = 2;
+ ipc->return_info.extended[0].unsigned_int = ::GetTickCount();
+ ipc->return_info.extended[1].unsigned_int = 2 * cookie;
+ return true;
+ }
+ case IPC_PING2_TAG: {
+ CountedBuffer* io_buffer = reinterpret_cast<CountedBuffer*>(arg1);
+ if (sizeof(uint32) != io_buffer->Size())
+ return false;
+
+ uint32* cookie = reinterpret_cast<uint32*>(io_buffer->Buffer());
+ *cookie = (*cookie) * 3;
+ return true;
+ }
+ default: return false;
+ }
+}
+
+Dispatcher* PolicyBase::GetDispatcher(int ipc_tag) {
+ if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG)
+ return NULL;
+
+ return ipc_targets_[ipc_tag];
+}
+
+bool PolicyBase::SetupAllInterceptions(TargetProcess* target) {
+ InterceptionManager manager(target, relaxed_interceptions_);
+
+ if (policy_) {
+ for (int i = 0; i < IPC_LAST_TAG; i++) {
+ if (policy_->entry[i] && !ipc_targets_[i]->SetupService(&manager, i))
+ return false;
+ }
+ }
+
+ if (!blacklisted_dlls_.empty()) {
+ std::vector<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;
+
+ if (!manager.InitializeInterceptions())
+ return false;
+
+ // Finally, setup imports on the target so the interceptions can work.
+ return SetupNtdllImports(target);
+}
+
+bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
+ return handle_closer_.InitializeTargetHandles(target);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.h b/chromium/sandbox/win/src/sandbox_policy_base.h
new file mode 100644
index 00000000000..d56501d78bc
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_policy_base.h
@@ -0,0 +1,160 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
+
+#include <windows.h>
+
+#include <list>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+class AppContainerAttributes;
+class LowLevelPolicy;
+class TargetProcess;
+struct PolicyGlobal;
+
+// We act as a policy dispatcher, implementing the handler for the "ping" IPC,
+// so we have to provide the appropriate handler on the OnMessageReady method.
+// There is a static_cast for the handler, and the compiler only performs the
+// cast if the first base class is Dispatcher.
+class PolicyBase : public Dispatcher, public TargetPolicy {
+ public:
+ PolicyBase();
+
+ // TargetPolicy:
+ virtual void AddRef() OVERRIDE;
+ virtual void Release() OVERRIDE;
+ virtual ResultCode SetTokenLevel(TokenLevel initial,
+ TokenLevel lockdown) OVERRIDE;
+ virtual ResultCode SetJobLevel(JobLevel job_level,
+ uint32 ui_exceptions) OVERRIDE;
+ virtual ResultCode SetAlternateDesktop(bool alternate_winstation) OVERRIDE;
+ virtual string16 GetAlternateDesktop() const OVERRIDE;
+ virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) OVERRIDE;
+ virtual void DestroyAlternateDesktop() OVERRIDE;
+ virtual ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) OVERRIDE;
+ virtual ResultCode SetDelayedIntegrityLevel(
+ IntegrityLevel integrity_level) OVERRIDE;
+ virtual ResultCode SetAppContainer(const wchar_t* sid) OVERRIDE;
+ virtual ResultCode SetCapability(const wchar_t* sid) OVERRIDE;
+ virtual ResultCode SetProcessMitigations(MitigationFlags flags) OVERRIDE;
+ virtual MitigationFlags GetProcessMitigations() OVERRIDE;
+ virtual ResultCode SetDelayedProcessMitigations(
+ MitigationFlags flags) OVERRIDE;
+ virtual MitigationFlags GetDelayedProcessMitigations() 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;
+
+ // Dispatcher:
+ virtual Dispatcher* OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) OVERRIDE;
+ virtual bool SetupService(InterceptionManager* manager, int service) OVERRIDE;
+
+ // Creates a Job object with the level specified in a previous call to
+ // SetJobLevel().
+ ResultCode MakeJobObject(HANDLE* job);
+
+ // Creates the two tokens with the levels specified in a previous call to
+ // SetTokenLevel().
+ ResultCode MakeTokens(HANDLE* initial, HANDLE* lockdown);
+
+ const AppContainerAttributes* GetAppContainer();
+
+ // Adds a target process to the internal list of targets. Internally a
+ // call to TargetProcess::Init() is issued.
+ bool AddTarget(TargetProcess* target);
+
+ // Called when there are no more active processes in a Job.
+ // Removes a Job object associated with this policy and the target associated
+ // with the job.
+ bool OnJobEmpty(HANDLE job);
+
+ EvalResult EvalPolicy(int service, CountedParameterSetBase* params);
+
+ HANDLE GetStdoutHandle();
+ HANDLE GetStderrHandle();
+
+ private:
+ ~PolicyBase();
+
+ // Test IPC providers.
+ bool Ping(IPCInfo* ipc, void* cookie);
+
+ // Returns a dispatcher from ipc_targets_.
+ Dispatcher* GetDispatcher(int ipc_tag);
+
+ // Sets up interceptions for a new target.
+ bool SetupAllInterceptions(TargetProcess* target);
+
+ // Sets up the handle closer for a new target.
+ bool SetupHandleCloser(TargetProcess* target);
+
+ // This lock synchronizes operations on the targets_ collection.
+ CRITICAL_SECTION lock_;
+ // Maintains the list of target process associated with this policy.
+ // The policy takes ownership of them.
+ typedef std::list<TargetProcess*> TargetSet;
+ TargetSet targets_;
+ // Standard object-lifetime reference counter.
+ volatile LONG ref_count;
+ // The user-defined global policy settings.
+ TokenLevel lockdown_level_;
+ TokenLevel initial_level_;
+ JobLevel job_level_;
+ uint32 ui_exceptions_;
+ bool use_alternate_desktop_;
+ bool use_alternate_winstation_;
+ // Helps the file system policy initialization.
+ bool file_system_init_;
+ bool relaxed_interceptions_;
+ HANDLE stdout_handle_;
+ HANDLE stderr_handle_;
+ IntegrityLevel integrity_level_;
+ IntegrityLevel delayed_integrity_level_;
+ MitigationFlags mitigations_;
+ MitigationFlags delayed_mitigations_;
+ // The array of objects that will answer IPC calls.
+ Dispatcher* ipc_targets_[IPC_LAST_TAG];
+ // Object in charge of generating the low level policy.
+ LowLevelPolicy* policy_maker_;
+ // 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_;
+ // 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_;
+ scoped_ptr<AppContainerAttributes> appcontainer_list_;
+
+ static HDESK alternate_desktop_handle_;
+ static HWINSTA alternate_winstation_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
diff --git a/chromium/sandbox/win/src/sandbox_types.h b/chromium/sandbox/win/src/sandbox_types.h
new file mode 100644
index 00000000000..48f1b613f1e
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_types.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+#define SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+
+namespace sandbox {
+
+// Operation result codes returned by the sandbox API.
+enum ResultCode {
+ SBOX_ALL_OK = 0,
+ // Error is originating on the win32 layer. Call GetlastError() for more
+ // information.
+ SBOX_ERROR_GENERIC = 1,
+ // An invalid combination of parameters was given to the API.
+ SBOX_ERROR_BAD_PARAMS = 2,
+ // The desired operation is not supported at this time.
+ SBOX_ERROR_UNSUPPORTED = 3,
+ // The request requires more memory that allocated or available.
+ SBOX_ERROR_NO_SPACE = 4,
+ // The ipc service requested does not exist.
+ SBOX_ERROR_INVALID_IPC = 5,
+ // The ipc service did not complete.
+ SBOX_ERROR_FAILED_IPC = 6,
+ // The requested handle was not found.
+ SBOX_ERROR_NO_HANDLE = 7,
+ // This function was not expected to be called at this time.
+ SBOX_ERROR_UNEXPECTED_CALL = 8,
+ // WaitForAllTargets is already called.
+ SBOX_ERROR_WAIT_ALREADY_CALLED = 9,
+ // A channel error prevented DoCall from executing.
+ SBOX_ERROR_CHANNEL_ERROR = 10,
+ // Failed to create the alternate desktop.
+ SBOX_ERROR_CANNOT_CREATE_DESKTOP = 11,
+ // Failed to create the alternate window station.
+ SBOX_ERROR_CANNOT_CREATE_WINSTATION = 12,
+ // Failed to switch back to the interactive window station.
+ SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION = 13,
+ // The supplied AppContainer is not valid.
+ SBOX_ERROR_INVALID_APP_CONTAINER = 14,
+ // The supplied capability is not valid.
+ SBOX_ERROR_INVALID_CAPABILITY = 15,
+ // There is a failure initializing the AppContainer.
+ SBOX_ERROR_CANNOT_INIT_APPCONTAINER = 16,
+ // Initializing or updating ProcThreadAttributes failed.
+ SBOX_ERROR_PROC_THREAD_ATTRIBUTES = 17,
+ // Placeholder for last item of the enum.
+ SBOX_ERROR_LAST
+};
+
+// If the sandbox cannot create a secure environment for the target, the
+// target will be forcibly terminated. These are the process exit codes.
+enum TerminationCodes {
+ SBOX_FATAL_INTEGRITY = 7006, // Could not set the integrity level.
+ SBOX_FATAL_DROPTOKEN = 7007, // Could not lower the token.
+ SBOX_FATAL_FLUSHANDLES = 7008, // Failed to flush registry handles.
+ 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_LAST
+};
+
+class BrokerServices;
+class TargetServices;
+
+// Contains the pointer to a target or broker service.
+struct SandboxInterfaceInfo {
+ BrokerServices* broker_services;
+ TargetServices* target_services;
+};
+
+#if SANDBOX_EXPORTS
+#define SANDBOX_INTERCEPT extern "C" __declspec(dllexport)
+#else
+#define SANDBOX_INTERCEPT extern "C"
+#endif
+
+enum InterceptionType {
+ INTERCEPTION_INVALID = 0,
+ INTERCEPTION_SERVICE_CALL, // Trampoline of an NT native call
+ INTERCEPTION_EAT,
+ INTERCEPTION_SIDESTEP, // Preamble patch
+ INTERCEPTION_SMART_SIDESTEP, // Preamble patch but bypass internal calls
+ INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch)
+ INTERCEPTION_LAST // Placeholder for last item in the enumeration
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
diff --git a/chromium/sandbox/win/src/sandbox_utils.cc b/chromium/sandbox/win/src/sandbox_utils.cc
new file mode 100644
index 00000000000..f4511a47513
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_utils.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_utils.h"
+
+#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 std::wstring& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name) {
+ static RtlInitUnicodeStringFunction RtlInitUnicodeString;
+ if (!RtlInitUnicodeString) {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
+ GetProcAddress(ntdll, "RtlInitUnicodeString"));
+ DCHECK(RtlInitUnicodeString);
+ }
+ RtlInitUnicodeString(uni_name, name.c_str());
+ InitializeObjectAttributes(obj_attr, uni_name, attributes, root, NULL);
+}
+
+}; // namespace sandbox
diff --git a/chromium/sandbox/win/src/sandbox_utils.h b/chromium/sandbox/win/src/sandbox_utils.h
new file mode 100644
index 00000000000..78e76fbc733
--- /dev/null
+++ b/chromium/sandbox/win/src/sandbox_utils.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_SANDBOX_UTILS_H__
+#define SANDBOX_SRC_SANDBOX_UTILS_H__
+
+#include <windows.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+// Returns true if the current OS is Windows XP SP2 or later.
+bool IsXPSP2OrLater();
+
+void InitObjectAttribs(const std::wstring& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name);
+
+}; // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_UTILS_H__
diff --git a/chromium/sandbox/win/src/security_level.h b/chromium/sandbox/win/src/security_level.h
new file mode 100644
index 00000000000..f8e72b3ce00
--- /dev/null
+++ b/chromium/sandbox/win/src/security_level.h
@@ -0,0 +1,203 @@
+// Copyright (c) 2006-2008 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_SECURITY_LEVEL_H_
+#define SANDBOX_SRC_SECURITY_LEVEL_H_
+
+#include "base/basictypes.h"
+
+namespace sandbox {
+
+// List of all the integrity levels supported in the sandbox. This is used
+// only on Windows Vista. You can't set the integrity level of the process
+// in the sandbox to a level higher than yours.
+enum IntegrityLevel {
+ INTEGRITY_LEVEL_SYSTEM,
+ INTEGRITY_LEVEL_HIGH,
+ INTEGRITY_LEVEL_MEDIUM,
+ INTEGRITY_LEVEL_MEDIUM_LOW,
+ INTEGRITY_LEVEL_LOW,
+ INTEGRITY_LEVEL_BELOW_LOW,
+ INTEGRITY_LEVEL_UNTRUSTED,
+ INTEGRITY_LEVEL_LAST
+};
+
+// The Token level specifies a set of security profiles designed to
+// provide the bulk of the security of sandbox.
+//
+// TokenLevel |Restricting |Deny Only |Privileges|
+// |Sids |Sids | |
+// ----------------------------|--------------|----------------|----------|
+// USER_LOCKDOWN | Null Sid | All | None |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED | RESTRICTED | All | Traverse |
+// ----------------------------|--------------|----------------|----------|
+// USER_LIMITED | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | RESTRICTED | Everyone | |
+// | | Interactive | |
+// ----------------------------|--------------|----------------|----------|
+// USER_INTERACTIVE | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | RESTRICTED | Everyone | |
+// | Owner | Interactive | |
+// | | Local | |
+// | | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_NON_ADMIN | None | All except: | Traverse |
+// | | Users | |
+// | | Everyone | |
+// | | Interactive | |
+// | | Local | |
+// | | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED_SAME_ACCESS | All | None | All |
+// ----------------------------|--------------|----------------|----------|
+// USER_UNPROTECTED | None | None | All |
+// ----------------------------|--------------|----------------|----------|
+//
+// The above restrictions are actually a transformation that is applied to
+// the existing broker process token. The resulting token that will be
+// applied to the target process depends both on the token level selected
+// and on the broker token itself.
+//
+// The LOCKDOWN and RESTRICTED are designed to allow access to almost
+// nothing that has security associated with and they are the recommended
+// levels to run sandboxed code specially if there is a chance that the
+// broker is process might be started by a user that belongs to the Admins
+// or power users groups.
+enum TokenLevel {
+ USER_LOCKDOWN = 0,
+ USER_RESTRICTED,
+ USER_LIMITED,
+ USER_INTERACTIVE,
+ USER_NON_ADMIN,
+ USER_RESTRICTED_SAME_ACCESS,
+ USER_UNPROTECTED
+};
+
+// The Job level specifies a set of decreasing security profiles for the
+// Job object that the target process will be placed into.
+// This table summarizes the security associated with each level:
+//
+// JobLevel |General |Quota |
+// |restrictions |restrictions |
+// -----------------|---------------------------------- |--------------------|
+// JOB_NONE | No job is assigned to the | None |
+// | sandboxed process. | |
+// -----------------|---------------------------------- |--------------------|
+// JOB_UNPROTECTED | None | *Kill on Job close.|
+// -----------------|---------------------------------- |--------------------|
+// JOB_INTERACTIVE | *Forbid system-wide changes using | |
+// | SystemParametersInfo(). | *Kill on Job close.|
+// | *Forbid the creation/switch of | |
+// | Desktops. | |
+// | *Forbids calls to ExitWindows(). | |
+// -----------------|---------------------------------- |--------------------|
+// JOB_LIMITED_USER | Same as INTERACTIVE_USER plus: | *One active process|
+// | *Forbid changes to the display | limit. |
+// | settings. | *Kill on Job close.|
+// -----------------|---------------------------------- |--------------------|
+// JOB_RESTRICTED | Same as LIMITED_USER plus: | *One active process|
+// | * No read/write to the clipboard. | limit. |
+// | * No access to User Handles that | *Kill on Job close.|
+// | belong to other processes. | |
+// | * Forbid message broadcasts. | |
+// | * Forbid setting global hooks. | |
+// | * No access to the global atoms | |
+// | table. | |
+// -----------------|-----------------------------------|--------------------|
+// JOB_LOCKDOWN | Same as RESTRICTED | *One active process|
+// | | limit. |
+// | | *Kill on Job close.|
+// | | *Kill on unhandled |
+// | | exception. |
+// | | |
+// In the context of the above table, 'user handles' refers to the handles of
+// windows, bitmaps, menus, etc. Files, treads and registry handles are kernel
+// handles and are not affected by the job level settings.
+enum JobLevel {
+ JOB_LOCKDOWN = 0,
+ JOB_RESTRICTED,
+ JOB_LIMITED_USER,
+ JOB_INTERACTIVE,
+ JOB_UNPROTECTED,
+ JOB_NONE
+};
+
+// These flags correspond to various process-level mitigations (eg. ASLR and
+// DEP). Most are implemented via UpdateProcThreadAttribute() plus flags for
+// the PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY attribute argument; documented
+// here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686880
+// Some mitigations are implemented directly by the sandbox or emulated to
+// the greatest extent possible when not directly supported by the OS.
+// Flags that are unsupported for the target OS will be silently ignored.
+// Flags that are invalid for their application (pre or post startup) will
+// return SBOX_ERROR_BAD_PARAMS.
+typedef uint64 MitigationFlags;
+
+// Permanently enables DEP for the target process. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE.
+const MitigationFlags MITIGATION_DEP = 0x00000001;
+
+// Permanently Disables ATL thunk emulation when DEP is enabled. Valid
+// only when MITIGATION_DEP is passed. Corresponds to not passing
+// PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE.
+const MitigationFlags MITIGATION_DEP_NO_ATL_THUNK = 0x00000002;
+
+// Enables Structured exception handling override prevention. Must be
+// enabled prior to process start. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE.
+const MitigationFlags MITIGATION_SEHOP = 0x00000004;
+
+// Forces ASLR on all images in the child process. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON .
+const MitigationFlags MITIGATION_RELOCATE_IMAGE = 0x00000008;
+
+// Refuses to load DLLs that cannot support ASLR. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS.
+const MitigationFlags MITIGATION_RELOCATE_IMAGE_REQUIRED = 0x00000010;
+
+// Terminates the process on Windows heap corruption. Coresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON.
+const MitigationFlags MITIGATION_HEAP_TERMINATE = 0x00000020;
+
+// Sets a random lower bound as the minimum user address. Must be
+// enabled prior to process start. On 32-bit processes this is
+// emulated to a much smaller degree. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON.
+const MitigationFlags MITIGATION_BOTTOM_UP_ASLR = 0x00000040;
+
+// Increases the randomness range of bottom-up ASLR to up to 1TB. Must be
+// enabled prior to process start and with MITIGATION_BOTTOM_UP_ASLR.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON
+const MitigationFlags MITIGATION_HIGH_ENTROPY_ASLR = 0x00000080;
+
+// Immediately raises an exception on a bad handle reference. Must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON.
+const MitigationFlags MITIGATION_STRICT_HANDLE_CHECKS = 0x00000100;
+
+// Prevents the process from making Win32k calls. Must be enabled after
+// startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON.
+const MitigationFlags MITIGATION_WIN32K_DISABLE = 0x00000200;
+
+// Disables common DLL injection methods (e.g. window hooks and
+// App_InitDLLs). Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON.
+const MitigationFlags MITIGATION_EXTENSION_DLL_DISABLE = 0x00000400;
+
+// Sets the DLL search order to LOAD_LIBRARY_SEARCH_DEFAULT_DIRS. Additional
+// directories can be added via the Windows AddDllDirectory() function.
+// http://msdn.microsoft.com/en-us/library/windows/desktop/hh310515
+// Must be enabled after startup.
+const MitigationFlags MITIGATION_DLL_SEARCH_ORDER = 0x00000001ULL << 32;
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SECURITY_LEVEL_H_
diff --git a/chromium/sandbox/win/src/service_resolver.cc b/chromium/sandbox/win/src/service_resolver.cc
new file mode 100644
index 00000000000..bae698c4576
--- /dev/null
+++ b/chromium/sandbox/win/src/service_resolver.cc
@@ -0,0 +1,42 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include "base/logging.h"
+#include "base/win/pe_image.h"
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::ResolveInterceptor(
+ const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ // After all, we are using a locally mapped version of the exe, so the
+ // action is the same as for a target function.
+ return ResolveTarget(interceptor_module, interceptor_name,
+ const_cast<void**>(address));
+}
+
+// In this case all the work is done from the parent, so resolve is
+// just a simple GetProcAddress.
+NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ DCHECK(address);
+ if (NULL == module)
+ return STATUS_UNSUCCESSFUL;
+
+ base::win::PEImage module_image(module);
+ *address = module_image.GetProcAddress(function_name);
+
+ if (NULL == *address) {
+ NOTREACHED();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/service_resolver.h b/chromium/sandbox/win/src/service_resolver.h
new file mode 100644
index 00000000000..3eb9e083810
--- /dev/null
+++ b/chromium/sandbox/win/src/service_resolver.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SERVICE_RESOLVER_H__
+#define SANDBOX_SRC_SERVICE_RESOLVER_H__
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+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),
+ relaxed_(relaxed), relative_jump_(0) {}
+ virtual ~ServiceResolverThunk() {}
+
+ // Implementation of Resolver::Setup.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ // Implementation of Resolver::ResolveInterceptor.
+ virtual NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address);
+
+ // Implementation of Resolver::ResolveTarget.
+ virtual NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address);
+
+ // Implementation of Resolver::GetThunkSize.
+ virtual size_t GetThunkSize() const;
+
+ protected:
+ // The unit test will use this member to allow local patch on a buffer.
+ HMODULE ntdll_base_;
+
+ // 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
+ // by local_thunk (should be directly accessible from the parent).
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ // Performs the actual patch of target_.
+ // local_thunk must be already fully initialized, and the first part must
+ // contain the original code. The real type of this buffer is ServiceFullThunk
+ // (yes, private). remote_thunk (real type ServiceFullThunk), must be
+ // allocated on the child, and will contain the thunk data, after this call.
+ // Returns the apropriate status code.
+ virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk);
+
+ // Provides basically the same functionality as IsFunctionAService but it
+ // continues even if it does not recognize the function code. remote_thunk
+ // is the address of our memory on the child.
+ bool SaveOriginalFunction(void* local_thunk, void* remote_thunk);
+
+ // true if we are allowed to patch already-patched functions.
+ bool relaxed_;
+ ULONG relative_jump_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 (32 bit ntdll on 64 bit Vista).
+class Wow64ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ virtual ~Wow64ResolverThunk() {}
+
+ private:
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64ResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 for Windows 8.
+class Wow64W8ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64W8ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ virtual ~Wow64W8ResolverThunk() {}
+
+ private:
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk);
+};
+
+// 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:
+ // The service resolver needs a child process to write to.
+ Win8ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ virtual ~Win8ResolverThunk() {}
+
+ private:
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SERVICE_RESOLVER_H__
diff --git a/chromium/sandbox/win/src/service_resolver_32.cc b/chromium/sandbox/win/src/service_resolver_32.cc
new file mode 100644
index 00000000000..2e69dbc9e7e
--- /dev/null
+++ b/chromium/sandbox/win/src/service_resolver_32.cc
@@ -0,0 +1,420 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+#pragma pack(push, 1)
+
+const BYTE kMovEax = 0xB8;
+const BYTE kMovEdx = 0xBA;
+const USHORT kMovEdxEsp = 0xD48B;
+const USHORT kCallPtrEdx = 0x12FF;
+const USHORT kCallEdx = 0xD2FF;
+const BYTE kCallEip = 0xE8;
+const BYTE kRet = 0xC2;
+const BYTE kRet2 = 0xC3;
+const BYTE kNop = 0x90;
+const USHORT kJmpEdx = 0xE2FF;
+const USHORT kXorEcx = 0xC933;
+const ULONG kLeaEdx = 0x0424548D;
+const ULONG kCallFs1 = 0xC015FF64;
+const USHORT kCallFs2 = 0;
+const BYTE kCallFs3 = 0;
+const BYTE kAddEsp1 = 0x83;
+const USHORT kAddEsp2 = 0x4C4;
+const BYTE kJmp32 = 0xE9;
+const USHORT kSysenter = 0x340F;
+
+const int kMaxService = 1000;
+
+// Service code for 32 bit systems.
+// NOTE: on win2003 "call dword ptr [edx]" is "call edx".
+struct ServiceEntry {
+ // This struct contains roughly the following code:
+ // 00 mov eax,25h
+ // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
+ // 0a call dword ptr [edx]
+ // 0c ret 2Ch
+ // 0f nop
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE mov_edx; // = BA
+ ULONG stub;
+ USHORT call_ptr_edx; // = FF 12
+ BYTE ret; // = C2
+ USHORT num_params;
+ BYTE nop;
+};
+
+// Service code for 32 bit Windows 8.
+struct ServiceEntryW8 {
+ // This struct contains the following code:
+ // 00 b825000000 mov eax,25h
+ // 05 e803000000 call eip+3
+ // 0a c22c00 ret 2Ch
+ // 0d 8bd4 mov edx,esp
+ // 0f 0f34 sysenter
+ // 11 c3 ret
+ // 12 8bff mov edi,edi
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE call_eip; // = E8
+ ULONG call_offset;
+ BYTE ret_p; // = C2
+ USHORT num_params;
+ USHORT mov_edx_esp; // = BD D4
+ USHORT sysenter; // = 0F 34
+ BYTE ret; // = C3
+ USHORT nop;
+};
+
+// Service code for a 32 bit process running on a 64 bit os.
+struct Wow64Entry {
+ // This struct may contain one of two versions of code:
+ // 1. For XP, Vista and 2K3:
+ // 00 b825000000 mov eax, 25h
+ // 05 33c9 xor ecx, ecx
+ // 07 8d542404 lea edx, [esp + 4]
+ // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 12 c22c00 ret 2Ch
+ //
+ // 2. For Windows 7:
+ // 00 b825000000 mov eax, 25h
+ // 05 33c9 xor ecx, ecx
+ // 07 8d542404 lea edx, [esp + 4]
+ // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 12 83c404 add esp, 4
+ // 15 c22c00 ret 2Ch
+ //
+ // So we base the structure on the bigger one:
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ USHORT xor_ecx; // = 33 C9
+ ULONG lea_edx; // = 8D 54 24 04
+ ULONG call_fs1; // = 64 FF 15 C0
+ USHORT call_fs2; // = 00 00
+ BYTE call_fs3; // = 00
+ BYTE add_esp1; // = 83 or ret
+ USHORT add_esp2; // = C4 04 or num_params
+ BYTE ret; // = C2
+ USHORT num_params;
+};
+
+// Service code for a 32 bit process running on 64 bit Windows 8.
+struct Wow64EntryW8 {
+ // 00 b825000000 mov eax, 25h
+ // 05 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 0b c22c00 ret 2Ch
+ // 0f 90 nop
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ ULONG call_fs1; // = 64 FF 15 C0
+ USHORT call_fs2; // = 00 00
+ BYTE call_fs3; // = 00
+ BYTE ret; // = C2
+ USHORT num_params;
+ BYTE nop;
+};
+
+// Make sure that relaxed patching works as expected.
+const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
+COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len);
+COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len);
+COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len);
+
+struct ServiceFullThunk {
+ union {
+ ServiceEntry original;
+ ServiceEntryW8 original_w8;
+ Wow64Entry wow_64;
+ Wow64EntryW8 wow_64_w8;
+ };
+ int internal_thunk; // Dummy member to the beginning of the internal thunk.
+};
+
+#pragma pack(pop)
+
+}; // namespace
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point,
+ thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ relative_jump_ = 0;
+ size_t thunk_bytes = GetThunkSize();
+ scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
+ thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage)))
+ return STATUS_UNSUCCESSFUL;
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+size_t ServiceResolverThunk::GetThunkSize() const {
+ return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
+}
+
+bool ServiceResolverThunk::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 ||
+ kMovEdx != function_code.mov_edx ||
+ (kCallPtrEdx != function_code.call_ptr_edx &&
+ kCallEdx != function_code.call_ptr_edx) ||
+ kRet != function_code.ret)
+ return false;
+
+ // Find the system call pointer if we don't already have it.
+ if (kCallEdx != function_code.call_ptr_edx) {
+ DWORD ki_system_call;
+ if (!::ReadProcessMemory(process_,
+ bit_cast<const void*>(function_code.stub),
+ &ki_system_call, sizeof(ki_system_call), &read))
+ return false;
+
+ if (sizeof(ki_system_call) != read)
+ return false;
+
+ HMODULE module_1, module_2;
+ // last check, call_stub should point to a KiXXSystemCall function on ntdll
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ bit_cast<const wchar_t*>(ki_system_call), &module_1))
+ return false;
+
+ if (NULL != ntdll_base_) {
+ // This path is only taken when running the unit tests. We want to be
+ // able to patch a buffer in memory, so target_ is not inside ntdll.
+ module_2 = ntdll_base_;
+ } else {
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<const wchar_t*>(target_),
+ &module_2))
+ return false;
+ }
+
+ if (module_1 != module_2)
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ ServiceEntry intercepted_code;
+ size_t bytes_to_write = sizeof(intercepted_code);
+ ServiceFullThunk *full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
+ local_thunk);
+ ServiceFullThunk *full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
+ remote_thunk);
+
+ // patch the original code
+ memcpy(&intercepted_code, &full_local_thunk->original,
+ sizeof(intercepted_code));
+ intercepted_code.mov_eax = kMovEax;
+ intercepted_code.service_id = full_local_thunk->original.service_id;
+ intercepted_code.mov_edx = kMovEdx;
+ intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
+ intercepted_code.call_ptr_edx = kJmpEdx;
+ bytes_to_write = kMinServiceSize;
+
+ if (relative_jump_) {
+ intercepted_code.mov_eax = kJmp32;
+ intercepted_code.service_id = relative_jump_;
+ bytes_to_write = offsetof(ServiceEntry, mov_edx);
+ }
+
+ // setup the thunk
+ SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
+ remote_thunk, interceptor_);
+
+ size_t thunk_size = GetThunkSize();
+
+ // copy the local thunk buffer to the child
+ SIZE_T written;
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
+ thunk_size, &written))
+ return STATUS_UNSUCCESSFUL;
+
+ if (thunk_size != written)
+ return STATUS_UNSUCCESSFUL;
+
+ // and now change the function to intercept, on the child
+ if (NULL != ntdll_base_) {
+ // running a unit test
+ if (!::WriteProcessMemory(process_, target_, &intercepted_code,
+ bytes_to_write, &written))
+ return STATUS_UNSUCCESSFUL;
+ } else {
+ if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
+ bytes_to_write))
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
+ void* remote_thunk) {
+ 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 (kJmp32 == function_code.mov_eax) {
+ // Plain old entry point patch. The relative jump address follows it.
+ ULONG relative = function_code.service_id;
+
+ // First, fix our copy of their patch.
+ relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
+
+ function_code.service_id = relative;
+
+ // And now, remember how to re-patch it.
+ ServiceFullThunk *full_thunk =
+ reinterpret_cast<ServiceFullThunk*>(remote_thunk);
+
+ const ULONG kJmp32Size = 5;
+
+ relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
+ bit_cast<ULONG>(target_) - kJmp32Size;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64Entry 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 || kXorEcx != function_code.xor_ecx ||
+ kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
+ kCallFs2 != function_code.call_fs2 || kCallFs3 != function_code.call_fs3)
+ return false;
+
+ if ((kAddEsp1 == function_code.add_esp1 &&
+ kAddEsp2 == function_code.add_esp2 &&
+ kRet == function_code.ret) || kRet == function_code.add_esp1) {
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ return true;
+ }
+
+ return false;
+}
+
+bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64EntryW8 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 || kCallFs1 != function_code.call_fs1 ||
+ kCallFs2 != function_code.call_fs2 ||
+ kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ 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;
+ 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 || kCallEip != function_code.call_eip ||
+ function_code.call_offset != 3 || kRet != function_code.ret_p ||
+ kMovEdxEsp != function_code.mov_edx_esp ||
+ kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/service_resolver_64.cc b/chromium/sandbox/win/src/service_resolver_64.cc
new file mode 100644
index 00000000000..32de53a43f4
--- /dev/null
+++ b/chromium/sandbox/win/src/service_resolver_64.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+#pragma pack(push, 1)
+
+const ULONG kMmovR10EcxMovEax = 0xB8D18B4C;
+const USHORT kSyscall = 0x050F;
+const BYTE kRetNp = 0xC3;
+const ULONG64 kMov1 = 0x54894808244C8948;
+const ULONG64 kMov2 = 0x4C182444894C1024;
+const ULONG kMov3 = 0x20244C89;
+
+// Service code for 64 bit systems.
+struct ServiceEntry {
+ // This struct contains roughly the following code:
+ // 00 mov r10,rcx
+ // 03 mov eax,52h
+ // 08 syscall
+ // 0a ret
+ // 0b xchg ax,ax
+ // 0e xchg ax,ax
+
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ BYTE pad; // = 66
+ USHORT xchg_ax_ax1; // = 66 90
+ USHORT xchg_ax_ax2; // = 66 90
+};
+
+// Service code for 64 bit Windows 8.
+struct ServiceEntryW8 {
+ // This struct contains the following code:
+ // 00 48894c2408 mov [rsp+8], rcx
+ // 05 4889542410 mov [rsp+10], rdx
+ // 0a 4c89442418 mov [rsp+18], r8
+ // 0f 4c894c2420 mov [rsp+20], r9
+ // 14 4c8bd1 mov r10,rcx
+ // 17 b825000000 mov eax,25h
+ // 1c 0f05 syscall
+ // 1e c3 ret
+ // 1f 90 nop
+
+ ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54
+ ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C
+ ULONG mov_3; // = 89 4C 24 20
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C2
+ BYTE nop; // = 90
+};
+
+// We don't have an internal thunk for x64.
+struct ServiceFullThunk {
+ union {
+ ServiceEntry original;
+ ServiceEntryW8 original_w8;
+ };
+};
+
+#pragma pack(pop)
+
+bool IsService(const void* source) {
+ const ServiceEntry* service =
+ reinterpret_cast<const ServiceEntry*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kSyscall == service->syscall && kRetNp == service->ret);
+}
+
+}; // namespace
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point,
+ thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
+ thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_UNSUCCESSFUL;
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+size_t ServiceResolverThunk::GetThunkSize() const {
+ return sizeof(ServiceFullThunk);
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceFullThunk 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 (!IsService(&function_code)) {
+ // See if it's the Win8 signature.
+ ServiceEntryW8* w8_service = &function_code.original_w8;
+ if (!IsService(&w8_service->mov_r10_rcx_mov_eax) ||
+ w8_service->mov_1 != kMov1 || w8_service->mov_1 != kMov1 ||
+ w8_service->mov_1 != kMov1) {
+ return false;
+ }
+ }
+
+ // Save the verified code.
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ ServiceFullThunk* full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
+ local_thunk);
+ ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
+ remote_thunk);
+
+ // Patch the original code.
+ ServiceEntry local_service;
+ DCHECK_GE(GetInternalThunkSize(), sizeof(local_service));
+ if (!SetInternalThunk(&local_service, sizeof(local_service), NULL,
+ interceptor_))
+ return STATUS_UNSUCCESSFUL;
+
+ // Copy the local thunk buffer to the child.
+ SIZE_T actual;
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
+ sizeof(ServiceFullThunk), &actual))
+ return STATUS_UNSUCCESSFUL;
+
+ if (sizeof(ServiceFullThunk) != actual)
+ return STATUS_UNSUCCESSFUL;
+
+ // And now change the function to intercept, on the child.
+ if (NULL != ntdll_base_) {
+ // Running a unit test.
+ if (!::WriteProcessMemory(process_, target_, &local_service,
+ sizeof(local_service), &actual))
+ return STATUS_UNSUCCESSFUL;
+ } else {
+ if (!WriteProtectedChildMemory(process_, target_, &local_service,
+ sizeof(local_service)))
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ NOTREACHED();
+ return false;
+}
+
+bool Win2kResolverThunk::IsFunctionAService(void* local_thunk) const {
+ NOTREACHED();
+ return false;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/service_resolver_unittest.cc b/chromium/sandbox/win/src/service_resolver_unittest.cc
new file mode 100644
index 00000000000..59105cc49de
--- /dev/null
+++ b/chromium/sandbox/win/src/service_resolver_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for ServiceResolverThunk.
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/resolver.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/service_resolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+template<typename T>
+class ResolverThunkTest : public T {
+ public:
+ // The service resolver needs a child process to write to.
+ explicit ResolverThunkTest(bool relaxed)
+ : T(::GetCurrentProcess(), relaxed) {}
+
+ // Sets the interception target to the desired address.
+ void set_target(void* target) {
+ fake_target_ = target;
+ }
+
+ protected:
+ // Overrides Resolver::Init
+ virtual NTSTATUS Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ ret = ResolverThunk::Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point,
+ thunk_storage, storage_bytes);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+
+ target_ = fake_target_;
+ ntdll_base_ = ::GetModuleHandle(L"ntdll.dll");
+ return ret;
+ };
+
+ private:
+ // Holds the address of the fake target.
+ void* fake_target_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest);
+};
+
+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;
+#endif
+
+const BYTE kJump32 = 0xE9;
+
+void CheckJump(void* source, void* target) {
+#pragma pack(push)
+#pragma pack(1)
+ struct Code {
+ BYTE jump;
+ ULONG delta;
+ };
+#pragma pack(pop)
+
+#if defined(_WIN64)
+ FAIL() << "Running 32-bit codepath";
+#else
+ Code* patched = reinterpret_cast<Code*>(source);
+ EXPECT_EQ(kJump32, patched->jump);
+
+ ULONG source_addr = bit_cast<ULONG>(source);
+ ULONG target_addr = bit_cast<ULONG>(target);
+ EXPECT_EQ(target_addr + 19 - source_addr, patched->delta);
+#endif
+}
+
+NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed,
+ sandbox::ServiceResolverThunk* resolver) {
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ EXPECT_TRUE(NULL != ntdll_base);
+
+ void* target = ::GetProcAddress(ntdll_base, function);
+ EXPECT_TRUE(NULL != target);
+ if (NULL == target)
+ return STATUS_UNSUCCESSFUL;
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+
+ static_cast<WinXpResolverTest*>(resolver)->set_target(service);
+
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ scoped_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL,
+ function_entry, thunk.get(), thunk_size,
+ &used);
+ if (NT_SUCCESS(ret)) {
+ EXPECT_EQ(thunk_size, used);
+ EXPECT_NE(0, memcmp(service, target, sizeof(service)));
+ EXPECT_NE(kJump32, service[0]);
+
+ if (relaxed) {
+ // It's already patched, let's patch again, and simulate a direct patch.
+ service[0] = kJump32;
+ ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry,
+ thunk.get(), thunk_size, &used);
+ CheckJump(service, thunk.get());
+ }
+ }
+
+ return ret;
+}
+
+sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) {
+#if defined(_WIN64)
+ return new WinXpResolverTest(relaxed);
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::VERSION_WIN8)
+ return new Wow64W8ResolverTest(relaxed);
+ return new Wow64ResolverTest(relaxed);
+ }
+
+ if (!sandbox::IsXPSP2OrLater())
+ return new Win2kResolverTest(relaxed);
+
+ if (os_info->version() >= base::win::VERSION_WIN8)
+ return new Win8ResolverTest(relaxed);
+
+ return new WinXpResolverTest(relaxed);
+#endif
+}
+
+NTSTATUS PatchNtdll(const char* function, bool relaxed) {
+ sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed);
+
+ NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver);
+ delete resolver;
+ return ret;
+}
+
+TEST(ServiceResolverTest, PatchesServices) {
+ NTSTATUS ret = PatchNtdll("NtClose", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
+ ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
+ ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
+ ::GetLastError();
+}
+
+TEST(ServiceResolverTest, FailsIfNotService) {
+#if !defined(_WIN64)
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false));
+#endif
+
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false));
+}
+
+TEST(ServiceResolverTest, PatchesPatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ NTSTATUS ret = PatchNtdll("NtClose", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
+ ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
+ ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
+ ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, MultiplePatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
+ NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateFile", true, resolver);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " <<
+ ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " <<
+ ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " <<
+ ::GetLastError();
+ delete resolver;
+#endif
+}
+
+} // namespace
diff --git a/chromium/sandbox/win/src/shared_handles.cc b/chromium/sandbox/win/src/shared_handles.cc
new file mode 100644
index 00000000000..423b67bab30
--- /dev/null
+++ b/chromium/sandbox/win/src/shared_handles.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 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/shared_handles.h"
+
+namespace sandbox {
+
+// Note once again the the assumption here is that the shared memory is
+// initialized with zeros in the process that calls SetHandle and that
+// the process that calls GetHandle 'sees' this memory.
+
+SharedHandles::SharedHandles() {
+ shared_.items = NULL;
+ shared_.max_items = 0;
+}
+
+bool SharedHandles::Init(void* raw_mem, size_t size_bytes) {
+ if (size_bytes < sizeof(shared_.items[0])) {
+ // The shared memory is too small!
+ return false;
+ }
+ shared_.items = static_cast<SharedItem*>(raw_mem);
+ shared_.max_items = size_bytes / sizeof(shared_.items[0]);
+ return true;
+}
+
+// Note that an empty slot is marked with a tag == 0 that is why is
+// not a valid imput tag
+bool SharedHandles::SetHandle(uint32 tag, HANDLE handle) {
+ if (0 == tag) {
+ // Invalid tag
+ return false;
+ }
+ // Find empty slot and put the tag and the handle there
+ SharedItem* empty_slot = FindByTag(0);
+ if (NULL == empty_slot) {
+ return false;
+ }
+ empty_slot->tag = tag;
+ empty_slot->item = handle;
+ return true;
+}
+
+bool SharedHandles::GetHandle(uint32 tag, HANDLE* handle) {
+ if (0 == tag) {
+ // Invalid tag
+ return false;
+ }
+ SharedItem* found = FindByTag(tag);
+ if (NULL == found) {
+ return false;
+ }
+ *handle = found->item;
+ return true;
+}
+
+SharedHandles::SharedItem* SharedHandles::FindByTag(uint32 tag) {
+ for (size_t ix = 0; ix != shared_.max_items; ++ix) {
+ if (tag == shared_.items[ix].tag) {
+ return &shared_.items[ix];
+ }
+ }
+ return NULL;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/shared_handles.h b/chromium/sandbox/win/src/shared_handles.h
new file mode 100644
index 00000000000..2c76bfb2802
--- /dev/null
+++ b/chromium/sandbox/win/src/shared_handles.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2010 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_SHARED_HANDLES_H__
+#define SANDBOX_SRC_SHARED_HANDLES_H__
+
+#include "base/basictypes.h"
+
+#ifndef HANDLE
+// We can provide our own windows compatilble handle definition, but
+// in general we want to rely on the client of this api to include
+// the proper windows headers. Note that we don't want to bring the
+// whole <windows.h> into scope if we don't have to.
+typedef void* HANDLE;
+#endif
+
+namespace sandbox {
+
+// SharedHandles is a simple class to stash and find windows object handles
+// given a raw block of memory which is shared between two processes.
+// It addresses the need to communicate a handle value between two windows
+// processes given that they are already sharing some memory.
+//
+// This class is not exposed directly to users of the sanbox API, instead
+// we expose the wrapper methods TargetProcess::TransferHandle( ) and
+// TargetServices::GetTransferHandle()
+//
+// Use it for a small number of items, since internaly uses linear seach
+//
+// The use is very simple. Given a shared memory between proces A and B:
+// process A:
+// HANDLE handle = SomeFunction(..);
+// SharedHandles shared_handes;
+// shared_handles.Init(memory)
+// shared_handles.SetHandle(3, handle);
+//
+// process B:
+// SharedHandles shared_handes;
+// shared_handles.Init(memory)
+// HANDLE handle = shared_handles.GetHandle(3);
+//
+// Note that '3' in this example is a unique id, that must be agreed before
+// transfer
+//
+// Note2: While this class can be used in a single process, there are
+// better alternatives such as STL
+//
+// Note3: Under windows a kernel object handle in one process does not
+// make sense for another process unless there is a DuplicateHandle( )
+// call involved which this class DOES NOT do that for you.
+//
+// Note4: Under windows, shared memory when created is initialized to
+// zeros always. If you are not using shared memory it is your responsability
+// to zero it for the setter process and to copy it to the getter process.
+class SharedHandles {
+ public:
+ SharedHandles();
+
+ // Initializes the shared memory for use.
+ // Pass the shared memory base and size. It will internally compute
+ // how many handles can it store. If initialization fails the return value
+ // is false.
+ bool Init(void* raw_mem, size_t size_bytes);
+
+ // Sets a handle in the shared memory for transfer.
+ // Parameters:
+ // tag : an integer, different from zero that uniquely identfies the
+ // handle to transfer.
+ // handle: the handle value associated with 'tag' to tranfer
+ // Returns false if there is not enough space in the shared memory for
+ // this handle.
+ bool SetHandle(uint32 tag, HANDLE handle);
+
+ // Gets a handle previously stored by SetHandle.
+ // Parameters:
+ // tag: an integer different from zero that uniquely identfies the handle
+ // to retrieve.
+ // *handle: output handle value if the call was succesful.
+ // If a handle with the provided tag is not found the return value is false.
+ // If the tag is found the return value is true.
+ bool GetHandle(uint32 tag, HANDLE* handle);
+
+ private:
+ // A single item is the tuple handle/tag
+ struct SharedItem {
+ uint32 tag;
+ void* item;
+ };
+
+ // SharedMem is used to layout the memory as an array of SharedItems
+ struct SharedMem {
+ size_t max_items;
+ SharedItem* items;
+ };
+
+ // Finds an Item tuple provided the handle tag.
+ // Uses linear search because we expect the number of handles to be
+ // small (say less than ~100).
+ SharedItem* FindByTag(uint32 tag);
+
+ SharedMem shared_;
+ DISALLOW_COPY_AND_ASSIGN(SharedHandles);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHARED_HANDLES_H__
diff --git a/chromium/sandbox/win/src/sharedmem_ipc_client.cc b/chromium/sandbox/win/src/sharedmem_ipc_client.cc
new file mode 100644
index 00000000000..a9eb01f36b2
--- /dev/null
+++ b/chromium/sandbox/win/src/sharedmem_ipc_client.cc
@@ -0,0 +1,152 @@
+// Copyright (c) 2006-2008 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 <string.h>
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "base/logging.h"
+
+namespace sandbox {
+
+// Get the base of the data buffer of the channel; this is where the input
+// parameters get serialized. Since they get serialized directly into the
+// channel we avoid one copy.
+void* SharedMemIPCClient::GetBuffer() {
+ bool failure = false;
+ size_t ix = LockFreeChannel(&failure);
+ if (failure) {
+ return NULL;
+ }
+ return reinterpret_cast<char*>(control_) +
+ control_->channels[ix].channel_base;
+}
+
+// If we need to cancel an IPC before issuing DoCall
+// our client should call FreeBuffer with the same pointer
+// returned by GetBuffer.
+void SharedMemIPCClient::FreeBuffer(void* buffer) {
+ size_t num = ChannelIndexFromBuffer(buffer);
+ ChannelControl* channel = control_->channels;
+ LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
+ DCHECK(kFreeChannel != result);
+ result;
+}
+
+// The constructor simply casts the shared memory to the internal
+// structures. This is a cheap step that is why this IPC object can
+// and should be constructed per call.
+SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
+ : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
+ first_base_ = reinterpret_cast<char*>(shared_mem) +
+ control_->channels[0].channel_base;
+ // There must be at least one channel.
+ DCHECK(0 != control_->channels_count);
+}
+
+// Do the IPC. At this point the channel should have already been
+// filled with the serialized input parameters.
+// We follow the pattern explained in the header file.
+ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
+ CrossCallReturn* answer) {
+ if (!control_->server_alive)
+ return SBOX_ERROR_CHANNEL_ERROR;
+
+ size_t num = ChannelIndexFromBuffer(params->GetBuffer());
+ ChannelControl* channel = control_->channels;
+ // Note that the IPC tag goes outside the buffer as well inside
+ // the buffer. This should enable the server to prioritize based on
+ // IPC tags without having to de-serialize the entire message.
+ channel[num].ipc_tag = params->GetTag();
+
+ // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
+ // we check if the server_alive mutex was abandoned which will indicate
+ // that the server has died.
+
+ // While the atomic signaling and waiting is not a requirement, it
+ // is nice because we save a trip to kernel.
+ DWORD wait = ::SignalObjectAndWait(channel[num].ping_event,
+ channel[num].pong_event,
+ kIPCWaitTimeOut1, FALSE);
+ if (WAIT_TIMEOUT == wait) {
+ // The server is taking too long. Enter a loop were we check if the
+ // server_alive mutex has been abandoned which would signal a server crash
+ // or else we keep waiting for a response.
+ while (true) {
+ wait = ::WaitForSingleObject(control_->server_alive, 0);
+ if (WAIT_TIMEOUT == wait) {
+ // Server seems still alive. We already signaled so here we just wait.
+ wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1);
+ if (WAIT_OBJECT_0 == wait) {
+ // The server took a long time but responded.
+ break;
+ } else if (WAIT_TIMEOUT == wait) {
+ continue;
+ } else {
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+ } else {
+ // The server has crashed and windows has signaled the mutex as
+ // abandoned.
+ ::InterlockedExchange(&channel[num].state, kAbandonnedChannel);
+ control_->server_alive = 0;
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+ }
+ } else if (WAIT_OBJECT_0 != wait) {
+ // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+
+ // The server has returned an answer, copy it and free the channel.
+ memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
+
+ // Return the IPC state It can indicate that while the IPC has
+ // completed some error in the Broker has caused to not return valid
+ // results.
+ return answer->call_outcome;
+}
+
+// Locking a channel is a simple as looping over all the channels
+// looking for one that is has state = kFreeChannel and atomically
+// swapping it to kBusyChannel.
+// If there is no free channel, then we must back off so some other
+// thread makes progress and frees a channel. To back off we sleep.
+size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
+ if (0 == control_->channels_count) {
+ *severe_failure = true;
+ return 0;
+ }
+ ChannelControl* channel = control_->channels;
+ do {
+ for (size_t ix = 0; ix != control_->channels_count; ++ix) {
+ if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state,
+ kBusyChannel,
+ kFreeChannel)) {
+ *severe_failure = false;
+ return ix;
+ }
+ }
+ // We did not find any available channel, maybe the server is dead.
+ DWORD wait = ::WaitForSingleObject(control_->server_alive,
+ kIPCWaitTimeOut2);
+ if (WAIT_TIMEOUT != wait) {
+ // The server is dead and we outlive it enough to get in trouble.
+ *severe_failure = true;
+ return 0;
+ }
+ }
+ while (true);
+}
+
+// Find out which channel we are from the pointer returned by GetBuffer.
+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);
+ return (num);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sharedmem_ipc_client.h b/chromium/sandbox/win/src/sharedmem_ipc_client.h
new file mode 100644
index 00000000000..a0c5f2f78de
--- /dev/null
+++ b/chromium/sandbox/win/src/sharedmem_ipc_client.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2006-2008 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_SHAREDMEM_IPC_CLIENT_H__
+#define SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+// IPC transport implementation that uses shared memory.
+// This is the client side
+//
+// The shared memory is divided on blocks called channels, and potentially
+// it can perform as many concurrent IPC calls as channels. The IPC over
+// each channel is strictly synchronous for the client.
+//
+// Each channel as a channel control section associated with. Each control
+// section has two kernel events (known as ping and pong) and a integer
+// variable that maintains a state
+//
+// this is the state diagram of a channel:
+//
+// locked in service
+// kFreeChannel---------->BusyChannel-------------->kAckChannel
+// ^ |
+// |_________________________________________________|
+// answer ready
+//
+// The protocol is as follows:
+// 1) client finds a free channel: state = kFreeChannel
+// 2) does an atomic compare-and-swap, now state = BusyChannel
+// 3) client writes the data into the channel buffer
+// 4) client signals the ping event and waits (blocks) on the pong event
+// 5) eventually the server signals the pong event
+// 6) the client awakes and reads the answer from the same channel
+// 7) the client updates its InOut parameters with the new data from the
+// shared memory section.
+// 8) the client atomically sets the state = kFreeChannel
+//
+// In the shared memory the layout is as follows:
+//
+// [ channel count ]
+// [ channel control 0]
+// [ channel control 1]
+// [ channel control N]
+// [ channel buffer 0 ] 1024 bytes
+// [ channel buffer 1 ] 1024 bytes
+// [ channel buffer N ] 1024 bytes
+//
+// By default each channel buffer is 1024 bytes
+namespace sandbox {
+
+// the possible channel states as described above
+enum ChannelState {
+ // channel is free
+ kFreeChannel = 1,
+ // IPC in progress client side
+ kBusyChannel,
+ // IPC in progress server side
+ kAckChannel,
+ // not used right now
+ kReadyChannel,
+ // IPC abandoned by client side
+ kAbandonnedChannel
+};
+
+// The next two constants control the time outs for the IPC.
+const DWORD kIPCWaitTimeOut1 = 1000; // Milliseconds.
+const DWORD kIPCWaitTimeOut2 = 50; // Milliseconds.
+
+// the channel control structure
+struct ChannelControl {
+ // points to be beginning of the channel buffer, where data goes
+ size_t channel_base;
+ // maintains the state from the ChannelState enumeration
+ volatile LONG state;
+ // the ping event is signaled by the client when the IPC data is ready on
+ // the buffer
+ HANDLE ping_event;
+ // the client waits on the pong event for the IPC answer back
+ HANDLE pong_event;
+ // the IPC unique identifier
+ uint32 ipc_tag;
+};
+
+struct IPCControl {
+ // total number of channels available, some might be busy at a given time
+ size_t channels_count;
+ // handle to a shared mutex to detect when the server is dead
+ HANDLE server_alive;
+ // array of channel control structures
+ ChannelControl channels[1];
+};
+
+// the actual shared memory IPC implementation class. This object is designed
+// to be lightweight so it can be constructed on-site (at the calling place)
+// wherever an IPC call is needed.
+class SharedMemIPCClient {
+ public:
+ // Creates the IPC client.
+ // as parameter it takes the base address of the shared memory
+ explicit SharedMemIPCClient(void* shared_mem);
+
+ // locks a free channel and returns the channel buffer memory base. This call
+ // blocks until there is a free channel
+ void* GetBuffer();
+
+ // releases the lock on the channel, for other to use. call this if you have
+ // called GetBuffer and you want to abort but have not called yet DoCall()
+ void FreeBuffer(void* buffer);
+
+ // Performs the actual IPC call.
+ // params: The blob of packed input parameters.
+ // answer: upon IPC completion, it contains the server answer to the IPC.
+ // If the return value is not SBOX_ERROR_CHANNEL_ERROR, the caller has to free
+ // the channel.
+ // returns ALL_OK if the IPC mechanism successfully delivered. You still need
+ // to check on the answer structure to see the actual IPC result.
+ ResultCode DoCall(CrossCallParams* params, CrossCallReturn* answer);
+
+ private:
+ // Returns the index of the first free channel. It sets 'severe_failure'
+ // to true if there is an unrecoverable error that does not allow to
+ // find a channel.
+ size_t LockFreeChannel(bool* severe_failure);
+ // Return the channel index given the address of the buffer.
+ size_t ChannelIndexFromBuffer(const void* buffer);
+ IPCControl* control_;
+ // point to the first channel base
+ char* first_base_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
diff --git a/chromium/sandbox/win/src/sharedmem_ipc_server.cc b/chromium/sandbox/win/src/sharedmem_ipc_server.cc
new file mode 100644
index 00000000000..e2a30c72366
--- /dev/null
+++ b/chromium/sandbox/win/src/sharedmem_ipc_server.cc
@@ -0,0 +1,410 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process,
+ DWORD target_process_id,
+ HANDLE target_job,
+ ThreadProvider* thread_provider,
+ Dispatcher* dispatcher)
+ : client_control_(NULL),
+ thread_provider_(thread_provider),
+ target_process_(target_process),
+ target_process_id_(target_process_id),
+ target_job_object_(target_job),
+ call_dispatcher_(dispatcher) {
+}
+
+SharedMemIPCServer::~SharedMemIPCServer() {
+ // Free the wait handles associated with the thread pool.
+ if (!thread_provider_->UnRegisterWaits(this)) {
+ // Better to leak than to crash.
+ return;
+ }
+ // Free the IPC signal events.
+ ServerContexts::iterator it;
+ for (it = server_contexts_.begin(); it != server_contexts_.end(); ++it) {
+ ServerControl* context = (*it);
+ ::CloseHandle(context->ping_event);
+ ::CloseHandle(context->pong_event);
+ delete context;
+ }
+}
+
+bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size,
+ uint32 channel_size) {
+ // The shared memory needs to be at least as big as a channel.
+ if (shared_size < channel_size) {
+ return false;
+ }
+ // The channel size should be aligned.
+ if (0 != (channel_size % 32)) {
+ return false;
+ }
+
+ // Calculate how many channels we can fit in the shared memory.
+ shared_size -= offsetof(IPCControl, channels);
+ size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size);
+
+ // If we cannot fit even one channel we bail out.
+ if (0 == channel_count) {
+ return false;
+ }
+ // Calculate the start of the first channel.
+ size_t base_start = (sizeof(ChannelControl)* channel_count) +
+ offsetof(IPCControl, channels);
+
+ client_control_ = reinterpret_cast<IPCControl*>(shared_mem);
+ client_control_->channels_count = 0;
+
+ // This is the initialization that we do per-channel. Basically:
+ // 1) make two events (ping & pong)
+ // 2) create handles to the events for the client and the server.
+ // 3) initialize the channel (client_context) with the state.
+ // 4) initialize the server side of the channel (service_context).
+ // 5) call the thread provider RegisterWait to register the ping events.
+ for (size_t ix = 0; ix != channel_count; ++ix) {
+ ChannelControl* client_context = &client_control_->channels[ix];
+ ServerControl* service_context = new ServerControl;
+ server_contexts_.push_back(service_context);
+
+ if (!MakeEvents(&service_context->ping_event,
+ &service_context->pong_event,
+ &client_context->ping_event,
+ &client_context->pong_event)) {
+ return false;
+ }
+
+ client_context->channel_base = base_start;
+ client_context->state = kFreeChannel;
+
+ // Note that some of these values are available as members of this
+ // object but we put them again into the service_context because we
+ // will be called on a static method (ThreadPingEventReady)
+ service_context->shared_base = reinterpret_cast<char*>(shared_mem);
+ service_context->channel_size = channel_size;
+ service_context->channel = client_context;
+ service_context->channel_buffer = service_context->shared_base +
+ client_context->channel_base;
+ service_context->dispatcher = call_dispatcher_;
+ service_context->target_info.process = target_process_;
+ service_context->target_info.process_id = target_process_id_;
+ service_context->target_info.job_object = target_job_object_;
+ // Advance to the next channel.
+ base_start += channel_size;
+ // Register the ping event with the threadpool.
+ 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),
+ target_process_, &client_control_->server_alive,
+ SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, 0)) {
+ return false;
+ }
+ // This last setting indicates to the client all is setup.
+ client_control_->channels_count = channel_count;
+ return true;
+}
+
+// Releases memory allocated for IPC arguments, if needed.
+void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) {
+ for (size_t i = 0; i < kMaxIpcParams; i++) {
+ switch (ipc_params->args[i]) {
+ case WCHAR_TYPE: {
+ delete reinterpret_cast<std::wstring*>(args[i]);
+ args[i] = NULL;
+ break;
+ }
+ case INOUTPTR_TYPE: {
+ delete reinterpret_cast<CountedBuffer*>(args[i]);
+ args[i] = NULL;
+ break;
+ }
+ default: break;
+ }
+ }
+}
+
+// Fills up the list of arguments (args and ipc_params) for an IPC call.
+bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params,
+ void* args[kMaxIpcParams]) {
+ if (kMaxIpcParams < params->GetParamsCount())
+ return false;
+
+ for (uint32 i = 0; i < params->GetParamsCount(); i++) {
+ uint32 size;
+ ArgType type;
+ args[i] = params->GetRawParameter(i, &size, &type);
+ if (args[i]) {
+ ipc_params->args[i] = type;
+ switch (type) {
+ case WCHAR_TYPE: {
+ scoped_ptr<std::wstring> data(new std::wstring);
+ if (!params->GetParameterStr(i, data.get())) {
+ args[i] = 0;
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data.release();
+ break;
+ }
+ case ULONG_TYPE: {
+ uint32 data;
+ if (!params->GetParameter32(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ IPCInt ipc_int(data);
+ args[i] = ipc_int.AsVoidPtr();
+ break;
+ }
+ case VOIDPTR_TYPE : {
+ void* data;
+ if (!params->GetParameterVoidPtr(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data;
+ break;
+ }
+ case INOUTPTR_TYPE: {
+ if (!args[i]) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ CountedBuffer* buffer = new CountedBuffer(args[i] , size);
+ args[i] = buffer;
+ break;
+ }
+ default: break;
+ }
+ }
+ }
+ return true;
+}
+
+bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context,
+ void* ipc_buffer,
+ CrossCallReturn* call_result) {
+ // Set the default error code;
+ SetCallError(SBOX_ERROR_INVALID_IPC, call_result);
+ uint32 output_size = 0;
+ // Parse, verify and copy the message. The handler operates on a copy
+ // of the message so the client cannot play dirty tricks by changing the
+ // data in the channel while the IPC is being processed.
+ scoped_ptr<CrossCallParamsEx> params(
+ CrossCallParamsEx::CreateFromBuffer(ipc_buffer,
+ service_context->channel_size,
+ &output_size));
+ if (!params.get())
+ return false;
+
+ uint32 tag = params->GetTag();
+ COMPILE_ASSERT(0 == INVALID_TYPE, Incorrect_type_enum);
+ IPCParams ipc_params = {0};
+ ipc_params.ipc_tag = tag;
+
+ void* args[kMaxIpcParams];
+ if (!GetArgs(params.get(), &ipc_params, args))
+ return false;
+
+ IPCInfo ipc_info = {0};
+ ipc_info.ipc_tag = tag;
+ ipc_info.client_info = &service_context->target_info;
+ Dispatcher* dispatcher = service_context->dispatcher;
+ DCHECK(dispatcher);
+ bool error = true;
+ Dispatcher* handler = NULL;
+
+ Dispatcher::CallbackGeneric callback_generic;
+ handler = dispatcher->OnMessageReady(&ipc_params, &callback_generic);
+ if (handler) {
+ switch (params->GetParamsCount()) {
+ case 0: {
+ // Ask the IPC dispatcher if she can service this IPC.
+ Dispatcher::Callback0 callback =
+ reinterpret_cast<Dispatcher::Callback0>(callback_generic);
+ if (!(handler->*callback)(&ipc_info))
+ break;
+ error = false;
+ break;
+ }
+ case 1: {
+ Dispatcher::Callback1 callback =
+ reinterpret_cast<Dispatcher::Callback1>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0]))
+ break;
+ error = false;
+ break;
+ }
+ case 2: {
+ Dispatcher::Callback2 callback =
+ reinterpret_cast<Dispatcher::Callback2>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1]))
+ break;
+ error = false;
+ break;
+ }
+ case 3: {
+ Dispatcher::Callback3 callback =
+ reinterpret_cast<Dispatcher::Callback3>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2]))
+ break;
+ error = false;
+ break;
+ }
+ case 4: {
+ Dispatcher::Callback4 callback =
+ reinterpret_cast<Dispatcher::Callback4>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2],
+ args[3]))
+ break;
+ error = false;
+ break;
+ }
+ case 5: {
+ Dispatcher::Callback5 callback =
+ reinterpret_cast<Dispatcher::Callback5>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4]))
+ break;
+ error = false;
+ break;
+ }
+ case 6: {
+ Dispatcher::Callback6 callback =
+ reinterpret_cast<Dispatcher::Callback6>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5]))
+ break;
+ error = false;
+ break;
+ }
+ case 7: {
+ Dispatcher::Callback7 callback =
+ reinterpret_cast<Dispatcher::Callback7>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6]))
+ break;
+ error = false;
+ break;
+ }
+ case 8: {
+ Dispatcher::Callback8 callback =
+ reinterpret_cast<Dispatcher::Callback8>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7]))
+ break;
+ error = false;
+ break;
+ }
+ case 9: {
+ Dispatcher::Callback9 callback =
+ reinterpret_cast<Dispatcher::Callback9>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7], args[8]))
+ break;
+ error = false;
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ }
+
+ if (error) {
+ if (handler)
+ SetCallError(SBOX_ERROR_FAILED_IPC, call_result);
+ } else {
+ memcpy(call_result, &ipc_info.return_info, sizeof(*call_result));
+ SetCallSuccess(call_result);
+ if (params->IsInOut()) {
+ // Maybe the params got changed by the broker. We need to upadte the
+ // memory section.
+ memcpy(ipc_buffer, params.get(), output_size);
+ }
+ }
+
+ ReleaseArgs(&ipc_params, args);
+
+ return !error;
+}
+
+// This function gets called by a thread from the thread pool when a
+// ping event fires. The context is the same as passed in the RegisterWait()
+// call above.
+void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context,
+ unsigned char) {
+ if (NULL == context) {
+ DCHECK(false);
+ return;
+ }
+ ServerControl* service_context = reinterpret_cast<ServerControl*>(context);
+ // Since the event fired, the channel *must* be busy. Change to kAckChannel
+ // while we service it.
+ LONG last_state =
+ ::InterlockedCompareExchange(&service_context->channel->state,
+ kAckChannel, kBusyChannel);
+ if (kBusyChannel != last_state) {
+ DCHECK(false);
+ return;
+ }
+
+ // Prepare the result structure. At this point we will return some result
+ // even if the IPC is invalid, malformed or has no handler.
+ CrossCallReturn call_result = {0};
+ void* buffer = service_context->channel_buffer;
+
+ InvokeCallback(service_context, buffer, &call_result);
+
+ // Copy the answer back into the channel and signal the pong event. This
+ // should wake up the client so he can finish the the ipc cycle.
+ CrossCallParams* call_params = reinterpret_cast<CrossCallParams*>(buffer);
+ memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result));
+ ::InterlockedExchange(&service_context->channel->state, kAckChannel);
+ ::SetEvent(service_context->pong_event);
+}
+
+bool SharedMemIPCServer::MakeEvents(HANDLE* server_ping, HANDLE* server_pong,
+ HANDLE* client_ping, HANDLE* client_pong) {
+ // Note that the IPC client has no right to delete the events. That would
+ // cause problems. The server *owns* the events.
+ const DWORD kDesiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE;
+
+ // The events are auto reset, and start not signaled.
+ *server_ping = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+ if (!::DuplicateHandle(::GetCurrentProcess(), *server_ping, target_process_,
+ client_ping, kDesiredAccess, FALSE, 0)) {
+ return false;
+ }
+ *server_pong = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+ if (!::DuplicateHandle(::GetCurrentProcess(), *server_pong, target_process_,
+ client_pong, kDesiredAccess, FALSE, 0)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sharedmem_ipc_server.h b/chromium/sandbox/win/src/sharedmem_ipc_server.h
new file mode 100644
index 00000000000..94d6959b812
--- /dev/null
+++ b/chromium/sandbox/win/src/sharedmem_ipc_server.h
@@ -0,0 +1,127 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
+#define SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+// IPC transport implementation that uses shared memory.
+// This is the server side
+//
+// The server side has knowledge about the layout of the shared memory
+// and the state transitions. Both are explained in sharedmem_ipc_client.h
+//
+// As opposed to SharedMemIPClient, the Server object should be one for the
+// entire lifetime of the target process. The server is in charge of creating
+// the events (ping, pong) both for the client and for the target that are used
+// to signal the IPC and also in charge of setting the initial state of the
+// channels.
+//
+// When an IPC is ready, the server relies on being called by on the
+// ThreadPingEventReady callback. The IPC server then retrieves the buffer,
+// marshals it into a CrossCallParam object and calls the Dispatcher, who is in
+// charge of fulfilling the IPC request.
+namespace sandbox {
+
+// the shared memory implementation of the IPC server. There should be one
+// of these objects per target (IPC client) process
+class SharedMemIPCServer {
+ public:
+ // Creates the IPC server.
+ // target_process: handle to the target process. It must be suspended.
+ // target_process_id: process id of the target process.
+ // target_job: the job object handle associated with the target process.
+ // thread_provider: a thread provider object.
+ // dispatcher: an object that can service IPC calls.
+ SharedMemIPCServer(HANDLE target_process, DWORD target_process_id,
+ HANDLE target_job, ThreadProvider* thread_provider,
+ Dispatcher* dispatcher);
+
+ ~SharedMemIPCServer();
+
+ // Initializes the server structures, shared memory structures and
+ // creates the kernels events used to signal the IPC.
+ bool Init(void* shared_mem, uint32 shared_size, uint32 channel_size);
+
+ private:
+ // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
+ // do not work with sandbox tests.
+ FRIEND_TEST_ALL_PREFIXES(IPCTest, SharedMemServerTests);
+ // When an event fires (IPC request). A thread from the ThreadProvider
+ // will call this function. The context parameter should be the same as
+ // provided when ThreadProvider::RegisterWait was called.
+ static void __stdcall ThreadPingEventReady(void* context,
+ unsigned char);
+
+ // Makes the client and server events. This function is called once
+ // per channel.
+ bool MakeEvents(HANDLE* server_ping, HANDLE* server_pong,
+ HANDLE* client_ping, HANDLE* client_pong);
+
+ // A copy this structure is maintained per channel.
+ // Note that a lot of the fields are just the same of what we have in the IPC
+ // object itself. It is better to have the copies since we can dispatch in the
+ // static method without worrying about converting back to a member function
+ // call or about threading issues.
+ struct ServerControl {
+ // This channel server ping event.
+ HANDLE ping_event;
+ // This channel server pong event.
+ HANDLE pong_event;
+ // The size of this channel.
+ uint32 channel_size;
+ // The pointer to the actual channel data.
+ char* channel_buffer;
+ // The pointer to the base of the shared memory.
+ char* shared_base;
+ // A pointer to this channel's client-side control structure this structure
+ // lives in the shared memory.
+ ChannelControl* channel;
+ // the IPC dispatcher associated with this channel.
+ Dispatcher* dispatcher;
+ // The target process information associated with this channel.
+ ClientInfo target_info;
+ };
+
+ // Looks for the appropriate handler for this IPC and invokes it.
+ static bool InvokeCallback(const ServerControl* service_context,
+ void* ipc_buffer, CrossCallReturn* call_result);
+
+ // Points to the shared memory channel control which lives at
+ // the start of the shared section.
+ IPCControl* client_control_;
+
+ // Keeps track of the server side objects that are used to answer an IPC.
+ typedef std::list<ServerControl*> ServerContexts;
+ ServerContexts server_contexts_;
+
+ // The thread provider provides the threads that call back into this object
+ // when the IPC events fire.
+ ThreadProvider* thread_provider_;
+
+ // The IPC object is associated with a target process.
+ HANDLE target_process_;
+
+ // The target process id associated with the IPC object.
+ DWORD target_process_id_;
+
+ // The target object is inside a job too.
+ HANDLE target_job_object_;
+
+ // The dispatcher handles 'ready' IPC calls.
+ Dispatcher* call_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemIPCServer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
diff --git a/chromium/sandbox/win/src/sid.cc b/chromium/sandbox/win/src/sid.cc
new file mode 100644
index 00000000000..261605d547b
--- /dev/null
+++ b/chromium/sandbox/win/src/sid.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2006-2008 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/sid.h"
+
+#include "base/logging.h"
+
+namespace sandbox {
+
+Sid::Sid(const SID *sid) {
+ ::CopySid(SECURITY_MAX_SID_SIZE, sid_, const_cast<SID*>(sid));
+};
+
+Sid::Sid(WELL_KNOWN_SID_TYPE type) {
+ DWORD size_sid = SECURITY_MAX_SID_SIZE;
+ BOOL result = ::CreateWellKnownSid(type, NULL, sid_, &size_sid);
+ DCHECK(result);
+ DBG_UNREFERENCED_LOCAL_VARIABLE(result);
+}
+
+const SID *Sid::GetPSID() const {
+ return reinterpret_cast<SID*>(const_cast<BYTE*>(sid_));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sid.h b/chromium/sandbox/win/src/sid.h
new file mode 100644
index 00000000000..4656859bec9
--- /dev/null
+++ b/chromium/sandbox/win/src/sid.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2006-2008 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_SID_H_
+#define SANDBOX_SRC_SID_H_
+
+#include <windows.h>
+
+namespace sandbox {
+
+// This class is used to hold and generate SIDS.
+class Sid {
+ public:
+ // Constructors initializing the object with the SID passed.
+ // This is a converting constructor. It is not explicit.
+ Sid(const SID *sid);
+ Sid(WELL_KNOWN_SID_TYPE type);
+
+ // Returns sid_.
+ const SID *GetPSID() const;
+
+ private:
+ BYTE sid_[SECURITY_MAX_SID_SIZE];
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SID_H_
diff --git a/chromium/sandbox/win/src/sid_unittest.cc b/chromium/sandbox/win/src/sid_unittest.cc
new file mode 100644
index 00000000000..76d61e82f5b
--- /dev/null
+++ b/chromium/sandbox/win/src/sid_unittest.cc
@@ -0,0 +1,71 @@
+// Copyright (c) 2006-2008 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 unit tests for the sid class.
+
+#define _ATL_NO_EXCEPTIONS
+#include <atlbase.h>
+#include <atlsecurity.h>
+
+#include "sandbox/win/src/sid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Calls ::EqualSid. This function exists only to simplify the calls to
+// ::EqualSid by removing the need to cast the input params.
+BOOL EqualSid(const SID *sid1, const SID *sid2) {
+ return ::EqualSid(const_cast<SID*>(sid1), const_cast<SID*>(sid2));
+}
+
+// Tests the creation if a Sid
+TEST(SidTest, Constructors) {
+ ATL::CSid sid_world = ATL::Sids::World();
+ SID *sid_world_pointer = const_cast<SID*>(sid_world.GetPSID());
+
+ // Check the SID* constructor
+ Sid sid_sid_star(sid_world_pointer);
+ ASSERT_TRUE(EqualSid(sid_world_pointer, sid_sid_star.GetPSID()));
+
+ // Check the copy constructor
+ Sid sid_copy(sid_sid_star);
+ ASSERT_TRUE(EqualSid(sid_world_pointer, sid_copy.GetPSID()));
+
+ // Note that the WELL_KNOWN_SID_TYPE constructor is tested in the GetPSID
+ // test.
+}
+
+// Tests the method GetPSID
+TEST(SidTest, GetPSID) {
+ // Check for non-null result;
+ ASSERT_NE(static_cast<SID*>(NULL), Sid(::WinLocalSid).GetPSID());
+ ASSERT_NE(static_cast<SID*>(NULL), Sid(::WinCreatorOwnerSid).GetPSID());
+ ASSERT_NE(static_cast<SID*>(NULL), Sid(::WinBatchSid).GetPSID());
+
+ ASSERT_TRUE(EqualSid(Sid(::WinNullSid).GetPSID(),
+ ATL::Sids::Null().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinWorldSid).GetPSID(),
+ ATL::Sids::World().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinDialupSid).GetPSID(),
+ ATL::Sids::Dialup().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinNetworkSid).GetPSID(),
+ ATL::Sids::Network().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinAdministratorsSid).GetPSID(),
+ ATL::Sids::Admins().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinUsersSid).GetPSID(),
+ ATL::Sids::Users().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinGuestsSid).GetPSID(),
+ ATL::Sids::Guests().GetPSID()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinProxySid).GetPSID(),
+ ATL::Sids::Proxy().GetPSID()));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp b/chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp
new file mode 100644
index 00000000000..89bc1895eb0
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp
@@ -0,0 +1,92 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Table of relevant information about how to decode the ModR/M byte.
+// Based on information in the IA-32 Intel Architecture
+// Software Developer's Manual Volume 2: Instruction Set Reference.
+
+#include "sandbox/win/src/sidestep/mini_disassembler.h"
+#include "sandbox/win/src/sidestep/mini_disassembler_types.h"
+
+namespace sidestep {
+
+const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = {
+// mod == 00
+ /* r/m == 000 */ { false, false, OS_ZERO },
+ /* r/m == 001 */ { false, false, OS_ZERO },
+ /* r/m == 010 */ { false, false, OS_ZERO },
+ /* r/m == 011 */ { false, false, OS_ZERO },
+ /* r/m == 100 */ { false, false, OS_ZERO },
+ /* r/m == 101 */ { false, false, OS_ZERO },
+ /* r/m == 110 */ { true, false, OS_WORD },
+ /* r/m == 111 */ { false, false, OS_ZERO },
+// mod == 01
+ /* r/m == 000 */ { true, false, OS_BYTE },
+ /* r/m == 001 */ { true, false, OS_BYTE },
+ /* r/m == 010 */ { true, false, OS_BYTE },
+ /* r/m == 011 */ { true, false, OS_BYTE },
+ /* r/m == 100 */ { true, false, OS_BYTE },
+ /* r/m == 101 */ { true, false, OS_BYTE },
+ /* r/m == 110 */ { true, false, OS_BYTE },
+ /* r/m == 111 */ { true, false, OS_BYTE },
+// mod == 10
+ /* r/m == 000 */ { true, false, OS_WORD },
+ /* r/m == 001 */ { true, false, OS_WORD },
+ /* r/m == 010 */ { true, false, OS_WORD },
+ /* r/m == 011 */ { true, false, OS_WORD },
+ /* r/m == 100 */ { true, false, OS_WORD },
+ /* r/m == 101 */ { true, false, OS_WORD },
+ /* r/m == 110 */ { true, false, OS_WORD },
+ /* r/m == 111 */ { true, false, OS_WORD },
+// mod == 11
+ /* r/m == 000 */ { false, false, OS_ZERO },
+ /* r/m == 001 */ { false, false, OS_ZERO },
+ /* r/m == 010 */ { false, false, OS_ZERO },
+ /* r/m == 011 */ { false, false, OS_ZERO },
+ /* r/m == 100 */ { false, false, OS_ZERO },
+ /* r/m == 101 */ { false, false, OS_ZERO },
+ /* r/m == 110 */ { false, false, OS_ZERO },
+ /* r/m == 111 */ { false, false, OS_ZERO }
+};
+
+const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = {
+// mod == 00
+ /* r/m == 000 */ { false, false, OS_ZERO },
+ /* r/m == 001 */ { false, false, OS_ZERO },
+ /* r/m == 010 */ { false, false, OS_ZERO },
+ /* r/m == 011 */ { false, false, OS_ZERO },
+ /* r/m == 100 */ { false, true, OS_ZERO },
+ /* r/m == 101 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 110 */ { false, false, OS_ZERO },
+ /* r/m == 111 */ { false, false, OS_ZERO },
+// mod == 01
+ /* r/m == 000 */ { true, false, OS_BYTE },
+ /* r/m == 001 */ { true, false, OS_BYTE },
+ /* r/m == 010 */ { true, false, OS_BYTE },
+ /* r/m == 011 */ { true, false, OS_BYTE },
+ /* r/m == 100 */ { true, true, OS_BYTE },
+ /* r/m == 101 */ { true, false, OS_BYTE },
+ /* r/m == 110 */ { true, false, OS_BYTE },
+ /* r/m == 111 */ { true, false, OS_BYTE },
+// mod == 10
+ /* r/m == 000 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 001 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 010 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 011 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 100 */ { true, true, OS_DOUBLE_WORD },
+ /* r/m == 101 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 110 */ { true, false, OS_DOUBLE_WORD },
+ /* r/m == 111 */ { true, false, OS_DOUBLE_WORD },
+// mod == 11
+ /* r/m == 000 */ { false, false, OS_ZERO },
+ /* r/m == 001 */ { false, false, OS_ZERO },
+ /* r/m == 010 */ { false, false, OS_ZERO },
+ /* r/m == 011 */ { false, false, OS_ZERO },
+ /* r/m == 100 */ { false, false, OS_ZERO },
+ /* r/m == 101 */ { false, false, OS_ZERO },
+ /* r/m == 110 */ { false, false, OS_ZERO },
+ /* r/m == 111 */ { false, false, OS_ZERO },
+};
+
+}; // namespace sidestep
diff --git a/chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp b/chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp
new file mode 100644
index 00000000000..b7d8a60bc13
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp
@@ -0,0 +1,1159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Opcode decoding maps. Based on the IA-32 Intel Architecture
+// Software Developer's Manual Volume 2: Instruction Set Reference. Idea
+// for how to lay out the tables in memory taken from the implementation
+// in the Bastard disassembly environment.
+
+#include "sandbox/win/src/sidestep/mini_disassembler.h"
+
+namespace sidestep {
+
+/*
+* This is the first table to be searched; the first field of each
+* Opcode in the table is either 0 to indicate you're in the
+* right table, or an index to the correct table, in the global
+* map g_pentiumOpcodeMap
+*/
+const Opcode s_first_opcode_byte[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF */ { 1, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x10 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x11 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x12 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x13 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x14 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x15 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x16 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x17 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x18 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x19 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1E */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1F */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x20 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x21 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x22 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x23 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x24 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x25 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x26 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x27 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "daa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x28 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x29 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "das", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x30 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x31 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x32 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x33 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x34 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x35 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x36 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x37 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aaa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x38 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x39 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aas", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x40 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x41 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x42 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x43 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x44 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x45 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x46 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x47 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x48 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x49 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x50 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x51 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x52 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x53 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x54 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x55 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x56 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x57 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x58 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x59 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x60 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x61 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x62 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_A, AM_NOT_USED, "bound", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x63 */ { 0, IT_GENERIC, AM_E | OT_W, AM_G | OT_W, AM_NOT_USED, "arpl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x64 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x65 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x66 */ { 0, IT_PREFIX_OPERAND, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x67 */ { 0, IT_PREFIX_ADDRESS, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x68 */ { 0, IT_GENERIC, AM_I | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x69 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_V, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6A */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_B, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6C */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "insb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6D */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "insd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6E */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X | OT_B, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X | OT_V, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x70 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x71 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x72 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x73 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x74 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x75 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x76 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x77 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x78 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x79 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7A */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7B */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7C */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7D */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7E */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7F */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x80 */ { 2, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x81 */ { 3, IT_REFERENCE, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x82 */ { 4, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x83 */ { 5, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x84 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x85 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x86 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x87 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x88 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x89 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8C */ { 0, IT_GENERIC, AM_E | OT_W, AM_S | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8D */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, "lea", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8E */ { 0, IT_GENERIC, AM_S | OT_W, AM_E | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8F */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x90 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "nop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x91 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x92 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x93 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x94 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x95 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x96 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x97 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x98 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cwde", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x99 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cdq", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9A */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "callf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9B */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wait", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9E */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_O | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_O | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA2 */ { 0, IT_GENERIC, AM_O | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA3 */ { 0, IT_GENERIC, AM_O | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA4 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "movsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA5 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "movsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA6 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "cmpsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA7 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "cmpsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAA */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "stosb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAB */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "stosd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X| OT_B, AM_NOT_USED, "lodsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X| OT_V, AM_NOT_USED, "lodsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAE */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_Y | OT_B, AM_NOT_USED, "scasb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_Y | OT_V, AM_NOT_USED, "scasd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB1 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB2 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB3 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB8 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBA */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBB */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBC */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBE */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC0 */ { 6, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC1 */ { 7, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC2 */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC3 */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC4 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "les", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "lds", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC8 */ { 0, IT_GENERIC, AM_I | OT_W, AM_I | OT_B, AM_NOT_USED, "enter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "leave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCA */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCB */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "int3", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCD */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "int", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCE */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "into", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCF */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "iret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD0 */ { 8, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD1 */ { 9, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD2 */ { 10, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD3 */ { 11, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD4 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aam", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD5 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "xlat", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+
+ // The following 8 lines would be references to the FPU tables, but we currently
+ // do not support the FPU instructions in this disassembler.
+
+ /* 0xD8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xDA */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xDB */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xDC */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xDD */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xDE */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xDF */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+
+
+ /* 0xE0 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE1 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE2 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE3 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jcxz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE6 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE7 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE8 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE9 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xEA */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xEB */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xEC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xED */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xEE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xEF */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_V, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF0 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lock:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF2 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "repne:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF3 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rep:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF4 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "hlt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF6 */ { 12, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF7 */ { 13, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xFA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cli", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xFB */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xFC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xFD */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "std", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xFE */ { 14, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xFF */ { 15, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f[] = {
+ /* 0x0 */ { 16, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 17, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lsl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "invd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wbinvd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud2", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xE */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x10 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movups", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "movsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "movss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movupd" } },
+ /* 0x11 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movups", true,
+ /* F2h */ { 0, IT_GENERIC, AM_W | OT_SD, AM_V | OT_SD, AM_NOT_USED, "movsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_W | OT_SS, AM_V | OT_SS, AM_NOT_USED, "movss" },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movupd" } },
+ /* 0x12 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // only one of ...
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // ...these two is correct, Intel doesn't specify which
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_S, AM_NOT_USED, "movlpd" } },
+ /* 0x13 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlpd" } },
+ /* 0x14 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpcklps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpcklpd" } },
+ /* 0x15 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpckhps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpckhpd" } },
+ /* 0x16 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // only one of...
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // ...these two is correct, Intel doesn't specify which
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhpd" } },
+ /* 0x17 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhpd" } },
+ /* 0x18 */ { 18, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x19 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1C */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x20 */ { 0, IT_GENERIC, AM_R | OT_D, AM_C | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x21 */ { 0, IT_GENERIC, AM_R | OT_D, AM_D | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x22 */ { 0, IT_GENERIC, AM_C | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x23 */ { 0, IT_GENERIC, AM_D | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x24 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x25 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x26 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x27 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x28 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movaps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movapd" } },
+ /* 0x29 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movaps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movapd" } },
+ /* 0x2A */ { 0, IT_GENERIC, AM_V | OT_PS, AM_Q | OT_Q, AM_NOT_USED, "cvtpi2ps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_E | OT_D, AM_NOT_USED, "cvtsi2sd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_E | OT_D, AM_NOT_USED, "cvtsi2ss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_Q | OT_DQ, AM_NOT_USED, "cvtpi2pd" } },
+ /* 0x2B */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movntps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movntpd" } },
+ /* 0x2C */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvttps2pi", true,
+ /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvttsd2si" },
+ /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvttss2si" },
+ /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2pi" } },
+ /* 0x2D */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvtps2pi", true,
+ /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvtsd2si" },
+ /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvtss2si" },
+ /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2pi" } },
+ /* 0x2E */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "ucomiss", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "ucomisd" } },
+ /* 0x2F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_SS, AM_NOT_USED, "comiss", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "comisd" } },
+ /* 0x30 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wrmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x31 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdtsc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x32 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x33 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdpmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x34 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysenter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x35 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysexit", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x36 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x37 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x38 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x39 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x40 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x41 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x42 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x43 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x44 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x45 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x46 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x47 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmova", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x48 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x49 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4A */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4D */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4E */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4F */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x50 */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PS, AM_NOT_USED, "movmskps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PD, AM_NOT_USED, "movmskpd" } },
+ /* 0x51 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "sqrtps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "sqrtsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "sqrtss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "sqrtpd" } },
+ /* 0x52 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rsqrtps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rsqrtss" },
+ /* 66h */ { 0 } },
+ /* 0x53 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rcpps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rcpss" },
+ /* 66h */ { 0 } },
+ /* 0x54 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andpd" } },
+ /* 0x55 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andnps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andnpd" } },
+ /* 0x56 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "orps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "orpd" } },
+ /* 0x57 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "xorps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "xorpd" } },
+ /* 0x58 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "addps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "addsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "addss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "addpd" } },
+ /* 0x59 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "mulps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "mulsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "mulss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "mulpd" } },
+ /* 0x5A */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PS, AM_NOT_USED, "cvtps2pd", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "cvtsd2ss" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "cvtss2sd" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PD, AM_NOT_USED, "cvtpd2ps" } },
+ /* 0x5B */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2ps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvttps2dq" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvtps2dq" } },
+ /* 0x5C */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "subps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "subsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "subss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "subpd" } },
+ /* 0x5D */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "minps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "minsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "minss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "minpd" } },
+ /* 0x5E */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "divps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "divsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "divss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "divpd" } },
+ /* 0x5F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "maxps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "maxsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "maxss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "maxpd" } },
+ /* 0x60 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklbw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklbw" } },
+ /* 0x61 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklwd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklwd" } },
+ /* 0x62 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckldq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpckldq" } },
+ /* 0x63 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packsswb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packsswb" } },
+ /* 0x64 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtb" } },
+ /* 0x65 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtw" } },
+ /* 0x66 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtd" } },
+ /* 0x67 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packuswb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packuswb" } },
+ /* 0x68 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhbw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhbw" } },
+ /* 0x69 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhwd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhwd" } },
+ /* 0x6A */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhdq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhdq" } },
+ /* 0x6B */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packssdw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "packssdw" } },
+ /* 0x6C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } },
+ /* 0x6D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } },
+ /* 0x6E */ { 0, IT_GENERIC, AM_P | OT_D, AM_E | OT_D, AM_NOT_USED, "movd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_NOT_USED, "movd" } },
+ /* 0x6F */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "movq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqu" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqa" } },
+ /* 0x70 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_I | OT_B, "pshuf", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshuflw" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufhw" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufd" } },
+ /* 0x71 */ { 19, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x72 */ { 20, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x73 */ { 21, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x74 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqb" } },
+ /* 0x75 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqw" } },
+ /* 0x76 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqd" } },
+ /* 0x77 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "emms", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+
+ // The following six opcodes are escapes into the MMX stuff, which this disassembler does not support.
+ /* 0x78 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x79 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7A */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7B */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7C */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7D */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+
+ /* 0x7E */ { 0, IT_GENERIC, AM_E | OT_D, AM_P | OT_D, AM_NOT_USED, "movd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movq" },
+ /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_DQ, AM_NOT_USED, "movd" } },
+ /* 0x7F */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_P | OT_Q, AM_NOT_USED, "movq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqu" },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqa" } },
+ /* 0x80 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x81 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x82 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x83 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x84 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x85 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x86 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x87 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x88 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x89 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8A */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8B */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8C */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8D */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8E */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x8F */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x90 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seto", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x91 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x92 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x93 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x94 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x95 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x96 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x97 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seta", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x98 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "sets", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x99 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9A */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9B */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9C */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9D */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9E */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x9F */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cpuid", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA6 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA7 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rsm", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAC */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAD */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAE */ { 22, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xAF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB2 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lss", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB4 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lfs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB5 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lgs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB6 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB7 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xB9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud1", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBA */ { 23, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBC */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBD */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBE */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xBF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC2 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "cmpps", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_I | OT_B, "cmpsd" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_I | OT_B, "cmpss" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "cmppd" } },
+ /* 0xC3 */ { 0, IT_GENERIC, AM_E | OT_D, AM_G | OT_D, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_E | OT_D, AM_I | OT_B, "pinsrw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_I | OT_B, "pinsrw" } },
+ /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_I | OT_B, "pextrw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_I | OT_B, "pextrw" } },
+ /* 0xC6 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "shufps", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "shufpd" } },
+ /* 0xC7 */ { 24, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC8 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xC9 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCA */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCB */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCC */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCD */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCE */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xCF */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xD1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlw" } },
+ /* 0xD2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrld", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrld" } },
+ /* 0xD3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlq" } },
+ /* 0xD4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddq" } },
+ /* 0xD5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmullw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmullw" } },
+ /* 0xD6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "unused without prefix", true,
+ /* F2h */ { 0, IT_GENERIC, AM_P | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movdq2q" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_Q | OT_Q, AM_NOT_USED, "movq2dq" },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movq" } },
+ /* 0xD7 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_NOT_USED, "pmovmskb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_NOT_USED, "pmovmskb" } },
+ /* 0xD8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusb" } },
+ /* 0xD9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusw" } },
+ /* 0xDA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminub", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminub" } },
+ /* 0xDB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pand", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pand" } },
+ /* 0xDC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusb" } },
+ /* 0xDD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusw" } },
+ /* 0xDE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxub", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxub" } },
+ /* 0xDF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pandn", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pandn" } },
+ /* 0xE0 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgb" } },
+ /* 0xE1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psraw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrqw" } },
+ /* 0xE2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrad", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrad" } },
+ /* 0xE3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgw" } },
+ /* 0xE4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhuw" } },
+ /* 0xE5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhw" } },
+ /* 0xE6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true,
+ /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2dq" },
+ /* F3h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2pd" },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2dq" } },
+ /* 0xE7 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movntq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movntdq" } },
+ /* 0xE8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsb" } },
+ /* 0xE9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsw" } },
+ /* 0xEA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminsw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminsw" } },
+ /* 0xEB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "por", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "por" } },
+ /* 0xEC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsb" } },
+ /* 0xED */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsw" } },
+ /* 0xEE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxsw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxsw" } },
+ /* 0xEF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pxor", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pxor" } },
+ /* 0xF0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0xF1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllw" } },
+ /* 0xF2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pslld", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pslld" } },
+ /* 0xF3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllq" } },
+ /* 0xF4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmuludq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmuludq" } },
+ /* 0xF5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaddwd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaddwd" } },
+ /* 0xF6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psadbw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psadbw" } },
+ /* 0xF7 */ { 0, IT_GENERIC, AM_P | OT_PI, AM_Q | OT_PI, AM_NOT_USED, "maskmovq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "maskmovdqu" } },
+ /* 0xF8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubb" } },
+ /* 0xF9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubw" } },
+ /* 0xFA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubd" } },
+ /* 0xFB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubq" } },
+ /* 0xFC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddb", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddb" } },
+ /* 0xFD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddw" } },
+ /* 0xFE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddd", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddd" } },
+ /* 0xFF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f00[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "sldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "str", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "ltr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f01[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "smsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lmsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_M | OT_B, AM_NOT_USED, AM_NOT_USED, "invlpg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f18[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f71[] = {
+ /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlw" } },
+ /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psraw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psraw" } },
+ /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllw", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllw" } },
+ /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f72[] = {
+ /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrld", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrld" } },
+ /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrad", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrad" } },
+ /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "pslld", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslld" } },
+ /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0f73[] = {
+ /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlq" } },
+ /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllq" } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq", true,
+ /* F2h */ { 0 },
+ /* F3h */ { 0 },
+ /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq" } },
+};
+
+const Opcode s_opcode_byte_after_0fae[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxsave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxrstor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ldmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "mfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clflush/sfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+};
+
+const Opcode s_opcode_byte_after_0fba[] = {
+ /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_0fc7[] = {
+ /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_Q, AM_NOT_USED, AM_NOT_USED, "cmpxch8b", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_80[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_81[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_82[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_83[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_c0[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_c1[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_d0[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_d1[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_d2[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_d3[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_f6[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_f7[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_fe[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+const Opcode s_opcode_byte_after_ff[] = {
+ /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x2 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x3 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x4 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x5 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } },
+ /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }
+};
+
+/*
+* A table of all the other tables, containing some extra information, e.g.
+* how to mask out the byte we're looking at.
+*/
+const OpcodeTable MiniDisassembler::s_ia32_opcode_map_[]={
+ // One-byte opcodes and jumps to larger
+ /* 0 */ {s_first_opcode_byte, 0, 0xff, 0, 0xff},
+ // Two-byte opcodes (second byte)
+ /* 1 */ {s_opcode_byte_after_0f, 0, 0xff, 0, 0xff},
+ // Start of tables for opcodes using ModR/M bits as extension
+ /* 2 */ {s_opcode_byte_after_80, 3, 0x07, 0, 0x07},
+ /* 3 */ {s_opcode_byte_after_81, 3, 0x07, 0, 0x07},
+ /* 4 */ {s_opcode_byte_after_82, 3, 0x07, 0, 0x07},
+ /* 5 */ {s_opcode_byte_after_83, 3, 0x07, 0, 0x07},
+ /* 6 */ {s_opcode_byte_after_c0, 3, 0x07, 0, 0x07},
+ /* 7 */ {s_opcode_byte_after_c1, 3, 0x07, 0, 0x07},
+ /* 8 */ {s_opcode_byte_after_d0, 3, 0x07, 0, 0x07},
+ /* 9 */ {s_opcode_byte_after_d1, 3, 0x07, 0, 0x07},
+ /* 10 */ {s_opcode_byte_after_d2, 3, 0x07, 0, 0x07},
+ /* 11 */ {s_opcode_byte_after_d3, 3, 0x07, 0, 0x07},
+ /* 12 */ {s_opcode_byte_after_f6, 3, 0x07, 0, 0x07},
+ /* 13 */ {s_opcode_byte_after_f7, 3, 0x07, 0, 0x07},
+ /* 14 */ {s_opcode_byte_after_fe, 3, 0x07, 0, 0x01},
+ /* 15 */ {s_opcode_byte_after_ff, 3, 0x07, 0, 0x07},
+ /* 16 */ {s_opcode_byte_after_0f00, 3, 0x07, 0, 0x07},
+ /* 17 */ {s_opcode_byte_after_0f01, 3, 0x07, 0, 0x07},
+ /* 18 */ {s_opcode_byte_after_0f18, 3, 0x07, 0, 0x07},
+ /* 19 */ {s_opcode_byte_after_0f71, 3, 0x07, 0, 0x07},
+ /* 20 */ {s_opcode_byte_after_0f72, 3, 0x07, 0, 0x07},
+ /* 21 */ {s_opcode_byte_after_0f73, 3, 0x07, 0, 0x07},
+ /* 22 */ {s_opcode_byte_after_0fae, 3, 0x07, 0, 0x07},
+ /* 23 */ {s_opcode_byte_after_0fba, 3, 0x07, 0, 0x07},
+ /* 24 */ {s_opcode_byte_after_0fc7, 3, 0x07, 0, 0x01}
+};
+
+}; // namespace sidestep
diff --git a/chromium/sandbox/win/src/sidestep/mini_disassembler.cpp b/chromium/sandbox/win/src/sidestep/mini_disassembler.cpp
new file mode 100644
index 00000000000..1e8e0bd9729
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/mini_disassembler.cpp
@@ -0,0 +1,395 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation of MiniDisassembler.
+
+#ifdef _WIN64
+#error The code in this file should not be used on 64-bit Windows.
+#endif
+
+#include "sandbox/win/src/sidestep/mini_disassembler.h"
+
+namespace sidestep {
+
+MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits,
+ bool address_default_is_32_bits)
+ : operand_default_is_32_bits_(operand_default_is_32_bits),
+ address_default_is_32_bits_(address_default_is_32_bits) {
+ Initialize();
+}
+
+MiniDisassembler::MiniDisassembler()
+ : operand_default_is_32_bits_(true),
+ address_default_is_32_bits_(true) {
+ Initialize();
+}
+
+InstructionType MiniDisassembler::Disassemble(
+ unsigned char* start_byte,
+ unsigned int* instruction_bytes) {
+ // Clean up any state from previous invocations.
+ Initialize();
+
+ // Start by processing any prefixes.
+ unsigned char* current_byte = start_byte;
+ unsigned int size = 0;
+ InstructionType instruction_type = ProcessPrefixes(current_byte, &size);
+
+ if (IT_UNKNOWN == instruction_type)
+ return instruction_type;
+
+ current_byte += size;
+ size = 0;
+
+ // Invariant: We have stripped all prefixes, and the operand_is_32_bits_
+ // and address_is_32_bits_ flags are correctly set.
+
+ instruction_type = ProcessOpcode(current_byte, 0, &size);
+
+ // Check for error processing instruction
+ if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) {
+ return IT_UNKNOWN;
+ }
+
+ current_byte += size;
+
+ // Invariant: operand_bytes_ indicates the total size of operands
+ // specified by the opcode and/or ModR/M byte and/or SIB byte.
+ // pCurrentByte points to the first byte after the ModR/M byte, or after
+ // the SIB byte if it is present (i.e. the first byte of any operands
+ // encoded in the instruction).
+
+ // We get the total length of any prefixes, the opcode, and the ModR/M and
+ // SIB bytes if present, by taking the difference of the original starting
+ // address and the current byte (which points to the first byte of the
+ // operands if present, or to the first byte of the next instruction if
+ // they are not). Adding the count of bytes in the operands encoded in
+ // the instruction gives us the full length of the instruction in bytes.
+ *instruction_bytes += operand_bytes_ + (current_byte - start_byte);
+
+ // Return the instruction type, which was set by ProcessOpcode().
+ return instruction_type_;
+}
+
+void MiniDisassembler::Initialize() {
+ operand_is_32_bits_ = operand_default_is_32_bits_;
+ address_is_32_bits_ = address_default_is_32_bits_;
+ operand_bytes_ = 0;
+ have_modrm_ = false;
+ should_decode_modrm_ = false;
+ instruction_type_ = IT_UNKNOWN;
+ got_f2_prefix_ = false;
+ got_f3_prefix_ = false;
+ got_66_prefix_ = false;
+}
+
+InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte,
+ unsigned int* size) {
+ InstructionType instruction_type = IT_GENERIC;
+ const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte];
+
+ switch (opcode.type_) {
+ case IT_PREFIX_ADDRESS:
+ address_is_32_bits_ = !address_default_is_32_bits_;
+ goto nochangeoperand;
+ case IT_PREFIX_OPERAND:
+ operand_is_32_bits_ = !operand_default_is_32_bits_;
+ nochangeoperand:
+ case IT_PREFIX:
+
+ if (0xF2 == (*start_byte))
+ got_f2_prefix_ = true;
+ else if (0xF3 == (*start_byte))
+ got_f3_prefix_ = true;
+ else if (0x66 == (*start_byte))
+ got_66_prefix_ = true;
+
+ instruction_type = opcode.type_;
+ (*size)++;
+ // we got a prefix, so add one and check next byte
+ ProcessPrefixes(start_byte + 1, size);
+ default:
+ break; // not a prefix byte
+ }
+
+ return instruction_type;
+}
+
+InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte,
+ unsigned int table_index,
+ unsigned int* size) {
+ const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table
+ unsigned char current_byte = (*start_byte) >> table.shift_;
+ current_byte = current_byte & table.mask_; // Mask out the bits we will use
+
+ // Check whether the byte we have is inside the table we have.
+ if (current_byte < table.min_lim_ || current_byte > table.max_lim_) {
+ instruction_type_ = IT_UNKNOWN;
+ return instruction_type_;
+ }
+
+ const Opcode& opcode = table.table_[current_byte];
+ if (IT_UNUSED == opcode.type_) {
+ // This instruction is not used by the IA-32 ISA, so we indicate
+ // this to the user. Probably means that we were pointed to
+ // a byte in memory that was not the start of an instruction.
+ instruction_type_ = IT_UNUSED;
+ return instruction_type_;
+ } else if (IT_REFERENCE == opcode.type_) {
+ // We are looking at an opcode that has more bytes (or is continued
+ // in the ModR/M byte). Recursively find the opcode definition in
+ // the table for the opcode's next byte.
+ (*size)++;
+ ProcessOpcode(start_byte + 1, opcode.table_index_, size);
+ return instruction_type_;
+ }
+
+ const SpecificOpcode* specific_opcode = reinterpret_cast<
+ const SpecificOpcode*>(&opcode);
+ if (opcode.is_prefix_dependent_) {
+ if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) {
+ specific_opcode = &opcode.opcode_if_f2_prefix_;
+ } else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) {
+ specific_opcode = &opcode.opcode_if_f3_prefix_;
+ } else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) {
+ specific_opcode = &opcode.opcode_if_66_prefix_;
+ }
+ }
+
+ // Inv: The opcode type is known.
+ instruction_type_ = specific_opcode->type_;
+
+ // Let's process the operand types to see if we have any immediate
+ // operands, and/or a ModR/M byte.
+
+ ProcessOperand(specific_opcode->flag_dest_);
+ ProcessOperand(specific_opcode->flag_source_);
+ ProcessOperand(specific_opcode->flag_aux_);
+
+ // Inv: We have processed the opcode and incremented operand_bytes_
+ // by the number of bytes of any operands specified by the opcode
+ // that are stored in the instruction (not registers etc.). Now
+ // we need to return the total number of bytes for the opcode and
+ // for the ModR/M or SIB bytes if they are present.
+
+ if (table.mask_ != 0xff) {
+ if (have_modrm_) {
+ // we're looking at a ModR/M byte so we're not going to
+ // count that into the opcode size
+ ProcessModrm(start_byte, size);
+ return IT_GENERIC;
+ } else {
+ // need to count the ModR/M byte even if it's just being
+ // used for opcode extension
+ (*size)++;
+ return IT_GENERIC;
+ }
+ } else {
+ if (have_modrm_) {
+ // The ModR/M byte is the next byte.
+ (*size)++;
+ ProcessModrm(start_byte + 1, size);
+ return IT_GENERIC;
+ } else {
+ (*size)++;
+ return IT_GENERIC;
+ }
+ }
+}
+
+bool MiniDisassembler::ProcessOperand(int flag_operand) {
+ bool succeeded = true;
+ if (AM_NOT_USED == flag_operand)
+ return succeeded;
+
+ // Decide what to do based on the addressing mode.
+ switch (flag_operand & AM_MASK) {
+ // No ModR/M byte indicated by these addressing modes, and no
+ // additional (e.g. immediate) parameters.
+ case AM_A: // Direct address
+ case AM_F: // EFLAGS register
+ case AM_X: // Memory addressed by the DS:SI register pair
+ case AM_Y: // Memory addressed by the ES:DI register pair
+ case AM_IMPLICIT: // Parameter is implicit, occupies no space in
+ // instruction
+ break;
+
+ // There is a ModR/M byte but it does not necessarily need
+ // to be decoded.
+ case AM_C: // reg field of ModR/M selects a control register
+ case AM_D: // reg field of ModR/M selects a debug register
+ case AM_G: // reg field of ModR/M selects a general register
+ case AM_P: // reg field of ModR/M selects an MMX register
+ case AM_R: // mod field of ModR/M may refer only to a general register
+ case AM_S: // reg field of ModR/M selects a segment register
+ case AM_T: // reg field of ModR/M selects a test register
+ case AM_V: // reg field of ModR/M selects a 128-bit XMM register
+ have_modrm_ = true;
+ break;
+
+ // In these addressing modes, there is a ModR/M byte and it needs to be
+ // decoded. No other (e.g. immediate) params than indicated in ModR/M.
+ case AM_E: // Operand is either a general-purpose register or memory,
+ // specified by ModR/M byte
+ case AM_M: // ModR/M byte will refer only to memory
+ case AM_Q: // Operand is either an MMX register or memory (complex
+ // evaluation), specified by ModR/M byte
+ case AM_W: // Operand is either a 128-bit XMM register or memory (complex
+ // eval), specified by ModR/M byte
+ have_modrm_ = true;
+ should_decode_modrm_ = true;
+ break;
+
+ // These addressing modes specify an immediate or an offset value
+ // directly, so we need to look at the operand type to see how many
+ // bytes.
+ case AM_I: // Immediate data.
+ case AM_J: // Jump to offset.
+ case AM_O: // Operand is at offset.
+ switch (flag_operand & OT_MASK) {
+ case OT_B: // Byte regardless of operand-size attribute.
+ operand_bytes_ += OS_BYTE;
+ break;
+ case OT_C: // Byte or word, depending on operand-size attribute.
+ if (operand_is_32_bits_)
+ operand_bytes_ += OS_WORD;
+ else
+ operand_bytes_ += OS_BYTE;
+ break;
+ case OT_D: // Doubleword, regardless of operand-size attribute.
+ operand_bytes_ += OS_DOUBLE_WORD;
+ break;
+ case OT_DQ: // Double-quadword, regardless of operand-size attribute.
+ operand_bytes_ += OS_DOUBLE_QUAD_WORD;
+ break;
+ case OT_P: // 32-bit or 48-bit pointer, depending on operand-size
+ // attribute.
+ if (operand_is_32_bits_)
+ operand_bytes_ += OS_48_BIT_POINTER;
+ else
+ operand_bytes_ += OS_32_BIT_POINTER;
+ break;
+ case OT_PS: // 128-bit packed single-precision floating-point data.
+ operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING;
+ break;
+ case OT_Q: // Quadword, regardless of operand-size attribute.
+ operand_bytes_ += OS_QUAD_WORD;
+ break;
+ case OT_S: // 6-byte pseudo-descriptor.
+ operand_bytes_ += OS_PSEUDO_DESCRIPTOR;
+ break;
+ case OT_SD: // Scalar Double-Precision Floating-Point Value
+ case OT_PD: // Unaligned packed double-precision floating point value
+ operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING;
+ break;
+ case OT_SS:
+ // Scalar element of a 128-bit packed single-precision
+ // floating data.
+ // We simply return enItUnknown since we don't have to support
+ // floating point
+ succeeded = false;
+ break;
+ case OT_V: // Word or doubleword, depending on operand-size attribute.
+ if (operand_is_32_bits_)
+ operand_bytes_ += OS_DOUBLE_WORD;
+ else
+ operand_bytes_ += OS_WORD;
+ break;
+ case OT_W: // Word, regardless of operand-size attribute.
+ operand_bytes_ += OS_WORD;
+ break;
+
+ // Can safely ignore these.
+ case OT_A: // Two one-word operands in memory or two double-word
+ // operands in memory
+ case OT_PI: // Quadword MMX technology register (e.g. mm0)
+ case OT_SI: // Doubleword integer register (e.g., eax)
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return succeeded;
+}
+
+bool MiniDisassembler::ProcessModrm(unsigned char* start_byte,
+ unsigned int* size) {
+ // If we don't need to decode, we just return the size of the ModR/M
+ // byte (there is never a SIB byte in this case).
+ if (!should_decode_modrm_) {
+ (*size)++;
+ return true;
+ }
+
+ // We never care about the reg field, only the combination of the mod
+ // and r/m fields, so let's start by packing those fields together into
+ // 5 bits.
+ unsigned char modrm = (*start_byte);
+ unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field
+ modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field
+ mod = mod >> 3; // shift the mod field to the right place
+ modrm = mod | modrm; // combine the r/m and mod fields as discussed
+ mod = mod >> 3; // shift the mod field to bits 2..0
+
+ // Invariant: modrm contains the mod field in bits 4..3 and the r/m field
+ // in bits 2..0, and mod contains the mod field in bits 2..0
+
+ const ModrmEntry* modrm_entry = 0;
+ if (address_is_32_bits_)
+ modrm_entry = &s_ia32_modrm_map_[modrm];
+ else
+ modrm_entry = &s_ia16_modrm_map_[modrm];
+
+ // Invariant: modrm_entry points to information that we need to decode
+ // the ModR/M byte.
+
+ // Add to the count of operand bytes, if the ModR/M byte indicates
+ // that some operands are encoded in the instruction.
+ if (modrm_entry->is_encoded_in_instruction_)
+ operand_bytes_ += modrm_entry->operand_size_;
+
+ // Process the SIB byte if necessary, and return the count
+ // of ModR/M and SIB bytes.
+ if (modrm_entry->use_sib_byte_) {
+ (*size)++;
+ return ProcessSib(start_byte + 1, mod, size);
+ } else {
+ (*size)++;
+ return true;
+ }
+}
+
+bool MiniDisassembler::ProcessSib(unsigned char* start_byte,
+ unsigned char mod,
+ unsigned int* size) {
+ // get the mod field from the 2..0 bits of the SIB byte
+ unsigned char sib_base = (*start_byte) & 0x07;
+ if (0x05 == sib_base) {
+ switch (mod) {
+ case 0x00: // mod == 00
+ case 0x02: // mod == 10
+ operand_bytes_ += OS_DOUBLE_WORD;
+ break;
+ case 0x01: // mod == 01
+ operand_bytes_ += OS_BYTE;
+ break;
+ case 0x03: // mod == 11
+ // According to the IA-32 docs, there does not seem to be a disp
+ // value for this value of mod
+ default:
+ break;
+ }
+ }
+
+ (*size)++;
+ return true;
+}
+
+}; // namespace sidestep
diff --git a/chromium/sandbox/win/src/sidestep/mini_disassembler.h b/chromium/sandbox/win/src/sidestep/mini_disassembler.h
new file mode 100644
index 00000000000..202c4ecc204
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/mini_disassembler.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Definition of MiniDisassembler.
+
+#ifndef SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__
+#define SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__
+
+#include "sandbox/win/src/sidestep/mini_disassembler_types.h"
+
+namespace sidestep {
+
+// This small disassembler is very limited
+// in its functionality, and in fact does only the bare minimum required by the
+// preamble patching utility. It may be useful for other purposes, however.
+//
+// The limitations include at least the following:
+// -# No support for coprocessor opcodes, MMX, etc.
+// -# No machine-readable identification of opcodes or decoding of
+// assembly parameters. The name of the opcode (as a string) is given,
+// however, to aid debugging.
+//
+// You may ask what this little disassembler actually does, then? The answer is
+// that it does the following, which is exactly what the patching utility needs:
+// -# Indicates if opcode is a jump (any kind) or a return (any kind)
+// because this is important for the patching utility to determine if
+// a function is too short or there are jumps too early in it for it
+// to be preamble patched.
+// -# The opcode length is always calculated, so that the patching utility
+// can figure out where the next instruction starts, and whether it
+// already has enough instructions to replace with the absolute jump
+// to the patching code.
+//
+// The usage is quite simple; just create a MiniDisassembler and use its
+// Disassemble() method.
+//
+// If you would like to extend this disassembler, please refer to the
+// IA-32 Intel Architecture Software Developer's Manual Volume 2:
+// Instruction Set Reference for information about operand decoding
+// etc.
+class MiniDisassembler {
+ public:
+
+ // Creates a new instance and sets defaults.
+ //
+ // operand_default_32_bits: If true, the default operand size is
+ // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits.
+ // address_default_32_bits: If true, the default address size is
+ // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits.
+ MiniDisassembler(bool operand_default_32_bits,
+ bool address_default_32_bits);
+
+ // Equivalent to MiniDisassembler(true, true);
+ MiniDisassembler();
+
+ // Attempts to disassemble a single instruction starting from the
+ // address in memory it is pointed to.
+ //
+ // start: Address where disassembly should start.
+ // instruction_bytes: Variable that will be incremented by
+ // the length in bytes of the instruction.
+ // Returns enItJump, enItReturn or enItGeneric on success. enItUnknown
+ // if unable to disassemble, enItUnused if this seems to be an unused
+ // opcode. In the last two (error) cases, cbInstruction will be set
+ // to 0xffffffff.
+ //
+ // Postcondition: This instance of the disassembler is ready to be used again,
+ // with unchanged defaults from creation time.
+ InstructionType Disassemble(unsigned char* start,
+ unsigned int* instruction_bytes);
+
+ private:
+
+ // Makes the disassembler ready for reuse.
+ void Initialize();
+
+ // Sets the flags for address and operand sizes.
+ // Returns Number of prefix bytes.
+ InstructionType ProcessPrefixes(unsigned char* start, unsigned int* size);
+
+ // Sets the flag for whether we have ModR/M, and increments
+ // operand_bytes_ if any are specifies by the opcode directly.
+ // Returns Number of opcode bytes.
+ InstructionType ProcessOpcode(unsigned char* start,
+ unsigned int table,
+ unsigned int* size);
+
+ // Checks the type of the supplied operand. Increments
+ // operand_bytes_ if it directly indicates an immediate etc.
+ // operand. Asserts have_modrm_ if the operand specifies
+ // a ModR/M byte.
+ bool ProcessOperand(int flag_operand);
+
+ // Increments operand_bytes_ by size specified by ModR/M and
+ // by SIB if present.
+ // Returns 0 in case of error, 1 if there is just a ModR/M byte,
+ // 2 if there is a ModR/M byte and a SIB byte.
+ bool ProcessModrm(unsigned char* start, unsigned int* size);
+
+ // Processes the SIB byte that it is pointed to.
+ // start: Pointer to the SIB byte.
+ // mod: The mod field from the ModR/M byte.
+ // Returns 1 to indicate success (indicates 1 SIB byte)
+ bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int* size);
+
+ // The instruction type we have decoded from the opcode.
+ InstructionType instruction_type_;
+
+ // Counts the number of bytes that is occupied by operands in
+ // the current instruction (note: we don't care about how large
+ // operands stored in registers etc. are).
+ unsigned int operand_bytes_;
+
+ // True iff there is a ModR/M byte in this instruction.
+ bool have_modrm_;
+
+ // True iff we need to decode the ModR/M byte (sometimes it just
+ // points to a register, we can tell by the addressing mode).
+ bool should_decode_modrm_;
+
+ // Current operand size is 32 bits if true, 16 bits if false.
+ bool operand_is_32_bits_;
+
+ // Default operand size is 32 bits if true, 16 bits if false.
+ bool operand_default_is_32_bits_;
+
+ // Current address size is 32 bits if true, 16 bits if false.
+ bool address_is_32_bits_;
+
+ // Default address size is 32 bits if true, 16 bits if false.
+ bool address_default_is_32_bits_;
+
+ // Huge big opcode table based on the IA-32 manual, defined
+ // in Ia32OpcodeMap.cpp
+ static const OpcodeTable s_ia32_opcode_map_[];
+
+ // Somewhat smaller table to help with decoding ModR/M bytes
+ // when 16-bit addressing mode is being used. Defined in
+ // Ia32ModrmMap.cpp
+ static const ModrmEntry s_ia16_modrm_map_[];
+
+ // Somewhat smaller table to help with decoding ModR/M bytes
+ // when 32-bit addressing mode is being used. Defined in
+ // Ia32ModrmMap.cpp
+ static const ModrmEntry s_ia32_modrm_map_[];
+
+ // Indicators of whether we got certain prefixes that certain
+ // silly Intel instructions depend on in nonstandard ways for
+ // their behaviors.
+ bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_;
+};
+
+}; // namespace sidestep
+
+#endif // SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_H__
diff --git a/chromium/sandbox/win/src/sidestep/mini_disassembler_types.h b/chromium/sandbox/win/src/sidestep/mini_disassembler_types.h
new file mode 100644
index 00000000000..1c106263134
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/mini_disassembler_types.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2006-2008 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.
+//
+// Several simple types used by the disassembler and some of the patching
+// mechanisms.
+
+#ifndef SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__
+#define SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__
+
+namespace sidestep {
+
+// Categories of instructions that we care about
+enum InstructionType {
+ // This opcode is not used
+ IT_UNUSED,
+ // This disassembler does not recognize this opcode (error)
+ IT_UNKNOWN,
+ // This is not an instruction but a reference to another table
+ IT_REFERENCE,
+ // This byte is a prefix byte that we can ignore
+ IT_PREFIX,
+ // This is a prefix byte that switches to the nondefault address size
+ IT_PREFIX_ADDRESS,
+ // This is a prefix byte that switches to the nondefault operand size
+ IT_PREFIX_OPERAND,
+ // A jump or call instruction
+ IT_JUMP,
+ // A return instruction
+ IT_RETURN,
+ // Any other type of instruction (in this case we don't care what it is)
+ IT_GENERIC,
+};
+
+// Lists IA-32 operand sizes in multiples of 8 bits
+enum OperandSize {
+ OS_ZERO = 0,
+ OS_BYTE = 1,
+ OS_WORD = 2,
+ OS_DOUBLE_WORD = 4,
+ OS_QUAD_WORD = 8,
+ OS_DOUBLE_QUAD_WORD = 16,
+ OS_32_BIT_POINTER = 32/8,
+ OS_48_BIT_POINTER = 48/8,
+ OS_SINGLE_PRECISION_FLOATING = 32/8,
+ OS_DOUBLE_PRECISION_FLOATING = 64/8,
+ OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80/8,
+ OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128/8,
+ OS_PSEUDO_DESCRIPTOR = 6
+};
+
+// Operand addressing methods from the IA-32 manual. The enAmMask value
+// is a mask for the rest. The other enumeration values are named for the
+// names given to the addressing methods in the manual, e.g. enAm_D is for
+// the D addressing method.
+//
+// The reason we use a full 4 bytes and a mask, is that we need to combine
+// these flags with the enOperandType to store the details
+// on the operand in a single integer.
+enum AddressingMethod {
+ AM_NOT_USED = 0, // This operand is not used for this instruction
+ AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration
+ AM_A = 0x00010000, // A addressing type
+ AM_C = 0x00020000, // C addressing type
+ AM_D = 0x00030000, // D addressing type
+ AM_E = 0x00040000, // E addressing type
+ AM_F = 0x00050000, // F addressing type
+ AM_G = 0x00060000, // G addressing type
+ AM_I = 0x00070000, // I addressing type
+ AM_J = 0x00080000, // J addressing type
+ AM_M = 0x00090000, // M addressing type
+ AM_O = 0x000A0000, // O addressing type
+ AM_P = 0x000B0000, // P addressing type
+ AM_Q = 0x000C0000, // Q addressing type
+ AM_R = 0x000D0000, // R addressing type
+ AM_S = 0x000E0000, // S addressing type
+ AM_T = 0x000F0000, // T addressing type
+ AM_V = 0x00100000, // V addressing type
+ AM_W = 0x00110000, // W addressing type
+ AM_X = 0x00120000, // X addressing type
+ AM_Y = 0x00130000, // Y addressing type
+ AM_REGISTER = 0x00140000, // Specific register is always used as this op
+ AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used
+};
+
+// Operand types from the IA-32 manual. The enOtMask value is
+// a mask for the rest. The rest of the values are named for the
+// names given to these operand types in the manual, e.g. enOt_ps
+// is for the ps operand type in the manual.
+//
+// The reason we use a full 4 bytes and a mask, is that we need
+// to combine these flags with the enAddressingMethod to store the details
+// on the operand in a single integer.
+enum OperandType {
+ OT_MASK = 0xFF000000,
+ OT_A = 0x01000000,
+ OT_B = 0x02000000,
+ OT_C = 0x03000000,
+ OT_D = 0x04000000,
+ OT_DQ = 0x05000000,
+ OT_P = 0x06000000,
+ OT_PI = 0x07000000,
+ OT_PS = 0x08000000, // actually unsupported for (we don't know its size)
+ OT_Q = 0x09000000,
+ OT_S = 0x0A000000,
+ OT_SS = 0x0B000000,
+ OT_SI = 0x0C000000,
+ OT_V = 0x0D000000,
+ OT_W = 0x0E000000,
+ OT_SD = 0x0F000000, // scalar double-precision floating-point value
+ OT_PD = 0x10000000, // double-precision floating point
+ // dummy "operand type" for address mode M - which doesn't specify
+ // operand type
+ OT_ADDRESS_MODE_M = 0x80000000
+};
+
+// Everything that's in an Opcode (see below) except the three
+// alternative opcode structs for different prefixes.
+struct SpecificOpcode {
+ // Index to continuation table, or 0 if this is the last
+ // byte in the opcode.
+ int table_index_;
+
+ // The opcode type
+ InstructionType type_;
+
+ // Description of the type of the dest, src and aux operands,
+ // put together from an enOperandType flag and an enAddressingMethod
+ // flag.
+ int flag_dest_;
+ int flag_source_;
+ int flag_aux_;
+
+ // We indicate the mnemonic for debugging purposes
+ const char* mnemonic_;
+};
+
+// The information we keep in our tables about each of the different
+// valid instructions recognized by the IA-32 architecture.
+struct Opcode {
+ // Index to continuation table, or 0 if this is the last
+ // byte in the opcode.
+ int table_index_;
+
+ // The opcode type
+ InstructionType type_;
+
+ // Description of the type of the dest, src and aux operands,
+ // put together from an enOperandType flag and an enAddressingMethod
+ // flag.
+ int flag_dest_;
+ int flag_source_;
+ int flag_aux_;
+
+ // We indicate the mnemonic for debugging purposes
+ const char* mnemonic_;
+
+ // Alternative opcode info if certain prefixes are specified.
+ // In most cases, all of these are zeroed-out. Only used if
+ // bPrefixDependent is true.
+ bool is_prefix_dependent_;
+ SpecificOpcode opcode_if_f2_prefix_;
+ SpecificOpcode opcode_if_f3_prefix_;
+ SpecificOpcode opcode_if_66_prefix_;
+};
+
+// Information about each table entry.
+struct OpcodeTable {
+ // Table of instruction entries
+ const Opcode* table_;
+ // How many bytes left to shift ModR/M byte <b>before</b> applying mask
+ unsigned char shift_;
+ // Mask to apply to byte being looked at before comparing to table
+ unsigned char mask_;
+ // Minimum/maximum indexes in table.
+ unsigned char min_lim_;
+ unsigned char max_lim_;
+};
+
+// Information about each entry in table used to decode ModR/M byte.
+struct ModrmEntry {
+ // Is the operand encoded as bytes in the instruction (rather than
+ // if it's e.g. a register in which case it's just encoded in the
+ // ModR/M byte)
+ bool is_encoded_in_instruction_;
+
+ // Is there a SIB byte? In this case we always need to decode it.
+ bool use_sib_byte_;
+
+ // What is the size of the operand (only important if it's encoded
+ // in the instruction)?
+ OperandSize operand_size_;
+};
+
+}; // namespace sidestep
+
+#endif // SANDBOX_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H__
diff --git a/chromium/sandbox/win/src/sidestep/preamble_patcher.h b/chromium/sandbox/win/src/sidestep/preamble_patcher.h
new file mode 100644
index 00000000000..3a0985ce9fa
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/preamble_patcher.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Definition of PreamblePatcher
+
+#ifndef SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__
+#define SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__
+
+#include <stddef.h>
+
+namespace sidestep {
+
+// Maximum size of the preamble stub. We overwrite at least the first 5
+// bytes of the function. Considering the worst case scenario, we need 4
+// bytes + the max instruction size + 5 more bytes for our jump back to
+// the original code. With that in mind, 32 is a good number :)
+const size_t kMaxPreambleStubSize = 32;
+
+// Possible results of patching/unpatching
+enum SideStepError {
+ SIDESTEP_SUCCESS = 0,
+ SIDESTEP_INVALID_PARAMETER,
+ SIDESTEP_INSUFFICIENT_BUFFER,
+ SIDESTEP_JUMP_INSTRUCTION,
+ SIDESTEP_FUNCTION_TOO_SMALL,
+ SIDESTEP_UNSUPPORTED_INSTRUCTION,
+ SIDESTEP_NO_SUCH_MODULE,
+ SIDESTEP_NO_SUCH_FUNCTION,
+ SIDESTEP_ACCESS_DENIED,
+ SIDESTEP_UNEXPECTED,
+};
+
+// Implements a patching mechanism that overwrites the first few bytes of
+// a function preamble with a jump to our hook function, which is then
+// able to call the original function via a specially-made preamble-stub
+// that imitates the action of the original preamble.
+//
+// Note that there are a number of ways that this method of patching can
+// fail. The most common are:
+// - If there is a jump (jxx) instruction in the first 5 bytes of
+// the function being patched, we cannot patch it because in the
+// current implementation we do not know how to rewrite relative
+// jumps after relocating them to the preamble-stub. Note that
+// if you really really need to patch a function like this, it
+// would be possible to add this functionality (but at some cost).
+// - If there is a return (ret) instruction in the first 5 bytes
+// we cannot patch the function because it may not be long enough
+// for the jmp instruction we use to inject our patch.
+// - If there is another thread currently executing within the bytes
+// that are copied to the preamble stub, it will crash in an undefined
+// way.
+//
+// If you get any other error than the above, you're either pointing the
+// patcher at an invalid instruction (e.g. into the middle of a multi-
+// byte instruction, or not at memory containing executable instructions)
+// or, there may be a bug in the disassembler we use to find
+// instruction boundaries.
+class PreamblePatcher {
+ public:
+ // Patches target_function to point to replacement_function using a provided
+ // preamble_stub of stub_size bytes.
+ // Returns An error code indicating the result of patching.
+ template <class T>
+ static SideStepError Patch(T target_function, T replacement_function,
+ void* preamble_stub, size_t stub_size) {
+ return RawPatchWithStub(target_function, replacement_function,
+ reinterpret_cast<unsigned char*>(preamble_stub),
+ stub_size, NULL);
+ }
+
+ private:
+
+ // Patches a function by overwriting its first few bytes with
+ // a jump to a different function. This is similar to the RawPatch
+ // function except that it uses the stub allocated by the caller
+ // instead of allocating it.
+ //
+ // To use this function, you first have to call VirtualProtect to make the
+ // target function writable at least for the duration of the call.
+ //
+ // target_function: A pointer to the function that should be
+ // patched.
+ //
+ // replacement_function: A pointer to the function that should
+ // replace the target function. The replacement function must have
+ // exactly the same calling convention and parameters as the original
+ // function.
+ //
+ // preamble_stub: A pointer to a buffer where the preamble stub
+ // should be copied. The size of the buffer should be sufficient to
+ // hold the preamble bytes.
+ //
+ // stub_size: Size in bytes of the buffer allocated for the
+ // preamble_stub
+ //
+ // bytes_needed: Pointer to a variable that receives the minimum
+ // number of bytes required for the stub. Can be set to NULL if you're
+ // not interested.
+ //
+ // Returns An error code indicating the result of patching.
+ static SideStepError RawPatchWithStub(void* target_function,
+ void *replacement_function,
+ unsigned char* preamble_stub,
+ size_t stub_size,
+ size_t* bytes_needed);
+};
+
+}; // namespace sidestep
+
+#endif // SANDBOX_SRC_SIDESTEP_PREAMBLE_PATCHER_H__
diff --git a/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp b/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp
new file mode 100644
index 00000000000..999d76bb21a
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp
@@ -0,0 +1,179 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Implementation of PreamblePatcher
+
+#include "sandbox/win/src/sidestep/preamble_patcher.h"
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sidestep/mini_disassembler.h"
+
+// Definitions of assembly statements we need
+#define ASM_JMP32REL 0xE9
+#define ASM_INT3 0xCC
+
+namespace {
+
+// Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there
+// is no attempt to optimize this code or have a general purpose function.
+// We don't want to call the crt from this code.
+inline void* RawMemcpy(void* destination, const void* source, size_t bytes) {
+ const char* from = reinterpret_cast<const char*>(source);
+ char* to = reinterpret_cast<char*>(destination);
+
+ for (size_t i = 0; i < bytes ; i++)
+ to[i] = from[i];
+
+ return destination;
+}
+
+// Very basic memset. We are filling 1 to 7 bytes most of the time, so there
+// is no attempt to optimize this code or have a general purpose function.
+// We don't want to call the crt from this code.
+inline void* RawMemset(void* destination, int value, size_t bytes) {
+ char* to = reinterpret_cast<char*>(destination);
+
+ for (size_t i = 0; i < bytes ; i++)
+ to[i] = static_cast<char>(value);
+
+ return destination;
+}
+
+} // namespace
+
+#define ASSERT(a, b) DCHECK_NT(a)
+
+namespace sidestep {
+
+SideStepError PreamblePatcher::RawPatchWithStub(
+ void* target_function,
+ void* replacement_function,
+ unsigned char* preamble_stub,
+ size_t stub_size,
+ size_t* bytes_needed) {
+ if ((NULL == target_function) ||
+ (NULL == replacement_function) ||
+ (NULL == preamble_stub)) {
+ ASSERT(false, (L"Invalid parameters - either pTargetFunction or "
+ L"pReplacementFunction or pPreambleStub were NULL."));
+ return SIDESTEP_INVALID_PARAMETER;
+ }
+
+ // TODO(V7:joi) Siggi and I just had a discussion and decided that both
+ // patching and unpatching are actually unsafe. We also discussed a
+ // method of making it safe, which is to freeze all other threads in the
+ // process, check their thread context to see if their eip is currently
+ // inside the block of instructions we need to copy to the stub, and if so
+ // wait a bit and try again, then unfreeze all threads once we've patched.
+ // Not implementing this for now since we're only using SideStep for unit
+ // testing, but if we ever use it for production code this is what we
+ // should do.
+ //
+ // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using
+ // FPU instructions, and on newer processors we could use cmpxchg8b or
+ // cmpxchg16b. So it might be possible to do the patching/unpatching
+ // atomically and avoid having to freeze other threads. Note though, that
+ // doing it atomically does not help if one of the other threads happens
+ // to have its eip in the middle of the bytes you change while you change
+ // them.
+ unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
+
+ // Let's disassemble the preamble of the target function to see if we can
+ // patch, and to see how much of the preamble we need to take. We need 5
+ // bytes for our jmp instruction, so let's find the minimum number of
+ // instructions to get 5 bytes.
+ MiniDisassembler disassembler;
+ unsigned int preamble_bytes = 0;
+ while (preamble_bytes < 5) {
+ InstructionType instruction_type =
+ disassembler.Disassemble(target + preamble_bytes, &preamble_bytes);
+ if (IT_JUMP == instruction_type) {
+ ASSERT(false, (L"Unable to patch because there is a jump instruction "
+ L"in the first 5 bytes."));
+ return SIDESTEP_JUMP_INSTRUCTION;
+ } else if (IT_RETURN == instruction_type) {
+ ASSERT(false, (L"Unable to patch because function is too short"));
+ return SIDESTEP_FUNCTION_TOO_SMALL;
+ } else if (IT_GENERIC != instruction_type) {
+ ASSERT(false, (L"Disassembler encountered unsupported instruction "
+ L"(either unused or unknown"));
+ return SIDESTEP_UNSUPPORTED_INSTRUCTION;
+ }
+ }
+
+ if (NULL != bytes_needed)
+ *bytes_needed = preamble_bytes + 5;
+
+ // Inv: preamble_bytes is the number of bytes (at least 5) that we need to
+ // take from the preamble to have whole instructions that are 5 bytes or more
+ // in size total. The size of the stub required is cbPreamble + size of
+ // jmp (5)
+ if (preamble_bytes + 5 > stub_size) {
+ NOTREACHED_NT();
+ return SIDESTEP_INSUFFICIENT_BUFFER;
+ }
+
+ // First, copy the preamble that we will overwrite.
+ RawMemcpy(reinterpret_cast<void*>(preamble_stub),
+ reinterpret_cast<void*>(target), preamble_bytes);
+
+ // Now, make a jmp instruction to the rest of the target function (minus the
+ // preamble bytes we moved into the stub) and copy it into our preamble-stub.
+ // find address to jump to, relative to next address after jmp instruction
+#pragma warning(push)
+#pragma warning(disable:4244)
+ // This assignment generates a warning because it is 32 bit specific.
+ int relative_offset_to_target_rest
+ = ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
+ (preamble_stub + preamble_bytes + 5));
+#pragma warning(pop)
+ // jmp (Jump near, relative, displacement relative to next instruction)
+ preamble_stub[preamble_bytes] = ASM_JMP32REL;
+ // copy the address
+ RawMemcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1),
+ reinterpret_cast<void*>(&relative_offset_to_target_rest), 4);
+
+ // Inv: preamble_stub points to assembly code that will execute the
+ // original function by first executing the first cbPreamble bytes of the
+ // preamble, then jumping to the rest of the function.
+
+ // Overwrite the first 5 bytes of the target function with a jump to our
+ // replacement function.
+ // (Jump near, relative, displacement relative to next instruction)
+ target[0] = ASM_JMP32REL;
+
+ // Find offset from instruction after jmp, to the replacement function.
+#pragma warning(push)
+#pragma warning(disable:4244)
+ int offset_to_replacement_function =
+ reinterpret_cast<unsigned char*>(replacement_function) -
+ reinterpret_cast<unsigned char*>(target) - 5;
+#pragma warning(pop)
+ // complete the jmp instruction
+ RawMemcpy(reinterpret_cast<void*>(target + 1),
+ reinterpret_cast<void*>(&offset_to_replacement_function), 4);
+ // Set any remaining bytes that were moved to the preamble-stub to INT3 so
+ // as not to cause confusion (otherwise you might see some strange
+ // instructions if you look at the disassembly, or even invalid
+ // instructions). Also, by doing this, we will break into the debugger if
+ // some code calls into this portion of the code. If this happens, it
+ // means that this function cannot be patched using this patcher without
+ // further thought.
+ if (preamble_bytes > 5) {
+ RawMemset(reinterpret_cast<void*>(target + 5), ASM_INT3,
+ preamble_bytes - 5);
+ }
+
+ // Inv: The memory pointed to by target_function now points to a relative
+ // jump instruction that jumps over to the preamble_stub. The preamble
+ // stub contains the first stub_size bytes of the original target
+ // function's preamble code, followed by a relative jump back to the next
+ // instruction after the first cbPreamble bytes.
+
+ return SIDESTEP_SUCCESS;
+}
+
+}; // namespace sidestep
+
+#undef ASSERT
diff --git a/chromium/sandbox/win/src/sidestep_resolver.cc b/chromium/sandbox/win/src/sidestep_resolver.cc
new file mode 100644
index 00000000000..828c000a7cf
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep_resolver.cc
@@ -0,0 +1,202 @@
+// Copyright (c) 2006-2008 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/sidestep_resolver.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sidestep/preamble_patcher.h"
+
+namespace {
+
+const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize;
+
+struct SidestepThunk {
+ char sidestep[kSizeOfSidestepStub]; // Storage for the sidestep stub.
+ int internal_thunk; // Dummy member to the beginning of the internal thunk.
+};
+
+struct SmartThunk {
+ const void* module_base; // Target module's base.
+ const void* interceptor; // Real interceptor.
+ SidestepThunk sidestep; // Standard sidestep thunk.
+};
+
+} // namespace
+
+namespace sandbox {
+
+NTSTATUS SidestepResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point,
+ thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ SidestepThunk* thunk = reinterpret_cast<SidestepThunk*>(thunk_storage);
+
+ size_t internal_bytes = storage_bytes - kSizeOfSidestepStub;
+ if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage,
+ interceptor_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ AutoProtectMemory memory;
+ ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch(
+ target_, reinterpret_cast<void*>(&thunk->internal_thunk), thunk_storage,
+ kSizeOfSidestepStub);
+
+ if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv)
+ return STATUS_BUFFER_TOO_SMALL;
+
+ if (sidestep::SIDESTEP_SUCCESS != rv)
+ return STATUS_UNSUCCESSFUL;
+
+ if (storage_used)
+ *storage_used = GetThunkSize();
+
+ return ret;
+}
+
+size_t SidestepResolverThunk::GetThunkSize() const {
+ return GetInternalThunkSize() + kSizeOfSidestepStub;
+}
+
+// This is basically a wrapper around the normal sidestep patch that extends
+// the thunk to use a chained interceptor. It uses the fact that
+// SetInternalThunk generates the code to pass as the first parameter whatever
+// it receives as original_function; we let SidestepResolverThunk set this value
+// to its saved code, and then we change it to our thunk data.
+NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ if (storage_bytes < GetThunkSize())
+ return STATUS_BUFFER_TOO_SMALL;
+
+ SmartThunk* thunk = reinterpret_cast<SmartThunk*>(thunk_storage);
+ thunk->module_base = target_module;
+
+ NTSTATUS ret;
+ if (interceptor_entry_point) {
+ thunk->interceptor = interceptor_entry_point;
+ } else {
+ ret = ResolveInterceptor(interceptor_module, interceptor_name,
+ &thunk->interceptor);
+ if (!NT_SUCCESS(ret))
+ return ret;
+ }
+
+ // Perform a standard sidestep patch on the last part of the thunk, but point
+ // to our internal smart interceptor.
+ size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep);
+ ret = SidestepResolverThunk::Setup(target_module, interceptor_module,
+ target_name, NULL, &SmartStub,
+ &thunk->sidestep, standard_bytes, NULL);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ // Fix the internal thunk to pass the whole buffer to the interceptor.
+ SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(),
+ thunk_storage, &SmartStub);
+
+ if (storage_used)
+ *storage_used = GetThunkSize();
+
+ return ret;
+}
+
+size_t SmartSidestepResolverThunk::GetThunkSize() const {
+ return GetInternalThunkSize() + kSizeOfSidestepStub +
+ offsetof(SmartThunk, sidestep);
+}
+
+// This code must basically either call the intended interceptor or skip the
+// call and invoke instead the original function. In any case, we are saving
+// the registers that may be trashed by our c++ code.
+//
+// This function is called with a first parameter inserted by us, that points
+// to our SmartThunk. When we call the interceptor we have to replace this
+// parameter with the one expected by that function (stored inside our
+// structure); on the other hand, when we skip the interceptor we have to remove
+// that extra argument before calling the original function.
+//
+// When we skip the interceptor, the transformation of the stack looks like:
+// On Entry: On Use: On Exit:
+// [param 2] = first real argument [param 2] (esp+1c) [param 2]
+// [param 1] = our SmartThunk [param 1] (esp+18) [ret address]
+// [ret address] = real caller [ret address] (esp+14) [xxx]
+// [xxx] [addr to jump to] (esp+10) [xxx]
+// [xxx] [saved eax] [xxx]
+// [xxx] [saved ebx] [xxx]
+// [xxx] [saved ecx] [xxx]
+// [xxx] [saved edx] [xxx]
+__declspec(naked)
+void SmartSidestepResolverThunk::SmartStub() {
+ __asm {
+ push eax // Space for the jump.
+ push eax // Save registers.
+ push ebx
+ push ecx
+ push edx
+ mov ebx, [esp + 0x18] // First parameter = SmartThunk.
+ mov edx, [esp + 0x14] // Get the return address.
+ mov eax, [ebx]SmartThunk.module_base
+ push edx
+ push eax
+ call SmartSidestepResolverThunk::IsInternalCall
+ add esp, 8
+
+ test eax, eax
+ lea edx, [ebx]SmartThunk.sidestep // The original function.
+ jz call_interceptor
+
+ // Skip this call
+ mov ecx, [esp + 0x14] // Return address.
+ mov [esp + 0x18], ecx // Remove first parameter.
+ mov [esp + 0x10], edx
+ pop edx // Restore registers.
+ pop ecx
+ pop ebx
+ pop eax
+ ret 4 // Jump to original function.
+
+ call_interceptor:
+ mov ecx, [ebx]SmartThunk.interceptor
+ mov [esp + 0x18], edx // Replace first parameter.
+ mov [esp + 0x10], ecx
+ pop edx // Restore registers.
+ pop ecx
+ pop ebx
+ pop eax
+ ret // Jump to original function.
+ }
+}
+
+bool SmartSidestepResolverThunk::IsInternalCall(const void* base,
+ void* return_address) {
+ DCHECK_NT(base);
+ DCHECK_NT(return_address);
+
+ base::win::PEImage pe(base);
+ if (pe.GetImageSectionFromAddr(return_address))
+ return true;
+ return false;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sidestep_resolver.h b/chromium/sandbox/win/src/sidestep_resolver.h
new file mode 100644
index 00000000000..30d8cb3fbe1
--- /dev/null
+++ b/chromium/sandbox/win/src/sidestep_resolver.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2010 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_SIDESTEP_RESOLVER_H__
+#define SANDBOX_SRC_SIDESTEP_RESOLVER_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform sidestep interceptions.
+class SidestepResolverThunk : public ResolverThunk {
+ public:
+ SidestepResolverThunk() {}
+ virtual ~SidestepResolverThunk() {}
+
+ // Implementation of Resolver::Setup.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ // Implementation of Resolver::GetThunkSize.
+ virtual size_t GetThunkSize() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SidestepResolverThunk);
+};
+
+// This is the concrete resolver used to perform smart sidestep interceptions.
+// This means basically a sidestep interception that skips the interceptor when
+// the caller resides on the same dll being intercepted. It is intended as
+// a helper only, because that determination is not infallible.
+class SmartSidestepResolverThunk : public SidestepResolverThunk {
+ public:
+ SmartSidestepResolverThunk() {}
+ virtual ~SmartSidestepResolverThunk() {}
+
+ // Implementation of Resolver::Setup.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ // Implementation of Resolver::GetThunkSize.
+ virtual size_t GetThunkSize() const;
+
+ private:
+ // Performs the actual call to the interceptor if the conditions are correct
+ // (as determined by IsInternalCall).
+ static void SmartStub();
+
+ // Returns true if return_address is inside the module loaded at base.
+ static bool IsInternalCall(const void* base, void* return_address);
+
+ DISALLOW_COPY_AND_ASSIGN(SmartSidestepResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SIDESTEP_RESOLVER_H__
diff --git a/chromium/sandbox/win/src/sync_dispatcher.cc b/chromium/sandbox/win/src/sync_dispatcher.cc
new file mode 100644
index 00000000000..eb1c1e4740f
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_dispatcher.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_dispatcher.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/sync_policy.h"
+
+namespace sandbox {
+
+SyncDispatcher::SyncDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IPC_CREATEEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::CreateEvent)
+ };
+
+ static const IPCCall open_params = {
+ {IPC_OPENEVENT_TAG, WCHAR_TYPE, ULONG_TYPE, ULONG_TYPE},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::OpenEvent)
+ };
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_params);
+}
+
+bool SyncDispatcher::SetupService(InterceptionManager* manager,
+ int service) {
+ if (IPC_CREATEEVENT_TAG == service)
+ return INTERCEPT_EAT(manager, L"kernel32.dll", CreateEventW,
+ CREATE_EVENT_ID, 20);
+
+ if (IPC_OPENEVENT_TAG == service)
+ return INTERCEPT_EAT(manager, L"kernel32.dll", OpenEventW,
+ OPEN_EVENT_ID, 16);
+
+ return false;
+}
+
+bool SyncDispatcher::CreateEvent(IPCInfo* ipc, std::wstring* name,
+ DWORD manual_reset, DWORD initial_state) {
+ const wchar_t* event_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(event_name);
+
+ EvalResult result = policy_base_->EvalPolicy(IPC_CREATEEVENT_TAG,
+ params.GetBase());
+ HANDLE handle = NULL;
+ DWORD ret = SyncPolicy::CreateEventAction(result, *ipc->client_info, *name,
+ manual_reset, initial_state,
+ &handle);
+ // Return operation status on the IPC.
+ ipc->return_info.win32_result = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool SyncDispatcher::OpenEvent(IPCInfo* ipc, std::wstring* name,
+ DWORD desired_access, DWORD inherit_handle) {
+ const wchar_t* event_name = name->c_str();
+
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(event_name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result = policy_base_->EvalPolicy(IPC_OPENEVENT_TAG,
+ params.GetBase());
+ HANDLE handle = NULL;
+ DWORD ret = SyncPolicy::OpenEventAction(result, *ipc->client_info, *name,
+ desired_access, inherit_handle,
+ &handle);
+ // Return operation status on the IPC.
+ ipc->return_info.win32_result = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sync_dispatcher.h b/chromium/sandbox/win/src/sync_dispatcher.h
new file mode 100644
index 00000000000..13c8b9d31a0
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_dispatcher.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2010 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_SYNC_DISPATCHER_H_
+#define SANDBOX_SRC_SYNC_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles sync-related IPC calls.
+class SyncDispatcher : public Dispatcher {
+ public:
+ explicit SyncDispatcher(PolicyBase* policy_base);
+ ~SyncDispatcher() {}
+
+ // Dispatcher interface.
+ virtual bool SetupService(InterceptionManager* manager, int service);
+
+private:
+ // Processes IPC requests coming from calls to CreateEvent in the target.
+ bool CreateEvent(IPCInfo* ipc, std::wstring* name, DWORD manual_reset,
+ DWORD initial_state);
+
+ // Processes IPC requests coming from calls to OpenEvent in the target.
+ bool OpenEvent(IPCInfo* ipc, std::wstring* name, DWORD desired_access,
+ DWORD inherit_handle);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(SyncDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_DISPATCHER_H_
diff --git a/chromium/sandbox/win/src/sync_interception.cc b/chromium/sandbox/win/src/sync_interception.cc
new file mode 100644
index 00000000000..d23572389e3
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_interception.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+HANDLE WINAPI TargetCreateEventW(CreateEventWFunction orig_CreateEvent,
+ LPSECURITY_ATTRIBUTES security_attributes,
+ BOOL manual_reset, BOOL initial_state,
+ LPCWSTR name) {
+ // Check if the process can create it first.
+ HANDLE handle = orig_CreateEvent(security_attributes, manual_reset,
+ initial_state, name);
+ DWORD original_error = ::GetLastError();
+ if (NULL != handle)
+ return handle;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return NULL;
+
+ do {
+ if (security_attributes)
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(name);
+
+ if (!QueryBroker(IPC_CREATEEVENT_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_CREATEEVENT_TAG, name, manual_reset,
+ initial_state, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ return answer.handle;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return NULL;
+}
+
+// Interception of OpenEventW on the child process.
+// It should never be called directly
+HANDLE WINAPI TargetOpenEventW(OpenEventWFunction orig_OpenEvent,
+ ACCESS_MASK desired_access, BOOL inherit_handle,
+ LPCWSTR name) {
+ // Check if the process can open it first.
+ HANDLE handle = orig_OpenEvent(desired_access, inherit_handle, name);
+ DWORD original_error = ::GetLastError();
+ if (NULL != handle)
+ return handle;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return NULL;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ uint32 inherit_handle_ipc = inherit_handle;
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ if (!QueryBroker(IPC_OPENEVENT_TAG, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IPC_OPENEVENT_TAG, name, desired_access,
+ inherit_handle_ipc, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ return answer.handle;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return NULL;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sync_interception.h b/chromium/sandbox/win/src/sync_interception.h
new file mode 100644
index 00000000000..6c5c46e9478
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_interception.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2008 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_SYNC_INTERCEPTION_H__
+#define SANDBOX_SRC_SYNC_INTERCEPTION_H__
+
+namespace sandbox {
+
+extern "C" {
+
+typedef HANDLE (WINAPI *CreateEventWFunction) (
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ LPCWSTR lpName);
+
+typedef HANDLE (WINAPI *OpenEventWFunction) (
+ BOOL bManualReset,
+ BOOL bInitialState,
+ LPCWSTR lpName);
+
+// Interception of CreateEvent on the child process.
+SANDBOX_INTERCEPT HANDLE WINAPI TargetCreateEventW(
+ CreateEventWFunction orig_CreateEvent,
+ LPSECURITY_ATTRIBUTES security_attributes, BOOL manual_reset,
+ BOOL initial_state, LPCWSTR name);
+
+// Interception of OpenEvent on the child process.
+SANDBOX_INTERCEPT HANDLE WINAPI TargetOpenEventW(
+ OpenEventWFunction orig_OpenEvent, ACCESS_MASK desired_access,
+ BOOL inherit_handle, LPCWSTR name);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_INTERCEPTION_H__
diff --git a/chromium/sandbox/win/src/sync_policy.cc b/chromium/sandbox/win/src/sync_policy.cc
new file mode 100644
index 00000000000..87ef0bdd15a
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_policy.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2006-2008 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 <string>
+
+#include "sandbox/win/src/sync_policy.h"
+
+#include "base/logging.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+bool SyncPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ if (TargetPolicy::EVENTS_ALLOW_ANY != semantics &&
+ TargetPolicy::EVENTS_ALLOW_READONLY != semantics) {
+ // Other flags are not valid for sync policy yet.
+ NOTREACHED();
+ return false;
+ }
+
+ // Add the open rule.
+ EvalResult result = ASK_BROKER;
+ PolicyRule open(result);
+
+ if (!open.AddStringMatch(IF, OpenEventParams::NAME, name, CASE_INSENSITIVE))
+ return false;
+
+ if (TargetPolicy::EVENTS_ALLOW_READONLY == semantics) {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write.
+ DWORD allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL;
+ DWORD restricted_flags = ~allowed_flags;
+ open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND);
+ }
+
+ if (!policy->AddRule(IPC_OPENEVENT_TAG, &open))
+ return false;
+
+ // If it's not a read only, add the create rule.
+ if (TargetPolicy::EVENTS_ALLOW_READONLY != semantics) {
+ PolicyRule create(result);
+ if (!create.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE))
+ return false;
+
+ if (!policy->AddRule(IPC_CREATEEVENT_TAG, &create))
+ return false;
+ }
+
+ return true;
+}
+
+DWORD SyncPolicy::CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &event_name,
+ uint32 manual_reset,
+ uint32 initial_state,
+ HANDLE *handle) {
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE local_handle = ::CreateEvent(NULL, manual_reset, initial_state,
+ event_name.c_str());
+ if (NULL == local_handle)
+ return ::GetLastError();
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+DWORD SyncPolicy::OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &event_name,
+ uint32 desired_access,
+ uint32 inherit_handle,
+ HANDLE *handle) {
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE local_handle = ::OpenEvent(desired_access, FALSE,
+ event_name.c_str());
+ if (NULL == local_handle)
+ return ::GetLastError();
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, inherit_handle,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sync_policy.h b/chromium/sandbox/win/src/sync_policy.h
new file mode 100644
index 00000000000..2b8b4227025
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_policy.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2006-2008 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_SYNC_POLICY_H__
+#define SANDBOX_SRC_SYNC_POLICY_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.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 sync policy
+class SyncPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for sync calls, in particular open or create actions.
+ // name is the sync 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);
+
+ // Performs the desired policy action on a request.
+ // client_info is the target process that is making the request and
+ // eval_result is the desired policy action to accomplish.
+ static DWORD CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &event_name,
+ uint32 manual_reset,
+ uint32 initial_state,
+ HANDLE *handle);
+ static DWORD OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring &event_name,
+ uint32 desired_access,
+ uint32 inherit_handle,
+ HANDLE *handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_POLICY_H__
diff --git a/chromium/sandbox/win/src/sync_policy_test.cc b/chromium/sandbox/win/src/sync_policy_test.cc
new file mode 100644
index 00000000000..87d03f1028f
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_policy_test.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_policy_test.h"
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ DWORD desired_access = SYNCHRONIZE;
+ if (L'f' == argv[0][0])
+ desired_access = EVENT_ALL_ACCESS;
+
+ base::win::ScopedHandle event_open(::OpenEvent(
+ desired_access, FALSE, argv[1]));
+ DWORD error_open = ::GetLastError();
+
+ if (event_open.Get())
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error_open ||
+ ERROR_BAD_PATHNAME == error_open)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t **argv) {
+ if (argc < 2 || argc > 3)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ wchar_t *event_name = NULL;
+ if (3 == argc)
+ event_name = argv[2];
+
+ BOOL manual_reset = FALSE;
+ BOOL initial_state = FALSE;
+ if (L't' == argv[0][0])
+ manual_reset = TRUE;
+ if (L't' == argv[1][0])
+ initial_state = TRUE;
+
+ base::win::ScopedHandle event_create(::CreateEvent(
+ NULL, manual_reset, initial_state, event_name));
+ DWORD error_create = ::GetLastError();
+ base::win::ScopedHandle event_open;
+ if (event_name)
+ event_open.Set(::OpenEvent(EVENT_ALL_ACCESS, FALSE, event_name));
+
+ if (event_create.Get()) {
+ DWORD wait = ::WaitForSingleObject(event_create.Get(), 0);
+ if (initial_state && WAIT_OBJECT_0 != wait)
+ return SBOX_TEST_FAILED;
+
+ if (!initial_state && WAIT_TIMEOUT != wait)
+ return SBOX_TEST_FAILED;
+ }
+
+ if (event_name) {
+ // Both event_open and event_create have to be valid.
+ if (event_open.Get() && event_create)
+ return SBOX_TEST_SUCCEEDED;
+
+ if (event_open.Get() && !event_create || !event_open.Get() && event_create)
+ return SBOX_TEST_FAILED;
+ } else {
+ // Only event_create has to be valid.
+ if (event_create.Get())
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ if (ERROR_ACCESS_DENIED == error_create ||
+ ERROR_BAD_PATHNAME == error_create)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+// Tests the creation of events using all the possible combinations.
+TEST(SyncPolicyTest, TestEvent) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY,
+ L"test1"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY,
+ L"test2"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f test2"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t test2"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test4"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test4"));
+}
+
+// Tests opening events with read only access.
+TEST(SyncPolicyTest, TestEventReadOnly) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY,
+ L"test1"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY,
+ L"test2"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY,
+ L"test5"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY,
+ L"test6"));
+
+ base::win::ScopedHandle handle1(::CreateEvent(NULL, FALSE, FALSE, L"test1"));
+ base::win::ScopedHandle handle2(::CreateEvent(NULL, FALSE, FALSE, L"test2"));
+ base::win::ScopedHandle handle3(::CreateEvent(NULL, FALSE, FALSE, L"test3"));
+ base::win::ScopedHandle handle4(::CreateEvent(NULL, FALSE, FALSE, L"test4"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open s test2"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open s test4"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test5"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test6"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test5"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test6"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/sync_policy_test.h b/chromium/sandbox/win/src/sync_policy_test.h
new file mode 100644
index 00000000000..4f354b35be6
--- /dev/null
+++ b/chromium/sandbox/win/src/sync_policy_test.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
+#define SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
+
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+// Opens the named event received on argv[1]. The requested access is
+// EVENT_ALL_ACCESS if argv[0] starts with 'f', or SYNCHRONIZE otherwise.
+SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
diff --git a/chromium/sandbox/win/src/target_interceptions.cc b/chromium/sandbox/win/src/target_interceptions.cc
new file mode 100644
index 00000000000..e6b0dcf780c
--- /dev/null
+++ b/chromium/sandbox/win/src/target_interceptions.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2006-2008 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/target_interceptions.h"
+
+#include "sandbox/win/src/interception_agent.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is
+// required for this dll, this functions patches it.
+NTSTATUS WINAPI TargetNtMapViewOfSection(
+ NtMapViewOfSectionFunction orig_MapViewOfSection, HANDLE section,
+ HANDLE process, PVOID *base, ULONG_PTR zero_bits, SIZE_T commit_size,
+ PLARGE_INTEGER offset, PSIZE_T view_size, SECTION_INHERIT inherit,
+ ULONG allocation_type, ULONG protect) {
+ NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits,
+ commit_size, offset, view_size, inherit,
+ allocation_type, protect);
+
+ static int s_load_count = 0;
+ if (1 == s_load_count) {
+ SandboxFactory::GetTargetServices()->GetState()->SetKernel32Loaded();
+ s_load_count = 2;
+ }
+
+ do {
+ if (!NT_SUCCESS(ret))
+ break;
+
+ if (!InitHeap())
+ break;
+
+ if (!IsSameProcess(process))
+ break;
+
+ if (!IsValidImageSection(section, base, offset, view_size))
+ break;
+
+ UINT image_flags;
+ UNICODE_STRING* module_name =
+ GetImageInfoFromModule(reinterpret_cast<HMODULE>(*base), &image_flags);
+ UNICODE_STRING* file_name = GetBackingFilePath(*base);
+
+ if ((!module_name) && (image_flags & MODULE_HAS_CODE)) {
+ // If the module has no exports we retrieve the module name from the
+ // full path of the mapped section.
+ module_name = ExtractModuleName(file_name);
+ }
+
+ InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
+
+ if (agent) {
+ if (!agent->OnDllLoad(file_name, module_name, *base)) {
+ // Interception agent is demanding to un-map the module.
+ g_nt.UnmapViewOfSection(process, *base);
+ ret = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (module_name)
+ operator delete(module_name, NT_ALLOC);
+
+ if (file_name)
+ operator delete(file_name, NT_ALLOC);
+
+ } while (false);
+
+ if (!s_load_count)
+ s_load_count = 1;
+
+ return ret;
+}
+
+NTSTATUS WINAPI TargetNtUnmapViewOfSection(
+ NtUnmapViewOfSectionFunction orig_UnmapViewOfSection, HANDLE process,
+ PVOID base) {
+ NTSTATUS ret = orig_UnmapViewOfSection(process, base);
+
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ if (!IsSameProcess(process))
+ return ret;
+
+ InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
+
+ if (agent)
+ agent->OnDllUnload(base);
+
+ return ret;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/target_interceptions.h b/chromium/sandbox/win/src/target_interceptions.h
new file mode 100644
index 00000000000..f4805fec5ca
--- /dev/null
+++ b/chromium/sandbox/win/src/target_interceptions.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2006-2008 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/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_TARGET_INTERCEPTIONS_H__
+#define SANDBOX_SRC_TARGET_INTERCEPTIONS_H__
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtMapViewOfSection(
+ NtMapViewOfSectionFunction orig_MapViewOfSection, HANDLE section,
+ HANDLE process, PVOID *base, ULONG_PTR zero_bits, SIZE_T commit_size,
+ PLARGE_INTEGER offset, PSIZE_T view_size, SECTION_INHERIT inherit,
+ ULONG allocation_type, ULONG protect);
+
+// Interception of NtUnmapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being unloaded, so we can clean up our interceptions.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection(
+ NtUnmapViewOfSectionFunction orig_UnmapViewOfSection, HANDLE process,
+ PVOID base);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_TARGET_INTERCEPTIONS_H__
diff --git a/chromium/sandbox/win/src/target_process.cc b/chromium/sandbox/win/src/target_process.cc
new file mode 100644
index 00000000000..9300ccee076
--- /dev/null
+++ b/chromium/sandbox/win/src/target_process.cc
@@ -0,0 +1,337 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_process.h"
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/pe_image.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+
+namespace {
+
+void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
+ if (!source || !size)
+ return;
+ memcpy(dest, source, size);
+ sandbox::PolicyGlobal* policy =
+ reinterpret_cast<sandbox::PolicyGlobal*>(dest);
+
+ size_t offset = reinterpret_cast<size_t>(source);
+
+ for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
+ size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
+ if (buffer) {
+ buffer -= offset;
+ policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
+ }
+ }
+}
+
+}
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size;
+SANDBOX_INTERCEPT size_t g_shared_policy_size;
+
+// Returns the address of the main exe module in memory taking in account
+// address space layout randomization.
+void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) {
+ HMODULE exe = ::LoadLibrary(exe_name);
+ if (NULL == exe)
+ return exe;
+
+ base::win::PEImage pe(exe);
+ if (!pe.VerifyMagic()) {
+ ::FreeLibrary(exe);
+ return exe;
+ }
+ PIMAGE_NT_HEADERS nt_header = pe.GetNTHeaders();
+ char* base = reinterpret_cast<char*>(entry_point) -
+ nt_header->OptionalHeader.AddressOfEntryPoint;
+
+ ::FreeLibrary(exe);
+ return base;
+}
+
+
+TargetProcess::TargetProcess(HANDLE initial_token, HANDLE lockdown_token,
+ HANDLE job, ThreadProvider* thread_pool)
+ // This object owns everything initialized here except thread_pool and
+ // the job_ handle. The Job handle is closed by BrokerServices and results
+ // eventually in a call to our dtor.
+ : lockdown_token_(lockdown_token),
+ initial_token_(initial_token),
+ job_(job),
+ thread_pool_(thread_pool),
+ base_address_(NULL) {
+}
+
+TargetProcess::~TargetProcess() {
+ DWORD exit_code = 0;
+ // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
+ // will take effect only when the context changes. As far as the testing went,
+ // this wait was enough to switch context and kill the processes in the job.
+ // If this process is already dead, the function will return without waiting.
+ // TODO(nsylvain): If the process is still alive at the end, we should kill
+ // it. http://b/893891
+ // For now, this wait is there only to do a best effort to prevent some leaks
+ // from showing up in purify.
+ if (sandbox_process_info_.IsValid()) {
+ ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
+ if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
+ &exit_code) || (STILL_ACTIVE == exit_code)) {
+ // It is an error to destroy this object while the target process is still
+ // alive because we need to destroy the IPC subsystem and cannot risk to
+ // have an IPC reach us after this point.
+ if (shared_section_.IsValid())
+ shared_section_.Take();
+ SharedMemIPCServer* server = ipc_server_.release();
+ sandbox_process_info_.TakeProcessHandle();
+ return;
+ }
+ }
+
+ // ipc_server_ references our process handle, so make sure the former is shut
+ // down before the latter is closed (by ScopedProcessInformation).
+ ipc_server_.reset();
+}
+
+// Creates the target (child) process suspended and assigns it to the job
+// object.
+DWORD TargetProcess::Create(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info) {
+ 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));
+
+ // Start the target process suspended.
+ DWORD flags =
+ CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
+
+ if (startup_info.has_extended_startup_info())
+ flags |= EXTENDED_STARTUPINFO_PRESENT;
+
+ if (job_ && base::win::GetVersion() < base::win::VERSION_WIN8) {
+ // Windows 8 implements nested jobs, but for older systems we need to
+ // break out of any job we're in to enforce our restrictions.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ base::win::ScopedProcessInformation process_info;
+
+ if (!::CreateProcessAsUserW(lockdown_token_,
+ exe_path,
+ cmd_line.get(),
+ NULL, // No security attribute.
+ NULL, // No thread attribute.
+ inherit_handles,
+ flags,
+ NULL, // Use the environment of the caller.
+ NULL, // Use current directory of the caller.
+ startup_info.startup_info(),
+ process_info.Receive())) {
+ return ::GetLastError();
+ }
+ lockdown_token_.Close();
+
+ DWORD win_result = ERROR_SUCCESS;
+
+ if (job_) {
+ // Assign the suspended target to the windows job object.
+ if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
+ win_result = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return win_result;
+ }
+ }
+
+ if (initial_token_.IsValid()) {
+ // Change the token of the main thread of the new process for the
+ // impersonation token with more rights. This allows the target to start;
+ // otherwise it will crash too early for us to help.
+ HANDLE temp_thread = process_info.thread_handle();
+ if (!::SetThreadToken(&temp_thread, initial_token_)) {
+ win_result = ::GetLastError();
+ // It might be a security breach if we let the target run outside the job
+ // so kill it before it causes damage.
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return win_result;
+ }
+ initial_token_.Close();
+ }
+
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_ALL;
+ if (!::GetThreadContext(process_info.thread_handle(), &context)) {
+ win_result = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return win_result;
+ }
+
+#if defined(_WIN64)
+ void* entry_point = reinterpret_cast<void*>(context.Rcx);
+#else
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // This cast generates a warning because it is 32 bit specific.
+ void* entry_point = reinterpret_cast<void*>(context.Eax);
+#pragma warning(pop)
+#endif // _WIN64
+
+ if (!target_info->DuplicateFrom(process_info)) {
+ win_result = ::GetLastError(); // This may or may not be correct.
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return win_result;
+ }
+
+ base_address_ = GetBaseAddress(exe_path, entry_point);
+ sandbox_process_info_.Set(process_info.Take());
+ return win_result;
+}
+
+ResultCode TargetProcess::TransferVariable(const char* name, void* address,
+ size_t size) {
+ if (!sandbox_process_info_.IsValid())
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ void* child_var = address;
+
+#if SANDBOX_EXPORTS
+ HMODULE module = ::LoadLibrary(exe_name_.get());
+ if (NULL == module)
+ return SBOX_ERROR_GENERIC;
+
+ child_var = ::GetProcAddress(module, name);
+ ::FreeLibrary(module);
+
+ if (NULL == child_var)
+ return SBOX_ERROR_GENERIC;
+
+ size_t offset = reinterpret_cast<char*>(child_var) -
+ reinterpret_cast<char*>(module);
+ child_var = reinterpret_cast<char*>(MainModule()) + offset;
+#else
+ UNREFERENCED_PARAMETER(name);
+#endif
+
+ SIZE_T written;
+ if (!::WriteProcessMemory(sandbox_process_info_.process_handle(),
+ child_var, address, size, &written))
+ return SBOX_ERROR_GENERIC;
+
+ if (written != size)
+ return SBOX_ERROR_GENERIC;
+
+ return SBOX_ALL_OK;
+}
+
+// Construct the IPC server and the IPC dispatcher. When the target does
+// an IPC it will eventually call the dispatcher.
+DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy,
+ uint32 shared_IPC_size, uint32 shared_policy_size) {
+ // We need to map the shared memory on the target. This is necessary for
+ // any IPC that needs to take place, even if the target has not yet hit
+ // the main( ) function or even has initialized the CRT. So here we set
+ // the handle to the shared section. The target on the first IPC must do
+ // the rest, which boils down to calling MapViewofFile()
+
+ // We use this single memory pool for IPC and for policy.
+ DWORD shared_mem_size = static_cast<DWORD>(shared_IPC_size +
+ shared_policy_size);
+ shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
+ PAGE_READWRITE | SEC_COMMIT,
+ 0, shared_mem_size, NULL));
+ if (!shared_section_.IsValid()) {
+ return ::GetLastError();
+ }
+
+ DWORD access = FILE_MAP_READ | FILE_MAP_WRITE;
+ HANDLE target_shared_section;
+ if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_,
+ sandbox_process_info_.process_handle(),
+ &target_shared_section, access, FALSE, 0)) {
+ return ::GetLastError();
+ }
+
+ void* shared_memory = ::MapViewOfFile(shared_section_,
+ FILE_MAP_WRITE|FILE_MAP_READ,
+ 0, 0, 0);
+ if (NULL == shared_memory) {
+ return ::GetLastError();
+ }
+
+ CopyPolicyToTarget(policy, shared_policy_size,
+ reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
+
+ ResultCode ret;
+ // Set the global variables in the target. These are not used on the broker.
+ g_shared_section = target_shared_section;
+ ret = TransferVariable("g_shared_section", &g_shared_section,
+ sizeof(g_shared_section));
+ g_shared_section = NULL;
+ if (SBOX_ALL_OK != ret) {
+ return (SBOX_ERROR_GENERIC == ret)?
+ ::GetLastError() : ERROR_INVALID_FUNCTION;
+ }
+ g_shared_IPC_size = shared_IPC_size;
+ ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
+ sizeof(g_shared_IPC_size));
+ g_shared_IPC_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ return (SBOX_ERROR_GENERIC == ret) ?
+ ::GetLastError() : ERROR_INVALID_FUNCTION;
+ }
+ g_shared_policy_size = shared_policy_size;
+ ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
+ sizeof(g_shared_policy_size));
+ g_shared_policy_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ return (SBOX_ERROR_GENERIC == ret) ?
+ ::GetLastError() : ERROR_INVALID_FUNCTION;
+ }
+
+ ipc_server_.reset(
+ new SharedMemIPCServer(sandbox_process_info_.process_handle(),
+ sandbox_process_info_.process_id(),
+ job_, thread_pool_, ipc_dispatcher));
+
+ if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ // After this point we cannot use this handle anymore.
+ ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
+
+ return ERROR_SUCCESS;
+}
+
+void TargetProcess::Terminate() {
+ if (!sandbox_process_info_.IsValid())
+ return;
+
+ ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
+}
+
+TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
+ TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL);
+ PROCESS_INFORMATION process_info = {};
+ process_info.hProcess = process;
+ target->sandbox_process_info_.Set(process_info);
+ target->base_address_ = base_address;
+ return target;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/target_process.h b/chromium/sandbox/win/src/target_process.h
new file mode 100644
index 00000000000..0b72d98f231
--- /dev/null
+++ b/chromium/sandbox/win/src/target_process.h
@@ -0,0 +1,132 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_TARGET_PROCESS_H_
+#define SANDBOX_WIN_SRC_TARGET_PROCESS_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace base {
+namespace win {
+
+class StartupInformation;
+
+}; // namespace win
+}; // namespace base
+
+namespace sandbox {
+
+class AttributeList;
+class SharedMemIPCServer;
+class ThreadProvider;
+
+// TargetProcess models a target instance (child process). Objects of this
+// class are owned by the Policy used to create them.
+class TargetProcess {
+ public:
+ // The constructor takes ownership of |initial_token| and |lockdown_token|.
+ TargetProcess(HANDLE initial_token, HANDLE lockdown_token, HANDLE job,
+ ThreadProvider* thread_pool);
+ ~TargetProcess();
+
+ // TODO(cpu): Currently there does not seem to be a reason to implement
+ // reference counting for this class since is internal, but kept the
+ // the same interface so the interception framework does not need to be
+ // touched at this point.
+ void AddRef() {}
+ void Release() {}
+
+ // Creates the new target process. The process is created suspended.
+ DWORD Create(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info);
+
+ // Destroys the target process.
+ void Terminate();
+
+ // Creates the IPC objects such as the BrokerDispatcher and the
+ // IPC server. The IPC server uses the services of the thread_pool.
+ DWORD Init(Dispatcher* ipc_dispatcher, void* policy,
+ uint32 shared_IPC_size, uint32 shared_policy_size);
+
+ // Returns the handle to the target process.
+ HANDLE Process() const {
+ return sandbox_process_info_.process_handle();
+ }
+
+ // Returns the handle to the job object that the target process belongs to.
+ HANDLE Job() const {
+ return job_;
+ }
+
+ // Returns the address of the target main exe. This is used by the
+ // interceptions framework.
+ HMODULE MainModule() const {
+ return reinterpret_cast<HMODULE>(base_address_);
+ }
+
+ // Returns the name of the executable.
+ const wchar_t* Name() const {
+ return exe_name_.get();
+ }
+
+ // Returns the process id.
+ DWORD ProcessId() const {
+ return sandbox_process_info_.process_id();
+ }
+
+ // Returns the handle to the main thread.
+ HANDLE MainThread() const {
+ return sandbox_process_info_.thread_handle();
+ }
+
+ // Transfers a 32-bit variable between the broker and the target.
+ ResultCode TransferVariable(const char* name, void* address, size_t size);
+
+ private:
+ // Details of the target process.
+ base::win::ScopedProcessInformation sandbox_process_info_;
+ // The token associated with the process. It provides the core of the
+ // sbox security.
+ base::win::ScopedHandle lockdown_token_;
+ // The token given to the initial thread so that the target process can
+ // start. It has more powers than the lockdown_token.
+ base::win::ScopedHandle initial_token_;
+ // Kernel handle to the shared memory used by the IPC server.
+ base::win::ScopedHandle shared_section_;
+ // Job object containing the target process.
+ HANDLE job_;
+ // Reference to the IPC subsystem.
+ scoped_ptr<SharedMemIPCServer> ipc_server_;
+ // Provides the threads used by the IPC. This class does not own this pointer.
+ ThreadProvider* thread_pool_;
+ // Base address of the main executable
+ void* base_address_;
+ // Full name of the target executable.
+ scoped_ptr_malloc<wchar_t> exe_name_;
+
+ // Function used for testing.
+ friend TargetProcess* MakeTestTargetProcess(HANDLE process,
+ HMODULE base_address);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TargetProcess);
+};
+
+// Creates a mock TargetProcess used for testing interceptions.
+// TODO(cpu): It seems that this method is not going to be used anymore.
+TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address);
+
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_TARGET_PROCESS_H_
diff --git a/chromium/sandbox/win/src/target_services.cc b/chromium/sandbox/win/src/target_services.cc
new file mode 100644
index 00000000000..03813c8feb7
--- /dev/null
+++ b/chromium/sandbox/win/src/target_services.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_services.h"
+
+#include <process.h>
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/handle_interception.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+// Flushing a cached key is triggered by just opening the key and closing the
+// resulting handle. RegDisablePredefinedCache() is the documented way to flush
+// HKCU so do not use it with this function.
+bool FlushRegKey(HKEY root) {
+ HKEY key;
+ if (ERROR_SUCCESS == ::RegOpenKeyExW(root, NULL, 0, MAXIMUM_ALLOWED, &key)) {
+ if (ERROR_SUCCESS != ::RegCloseKey(key))
+ return false;
+ }
+ return true;
+}
+
+// This function forces advapi32.dll to release some internally cached handles
+// that were made during calls to RegOpenkey and RegOpenKeyEx if it is called
+// with a more restrictive token. Returns true if the flushing is succesful
+// although this behavior is undocumented and there is no guarantee that in
+// fact this will happen in future versions of windows.
+bool FlushCachedRegHandles() {
+ return (FlushRegKey(HKEY_LOCAL_MACHINE) &&
+ FlushRegKey(HKEY_CLASSES_ROOT) &&
+ FlushRegKey(HKEY_USERS));
+}
+
+// Checks if we have handle entries pending and runs the closer.
+bool CloseOpenHandles() {
+ if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) {
+ sandbox::HandleCloserAgent handle_closer;
+
+ handle_closer.InitializeHandlesToClose();
+ if (!handle_closer.CloseHandles())
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level =
+ INTEGRITY_LEVEL_LAST;
+SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations = 0;
+
+TargetServicesBase::TargetServicesBase() {
+}
+
+ResultCode TargetServicesBase::Init() {
+ process_state_.SetInitCalled();
+ return SBOX_ALL_OK;
+}
+
+// Failure here is a breach of security so the process is terminated.
+void TargetServicesBase::LowerToken() {
+ if (ERROR_SUCCESS !=
+ SetProcessIntegrityLevel(g_shared_delayed_integrity_level))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY);
+ process_state_.SetRevertedToSelf();
+ // If the client code as called RegOpenKey, advapi32.dll has cached some
+ // handles. The following code gets rid of them.
+ if (!::RevertToSelf())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN);
+ if (!FlushCachedRegHandles())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES);
+ if (ERROR_SUCCESS != ::RegDisablePredefinedCache())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE);
+ if (!CloseOpenHandles())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES);
+ // Enabling mitigations must happen last otherwise handle closing breaks
+ if (g_shared_delayed_mitigations &&
+ !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION);
+}
+
+ProcessState* TargetServicesBase::GetState() {
+ return &process_state_;
+}
+
+TargetServicesBase* TargetServicesBase::GetInstance() {
+ static TargetServicesBase instance;
+ return &instance;
+}
+
+// The broker services a 'test' IPC service with the IPC_PING_TAG tag.
+bool TargetServicesBase::TestIPCPing(int version) {
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory) {
+ return false;
+ }
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ if (1 == version) {
+ uint32 tick1 = ::GetTickCount();
+ uint32 cookie = 717115;
+ ResultCode code = CrossCall(ipc, IPC_PING1_TAG, cookie, &answer);
+
+ if (SBOX_ALL_OK != code) {
+ return false;
+ }
+ // We should get two extended returns values from the IPC, one is the
+ // tick count on the broker and the other is the cookie times two.
+ if ((answer.extended_count != 2)) {
+ return false;
+ }
+ // We test the first extended answer to be within the bounds of the tick
+ // count only if there was no tick count wraparound.
+ uint32 tick2 = ::GetTickCount();
+ if (tick2 >= tick1) {
+ if ((answer.extended[0].unsigned_int < tick1) ||
+ (answer.extended[0].unsigned_int > tick2)) {
+ return false;
+ }
+ }
+
+ if (answer.extended[1].unsigned_int != cookie * 2) {
+ return false;
+ }
+ } else if (2 == version) {
+ uint32 cookie = 717111;
+ InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie));
+ ResultCode code = CrossCall(ipc, IPC_PING2_TAG, counted_buffer, &answer);
+
+ if (SBOX_ALL_OK != code) {
+ return false;
+ }
+ if (cookie != 717111 * 3) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool ProcessState::IsKernel32Loaded() {
+ return process_state_ != 0;
+}
+
+bool ProcessState::InitCalled() {
+ return process_state_ > 1;
+}
+
+bool ProcessState::RevertedToSelf() {
+ return process_state_ > 2;
+}
+
+void ProcessState::SetKernel32Loaded() {
+ if (!process_state_)
+ process_state_ = 1;
+}
+
+void ProcessState::SetInitCalled() {
+ if (process_state_ < 2)
+ process_state_ = 2;
+}
+
+void ProcessState::SetRevertedToSelf() {
+ if (process_state_ < 3)
+ process_state_ = 3;
+}
+
+ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ return sandbox::DuplicateHandleProxy(source_handle, target_process_id,
+ target_handle, desired_access, options);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/target_services.h b/chromium/sandbox/win/src/target_services.h
new file mode 100644
index 00000000000..70f173b2118
--- /dev/null
+++ b/chromium/sandbox/win/src/target_services.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_TARGET_SERVICES_H__
+#define SANDBOX_SRC_TARGET_SERVICES_H__
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+class ProcessState {
+ public:
+ ProcessState() : process_state_(0) {}
+
+ // Returns true if kernel32.dll has been loaded.
+ bool IsKernel32Loaded();
+
+ // Returns true if main has been called.
+ bool InitCalled();
+
+ // Returns true if LowerToken has been called.
+ bool RevertedToSelf();
+
+ // Set the current state.
+ void SetKernel32Loaded();
+ void SetInitCalled();
+ void SetRevertedToSelf();
+
+ public:
+ int process_state_;
+ DISALLOW_COPY_AND_ASSIGN(ProcessState);
+};
+
+// This class is an implementation of the TargetServices.
+// Look in the documentation of sandbox::TargetServices for more info.
+// Do NOT add a destructor to this class without changing the implementation of
+// the factory method.
+class TargetServicesBase : public TargetServices {
+ public:
+ TargetServicesBase();
+
+ // Public interface of TargetServices.
+ virtual ResultCode Init();
+ virtual void LowerToken();
+ virtual ProcessState* GetState();
+ virtual ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options);
+
+ // Factory method.
+ static TargetServicesBase* GetInstance();
+
+ // Sends a simple IPC Message that has a well-known answer. Returns true
+ // if the IPC was successful and false otherwise. There are 2 versions of
+ // this test: 1 and 2. The first one send a simple message while the
+ // second one send a message with an in/out param.
+ bool TestIPCPing(int version);
+
+ private:
+ ProcessState process_state_;
+ DISALLOW_COPY_AND_ASSIGN(TargetServicesBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_TARGET_SERVICES_H__
diff --git a/chromium/sandbox/win/src/threadpool_unittest.cc b/chromium/sandbox/win/src/threadpool_unittest.cc
new file mode 100644
index 00000000000..f439810851f
--- /dev/null
+++ b/chromium/sandbox/win/src/threadpool_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright (c) 2006-2008 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/win2k_threadpool.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+void __stdcall EmptyCallBack(void*, unsigned char) {
+}
+
+void __stdcall TestCallBack(void* context, unsigned char) {
+ HANDLE event = reinterpret_cast<HANDLE>(context);
+ ::SetEvent(event);
+}
+
+namespace sandbox {
+
+// Test that register and unregister work, part 1.
+TEST(IPCTest, ThreadPoolRegisterTest1) {
+ Win2kThreadPool thread_pool;
+
+ EXPECT_EQ(0, thread_pool.OutstandingWaits());
+
+ HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+ HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+
+ uint32 context = 0;
+ EXPECT_FALSE(thread_pool.RegisterWait(0, event1, EmptyCallBack, &context));
+ EXPECT_EQ(0, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event1, EmptyCallBack, &context));
+ EXPECT_EQ(1, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event2, EmptyCallBack, &context));
+ EXPECT_EQ(2, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(this));
+ EXPECT_EQ(0, thread_pool.OutstandingWaits());
+
+ EXPECT_EQ(TRUE, ::CloseHandle(event1));
+ EXPECT_EQ(TRUE, ::CloseHandle(event2));
+}
+
+// Test that register and unregister work, part 2.
+TEST(IPCTest, ThreadPoolRegisterTest2) {
+ Win2kThreadPool thread_pool;
+
+ HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+ HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+
+ uint32 context = 0;
+ uint32 c1 = 0;
+ uint32 c2 = 0;
+
+ EXPECT_TRUE(thread_pool.RegisterWait(&c1, event1, EmptyCallBack, &context));
+ EXPECT_EQ(1, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.RegisterWait(&c2, event2, EmptyCallBack, &context));
+ EXPECT_EQ(2, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2));
+ EXPECT_EQ(1, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2));
+ EXPECT_EQ(1, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c1));
+ EXPECT_EQ(0, thread_pool.OutstandingWaits());
+
+ EXPECT_EQ(TRUE, ::CloseHandle(event1));
+ EXPECT_EQ(TRUE, ::CloseHandle(event2));
+}
+
+// Test that the thread pool has at least a thread that services an event.
+// Test that when the event is un-registered is no longer serviced.
+TEST(IPCTest, ThreadPoolSignalAndWaitTest) {
+ Win2kThreadPool thread_pool;
+
+ // The events are auto reset and start not signaled.
+ HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+ HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL);
+
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event1, TestCallBack, event2));
+
+ EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE));
+ EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE));
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(this));
+ EXPECT_EQ(0, thread_pool.OutstandingWaits());
+
+ EXPECT_EQ(WAIT_TIMEOUT, ::SignalObjectAndWait(event1, event2, 1000, FALSE));
+
+ EXPECT_EQ(TRUE, ::CloseHandle(event1));
+ EXPECT_EQ(TRUE, ::CloseHandle(event2));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/unload_dll_test.cc b/chromium/sandbox/win/src/unload_dll_test.cc
new file mode 100644
index 00000000000..620016c6bd8
--- /dev/null
+++ b/chromium/sandbox/win/src/unload_dll_test.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Loads and or unloads a DLL passed in the second parameter of argv.
+// The first parameter of argv is 'L' = load, 'U' = unload or 'B' for both.
+SBOX_TESTS_COMMAND int UseOneDLL(int argc, wchar_t **argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ int rv = SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ wchar_t option = (argv[0])[0];
+ if ((option == L'L') || (option == L'B')) {
+ HMODULE module1 = ::LoadLibraryW(argv[1]);
+ rv = (module1 == NULL) ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+
+ if ((option == L'U') || (option == L'B')) {
+ HMODULE module2 = ::GetModuleHandleW(argv[1]);
+ rv = ::FreeLibrary(module2) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ }
+ return rv;
+}
+
+// Opens an event passed as the first parameter of argv.
+SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ base::win::ScopedHandle event_open(::OpenEvent(SYNCHRONIZE, FALSE, argv[0]));
+ return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+}
+
+// Flaky on windows, see http://crbug.com/80569.
+TEST(UnloadDllTest, DISABLED_BaselineAvicapDll) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ runner.SetTimeout(2000);
+ // Add a sync rule, because that ensures that the interception agent has
+ // more than one item in its internal table.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"t0001"));
+
+ // Note for the puzzled: avicap32.dll is a 64-bit dll in 64-bit versions of
+ // windows so this test and the others just work.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll"));
+}
+
+// Flaky on windows, see http://crbug.com/80569.
+TEST(UnloadDllTest, DISABLED_UnloadAviCapDllNoPatching) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ runner.SetTimeout(2000);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->AddDllToUnload(L"avicap32.dll");
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll"));
+}
+
+// Flaky: http://crbug.com/38404
+TEST(UnloadDllTest, DISABLED_UnloadAviCapDllWithPatching) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(BEFORE_REVERT);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->AddDllToUnload(L"avicap32.dll");
+
+ base::win::ScopedHandle handle1(::CreateEvent(
+ NULL, FALSE, FALSE, L"tst0001"));
+
+ // Add a couple of rules that ensures that the interception agent add EAT
+ // patching on the client which makes sure that the unload dll record does
+ // not interact badly with them.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"tst0001"));
+
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"SimpleOpenEvent tst0001"));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/win2k_threadpool.cc b/chromium/sandbox/win/src/win2k_threadpool.cc
new file mode 100644
index 00000000000..24cbacf2b46
--- /dev/null
+++ b/chromium/sandbox/win/src/win2k_threadpool.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/win2k_threadpool.h"
+
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+bool Win2kThreadPool::RegisterWait(const void* cookie, HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) {
+ if (0 == cookie) {
+ return false;
+ }
+ HANDLE pool_object = NULL;
+ // create a wait for a kernel object, with no timeout
+ if (!::RegisterWaitForSingleObject(&pool_object, waitable_object, callback,
+ context, INFINITE, WT_EXECUTEDEFAULT)) {
+ return false;
+ }
+ PoolObject pool_obj = {cookie, pool_object};
+ AutoLock lock(&lock_);
+ pool_objects_.push_back(pool_obj);
+ return true;
+}
+
+bool Win2kThreadPool::UnRegisterWaits(void* cookie) {
+ if (0 == cookie) {
+ return false;
+ }
+ AutoLock lock(&lock_);
+ bool success = true;
+ PoolObjects::iterator it = pool_objects_.begin();
+ while (it != pool_objects_.end()) {
+ if (it->cookie == cookie) {
+ HANDLE wait = it->wait;
+ it = pool_objects_.erase(it);
+ success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0);
+ } else {
+ ++it;
+ }
+ }
+ return success;
+}
+
+size_t Win2kThreadPool::OutstandingWaits() {
+ AutoLock lock(&lock_);
+ return pool_objects_.size();
+}
+
+Win2kThreadPool::~Win2kThreadPool() {
+ // Here we used to unregister all the pool wait handles. Now, following the
+ // rest of the code we avoid lengthy or blocking calls given that the process
+ // is being torn down.
+ ::DeleteCriticalSection(&lock_);
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/win2k_threadpool.h b/chromium/sandbox/win/src/win2k_threadpool.h
new file mode 100644
index 00000000000..0abb358e690
--- /dev/null
+++ b/chromium/sandbox/win/src/win2k_threadpool.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN2K_THREADPOOL_H_
+#define SANDBOX_SRC_WIN2K_THREADPOOL_H_
+
+#include <list>
+#include <algorithm>
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Win2kThreadPool a simple implementation of a thread provider as required
+// for the sandbox IPC subsystem. See sandbox\crosscall_server.h for the details
+// and requirements of this interface.
+//
+// Implementing the thread provider as a thread pool is desirable in the case
+// of shared memory IPC because it can generate a large number of waitable
+// events: as many as channels. A thread pool does not create a thread per
+// event, instead maintains a few idle threads but can create more if the need
+// arises.
+//
+// This implementation simply thunks to the nice thread pool API of win2k.
+class Win2kThreadPool : public ThreadProvider {
+ public:
+ Win2kThreadPool() {
+ ::InitializeCriticalSection(&lock_);
+ }
+ virtual ~Win2kThreadPool();
+
+ virtual bool RegisterWait(const void* cookie, HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context);
+
+ virtual bool UnRegisterWaits(void* cookie);
+
+ // Returns the total number of wait objects associated with
+ // the thread pool.
+ size_t OutstandingWaits();
+
+ private:
+ // record to keep track of a wait and its associated cookie.
+ struct PoolObject {
+ const void* cookie;
+ HANDLE wait;
+ };
+ // The list of pool wait objects.
+ typedef std::list<PoolObject> PoolObjects;
+ PoolObjects pool_objects_;
+ // This lock protects the list of pool wait objects.
+ CRITICAL_SECTION lock_;
+ DISALLOW_COPY_AND_ASSIGN(Win2kThreadPool);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WIN2K_THREADPOOL_H_
diff --git a/chromium/sandbox/win/src/win_utils.cc b/chromium/sandbox/win/src/win_utils.cc
new file mode 100644
index 00000000000..f5c479a35a2
--- /dev/null
+++ b/chromium/sandbox/win/src/win_utils.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2011 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/win_utils.h"
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace {
+
+// Holds the information about a known registry key.
+struct KnownReservedKey {
+ const wchar_t* name;
+ HKEY key;
+};
+
+// Contains all the known registry key by name and by handle.
+const KnownReservedKey kKnownKey[] = {
+ { L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT },
+ { L"HKEY_CURRENT_USER", HKEY_CURRENT_USER },
+ { L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
+ { L"HKEY_USERS", HKEY_USERS},
+ { L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
+ { L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT},
+ { L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT},
+ { L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
+ { L"HKEY_DYN_DATA", HKEY_DYN_DATA}
+};
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const std::wstring& path) {
+ size_t start = 0;
+ if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))
+ start = sandbox::kNTPrefixLen;
+
+ const wchar_t kPipe[] = L"pipe\\";
+ return (0 == path.compare(start, arraysize(kPipe) - 1, kPipe));
+}
+
+} // namespace
+
+namespace sandbox {
+
+HKEY GetReservedKeyFromName(const std::wstring& name) {
+ for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
+ if (name == kKnownKey[i].name)
+ return kKnownKey[i].key;
+ }
+
+ return NULL;
+}
+
+bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) {
+ for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
+ if (name.find(kKnownKey[i].name) == 0) {
+ HKEY key;
+ DWORD disposition;
+ if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, NULL, 0,
+ MAXIMUM_ALLOWED, NULL, &key,
+ &disposition))
+ return false;
+
+ bool result = GetPathFromHandle(key, resolved_name);
+ ::RegCloseKey(key);
+
+ if (!result)
+ return false;
+
+ *resolved_name += name.substr(wcslen(kKnownKey[i].name));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+DWORD IsReparsePoint(const std::wstring& full_path, bool* result) {
+ std::wstring path = full_path;
+
+ // Remove the nt prefix.
+ if (0 == path.compare(0, kNTPrefixLen, kNTPrefix))
+ path = path.substr(kNTPrefixLen);
+
+ // Check if it's a pipe. We can't query the attributes of a pipe.
+ if (IsPipe(path)) {
+ *result = FALSE;
+ return ERROR_SUCCESS;
+ }
+
+ std::wstring::size_type last_pos = std::wstring::npos;
+
+ do {
+ path = path.substr(0, last_pos);
+
+ DWORD attributes = ::GetFileAttributes(path.c_str());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ DWORD error = ::GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND &&
+ error != ERROR_PATH_NOT_FOUND &&
+ error != ERROR_INVALID_NAME) {
+ // Unexpected error.
+ NOTREACHED();
+ return error;
+ }
+ } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
+ // This is a reparse point.
+ *result = true;
+ return ERROR_SUCCESS;
+ }
+
+ last_pos = path.rfind(L'\\');
+ } while (last_pos != std::wstring::npos);
+
+ *result = false;
+ return ERROR_SUCCESS;
+}
+
+// We get a |full_path| of the form \??\c:\some\foo\bar, and the name that
+// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
+bool SameObject(HANDLE handle, const wchar_t* full_path) {
+ std::wstring path(full_path);
+ DCHECK(!path.empty());
+
+ // Check if it's a pipe.
+ if (IsPipe(path))
+ return true;
+
+ std::wstring actual_path;
+ if (!GetPathFromHandle(handle, &actual_path))
+ return false;
+
+ // This may end with a backslash.
+ const wchar_t kBackslash = '\\';
+ if (path[path.length() - 1] == kBackslash)
+ path = path.substr(0, path.length() - 1);
+
+ // Perfect match (case-insesitive check).
+ if (0 == _wcsicmp(actual_path.c_str(), path.c_str()))
+ return true;
+
+ // Look for the drive letter.
+ size_t colon_pos = path.find(L':');
+ if (colon_pos == 0 || colon_pos == std::wstring::npos)
+ return false;
+
+ // Only one character for the drive.
+ if (colon_pos > 1 && path[colon_pos - 2] != kBackslash)
+ return false;
+
+ // We only need 3 chars, but let's alloc a buffer for four.
+ wchar_t drive[4] = {0};
+ wchar_t vol_name[MAX_PATH];
+ memcpy(drive, &path[colon_pos - 1], 2 * sizeof(*drive));
+
+ // We'll get a double null terminated string.
+ DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH);
+ if (vol_length < 2 || vol_length == MAX_PATH)
+ return false;
+
+ // Ignore the nulls at the end.
+ vol_length = static_cast<DWORD>(wcslen(vol_name));
+
+ // The two paths should be the same length.
+ if (vol_length + path.size() - (colon_pos + 1) != actual_path.size())
+ return false;
+
+ // Check up to the drive letter.
+ if (0 != _wcsnicmp(actual_path.c_str(), vol_name, vol_length))
+ return false;
+
+ // Check the path after the drive letter.
+ if (0 != _wcsicmp(&actual_path[vol_length], &path[colon_pos + 1]))
+ return false;
+
+ return true;
+}
+
+bool ConvertToLongPath(const std::wstring& short_path,
+ std::wstring* long_path) {
+ // Check if the path is a NT path.
+ bool is_nt_path = false;
+ std::wstring path = short_path;
+ if (0 == path.compare(0, kNTPrefixLen, kNTPrefix)) {
+ path = path.substr(kNTPrefixLen);
+ is_nt_path = true;
+ }
+
+ DWORD size = MAX_PATH;
+ scoped_ptr<wchar_t[]> long_path_buf(new wchar_t[size]);
+
+ DWORD return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(),
+ size);
+ while (return_value >= size) {
+ size *= 2;
+ long_path_buf.reset(new wchar_t[size]);
+ return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), size);
+ }
+
+ DWORD last_error = ::GetLastError();
+ if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error ||
+ ERROR_PATH_NOT_FOUND == last_error ||
+ ERROR_INVALID_NAME == last_error)) {
+ // The file does not exist, but maybe a sub path needs to be expanded.
+ std::wstring::size_type last_slash = path.rfind(L'\\');
+ if (std::wstring::npos == last_slash)
+ return false;
+
+ std::wstring begin = path.substr(0, last_slash);
+ std::wstring end = path.substr(last_slash);
+ if (!ConvertToLongPath(begin, &begin))
+ return false;
+
+ // Ok, it worked. Let's reset the return value.
+ path = begin + end;
+ return_value = 1;
+ } else if (0 != return_value) {
+ path = long_path_buf.get();
+ }
+
+ if (return_value != 0) {
+ if (is_nt_path) {
+ *long_path = kNTPrefix;
+ *long_path += path;
+ } else {
+ *long_path = path;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GetPathFromHandle(HANDLE handle, std::wstring* path) {
+ NtQueryObjectFunction NtQueryObject = NULL;
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ OBJECT_NAME_INFORMATION initial_buffer;
+ OBJECT_NAME_INFORMATION* name = &initial_buffer;
+ ULONG size = sizeof(initial_buffer);
+ // Query the name information a first time to get the size of the name.
+ NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size,
+ &size);
+
+ scoped_ptr<OBJECT_NAME_INFORMATION> name_ptr;
+ if (size) {
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(new BYTE[size]);
+ name_ptr.reset(name);
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
+ }
+
+ if (STATUS_SUCCESS != status)
+ return false;
+
+ path->assign(name->ObjectName.Buffer, name->ObjectName.Length /
+ sizeof(name->ObjectName.Buffer[0]));
+ return true;
+}
+
+bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) {
+ HANDLE file = ::CreateFileW(path.c_str(), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+ bool rv = GetPathFromHandle(file, nt_path);
+ ::CloseHandle(file);
+ return rv;
+}
+
+bool WriteProtectedChildMemory(HANDLE child_process, void* address,
+ const void* buffer, size_t length) {
+ // First, remove the protections.
+ DWORD old_protection;
+ if (!::VirtualProtectEx(child_process, address, length,
+ PAGE_WRITECOPY, &old_protection))
+ return false;
+
+ SIZE_T written;
+ bool ok = ::WriteProcessMemory(child_process, address, buffer, length,
+ &written) && (length == written);
+
+ // Always attempt to restore the original protection.
+ if (!::VirtualProtectEx(child_process, address, length,
+ old_protection, &old_protection))
+ return false;
+
+ return ok;
+}
+
+}; // 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 HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName);
+
+ 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(*function_ptr);
+}
diff --git a/chromium/sandbox/win/src/win_utils.h b/chromium/sandbox/win/src/win_utils.h
new file mode 100644
index 00000000000..a80bb81fa24
--- /dev/null
+++ b/chromium/sandbox/win/src/win_utils.h
@@ -0,0 +1,110 @@
+// Copyright (c) 2006-2010 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_WIN_UTILS_H_
+#define SANDBOX_SRC_WIN_UTILS_H_
+
+#include <windows.h>
+#include <string>
+#include "base/basictypes.h"
+
+namespace sandbox {
+
+// Prefix for path used by NT calls.
+const wchar_t kNTPrefix[] = L"\\??\\";
+const size_t kNTPrefixLen = arraysize(kNTPrefix) - 1;
+
+const wchar_t kNTObjManPrefix[] = L"\\Device\\";
+const size_t kNTObjManPrefixLen = arraysize(kNTObjManPrefix) - 1;
+
+// Automatically acquires and releases a lock when the object is
+// is destroyed.
+class AutoLock {
+ public:
+ // Acquires the lock.
+ explicit AutoLock(CRITICAL_SECTION *lock) : lock_(lock) {
+ ::EnterCriticalSection(lock);
+ };
+
+ // Releases the lock;
+ ~AutoLock() {
+ ::LeaveCriticalSection(lock_);
+ };
+
+ private:
+ CRITICAL_SECTION *lock_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock);
+};
+
+// Basic implementation of a singleton which calls the destructor
+// when the exe is shutting down or the DLL is being unloaded.
+template <typename Derived>
+class SingletonBase {
+ public:
+ static Derived* GetInstance() {
+ static Derived* instance = NULL;
+ if (NULL == instance) {
+ instance = new Derived();
+ // Microsoft CRT extension. In an exe this this called after
+ // winmain returns, in a dll is called in DLL_PROCESS_DETACH
+ _onexit(OnExit);
+ }
+ return instance;
+ }
+
+ private:
+ // this is the function that gets called by the CRT when the
+ // process is shutting down.
+ static int __cdecl OnExit() {
+ delete GetInstance();
+ return 0;
+ }
+};
+
+// Convert a short path (C:\path~1 or \\??\\c:\path~1) to the long version of
+// the path. If the path is not a valid filesystem path, the function returns
+// false and the output parameter is not modified.
+bool ConvertToLongPath(const std::wstring& short_path, std::wstring* long_path);
+
+// Sets result to true if the path contains a reparse point. The return value
+// is ERROR_SUCCESS when the function succeeds or the appropriate error code
+// when the function fails.
+// This function is not smart. It looks for each element in the path and
+// returns true if any of them is a reparse point.
+DWORD IsReparsePoint(const std::wstring& full_path, bool* result);
+
+// Returns true if the handle corresponds to the object pointed by this path.
+bool SameObject(HANDLE handle, const wchar_t* full_path);
+
+// Resolves a handle to an nt path. Returns true if the handle can be resolved.
+bool GetPathFromHandle(HANDLE handle, std::wstring* path);
+
+// Resolves a win32 path to an nt path using GetPathFromHandle. The path must
+// exist. Returs true if the translation was succesful.
+bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path);
+
+// Translates a reserved key name to its handle.
+// For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE.
+// Returns NULL if the name does not represent any reserved key name.
+HKEY GetReservedKeyFromName(const std::wstring& name);
+
+// Resolves a user-readable registry path to a system-readable registry path.
+// For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to
+// \\registry\\machine\\software\\microsoft. Returns false if the path
+// cannot be resolved.
+bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name);
+
+// Writes |length| bytes from the provided |buffer| into the address space of
+// |child_process|, at the specified |address|, preserving the original write
+// protection attributes. Returns true on success.
+bool WriteProtectedChildMemory(HANDLE child_process, void* address,
+ const void* buffer, size_t length);
+
+} // namespace sandbox
+
+// Resolves a function name in NTDLL to a function pointer. The second parameter
+// is a pointer to the function pointer.
+void ResolveNTFunctionPtr(const char* name, void* ptr);
+
+#endif // SANDBOX_SRC_WIN_UTILS_H_
diff --git a/chromium/sandbox/win/src/win_utils_unittest.cc b/chromium/sandbox/win/src/win_utils_unittest.cc
new file mode 100644
index 00000000000..72653167eb1
--- /dev/null
+++ b/chromium/sandbox/win/src/win_utils_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(WinUtils, IsReparsePoint) {
+ using sandbox::IsReparsePoint;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t my_folder[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(my_folder));
+ ASSERT_TRUE(::CreateDirectory(my_folder, NULL));
+
+ bool result = true;
+ EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(my_folder, &result));
+ EXPECT_FALSE(result);
+
+ // We have to fix Bug 32224 to pass this test.
+ std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar";
+ // EXPECT_EQ(ERROR_PATH_NOT_FOUND, IsReparsePoint(not_found, &result));
+
+ std::wstring new_file = std::wstring(my_folder) + L"\\foo";
+ EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result));
+ EXPECT_FALSE(result);
+
+ // Replace the directory with a reparse point to %temp%.
+ HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ EXPECT_NE(INVALID_HANDLE_VALUE, dir);
+
+ std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+
+ EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result));
+ EXPECT_TRUE(result);
+
+ EXPECT_TRUE(DeleteReparsePoint(dir));
+ EXPECT_TRUE(::CloseHandle(dir));
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
+
+TEST(WinUtils, SameObject) {
+ using sandbox::SameObject;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t my_folder[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(my_folder));
+ ASSERT_TRUE(::CreateDirectory(my_folder, NULL));
+
+ std::wstring folder(my_folder);
+ std::wstring file_name = folder + L"\\foo.txt";
+ const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+ base::win::ScopedHandle file(CreateFile(
+ file_name.c_str(), GENERIC_WRITE, kSharing, NULL, CREATE_ALWAYS,
+ FILE_FLAG_DELETE_ON_CLOSE, NULL));
+
+ EXPECT_TRUE(file.IsValid());
+ std::wstring file_name_nt1 = std::wstring(L"\\??\\") + file_name;
+ std::wstring file_name_nt2 = std::wstring(L"\\??\\") + folder + L"\\FOO.txT";
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str()));
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str()));
+
+ file.Close();
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
diff --git a/chromium/sandbox/win/src/window.cc b/chromium/sandbox/win/src/window.cc
new file mode 100644
index 00000000000..d8de9672726
--- /dev/null
+++ b/chromium/sandbox/win/src/window.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2011 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/window.h"
+
+#include <aclapi.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace {
+
+// Gets the security attributes of a window object referenced by |handle|. The
+// lpSecurityDescriptor member of the SECURITY_ATTRIBUTES parameter returned
+// must be freed using LocalFree by the caller.
+bool GetSecurityAttributes(HANDLE handle, SECURITY_ATTRIBUTES* attributes) {
+ attributes->bInheritHandle = FALSE;
+ attributes->nLength = sizeof(SECURITY_ATTRIBUTES);
+
+ PACL dacl = NULL;
+ DWORD result = ::GetSecurityInfo(handle, SE_WINDOW_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
+ NULL, &attributes->lpSecurityDescriptor);
+ if (ERROR_SUCCESS == result)
+ return true;
+
+ return false;
+}
+
+}
+
+namespace sandbox {
+
+ResultCode CreateAltWindowStation(HWINSTA* winsta) {
+ // Get the security attributes from the current window station; we will
+ // use this as the base security attributes for the new window station.
+ SECURITY_ATTRIBUTES attributes = {0};
+ if (!GetSecurityAttributes(::GetProcessWindowStation(), &attributes)) {
+ return SBOX_ERROR_CANNOT_CREATE_WINSTATION;
+ }
+
+ // Create the window station using NULL for the name to ask the os to
+ // generate it.
+ // TODO(nsylvain): don't ask for WINSTA_ALL_ACCESS if we don't need to.
+ *winsta = ::CreateWindowStationW(NULL, 0, WINSTA_ALL_ACCESS, &attributes);
+ LocalFree(attributes.lpSecurityDescriptor);
+
+ if (*winsta)
+ return SBOX_ALL_OK;
+
+ return SBOX_ERROR_CANNOT_CREATE_WINSTATION;
+}
+
+ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) {
+ std::wstring desktop_name = L"sbox_alternate_desktop_";
+
+ // Append the current PID to the desktop name.
+ wchar_t buffer[16];
+ _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X",
+ ::GetCurrentProcessId());
+ desktop_name += buffer;
+
+ // Get the security attributes from the current desktop, we will use this as
+ // the base security attributes for the new desktop.
+ SECURITY_ATTRIBUTES attributes = {0};
+ if (!GetSecurityAttributes(GetThreadDesktop(GetCurrentThreadId()),
+ &attributes)) {
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+
+ // Back up the current window station, in case we need to switch it.
+ HWINSTA current_winsta = ::GetProcessWindowStation();
+
+ if (winsta) {
+ // We need to switch to the alternate window station before creating the
+ // desktop.
+ if (!::SetProcessWindowStation(winsta)) {
+ ::LocalFree(attributes.lpSecurityDescriptor);
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ }
+
+ // Create the destkop.
+ // TODO(nsylvain): don't ask for GENERIC_ALL if we don't need to.
+ *desktop = ::CreateDesktop(desktop_name.c_str(), NULL, NULL, 0, GENERIC_ALL,
+ &attributes);
+ ::LocalFree(attributes.lpSecurityDescriptor);
+
+ if (winsta) {
+ // Revert to the right window station.
+ if (!::SetProcessWindowStation(current_winsta)) {
+ return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION;
+ }
+ }
+
+ if (*desktop)
+ return SBOX_ALL_OK;
+
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+}
+
+std::wstring GetWindowObjectName(HANDLE handle) {
+ // Get the size of the name.
+ DWORD size = 0;
+ ::GetUserObjectInformation(handle, UOI_NAME, NULL, 0, &size);
+
+ if (!size) {
+ NOTREACHED();
+ return std::wstring();
+ }
+
+ // Create the buffer that will hold the name.
+ scoped_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
+
+ // Query the name of the object.
+ if (!::GetUserObjectInformation(handle, UOI_NAME, name_buffer.get(), size,
+ &size)) {
+ NOTREACHED();
+ return std::wstring();
+ }
+
+ return std::wstring(name_buffer.get());
+}
+
+std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
+ if (!desktop) {
+ NOTREACHED();
+ return std::wstring();
+ }
+
+ std::wstring name;
+ if (winsta) {
+ name = GetWindowObjectName(winsta);
+ name += L'\\';
+ }
+
+ name += GetWindowObjectName(desktop);
+ return name;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/window.h b/chromium/sandbox/win/src/window.h
new file mode 100644
index 00000000000..e8233e734ee
--- /dev/null
+++ b/chromium/sandbox/win/src/window.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2009 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_WINDOW_H_
+#define SANDBOX_SRC_WINDOW_H_
+
+#include <windows.h>
+#include <string>
+
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+ // Creates a window station. The name is generated by the OS. The security
+ // descriptor is based on the security descriptor of the current window
+ // station.
+ ResultCode CreateAltWindowStation(HWINSTA* winsta);
+
+ // Creates a desktop. The name is a static string followed by the pid of the
+ // current process. The security descriptor on the new desktop is based on the
+ // security descriptor of the desktop associated with the current thread.
+ // If a winsta is specified, the function will switch to it before creating
+ // the desktop. If the functions fails the switch back to the current winsta,
+ // the function will return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION.
+ ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop);
+
+ // Returns the name of a desktop or a window station.
+ std::wstring GetWindowObjectName(HANDLE handle);
+
+ // Returns the name of the desktop referenced by |desktop|. If a window
+ // station is specified, the name is prepended with the window station name,
+ // followed by a backslash. This name can be used as the lpDesktop parameter
+ // to CreateProcess.
+ std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WINDOW_H_
diff --git a/chromium/sandbox/win/tools/finder/finder.cc b/chromium/sandbox/win/tools/finder/finder.cc
new file mode 100644
index 00000000000..9b829628e3e
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/finder.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2006-2008 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/restricted_token.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/tools/finder/finder.h"
+
+Finder::Finder() {
+ file_output_ = NULL;
+ object_type_ = 0;
+ access_type_ = 0;
+ token_handle_ = NULL;
+ memset(filesystem_stats_, 0, sizeof(filesystem_stats_));
+ memset(registry_stats_, 0, sizeof(registry_stats_));
+ memset(kernel_object_stats_, 0, sizeof(kernel_object_stats_));
+}
+
+Finder::~Finder() {
+ if (token_handle_)
+ ::CloseHandle(token_handle_);
+}
+
+DWORD Finder::Init(sandbox::TokenLevel token_type,
+ DWORD object_type,
+ DWORD access_type,
+ FILE *file_output) {
+ DWORD err_code = ERROR_SUCCESS;
+
+ err_code = InitNT();
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+
+ object_type_ = object_type;
+ access_type_ = access_type;
+ file_output_ = file_output;
+
+ err_code = sandbox::CreateRestrictedToken(&token_handle_, token_type,
+ sandbox::INTEGRITY_LEVEL_LAST,
+ sandbox::PRIMARY);
+ return err_code;
+}
+
+DWORD Finder::Scan() {
+ if (!token_handle_) {
+ return ERROR_NO_TOKEN;
+ }
+
+ if (object_type_ & kScanRegistry) {
+ ParseRegistry(HKEY_LOCAL_MACHINE, L"HKLM\\");
+ ParseRegistry(HKEY_USERS, L"HKU\\");
+ ParseRegistry(HKEY_CURRENT_CONFIG, L"HKCC\\");
+ }
+
+ if (object_type_ & kScanFileSystem) {
+ ParseFileSystem(L"\\\\?\\C:");
+ }
+
+ if (object_type_ & kScanKernelObjects) {
+ ParseKernelObjects(L"\\");
+ }
+
+ return ERROR_SUCCESS;
+}
diff --git a/chromium/sandbox/win/tools/finder/finder.h b/chromium/sandbox/win/tools/finder/finder.h
new file mode 100644
index 00000000000..23255cea439
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/finder.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2006-2008 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_TOOLS_FINDER_FINDER_H__
+#define SANDBOX_TOOLS_FINDER_FINDER_H__
+
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/tools/finder/ntundoc.h"
+
+// Type of stats that we calculate during the Scan operation
+enum Stats {
+ READ = 0, // Number of objects with read access
+ WRITE, // Number of objects with write access
+ ALL, // Number of objects with r/w access
+ PARSE, // Number of objects parsed
+ BROKEN, // Number of errors while parsing the objects
+ SIZE_STATS // size of the enum
+};
+
+const int kScanRegistry = 0x01;
+const int kScanFileSystem = 0x02;
+const int kScanKernelObjects = 0x04;
+
+const int kTestForRead = 0x01;
+const int kTestForWrite = 0x02;
+const int kTestForAll = 0x04;
+
+#define FS_ERR L"FILE-ERROR"
+#define OBJ_ERR L"OBJ-ERROR"
+#define REG_ERR L"REG_ERROR"
+#define OBJ L"OBJ"
+#define FS L"FILE"
+#define REG L"REG"
+
+// The impersonater class will impersonate a token when the object is created
+// and revert when the object is going out of scope.
+class Impersonater {
+ public:
+ Impersonater(HANDLE token_handle) {
+ if (token_handle)
+ ::ImpersonateLoggedOnUser(token_handle);
+ };
+ ~Impersonater() {
+ ::RevertToSelf();
+ };
+};
+
+// The finder class handles the search of objects (file system, registry, kernel
+// objects) on the system that can be opened by a restricted token. It can
+// support multiple levels of restriction for the restricted token and can check
+// for read, write or r/w access. It outputs the results to a file or stdout.
+class Finder {
+ public:
+ Finder();
+ ~Finder();
+ DWORD Init(sandbox::TokenLevel token_type, DWORD object_type,
+ DWORD access_type, FILE *file_output);
+ DWORD Scan();
+
+ private:
+ // Parses a file system path and perform an access check on all files and
+ // folder found.
+ // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the
+ // win32 error code associated with the error.
+ DWORD ParseFileSystem(ATL::CString path);
+
+ // Parses a registry hive referenced by "key" and performs an access check on
+ // all subkeys found.
+ // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the
+ // win32 error code associated with the error.
+ DWORD ParseRegistry(HKEY key, ATL::CString print_name);
+
+ // Parses the kernel namespace beginning at "path" and performs an access
+ // check on all objects found. However, only some object types are supported,
+ // all non supported objects are ignored.
+ // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the
+ // win32 error code associated with the error.
+ DWORD ParseKernelObjects(ATL::CString path);
+
+ // Checks if "path" can be accessed with the restricted token.
+ // Returns the access granted.
+ DWORD TestFileAccess(ATL::CString path);
+
+ // Checks if the registry key with the path key\name can be accessed with the
+ // restricted token.
+ // print_name is only use for logging purpose.
+ // Returns the access granted.
+ DWORD TestRegAccess(HKEY key, ATL::CString name, ATL::CString print_name);
+
+ // Checks if the kernel object "path" of type "type" can be accessed with
+ // the restricted token.
+ // Returns the access granted.
+ DWORD TestKernelObjectAccess(ATL::CString path, ATL::CString type);
+
+ // Outputs information to the logfile
+ void Output(ATL::CString type, ATL::CString access, ATL::CString info) {
+ fprintf(file_output_, "\n%S;%S;%S", type.GetBuffer(), access.GetBuffer(),
+ info.GetBuffer());
+ };
+
+ // Output information to the log file.
+ void Output(ATL::CString type, DWORD error, ATL::CString info) {
+ fprintf(file_output_, "\n%S;0x%X;%S", type.GetBuffer(), error,
+ info.GetBuffer());
+ };
+
+ // Set func_to_call to the function pointer of the function used to handle
+ // requests for the kernel objects of type "type". If the type is not
+ // supported at the moment the function returns false and the func_to_call
+ // parameter is not modified.
+ bool GetFunctionForType(ATL::CString type, NTGENERICOPEN * func_to_call);
+
+ // Initializes the NT function pointers to be able to use all the needed
+ // functions in NTDDL.
+ // Returns ERROR_SUCCESS if the function succeeded, otherwise, it returns the
+ // win32 error code associated with the error.
+ DWORD InitNT();
+
+ // Calls func_to_call with the parameters desired_access, object_attributes
+ // and handle. func_to_call is a pointer to a function to open a kernel
+ // object.
+ NTSTATUS NtGenericOpen(ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES *object_attributes,
+ NTGENERICOPEN func_to_call,
+ HANDLE *handle);
+
+ // Type of object to check for.
+ DWORD object_type_;
+ // Access to try.
+ DWORD access_type_;
+ // Output file for the results.
+ FILE * file_output_;
+ // Handle to the restricted token.
+ HANDLE token_handle_;
+ // Stats containing the number of operations performed on the different
+ // objects.
+ int filesystem_stats_[SIZE_STATS];
+ int registry_stats_[SIZE_STATS];
+ int kernel_object_stats_[SIZE_STATS];
+};
+
+#endif // SANDBOX_TOOLS_FINDER_FINDER_H__
diff --git a/chromium/sandbox/win/tools/finder/finder.vcproj b/chromium/sandbox/win/tools/finder/finder.vcproj
new file mode 100644
index 00000000000..787c8477c2c
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/finder.vcproj
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="finder"
+ ProjectGUID="{ACDC2E06-0366-41A4-A646-C37E130A605D}"
+ RootNamespace="finder"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\finder.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\finder.h"
+ >
+ </File>
+ <File
+ RelativePath=".\finder_fs.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\finder_kernel.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\finder_registry.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\main.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\ntundoc.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chromium/sandbox/win/tools/finder/finder_fs.cc b/chromium/sandbox/win/tools/finder/finder_fs.cc
new file mode 100644
index 00000000000..ddcc4bec605
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/finder_fs.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2006-2008 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/restricted_token.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/tools/finder/finder.h"
+
+DWORD Finder::ParseFileSystem(ATL::CString directory) {
+ WIN32_FIND_DATA find_data;
+ HANDLE find;
+
+ //Search for items in the directory.
+ ATL::CString name_to_search = directory + L"\\*";
+ find = ::FindFirstFile(name_to_search, &find_data);
+ if (INVALID_HANDLE_VALUE == find) {
+ DWORD error = ::GetLastError();
+ Output(FS_ERR, error, directory);
+ filesystem_stats_[BROKEN]++;
+ return error;
+ }
+
+ // parse all files or folders.
+ do {
+ if (_tcscmp(find_data.cFileName, L".") == 0 ||
+ _tcscmp(find_data.cFileName, L"..") == 0)
+ continue;
+
+ ATL::CString complete_name = directory + L"\\" + find_data.cFileName;
+ TestFileAccess(complete_name);
+
+ // Call recursively the function if the path found is a directory.
+ if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ ParseFileSystem(complete_name);
+ }
+ } while (::FindNextFile(find, &find_data) != 0);
+
+ DWORD err_code = ::GetLastError();
+ ::FindClose(find);
+
+ if (ERROR_NO_MORE_FILES != err_code) {
+ Output(FS_ERR, err_code, directory);
+ filesystem_stats_[BROKEN]++;
+ return err_code;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD Finder::TestFileAccess(ATL::CString name) {
+ Impersonater impersonate(token_handle_);
+
+ filesystem_stats_[PARSE]++;
+
+ HANDLE file;
+ if (access_type_ & kTestForAll) {
+ file = ::CreateFile(name.GetBuffer(),
+ GENERIC_ALL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (file != INVALID_HANDLE_VALUE) {
+ filesystem_stats_[ALL]++;
+ Output(FS, L"R/W", name.GetBuffer());
+ ::CloseHandle(file);
+ return GENERIC_ALL;
+ } else if (::GetLastError() != ERROR_ACCESS_DENIED) {
+ Output(FS_ERR, GetLastError(), name);
+ filesystem_stats_[BROKEN]++;
+ }
+ }
+
+ if (access_type_ & kTestForWrite) {
+ file = ::CreateFile(name.GetBuffer(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (file != INVALID_HANDLE_VALUE) {
+ filesystem_stats_[WRITE]++;
+ Output(FS, L"W", name);
+ ::CloseHandle(file);
+ return GENERIC_WRITE;
+ } else if (::GetLastError() != ERROR_ACCESS_DENIED) {
+ Output(FS_ERR, ::GetLastError(), name);
+ filesystem_stats_[BROKEN]++;
+ }
+ }
+
+ if (access_type_ & kTestForRead) {
+ file = ::CreateFile(name.GetBuffer(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (file != INVALID_HANDLE_VALUE) {
+ filesystem_stats_[READ]++;
+ Output(FS, L"R", name);
+ ::CloseHandle(file);
+ return GENERIC_READ;
+ } else if (::GetLastError() != ERROR_ACCESS_DENIED) {
+ Output(FS_ERR, GetLastError(), name);
+ filesystem_stats_[BROKEN]++;
+ }
+ }
+
+ return 0;
+}
diff --git a/chromium/sandbox/win/tools/finder/finder_kernel.cc b/chromium/sandbox/win/tools/finder/finder_kernel.cc
new file mode 100644
index 00000000000..430a4c08ab4
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/finder_kernel.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2006-2008 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/restricted_token.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/tools/finder/finder.h"
+#include "sandbox/win/tools/finder/ntundoc.h"
+
+#define BUFFER_SIZE 0x800
+#define CHECKPTR(x) if (!x) return ::GetLastError()
+
+// NT API
+NTQUERYDIRECTORYOBJECT NtQueryDirectoryObject;
+NTOPENDIRECTORYOBJECT NtOpenDirectoryObject;
+NTOPENEVENT NtOpenEvent;
+NTOPENJOBOBJECT NtOpenJobObject;
+NTOPENKEYEDEVENT NtOpenKeyedEvent;
+NTOPENMUTANT NtOpenMutant;
+NTOPENSECTION NtOpenSection;
+NTOPENSEMAPHORE NtOpenSemaphore;
+NTOPENSYMBOLICLINKOBJECT NtOpenSymbolicLinkObject;
+NTOPENTIMER NtOpenTimer;
+NTOPENFILE NtOpenFile;
+NTCLOSE NtClose;
+
+DWORD Finder::InitNT() {
+ HMODULE ntdll_handle = ::LoadLibrary(L"ntdll.dll");
+ CHECKPTR(ntdll_handle);
+
+ NtOpenSymbolicLinkObject = (NTOPENSYMBOLICLINKOBJECT) ::GetProcAddress(
+ ntdll_handle, "NtOpenSymbolicLinkObject");
+ CHECKPTR(NtOpenSymbolicLinkObject);
+
+ NtQueryDirectoryObject = (NTQUERYDIRECTORYOBJECT) ::GetProcAddress(
+ ntdll_handle, "NtQueryDirectoryObject");
+ CHECKPTR(NtQueryDirectoryObject);
+
+ NtOpenDirectoryObject = (NTOPENDIRECTORYOBJECT) ::GetProcAddress(
+ ntdll_handle, "NtOpenDirectoryObject");
+ CHECKPTR(NtOpenDirectoryObject);
+
+ NtOpenKeyedEvent = (NTOPENKEYEDEVENT) ::GetProcAddress(
+ ntdll_handle, "NtOpenKeyedEvent");
+ CHECKPTR(NtOpenKeyedEvent);
+
+ NtOpenJobObject = (NTOPENJOBOBJECT) ::GetProcAddress(
+ ntdll_handle, "NtOpenJobObject");
+ CHECKPTR(NtOpenJobObject);
+
+ NtOpenSemaphore = (NTOPENSEMAPHORE) ::GetProcAddress(
+ ntdll_handle, "NtOpenSemaphore");
+ CHECKPTR(NtOpenSemaphore);
+
+ NtOpenSection = (NTOPENSECTION) ::GetProcAddress(
+ ntdll_handle, "NtOpenSection");
+ CHECKPTR(NtOpenSection);
+
+ NtOpenMutant= (NTOPENMUTANT) ::GetProcAddress(ntdll_handle, "NtOpenMutant");
+ CHECKPTR(NtOpenMutant);
+
+ NtOpenEvent = (NTOPENEVENT) ::GetProcAddress(ntdll_handle, "NtOpenEvent");
+ CHECKPTR(NtOpenEvent);
+
+ NtOpenTimer = (NTOPENTIMER) ::GetProcAddress(ntdll_handle, "NtOpenTimer");
+ CHECKPTR(NtOpenTimer);
+
+ NtOpenFile = (NTOPENFILE) ::GetProcAddress(ntdll_handle, "NtOpenFile");
+ CHECKPTR(NtOpenFile);
+
+ NtClose = (NTCLOSE) ::GetProcAddress(ntdll_handle, "NtClose");
+ CHECKPTR(NtClose);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD Finder::ParseKernelObjects(ATL::CString path) {
+ UNICODE_STRING unicode_str;
+ unicode_str.Length = (USHORT)path.GetLength()*2;
+ unicode_str.MaximumLength = (USHORT)path.GetLength()*2+2;
+ unicode_str.Buffer = path.GetBuffer();
+
+ OBJECT_ATTRIBUTES path_attributes;
+ InitializeObjectAttributes(&path_attributes,
+ &unicode_str,
+ 0, // No Attributes
+ NULL, // No Root Directory
+ NULL); // No Security Descriptor
+
+
+ DWORD object_index = 0;
+ DWORD data_written = 0;
+
+ // TODO(nsylvain): Do not use BUFFER_SIZE. Try to get the size
+ // dynamically.
+ OBJDIR_INFORMATION *object_directory_info =
+ (OBJDIR_INFORMATION*) ::HeapAlloc(GetProcessHeap(),
+ 0,
+ BUFFER_SIZE);
+
+ HANDLE file_handle;
+ NTSTATUS status_code = NtOpenDirectoryObject(&file_handle,
+ DIRECTORY_QUERY,
+ &path_attributes);
+ if (status_code != 0)
+ return ERROR_UNIDENTIFIED_ERROR;
+
+ status_code = NtQueryDirectoryObject(file_handle,
+ object_directory_info,
+ BUFFER_SIZE,
+ TRUE, // Get Next Index
+ TRUE, // Ignore Input Index
+ &object_index,
+ &data_written);
+
+ if (status_code != 0)
+ return ERROR_UNIDENTIFIED_ERROR;
+
+ while (NtQueryDirectoryObject(file_handle, object_directory_info,
+ BUFFER_SIZE, TRUE, FALSE, &object_index,
+ &data_written) == 0 ) {
+ ATL::CString cur_path(object_directory_info->ObjectName.Buffer,
+ object_directory_info->ObjectName.Length / sizeof(WCHAR));
+
+ ATL::CString cur_type(object_directory_info->ObjectTypeName.Buffer,
+ object_directory_info->ObjectTypeName.Length / sizeof(WCHAR));
+
+ ATL::CString new_path;
+ if (path == L"\\") {
+ new_path = path + cur_path;
+ } else {
+ new_path = path + L"\\" + cur_path;
+ }
+
+ TestKernelObjectAccess(new_path, cur_type);
+
+ // Call the function recursively for all subdirectories
+ if (cur_type == L"Directory") {
+ ParseKernelObjects(new_path);
+ }
+ }
+
+ NtClose(file_handle);
+ return ERROR_SUCCESS;
+}
+
+DWORD Finder::TestKernelObjectAccess(ATL::CString path, ATL::CString type) {
+ Impersonater impersonate(token_handle_);
+
+ kernel_object_stats_[PARSE]++;
+
+ NTGENERICOPEN func = NULL;
+ GetFunctionForType(type, &func);
+
+ if (!func) {
+ kernel_object_stats_[BROKEN]++;
+ Output(OBJ_ERR, type + L" Unsupported", path);
+ return ERROR_UNSUPPORTED_TYPE;
+ }
+
+ UNICODE_STRING unicode_str;
+ unicode_str.Length = (USHORT)path.GetLength()*2;
+ unicode_str.MaximumLength = (USHORT)path.GetLength()*2+2;
+ unicode_str.Buffer = path.GetBuffer();
+
+ OBJECT_ATTRIBUTES path_attributes;
+ InitializeObjectAttributes(&path_attributes,
+ &unicode_str,
+ 0, // No Attributes
+ NULL, // No Root Directory
+ NULL); // No Security Descriptor
+
+ HANDLE handle;
+ NTSTATUS status_code = 0;
+
+ if (access_type_ & kTestForAll) {
+ status_code = NtGenericOpen(GENERIC_ALL, &path_attributes, func, &handle);
+ if (STATUS_SUCCESS == status_code) {
+ kernel_object_stats_[ALL]++;
+ Output(OBJ, L"R/W", path);
+ NtClose(handle);
+ return GENERIC_ALL;
+ } else if (status_code != EXCEPTION_ACCESS_VIOLATION &&
+ status_code != STATUS_ACCESS_DENIED) {
+ Output(OBJ_ERR, status_code, path);
+ kernel_object_stats_[BROKEN]++;
+ }
+ }
+
+ if (access_type_ & kTestForWrite) {
+ status_code = NtGenericOpen(GENERIC_WRITE, &path_attributes, func, &handle);
+ if (STATUS_SUCCESS == status_code) {
+ kernel_object_stats_[WRITE]++;
+ Output(OBJ, L"W", path);
+ NtClose(handle);
+ return GENERIC_WRITE;
+ } else if (status_code != EXCEPTION_ACCESS_VIOLATION &&
+ status_code != STATUS_ACCESS_DENIED) {
+ Output(OBJ_ERR, status_code, path);
+ kernel_object_stats_[BROKEN]++;
+ }
+ }
+
+ if (access_type_ & kTestForRead) {
+ status_code = NtGenericOpen(GENERIC_READ, &path_attributes, func, &handle);
+ if (STATUS_SUCCESS == status_code) {
+ kernel_object_stats_[READ]++;
+ Output(OBJ, L"R", path);
+ NtClose(handle);
+ return GENERIC_READ;
+ } else if (status_code != EXCEPTION_ACCESS_VIOLATION &&
+ status_code != STATUS_ACCESS_DENIED) {
+ Output(OBJ_ERR, status_code, path);
+ kernel_object_stats_[BROKEN]++;
+ }
+ }
+
+ return 0;
+}
+
+NTSTATUS Finder::NtGenericOpen(ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES *object_attributes,
+ NTGENERICOPEN func_to_call,
+ HANDLE *handle) {
+ return func_to_call(handle, desired_access, object_attributes);
+}
+
+bool Finder::GetFunctionForType(ATL::CString type,
+ NTGENERICOPEN * func_to_call) {
+ NTGENERICOPEN func = NULL;
+
+ if (type == L"Event") func = NtOpenEvent;
+ else if (type == L"Job") func = NtOpenJobObject;
+ else if (type == L"KeyedEvent") func = NtOpenKeyedEvent;
+ else if (type == L"Mutant") func = NtOpenMutant;
+ else if (type == L"Section") func = NtOpenSection;
+ else if (type == L"Semaphore") func = NtOpenSemaphore;
+ else if (type == L"Timer") func = NtOpenTimer;
+ else if (type == L"SymbolicLink") func = NtOpenSymbolicLinkObject;
+ else if (type == L"Directory") func = NtOpenDirectoryObject;
+
+ if (func) {
+ *func_to_call = func;
+ return true;
+ }
+
+ return false;
+}
diff --git a/chromium/sandbox/win/tools/finder/finder_registry.cc b/chromium/sandbox/win/tools/finder/finder_registry.cc
new file mode 100644
index 00000000000..a84e413d326
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/finder_registry.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2006-2008 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/restricted_token.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/tools/finder/finder.h"
+
+DWORD Finder::ParseRegistry(HKEY key, ATL::CString print_name) {
+ DWORD index = 0;
+ DWORD name_size = 2048;
+ wchar_t buffer[2048] = {0};
+ // TODO(nsylvain): Don't hardcode 2048. Get the key len by calling the
+ // function.
+ LONG err_code = ::RegEnumKey(key, index, buffer, name_size);
+ while (ERROR_SUCCESS == err_code) {
+ ATL::CString name_complete = print_name + buffer + L"\\";
+ TestRegAccess(key, buffer, name_complete);
+
+ // Call the function recursively to parse all subkeys
+ HKEY key_to_parse;
+ err_code = ::RegOpenKeyEx(key, buffer, 0, KEY_ENUMERATE_SUB_KEYS,
+ &key_to_parse);
+ if (ERROR_SUCCESS == err_code) {
+ ParseRegistry(key_to_parse, name_complete);
+ ::RegCloseKey(key_to_parse);
+ } else {
+ registry_stats_[BROKEN]++;
+ Output(REG_ERR, err_code, name_complete);
+ }
+
+ index++;
+ err_code = ::RegEnumKey(key, index, buffer, name_size);
+ }
+
+ if (ERROR_NO_MORE_ITEMS != err_code) {
+ registry_stats_[BROKEN]++;
+ Output(REG_ERR, err_code, print_name);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD Finder::TestRegAccess(HKEY key, ATL::CString name,
+ ATL::CString print_name) {
+ Impersonater impersonate(token_handle_);
+
+ registry_stats_[PARSE]++;
+
+ HKEY key_res;
+ LONG err_code = 0;
+
+ if (access_type_ & kTestForAll) {
+ err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_ALL, &key_res);
+ if (ERROR_SUCCESS == err_code) {
+ registry_stats_[ALL]++;
+ Output(REG, L"R/W", print_name);
+ ::RegCloseKey(key_res);
+ return GENERIC_ALL;
+ } else if (err_code != ERROR_ACCESS_DENIED) {
+ Output(REG_ERR, err_code, print_name);
+ registry_stats_[BROKEN]++;
+ }
+ }
+
+ if (access_type_ & kTestForWrite) {
+ err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_WRITE, &key_res);
+ if (ERROR_SUCCESS == err_code) {
+ registry_stats_[WRITE]++;
+ Output(REG, L"W", print_name);
+ ::RegCloseKey(key_res);
+ return GENERIC_WRITE;
+ } else if (err_code != ERROR_ACCESS_DENIED) {
+ Output(REG_ERR, err_code, print_name);
+ registry_stats_[BROKEN]++;
+ }
+ }
+
+ if (access_type_ & kTestForRead) {
+ err_code = ::RegOpenKeyEx(key, name, 0, GENERIC_READ, &key_res);
+ if (ERROR_SUCCESS == err_code) {
+ registry_stats_[READ]++;
+ Output(REG, L"R", print_name);
+ ::RegCloseKey(key_res);
+ return GENERIC_READ;
+ } else if (err_code != ERROR_ACCESS_DENIED) {
+ Output(REG_ERR, err_code, print_name);
+ registry_stats_[BROKEN]++;
+ }
+ }
+
+ return 0;
+}
diff --git a/chromium/sandbox/win/tools/finder/main.cc b/chromium/sandbox/win/tools/finder/main.cc
new file mode 100644
index 00000000000..7cadbef8f69
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/main.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2006-2008 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/restricted_token_utils.h"
+#include "sandbox/win/tools/finder/finder.h"
+
+#define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0)
+
+void PrintUsage(wchar_t *application_name) {
+ wprintf(L"\n\nUsage: \n %ls --token type --object ob1 [ob2 ob3] "
+ L"--access ac1 [ac2 ac3] [--log filename]", application_name);
+ wprintf(L"\n\n Token Types : \n\tLOCKDOWN \n\tRESTRICTED "
+ L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED");
+ wprintf(L"\n Object Types: \n\tREG \n\tFILE \n\tKERNEL");
+ wprintf(L"\n Access Types: \n\tR \n\tW \n\tALL");
+ wprintf(L"\n\nSample: \n %ls --token LOCKDOWN --object REG FILE KERNEL "
+ L"--access R W ALL", application_name);
+}
+
+int wmain(int argc, wchar_t* argv[]) {
+ // Extract the filename from the path.
+ wchar_t *app_name = wcsrchr(argv[0], L'\\');
+ if (!app_name) {
+ app_name = argv[0];
+ } else {
+ app_name++;
+ }
+
+ // parameters to read
+ ATL::CString log_file;
+ sandbox::TokenLevel token_type = sandbox::USER_LOCKDOWN;
+ DWORD object_type = 0;
+ DWORD access_type = 0;
+
+ // no arguments
+ if (argc == 1) {
+ PrintUsage(app_name);
+ return -1;
+ }
+
+ // parse command line.
+ for (int i = 1; i < argc; ++i) {
+ if (PARAM_IS(L"--token")) {
+ i++;
+ if (argc > i) {
+ if (PARAM_IS(L"LOCKDOWN")) {
+ token_type = sandbox::USER_LOCKDOWN;
+ } else if (PARAM_IS(L"RESTRICTED")) {
+ token_type = sandbox::USER_RESTRICTED;
+ } else if (PARAM_IS(L"LIMITED_USER")) {
+ token_type = sandbox::USER_LIMITED;
+ } else if (PARAM_IS(L"INTERACTIVE_USER")) {
+ token_type = sandbox::USER_INTERACTIVE;
+ } else if (PARAM_IS(L"NON_ADMIN")) {
+ token_type = sandbox::USER_NON_ADMIN;
+ } else if (PARAM_IS(L"USER_RESTRICTED_SAME_ACCESS")) {
+ token_type = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ } else if (PARAM_IS(L"UNPROTECTED")) {
+ token_type = sandbox::USER_UNPROTECTED;
+ } else {
+ wprintf(L"\nAbord. Invalid token type \"%ls\"", argv[i]);
+ PrintUsage(app_name);
+ return -1;
+ }
+ }
+ } else if (PARAM_IS(L"--object")) {
+ bool is_object = true;
+ do {
+ i++;
+ if (PARAM_IS(L"REG")) {
+ object_type |= kScanRegistry;
+ } else if (PARAM_IS(L"FILE")) {
+ object_type |= kScanFileSystem;
+ } else if (PARAM_IS(L"KERNEL")) {
+ object_type |= kScanKernelObjects;
+ } else {
+ is_object = false;
+ }
+ } while(is_object);
+ i--;
+ } else if (PARAM_IS(L"--access")) {
+ bool is_access = true;
+ do {
+ i++;
+ if (PARAM_IS(L"R")) {
+ access_type |= kTestForRead;
+ } else if (PARAM_IS(L"W")) {
+ access_type |= kTestForWrite;
+ } else if (PARAM_IS(L"ALL")) {
+ access_type |= kTestForAll;
+ } else {
+ is_access = false;
+ }
+ } while(is_access);
+ i--;
+ } else if (PARAM_IS(L"--log")) {
+ i++;
+ if (argc > i) {
+ log_file = argv[i];
+ }
+ else {
+ wprintf(L"\nAbord. No log file specified");
+ PrintUsage(app_name);
+ return -1;
+ }
+ } else {
+ wprintf(L"\nAbord. Unrecognized parameter \"%ls\"", argv[i]);
+ PrintUsage(app_name);
+ return -1;
+ }
+ }
+
+ // validate parameters
+ if (0 == access_type) {
+ wprintf(L"\nAbord, Access type not specified");
+ PrintUsage(app_name);
+ return -1;
+ }
+
+ if (0 == object_type) {
+ wprintf(L"\nAbord, Object type not specified");
+ PrintUsage(app_name);
+ return -1;
+ }
+
+
+ // Open log file
+ FILE * file_output;
+ if (log_file.GetLength()) {
+ errno_t err = _wfopen_s(&file_output, log_file, L"w");
+ if (err) {
+ wprintf(L"\nAbord, Cannot open file \"%ls\"", log_file.GetBuffer());
+ return -1;
+ }
+ } else {
+ file_output = stdout;
+ }
+
+ Finder finder_obj;
+ finder_obj.Init(token_type, object_type, access_type, file_output);
+ finder_obj.Scan();
+
+ fclose(file_output);
+
+ return 0;
+}
diff --git a/chromium/sandbox/win/tools/finder/ntundoc.h b/chromium/sandbox/win/tools/finder/ntundoc.h
new file mode 100644
index 00000000000..dc8c3a57cb1
--- /dev/null
+++ b/chromium/sandbox/win/tools/finder/ntundoc.h
@@ -0,0 +1,275 @@
+// Copyright (c) 2006-2011 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_TOOLS_FINDER_NTUNDOC_H__
+#define SANDBOX_TOOLS_FINDER_NTUNDOC_H__
+
+#define NTSTATUS ULONG
+#define STATUS_SUCCESS 0x00000000
+#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
+#define STATUS_ACCESS_DENIED 0xC0000022
+#define STATUS_BUFFER_OVERFLOW 0x80000005
+
+typedef struct _LSA_UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING;
+
+typedef struct _OBJDIR_INFORMATION {
+ UNICODE_STRING ObjectName;
+ UNICODE_STRING ObjectTypeName;
+ BYTE Data[1];
+} OBJDIR_INFORMATION;
+
+typedef struct _OBJECT_ATTRIBUTES {
+ ULONG Length;
+ HANDLE RootDirectory;
+ UNICODE_STRING *ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+} OBJECT_ATTRIBUTES;
+
+typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG Reserved[10]; // reserved for internal use
+ } PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION;
+
+typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING TypeName;
+ ULONG Reserved [22]; // reserved for internal use
+} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;
+
+typedef enum _POOL_TYPE {
+ NonPagedPool,
+ PagedPool,
+ NonPagedPoolMustSucceed,
+ ReservedType,
+ NonPagedPoolCacheAligned,
+ PagedPoolCacheAligned,
+ NonPagedPoolCacheAlignedMustS
+} POOL_TYPE;
+
+typedef struct _OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING Name;
+ ULONG TotalNumberOfObjects;
+ ULONG TotalNumberOfHandles;
+ ULONG TotalPagedPoolUsage;
+ ULONG TotalNonPagedPoolUsage;
+ ULONG TotalNamePoolUsage;
+ ULONG TotalHandleTableUsage;
+ ULONG HighWaterNumberOfObjects;
+ ULONG HighWaterNumberOfHandles;
+ ULONG HighWaterPagedPoolUsage;
+ ULONG HighWaterNonPagedPoolUsage;
+ ULONG HighWaterNamePoolUsage;
+ ULONG HighWaterHandleTableUsage;
+ ULONG InvalidAttributes;
+ GENERIC_MAPPING GenericMapping;
+ ULONG ValidAccess;
+ BOOLEAN SecurityRequired;
+ BOOLEAN MaintainHandleCount;
+ USHORT MaintainTypeList;
+ POOL_TYPE PoolType;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
+
+typedef struct _OBJECT_NAME_INFORMATION {
+ UNICODE_STRING ObjectName;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+typedef enum _OBJECT_INFORMATION_CLASS {
+ ObjectBasicInformation,
+ ObjectNameInformation,
+ ObjectTypeInformation,
+ ObjectAllInformation,
+ ObjectDataInformation
+} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
+
+typedef struct _FILE_NAME_INFORMATION {
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+} FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
+
+typedef enum _FILE_INFORMATION_CLASS {
+ // end_wdm
+ FileDirectoryInformation = 1,
+ FileFullDirectoryInformation, // 2
+ FileBothDirectoryInformation, // 3
+ FileBasicInformation, // 4 wdm
+ FileStandardInformation, // 5 wdm
+ FileInternalInformation, // 6
+ FileEaInformation, // 7
+ FileAccessInformation, // 8
+ FileNameInformation, // 9
+ FileRenameInformation, // 10
+ FileLinkInformation, // 11
+ FileNamesInformation, // 12
+ FileDispositionInformation, // 13
+ FilePositionInformation, // 14 wdm
+ FileFullEaInformation, // 15
+ FileModeInformation, // 16
+ FileAlignmentInformation, // 17
+ FileAllInformation, // 18
+ FileAllocationInformation, // 19
+ FileEndOfFileInformation, // 20 wdm
+ FileAlternateNameInformation, // 21
+ FileStreamInformation, // 22
+ FilePipeInformation, // 23
+ FilePipeLocalInformation, // 24
+ FilePipeRemoteInformation, // 25
+ FileMailslotQueryInformation, // 26
+ FileMailslotSetInformation, // 27
+ FileCompressionInformation, // 28
+ FileObjectIdInformation, // 29
+ FileCompletionInformation, // 30
+ FileMoveClusterInformation, // 31
+ FileQuotaInformation, // 32
+ FileReparsePointInformation, // 33
+ FileNetworkOpenInformation, // 34
+ FileAttributeTagInformation, // 35
+ FileTrackingInformation, // 36
+ FileMaximumInformation
+ // begin_wdm
+} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemHandleInformation = 16
+} SYSTEM_INFORMATION_CLASS;
+
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+#define InitializeObjectAttributes( p, n, a, r, s ) { \
+ (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
+ (p)->RootDirectory = r; \
+ (p)->Attributes = a; \
+ (p)->ObjectName = n; \
+ (p)->SecurityDescriptor = s; \
+ (p)->SecurityQualityOfService = NULL; \
+}
+
+typedef struct _SYSTEM_HANDLE_INFORMATION {
+ USHORT ProcessId;
+ USHORT CreatorBackTraceIndex;
+ UCHAR ObjectTypeNumber;
+ UCHAR Flags;
+ USHORT Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
+ ULONG NumberOfHandles;
+ SYSTEM_HANDLE_INFORMATION Information[1];
+} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
+
+#define POBJECT_ATTRIBUTES OBJECT_ATTRIBUTES*
+
+typedef NTSTATUS (WINAPI* NTQUERYDIRECTORYOBJECT)(
+ HANDLE,
+ OBJDIR_INFORMATION*,
+ DWORD,
+ DWORD,
+ DWORD,
+ DWORD*,
+ DWORD*);
+
+typedef NTSTATUS (WINAPI* NTOPENDIRECTORYOBJECT)(
+ HANDLE *,
+ DWORD,
+ OBJECT_ATTRIBUTES* );
+
+typedef NTSTATUS (WINAPI* NTGENERICOPEN) (
+ OUT PHANDLE EventHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENEVENT)(
+ OUT PHANDLE EventHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENJOBOBJECT)(
+ OUT PHANDLE JobHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENKEYEDEVENT)(
+ OUT PHANDLE KeyedEventHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENMUTANT)(
+ OUT PHANDLE MutantHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENSECTION)(
+ OUT PHANDLE SectionHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENSEMAPHORE)(
+ OUT PHANDLE SemaphoreHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENSYMBOLICLINKOBJECT)(
+ OUT PHANDLE SymbolicLinkHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENTIMER)(
+ OUT PHANDLE TimerHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NTOPENFILE)(
+ HANDLE *,
+ DWORD,
+ OBJECT_ATTRIBUTES *,
+ IO_STATUS_BLOCK *,
+ DWORD,
+ DWORD);
+
+typedef NTSTATUS (WINAPI* NTQUERYINFORMATIONFILE)(
+ HANDLE,
+ PIO_STATUS_BLOCK,
+ PVOID,
+ ULONG,
+ FILE_INFORMATION_CLASS);
+
+typedef NTSTATUS (WINAPI* NTQUERYSYSTEMINFORMATION)(
+ SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength,
+ PULONG ReturnLength);
+
+typedef NTSTATUS (WINAPI* NTQUERYOBJECT)(
+ HANDLE Handle,
+ OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ PVOID ObjectInformation,
+ ULONG ObjectInformationLength,
+ PULONG ReturnLength);
+
+typedef NTSTATUS (WINAPI* NTCLOSE) (HANDLE);
+
+#define DIRECTORY_QUERY 0x0001
+#define DIRECTORY_TRAVERSE 0x0002
+#define DIRECTORY_CREATE_OBJECT 0x0004
+#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008
+#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF)
+
+#endif // SANDBOX_TOOLS_FINDER_NTUNDOC_H__
diff --git a/chromium/sandbox/win/tools/launcher/launcher.cc b/chromium/sandbox/win/tools/launcher/launcher.cc
new file mode 100644
index 00000000000..efcc4a43ccb
--- /dev/null
+++ b/chromium/sandbox/win/tools/launcher/launcher.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2008 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/restricted_token_utils.h"
+
+// launcher.exe is an application used to launch another application with a
+// restricted token. This is to be used for testing only.
+// The parameters are the level of security of the primary token, the
+// impersonation token and the job object along with the command line to
+// execute.
+// See the usage (launcher.exe without parameters) for the correct format.
+
+#define PARAM_IS(y) (argc > i) && (_wcsicmp(argv[i], y) == 0)
+
+void PrintUsage(const wchar_t *application_name) {
+ wprintf(L"\n\nUsage: \n %ls --main level --init level --job level cmd_line ",
+ application_name);
+ wprintf(L"\n\n Levels : \n\tLOCKDOWN \n\tRESTRICTED "
+ L"\n\tLIMITED_USER \n\tINTERACTIVE_USER \n\tNON_ADMIN \n\tUNPROTECTED");
+ wprintf(L"\n\n main: Security level of the main token");
+ wprintf(L"\n init: Security level of the impersonation token");
+ wprintf(L"\n job: Security level of the job object");
+}
+
+bool GetTokenLevelFromString(const wchar_t *param,
+ sandbox::TokenLevel* level) {
+ if (_wcsicmp(param, L"LOCKDOWN") == 0) {
+ *level = sandbox::USER_LOCKDOWN;
+ } else if (_wcsicmp(param, L"RESTRICTED") == 0) {
+ *level = sandbox::USER_RESTRICTED;
+ } else if (_wcsicmp(param, L"LIMITED_USER") == 0) {
+ *level = sandbox::USER_LIMITED;
+ } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) {
+ *level = sandbox::USER_INTERACTIVE;
+ } else if (_wcsicmp(param, L"NON_ADMIN") == 0) {
+ *level = sandbox::USER_NON_ADMIN;
+ } else if (_wcsicmp(param, L"USER_RESTRICTED_SAME_ACCESS") == 0) {
+ *level = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ } else if (_wcsicmp(param, L"UNPROTECTED") == 0) {
+ *level = sandbox::USER_UNPROTECTED;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool GetJobLevelFromString(const wchar_t *param, sandbox::JobLevel* level) {
+ if (_wcsicmp(param, L"LOCKDOWN") == 0) {
+ *level = sandbox::JOB_LOCKDOWN;
+ } else if (_wcsicmp(param, L"RESTRICTED") == 0) {
+ *level = sandbox::JOB_RESTRICTED;
+ } else if (_wcsicmp(param, L"LIMITED_USER") == 0) {
+ *level = sandbox::JOB_LIMITED_USER;
+ } else if (_wcsicmp(param, L"INTERACTIVE_USER") == 0) {
+ *level = sandbox::JOB_INTERACTIVE;
+ } else if (_wcsicmp(param, L"NON_ADMIN") == 0) {
+ wprintf(L"\nNON_ADMIN is not a supported job type");
+ return false;
+ } else if (_wcsicmp(param, L"UNPROTECTED") == 0) {
+ *level = sandbox::JOB_UNPROTECTED;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+int wmain(int argc, wchar_t *argv[]) {
+ // Extract the filename from the path.
+ wchar_t *app_name = wcsrchr(argv[0], L'\\');
+ if (!app_name) {
+ app_name = argv[0];
+ } else {
+ app_name++;
+ }
+
+ // no argument
+ if (argc == 1) {
+ PrintUsage(app_name);
+ return -1;
+ }
+
+ sandbox::TokenLevel primary_level = sandbox::USER_LOCKDOWN;
+ sandbox::TokenLevel impersonation_level =
+ sandbox::USER_RESTRICTED_SAME_ACCESS;
+ sandbox::JobLevel job_level = sandbox::JOB_LOCKDOWN;
+ ATL::CString command_line;
+
+ // parse command line.
+ for (int i = 1; i < argc; ++i) {
+ if (PARAM_IS(L"--main")) {
+ i++;
+ if (argc > i) {
+ if (!GetTokenLevelFromString(argv[i], &primary_level)) {
+ wprintf(L"\nAbord, Unrecognized main token level \"%ls\"", argv[i]);
+ PrintUsage(app_name);
+ return -1;
+ }
+ }
+ } else if (PARAM_IS(L"--init")) {
+ i++;
+ if (argc > i) {
+ if (!GetTokenLevelFromString(argv[i], &impersonation_level)) {
+ wprintf(L"\nAbord, Unrecognized init token level \"%ls\"", argv[i]);
+ PrintUsage(app_name);
+ return -1;
+ }
+ }
+ } else if (PARAM_IS(L"--job")) {
+ i++;
+ if (argc > i) {
+ if (!GetJobLevelFromString(argv[i], &job_level)) {
+ wprintf(L"\nAbord, Unrecognized job security level \"%ls\"", argv[i]);
+ PrintUsage(app_name);
+ return -1;
+ }
+ }
+ } else {
+ if (command_line.GetLength()) {
+ command_line += L' ';
+ }
+ command_line += argv[i];
+ }
+ }
+
+ if (!command_line.GetLength()) {
+ wprintf(L"\nAbord, No command line specified");
+ PrintUsage(app_name);
+ return -1;
+ }
+
+ wprintf(L"\nLaunching command line: \"%ls\"\n", command_line.GetBuffer());
+
+ HANDLE job_handle;
+ DWORD err_code = sandbox::StartRestrictedProcessInJob(
+ command_line.GetBuffer(),
+ primary_level,
+ impersonation_level,
+ job_level,
+ &job_handle);
+ if (ERROR_SUCCESS != err_code) {
+ wprintf(L"\nAbord, Error %d while launching command line.", err_code);
+ return -1;
+ }
+
+ wprintf(L"\nPress any key to continue.");
+ while(!_kbhit()) {
+ Sleep(100);
+ }
+
+ ::CloseHandle(job_handle);
+
+ return 0;
+}
diff --git a/chromium/sandbox/win/tools/launcher/launcher.vcproj b/chromium/sandbox/win/tools/launcher/launcher.vcproj
new file mode 100644
index 00000000000..71ed011d2b2
--- /dev/null
+++ b/chromium/sandbox/win/tools/launcher/launcher.vcproj
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="launcher"
+ ProjectGUID="{386FA217-FBC2-4461-882D-CDAD221ED800}"
+ RootNamespace="launcher"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath=".\launcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chromium/sandbox/win/wow_helper.sln b/chromium/sandbox/win/wow_helper.sln
new file mode 100644
index 00000000000..26d0da25262
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wow_helper", "wow_helper\wow_helper.vcproj", "{BCF3A457-39F1-4DAA-9A65-93CFCD559036}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.ActiveCfg = Debug|x64
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Debug|x64.Build.0 = Debug|x64
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.ActiveCfg = Release|x64
+ {BCF3A457-39F1-4DAA-9A65-93CFCD559036}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/chromium/sandbox/win/wow_helper/service64_resolver.cc b/chromium/sandbox/win/wow_helper/service64_resolver.cc
new file mode 100644
index 00000000000..033b9d771e1
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/service64_resolver.cc
@@ -0,0 +1,342 @@
+// Copyright (c) 2011 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/wow_helper/service64_resolver.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/wow_helper/target_code.h"
+
+namespace {
+#pragma pack(push, 1)
+
+const BYTE kMovEax = 0xB8;
+const BYTE kMovEdx = 0xBA;
+const USHORT kCallPtrEdx = 0x12FF;
+const BYTE kRet = 0xC2;
+const BYTE kNop = 0x90;
+const USHORT kJmpEdx = 0xE2FF;
+const USHORT kXorEcx = 0xC933;
+const ULONG kLeaEdx = 0x0424548D;
+const ULONG kCallFs1 = 0xC015FF64;
+const ULONG kCallFs2Ret = 0xC2000000;
+const BYTE kPopEdx = 0x5A;
+const BYTE kPushEdx = 0x52;
+const BYTE kPush32 = 0x68;
+
+const ULONG kMmovR10EcxMovEax = 0xB8D18B4C;
+const USHORT kSyscall = 0x050F;
+const BYTE kRetNp = 0xC3;
+const BYTE kPad = 0x66;
+const USHORT kNop16 = 0x9066;
+const BYTE kRelJmp = 0xE9;
+
+const ULONG kXorRaxMovEax = 0xB8C03148;
+const ULONG kSaveRcx = 0x10488948;
+const ULONG kMovRcxRaxJmp = 0xE9C88B48;
+
+// Service code for 64 bit systems.
+struct ServiceEntry {
+ // this struct contains roughly the following code:
+ // mov r10,rcx
+ // mov eax,52h
+ // syscall
+ // ret
+ // xchg ax,ax
+ // xchg ax,ax
+
+ ULONG mov_r10_ecx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ BYTE pad; // = 66
+ USHORT xchg_ax_ax1; // = 66 90
+ USHORT xchg_ax_ax2; // = 66 90
+};
+
+struct Redirected {
+ // this struct contains roughly the following code:
+ // jmp relative_32
+ // xchg ax,ax // 3 byte nop
+
+ Redirected() {
+ jmp = kRelJmp;
+ relative = 0;
+ pad = kPad;
+ xchg_ax_ax = kNop16;
+ };
+ BYTE jmp; // = E9
+ ULONG relative;
+ BYTE pad; // = 66
+ USHORT xchg_ax_ax; // = 66 90
+};
+
+struct InternalThunk {
+ // this struct contains roughly the following code:
+ // xor rax,rax
+ // mov eax, 0x00080000 // Thunk storage.
+ // mov [rax]PatchInfo.service, rcx // Save first argument.
+ // mov rcx, rax
+ // jmp relative_to_interceptor
+
+ InternalThunk() {
+ xor_rax_mov_eax = kXorRaxMovEax;
+ patch_info = 0;
+ save_rcx = kSaveRcx;
+ mov_rcx_rax_jmp = kMovRcxRaxJmp;
+ relative = 0;
+ };
+ ULONG xor_rax_mov_eax; // = 48 31 C0 B8
+ ULONG patch_info;
+ ULONG save_rcx; // = 48 89 48 10
+ ULONG mov_rcx_rax_jmp; // = 48 8b c8 e9
+ ULONG relative;
+};
+
+struct ServiceFullThunk {
+ sandbox::PatchInfo patch_info;
+ ServiceEntry original;
+ InternalThunk internal_thunk;
+};
+
+#pragma pack(pop)
+
+// Simple utility function to write to a buffer on the child, if the memery has
+// write protection attributes.
+// Arguments:
+// child_process (in): process to write to.
+// address (out): memory position on the child to write to.
+// buffer (in): local buffer with the data to write .
+// length (in): number of bytes to write.
+// Returns true on success.
+bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+ size_t length) {
+ // first, remove the protections
+ DWORD old_protection;
+ if (!::VirtualProtectEx(child_process, address, length,
+ PAGE_WRITECOPY, &old_protection))
+ return false;
+
+ SIZE_T written;
+ bool ok = ::WriteProcessMemory(child_process, address, buffer, length,
+ &written) && (length == written);
+
+ // always attempt to restore the original protection
+ if (!::VirtualProtectEx(child_process, address, length,
+ old_protection, &old_protection))
+ return false;
+
+ return ok;
+}
+
+// Get pointers to the functions that we need from ntdll.dll.
+NTSTATUS ResolveNtdll(sandbox::PatchInfo* patch_info) {
+ wchar_t* ntdll_name = L"ntdll.dll";
+ HMODULE ntdll = ::GetModuleHandle(ntdll_name);
+ if (!ntdll)
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ void* signal = ::GetProcAddress(ntdll, "NtSignalAndWaitForSingleObject");
+ if (!signal)
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ patch_info->signal_and_wait =
+ reinterpret_cast<NtSignalAndWaitForSingleObjectFunction>(signal);
+
+ return STATUS_SUCCESS;
+}
+
+}; // namespace
+
+namespace sandbox {
+
+NTSTATUS ResolverThunk::Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ if (NULL == thunk_storage || 0 == storage_bytes ||
+ NULL == target_module || NULL == target_name)
+ return STATUS_INVALID_PARAMETER;
+
+ if (storage_bytes < GetThunkSize())
+ return STATUS_BUFFER_TOO_SMALL;
+
+ NTSTATUS ret = STATUS_SUCCESS;
+ if (NULL == interceptor_entry_point) {
+ ret = ResolveInterceptor(interceptor_module, interceptor_name,
+ &interceptor_entry_point);
+ if (!NT_SUCCESS(ret))
+ return ret;
+ }
+
+ ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ interceptor_ = interceptor_entry_point;
+
+ return ret;
+}
+
+NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS Service64ResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point,
+ thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ scoped_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(
+ thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_UNSUCCESSFUL;
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+NTSTATUS Service64ResolverThunk::ResolveInterceptor(
+ const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ // After all, we are using a locally mapped version of the exe, so the
+ // action is the same as for a target function.
+ return ResolveTarget(interceptor_module, interceptor_name,
+ const_cast<void**>(address));
+}
+
+// In this case all the work is done from the parent, so resolve is
+// just a simple GetProcAddress.
+NTSTATUS Service64ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ if (NULL == module)
+ return STATUS_UNSUCCESSFUL;
+
+ *address = ::GetProcAddress(bit_cast<HMODULE>(module), function_name);
+
+ if (NULL == *address)
+ return STATUS_UNSUCCESSFUL;
+
+ return STATUS_SUCCESS;
+}
+
+size_t Service64ResolverThunk::GetThunkSize() const {
+ return sizeof(ServiceFullThunk);
+}
+
+bool Service64ResolverThunk::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 (kMmovR10EcxMovEax != function_code.mov_r10_ecx_mov_eax ||
+ kSyscall != function_code.syscall || kRetNp != function_code.ret)
+ return false;
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS Service64ResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ ServiceFullThunk* full_local_thunk = reinterpret_cast<ServiceFullThunk*>(
+ local_thunk);
+ ServiceFullThunk* full_remote_thunk = reinterpret_cast<ServiceFullThunk*>(
+ remote_thunk);
+
+ // If the source or target are above 4GB we cannot do this relative jump.
+ if (reinterpret_cast<ULONG_PTR>(full_remote_thunk) >
+ static_cast<ULONG_PTR>(ULONG_MAX))
+ return STATUS_CONFLICTING_ADDRESSES;
+
+ if (reinterpret_cast<ULONG_PTR>(target_) > static_cast<ULONG_PTR>(ULONG_MAX))
+ return STATUS_CONFLICTING_ADDRESSES;
+
+ // Patch the original code.
+ Redirected local_service;
+ Redirected* remote_service = reinterpret_cast<Redirected*>(target_);
+ ULONG_PTR diff = reinterpret_cast<BYTE*>(&full_remote_thunk->internal_thunk) -
+ &remote_service->pad;
+ local_service.relative = static_cast<ULONG>(diff);
+
+ // Setup the PatchInfo structure.
+ SIZE_T actual;
+ if (!::ReadProcessMemory(process_, remote_thunk, local_thunk,
+ sizeof(PatchInfo), &actual))
+ return STATUS_UNSUCCESSFUL;
+ if (sizeof(PatchInfo) != actual)
+ return STATUS_UNSUCCESSFUL;
+
+ full_local_thunk->patch_info.orig_MapViewOfSection = reinterpret_cast<
+ NtMapViewOfSectionFunction>(&full_remote_thunk->original);
+ full_local_thunk->patch_info.patch_location = target_;
+ NTSTATUS ret = ResolveNtdll(&full_local_thunk->patch_info);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ // Setup the thunk. The jump out is performed from right after the end of the
+ // thunk (full_remote_thunk + 1).
+ InternalThunk my_thunk;
+ ULONG_PTR patch_info = reinterpret_cast<ULONG_PTR>(remote_thunk);
+ my_thunk.patch_info = static_cast<ULONG>(patch_info);
+ diff = reinterpret_cast<const BYTE*>(interceptor_) -
+ reinterpret_cast<BYTE*>(full_remote_thunk + 1);
+ my_thunk.relative = static_cast<ULONG>(diff);
+
+ memcpy(&full_local_thunk->internal_thunk, &my_thunk, sizeof(my_thunk));
+
+ // copy the local thunk buffer to the child
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
+ sizeof(ServiceFullThunk), &actual))
+ return STATUS_UNSUCCESSFUL;
+
+ if (sizeof(ServiceFullThunk) != actual)
+ return STATUS_UNSUCCESSFUL;
+
+ // and now change the function to intercept, on the child
+ if (!::WriteProtectedChildMemory(process_, target_, &local_service,
+ sizeof(local_service)))
+ return STATUS_UNSUCCESSFUL;
+
+ return STATUS_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/wow_helper/service64_resolver.h b/chromium/sandbox/win/wow_helper/service64_resolver.h
new file mode 100644
index 00000000000..abd7efd813c
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/service64_resolver.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2010 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_WOW_HELPER_SERVICE64_RESOLVER_H__
+#define SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll (64-bit).
+class Service64ResolverThunk : public ResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ explicit Service64ResolverThunk(HANDLE process)
+ : process_(process), ntdll_base_(NULL) {}
+ virtual ~Service64ResolverThunk() {}
+
+ // Implementation of Resolver::Setup.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ // Implementation of Resolver::ResolveInterceptor.
+ virtual NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address);
+
+ // Implementation of Resolver::ResolveTarget.
+ virtual NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address);
+
+ // Implementation of Resolver::GetThunkSize.
+ virtual size_t GetThunkSize() const;
+
+ protected:
+ // The unit test will use this member to allow local patch on a buffer.
+ HMODULE ntdll_base_;
+
+ // Handle of the child process.
+ HANDLE process_;
+
+ 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
+ // by local_thunk (should be directly accessible from the parent).
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ // Performs the actual patch of target_.
+ // local_thunk must be already fully initialized, and the first part must
+ // contain the original code. The real type of this buffer is ServiceFullThunk
+ // (yes, private). remote_thunk (real type ServiceFullThunk), must be
+ // allocated on the child, and will contain the thunk data, after this call.
+ // Returns the apropriate status code.
+ virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk);
+
+ DISALLOW_COPY_AND_ASSIGN(Service64ResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__
diff --git a/chromium/sandbox/win/wow_helper/target_code.cc b/chromium/sandbox/win/wow_helper/target_code.cc
new file mode 100644
index 00000000000..8da27cc5764
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/target_code.cc
@@ -0,0 +1,34 @@
+// Copyright (c) 2006-2008 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/wow_helper/target_code.h"
+
+namespace sandbox {
+
+// Hooks NtMapViewOfSection to detect the load of dlls.
+NTSTATUS WINAPI TargetNtMapViewOfSection(
+ PatchInfo *patch_info, HANDLE process, PVOID *base, ULONG_PTR zero_bits,
+ SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size,
+ SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect) {
+ NTSTATUS ret = patch_info->orig_MapViewOfSection(patch_info->section, process,
+ base, zero_bits, commit_size,
+ offset, view_size, inherit,
+ allocation_type, protect);
+
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -(5 * 10000000); // 5 seconds.
+
+ // The wait is alertable.
+ patch_info->signal_and_wait(patch_info->dll_load, patch_info->continue_load,
+ TRUE, &timeout);
+
+ return ret;
+}
+
+// Marks the end of the code to copy to the target process.
+NTSTATUS WINAPI TargetEnd() {
+ return STATUS_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/wow_helper/target_code.h b/chromium/sandbox/win/wow_helper/target_code.h
new file mode 100644
index 00000000000..c198a852e2d
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/target_code.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2008 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_WOW_HELPER_TARGET_CODE_H__
+#define SANDBOX_WOW_HELPER_TARGET_CODE_H__
+
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Holds the information needed for the interception of NtMapViewOfSection.
+// Changes of this structure must be synchronized with changes of PatchInfo32
+// on sandbox/win/src/wow64.cc.
+struct PatchInfo {
+ HANDLE dll_load; // Event to signal the broker.
+ HANDLE continue_load; // Event to wait for the broker.
+ HANDLE section; // First argument of the call.
+ NtMapViewOfSectionFunction orig_MapViewOfSection;
+ NtSignalAndWaitForSingleObjectFunction signal_and_wait;
+ void* patch_location;
+};
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+NTSTATUS WINAPI TargetNtMapViewOfSection(
+ PatchInfo* patch_info, HANDLE process, PVOID* base, ULONG_PTR zero_bits,
+ SIZE_T commit_size, PLARGE_INTEGER offset, PSIZE_T view_size,
+ SECTION_INHERIT inherit, ULONG allocation_type, ULONG protect);
+
+// Marker of the end of TargetNtMapViewOfSection.
+NTSTATUS WINAPI TargetEnd();
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WOW_HELPER_TARGET_CODE_H__
diff --git a/chromium/sandbox/win/wow_helper/wow_helper.cc b/chromium/sandbox/win/wow_helper/wow_helper.cc
new file mode 100644
index 00000000000..2a27a85e91e
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/wow_helper.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2006-2008 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.
+
+// Wow_helper.exe is a simple Win32 64-bit executable designed to help to
+// sandbox a 32 bit application running on a 64 bit OS. The basic idea is to
+// perform a 64 bit interception of the target process and notify the 32-bit
+// broker process whenever a DLL is being loaded. This allows the broker to
+// setup the interceptions (32-bit) properly on the target.
+
+#include <windows.h>
+
+#include <string>
+
+#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 std::wstring& 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 std::wstring& wide) {
+ return WideToMultiByte(wide, CP_UTF8);
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Performs the interception of NtMapViewOfSection on the 64-bit version of
+// ntdll.dll. 'thunk' is the buffer on the address space of process 'child',
+// that will be used to store the information about the patch.
+int PatchNtdll(HANDLE child, void* thunk, size_t thunk_bytes) {
+ wchar_t* ntdll_name = L"ntdll.dll";
+ HMODULE ntdll_base = ::GetModuleHandle(ntdll_name);
+ if (!ntdll_base)
+ return 100;
+
+ Service64ResolverThunk resolver(child);
+ size_t used = resolver.GetThunkSize();
+ char* code = reinterpret_cast<char*>(thunk) + used;
+ NTSTATUS ret = resolver.Setup(ntdll_base, NULL, "NtMapViewOfSection", NULL,
+ code, thunk, thunk_bytes, NULL);
+ if (!NT_SUCCESS(ret))
+ return 101;
+
+ size_t size = reinterpret_cast<char*>(&TargetEnd) -
+ reinterpret_cast<char*>(&TargetNtMapViewOfSection);
+
+ if (size + used > thunk_bytes)
+ return 102;
+
+ SIZE_T written;
+ if (!::WriteProcessMemory(child, code, &TargetNtMapViewOfSection, size,
+ &written))
+ return 103;
+
+ if (size != written)
+ return 104;
+
+ return 0;
+}
+
+} // namespace sandbox
+
+// We must receive two arguments: the process id of the target to intercept and
+// the address of a page of memory on that process that will be used for the
+// interception. We receive the address because the broker will cleanup the
+// patch when the work is performed.
+//
+// It should be noted that we don't wait until the real work is done; this
+// program quits as soon as the 64-bit interception is performed.
+int wWinMain(HINSTANCE, HINSTANCE, wchar_t* command_line, int) {
+ COMPILE_ASSERT(sizeof(void*) > sizeof(DWORD), unsupported_32_bits);
+ if (!command_line)
+ return 1;
+
+ wchar_t* next;
+ DWORD process_id = wcstoul(command_line, &next, 0);
+ if (!process_id)
+ return 2;
+
+ DWORD access = PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE;
+ HANDLE child = ::OpenProcess(access, FALSE, process_id);
+ if (!child)
+ return 3;
+
+ DWORD buffer = wcstoul(next, NULL, 0);
+ if (!buffer)
+ return 4;
+
+ void* thunk = reinterpret_cast<void*>(static_cast<ULONG_PTR>(buffer));
+
+ const size_t kPageSize = 4096;
+ return sandbox::PatchNtdll(child, thunk, kPageSize);
+}
diff --git a/chromium/sandbox/win/wow_helper/wow_helper.exe b/chromium/sandbox/win/wow_helper/wow_helper.exe
new file mode 100755
index 00000000000..f9bfb4bbdde
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/wow_helper.exe
Binary files differ
diff --git a/chromium/sandbox/win/wow_helper/wow_helper.pdb b/chromium/sandbox/win/wow_helper/wow_helper.pdb
new file mode 100644
index 00000000000..9cb67d001df
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/wow_helper.pdb
Binary files differ
diff --git a/chromium/sandbox/win/wow_helper/wow_helper.vcproj b/chromium/sandbox/win/wow_helper/wow_helper.vcproj
new file mode 100644
index 00000000000..5482fbddce2
--- /dev/null
+++ b/chromium/sandbox/win/wow_helper/wow_helper.vcproj
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wow_helper"
+ ProjectGUID="{BCF3A457-39F1-4DAA-9A65-93CFCD559036}"
+ RootNamespace="wow_helper"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(ProjectDir)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="$(SolutionDir)..;$(SolutionDir)..\third_party\platformsdk_win2008_6_1\files\Include;$(VSInstallDir)\VC\atlmfc\include"
+ PreprocessorDefinitions="_WIN32_WINNT=0x0501;WINVER=0x0501;WIN32;_DEBUG"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="0"
+ RuntimeLibrary="1"
+ BufferSecurityCheck="false"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(ProjectDir)"
+ IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="$(SolutionDir)..;$(SolutionDir)..\third_party\platformsdk_win2008_6_1\files\Include;$(VSInstallDir)\VC\atlmfc\include"
+ PreprocessorDefinitions="_WIN32_WINNT=0x0501;WINVER=0x0501;WIN32;NDEBUG"
+ RuntimeLibrary="0"
+ BufferSecurityCheck="false"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="base"
+ >
+ <File
+ RelativePath="..\..\base\scoped_ptr.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="sandbox"
+ >
+ <File
+ RelativePath="..\src\nt_internals.h"
+ >
+ </File>
+ <File
+ RelativePath="..\src\resolver.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\service64_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\service64_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_code.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_code.h"
+ >
+ </File>
+ <File
+ RelativePath=".\wow_helper.cc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>