summaryrefslogtreecommitdiff
path: root/sandbox
diff options
context:
space:
mode:
authorDaniel Erat <derat@google.com>2015-08-24 12:50:25 -0600
committerDaniel Erat <derat@google.com>2015-08-24 12:50:25 -0600
commit59c5f4b0fb104e8e4806e4934a3d5d112ad695ab (patch)
treea392cfe92bfebf2f2ee13d6bd8ca3e4a09cafa51 /sandbox
parentdb7c5941371d7639578632d11f31fa6698198c36 (diff)
downloadlibchrome-59c5f4b0fb104e8e4806e4934a3d5d112ad695ab.tar.gz
Pull in upstream crypto/ and sandbox/ dirs at r334380.
Add code from https://chromium.googlesource.com/chromium/src/crypto at 3b5d1294 (r333554) and https://chromium.googlesource.com/chromium/src/sandbox at 50337f60 (r334108). These won't be built in AOSP, but they correspond to the versions checked out by Chrome OS. BUG=chromium:521005 Change-Id: Id82858f3a870d8ab9e3e8fe1c3bb598ba065dd14
Diffstat (limited to 'sandbox')
-rw-r--r--sandbox/BUILD.gn23
-rw-r--r--sandbox/OWNERS3
-rw-r--r--sandbox/linux/BUILD.gn382
-rw-r--r--sandbox/linux/DEPS25
-rw-r--r--sandbox/linux/OWNERS3
-rw-r--r--sandbox/linux/bpf_dsl/DEPS5
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.cc363
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl.h317
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_forward.h42
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_impl.h69
-rw-r--r--sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc486
-rw-r--r--sandbox/linux/bpf_dsl/codegen.cc159
-rw-r--r--sandbox/linux/bpf_dsl/codegen.h123
-rw-r--r--sandbox/linux/bpf_dsl/codegen_unittest.cc402
-rw-r--r--sandbox/linux/bpf_dsl/cons.h138
-rw-r--r--sandbox/linux/bpf_dsl/cons_unittest.cc33
-rw-r--r--sandbox/linux/bpf_dsl/dump_bpf.cc109
-rw-r--r--sandbox/linux/bpf_dsl/dump_bpf.h18
-rw-r--r--sandbox/linux/bpf_dsl/linux_syscall_ranges.h57
-rw-r--r--sandbox/linux/bpf_dsl/policy.cc19
-rw-r--r--sandbox/linux/bpf_dsl/policy.h37
-rw-r--r--sandbox/linux/bpf_dsl/policy_compiler.cc499
-rw-r--r--sandbox/linux/bpf_dsl/policy_compiler.h159
-rw-r--r--sandbox/linux/bpf_dsl/seccomp_macros.h295
-rw-r--r--sandbox/linux/bpf_dsl/syscall_set.cc144
-rw-r--r--sandbox/linux/bpf_dsl/syscall_set.h103
-rw-r--r--sandbox/linux/bpf_dsl/syscall_set_unittest.cc124
-rw-r--r--sandbox/linux/bpf_dsl/trap_registry.h73
-rw-r--r--sandbox/linux/bpf_dsl/verifier.cc396
-rw-r--r--sandbox/linux/bpf_dsl/verifier.h57
-rw-r--r--sandbox/linux/integration_tests/DEPS7
-rw-r--r--sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc2259
-rw-r--r--sandbox/linux/integration_tests/namespace_unix_domain_socket_unittest.cc267
-rw-r--r--sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc180
-rw-r--r--sandbox/linux/sandbox_linux.gypi416
-rw-r--r--sandbox/linux/sandbox_linux_nacl_nonsfi.gyp88
-rw-r--r--sandbox/linux/sandbox_linux_test_sources.gypi84
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/DEPS7
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc270
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy.h48
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc334
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc297
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h82
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc319
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h100
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc282
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc1060
-rw-r--r--sandbox/linux/seccomp-bpf-helpers/syscall_sets.h112
-rw-r--r--sandbox/linux/seccomp-bpf/DEPS5
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h54
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tests.h122
-rw-r--r--sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc153
-rw-r--r--sandbox/linux/seccomp-bpf/die.cc93
-rw-r--r--sandbox/linux/seccomp-bpf/die.h68
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.cc115
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode.h203
-rw-r--r--sandbox/linux/seccomp-bpf/errorcode_unittest.cc120
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.cc279
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf.h118
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc65
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h61
-rw-r--r--sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc85
-rw-r--r--sandbox/linux/seccomp-bpf/syscall.cc421
-rw-r--r--sandbox/linux/seccomp-bpf/syscall.h166
-rw-r--r--sandbox/linux/seccomp-bpf/syscall_unittest.cc240
-rw-r--r--sandbox/linux/seccomp-bpf/trap.cc390
-rw-r--r--sandbox/linux/seccomp-bpf/trap.h85
-rw-r--r--sandbox/linux/seccomp-bpf/trap_unittest.cc28
-rw-r--r--sandbox/linux/services/DEPS3
-rw-r--r--sandbox/linux/services/credentials.cc299
-rw-r--r--sandbox/linux/services/credentials.h104
-rw-r--r--sandbox/linux/services/credentials_unittest.cc242
-rw-r--r--sandbox/linux/services/init_process_reaper.cc101
-rw-r--r--sandbox/linux/services/init_process_reaper.h25
-rw-r--r--sandbox/linux/services/libc_urandom_override.cc236
-rw-r--r--sandbox/linux/services/libc_urandom_override.h14
-rw-r--r--sandbox/linux/services/namespace_sandbox.cc208
-rw-r--r--sandbox/linux/services/namespace_sandbox.h101
-rw-r--r--sandbox/linux/services/namespace_sandbox_unittest.cc217
-rw-r--r--sandbox/linux/services/namespace_utils.cc117
-rw-r--r--sandbox/linux/services/namespace_utils.h53
-rw-r--r--sandbox/linux/services/namespace_utils_unittest.cc72
-rw-r--r--sandbox/linux/services/proc_util.cc119
-rw-r--r--sandbox/linux/services/proc_util.h42
-rw-r--r--sandbox/linux/services/proc_util_unittest.cc62
-rw-r--r--sandbox/linux/services/resource_limits.cc26
-rw-r--r--sandbox/linux/services/resource_limits.h29
-rw-r--r--sandbox/linux/services/resource_limits_unittests.cc43
-rw-r--r--sandbox/linux/services/scoped_process.cc119
-rw-r--r--sandbox/linux/services/scoped_process.h55
-rw-r--r--sandbox/linux/services/scoped_process_unittest.cc130
-rw-r--r--sandbox/linux/services/syscall_wrappers.cc246
-rw-r--r--sandbox/linux/services/syscall_wrappers.h83
-rw-r--r--sandbox/linux/services/syscall_wrappers_unittest.cc99
-rw-r--r--sandbox/linux/services/thread_helpers.cc157
-rw-r--r--sandbox/linux/services/thread_helpers.h43
-rw-r--r--sandbox/linux/services/thread_helpers_unittests.cc147
-rw-r--r--sandbox/linux/services/yama.cc115
-rw-r--r--sandbox/linux/services/yama.h57
-rw-r--r--sandbox/linux/services/yama_unittests.cc172
-rw-r--r--sandbox/linux/suid/client/DEPS3
-rw-r--r--sandbox/linux/suid/client/setuid_sandbox_client.cc151
-rw-r--r--sandbox/linux/suid/client/setuid_sandbox_client.h71
-rw-r--r--sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc46
-rw-r--r--sandbox/linux/suid/client/setuid_sandbox_host.cc195
-rw-r--r--sandbox/linux/suid/client/setuid_sandbox_host.h70
-rw-r--r--sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc72
-rw-r--r--sandbox/linux/suid/common/sandbox.h41
-rw-r--r--sandbox/linux/suid/common/suid_unsafe_environment_variables.h73
-rw-r--r--sandbox/linux/suid/process_util.h30
-rw-r--r--sandbox/linux/suid/process_util_linux.c78
-rw-r--r--sandbox/linux/suid/sandbox.c480
-rw-r--r--sandbox/linux/syscall_broker/DEPS3
-rw-r--r--sandbox/linux/syscall_broker/broker_channel.cc35
-rw-r--r--sandbox/linux/syscall_broker/broker_channel.h31
-rw-r--r--sandbox/linux/syscall_broker/broker_client.cc144
-rw-r--r--sandbox/linux/syscall_broker/broker_client.h75
-rw-r--r--sandbox/linux/syscall_broker/broker_common.h41
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission.cc243
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission.h119
-rw-r--r--sandbox/linux/syscall_broker/broker_file_permission_unittest.cc262
-rw-r--r--sandbox/linux/syscall_broker/broker_host.cc231
-rw-r--r--sandbox/linux/syscall_broker/broker_host.h41
-rw-r--r--sandbox/linux/syscall_broker/broker_policy.cc99
-rw-r--r--sandbox/linux/syscall_broker/broker_policy.h87
-rw-r--r--sandbox/linux/syscall_broker/broker_process.cc120
-rw-r--r--sandbox/linux/syscall_broker/broker_process.h94
-rw-r--r--sandbox/linux/syscall_broker/broker_process_unittest.cc656
-rw-r--r--sandbox/linux/system_headers/arm64_linux_syscalls.h1062
-rw-r--r--sandbox/linux/system_headers/arm64_linux_ucontext.h29
-rw-r--r--sandbox/linux/system_headers/arm_linux_syscalls.h1418
-rw-r--r--sandbox/linux/system_headers/arm_linux_ucontext.h67
-rw-r--r--sandbox/linux/system_headers/capability.h42
-rw-r--r--sandbox/linux/system_headers/i386_linux_ucontext.h93
-rw-r--r--sandbox/linux/system_headers/linux_filter.h140
-rw-r--r--sandbox/linux/system_headers/linux_futex.h84
-rw-r--r--sandbox/linux/system_headers/linux_seccomp.h107
-rw-r--r--sandbox/linux/system_headers/linux_signal.h73
-rw-r--r--sandbox/linux/system_headers/linux_syscalls.h37
-rw-r--r--sandbox/linux/system_headers/linux_time.h18
-rw-r--r--sandbox/linux/system_headers/linux_ucontext.h28
-rw-r--r--sandbox/linux/system_headers/mips64_linux_syscalls.h1266
-rw-r--r--sandbox/linux/system_headers/mips_linux_syscalls.h1428
-rw-r--r--sandbox/linux/system_headers/mips_linux_ucontext.h51
-rw-r--r--sandbox/linux/system_headers/x86_32_linux_syscalls.h1426
-rw-r--r--sandbox/linux/system_headers/x86_64_linux_syscalls.h1294
-rw-r--r--sandbox/linux/system_headers/x86_64_linux_ucontext.h88
-rw-r--r--sandbox/linux/tests/main.cc82
-rw-r--r--sandbox/linux/tests/sandbox_test_runner.cc19
-rw-r--r--sandbox/linux/tests/sandbox_test_runner.h30
-rw-r--r--sandbox/linux/tests/sandbox_test_runner_function_pointer.cc25
-rw-r--r--sandbox/linux/tests/sandbox_test_runner_function_pointer.h26
-rw-r--r--sandbox/linux/tests/scoped_temporary_file.cc35
-rw-r--r--sandbox/linux/tests/scoped_temporary_file.h30
-rw-r--r--sandbox/linux/tests/scoped_temporary_file_unittest.cc76
-rw-r--r--sandbox/linux/tests/test_utils.cc42
-rw-r--r--sandbox/linux/tests/test_utils.h29
-rw-r--r--sandbox/linux/tests/test_utils_unittest.cc24
-rw-r--r--sandbox/linux/tests/unit_tests.cc354
-rw-r--r--sandbox/linux/tests/unit_tests.h201
-rw-r--r--sandbox/linux/tests/unit_tests_unittest.cc62
-rw-r--r--sandbox/mac/BUILD.gn101
-rw-r--r--sandbox/mac/OWNERS2
-rw-r--r--sandbox/mac/bootstrap_sandbox.cc133
-rw-r--r--sandbox/mac/bootstrap_sandbox.h114
-rw-r--r--sandbox/mac/bootstrap_sandbox_unittest.mm518
-rw-r--r--sandbox/mac/launchd_interception_server.cc153
-rw-r--r--sandbox/mac/launchd_interception_server.h73
-rw-r--r--sandbox/mac/mach_message_server.cc192
-rw-r--r--sandbox/mac/mach_message_server.h72
-rw-r--r--sandbox/mac/message_server.h71
-rw-r--r--sandbox/mac/os_compatibility.cc135
-rw-r--r--sandbox/mac/os_compatibility.h59
-rw-r--r--sandbox/mac/policy.cc56
-rw-r--r--sandbox/mac/policy.h70
-rw-r--r--sandbox/mac/policy_unittest.cc98
-rw-r--r--sandbox/mac/sandbox_mac.gypi114
-rw-r--r--sandbox/mac/xpc.cc25
-rw-r--r--sandbox/mac/xpc.h50
-rw-r--r--sandbox/mac/xpc_message_server.cc126
-rw-r--r--sandbox/mac/xpc_message_server.h74
-rw-r--r--sandbox/mac/xpc_message_server_unittest.cc238
-rw-r--r--sandbox/mac/xpc_private_stubs.sig19
-rw-r--r--sandbox/mac/xpc_stubs.sig19
-rw-r--r--sandbox/mac/xpc_stubs_header.fragment31
-rw-r--r--sandbox/sandbox.gyp35
-rw-r--r--sandbox/sandbox_export.h29
-rw-r--r--sandbox/sandbox_linux_unittests.isolate27
-rw-r--r--sandbox/win/BUILD.gn302
-rw-r--r--sandbox/win/OWNERS4
-rw-r--r--sandbox/win/sandbox_poc/main_ui_window.cc670
-rw-r--r--sandbox/win/sandbox_poc/main_ui_window.h194
-rw-r--r--sandbox/win/sandbox_poc/pocdll/exports.h89
-rw-r--r--sandbox/win/sandbox_poc/pocdll/fs.cc54
-rw-r--r--sandbox/win/sandbox_poc/pocdll/handles.cc186
-rw-r--r--sandbox/win/sandbox_poc/pocdll/invasive.cc196
-rw-r--r--sandbox/win/sandbox_poc/pocdll/network.cc66
-rw-r--r--sandbox/win/sandbox_poc/pocdll/pocdll.cc27
-rw-r--r--sandbox/win/sandbox_poc/pocdll/pocdll.vcproj218
-rw-r--r--sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc102
-rw-r--r--sandbox/win/sandbox_poc/pocdll/registry.cc49
-rw-r--r--sandbox/win/sandbox_poc/pocdll/spyware.cc68
-rw-r--r--sandbox/win/sandbox_poc/pocdll/utils.h65
-rw-r--r--sandbox/win/sandbox_poc/resource.h30
-rw-r--r--sandbox/win/sandbox_poc/sandbox.cc182
-rw-r--r--sandbox/win/sandbox_poc/sandbox.h10
-rw-r--r--sandbox/win/sandbox_poc/sandbox.icobin0 -> 1078 bytes
-rw-r--r--sandbox/win/sandbox_poc/sandbox.rc136
-rw-r--r--sandbox/win/sandbox_poc/sandbox_poc.vcproj202
-rw-r--r--sandbox/win/sandbox_standalone.sln127
-rw-r--r--sandbox/win/sandbox_win.gypi373
-rw-r--r--sandbox/win/src/Wow64.cc221
-rw-r--r--sandbox/win/src/Wow64.h50
-rw-r--r--sandbox/win/src/Wow64_64.cc18
-rw-r--r--sandbox/win/src/acl.cc125
-rw-r--r--sandbox/win/src/acl.h44
-rw-r--r--sandbox/win/src/address_sanitizer_test.cc107
-rw-r--r--sandbox/win/src/app_container.cc183
-rw-r--r--sandbox/win/src/app_container.h69
-rw-r--r--sandbox/win/src/app_container_test.cc161
-rw-r--r--sandbox/win/src/app_container_unittest.cc58
-rw-r--r--sandbox/win/src/broker_services.cc623
-rw-r--r--sandbox/win/src/broker_services.h118
-rw-r--r--sandbox/win/src/crosscall_client.h483
-rw-r--r--sandbox/win/src/crosscall_params.h296
-rw-r--r--sandbox/win/src/crosscall_server.cc307
-rw-r--r--sandbox/win/src/crosscall_server.h226
-rw-r--r--sandbox/win/src/eat_resolver.cc86
-rw-r--r--sandbox/win/src/eat_resolver.h48
-rw-r--r--sandbox/win/src/file_policy_test.cc674
-rw-r--r--sandbox/win/src/filesystem_dispatcher.cc314
-rw-r--r--sandbox/win/src/filesystem_dispatcher.h72
-rw-r--r--sandbox/win/src/filesystem_interception.cc367
-rw-r--r--sandbox/win/src/filesystem_interception.h53
-rw-r--r--sandbox/win/src/filesystem_policy.cc430
-rw-r--r--sandbox/win/src/filesystem_policy.h113
-rw-r--r--sandbox/win/src/handle_closer.cc194
-rw-r--r--sandbox/win/src/handle_closer.h74
-rw-r--r--sandbox/win/src/handle_closer_agent.cc194
-rw-r--r--sandbox/win/src/handle_closer_agent.h44
-rw-r--r--sandbox/win/src/handle_closer_test.cc297
-rw-r--r--sandbox/win/src/handle_dispatcher.cc90
-rw-r--r--sandbox/win/src/handle_dispatcher.h39
-rw-r--r--sandbox/win/src/handle_inheritance_test.cc52
-rw-r--r--sandbox/win/src/handle_interception.cc45
-rw-r--r--sandbox/win/src/handle_interception.h24
-rw-r--r--sandbox/win/src/handle_policy.cc92
-rw-r--r--sandbox/win/src/handle_policy.h40
-rw-r--r--sandbox/win/src/handle_policy_test.cc114
-rw-r--r--sandbox/win/src/handle_table.cc189
-rw-r--r--sandbox/win/src/handle_table.h162
-rw-r--r--sandbox/win/src/integrity_level_test.cc91
-rw-r--r--sandbox/win/src/interception.cc554
-rw-r--r--sandbox/win/src/interception.h284
-rw-r--r--sandbox/win/src/interception_agent.cc233
-rw-r--r--sandbox/win/src/interception_agent.h87
-rw-r--r--sandbox/win/src/interception_internal.h76
-rw-r--r--sandbox/win/src/interception_unittest.cc212
-rw-r--r--sandbox/win/src/interceptors.h55
-rw-r--r--sandbox/win/src/interceptors_64.cc278
-rw-r--r--sandbox/win/src/interceptors_64.h175
-rw-r--r--sandbox/win/src/internal_types.h76
-rw-r--r--sandbox/win/src/ipc_ping_test.cc58
-rw-r--r--sandbox/win/src/ipc_tags.h40
-rw-r--r--sandbox/win/src/ipc_unittest.cc639
-rw-r--r--sandbox/win/src/job.cc118
-rw-r--r--sandbox/win/src/job.h65
-rw-r--r--sandbox/win/src/job_unittest.cc195
-rw-r--r--sandbox/win/src/named_pipe_dispatcher.cc98
-rw-r--r--sandbox/win/src/named_pipe_dispatcher.h42
-rw-r--r--sandbox/win/src/named_pipe_interception.cc72
-rw-r--r--sandbox/win/src/named_pipe_interception.h36
-rw-r--r--sandbox/win/src/named_pipe_policy.cc86
-rw-r--r--sandbox/win/src/named_pipe_policy.h46
-rw-r--r--sandbox/win/src/named_pipe_policy_test.cc140
-rw-r--r--sandbox/win/src/nt_internals.h690
-rw-r--r--sandbox/win/src/policy_broker.cc116
-rw-r--r--sandbox/win/src/policy_broker.h23
-rw-r--r--sandbox/win/src/policy_engine_opcodes.cc454
-rw-r--r--sandbox/win/src/policy_engine_opcodes.h386
-rw-r--r--sandbox/win/src/policy_engine_params.h202
-rw-r--r--sandbox/win/src/policy_engine_processor.cc107
-rw-r--r--sandbox/win/src/policy_engine_processor.h145
-rw-r--r--sandbox/win/src/policy_engine_unittest.cc102
-rw-r--r--sandbox/win/src/policy_low_level.cc356
-rw-r--r--sandbox/win/src/policy_low_level.h184
-rw-r--r--sandbox/win/src/policy_low_level_unittest.cc618
-rw-r--r--sandbox/win/src/policy_opcodes_unittest.cc369
-rw-r--r--sandbox/win/src/policy_params.h67
-rw-r--r--sandbox/win/src/policy_target.cc117
-rw-r--r--sandbox/win/src/policy_target.h45
-rw-r--r--sandbox/win/src/policy_target_test.cc412
-rw-r--r--sandbox/win/src/process_mitigations.cc331
-rw-r--r--sandbox/win/src/process_mitigations.h44
-rw-r--r--sandbox/win/src/process_mitigations_test.cc248
-rw-r--r--sandbox/win/src/process_mitigations_win32k_dispatcher.cc57
-rw-r--r--sandbox/win/src/process_mitigations_win32k_dispatcher.h31
-rw-r--r--sandbox/win/src/process_mitigations_win32k_interception.cc29
-rw-r--r--sandbox/win/src/process_mitigations_win32k_interception.h46
-rw-r--r--sandbox/win/src/process_mitigations_win32k_policy.cc24
-rw-r--r--sandbox/win/src/process_mitigations_win32k_policy.h35
-rw-r--r--sandbox/win/src/process_policy_test.cc385
-rw-r--r--sandbox/win/src/process_thread_dispatcher.cc249
-rw-r--r--sandbox/win/src/process_thread_dispatcher.h53
-rw-r--r--sandbox/win/src/process_thread_interception.cc403
-rw-r--r--sandbox/win/src/process_thread_interception.h90
-rw-r--r--sandbox/win/src/process_thread_policy.cc239
-rw-r--r--sandbox/win/src/process_thread_policy.h83
-rw-r--r--sandbox/win/src/registry_dispatcher.cc170
-rw-r--r--sandbox/win/src/registry_dispatcher.h47
-rw-r--r--sandbox/win/src/registry_interception.cc224
-rw-r--r--sandbox/win/src/registry_interception.h38
-rw-r--r--sandbox/win/src/registry_policy.cc225
-rw-r--r--sandbox/win/src/registry_policy.h58
-rw-r--r--sandbox/win/src/registry_policy_test.cc289
-rw-r--r--sandbox/win/src/resolver.cc62
-rw-r--r--sandbox/win/src/resolver.h105
-rw-r--r--sandbox/win/src/resolver_32.cc92
-rw-r--r--sandbox/win/src/resolver_64.cc73
-rw-r--r--sandbox/win/src/restricted_token.cc481
-rw-r--r--sandbox/win/src/restricted_token.h193
-rw-r--r--sandbox/win/src/restricted_token_unittest.cc588
-rw-r--r--sandbox/win/src/restricted_token_utils.cc408
-rw-r--r--sandbox/win/src/restricted_token_utils.h100
-rw-r--r--sandbox/win/src/sandbox.cc48
-rw-r--r--sandbox/win/src/sandbox.h165
-rw-r--r--sandbox/win/src/sandbox.vcproj658
-rw-r--r--sandbox/win/src/sandbox_factory.h50
-rw-r--r--sandbox/win/src/sandbox_globals.cc18
-rw-r--r--sandbox/win/src/sandbox_nt_types.h46
-rw-r--r--sandbox/win/src/sandbox_nt_util.cc679
-rw-r--r--sandbox/win/src/sandbox_nt_util.h191
-rw-r--r--sandbox/win/src/sandbox_policy.h256
-rw-r--r--sandbox/win/src/sandbox_policy_base.cc887
-rw-r--r--sandbox/win/src/sandbox_policy_base.h187
-rw-r--r--sandbox/win/src/sandbox_types.h94
-rw-r--r--sandbox/win/src/sandbox_utils.cc32
-rw-r--r--sandbox/win/src/sandbox_utils.h26
-rw-r--r--sandbox/win/src/security_level.h209
-rw-r--r--sandbox/win/src/service_resolver.cc46
-rw-r--r--sandbox/win/src/service_resolver.h139
-rw-r--r--sandbox/win/src/service_resolver_32.cc427
-rw-r--r--sandbox/win/src/service_resolver_64.cc207
-rw-r--r--sandbox/win/src/service_resolver_unittest.cc262
-rw-r--r--sandbox/win/src/shared_handles.cc67
-rw-r--r--sandbox/win/src/shared_handles.h108
-rw-r--r--sandbox/win/src/sharedmem_ipc_client.cc152
-rw-r--r--sandbox/win/src/sharedmem_ipc_client.h136
-rw-r--r--sandbox/win/src/sharedmem_ipc_server.cc423
-rw-r--r--sandbox/win/src/sharedmem_ipc_server.h127
-rw-r--r--sandbox/win/src/sid.cc26
-rw-r--r--sandbox/win/src/sid.h29
-rw-r--r--sandbox/win/src/sid_unittest.cc71
-rw-r--r--sandbox/win/src/sidestep/ia32_modrm_map.cpp92
-rw-r--r--sandbox/win/src/sidestep/ia32_opcode_map.cpp1159
-rw-r--r--sandbox/win/src/sidestep/mini_disassembler.cpp395
-rw-r--r--sandbox/win/src/sidestep/mini_disassembler.h156
-rw-r--r--sandbox/win/src/sidestep/mini_disassembler_types.h197
-rw-r--r--sandbox/win/src/sidestep/preamble_patcher.h111
-rw-r--r--sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp179
-rw-r--r--sandbox/win/src/sidestep_resolver.cc202
-rw-r--r--sandbox/win/src/sidestep_resolver.h73
-rw-r--r--sandbox/win/src/sync_dispatcher.cc82
-rw-r--r--sandbox/win/src/sync_dispatcher.h40
-rw-r--r--sandbox/win/src/sync_interception.cc161
-rw-r--r--sandbox/win/src/sync_interception.h46
-rw-r--r--sandbox/win/src/sync_policy.cc254
-rw-r--r--sandbox/win/src/sync_policy.h51
-rw-r--r--sandbox/win/src/sync_policy_test.cc148
-rw-r--r--sandbox/win/src/sync_policy_test.h18
-rw-r--r--sandbox/win/src/target_interceptions.cc100
-rw-r--r--sandbox/win/src/target_interceptions.h35
-rw-r--r--sandbox/win/src/target_process.cc385
-rw-r--r--sandbox/win/src/target_process.h135
-rw-r--r--sandbox/win/src/target_services.cc209
-rw-r--r--sandbox/win/src/target_services.h67
-rw-r--r--sandbox/win/src/threadpool_unittest.cc94
-rw-r--r--sandbox/win/src/unload_dll_test.cc96
-rw-r--r--sandbox/win/src/win2k_threadpool.cc64
-rw-r--r--sandbox/win/src/win2k_threadpool.h58
-rw-r--r--sandbox/win/src/win_utils.cc432
-rw-r--r--sandbox/win/src/win_utils.h117
-rw-r--r--sandbox/win/src/win_utils_unittest.cc113
-rw-r--r--sandbox/win/src/window.cc161
-rw-r--r--sandbox/win/src/window.h40
-rw-r--r--sandbox/win/tests/common/controller.cc363
-rw-r--r--sandbox/win/tests/common/controller.h159
-rw-r--r--sandbox/win/tests/common/test_utils.cc72
-rw-r--r--sandbox/win/tests/common/test_utils.h19
-rw-r--r--sandbox/win/tests/integration_tests/integration_tests.cc25
-rw-r--r--sandbox/win/tests/integration_tests/integration_tests_test.cc306
-rw-r--r--sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj242
-rw-r--r--sandbox/win/tests/unit_tests/sbox_unittests.vcproj258
-rw-r--r--sandbox/win/tests/unit_tests/unit_tests.cc23
-rw-r--r--sandbox/win/tests/validation_tests/commands.cc289
-rw-r--r--sandbox/win/tests/validation_tests/commands.h48
-rw-r--r--sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj216
-rw-r--r--sandbox/win/tests/validation_tests/suite.cc241
-rw-r--r--sandbox/win/tests/validation_tests/unit_tests.cc23
-rw-r--r--sandbox/win/tools/finder/finder.cc64
-rw-r--r--sandbox/win/tools/finder/finder.h143
-rw-r--r--sandbox/win/tools/finder/finder.vcproj201
-rw-r--r--sandbox/win/tools/finder/finder_fs.cc117
-rw-r--r--sandbox/win/tools/finder/finder_kernel.cc248
-rw-r--r--sandbox/win/tools/finder/finder_registry.cc93
-rw-r--r--sandbox/win/tools/finder/main.cc147
-rw-r--r--sandbox/win/tools/finder/ntundoc.h275
-rw-r--r--sandbox/win/tools/launcher/launcher.cc156
-rw-r--r--sandbox/win/tools/launcher/launcher.vcproj177
-rw-r--r--sandbox/win/wow_helper.sln19
-rw-r--r--sandbox/win/wow_helper/service64_resolver.cc342
-rw-r--r--sandbox/win/wow_helper/service64_resolver.h72
-rw-r--r--sandbox/win/wow_helper/target_code.cc34
-rw-r--r--sandbox/win/wow_helper/target_code.h41
-rw-r--r--sandbox/win/wow_helper/wow_helper.cc86
-rwxr-xr-xsandbox/win/wow_helper/wow_helper.exebin0 -> 67072 bytes
-rw-r--r--sandbox/win/wow_helper/wow_helper.pdbbin0 -> 699392 bytes
-rw-r--r--sandbox/win/wow_helper/wow_helper.vcproj223
418 files changed, 73146 insertions, 0 deletions
diff --git a/sandbox/BUILD.gn b/sandbox/BUILD.gn
new file mode 100644
index 0000000000..15fb62092f
--- /dev/null
+++ b/sandbox/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Meta-target that forwards to the proper platform one.
+group("sandbox") {
+ if (is_win) {
+ deps = [
+ "//sandbox/win:sandbox",
+ ]
+ } else if (is_mac) {
+ # TODO(GYP): Make sandbox compile w/ 10.6 SDK.
+ if (false) {
+ deps = [
+ "//sandbox/mac:sandbox",
+ ]
+ }
+ } else if (is_linux || is_android) {
+ deps = [
+ "//sandbox/linux:sandbox",
+ ]
+ }
+}
diff --git a/sandbox/OWNERS b/sandbox/OWNERS
new file mode 100644
index 0000000000..5d3f6ff9e1
--- /dev/null
+++ b/sandbox/OWNERS
@@ -0,0 +1,3 @@
+cpu@chromium.org
+jln@chromium.org
+jschuh@chromium.org
diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn
new file mode 100644
index 0000000000..a1a77204f5
--- /dev/null
+++ b/sandbox/linux/BUILD.gn
@@ -0,0 +1,382 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/features.gni")
+import("//testing/test.gni")
+
+declare_args() {
+ compile_suid_client = is_linux
+
+ compile_credentials = is_linux
+
+ # On Android, use plain GTest.
+ use_base_test_suite = is_linux
+}
+
+# We have two principal targets: sandbox and sandbox_linux_unittests
+# All other targets are listed as dependencies.
+# There is one notable exception: for historical reasons, chrome_sandbox is
+# the setuid sandbox and is its own target.
+
+group("sandbox") {
+ deps = [
+ ":sandbox_services",
+ ]
+
+ if (compile_suid_client) {
+ deps += [ ":suid_sandbox_client" ]
+ }
+ if (use_seccomp_bpf) {
+ deps += [
+ ":seccomp_bpf",
+ ":seccomp_bpf_helpers",
+ ]
+ }
+}
+
+source_set("sandbox_linux_test_utils") {
+ testonly = true
+ sources = [
+ "tests/sandbox_test_runner.cc",
+ "tests/sandbox_test_runner.h",
+ "tests/sandbox_test_runner_function_pointer.cc",
+ "tests/sandbox_test_runner_function_pointer.h",
+ "tests/test_utils.cc",
+ "tests/test_utils.h",
+ "tests/unit_tests.cc",
+ "tests/unit_tests.h",
+ ]
+
+ deps = [
+ "//testing/gtest",
+ ]
+
+ if (use_seccomp_bpf) {
+ sources += [
+ "seccomp-bpf/bpf_tester_compatibility_delegate.h",
+ "seccomp-bpf/bpf_tests.h",
+ "seccomp-bpf/sandbox_bpf_test_runner.cc",
+ "seccomp-bpf/sandbox_bpf_test_runner.h",
+ ]
+ deps += [ ":seccomp_bpf" ]
+ }
+
+ if (use_base_test_suite) {
+ deps += [ "//base/test:test_support" ]
+ defines = [ "SANDBOX_USES_BASE_TEST_SUITE" ]
+ }
+}
+
+# Sources shared by sandbox_linux_unittests and sandbox_linux_jni_unittests.
+source_set("sandbox_linux_unittests_sources") {
+ testonly = true
+
+ sources = [
+ "services/proc_util_unittest.cc",
+ "services/resource_limits_unittests.cc",
+ "services/scoped_process_unittest.cc",
+ "services/syscall_wrappers_unittest.cc",
+ "services/thread_helpers_unittests.cc",
+ "services/yama_unittests.cc",
+ "syscall_broker/broker_file_permission_unittest.cc",
+ "syscall_broker/broker_process_unittest.cc",
+ "tests/main.cc",
+ "tests/scoped_temporary_file.cc",
+ "tests/scoped_temporary_file.h",
+ "tests/scoped_temporary_file_unittest.cc",
+ "tests/test_utils_unittest.cc",
+ "tests/unit_tests_unittest.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ ":sandbox_linux_test_utils",
+ "//base",
+ "//testing/gtest",
+ ]
+
+ if (use_base_test_suite) {
+ deps += [ "//base/test:test_support" ]
+ defines = [ "SANDBOX_USES_BASE_TEST_SUITE" ]
+ }
+
+ if (is_linux) {
+ # Don't use this on Android.
+ libs = [ "rt" ]
+ }
+
+ if (compile_suid_client) {
+ sources += [
+ "suid/client/setuid_sandbox_client_unittest.cc",
+ "suid/client/setuid_sandbox_host_unittest.cc",
+ ]
+ }
+ if (use_seccomp_bpf) {
+ sources += [
+ "bpf_dsl/bpf_dsl_unittest.cc",
+ "bpf_dsl/codegen_unittest.cc",
+ "bpf_dsl/cons_unittest.cc",
+ "bpf_dsl/syscall_set_unittest.cc",
+ "integration_tests/bpf_dsl_seccomp_unittest.cc",
+ "integration_tests/seccomp_broker_process_unittest.cc",
+ "seccomp-bpf-helpers/baseline_policy_unittest.cc",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc",
+ "seccomp-bpf/bpf_tests_unittest.cc",
+ "seccomp-bpf/errorcode_unittest.cc",
+ "seccomp-bpf/sandbox_bpf_unittest.cc",
+ "seccomp-bpf/syscall_unittest.cc",
+ "seccomp-bpf/trap_unittest.cc",
+ ]
+ }
+ if (compile_credentials) {
+ sources += [
+ "integration_tests/namespace_unix_domain_socket_unittest.cc",
+ "services/credentials_unittest.cc",
+ "services/namespace_utils_unittest.cc",
+ ]
+
+ if (use_base_test_suite) {
+ # Tests that use advanced features not available in stock GTest.
+ sources += [ "services/namespace_sandbox_unittest.cc" ]
+ }
+
+ # For credentials_unittest.cc
+ configs += [ "//build/config/linux:libcap" ]
+ }
+}
+
+# The main sandboxing test target.
+test("sandbox_linux_unittests") {
+ deps = [
+ ":sandbox_linux_unittests_sources",
+ ]
+}
+
+# This target is the shared library used by Android APK (i.e.
+# JNI-friendly) tests.
+shared_library("sandbox_linux_jni_unittests") {
+ testonly = true
+ deps = [
+ ":sandbox_linux_unittests_sources",
+ ]
+ if (is_android) {
+ deps += [ "//testing/android/native_test:native_test_native_code" ]
+ }
+}
+
+component("seccomp_bpf") {
+ sources = [
+ "bpf_dsl/bpf_dsl.cc",
+ "bpf_dsl/bpf_dsl.h",
+ "bpf_dsl/bpf_dsl_forward.h",
+ "bpf_dsl/bpf_dsl_impl.h",
+ "bpf_dsl/codegen.cc",
+ "bpf_dsl/codegen.h",
+ "bpf_dsl/cons.h",
+ "bpf_dsl/dump_bpf.cc",
+ "bpf_dsl/dump_bpf.h",
+ "bpf_dsl/linux_syscall_ranges.h",
+ "bpf_dsl/policy.cc",
+ "bpf_dsl/policy.h",
+ "bpf_dsl/policy_compiler.cc",
+ "bpf_dsl/policy_compiler.h",
+ "bpf_dsl/seccomp_macros.h",
+ "bpf_dsl/syscall_set.cc",
+ "bpf_dsl/syscall_set.h",
+ "bpf_dsl/trap_registry.h",
+ "bpf_dsl/verifier.cc",
+ "bpf_dsl/verifier.h",
+ "seccomp-bpf/die.cc",
+ "seccomp-bpf/die.h",
+ "seccomp-bpf/errorcode.cc",
+ "seccomp-bpf/errorcode.h",
+ "seccomp-bpf/sandbox_bpf.cc",
+ "seccomp-bpf/sandbox_bpf.h",
+ "seccomp-bpf/syscall.cc",
+ "seccomp-bpf/syscall.h",
+ "seccomp-bpf/trap.cc",
+ "seccomp-bpf/trap.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services",
+ ":sandbox_services_headers",
+ "//base",
+ ]
+}
+
+component("seccomp_bpf_helpers") {
+ sources = [
+ "seccomp-bpf-helpers/baseline_policy.cc",
+ "seccomp-bpf-helpers/baseline_policy.h",
+ "seccomp-bpf-helpers/sigsys_handlers.cc",
+ "seccomp-bpf-helpers/sigsys_handlers.h",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions.cc",
+ "seccomp-bpf-helpers/syscall_parameters_restrictions.h",
+ "seccomp-bpf-helpers/syscall_sets.cc",
+ "seccomp-bpf-helpers/syscall_sets.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ ":sandbox_services",
+ ":seccomp_bpf",
+ ]
+}
+
+if (is_linux) {
+ # The setuid sandbox for Linux.
+ executable("chrome_sandbox") {
+ sources = [
+ "suid/common/sandbox.h",
+ "suid/common/suid_unsafe_environment_variables.h",
+ "suid/process_util.h",
+ "suid/process_util_linux.c",
+ "suid/sandbox.c",
+ ]
+
+ cflags = [
+ # For ULLONG_MAX
+ "-std=gnu99",
+
+ # These files have a suspicious comparison.
+ # TODO fix this and re-enable this warning.
+ "-Wno-sign-compare",
+ ]
+ }
+}
+
+component("sandbox_services") {
+ sources = [
+ "services/init_process_reaper.cc",
+ "services/init_process_reaper.h",
+ "services/proc_util.cc",
+ "services/proc_util.h",
+ "services/resource_limits.cc",
+ "services/resource_limits.h",
+ "services/scoped_process.cc",
+ "services/scoped_process.h",
+ "services/syscall_wrappers.cc",
+ "services/syscall_wrappers.h",
+ "services/thread_helpers.cc",
+ "services/thread_helpers.h",
+ "services/yama.cc",
+ "services/yama.h",
+ "syscall_broker/broker_channel.cc",
+ "syscall_broker/broker_channel.h",
+ "syscall_broker/broker_client.cc",
+ "syscall_broker/broker_client.h",
+ "syscall_broker/broker_common.h",
+ "syscall_broker/broker_file_permission.cc",
+ "syscall_broker/broker_file_permission.h",
+ "syscall_broker/broker_host.cc",
+ "syscall_broker/broker_host.h",
+ "syscall_broker/broker_policy.cc",
+ "syscall_broker/broker_policy.h",
+ "syscall_broker/broker_process.cc",
+ "syscall_broker/broker_process.h",
+ ]
+
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ ]
+
+ if (compile_credentials) {
+ sources += [
+ "services/credentials.cc",
+ "services/credentials.h",
+ "services/namespace_sandbox.cc",
+ "services/namespace_sandbox.h",
+ "services/namespace_utils.cc",
+ "services/namespace_utils.h",
+ ]
+
+ deps += [ ":sandbox_services_headers" ]
+ }
+}
+
+source_set("sandbox_services_headers") {
+ sources = [
+ "system_headers/arm64_linux_syscalls.h",
+ "system_headers/arm64_linux_ucontext.h",
+ "system_headers/arm_linux_syscalls.h",
+ "system_headers/arm_linux_ucontext.h",
+ "system_headers/i386_linux_ucontext.h",
+ "system_headers/linux_futex.h",
+ "system_headers/linux_seccomp.h",
+ "system_headers/linux_signal.h",
+ "system_headers/linux_syscalls.h",
+ "system_headers/linux_time.h",
+ "system_headers/linux_ucontext.h",
+ "system_headers/x86_32_linux_syscalls.h",
+ "system_headers/x86_64_linux_syscalls.h",
+ ]
+}
+
+# We make this its own target so that it does not interfere with our tests.
+source_set("libc_urandom_override") {
+ sources = [
+ "services/libc_urandom_override.cc",
+ "services/libc_urandom_override.h",
+ ]
+ deps = [
+ "//base",
+ ]
+}
+
+if (compile_suid_client) {
+ component("suid_sandbox_client") {
+ sources = [
+ "suid/client/setuid_sandbox_client.cc",
+ "suid/client/setuid_sandbox_client.h",
+ "suid/client/setuid_sandbox_host.cc",
+ "suid/client/setuid_sandbox_host.h",
+ "suid/common/sandbox.h",
+ "suid/common/suid_unsafe_environment_variables.h",
+ ]
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+
+ deps = [
+ ":sandbox_services",
+ "//base",
+ ]
+ }
+}
+
+if (is_android) {
+ # TODO(GYP) enable this. Needs an android_strip wrapper python script.
+ #action("sandbox_linux_unittests_stripped") {
+ # script = "android_stip.py"
+ #
+ # in_file = "$root_out_dir/sandbox_linux_unittests"
+ #
+ # out_file = "$root_out_dir/sandbox_linux_unittests_stripped"
+ # outputs = [ out_file ]
+ #
+ # args = [
+ # rebase_path(in_file, root_build_dir),
+ # "-o", rebase_path(out_file, root_build_dir),
+ # ]
+ #
+ # deps = [
+ # ":sandbox_linux_unittests",
+ # ]
+ #}
+ # TODO(GYP) convert this.
+ # {
+ # 'target_name': 'sandbox_linux_jni_unittests_apk',
+ # 'type': 'none',
+ # 'variables': {
+ # 'test_suite_name': 'sandbox_linux_jni_unittests',
+ # },
+ # 'dependencies': [
+ # 'sandbox_linux_jni_unittests',
+ # ],
+ # 'includes': [ '../../build/apk_test.gypi' ],
+ # }
+}
diff --git a/sandbox/linux/DEPS b/sandbox/linux/DEPS
new file mode 100644
index 0000000000..3912859344
--- /dev/null
+++ b/sandbox/linux/DEPS
@@ -0,0 +1,25 @@
+include_rules = [
+ # First, exclude everything.
+ # Exclude a few dependencies that are included in the root DEPS and that we
+ # don't need.
+ # Sadly, there is no way to exclude all root DEPS since the root has no name.
+ "-ipc",
+ "-library_loaders",
+ "-third_party",
+ "-url",
+ # Make sure that each subdirectory has to declare its dependencies in
+ # sandbox/ explicitly.
+ "-sandbox/linux",
+
+ # Second, add what we want to allow.
+ # Anything included from sandbox/linux must be declared after this line or in
+ # a more specific DEPS file.
+ # base/, build/ and testing/ are already included in the global DEPS file,
+ # but be explicit.
+ "+base",
+ "+build",
+ "+testing",
+ "+sandbox/sandbox_export.h",
+ # Everyone can use tests/
+ "+sandbox/linux/tests",
+]
diff --git a/sandbox/linux/OWNERS b/sandbox/linux/OWNERS
new file mode 100644
index 0000000000..f39e96736d
--- /dev/null
+++ b/sandbox/linux/OWNERS
@@ -0,0 +1,3 @@
+jln@chromium.org
+jorgelo@chromium.org
+mdempsky@chromium.org
diff --git a/sandbox/linux/bpf_dsl/DEPS b/sandbox/linux/bpf_dsl/DEPS
new file mode 100644
index 0000000000..be37a129c2
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ # TODO(mdempsky): Eliminate cyclic dependency on seccomp-bpf.
+ "+sandbox/linux/seccomp-bpf",
+ "+sandbox/linux/system_headers",
+]
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.cc b/sandbox/linux/bpf_dsl/bpf_dsl.cc
new file mode 100644
index 0000000000..3a35903ec9
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -0,0 +1,363 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) {
+ SANDBOX_DIE(static_cast<char*>(aux));
+}
+
+class AllowResultExprImpl : public internal::ResultExprImpl {
+ public:
+ AllowResultExprImpl() {}
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return ErrorCode(ErrorCode::ERR_ALLOWED);
+ }
+
+ bool IsAllow() const override { return true; }
+
+ private:
+ ~AllowResultExprImpl() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(AllowResultExprImpl);
+};
+
+class ErrorResultExprImpl : public internal::ResultExprImpl {
+ public:
+ explicit ErrorResultExprImpl(int err) : err_(err) {
+ CHECK(err_ >= ErrorCode::ERR_MIN_ERRNO && err_ <= ErrorCode::ERR_MAX_ERRNO);
+ }
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Error(err_);
+ }
+
+ bool IsDeny() const override { return true; }
+
+ private:
+ ~ErrorResultExprImpl() override {}
+
+ int err_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrorResultExprImpl);
+};
+
+class TraceResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TraceResultExprImpl(uint16_t aux) : aux_(aux) {}
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return ErrorCode(ErrorCode::ERR_TRACE + aux_);
+ }
+
+ private:
+ ~TraceResultExprImpl() override {}
+
+ uint16_t aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceResultExprImpl);
+};
+
+class TrapResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg, bool safe)
+ : func_(func), arg_(arg), safe_(safe) {
+ DCHECK(func_);
+ }
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return pc->Trap(func_, arg_, safe_);
+ }
+
+ bool HasUnsafeTraps() const override { return safe_ == false; }
+
+ bool IsDeny() const override { return true; }
+
+ private:
+ ~TrapResultExprImpl() override {}
+
+ TrapRegistry::TrapFnc func_;
+ const void* arg_;
+ bool safe_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
+};
+
+class IfThenResultExprImpl : public internal::ResultExprImpl {
+ public:
+ IfThenResultExprImpl(const BoolExpr& cond,
+ const ResultExpr& then_result,
+ const ResultExpr& else_result)
+ : cond_(cond), then_result_(then_result), else_result_(else_result) {}
+
+ ErrorCode Compile(PolicyCompiler* pc) const override {
+ return cond_->Compile(
+ pc, then_result_->Compile(pc), else_result_->Compile(pc));
+ }
+
+ bool HasUnsafeTraps() const override {
+ return then_result_->HasUnsafeTraps() || else_result_->HasUnsafeTraps();
+ }
+
+ private:
+ ~IfThenResultExprImpl() override {}
+
+ BoolExpr cond_;
+ ResultExpr then_result_;
+ ResultExpr else_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(IfThenResultExprImpl);
+};
+
+class ConstBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ ConstBoolExprImpl(bool value) : value_(value) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return value_ ? true_ec : false_ec;
+ }
+
+ private:
+ ~ConstBoolExprImpl() override {}
+
+ bool value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstBoolExprImpl);
+};
+
+class PrimitiveBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ PrimitiveBoolExprImpl(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value)
+ : argno_(argno), is_32bit_(is_32bit), mask_(mask), value_(value) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return pc->CondMaskedEqual(
+ argno_, is_32bit_, mask_, value_, true_ec, false_ec);
+ }
+
+ private:
+ ~PrimitiveBoolExprImpl() override {}
+
+ int argno_;
+ ErrorCode::ArgType is_32bit_;
+ uint64_t mask_;
+ uint64_t value_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveBoolExprImpl);
+};
+
+class NegateBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ explicit NegateBoolExprImpl(const BoolExpr& cond) : cond_(cond) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return cond_->Compile(pc, false_ec, true_ec);
+ }
+
+ private:
+ ~NegateBoolExprImpl() override {}
+
+ BoolExpr cond_;
+
+ DISALLOW_COPY_AND_ASSIGN(NegateBoolExprImpl);
+};
+
+class AndBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ AndBoolExprImpl(const BoolExpr& lhs, const BoolExpr& rhs)
+ : lhs_(lhs), rhs_(rhs) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return lhs_->Compile(pc, rhs_->Compile(pc, true_ec, false_ec), false_ec);
+ }
+
+ private:
+ ~AndBoolExprImpl() override {}
+
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndBoolExprImpl);
+};
+
+class OrBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ OrBoolExprImpl(const BoolExpr& lhs, const BoolExpr& rhs)
+ : lhs_(lhs), rhs_(rhs) {}
+
+ ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const override {
+ return lhs_->Compile(pc, true_ec, rhs_->Compile(pc, true_ec, false_ec));
+ }
+
+ private:
+ ~OrBoolExprImpl() override {}
+
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrBoolExprImpl);
+};
+
+} // namespace
+
+namespace internal {
+
+bool ResultExprImpl::HasUnsafeTraps() const {
+ return false;
+}
+
+bool ResultExprImpl::IsAllow() const {
+ return false;
+}
+
+bool ResultExprImpl::IsDeny() const {
+ return false;
+}
+
+uint64_t DefaultMask(size_t size) {
+ switch (size) {
+ case 4:
+ return std::numeric_limits<uint32_t>::max();
+ case 8:
+ return std::numeric_limits<uint64_t>::max();
+ default:
+ CHECK(false) << "Unimplemented DefaultMask case";
+ return 0;
+ }
+}
+
+BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) {
+ CHECK(size == 4 || size == 8);
+
+ // TODO(mdempsky): Should we just always use TP_64BIT?
+ const ErrorCode::ArgType arg_type =
+ (size == 4) ? ErrorCode::TP_32BIT : ErrorCode::TP_64BIT;
+
+ return BoolExpr(new const PrimitiveBoolExprImpl(num, arg_type, mask, val));
+}
+
+} // namespace internal
+
+ResultExpr Allow() {
+ return ResultExpr(new const AllowResultExprImpl());
+}
+
+ResultExpr Error(int err) {
+ return ResultExpr(new const ErrorResultExprImpl(err));
+}
+
+ResultExpr Kill(const char* msg) {
+ return Trap(BPFFailure, msg);
+}
+
+ResultExpr Trace(uint16_t aux) {
+ return ResultExpr(new const TraceResultExprImpl(aux));
+}
+
+ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return ResultExpr(
+ new const TrapResultExprImpl(trap_func, aux, true /* safe */));
+}
+
+ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return ResultExpr(
+ new const TrapResultExprImpl(trap_func, aux, false /* unsafe */));
+}
+
+BoolExpr BoolConst(bool value) {
+ return BoolExpr(new const ConstBoolExprImpl(value));
+}
+
+BoolExpr operator!(const BoolExpr& cond) {
+ return BoolExpr(new const NegateBoolExprImpl(cond));
+}
+
+BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs) {
+ return BoolExpr(new const AndBoolExprImpl(lhs, rhs));
+}
+
+BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs) {
+ return BoolExpr(new const OrBoolExprImpl(lhs, rhs));
+}
+
+Elser If(const BoolExpr& cond, const ResultExpr& then_result) {
+ return Elser(nullptr).ElseIf(cond, then_result);
+}
+
+Elser::Elser(cons::List<Clause> clause_list) : clause_list_(clause_list) {
+}
+
+Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) {
+}
+
+Elser::~Elser() {
+}
+
+Elser Elser::ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const {
+ return Elser(Cons(std::make_pair(cond, then_result), clause_list_));
+}
+
+ResultExpr Elser::Else(const ResultExpr& else_result) const {
+ // We finally have the default result expression for this
+ // if/then/else sequence. Also, we've already accumulated all
+ // if/then pairs into a list of reverse order (i.e., lower priority
+ // conditions are listed before higher priority ones). E.g., an
+ // expression like
+ //
+ // If(b1, e1).ElseIf(b2, e2).ElseIf(b3, e3).Else(e4)
+ //
+ // will have built up a list like
+ //
+ // [(b3, e3), (b2, e2), (b1, e1)].
+ //
+ // Now that we have e4, we can walk the list and create a ResultExpr
+ // tree like:
+ //
+ // expr = e4
+ // expr = (b3 ? e3 : expr) = (b3 ? e3 : e4)
+ // expr = (b2 ? e2 : expr) = (b2 ? e2 : (b3 ? e3 : e4))
+ // expr = (b1 ? e1 : expr) = (b1 ? e1 : (b2 ? e2 : (b3 ? e3 : e4)))
+ //
+ // and end up with an appropriately chained tree.
+
+ ResultExpr expr = else_result;
+ for (const Clause& clause : clause_list_) {
+ expr = ResultExpr(
+ new const IfThenResultExprImpl(clause.first, clause.second, expr));
+ }
+ return expr;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+template class scoped_refptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+template class scoped_refptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl.h b/sandbox/linux/bpf_dsl/bpf_dsl.h
new file mode 100644
index 0000000000..365e9b5466
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -0,0 +1,317 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+
+#include <stdint.h>
+
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/cons.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/sandbox_export.h"
+
+// The sandbox::bpf_dsl namespace provides a domain-specific language
+// to make writing BPF policies more expressive. In general, the
+// object types all have value semantics (i.e., they can be copied
+// around, returned from or passed to function calls, etc. without any
+// surprising side effects), though not all support assignment.
+//
+// An idiomatic and demonstrative (albeit silly) example of this API
+// would be:
+//
+// #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+//
+// using namespace sandbox::bpf_dsl;
+//
+// class SillyPolicy : public Policy {
+// public:
+// SillyPolicy() {}
+// ~SillyPolicy() override {}
+// ResultExpr EvaluateSyscall(int sysno) const override {
+// if (sysno == __NR_fcntl) {
+// Arg<int> fd(0), cmd(1);
+// Arg<unsigned long> flags(2);
+// const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
+// return If(fd == 0 && cmd == F_SETFL && (flags & ~kGoodFlags) == 0,
+// Allow())
+// .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC,
+// Error(EMFILE))
+// .Else(Trap(SetFlagHandler, NULL));
+// } else {
+// return Allow();
+// }
+// }
+//
+// private:
+// DISALLOW_COPY_AND_ASSIGN(SillyPolicy);
+// };
+//
+// More generally, the DSL currently supports the following grammar:
+//
+// result = Allow() | Error(errno) | Kill(msg) | Trace(aux)
+// | Trap(trap_func, aux) | UnsafeTrap(trap_func, aux)
+// | If(bool, result)[.ElseIf(bool, result)].Else(result)
+// | Switch(arg)[.Case(val, result)].Default(result)
+// bool = BoolConst(boolean) | !bool | bool && bool | bool || bool
+// | arg == val | arg != val
+// arg = Arg<T>(num) | arg & mask
+//
+// The semantics of each function and operator are intended to be
+// intuitive, but are described in more detail below.
+//
+// (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
+// talk at Going Native 2013 for promoting value semantics via shared
+// pointers to immutable state.)
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// ResultExpr is an opaque reference to an immutable result expression tree.
+typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
+
+// BoolExpr is an opaque reference to an immutable boolean expression tree.
+typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
+
+// Allow specifies a result that the system call should be allowed to
+// execute normally.
+SANDBOX_EXPORT ResultExpr Allow();
+
+// Error specifies a result that the system call should fail with
+// error number |err|. As a special case, Error(0) will result in the
+// system call appearing to have succeeded, but without having any
+// side effects.
+SANDBOX_EXPORT ResultExpr Error(int err);
+
+// Kill specifies a result to kill the program and print an error message.
+SANDBOX_EXPORT ResultExpr Kill(const char* msg);
+
+// Trace specifies a result to notify a tracing process via the
+// PTRACE_EVENT_SECCOMP event and allow it to change or skip the system call.
+// The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
+SANDBOX_EXPORT ResultExpr Trace(uint16_t aux);
+
+// Trap specifies a result that the system call should be handled by
+// trapping back into userspace and invoking |trap_func|, passing
+// |aux| as the second parameter.
+SANDBOX_EXPORT ResultExpr
+ Trap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// UnsafeTrap is like Trap, except the policy is marked as "unsafe"
+// and allowed to use SandboxSyscall to invoke any system call.
+//
+// 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.
+SANDBOX_EXPORT ResultExpr
+ UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// BoolConst converts a bool value into a BoolExpr.
+SANDBOX_EXPORT BoolExpr BoolConst(bool value);
+
+// Various ways to combine boolean expressions into more complex expressions.
+// They follow standard boolean algebra laws.
+SANDBOX_EXPORT BoolExpr operator!(const BoolExpr& cond);
+SANDBOX_EXPORT BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs);
+SANDBOX_EXPORT BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs);
+
+template <typename T>
+class SANDBOX_EXPORT Arg {
+ public:
+ // Initializes the Arg to represent the |num|th system call
+ // argument (indexed from 0), which is of type |T|.
+ explicit Arg(int num);
+
+ Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
+
+ // Returns an Arg representing the current argument, but after
+ // bitwise-and'ing it with |rhs|.
+ friend Arg operator&(const Arg& lhs, uint64_t rhs) {
+ return Arg(lhs.num_, lhs.mask_ & rhs);
+ }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) equals |rhs|.
+ friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) does not equal |rhs|.
+ friend BoolExpr operator!=(const Arg& lhs, T rhs) { return !(lhs == rhs); }
+
+ private:
+ Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
+
+ BoolExpr EqualTo(T val) const;
+
+ int num_;
+ uint64_t mask_;
+
+ DISALLOW_ASSIGN(Arg);
+};
+
+// If begins a conditional result expression predicated on the
+// specified boolean expression.
+SANDBOX_EXPORT Elser If(const BoolExpr& cond, const ResultExpr& then_result);
+
+class SANDBOX_EXPORT Elser {
+ public:
+ Elser(const Elser& elser);
+ ~Elser();
+
+ // ElseIf extends the conditional result expression with another
+ // "if then" clause, predicated on the specified boolean expression.
+ Elser ElseIf(const BoolExpr& cond, const ResultExpr& then_result) const;
+
+ // Else terminates a conditional result expression using |else_result| as
+ // the default fallback result expression.
+ ResultExpr Else(const ResultExpr& else_result) const;
+
+ private:
+ typedef std::pair<BoolExpr, ResultExpr> Clause;
+
+ explicit Elser(cons::List<Clause> clause_list);
+
+ cons::List<Clause> clause_list_;
+
+ friend Elser If(const BoolExpr&, const ResultExpr&);
+ template <typename T>
+ friend Caser<T> Switch(const Arg<T>&);
+ DISALLOW_ASSIGN(Elser);
+};
+
+// Switch begins a switch expression dispatched according to the
+// specified argument value.
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg);
+
+template <typename T>
+class SANDBOX_EXPORT Caser {
+ public:
+ Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {}
+ ~Caser() {}
+
+ // Case adds a single-value "case" clause to the switch.
+ Caser<T> Case(T value, ResultExpr result) const;
+
+ // Cases adds a multiple-value "case" clause to the switch.
+ // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
+ // of using this function.
+ Caser<T> Cases(const std::vector<T>& values, ResultExpr result) const;
+
+ // Terminate the switch with a "default" clause.
+ ResultExpr Default(ResultExpr result) const;
+
+ private:
+ Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {}
+
+ Arg<T> arg_;
+ Elser elser_;
+
+ template <typename U>
+ friend Caser<U> Switch(const Arg<U>&);
+ DISALLOW_ASSIGN(Caser);
+};
+
+// Recommended usage is to put
+// #define CASES SANDBOX_BPF_DSL_CASES
+// near the top of the .cc file (e.g., nearby any "using" statements), then
+// use like:
+// Switch(arg).CASES((3, 5, 7), result)...;
+#define SANDBOX_BPF_DSL_CASES(values, result) \
+ Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result)
+
+// Helper macro to construct a std::vector from an initializer list.
+// TODO(mdempsky): Convert to use C++11 initializer lists instead.
+#define SANDBOX_BPF_DSL_CASES_HELPER(value, ...) \
+ ({ \
+ const __typeof__(value) bpf_dsl_cases_values[] = {value, __VA_ARGS__}; \
+ std::vector<__typeof__(value)>( \
+ bpf_dsl_cases_values, \
+ bpf_dsl_cases_values + arraysize(bpf_dsl_cases_values)); \
+ })
+
+// =====================================================================
+// Official API ends here.
+// =====================================================================
+
+namespace internal {
+
+// Make argument-dependent lookup work. This is necessary because although
+// BoolExpr is defined in bpf_dsl, since it's merely a typedef for
+// scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
+// searches the "internal" nested namespace.
+using bpf_dsl::operator!;
+using bpf_dsl::operator||;
+using bpf_dsl::operator&&;
+
+// Returns a boolean expression that represents whether system call
+// argument |num| of size |size| is equal to |val|, when masked
+// according to |mask|. Users should use the Arg template class below
+// instead of using this API directly.
+SANDBOX_EXPORT BoolExpr
+ ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
+
+// Returns the default mask for a system call argument of the specified size.
+SANDBOX_EXPORT uint64_t DefaultMask(size_t size);
+
+} // namespace internal
+
+template <typename T>
+Arg<T>::Arg(int num)
+ : num_(num), mask_(internal::DefaultMask(sizeof(T))) {
+}
+
+// Definition requires ArgEq to have been declared. Moved out-of-line
+// to minimize how much internal clutter users have to ignore while
+// reading the header documentation.
+//
+// Additionally, we use this helper member function to avoid linker errors
+// caused by defining operator== out-of-line. For a more detailed explanation,
+// see http://www.parashift.com/c++-faq-lite/template-friends.html.
+template <typename T>
+BoolExpr Arg<T>::EqualTo(T val) const {
+ return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val));
+}
+
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
+ return Caser<T>(arg, Elser(nullptr));
+}
+
+template <typename T>
+Caser<T> Caser<T>::Case(T value, ResultExpr result) const {
+ return SANDBOX_BPF_DSL_CASES((value), result);
+}
+
+template <typename T>
+Caser<T> Caser<T>::Cases(const std::vector<T>& values,
+ ResultExpr result) const {
+ // Theoretically we could evaluate arg_ just once and emit a more efficient
+ // dispatch table, but for now we simply translate into an equivalent
+ // If/ElseIf/Else chain.
+
+ typedef typename std::vector<T>::const_iterator Iter;
+ BoolExpr test = BoolConst(false);
+ for (Iter i = values.begin(), end = values.end(); i != end; ++i) {
+ test = test || (arg_ == *i);
+ }
+ return Caser<T>(arg_, elser_.ElseIf(test, result));
+}
+
+template <typename T>
+ResultExpr Caser<T>::Default(ResultExpr result) const {
+ return elser_.Else(result);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_forward.h b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
new file mode 100644
index 0000000000..183038990a
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_
+
+#include "base/memory/ref_counted.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// The bpf_dsl_forward.h header provides forward declarations for the
+// types defined in bpf_dsl.h. It's intended for use in user headers
+// that need to reference bpf_dsl types, but don't require definitions.
+
+namespace internal {
+class ResultExprImpl;
+class BoolExprImpl;
+}
+
+typedef scoped_refptr<const internal::ResultExprImpl> ResultExpr;
+typedef scoped_refptr<const internal::BoolExprImpl> BoolExpr;
+
+template <typename T>
+class Arg;
+
+class Elser;
+
+template <typename T>
+class Caser;
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+extern template class SANDBOX_EXPORT
+ scoped_refptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+extern template class SANDBOX_EXPORT
+ scoped_refptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_impl.h b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
new file mode 100644
index 0000000000..2ffaf79c2f
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
@@ -0,0 +1,69 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+class ErrorCode;
+
+namespace bpf_dsl {
+class PolicyCompiler;
+
+namespace internal {
+
+// Internal interface implemented by BoolExpr implementations.
+class BoolExprImpl : public base::RefCounted<BoolExprImpl> {
+ public:
+ // Compile uses |pc| to construct an ErrorCode that conditionally continues
+ // to either |true_ec| or |false_ec|, depending on whether the represented
+ // boolean expression is true or false.
+ virtual ErrorCode Compile(PolicyCompiler* pc,
+ ErrorCode true_ec,
+ ErrorCode false_ec) const = 0;
+
+ protected:
+ BoolExprImpl() {}
+ virtual ~BoolExprImpl() {}
+
+ private:
+ friend class base::RefCounted<BoolExprImpl>;
+ DISALLOW_COPY_AND_ASSIGN(BoolExprImpl);
+};
+
+// Internal interface implemented by ResultExpr implementations.
+class ResultExprImpl : public base::RefCounted<ResultExprImpl> {
+ public:
+ // Compile uses |pc| to construct an ErrorCode analogous to the represented
+ // result expression.
+ virtual ErrorCode Compile(PolicyCompiler* pc) const = 0;
+
+ // HasUnsafeTraps returns whether the result expression is or recursively
+ // contains an unsafe trap expression.
+ virtual bool HasUnsafeTraps() const;
+
+ // IsAllow returns whether the result expression is an "allow" result.
+ virtual bool IsAllow() const;
+
+ // IsAllow returns whether the result expression is a "deny" result.
+ virtual bool IsDeny() const;
+
+ protected:
+ ResultExprImpl() {}
+ virtual ~ResultExprImpl() {}
+
+ private:
+ friend class base::RefCounted<ResultExprImpl>;
+ DISALLOW_COPY_AND_ASSIGN(ResultExprImpl);
+};
+
+} // namespace internal
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
diff --git a/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
new file mode 100644
index 0000000000..398ec59ef1
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/bpf_dsl_unittest.cc
@@ -0,0 +1,486 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <map>
+#include <utility>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/bpf_dsl/verifier.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define CASES SANDBOX_BPF_DSL_CASES
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+// Helper function to construct fake arch_seccomp_data objects.
+struct arch_seccomp_data FakeSyscall(int nr,
+ uint64_t p0 = 0,
+ uint64_t p1 = 0,
+ uint64_t p2 = 0,
+ uint64_t p3 = 0,
+ uint64_t p4 = 0,
+ uint64_t p5 = 0) {
+ // Made up program counter for syscall address.
+ const uint64_t kFakePC = 0x543210;
+
+ struct arch_seccomp_data data = {
+ nr,
+ SECCOMP_ARCH,
+ kFakePC,
+ {
+ p0, p1, p2, p3, p4, p5,
+ },
+ };
+
+ return data;
+}
+
+class FakeTrapRegistry : public TrapRegistry {
+ public:
+ FakeTrapRegistry() : map_() {}
+ virtual ~FakeTrapRegistry() {}
+
+ uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override {
+ EXPECT_TRUE(safe);
+
+ const uint16_t next_id = map_.size() + 1;
+ return map_.insert(std::make_pair(Key(fnc, aux), next_id)).first->second;
+ }
+
+ bool EnableUnsafeTraps() override {
+ ADD_FAILURE() << "Unimplemented";
+ return false;
+ }
+
+ private:
+ using Key = std::pair<TrapFnc, const void*>;
+
+ std::map<Key, uint16_t> map_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeTrapRegistry);
+};
+
+intptr_t FakeTrapFuncOne(const arch_seccomp_data& data, void* aux) { return 1; }
+intptr_t FakeTrapFuncTwo(const arch_seccomp_data& data, void* aux) { return 2; }
+
+// Test that FakeTrapRegistry correctly assigns trap IDs to trap handlers.
+TEST(FakeTrapRegistry, TrapIDs) {
+ struct {
+ TrapRegistry::TrapFnc fnc;
+ const void* aux;
+ } funcs[] = {
+ {FakeTrapFuncOne, nullptr},
+ {FakeTrapFuncTwo, nullptr},
+ {FakeTrapFuncOne, funcs},
+ {FakeTrapFuncTwo, funcs},
+ };
+
+ FakeTrapRegistry traps;
+
+ // Add traps twice to test that IDs are reused correctly.
+ for (int i = 0; i < 2; ++i) {
+ for (size_t j = 0; j < arraysize(funcs); ++j) {
+ // Trap IDs start at 1.
+ EXPECT_EQ(j + 1, traps.Add(funcs[j].fnc, funcs[j].aux, true));
+ }
+ }
+}
+
+class PolicyEmulator {
+ public:
+ explicit PolicyEmulator(const Policy* policy) : program_(), traps_() {
+ program_ = *PolicyCompiler(policy, &traps_).Compile(true /* verify */);
+ }
+ ~PolicyEmulator() {}
+
+ uint32_t Emulate(const struct arch_seccomp_data& data) const {
+ const char* err = nullptr;
+ uint32_t res = Verifier::EvaluateBPF(program_, data, &err);
+ if (err) {
+ ADD_FAILURE() << err;
+ return 0;
+ }
+ return res;
+ }
+
+ void ExpectAllow(const struct arch_seccomp_data& data) const {
+ EXPECT_EQ(SECCOMP_RET_ALLOW, Emulate(data));
+ }
+
+ void ExpectErrno(uint16_t err, const struct arch_seccomp_data& data) const {
+ EXPECT_EQ(SECCOMP_RET_ERRNO | err, Emulate(data));
+ }
+
+ private:
+ CodeGen::Program program_;
+ FakeTrapRegistry traps_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyEmulator);
+};
+
+class BasicPolicy : public Policy {
+ public:
+ BasicPolicy() {}
+ ~BasicPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_getpgid) {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0, Error(EPERM)).Else(Error(EINVAL));
+ }
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If(uid != 42, Error(ESRCH)).Else(Error(ENOMEM));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicPolicy);
+};
+
+TEST(BPFDSL, Basic) {
+ BasicPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_getpgid, 0));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_getpgid, 1));
+
+ emulator.ExpectErrno(ENOMEM, FakeSyscall(__NR_setuid, 42));
+ emulator.ExpectErrno(ESRCH, FakeSyscall(__NR_setuid, 43));
+}
+
+/* On IA-32, socketpair() is implemented via socketcall(). :-( */
+#if !defined(ARCH_CPU_X86)
+class BooleanLogicPolicy : public Policy {
+ public:
+ BooleanLogicPolicy() {}
+ ~BooleanLogicPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_socketpair) {
+ const Arg<int> domain(0), type(1), protocol(2);
+ return If(domain == AF_UNIX &&
+ (type == SOCK_STREAM || type == SOCK_DGRAM) &&
+ protocol == 0,
+ Error(EPERM)).Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BooleanLogicPolicy);
+};
+
+TEST(BPFDSL, BooleanLogic) {
+ BooleanLogicPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ const intptr_t kFakeSV = 0x12345;
+
+ // Acceptable combinations that should return EPERM.
+ emulator.ExpectErrno(
+ EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_STREAM, 0, kFakeSV));
+ emulator.ExpectErrno(
+ EPERM, FakeSyscall(__NR_socketpair, AF_UNIX, SOCK_DGRAM, 0, kFakeSV));
+
+ // Combinations that are invalid for only one reason; should return EINVAL.
+ emulator.ExpectErrno(
+ EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_STREAM, 0, kFakeSV));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX,
+ SOCK_SEQPACKET, 0, kFakeSV));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_socketpair, AF_UNIX,
+ SOCK_STREAM, IPPROTO_TCP, kFakeSV));
+
+ // Completely unacceptable combination; should also return EINVAL.
+ emulator.ExpectErrno(
+ EINVAL, FakeSyscall(__NR_socketpair, AF_INET, SOCK_SEQPACKET, IPPROTO_UDP,
+ kFakeSV));
+}
+#endif // !ARCH_CPU_X86
+
+class MoreBooleanLogicPolicy : public Policy {
+ public:
+ MoreBooleanLogicPolicy() {}
+ ~MoreBooleanLogicPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setresuid) {
+ const Arg<uid_t> ruid(0), euid(1), suid(2);
+ return If(ruid == 0 || euid == 0 || suid == 0, Error(EPERM))
+ .ElseIf(ruid == 1 && euid == 1 && suid == 1, Error(EAGAIN))
+ .Else(Error(EINVAL));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MoreBooleanLogicPolicy);
+};
+
+TEST(BPFDSL, MoreBooleanLogic) {
+ MoreBooleanLogicPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ // Expect EPERM if any set to 0.
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 0, 5, 5));
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 0, 5));
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_setresuid, 5, 5, 0));
+
+ // Expect EAGAIN if all set to 1.
+ emulator.ExpectErrno(EAGAIN, FakeSyscall(__NR_setresuid, 1, 1, 1));
+
+ // Expect EINVAL for anything else.
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 5, 1, 1));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 5, 1));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 1, 1, 5));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setresuid, 3, 4, 5));
+}
+
+static const uintptr_t kDeadBeefAddr =
+ static_cast<uintptr_t>(0xdeadbeefdeadbeefULL);
+
+class ArgSizePolicy : public Policy {
+ public:
+ ArgSizePolicy() {}
+ ~ArgSizePolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_uname) {
+ const Arg<uintptr_t> addr(0);
+ return If(addr == kDeadBeefAddr, Error(EPERM)).Else(Allow());
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArgSizePolicy);
+};
+
+TEST(BPFDSL, ArgSizeTest) {
+ ArgSizePolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ emulator.ExpectAllow(FakeSyscall(__NR_uname, 0));
+ emulator.ExpectErrno(EPERM, FakeSyscall(__NR_uname, kDeadBeefAddr));
+}
+
+#if 0
+// TODO(mdempsky): This is really an integration test.
+
+class TrappingPolicy : public Policy {
+ public:
+ TrappingPolicy() {}
+ ~TrappingPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_uname) {
+ return Trap(UnameTrap, &count_);
+ }
+ return Allow();
+ }
+
+ private:
+ static intptr_t count_;
+
+ static intptr_t UnameTrap(const struct arch_seccomp_data& data, void* aux) {
+ BPF_ASSERT_EQ(&count_, aux);
+ return ++count_;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrappingPolicy);
+};
+
+intptr_t TrappingPolicy::count_;
+
+BPF_TEST_C(BPFDSL, TrapTest, TrappingPolicy) {
+ ASSERT_SYSCALL_RESULT(1, uname, NULL);
+ ASSERT_SYSCALL_RESULT(2, uname, NULL);
+ ASSERT_SYSCALL_RESULT(3, uname, NULL);
+}
+#endif
+
+class MaskingPolicy : public Policy {
+ public:
+ MaskingPolicy() {}
+ ~MaskingPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xf) == 0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setgid) {
+ const Arg<gid_t> gid(0);
+ return If((gid & 0xf0) == 0xf0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ if (sysno == __NR_setpgid) {
+ const Arg<pid_t> pid(0);
+ return If((pid & 0xa5) == 0xa0, Error(EINVAL)).Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MaskingPolicy);
+};
+
+TEST(BPFDSL, MaskTest) {
+ MaskingPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ for (uid_t uid = 0; uid < 0x100; ++uid) {
+ const int expect_errno = (uid & 0xf) == 0 ? EINVAL : EACCES;
+ emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setuid, uid));
+ }
+
+ for (gid_t gid = 0; gid < 0x100; ++gid) {
+ const int expect_errno = (gid & 0xf0) == 0xf0 ? EINVAL : EACCES;
+ emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setgid, gid));
+ }
+
+ for (pid_t pid = 0; pid < 0x100; ++pid) {
+ const int expect_errno = (pid & 0xa5) == 0xa0 ? EINVAL : EACCES;
+ emulator.ExpectErrno(expect_errno, FakeSyscall(__NR_setpgid, pid, 0));
+ }
+}
+
+class ElseIfPolicy : public Policy {
+ public:
+ ElseIfPolicy() {}
+ ~ElseIfPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_setuid) {
+ const Arg<uid_t> uid(0);
+ return If((uid & 0xfff) == 0, Error(0))
+ .ElseIf((uid & 0xff0) == 0, Error(EINVAL))
+ .ElseIf((uid & 0xf00) == 0, Error(EEXIST))
+ .Else(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ElseIfPolicy);
+};
+
+TEST(BPFDSL, ElseIfTest) {
+ ElseIfPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ emulator.ExpectErrno(0, FakeSyscall(__NR_setuid, 0));
+
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0001));
+ emulator.ExpectErrno(EINVAL, FakeSyscall(__NR_setuid, 0x0002));
+
+ emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0011));
+ emulator.ExpectErrno(EEXIST, FakeSyscall(__NR_setuid, 0x0022));
+
+ emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0111));
+ emulator.ExpectErrno(EACCES, FakeSyscall(__NR_setuid, 0x0222));
+}
+
+class SwitchPolicy : public Policy {
+ public:
+ SwitchPolicy() {}
+ ~SwitchPolicy() override {}
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ if (sysno == __NR_fcntl) {
+ const Arg<int> cmd(1);
+ const Arg<unsigned long> long_arg(2);
+ return Switch(cmd)
+ .CASES((F_GETFL, F_GETFD), Error(ENOENT))
+ .Case(F_SETFD, If(long_arg == O_CLOEXEC, Allow()).Else(Error(EINVAL)))
+ .Case(F_SETFL, Error(EPERM))
+ .Default(Error(EACCES));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SwitchPolicy);
+};
+
+TEST(BPFDSL, SwitchTest) {
+ SwitchPolicy policy;
+ PolicyEmulator emulator(&policy);
+
+ const int kFakeSockFD = 42;
+
+ emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFD));
+ emulator.ExpectErrno(ENOENT, FakeSyscall(__NR_fcntl, kFakeSockFD, F_GETFL));
+
+ emulator.ExpectAllow(
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, O_CLOEXEC));
+ emulator.ExpectErrno(EINVAL,
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFD, 0));
+
+ emulator.ExpectErrno(EPERM,
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_SETFL, O_RDONLY));
+
+ emulator.ExpectErrno(EACCES,
+ FakeSyscall(__NR_fcntl, kFakeSockFD, F_DUPFD, 0));
+}
+
+static intptr_t DummyTrap(const struct arch_seccomp_data& data, void* aux) {
+ return 0;
+}
+
+TEST(BPFDSL, IsAllowDeny) {
+ ResultExpr allow = Allow();
+ EXPECT_TRUE(allow->IsAllow());
+ EXPECT_FALSE(allow->IsDeny());
+
+ ResultExpr error = Error(ENOENT);
+ EXPECT_FALSE(error->IsAllow());
+ EXPECT_TRUE(error->IsDeny());
+
+ ResultExpr trace = Trace(42);
+ EXPECT_FALSE(trace->IsAllow());
+ EXPECT_FALSE(trace->IsDeny());
+
+ ResultExpr trap = Trap(DummyTrap, nullptr);
+ EXPECT_FALSE(trap->IsAllow());
+ EXPECT_TRUE(trap->IsDeny());
+
+ const Arg<int> arg(0);
+ ResultExpr maybe = If(arg == 0, Allow()).Else(Error(EPERM));
+ EXPECT_FALSE(maybe->IsAllow());
+ EXPECT_FALSE(maybe->IsDeny());
+}
+
+TEST(BPFDSL, HasUnsafeTraps) {
+ ResultExpr allow = Allow();
+ EXPECT_FALSE(allow->HasUnsafeTraps());
+
+ ResultExpr safe = Trap(DummyTrap, nullptr);
+ EXPECT_FALSE(safe->HasUnsafeTraps());
+
+ ResultExpr unsafe = UnsafeTrap(DummyTrap, nullptr);
+ EXPECT_TRUE(unsafe->HasUnsafeTraps());
+
+ const Arg<int> arg(0);
+ ResultExpr maybe = If(arg == 0, allow).Else(unsafe);
+ EXPECT_TRUE(maybe->HasUnsafeTraps());
+}
+
+} // namespace
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/codegen.cc b/sandbox/linux/bpf_dsl/codegen.cc
new file mode 100644
index 0000000000..2d5c8e406e
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/codegen.cc
@@ -0,0 +1,159 @@
+// 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/bpf_dsl/codegen.h"
+
+#include <limits>
+#include <utility>
+
+#include "base/logging.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+
+// This CodeGen implementation strives for simplicity while still
+// generating acceptable BPF programs under typical usage patterns
+// (e.g., by PolicyCompiler).
+//
+// The key to its simplicity is that BPF programs only support forward
+// jumps/branches, which allows constraining the DAG construction API
+// to make instruction nodes immutable. Immutable nodes admits a
+// simple greedy approach of emitting new instructions as needed and
+// then reusing existing ones that have already been emitted. This
+// cleanly avoids any need to compute basic blocks or apply
+// topological sorting because the API effectively sorts instructions
+// for us (e.g., before MakeInstruction() can be called to emit a
+// branch instruction, it must have already been called for each
+// branch path).
+//
+// This greedy algorithm is not without (theoretical) weakness though:
+//
+// 1. In the general case, we don't eliminate dead code. If needed,
+// we could trace back through the program in Compile() and elide
+// any unneeded instructions, but in practice we only emit live
+// instructions anyway.
+//
+// 2. By not dividing instructions into basic blocks and sorting, we
+// lose an opportunity to move non-branch/non-return instructions
+// adjacent to their successor instructions, which means we might
+// need to emit additional jumps. But in practice, they'll
+// already be nearby as long as callers don't go out of their way
+// to interleave MakeInstruction() calls for unrelated code
+// sequences.
+
+namespace sandbox {
+
+// kBranchRange is the maximum value that can be stored in
+// sock_filter's 8-bit jt and jf fields.
+const size_t kBranchRange = std::numeric_limits<uint8_t>::max();
+
+const CodeGen::Node CodeGen::kNullNode;
+
+CodeGen::CodeGen() : program_(), equivalent_(), memos_() {
+}
+
+CodeGen::~CodeGen() {
+}
+
+void CodeGen::Compile(CodeGen::Node head, Program* out) {
+ DCHECK(out);
+ out->assign(program_.rbegin() + Offset(head), program_.rend());
+}
+
+CodeGen::Node CodeGen::MakeInstruction(uint16_t code,
+ uint32_t k,
+ Node jt,
+ Node jf) {
+ // To avoid generating redundant code sequences, we memoize the
+ // results from AppendInstruction().
+ auto res = memos_.insert(std::make_pair(MemoKey(code, k, jt, jf), kNullNode));
+ CodeGen::Node* node = &res.first->second;
+ if (res.second) { // Newly inserted memo entry.
+ *node = AppendInstruction(code, k, jt, jf);
+ }
+ return *node;
+}
+
+CodeGen::Node CodeGen::AppendInstruction(uint16_t code,
+ uint32_t k,
+ Node jt,
+ Node jf) {
+ if (BPF_CLASS(code) == BPF_JMP) {
+ CHECK_NE(BPF_JA, BPF_OP(code)) << "CodeGen inserts JAs as needed";
+
+ // Optimally adding jumps is rather tricky, so we use a quick
+ // approximation: by artificially reducing |jt|'s range, |jt| will
+ // stay within its true range even if we add a jump for |jf|.
+ jt = WithinRange(jt, kBranchRange - 1);
+ jf = WithinRange(jf, kBranchRange);
+ return Append(code, k, Offset(jt), Offset(jf));
+ }
+
+ CHECK_EQ(kNullNode, jf) << "Non-branch instructions shouldn't provide jf";
+ if (BPF_CLASS(code) == BPF_RET) {
+ CHECK_EQ(kNullNode, jt) << "Return instructions shouldn't provide jt";
+ } else {
+ // For non-branch/non-return instructions, execution always
+ // proceeds to the next instruction; so we need to arrange for
+ // that to be |jt|.
+ jt = WithinRange(jt, 0);
+ CHECK_EQ(0U, Offset(jt)) << "ICE: Failed to setup next instruction";
+ }
+ return Append(code, k, 0, 0);
+}
+
+CodeGen::Node CodeGen::WithinRange(Node target, size_t range) {
+ // Just use |target| if it's already within range.
+ if (Offset(target) <= range) {
+ return target;
+ }
+
+ // Alternatively, look for an equivalent instruction within range.
+ if (Offset(equivalent_.at(target)) <= range) {
+ return equivalent_.at(target);
+ }
+
+ // Otherwise, fall back to emitting a jump instruction.
+ Node jump = Append(BPF_JMP | BPF_JA, Offset(target), 0, 0);
+ equivalent_.at(target) = jump;
+ return jump;
+}
+
+CodeGen::Node CodeGen::Append(uint16_t code, uint32_t k, size_t jt, size_t jf) {
+ if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) {
+ CHECK_LE(jt, kBranchRange);
+ CHECK_LE(jf, kBranchRange);
+ } else {
+ CHECK_EQ(0U, jt);
+ CHECK_EQ(0U, jf);
+ }
+
+ CHECK_LT(program_.size(), static_cast<size_t>(BPF_MAXINSNS));
+ CHECK_EQ(program_.size(), equivalent_.size());
+
+ Node res = program_.size();
+ program_.push_back(sock_filter{
+ code, static_cast<uint8_t>(jt), static_cast<uint8_t>(jf), k});
+ equivalent_.push_back(res);
+ return res;
+}
+
+size_t CodeGen::Offset(Node target) const {
+ CHECK_LT(target, program_.size()) << "Bogus offset target node";
+ return (program_.size() - 1) - target;
+}
+
+// TODO(mdempsky): Move into a general base::Tuple helper library.
+bool CodeGen::MemoKeyLess::operator()(const MemoKey& lhs,
+ const MemoKey& rhs) const {
+ if (base::get<0>(lhs) != base::get<0>(rhs))
+ return base::get<0>(lhs) < base::get<0>(rhs);
+ if (base::get<1>(lhs) != base::get<1>(rhs))
+ return base::get<1>(lhs) < base::get<1>(rhs);
+ if (base::get<2>(lhs) != base::get<2>(rhs))
+ return base::get<2>(lhs) < base::get<2>(rhs);
+ if (base::get<3>(lhs) != base::get<3>(rhs))
+ return base::get<3>(lhs) < base::get<3>(rhs);
+ return false;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/codegen.h b/sandbox/linux/bpf_dsl/codegen.h
new file mode 100644
index 0000000000..9d898030b9
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/codegen.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_BPF_DSL_CODEGEN_H__
+#define SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/tuple.h"
+#include "sandbox/sandbox_export.h"
+
+struct sock_filter;
+
+namespace sandbox {
+
+// The code generator implements a basic assembler that can convert a
+// graph of BPF instructions into a well-formed array 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 instruction nodes. They'll eventually call
+// Compile() to convert this DAG to a Program.
+//
+// CodeGen gen;
+// CodeGen::Node allow, branch, dag;
+//
+// allow =
+// gen.MakeInstruction(BPF_RET+BPF_K,
+// ErrorCode(ErrorCode::ERR_ALLOWED).err()));
+// branch =
+// gen.MakeInstruction(BPF_JMP+BPF_EQ+BPF_K, __NR_getpid,
+// Trap(GetPidHandler, NULL), allow);
+// dag =
+// gen.MakeInstruction(BPF_LD+BPF_W+BPF_ABS,
+// offsetof(struct arch_seccomp_data, nr), branch);
+//
+// // Simplified code follows; in practice, it is important to avoid calling
+// // any C++ destructors after starting the sandbox.
+// CodeGen::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 SANDBOX_EXPORT CodeGen {
+ public:
+ // A vector of BPF instructions that need to be installed as a filter
+ // program in the kernel.
+ typedef std::vector<struct sock_filter> Program;
+
+ // Node represents a node within the instruction DAG being compiled.
+ using Node = Program::size_type;
+
+ // kNullNode represents the "null" node; i.e., the reserved node
+ // value guaranteed to not equal any actual nodes.
+ static const Node kNullNode = -1;
+
+ CodeGen();
+ ~CodeGen();
+
+ // MakeInstruction creates a node representing the specified
+ // instruction, or returns and existing equivalent node if one
+ // exists. For details on the possible parameters refer to
+ // https://www.kernel.org/doc/Documentation/networking/filter.txt.
+ // TODO(mdempsky): Reconsider using default arguments here.
+ Node MakeInstruction(uint16_t code,
+ uint32_t k,
+ Node jt = kNullNode,
+ Node jf = kNullNode);
+
+ // Compile linearizes the instruction DAG rooted at |head| into a
+ // program that can be executed by a BPF virtual machine.
+ void Compile(Node head, Program* program);
+
+ private:
+ using MemoKey = base::Tuple<uint16_t, uint32_t, Node, Node>;
+ struct MemoKeyLess {
+ bool operator()(const MemoKey& lhs, const MemoKey& rhs) const;
+ };
+
+ // AppendInstruction adds a new instruction, ensuring that |jt| and
+ // |jf| are within range as necessary for |code|.
+ Node AppendInstruction(uint16_t code, uint32_t k, Node jt, Node jf);
+
+ // WithinRange returns a node equivalent to |next| that is at most
+ // |range| instructions away from the (logical) beginning of the
+ // program.
+ Node WithinRange(Node next, size_t range);
+
+ // Append appends a new instruction to the physical end (i.e.,
+ // logical beginning) of |program_|.
+ Node Append(uint16_t code, uint32_t k, size_t jt, size_t jf);
+
+ // Offset returns how many instructions exist in |program_| after |target|.
+ size_t Offset(Node target) const;
+
+ // NOTE: program_ is the compiled program in *reverse*, so that
+ // indices remain stable as we add instructions.
+ Program program_;
+
+ // equivalent_ stores the most recent semantically-equivalent node for each
+ // instruction in program_. A node is defined as semantically-equivalent to N
+ // if it has the same instruction code and constant as N and its successor
+ // nodes (if any) are semantically-equivalent to N's successor nodes, or
+ // if it's an unconditional jump to a node semantically-equivalent to N.
+ std::vector<Node> equivalent_;
+
+ std::map<MemoKey, Node, MemoKeyLess> memos_;
+
+ DISALLOW_COPY_AND_ASSIGN(CodeGen);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
diff --git a/sandbox/linux/bpf_dsl/codegen_unittest.cc b/sandbox/linux/bpf_dsl/codegen_unittest.cc
new file mode 100644
index 0000000000..5961822123
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/codegen_unittest.cc
@@ -0,0 +1,402 @@
+// 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/bpf_dsl/codegen.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/md5.h"
+#include "base/strings/string_piece.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+// Hash provides an abstraction for building "hash trees" from BPF
+// control flow graphs, and efficiently identifying equivalent graphs.
+//
+// For simplicity, we use MD5, because base happens to provide a
+// convenient API for its use. However, any collision-resistant hash
+// should suffice.
+class Hash {
+ public:
+ static const Hash kZero;
+
+ Hash() : digest_() {}
+
+ Hash(uint16_t code,
+ uint32_t k,
+ const Hash& jt = kZero,
+ const Hash& jf = kZero)
+ : digest_() {
+ base::MD5Context ctx;
+ base::MD5Init(&ctx);
+ HashValue(&ctx, code);
+ HashValue(&ctx, k);
+ HashValue(&ctx, jt);
+ HashValue(&ctx, jf);
+ base::MD5Final(&digest_, &ctx);
+ }
+
+ Hash(const Hash& hash) = default;
+ Hash& operator=(const Hash& rhs) = default;
+
+ friend bool operator==(const Hash& lhs, const Hash& rhs) {
+ return lhs.Base16() == rhs.Base16();
+ }
+ friend bool operator!=(const Hash& lhs, const Hash& rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ template <typename T>
+ void HashValue(base::MD5Context* ctx, const T& value) {
+ base::MD5Update(ctx,
+ base::StringPiece(reinterpret_cast<const char*>(&value),
+ sizeof(value)));
+ }
+
+ std::string Base16() const {
+ return base::MD5DigestToBase16(digest_);
+ }
+
+ base::MD5Digest digest_;
+};
+
+const Hash Hash::kZero;
+
+// Sanity check that equality and inequality work on Hash as required.
+TEST(CodeGen, HashSanity) {
+ std::vector<Hash> hashes;
+
+ // Push a bunch of logically distinct hashes.
+ hashes.push_back(Hash::kZero);
+ for (int i = 0; i < 4; ++i) {
+ hashes.push_back(Hash(i & 1, i & 2));
+ }
+ for (int i = 0; i < 16; ++i) {
+ hashes.push_back(Hash(i & 1, i & 2, Hash(i & 4, i & 8)));
+ }
+ for (int i = 0; i < 64; ++i) {
+ hashes.push_back(
+ Hash(i & 1, i & 2, Hash(i & 4, i & 8), Hash(i & 16, i & 32)));
+ }
+
+ for (const Hash& a : hashes) {
+ for (const Hash& b : hashes) {
+ // Hashes should equal themselves, but not equal all others.
+ if (&a == &b) {
+ EXPECT_EQ(a, b);
+ } else {
+ EXPECT_NE(a, b);
+ }
+ }
+ }
+}
+
+// ProgramTest provides a fixture for writing compiling sample
+// programs with CodeGen and verifying the linearized output matches
+// the input DAG.
+class ProgramTest : public ::testing::Test {
+ protected:
+ ProgramTest() : gen_(), node_hashes_() {}
+
+ // MakeInstruction calls CodeGen::MakeInstruction() and associated
+ // the returned address with a hash of the instruction.
+ CodeGen::Node MakeInstruction(uint16_t code,
+ uint32_t k,
+ CodeGen::Node jt = CodeGen::kNullNode,
+ CodeGen::Node jf = CodeGen::kNullNode) {
+ CodeGen::Node res = gen_.MakeInstruction(code, k, jt, jf);
+ EXPECT_NE(CodeGen::kNullNode, res);
+
+ Hash digest(code, k, Lookup(jt), Lookup(jf));
+ auto it = node_hashes_.insert(std::make_pair(res, digest));
+ EXPECT_EQ(digest, it.first->second);
+
+ return res;
+ }
+
+ // RunTest compiles the program and verifies that the output matches
+ // what is expected. It should be called at the end of each program
+ // test case.
+ void RunTest(CodeGen::Node head) {
+ // Compile the program
+ CodeGen::Program program;
+ gen_.Compile(head, &program);
+
+ // Walk the program backwards, and compute the hash for each instruction.
+ std::vector<Hash> prog_hashes(program.size());
+ for (size_t i = program.size(); i > 0; --i) {
+ const sock_filter& insn = program.at(i - 1);
+ Hash& hash = prog_hashes.at(i - 1);
+
+ if (BPF_CLASS(insn.code) == BPF_JMP) {
+ if (BPF_OP(insn.code) == BPF_JA) {
+ // The compiler adds JA instructions as needed, so skip them.
+ hash = prog_hashes.at(i + insn.k);
+ } else {
+ hash = Hash(insn.code, insn.k, prog_hashes.at(i + insn.jt),
+ prog_hashes.at(i + insn.jf));
+ }
+ } else if (BPF_CLASS(insn.code) == BPF_RET) {
+ hash = Hash(insn.code, insn.k);
+ } else {
+ hash = Hash(insn.code, insn.k, prog_hashes.at(i));
+ }
+ }
+
+ EXPECT_EQ(Lookup(head), prog_hashes.at(0));
+ }
+
+ private:
+ const Hash& Lookup(CodeGen::Node next) const {
+ if (next == CodeGen::kNullNode) {
+ return Hash::kZero;
+ }
+ auto it = node_hashes_.find(next);
+ if (it == node_hashes_.end()) {
+ ADD_FAILURE() << "No hash found for node " << next;
+ return Hash::kZero;
+ }
+ return it->second;
+ }
+
+ CodeGen gen_;
+ std::map<CodeGen::Node, Hash> node_hashes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProgramTest);
+};
+
+TEST_F(ProgramTest, OneInstruction) {
+ // Create the most basic valid BPF program:
+ // RET 0
+ CodeGen::Node head = MakeInstruction(BPF_RET + BPF_K, 0);
+ RunTest(head);
+}
+
+TEST_F(ProgramTest, SimpleBranch) {
+ // Create a program with a single branch:
+ // JUMP if eq 42 then $0 else $1
+ // 0: RET 1
+ // 1: RET 0
+ CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42,
+ MakeInstruction(BPF_RET + BPF_K, 1),
+ MakeInstruction(BPF_RET + BPF_K, 0));
+ RunTest(head);
+}
+
+TEST_F(ProgramTest, AtypicalBranch) {
+ // Create a program with a single branch:
+ // JUMP if eq 42 then $0 else $0
+ // 0: RET 0
+
+ CodeGen::Node ret = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, ret, ret);
+
+ // 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".
+ RunTest(head);
+}
+
+TEST_F(ProgramTest, Complex) {
+ // 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 (insn2)
+ // 3: LD 42 (insn1)
+ // RET 42 (insn0)
+ // 4: LD 42 (insn3)
+ // RET 42 (insn3+)
+ CodeGen::Node insn0 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0);
+ CodeGen::Node insn2 = insn1; // Implicit JUMP
+
+ // We explicitly duplicate instructions to test that they're merged.
+ CodeGen::Node insn3 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42,
+ MakeInstruction(BPF_RET + BPF_K, 42));
+ EXPECT_EQ(insn2, insn3);
+
+ CodeGen::Node insn4 =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3);
+ CodeGen::Node insn5 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 23, 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).
+ CodeGen::Node insn6 =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4);
+
+ RunTest(insn6);
+}
+
+TEST_F(ProgramTest, ConfusingTails) {
+ // This simple program demonstrates https://crbug.com/351103/
+ // The two "LOAD 0" instructions are blocks of their own. MergeTails() could
+ // be tempted to merge them since they are the same. However, they are
+ // not mergeable because they fall-through to non semantically equivalent
+ // blocks.
+ // Without the fix for this bug, this program should trigger the check in
+ // CompileAndCompare: the serialized graphs from the program and its compiled
+ // version will differ.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0
+ // 7) RET 1
+
+ CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ RunTest(i0);
+}
+
+TEST_F(ProgramTest, ConfusingTailsBasic) {
+ // Without the fix for https://crbug.com/351103/, (see
+ // SampleProgramConfusingTails()), this would generate a cyclic graph and
+ // crash as the two "LOAD 0" instructions would get merged.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) LOAD 0 // System call number
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) LOAD 0 // System call number
+ // 5) RET 1
+
+ CodeGen::Node i5 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ RunTest(i0);
+}
+
+TEST_F(ProgramTest, ConfusingTailsMergeable) {
+ // This is similar to SampleProgramConfusingTails(), except that
+ // instructions 2 and 4 are now RET instructions.
+ // In PointerCompare(), this exercises the path where two blocks are of the
+ // same length and identical and the last instruction is a JMP or RET, so the
+ // following blocks don't need to be looked at and the blocks are mergeable.
+ //
+ // 0) LOAD 1 // ???
+ // 1) if A == 0x1; then JMP 2 else JMP 3
+ // 2) RET 42
+ // 3) if A == 0x2; then JMP 4 else JMP 5
+ // 4) RET 42
+ // 5) if A == 0x1; then JMP 6 else JMP 7
+ // 6) RET 0
+ // 7) RET 1
+
+ CodeGen::Node i7 = MakeInstruction(BPF_RET + BPF_K, 1);
+ CodeGen::Node i6 = MakeInstruction(BPF_RET + BPF_K, 0);
+ CodeGen::Node i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7);
+ CodeGen::Node i4 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5);
+ CodeGen::Node i2 = MakeInstruction(BPF_RET + BPF_K, 42);
+ CodeGen::Node i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3);
+ CodeGen::Node i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1);
+
+ RunTest(i0);
+}
+
+TEST_F(ProgramTest, InstructionFolding) {
+ // Check that simple instructions are folded as expected.
+ CodeGen::Node a = MakeInstruction(BPF_RET + BPF_K, 0);
+ EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0));
+ CodeGen::Node b = MakeInstruction(BPF_RET + BPF_K, 1);
+ EXPECT_EQ(a, MakeInstruction(BPF_RET + BPF_K, 0));
+ EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1));
+ EXPECT_EQ(b, MakeInstruction(BPF_RET + BPF_K, 1));
+
+ // Check that complex sequences are folded too.
+ CodeGen::Node c =
+ MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0,
+ MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b));
+ EXPECT_EQ(c, MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, 0,
+ MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, 0x100, a, b)));
+
+ RunTest(c);
+}
+
+TEST_F(ProgramTest, FarBranches) {
+ // BPF instructions use 8-bit fields for branch offsets, which means
+ // branch targets must be within 255 instructions of the branch
+ // instruction. CodeGen abstracts away this detail by inserting jump
+ // instructions as needed, which we test here by generating programs
+ // that should trigger any interesting boundary conditions.
+
+ // Populate with 260 initial instruction nodes.
+ std::vector<CodeGen::Node> nodes;
+ nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0));
+ for (size_t i = 1; i < 260; ++i) {
+ nodes.push_back(
+ MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back()));
+ }
+
+ // Exhaustively test branch offsets near BPF's limits.
+ for (size_t jt = 250; jt < 260; ++jt) {
+ for (size_t jf = 250; jf < 260; ++jf) {
+ nodes.push_back(MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0,
+ nodes.rbegin()[jt], nodes.rbegin()[jf]));
+ RunTest(nodes.back());
+ }
+ }
+}
+
+TEST_F(ProgramTest, JumpReuse) {
+ // As a code size optimization, we try to reuse jumps when possible
+ // instead of emitting new ones. Here we make sure that optimization
+ // is working as intended.
+ //
+ // NOTE: To simplify testing, we rely on implementation details
+ // about what CodeGen::Node values indicate (i.e., vector indices),
+ // but CodeGen users should treat them as opaque values.
+
+ // Populate with 260 initial instruction nodes.
+ std::vector<CodeGen::Node> nodes;
+ nodes.push_back(MakeInstruction(BPF_RET + BPF_K, 0));
+ for (size_t i = 1; i < 260; ++i) {
+ nodes.push_back(
+ MakeInstruction(BPF_ALU + BPF_ADD + BPF_K, i, nodes.back()));
+ }
+
+ // Branching to nodes[0] and nodes[1] should require 3 new
+ // instructions: two far jumps plus the branch itself.
+ CodeGen::Node one =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 0, nodes[0], nodes[1]);
+ EXPECT_EQ(nodes.back() + 3, one); // XXX: Implementation detail!
+ RunTest(one);
+
+ // Branching again to the same target nodes should require only one
+ // new instruction, as we can reuse the previous branch's jumps.
+ CodeGen::Node two =
+ MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, nodes[0], nodes[1]);
+ EXPECT_EQ(one + 1, two); // XXX: Implementation detail!
+ RunTest(two);
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/cons.h b/sandbox/linux/bpf_dsl/cons.h
new file mode 100644
index 0000000000..fa47c140ff
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/cons.h
@@ -0,0 +1,138 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_CONS_H_
+#define SANDBOX_LINUX_BPF_DSL_CONS_H_
+
+#include "base/memory/ref_counted.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace cons {
+
+// Namespace cons provides an abstraction for immutable "cons list"
+// data structures as commonly provided in functional programming
+// languages like Lisp or Haskell.
+//
+// A cons list is a linked list consisting of "cells", each of which
+// have a "head" and a "tail" element. A cell's head element contains
+// a user specified value, while the tail element contains a (possibly
+// null) pointer to another cell.
+//
+// An empty list (idiomatically referred to as "nil") can be
+// constructed as "cons::List<Foo>()" or simply as "nullptr" if Foo
+// can be inferred from context (e.g., calling a function that has a
+// "cons::List<Foo>" parameter).
+//
+// Existing lists (including empty lists) can be extended by
+// prepending new values to the front using the "Cons(head, tail)"
+// function, which will allocate a new cons cell. Notably, cons lists
+// support creating multiple lists that share a common tail sequence.
+//
+// Lastly, lists support iteration via C++11's range-based for loop
+// construct.
+//
+// Examples:
+//
+// // basic construction
+// const cons::List<char> kNil = nullptr;
+// cons::List<char> ba = Cons('b', Cons('a', kNil));
+//
+// // common tail sequence
+// cons::List<char> cba = Cons('c', ba);
+// cons::List<char> dba = Cons('d', ba);
+//
+// // iteration
+// for (const char& ch : cba) {
+// // iterates 'c', 'b', 'a'
+// }
+// for (const char& ch : dba) {
+// // iterates 'd', 'b', 'a'
+// }
+
+// Forward declarations.
+template <typename T>
+class Cell;
+template <typename T>
+class ListIterator;
+
+// List represents a (possibly null) pointer to a cons cell.
+template <typename T>
+using List = scoped_refptr<const Cell<T>>;
+
+// Cons extends a cons list by prepending a new value to the front.
+template <typename T>
+List<T> Cons(const T& head, const List<T>& tail) {
+ return List<T>(new const Cell<T>(head, tail));
+}
+
+// Cell represents an individual "cons cell" within a cons list.
+template <typename T>
+class Cell : public base::RefCounted<Cell<T>> {
+ public:
+ Cell(const T& head, const List<T>& tail) : head_(head), tail_(tail) {}
+
+ // Head returns this cell's head element.
+ const T& head() const { return head_; }
+
+ // Tail returns this cell's tail element.
+ const List<T>& tail() const { return tail_; }
+
+ private:
+ virtual ~Cell() {}
+
+ T head_;
+ List<T> tail_;
+
+ friend class base::RefCounted<Cell<T>>;
+ DISALLOW_COPY_AND_ASSIGN(Cell);
+};
+
+// Begin returns a list iterator pointing to the first element of the
+// cons list. It's provided to support range-based for loops.
+template <typename T>
+ListIterator<T> begin(const List<T>& list) {
+ return ListIterator<T>(list);
+}
+
+// End returns a list iterator pointing to the "past-the-end" element
+// of the cons list (i.e., nil). It's provided to support range-based
+// for loops.
+template <typename T>
+ListIterator<T> end(const List<T>& list) {
+ return ListIterator<T>();
+}
+
+// ListIterator provides C++ forward iterator semantics for traversing
+// a cons list.
+template <typename T>
+class ListIterator {
+ public:
+ ListIterator() : list_() {}
+ explicit ListIterator(const List<T>& list) : list_(list) {}
+
+ const T& operator*() const { return list_->head(); }
+
+ ListIterator& operator++() {
+ list_ = list_->tail();
+ return *this;
+ }
+
+ friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) {
+ return lhs.list_ == rhs.list_;
+ }
+
+ private:
+ List<T> list_;
+};
+
+template <typename T>
+bool operator!=(const ListIterator<T>& lhs, const ListIterator<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace cons
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_CONS_H_
diff --git a/sandbox/linux/bpf_dsl/cons_unittest.cc b/sandbox/linux/bpf_dsl/cons_unittest.cc
new file mode 100644
index 0000000000..ea2ba2f8dc
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/cons_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/cons.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+std::string Join(cons::List<char> char_list) {
+ std::string res;
+ for (const char& ch : char_list) {
+ res.push_back(ch);
+ }
+ return res;
+}
+
+TEST(ConsListTest, Basic) {
+ cons::List<char> ba = Cons('b', Cons('a', cons::List<char>()));
+ EXPECT_EQ("ba", Join(ba));
+
+ cons::List<char> cba = Cons('c', ba);
+ cons::List<char> dba = Cons('d', ba);
+ EXPECT_EQ("cba", Join(cba));
+ EXPECT_EQ("dba", Join(dba));
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/dump_bpf.cc b/sandbox/linux/bpf_dsl/dump_bpf.cc
new file mode 100644
index 0000000000..d0c8f75073
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/dump_bpf.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/dump_bpf.h"
+
+#include <stdio.h>
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+void DumpBPF::PrintProgram(const CodeGen::Program& program) {
+ for (CodeGen::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_ACTION) == SECCOMP_RET_TRACE) {
+ fprintf(stderr, "Trace #%d\n", iter->k & SECCOMP_RET_DATA);
+ } else if (iter->k == SECCOMP_RET_ALLOW) {
+ fprintf(stderr, "Allowed\n");
+ } else {
+ fprintf(stderr, "???\n");
+ }
+ break;
+ case BPF_ALU:
+ if (BPF_OP(iter->code) == BPF_NEG) {
+ fprintf(stderr, "A := -A\n");
+ } else {
+ fprintf(stderr, "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;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/dump_bpf.h b/sandbox/linux/bpf_dsl/dump_bpf.h
new file mode 100644
index 0000000000..cd12be793d
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/dump_bpf.h
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+class SANDBOX_EXPORT DumpBPF {
+ public:
+ // PrintProgram writes |program| in a human-readable format to stderr.
+ static void PrintProgram(const CodeGen::Program& program);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/linux_syscall_ranges.h b/sandbox/linux/bpf_dsl/linux_syscall_ranges.h
new file mode 100644
index 0000000000..a747770c78
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/linux_syscall_ranges.h
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium 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_BPF_DSL_LINUX_SYSCALL_RANGES_H_
+#define SANDBOX_LINUX_BPF_DSL_LINUX_SYSCALL_RANGES_H_
+
+#if defined(__x86_64__)
+
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#elif defined(__i386__)
+
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#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.
+
+// __NR_SYSCALL_BASE is 0 in thumb and ARM EABI.
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u)
+// __ARM_NR_BASE is __NR_SYSCALL_BASE + 0xf0000u
+#define MIN_PRIVATE_SYSCALL 0xf0000u
+#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
+#define MIN_GHOST_SYSCALL (MIN_PRIVATE_SYSCALL + 0xfff0u)
+#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u)
+
+#elif defined(__mips__) && (_MIPS_SIM == _ABIO32)
+
+#include <asm/unistd.h> // for __NR_O32_Linux and __NR_Linux_syscalls
+#define MIN_SYSCALL __NR_O32_Linux
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_Linux_syscalls)
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#elif defined(__mips__) && (_MIPS_SIM == _ABI64)
+
+#error "Add support to header file"
+
+#elif defined(__aarch64__)
+
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 279u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#else
+#error "Unsupported architecture"
+#endif
+
+#endif // SANDBOX_LINUX_BPF_DSL_LINUX_SYSCALL_RANGES_H_
diff --git a/sandbox/linux/bpf_dsl/policy.cc b/sandbox/linux/bpf_dsl/policy.cc
new file mode 100644
index 0000000000..c20edc6da8
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/policy.h"
+
+#include <errno.h>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+ResultExpr Policy::InvalidSyscall() const {
+ return Error(ENOSYS);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy.h b/sandbox/linux/bpf_dsl/policy.h
new file mode 100644
index 0000000000..6c67589456
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// Interface to implement to define a BPF sandbox policy.
+class SANDBOX_EXPORT Policy {
+ public:
+ Policy() {}
+ virtual ~Policy() {}
+
+ // User extension point for writing custom sandbox policies.
+ // The returned ResultExpr will control how the kernel responds to the
+ // specified system call number.
+ virtual ResultExpr EvaluateSyscall(int sysno) const = 0;
+
+ // Optional overload for specifying alternate behavior for invalid
+ // system calls. The default is to return ENOSYS.
+ virtual ResultExpr InvalidSyscall() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Policy);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_H_
diff --git a/sandbox/linux/bpf_dsl/policy_compiler.cc b/sandbox/linux/bpf_dsl/policy_compiler.cc
new file mode 100644
index 0000000000..f38232f85f
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy_compiler.cc
@@ -0,0 +1,499 @@
+// 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/bpf_dsl/policy_compiler.h"
+
+#include <errno.h>
+#include <sys/syscall.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/dump_bpf.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/bpf_dsl/verifier.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+#if defined(__i386__) || defined(__x86_64__)
+const bool kIsIntel = true;
+#else
+const bool kIsIntel = false;
+#endif
+#if defined(__x86_64__) && defined(__ILP32__)
+const bool kIsX32 = true;
+#else
+const bool kIsX32 = false;
+#endif
+
+const int kSyscallsRequiredForUnsafeTraps[] = {
+ __NR_rt_sigprocmask,
+ __NR_rt_sigreturn,
+#if defined(__NR_sigprocmask)
+ __NR_sigprocmask,
+#endif
+#if defined(__NR_sigreturn)
+ __NR_sigreturn,
+#endif
+};
+
+bool HasExactlyOneBit(uint64_t x) {
+ // Common trick; e.g., see http://stackoverflow.com/a/108329.
+ return x != 0 && (x & (x - 1)) == 0;
+}
+
+// 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;
+}
+
+bool HasUnsafeTraps(const Policy* policy) {
+ DCHECK(policy);
+ for (uint32_t sysnum : SyscallSet::ValidOnly()) {
+ if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
+ return true;
+ }
+ }
+ return policy->InvalidSyscall()->HasUnsafeTraps();
+}
+
+} // namespace
+
+struct PolicyCompiler::Range {
+ uint32_t from;
+ CodeGen::Node node;
+};
+
+PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry)
+ : policy_(policy),
+ registry_(registry),
+ escapepc_(0),
+ conds_(),
+ gen_(),
+ has_unsafe_traps_(HasUnsafeTraps(policy_)) {
+ DCHECK(policy);
+}
+
+PolicyCompiler::~PolicyCompiler() {
+}
+
+scoped_ptr<CodeGen::Program> PolicyCompiler::Compile(bool verify) {
+ CHECK(policy_->InvalidSyscall()->IsDeny())
+ << "Policies should deny invalid system calls";
+
+ // If our BPF program has unsafe traps, enable support for them.
+ if (has_unsafe_traps_) {
+ CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC";
+
+ for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
+ CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow())
+ << "Policies that use UnsafeTrap() must unconditionally allow all "
+ "required system calls";
+ }
+
+ CHECK(registry_->EnableUnsafeTraps())
+ << "We'd rather die than enable unsafe traps";
+ }
+
+ // Assemble the BPF filter program.
+ scoped_ptr<CodeGen::Program> program(new CodeGen::Program());
+ gen_.Compile(AssemblePolicy(), program.get());
+
+ // Make sure compilation resulted in a 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 (verify) {
+ const char* err = nullptr;
+ if (!Verifier::VerifyBPF(this, *program, *policy_, &err)) {
+ DumpBPF::PrintProgram(*program);
+ LOG(FATAL) << err;
+ }
+ }
+
+ return program.Pass();
+}
+
+void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) {
+ escapepc_ = escapepc;
+}
+
+CodeGen::Node PolicyCompiler::AssemblePolicy() {
+ // A compiled policy consists of three logical parts:
+ // 1. Check that the "arch" field matches the expected architecture.
+ // 2. If the policy involves unsafe traps, check if the syscall was
+ // invoked by Syscall::Call, and then allow it unconditionally.
+ // 3. Check the system call number and jump to the appropriate compiled
+ // system call policy number.
+ return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
+}
+
+CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) {
+ // If the architecture doesn't match SECCOMP_ARCH, disallow the
+ // system call.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed,
+ CompileResult(Kill("Invalid audit architecture in BPF filter"))));
+}
+
+CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) {
+ // If no unsafe traps, then simply return |rest|.
+ if (!has_unsafe_traps_) {
+ return rest;
+ }
+
+ // We already enabled unsafe traps in Compile, but enable them again to give
+ // the trap registry a second chance to complain before we add the backdoor.
+ CHECK(registry_->EnableUnsafeTraps());
+
+ // Allow system calls, if they originate from our magic return address.
+ const uint32_t lopc = static_cast<uint32_t>(escapepc_);
+ const uint32_t hipc = static_cast<uint32_t>(escapepc_ >> 32);
+
+ // BPF cannot do native 64-bit comparisons, so we have to compare
+ // both 32-bit 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.
+ //
+ // For simplicity, we check the full 64-bit instruction pointer even
+ // on 32-bit architectures.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, lopc,
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc,
+ CompileResult(Allow()), rest)),
+ rest));
+}
+
+CodeGen::Node PolicyCompiler::DispatchSyscall() {
+ // 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
+ CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
+
+ // Grab the system call number, so that we can check it and then
+ // execute the jump table.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable));
+}
+
+CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) {
+ if (kIsIntel) {
+ // On Intel architectures, verify that system call numbers are in the
+ // expected number range.
+ CodeGen::Node invalidX32 =
+ CompileResult(Kill("Illegal mixing of system call ABIs"));
+ if (kIsX32) {
+ // The newer x32 API always sets bit 30.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32);
+ } else {
+ // The older i386 and x86-64 APIs clear bit 30 on all system calls.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed);
+ }
+ }
+
+ // TODO(mdempsky): Similar validation for other architectures?
+ return passed;
+}
+
+void PolicyCompiler::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.
+ const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall());
+ uint32_t old_sysnum = 0;
+ CodeGen::Node old_node =
+ SyscallSet::IsValid(old_sysnum)
+ ? CompileResult(policy_->EvaluateSyscall(old_sysnum))
+ : invalid_node;
+
+ for (uint32_t sysnum : SyscallSet::All()) {
+ CodeGen::Node node =
+ SyscallSet::IsValid(sysnum)
+ ? CompileResult(policy_->EvaluateSyscall(static_cast<int>(sysnum)))
+ : invalid_node;
+ // N.B., here we rely on CodeGen folding (i.e., returning the same
+ // node value for) identical code sequences, otherwise our jump
+ // table will blow up in size.
+ if (node != old_node) {
+ ranges->push_back(Range{old_sysnum, old_node});
+ old_sysnum = sysnum;
+ old_node = node;
+ }
+ }
+ ranges->push_back(Range{old_sysnum, old_node});
+}
+
+CodeGen::Node PolicyCompiler::AssembleJumpTable(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.
+ CHECK(start < stop) << "Invalid iterator range";
+ const auto n = stop - start;
+ if (n == 1) {
+ // If we have narrowed things down to a single range object, we can
+ // return from the BPF filter program.
+ return start->node;
+ }
+
+ // 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 + n / 2;
+
+ // Sub-divide the list of ranges and continue recursively.
+ CodeGen::Node jf = AssembleJumpTable(start, mid);
+ CodeGen::Node jt = AssembleJumpTable(mid, stop);
+ return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
+}
+
+CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) {
+ return RetExpression(res->Compile(this));
+}
+
+CodeGen::Node PolicyCompiler::RetExpression(const ErrorCode& err) {
+ switch (err.error_type()) {
+ case ErrorCode::ET_COND:
+ return CondExpression(err);
+ case ErrorCode::ET_SIMPLE:
+ case ErrorCode::ET_TRAP:
+ return gen_.MakeInstruction(BPF_RET + BPF_K, err.err());
+ default:
+ LOG(FATAL)
+ << "ErrorCode is not suitable for returning from a BPF program";
+ return CodeGen::kNullNode;
+ }
+}
+
+CodeGen::Node PolicyCompiler::CondExpression(const ErrorCode& cond) {
+ // Sanity check that |cond| makes sense.
+ CHECK(cond.argno_ >= 0 && cond.argno_ < 6) << "Invalid argument number "
+ << cond.argno_;
+ CHECK(cond.width_ == ErrorCode::TP_32BIT ||
+ cond.width_ == ErrorCode::TP_64BIT)
+ << "Invalid argument width " << cond.width_;
+ CHECK_NE(0U, cond.mask_) << "Zero mask is invalid";
+ CHECK_EQ(cond.value_, cond.value_ & cond.mask_)
+ << "Value contains masked out bits";
+ if (sizeof(void*) == 4) {
+ CHECK_EQ(ErrorCode::TP_32BIT, cond.width_)
+ << "Invalid width on 32-bit platform";
+ }
+ if (cond.width_ == ErrorCode::TP_32BIT) {
+ CHECK_EQ(0U, cond.mask_ >> 32) << "Mask exceeds argument size";
+ CHECK_EQ(0U, cond.value_ >> 32) << "Value exceeds argument size";
+ }
+
+ CodeGen::Node passed = RetExpression(*cond.passed_);
+ CodeGen::Node failed = RetExpression(*cond.failed_);
+
+ // We want to emit code to check "(arg & mask) == value" where arg, mask, and
+ // value are 64-bit values, but the BPF machine is only 32-bit. We implement
+ // this by independently testing the upper and lower 32-bits and continuing to
+ // |passed| if both evaluate true, or to |failed| if either evaluate false.
+ return CondExpressionHalf(cond,
+ UpperHalf,
+ CondExpressionHalf(cond, LowerHalf, passed, failed),
+ failed);
+}
+
+CodeGen::Node PolicyCompiler::CondExpressionHalf(const ErrorCode& cond,
+ ArgHalf half,
+ CodeGen::Node passed,
+ CodeGen::Node failed) {
+ if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) {
+ // Special logic for sanity checking the upper 32-bits of 32-bit system
+ // call arguments.
+
+ // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
+ CodeGen::Node invalid_64bit = RetExpression(Unexpected64bitArgument());
+
+ const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_);
+ const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_);
+
+ if (sizeof(void*) == 4) {
+ // On 32-bit platforms, the upper 32-bits should always be 0:
+ // LDW [upper]
+ // JEQ 0, passed, invalid
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
+ }
+
+ // On 64-bit platforms, the upper 32-bits may be 0 or ~0; but we only allow
+ // ~0 if the sign bit of the lower 32-bits is set too:
+ // LDW [upper]
+ // JEQ 0, passed, (next)
+ // JEQ ~0, (next), invalid
+ // LDW [lower]
+ // JSET (1<<31), passed, invalid
+ //
+ // TODO(mdempsky): The JSET instruction could perhaps jump to passed->next
+ // instead, as the first instruction of passed should be "LDW [lower]".
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 0,
+ passed,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ std::numeric_limits<uint32_t>::max(),
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ lower,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ 1U << 31,
+ passed,
+ invalid_64bit)),
+ invalid_64bit)));
+ }
+
+ const uint32_t idx = (half == UpperHalf) ? SECCOMP_ARG_MSB_IDX(cond.argno_)
+ : SECCOMP_ARG_LSB_IDX(cond.argno_);
+ const uint32_t mask = (half == UpperHalf) ? cond.mask_ >> 32 : cond.mask_;
+ const uint32_t value = (half == UpperHalf) ? cond.value_ >> 32 : cond.value_;
+
+ // Emit a suitable instruction sequence for (arg & mask) == value.
+
+ // For (arg & 0) == 0, just return passed.
+ if (mask == 0) {
+ CHECK_EQ(0U, value);
+ return passed;
+ }
+
+ // For (arg & ~0) == value, emit:
+ // LDW [idx]
+ // JEQ value, passed, failed
+ if (mask == std::numeric_limits<uint32_t>::max()) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed));
+ }
+
+ // For (arg & mask) == 0, emit:
+ // LDW [idx]
+ // JSET mask, failed, passed
+ // (Note: failed and passed are intentionally swapped.)
+ if (value == 0) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, failed, passed));
+ }
+
+ // For (arg & x) == x where x is a single-bit value, emit:
+ // LDW [idx]
+ // JSET mask, passed, failed
+ if (mask == value && HasExactlyOneBit(mask)) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, passed, failed));
+ }
+
+ // Generic fallback:
+ // LDW [idx]
+ // AND mask
+ // JEQ value, passed, failed
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(
+ BPF_ALU + BPF_AND + BPF_K,
+ mask,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
+}
+
+ErrorCode PolicyCompiler::Unexpected64bitArgument() {
+ return Kill("Unexpected 64bit argument detected")->Compile(this);
+}
+
+ErrorCode PolicyCompiler::Error(int err) {
+ if (has_unsafe_traps_) {
+ // 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.
+ return Trap(ReturnErrno, reinterpret_cast<void*>(err), true);
+ }
+
+ return ErrorCode(err);
+}
+
+ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc,
+ const void* aux,
+ bool safe) {
+ uint16_t trap_id = registry_->Add(fnc, aux, safe);
+ return ErrorCode(trap_id, fnc, aux, safe);
+}
+
+bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) {
+ for (size_t i = 0; i < arraysize(kSyscallsRequiredForUnsafeTraps); ++i) {
+ if (sysno == kSyscallsRequiredForUnsafeTraps[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ErrorCode PolicyCompiler::CondMaskedEqual(int argno,
+ ErrorCode::ArgType width,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed) {
+ return ErrorCode(argno,
+ width,
+ mask,
+ value,
+ &*conds_.insert(passed).first,
+ &*conds_.insert(failed).first);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/policy_compiler.h b/sandbox/linux/bpf_dsl/policy_compiler.h
new file mode 100644
index 0000000000..df38d4ccbc
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/policy_compiler.h
@@ -0,0 +1,159 @@
+// 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_BPF_DSL_POLICY_COMPILER_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class Policy;
+
+// PolicyCompiler implements the bpf_dsl compiler, allowing users to
+// transform bpf_dsl policies into BPF programs to be executed by the
+// Linux kernel.
+class SANDBOX_EXPORT PolicyCompiler {
+ public:
+ PolicyCompiler(const Policy* policy, TrapRegistry* registry);
+ ~PolicyCompiler();
+
+ // Compile registers any trap handlers needed by the policy and
+ // compiles the policy to a BPF program, which it returns.
+ scoped_ptr<CodeGen::Program> Compile(bool verify);
+
+ // DangerousSetEscapePC sets the "escape PC" that is allowed to issue any
+ // system calls, regardless of policy.
+ void DangerousSetEscapePC(uint64_t escapepc);
+
+ // Error returns an ErrorCode to indicate the system call should fail with
+ // the specified error number.
+ ErrorCode Error(int err);
+
+ // Trap returns an ErrorCode to indicate the system call should
+ // instead invoke a trap handler.
+ ErrorCode Trap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // 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 bitwise-AND'd with "mask" and compared
+ // to "value"; if equal, then "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 CondMaskedEqual(int argno,
+ ErrorCode::ArgType is_32bit,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode& passed,
+ const ErrorCode& failed);
+
+ // 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:
+ struct Range;
+ typedef std::vector<Range> Ranges;
+ typedef std::set<ErrorCode, struct ErrorCode::LessThan> Conds;
+
+ // Used by CondExpressionHalf to track which half of the argument it's
+ // emitting instructions for.
+ enum ArgHalf {
+ LowerHalf,
+ UpperHalf,
+ };
+
+ // Compile the configured policy into a complete instruction sequence.
+ CodeGen::Node AssemblePolicy();
+
+ // Return an instruction sequence that checks the
+ // arch_seccomp_data's "arch" field is valid, and then passes
+ // control to |passed| if so.
+ CodeGen::Node CheckArch(CodeGen::Node passed);
+
+ // If |has_unsafe_traps_| is true, returns an instruction sequence
+ // that allows all system calls from |escapepc_|, and otherwise
+ // passes control to |rest|. Otherwise, simply returns |rest|.
+ CodeGen::Node MaybeAddEscapeHatch(CodeGen::Node rest);
+
+ // Return an instruction sequence that loads and checks the system
+ // call number, performs a binary search, and then dispatches to an
+ // appropriate instruction sequence compiled from the current
+ // policy.
+ CodeGen::Node DispatchSyscall();
+
+ // Return an instruction sequence that checks the system call number
+ // (expected to be loaded in register A) and if valid, passes
+ // control to |passed| (with register A still valid).
+ CodeGen::Node CheckSyscallNumber(CodeGen::Node passed);
+
+ // 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.
+ CodeGen::Node AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop);
+
+ // CompileResult compiles an individual result expression into a
+ // CodeGen node.
+ CodeGen::Node CompileResult(const ResultExpr& res);
+
+ // 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.
+ CodeGen::Node RetExpression(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().
+ CodeGen::Node CondExpression(const ErrorCode& cond);
+
+ // Returns a BPF program that evaluates half of a conditional expression;
+ // it should only ever be called from CondExpression().
+ CodeGen::Node CondExpressionHalf(const ErrorCode& cond,
+ ArgHalf half,
+ CodeGen::Node passed,
+ CodeGen::Node failed);
+
+ const Policy* policy_;
+ TrapRegistry* registry_;
+ uint64_t escapepc_;
+
+ Conds conds_;
+ CodeGen gen_;
+ bool has_unsafe_traps_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyCompiler);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
diff --git a/sandbox/linux/bpf_dsl/seccomp_macros.h b/sandbox/linux/bpf_dsl/seccomp_macros.h
new file mode 100644
index 0000000000..ca28c1d7cd
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/seccomp_macros.h
@@ -0,0 +1,295 @@
+// 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_BPF_DSL_SECCOMP_MACROS_H_
+#define SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_
+
+#include <sys/cdefs.h>
+// Old Bionic versions do not have sys/user.h. The if can be removed once we no
+// longer need to support these old Bionic versions.
+// All x86_64 builds use a new enough bionic to have sys/user.h.
+#if !defined(__BIONIC__) || defined(__x86_64__)
+#include <sys/types.h> // Fix for gcc 4.7, make sure __uint16_t is defined.
+#if !defined(__native_client_nonsfi__)
+#include <sys/user.h>
+#endif
+#if defined(__mips__)
+// sys/user.h in eglibc misses size_t definition
+#include <stddef.h>
+#endif
+#endif
+
+#include "sandbox/linux/system_headers/linux_seccomp.h" // For AUDIT_ARCH_*
+
+// 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 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)
+
+
+#if defined(__BIONIC__) || defined(__native_client_nonsfi__)
+// Old Bionic versions and PNaCl toolchain don't have sys/user.h, so we just
+// define regs_struct directly. This can be removed once we no longer need to
+// support these old Bionic versions and PNaCl toolchain.
+struct regs_struct {
+ long int ebx;
+ long int ecx;
+ long int edx;
+ long int esi;
+ long int edi;
+ long int ebp;
+ long int eax;
+ long int xds;
+ long int xes;
+ long int xfs;
+ long int xgs;
+ long int orig_eax;
+ long int eip;
+ long int xcs;
+ long int eflags;
+ long int esp;
+ long int xss;
+};
+#else
+typedef user_regs_struct regs_struct;
+#endif
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).eax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_eax
+#define SECCOMP_PT_IP(_regs) (_regs).eip
+#define SECCOMP_PT_PARM1(_regs) (_regs).ebx
+#define SECCOMP_PT_PARM2(_regs) (_regs).ecx
+#define SECCOMP_PT_PARM3(_regs) (_regs).edx
+#define SECCOMP_PT_PARM4(_regs) (_regs).esi
+#define SECCOMP_PT_PARM5(_regs) (_regs).edi
+#define SECCOMP_PT_PARM6(_regs) (_regs).ebp
+
+#elif defined(__x86_64__)
+#define 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)
+
+typedef user_regs_struct regs_struct;
+#define SECCOMP_PT_RESULT(_regs) (_regs).rax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_rax
+#define SECCOMP_PT_IP(_regs) (_regs).rip
+#define SECCOMP_PT_PARM1(_regs) (_regs).rdi
+#define SECCOMP_PT_PARM2(_regs) (_regs).rsi
+#define SECCOMP_PT_PARM3(_regs) (_regs).rdx
+#define SECCOMP_PT_PARM4(_regs) (_regs).r10
+#define SECCOMP_PT_PARM5(_regs) (_regs).r8
+#define SECCOMP_PT_PARM6(_regs) (_regs).r9
+
+#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+#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)
+
+#if defined(__BIONIC__) || defined(__native_client_nonsfi__)
+// Old Bionic versions and PNaCl toolchain don't have sys/user.h, so we just
+// define regs_struct directly. This can be removed once we no longer need to
+// support these old Bionic versions and PNaCl toolchain.
+struct regs_struct {
+ unsigned long uregs[18];
+};
+#else
+typedef user_regs regs_struct;
+#endif
+
+#define REG_cpsr uregs[16]
+#define REG_pc uregs[15]
+#define REG_lr uregs[14]
+#define REG_sp uregs[13]
+#define REG_ip uregs[12]
+#define REG_fp uregs[11]
+#define REG_r10 uregs[10]
+#define REG_r9 uregs[9]
+#define REG_r8 uregs[8]
+#define REG_r7 uregs[7]
+#define REG_r6 uregs[6]
+#define REG_r5 uregs[5]
+#define REG_r4 uregs[4]
+#define REG_r3 uregs[3]
+#define REG_r2 uregs[2]
+#define REG_r1 uregs[1]
+#define REG_r0 uregs[0]
+#define REG_ORIG_r0 uregs[17]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_r0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_r7
+#define SECCOMP_PT_IP(_regs) (_regs).REG_pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_r0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_r1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_r2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_r3
+#define SECCOMP_PT_PARM5(_regs) (_regs).REG_r4
+#define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5
+
+#elif defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+#define SECCOMP_ARCH AUDIT_ARCH_MIPSEL
+#define SYSCALL_EIGHT_ARGS
+// MIPS sigcontext_t is different from i386/x86_64 and ARM.
+// See </arch/mips/include/uapi/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[_reg])
+// Based on MIPS o32 ABI syscall convention.
+// On MIPS, when indirect syscall is being made (syscall(__NR_foo)),
+// real identificator (__NR_foo) is not in v0, but in a0
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 5)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 6)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 7)
+// Only the first 4 arguments of syscall are in registers.
+// The rest are on the stack.
+#define SECCOMP_STACKPARM(_ctx, n) (((long *)SECCOMP_REG(_ctx, 29))[(n)])
+#define SECCOMP_PARM5(_ctx) SECCOMP_STACKPARM(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_STACKPARM(_ctx, 5)
+#define SECCOMP_PARM7(_ctx) SECCOMP_STACKPARM(_ctx, 6)
+#define SECCOMP_PARM8(_ctx) SECCOMP_STACKPARM(_ctx, 7)
+#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)
+
+// On Mips we don't have structures like user_regs or user_regs_struct in
+// sys/user.h that we could use, so we just define regs_struct directly.
+struct regs_struct {
+ unsigned long long regs[32];
+};
+
+#define REG_a3 regs[7]
+#define REG_a2 regs[6]
+#define REG_a1 regs[5]
+#define REG_a0 regs[4]
+#define REG_v1 regs[3]
+#define REG_v0 regs[2]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_v0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_v0
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_a0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_a1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_a2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_a3
+
+#elif defined(__aarch64__)
+struct regs_struct {
+ unsigned long long regs[31];
+ unsigned long long sp;
+ unsigned long long pc;
+ unsigned long long pstate;
+};
+
+#define SECCOMP_ARCH AUDIT_ARCH_AARCH64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.regs[_reg])
+
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 8)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, 5)
+
+#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)
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).regs[0]
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).regs[8]
+#define SECCOMP_PT_IP(_regs) (_regs).pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).regs[0]
+#define SECCOMP_PT_PARM2(_regs) (_regs).regs[1]
+#define SECCOMP_PT_PARM3(_regs) (_regs).regs[2]
+#define SECCOMP_PT_PARM4(_regs) (_regs).regs[3]
+#define SECCOMP_PT_PARM5(_regs) (_regs).regs[4]
+#define SECCOMP_PT_PARM6(_regs) (_regs).regs[5]
+#else
+#error Unsupported target platform
+
+#endif
+
+#endif // SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_
diff --git a/sandbox/linux/bpf_dsl/syscall_set.cc b/sandbox/linux/bpf_dsl/syscall_set.cc
new file mode 100644
index 0000000000..47810e99ac
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/syscall_set.cc
@@ -0,0 +1,144 @@
+// 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/bpf_dsl/syscall_set.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+// This is true for Mips O32 ABI.
+static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 4000");
+#else
+// This true for supported architectures (Intel and ARM EABI).
+static_assert(MIN_SYSCALL == 0u,
+ "min syscall should always be zero");
+#endif
+
+// SyscallRange represents an inclusive range of system call numbers.
+struct SyscallRange {
+ uint32_t first;
+ uint32_t last;
+};
+
+const SyscallRange kValidSyscallRanges[] = {
+ // 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.
+ {MIN_SYSCALL, MAX_PUBLIC_SYSCALL},
+#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.
+ {MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL},
+ {MIN_GHOST_SYSCALL, MAX_SYSCALL},
+#endif
+};
+
+} // namespace
+
+SyscallSet::Iterator SyscallSet::begin() const {
+ return Iterator(set_, false);
+}
+
+SyscallSet::Iterator SyscallSet::end() const {
+ return Iterator(set_, true);
+}
+
+bool SyscallSet::IsValid(uint32_t num) {
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (num >= range.first && num <= range.last) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool operator==(const SyscallSet& lhs, const SyscallSet& rhs) {
+ return (lhs.set_ == rhs.set_);
+}
+
+SyscallSet::Iterator::Iterator(Set set, bool done)
+ : set_(set), done_(done), num_(0) {
+ // If the set doesn't contain 0, we need to skip to the next element.
+ if (!done && set_ == (IsValid(num_) ? Set::INVALID_ONLY : Set::VALID_ONLY)) {
+ ++*this;
+ }
+}
+
+uint32_t SyscallSet::Iterator::operator*() const {
+ DCHECK(!done_);
+ return num_;
+}
+
+SyscallSet::Iterator& SyscallSet::Iterator::operator++() {
+ DCHECK(!done_);
+
+ num_ = NextSyscall();
+ if (num_ == 0) {
+ done_ = true;
+ }
+
+ return *this;
+}
+
+// NextSyscall returns the next system call in the iterated system
+// call set after |num_|, or 0 if no such system call exists.
+uint32_t SyscallSet::Iterator::NextSyscall() const {
+ const bool want_valid = (set_ != Set::INVALID_ONLY);
+ const bool want_invalid = (set_ != Set::VALID_ONLY);
+
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (want_invalid && range.first > 0 && num_ < range.first - 1) {
+ // Even when iterating invalid syscalls, we only include the end points;
+ // so skip directly to just before the next (valid) range.
+ return range.first - 1;
+ }
+ if (want_valid && num_ < range.first) {
+ return range.first;
+ }
+ if (want_valid && num_ < range.last) {
+ return num_ + 1;
+ }
+ if (want_invalid && num_ <= range.last) {
+ return range.last + 1;
+ }
+ }
+
+ if (want_invalid) {
+ // 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.
+ if (num_ < 0x7FFFFFFFu)
+ return 0x7FFFFFFFu;
+ if (num_ < 0x80000000u)
+ return 0x80000000u;
+
+ if (num_ < 0xFFFFFFFFu)
+ return 0xFFFFFFFFu;
+ }
+
+ return 0;
+}
+
+bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ DCHECK(lhs.set_ == rhs.set_);
+ return (lhs.done_ == rhs.done_) && (lhs.num_ == rhs.num_);
+}
+
+bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/syscall_set.h b/sandbox/linux/bpf_dsl/syscall_set.h
new file mode 100644
index 0000000000..b9f076d932
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/syscall_set.h
@@ -0,0 +1,103 @@
+// 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_BPF_DSL_SYSCALL_SET_H__
+#define SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
+
+#include <stdint.h>
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
+// iterator is aware of how system calls look like and will skip quickly
+// 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).
+//
+// Example usage:
+// for (uint32_t sysnum : SyscallSet::All()) {
+// // Do something with sysnum.
+// }
+class SANDBOX_EXPORT SyscallSet {
+ public:
+ class Iterator;
+
+ SyscallSet(const SyscallSet& ss) : set_(ss.set_) {}
+ ~SyscallSet() {}
+
+ Iterator begin() const;
+ Iterator end() const;
+
+ // All returns a SyscallSet that contains both valid and invalid
+ // system call numbers.
+ static SyscallSet All() { return SyscallSet(Set::ALL); }
+
+ // ValidOnly returns a SyscallSet that contains only valid system
+ // call numbers.
+ static SyscallSet ValidOnly() { return SyscallSet(Set::VALID_ONLY); }
+
+ // InvalidOnly returns a SyscallSet that contains only invalid
+ // system call numbers, but still omits numbers in the middle of a
+ // range of invalid system call numbers.
+ static SyscallSet InvalidOnly() { return SyscallSet(Set::INVALID_ONLY); }
+
+ // IsValid returns whether |num| specifies a valid system call
+ // number.
+ static bool IsValid(uint32_t num);
+
+ private:
+ enum class Set { ALL, VALID_ONLY, INVALID_ONLY };
+
+ explicit SyscallSet(Set set) : set_(set) {}
+
+ Set set_;
+
+ friend bool operator==(const SyscallSet&, const SyscallSet&);
+ DISALLOW_ASSIGN(SyscallSet);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet& lhs, const SyscallSet& rhs);
+
+// Iterator provides C++ input iterator semantics for traversing a
+// SyscallSet.
+class SyscallSet::Iterator
+ : public std::iterator<std::input_iterator_tag, uint32_t> {
+ public:
+ Iterator(const Iterator& it)
+ : set_(it.set_), done_(it.done_), num_(it.num_) {}
+ ~Iterator() {}
+
+ uint32_t operator*() const;
+ Iterator& operator++();
+
+ private:
+ Iterator(Set set, bool done);
+
+ uint32_t NextSyscall() const;
+
+ Set set_;
+ bool done_;
+ uint32_t num_;
+
+ friend SyscallSet;
+ friend bool operator==(const Iterator&, const Iterator&);
+ DISALLOW_ASSIGN(Iterator);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
diff --git a/sandbox/linux/bpf_dsl/syscall_set_unittest.cc b/sandbox/linux/bpf_dsl/syscall_set_unittest.cc
new file mode 100644
index 0000000000..fafb6f6f73
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/syscall_set_unittest.cc
@@ -0,0 +1,124 @@
+// 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/bpf_dsl/syscall_set.h"
+
+#include <stdint.h>
+
+#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+const SyscallSet kSyscallSets[] = {
+ SyscallSet::All(),
+ SyscallSet::InvalidOnly(),
+};
+
+SANDBOX_TEST(SyscallSet, Monotonous) {
+ for (const SyscallSet& set : kSyscallSets) {
+ uint32_t prev = 0;
+ bool have_prev = false;
+ for (uint32_t sysnum : set) {
+ if (have_prev) {
+ SANDBOX_ASSERT(sysnum > prev);
+ } else if (set == SyscallSet::All()) {
+ // The iterator should start at 0.
+ SANDBOX_ASSERT(sysnum == 0);
+ }
+
+ prev = sysnum;
+ have_prev = true;
+ }
+
+ // The iterator should always return 0xFFFFFFFFu as the last value.
+ SANDBOX_ASSERT(have_prev);
+ SANDBOX_ASSERT(prev == 0xFFFFFFFFu);
+ }
+}
+
+// AssertRange checks that SyscallIterator produces all system call
+// numbers in the inclusive range [min, max].
+void AssertRange(uint32_t min, uint32_t max) {
+ SANDBOX_ASSERT(min < max);
+ uint32_t prev = min - 1;
+ for (uint32_t sysnum : SyscallSet::All()) {
+ if (sysnum >= min && sysnum <= max) {
+ SANDBOX_ASSERT(prev == sysnum - 1);
+ prev = sysnum;
+ }
+ }
+ SANDBOX_ASSERT(prev == max);
+}
+
+SANDBOX_TEST(SyscallSet, ValidSyscallRanges) {
+ AssertRange(MIN_SYSCALL, MAX_PUBLIC_SYSCALL);
+#if defined(__arm__)
+ AssertRange(MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL);
+ AssertRange(MIN_GHOST_SYSCALL, MAX_SYSCALL);
+#endif
+}
+
+SANDBOX_TEST(SyscallSet, InvalidSyscalls) {
+ static const uint32_t kExpected[] = {
+#if defined(__mips__)
+ 0,
+ MIN_SYSCALL - 1,
+#endif
+ MAX_PUBLIC_SYSCALL + 1,
+#if defined(__arm__)
+ MIN_PRIVATE_SYSCALL - 1,
+ MAX_PRIVATE_SYSCALL + 1,
+ MIN_GHOST_SYSCALL - 1,
+ MAX_SYSCALL + 1,
+#endif
+ 0x7FFFFFFFu,
+ 0x80000000u,
+ 0xFFFFFFFFu,
+ };
+
+ for (const SyscallSet& set : kSyscallSets) {
+ size_t i = 0;
+ for (uint32_t sysnum : set) {
+ if (!SyscallSet::IsValid(sysnum)) {
+ SANDBOX_ASSERT(i < arraysize(kExpected));
+ SANDBOX_ASSERT(kExpected[i] == sysnum);
+ ++i;
+ }
+ }
+ SANDBOX_ASSERT(i == arraysize(kExpected));
+ }
+}
+
+SANDBOX_TEST(SyscallSet, ValidOnlyIsOnlyValid) {
+ for (uint32_t sysnum : SyscallSet::ValidOnly()) {
+ SANDBOX_ASSERT(SyscallSet::IsValid(sysnum));
+ }
+}
+
+SANDBOX_TEST(SyscallSet, InvalidOnlyIsOnlyInvalid) {
+ for (uint32_t sysnum : SyscallSet::InvalidOnly()) {
+ SANDBOX_ASSERT(!SyscallSet::IsValid(sysnum));
+ }
+}
+
+SANDBOX_TEST(SyscallSet, AllIsValidOnlyPlusInvalidOnly) {
+ std::vector<uint32_t> merged;
+ const SyscallSet valid_only = SyscallSet::ValidOnly();
+ const SyscallSet invalid_only = SyscallSet::InvalidOnly();
+ std::merge(valid_only.begin(),
+ valid_only.end(),
+ invalid_only.begin(),
+ invalid_only.end(),
+ std::back_inserter(merged));
+
+ const SyscallSet all = SyscallSet::All();
+ SANDBOX_ASSERT(merged == std::vector<uint32_t>(all.begin(), all.end()));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/trap_registry.h b/sandbox/linux/bpf_dsl/trap_registry.h
new file mode 100644
index 0000000000..0a5d2f14cc
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/trap_registry.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
+#define SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This must match the kernel's seccomp_data structure.
+struct arch_seccomp_data {
+ int nr;
+ uint32_t arch;
+ uint64_t instruction_pointer;
+ uint64_t args[6];
+};
+
+namespace bpf_dsl {
+
+// TrapRegistry provides an interface for registering "trap handlers"
+// by associating them with non-zero 16-bit trap IDs. Trap IDs should
+// remain valid for the lifetime of the trap registry.
+class SANDBOX_EXPORT TrapRegistry {
+ public:
+ // TrapFnc is a pointer to a function that fulfills the trap handler
+ // function signature.
+ //
+ // Trap handlers follow the calling convention of native system
+ // calls; e.g., to report an error, they return an exit code in the
+ // range -1..-4096 instead of directly modifying errno. However,
+ // modifying errno is harmless, as the original value will be
+ // restored afterwards.
+ //
+ // Trap handlers are executed from signal context and possibly an
+ // async-signal context, so they must be async-signal safe:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+ typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void* aux);
+
+ // Add registers the specified trap handler tuple and returns a
+ // non-zero trap ID that uniquely identifies the tuple for the life
+ // time of the trap registry. If the same tuple is registered
+ // multiple times, the same value will be returned each time.
+ virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) = 0;
+
+ // EnableUnsafeTraps tries to enable unsafe traps and returns
+ // whether it was successful. This is a one-way operation.
+ //
+ // CAUTION: Enabling unsafe traps effectively defeats the security
+ // guarantees provided by the sandbox policy. TrapRegistry
+ // implementations should ensure unsafe traps are only enabled
+ // during testing.
+ virtual bool EnableUnsafeTraps() = 0;
+
+ protected:
+ TrapRegistry() {}
+
+ // TrapRegistry's destructor is intentionally non-virtual so that
+ // implementations can omit their destructor. Instead we protect against
+ // misuse by marking it protected.
+ ~TrapRegistry() {}
+
+ DISALLOW_COPY_AND_ASSIGN(TrapRegistry);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
diff --git a/sandbox/linux/bpf_dsl/verifier.cc b/sandbox/linux/bpf_dsl/verifier.cc
new file mode 100644
index 0000000000..417c663e30
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/verifier.cc
@@ -0,0 +1,396 @@
+// 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/bpf_dsl/verifier.h"
+
+#include <string.h>
+
+#include <limits>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+const uint64_t kLower32Bits = std::numeric_limits<uint32_t>::max();
+const uint64_t kUpper32Bits = static_cast<uint64_t>(kLower32Bits) << 32;
+
+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(bpf_dsl::PolicyCompiler* compiler,
+ 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 compiler->Unexpected64bitArgument().err();
+ }
+ bool equal = (data.args[code.argno()] & code.mask()) == code.value();
+ return EvaluateErrorCode(compiler, equal ? *code.passed() : *code.failed(),
+ data);
+ } else {
+ return SECCOMP_RET_INVALID;
+ }
+}
+
+bool VerifyErrorCode(bpf_dsl::PolicyCompiler* compiler,
+ 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) {
+ const uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err);
+ if (*err) {
+ return false;
+ }
+ const uint32_t policy_ret = EvaluateErrorCode(compiler, root_code, *data);
+ if (computed_ret != policy_ret) {
+ // 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;
+ }
+
+ // TODO(mdempsky): The test values generated here try to provide good
+ // coverage for generated BPF instructions while avoiding combinatorial
+ // explosion on large policies. Ideally we would instead take a fuzzing-like
+ // approach and generate a bounded number of test cases regardless of policy
+ // size.
+
+ // Verify that we can check a value for simple equality.
+ data->args[code.argno()] = code.value();
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.passed(),
+ err)) {
+ return false;
+ }
+
+ // If mask ignores any bits, verify that setting those bits is still
+ // detected as equality.
+ uint64_t ignored_bits = ~code.mask();
+ if (code.width() == ErrorCode::TP_32BIT) {
+ ignored_bits = static_cast<uint32_t>(ignored_bits);
+ }
+ if ((ignored_bits & kLower32Bits) != 0) {
+ data->args[code.argno()] = code.value() | (ignored_bits & kLower32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.passed(),
+ err)) {
+ return false;
+ }
+ }
+ if ((ignored_bits & kUpper32Bits) != 0) {
+ data->args[code.argno()] = code.value() | (ignored_bits & kUpper32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.passed(),
+ err)) {
+ return false;
+ }
+ }
+
+ // Verify that changing bits included in the mask is detected as inequality.
+ if ((code.mask() & kLower32Bits) != 0) {
+ data->args[code.argno()] = code.value() ^ (code.mask() & kLower32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.failed(),
+ err)) {
+ return false;
+ }
+ }
+ if ((code.mask() & kUpper32Bits) != 0) {
+ data->args[code.argno()] = code.value() ^ (code.mask() & kUpper32Bits);
+ if (!VerifyErrorCode(compiler, program, data, root_code, *code.failed(),
+ err)) {
+ return false;
+ }
+ }
+
+ if (code.width() == ErrorCode::TP_32BIT) {
+ // For 32-bit system call arguments, we emit additional instructions to
+ // validate the upper 32-bits. Here we test that validation.
+
+ // Arbitrary 64-bit values should be rejected.
+ data->args[code.argno()] = 1ULL << 32;
+ if (!VerifyErrorCode(compiler, program, data, root_code,
+ compiler->Unexpected64bitArgument(), err)) {
+ return false;
+ }
+
+ // Upper 32-bits set without the MSB of the lower 32-bits set should be
+ // rejected too.
+ data->args[code.argno()] = kUpper32Bits;
+ if (!VerifyErrorCode(compiler, program, data, root_code,
+ compiler->Unexpected64bitArgument(), err)) {
+ 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 ||
+ insn.jt != 0 || insn.jf != 0) {
+ *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
+
+bool Verifier::VerifyBPF(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ const bpf_dsl::Policy& policy,
+ const char** err) {
+ *err = NULL;
+ for (uint32_t sysnum : SyscallSet::All()) {
+ // 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 = SyscallSet::IsValid(sysnum)
+ ? policy.EvaluateSyscall(sysnum)->Compile(compiler)
+ : policy.InvalidSyscall()->Compile(compiler);
+ if (!VerifyErrorCode(compiler, 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_TRACE:
+ case SECCOMP_RET_ALLOW:
+ break;
+ case SECCOMP_RET_KILL: // 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 bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/bpf_dsl/verifier.h b/sandbox/linux/bpf_dsl/verifier.h
new file mode 100644
index 0000000000..b0435d1aa1
--- /dev/null
+++ b/sandbox/linux/bpf_dsl/verifier.h
@@ -0,0 +1,57 @@
+// 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_BPF_DSL_VERIFIER_H__
+#define SANDBOX_LINUX_BPF_DSL_VERIFIER_H__
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+struct sock_filter;
+
+namespace sandbox {
+struct arch_seccomp_data;
+
+namespace bpf_dsl {
+class Policy;
+class PolicyCompiler;
+
+class SANDBOX_EXPORT 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(bpf_dsl::PolicyCompiler* compiler,
+ const std::vector<struct sock_filter>& program,
+ const bpf_dsl::Policy& policy,
+ const char** err);
+
+ // Evaluate a given BPF program for a particular set of system call
+ // parameters. If evaluation failed for any reason, "err" will be set to
+ // 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 bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_VERIFIER_H__
diff --git a/sandbox/linux/integration_tests/DEPS b/sandbox/linux/integration_tests/DEPS
new file mode 100644
index 0000000000..d50729cea3
--- /dev/null
+++ b/sandbox/linux/integration_tests/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+sandbox/linux/bpf_dsl",
+ "+sandbox/linux/seccomp-bpf",
+ "+sandbox/linux/services",
+ "+sandbox/linux/syscall_broker",
+ "+sandbox/linux/system_headers",
+]
diff --git a/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc b/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc
new file mode 100644
index 0000000000..e884774146
--- /dev/null
+++ b/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc
@@ -0,0 +1,2259 @@
+// Copyright 2015 The Chromium 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 <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#if defined(ANDROID)
+// Work-around for buggy headers in Android's NDK
+#define __user
+#endif
+#include <linux/futex.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/sys_info.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/scoped_temporary_file.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
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+const int kExpectedReturnValue = 42;
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// Set the global environment to allow the use of UnsafeTrap() policies.
+void EnableUnsafeTraps() {
+ // 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);
+}
+
+// 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 IncreaseCounter(const struct arch_seccomp_data& args, void* aux) {
+ BPF_ASSERT(aux);
+ int* counter = static_cast<int*>(aux);
+ return (*counter)++;
+}
+
+class VerboseAPITestingPolicy : public Policy {
+ public:
+ explicit VerboseAPITestingPolicy(int* counter_ptr)
+ : counter_ptr_(counter_ptr) {}
+ ~VerboseAPITestingPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return Trap(IncreaseCounter, counter_ptr_);
+ }
+ return Allow();
+ }
+
+ private:
+ int* counter_ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(VerboseAPITestingPolicy);
+};
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(VerboseAPITesting)) {
+ if (SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
+ static int counter = 0;
+
+ SandboxBPF sandbox(new VerboseAPITestingPolicy(&counter));
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
+
+ BPF_ASSERT_EQ(0, counter);
+ BPF_ASSERT_EQ(0, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(1, counter);
+ BPF_ASSERT_EQ(1, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(2, counter);
+ }
+}
+
+// A simple blacklist test
+
+class BlacklistNanosleepPolicy : public Policy {
+ public:
+ BlacklistNanosleepPolicy() {}
+ ~BlacklistNanosleepPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_nanosleep:
+ return Error(EACCES);
+ default:
+ return Allow();
+ }
+ }
+
+ static void AssertNanosleepFails() {
+ const struct timespec ts = {0, 0};
+ errno = 0;
+ BPF_ASSERT_EQ(-1, HANDLE_EINTR(syscall(__NR_nanosleep, &ts, NULL)));
+ BPF_ASSERT_EQ(EACCES, errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BlacklistNanosleepPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ApplyBasicBlacklistPolicy, BlacklistNanosleepPolicy) {
+ BlacklistNanosleepPolicy::AssertNanosleepFails();
+}
+
+BPF_TEST_C(SandboxBPF, UseVsyscall, BlacklistNanosleepPolicy) {
+ 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!
+ BPF_ASSERT_NE(static_cast<time_t>(-1), time(&current_time));
+}
+
+// Now do a simple whitelist test
+
+class WhitelistGetpidPolicy : public Policy {
+ public:
+ WhitelistGetpidPolicy() {}
+ ~WhitelistGetpidPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getpid:
+ case __NR_exit_group:
+ return Allow();
+ default:
+ return Error(ENOMEM);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WhitelistGetpidPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ApplyBasicWhitelistPolicy, WhitelistGetpidPolicy) {
+ // getpid() should be allowed
+ errno = 0;
+ BPF_ASSERT(sys_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;
+}
+
+class BlacklistNanosleepTrapPolicy : public Policy {
+ public:
+ explicit BlacklistNanosleepTrapPolicy(int* aux) : aux_(aux) {}
+ ~BlacklistNanosleepTrapPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_nanosleep:
+ return Trap(EnomemHandler, aux_);
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ int* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlacklistNanosleepTrapPolicy);
+};
+
+BPF_TEST(SandboxBPF,
+ BasicBlacklistWithSigsys,
+ BlacklistNanosleepTrapPolicy,
+ int /* (*BPF_AUX) */) {
+ // getpid() should work properly
+ errno = 0;
+ BPF_ASSERT(sys_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.
+
+class ErrnoTestPolicy : public Policy {
+ public:
+ ErrnoTestPolicy() {}
+ ~ErrnoTestPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ErrnoTestPolicy);
+};
+
+ResultExpr ErrnoTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_dup3: // dup2 is a wrapper of dup3 in android
+#if defined(__NR_dup2)
+ case __NR_dup2:
+#endif
+ // Pretend that dup2() worked, but don't actually do anything.
+ return Error(0);
+ case __NR_setuid:
+#if defined(__NR_setuid32)
+ case __NR_setuid32:
+#endif
+ // Return errno = 1.
+ return Error(1);
+ case __NR_setgid:
+#if defined(__NR_setgid32)
+ case __NR_setgid32:
+#endif
+ // Return maximum errno value (typically 4095).
+ return Error(ErrorCode::ERR_MAX_ERRNO);
+ case __NR_uname:
+ // Return errno = 42;
+ return Error(42);
+ default:
+ return Allow();
+ }
+}
+
+BPF_TEST_C(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
+
+class StackingPolicyPartOne : public Policy {
+ public:
+ StackingPolicyPartOne() {}
+ ~StackingPolicyPartOne() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getppid: {
+ const Arg<int> arg(0);
+ return If(arg == 0, Allow()).Else(Error(EPERM));
+ }
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackingPolicyPartOne);
+};
+
+class StackingPolicyPartTwo : public Policy {
+ public:
+ StackingPolicyPartTwo() {}
+ ~StackingPolicyPartTwo() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ switch (sysno) {
+ case __NR_getppid: {
+ const Arg<int> arg(0);
+ return If(arg == 0, Error(EINVAL)).Else(Allow());
+ }
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StackingPolicyPartTwo);
+};
+
+BPF_TEST_C(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.
+ SandboxBPF sandbox(new StackingPolicyPartTwo());
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
+
+ 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;
+}
+
+class SyntheticPolicy : public Policy {
+ public:
+ SyntheticPolicy() {}
+ ~SyntheticPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_exit_group || sysno == __NR_write) {
+ // exit_group() is special, we really need it to work.
+ // write() is needed for BPF_ASSERT() to report a useful error message.
+ return Allow();
+ }
+ return Error(SysnoToRandomErrno(sysno));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyntheticPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, SyntheticPolicy, SyntheticPolicy) {
+ // Ensure that that kExpectedReturnValue + syscallnumber + 1 does not int
+ // overflow.
+ BPF_ASSERT(std::numeric_limits<int>::max() - kExpectedReturnValue - 1 >=
+ 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;
+ }
+}
+
+class ArmPrivatePolicy : public Policy {
+ public:
+ ArmPrivatePolicy() {}
+ ~ArmPrivatePolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Start from |__ARM_NR_set_tls + 1| so as not to mess with actual
+ // ARM private system calls.
+ if (sysno >= static_cast<int>(__ARM_NR_set_tls + 1) &&
+ sysno <= static_cast<int>(MAX_PRIVATE_SYSCALL)) {
+ return Error(ArmPrivateSysnoToErrno(sysno));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ArmPrivatePolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ArmPrivatePolicy, ArmPrivatePolicy) {
+ for (int syscall_number = static_cast<int>(__ARM_NR_set_tls + 1);
+ syscall_number <= static_cast<int>(MAX_PRIVATE_SYSCALL);
+ ++syscall_number) {
+ 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(sys_getpid() > 1);
+
+ // Verify that we can now call the underlying system call without causing
+ // infinite recursion.
+ return SandboxBPF::ForwardSyscall(args);
+}
+
+class GreyListedPolicy : public Policy {
+ public:
+ explicit GreyListedPolicy(int* aux) : aux_(aux) {
+ // Set the global environment for unsafe traps once.
+ EnableUnsafeTraps();
+ }
+ ~GreyListedPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Some system calls must always be allowed, if our policy wants to make
+ // use of UnsafeTrap()
+ if (SandboxBPF::IsRequiredForUnsafeTrap(sysno)) {
+ return Allow();
+ } else if (sysno == __NR_getpid) {
+ // Disallow getpid()
+ return Error(EPERM);
+ } else {
+ // Allow (and count) all other system calls.
+ return UnsafeTrap(CountSyscalls, aux_);
+ }
+ }
+
+ private:
+ int* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(GreyListedPolicy);
+};
+
+BPF_TEST(SandboxBPF, GreyListedPolicy, GreyListedPolicy, int /* (*BPF_AUX) */) {
+ BPF_ASSERT(sys_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::Registry()->EnableUnsafeTraps() == false);
+ setenv(kSandboxDebuggingEnv, "", 1);
+ SANDBOX_ASSERT(Trap::Registry()->EnableUnsafeTraps() == false);
+ setenv(kSandboxDebuggingEnv, "t", 1);
+ SANDBOX_ASSERT(Trap::Registry()->EnableUnsafeTraps() == 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 SandboxBPF::ForwardSyscall(args);
+ }
+}
+
+class PrctlPolicy : public Policy {
+ public:
+ PrctlPolicy() {}
+ ~PrctlPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ if (sysno == __NR_prctl) {
+ // Handle prctl() inside an UnsafeTrap()
+ return UnsafeTrap(PrctlHandler, NULL);
+ }
+
+ // Allow all other system calls.
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrctlPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, ForwardSyscall, PrctlPolicy) {
+ // This call should never be allowed. But our policy will intercept it and
+ // let it pass successfully.
+ BPF_ASSERT(
+ !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 SandboxBPF::ForwardSyscall(args);
+}
+
+class RedirectAllSyscallsPolicy : public Policy {
+ public:
+ RedirectAllSyscallsPolicy() {}
+ ~RedirectAllSyscallsPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RedirectAllSyscallsPolicy);
+};
+
+ResultExpr RedirectAllSyscallsPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ // Some system calls must always be allowed, if our policy wants to make
+ // use of UnsafeTrap()
+ if (SandboxBPF::IsRequiredForUnsafeTrap(sysno))
+ return Allow();
+ return UnsafeTrap(AllowRedirectedSyscall, NULL);
+}
+
+#if !defined(ADDRESS_SANITIZER)
+// ASan does not allow changing the signal handler for SIGBUS, and treats it as
+// a fatal signal.
+
+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_C(SandboxBPF, SigBus, RedirectAllSyscallsPolicy) {
+ // We use the SIGBUS bit in the signal mask as a thread-local boolean
+ // value in the implementation of UnsafeTrap(). This is obviously a bit
+ // of a hack that could conceivably interfere with code that uses SIGBUS
+ // 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(socketpair(AF_UNIX, SOCK_STREAM, 0, 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);
+}
+#endif // !defined(ADDRESS_SANITIZER)
+
+BPF_TEST_C(SandboxBPF, SigMask, RedirectAllSyscallsPolicy) {
+ // Signal masks are potentially tricky to handle. For instance, if we
+ // ever tried to update them from inside a Trap() or UnsafeTrap() handler,
+ // the call to sigreturn() at the end of the signal handler would undo
+ // 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_C(SandboxBPF, UnsafeTrapWithErrno, RedirectAllSyscallsPolicy) {
+ // An UnsafeTrap() (or for that matter, a Trap()) has to report error
+ // conditions by returning an exit code in the range -1..-4096. This
+ // should happen automatically if using ForwardSyscall(). If the TrapFnc()
+ // 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(SandboxBPF::ForwardSyscall(args) == -EBADF);
+ BPF_ASSERT(errno == 0);
+}
+
+// Simple test demonstrating how to use SandboxBPF::Cond()
+
+class SimpleCondTestPolicy : public Policy {
+ public:
+ SimpleCondTestPolicy() {}
+ ~SimpleCondTestPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleCondTestPolicy);
+};
+
+ResultExpr SimpleCondTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+
+ // We deliberately return unusual errno values upon failure, so that we
+ // can uniquely test for these values. In a "real" policy, you would want
+ // to return more traditional values.
+ int flags_argument_position = -1;
+ switch (sysno) {
+#if defined(__NR_open)
+ case __NR_open:
+ flags_argument_position = 1;
+#endif
+ case __NR_openat: { // open can be a wrapper for openat(2).
+ if (sysno == __NR_openat)
+ flags_argument_position = 2;
+
+ // Allow opening files for reading, but don't allow writing.
+ static_assert(O_RDONLY == 0, "O_RDONLY must be all zero bits");
+ const Arg<int> flags(flags_argument_position);
+ return If((flags & O_ACCMODE) != 0, Error(EROFS)).Else(Allow());
+ }
+ case __NR_prctl: {
+ // Allow prctl(PR_SET_DUMPABLE) and prctl(PR_GET_DUMPABLE), but
+ // disallow everything else.
+ const Arg<int> option(0);
+ return If(option == PR_SET_DUMPABLE || option == PR_GET_DUMPABLE, Allow())
+ .Else(Error(ENOMEM));
+ }
+ default:
+ return Allow();
+ }
+}
+
+BPF_TEST_C(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 SandboxBPF::Cond() method by building a complex
+// tree of conditional equality operations. It then makes system calls and
+// verifies that they return the values that we expected from our BPF
+// program.
+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.
+ static_assert(
+ kNumTestCases < (int)(MAX_PUBLIC_SYSCALL - MIN_SYSCALL - 10),
+ "kNumTestCases must be significantly smaller than the number "
+ "of 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);
+ }
+ }
+
+ ResultExpr Policy(int sysno) {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ 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 Allow();
+ } else {
+ // ToErrorCode() turns an ArgValue object into an ErrorCode that is
+ // suitable for use by a sandbox policy.
+ return ToErrorCode(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;
+ }
+ }
+
+ ResultExpr ToErrorCode(ArgValue* arg_value) {
+ // Compute the ResultExpr 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).
+ ResultExpr 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 = Error(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 SandboxBPF::Cond()
+ // tests to our ErrorCode.
+ err = ToErrorCode(arg_value->arg_value);
+ }
+
+ // Now, iterate over all the test cases that we want to compare against.
+ // This builds a chain of SandboxBPF::Cond() tests
+ // (aka "if ... elif ... elif ... elif ... fi")
+ for (int n = arg_value->size; n-- > 0;) {
+ ResultExpr matched;
+ // Again, we distinguish between leaf nodes and subtrees.
+ if (arg_value->tests[n].err) {
+ matched = Error(arg_value->tests[n].err);
+ } else {
+ matched = ToErrorCode(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.
+ const Arg<uint32_t> arg(arg_value->argno);
+ err = If(arg == arg_value->tests[n].k_value, matched).Else(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(
+ Syscall::Call(
+ 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.
+#if defined(__aarch64__)
+ static const int kNumTestCases = 30;
+#else
+ static const int kNumTestCases = 40;
+#endif
+ static const int kMaxFanOut = 3;
+ static const int kMaxArgs = 6;
+};
+
+class EqualityStressTestPolicy : public Policy {
+ public:
+ explicit EqualityStressTestPolicy(EqualityStressTest* aux) : aux_(aux) {}
+ ~EqualityStressTestPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ return aux_->Policy(sysno);
+ }
+
+ private:
+ EqualityStressTest* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(EqualityStressTestPolicy);
+};
+
+BPF_TEST(SandboxBPF,
+ EqualityTests,
+ EqualityStressTestPolicy,
+ EqualityStressTest /* (*BPF_AUX) */) {
+ BPF_AUX->VerifyFilter();
+}
+
+class EqualityArgumentWidthPolicy : public Policy {
+ public:
+ EqualityArgumentWidthPolicy() {}
+ ~EqualityArgumentWidthPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EqualityArgumentWidthPolicy);
+};
+
+ResultExpr EqualityArgumentWidthPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ const Arg<uint32_t> arg32(1);
+ const Arg<uint64_t> arg64(1);
+ return Switch(option)
+ .Case(0, If(arg32 == 0x55555555, Error(1)).Else(Error(2)))
+#if __SIZEOF_POINTER__ > 4
+ .Case(1, If(arg64 == 0x55555555AAAAAAAAULL, Error(1)).Else(Error(2)))
+#endif
+ .Default(Error(3));
+ }
+ return Allow();
+}
+
+BPF_TEST_C(SandboxBPF, EqualityArgumentWidth, EqualityArgumentWidthPolicy) {
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0, 0x55555555) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0, 0xAAAAAAAA) == -2);
+#if __SIZEOF_POINTER__ > 4
+ // On 32bit machines, there is no way to pass a 64bit argument through the
+ // syscall interface. So, we have to skip the part of the test that requires
+ // 64bit arguments.
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x55555555AAAAAAAAULL) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x5555555500000000ULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x5555555511111111ULL) == -2);
+ BPF_ASSERT(Syscall::Call(__NR_uname, 1, 0x11111111AAAAAAAAULL) == -2);
+#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_C(SandboxBPF,
+ EqualityArgumentUnallowed64bit,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityArgumentWidthPolicy) {
+ Syscall::Call(__NR_uname, 0, 0x5555555555555555ULL);
+}
+#endif
+
+class EqualityWithNegativeArgumentsPolicy : public Policy {
+ public:
+ EqualityWithNegativeArgumentsPolicy() {}
+ ~EqualityWithNegativeArgumentsPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ // TODO(mdempsky): This currently can't be Arg<int> because then
+ // 0xFFFFFFFF will be treated as a (signed) int, and then when
+ // Arg::EqualTo casts it to uint64_t, it will be sign extended.
+ const Arg<unsigned> arg(0);
+ return If(arg == 0xFFFFFFFF, Error(1)).Else(Error(2));
+ }
+ return Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(EqualityWithNegativeArgumentsPolicy);
+};
+
+BPF_TEST_C(SandboxBPF,
+ EqualityWithNegativeArguments,
+ EqualityWithNegativeArgumentsPolicy) {
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0xFFFFFFFF) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, -1) == -1);
+ BPF_ASSERT(Syscall::Call(__NR_uname, -1LL) == -1);
+}
+
+#if __SIZEOF_POINTER__ > 4
+BPF_DEATH_TEST_C(SandboxBPF,
+ EqualityWithNegative64bitArguments,
+ DEATH_MESSAGE("Unexpected 64bit argument detected"),
+ EqualityWithNegativeArgumentsPolicy) {
+ // When expecting a 32bit system call argument, we look at the MSB of the
+ // 64bit value and allow both "0" and "-1". But the latter is allowed only
+ // iff the LSB was negative. So, this death test should error out.
+ BPF_ASSERT(Syscall::Call(__NR_uname, 0xFFFFFFFF00000000LL) == -1);
+}
+#endif
+
+class AllBitTestPolicy : public Policy {
+ public:
+ AllBitTestPolicy() {}
+ ~AllBitTestPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static ResultExpr HasAllBits32(uint32_t bits);
+ static ResultExpr HasAllBits64(uint64_t bits);
+
+ DISALLOW_COPY_AND_ASSIGN(AllBitTestPolicy);
+};
+
+ResultExpr AllBitTestPolicy::HasAllBits32(uint32_t bits) {
+ if (bits == 0) {
+ return Error(1);
+ }
+ const Arg<uint32_t> arg(1);
+ return If((arg & bits) == bits, Error(1)).Else(Error(0));
+}
+
+ResultExpr AllBitTestPolicy::HasAllBits64(uint64_t bits) {
+ if (bits == 0) {
+ return Error(1);
+ }
+ const Arg<uint64_t> arg(1);
+ return If((arg & bits) == bits, Error(1)).Else(Error(0));
+}
+
+ResultExpr AllBitTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Test masked-equality cases that should trigger the "has all bits"
+ // peephole optimizations. 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 (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ return Switch(option)
+ .Case(0, HasAllBits32(0x0))
+ .Case(1, HasAllBits32(0x1))
+ .Case(2, HasAllBits32(0x3))
+ .Case(3, HasAllBits32(0x80000000))
+#if __SIZEOF_POINTER__ > 4
+ .Case(4, HasAllBits64(0x0))
+ .Case(5, HasAllBits64(0x1))
+ .Case(6, HasAllBits64(0x3))
+ .Case(7, HasAllBits64(0x80000000))
+ .Case(8, HasAllBits64(0x100000000ULL))
+ .Case(9, HasAllBits64(0x300000000ULL))
+ .Case(10, HasAllBits64(0x100000001ULL))
+#endif
+ .Default(Kill("Invalid test case number"));
+ }
+ return Allow();
+}
+
+// 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(Syscall::Call(__NR_uname, (testcase), (arg)) == (expected_value))
+
+// Our uname() system call returns ErrorCode(1) for success and
+// ErrorCode(0) for failure. Syscall::Call() 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_C(SandboxBPF, AllBitTests, AllBitTestPolicy) {
+ // 32bit test: all of 0x0 (should always be true)
+ BITMASK_TEST( 0, 0, ALLBITS32, 0, EXPECT_SUCCESS);
+ BITMASK_TEST( 0, 1, ALLBITS32, 0, EXPECT_SUCCESS);
+ 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);
+
+#if __SIZEOF_POINTER__ > 4
+ // 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);
+#endif
+}
+
+class AnyBitTestPolicy : public Policy {
+ public:
+ AnyBitTestPolicy() {}
+ ~AnyBitTestPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static ResultExpr HasAnyBits32(uint32_t);
+ static ResultExpr HasAnyBits64(uint64_t);
+
+ DISALLOW_COPY_AND_ASSIGN(AnyBitTestPolicy);
+};
+
+ResultExpr AnyBitTestPolicy::HasAnyBits32(uint32_t bits) {
+ if (bits == 0) {
+ return Error(0);
+ }
+ const Arg<uint32_t> arg(1);
+ return If((arg & bits) != 0, Error(1)).Else(Error(0));
+}
+
+ResultExpr AnyBitTestPolicy::HasAnyBits64(uint64_t bits) {
+ if (bits == 0) {
+ return Error(0);
+ }
+ const Arg<uint64_t> arg(1);
+ return If((arg & bits) != 0, Error(1)).Else(Error(0));
+}
+
+ResultExpr AnyBitTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Test masked-equality cases that should trigger the "has any bits"
+ // peephole optimizations. 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 (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ return Switch(option)
+ .Case(0, HasAnyBits32(0x0))
+ .Case(1, HasAnyBits32(0x1))
+ .Case(2, HasAnyBits32(0x3))
+ .Case(3, HasAnyBits32(0x80000000))
+#if __SIZEOF_POINTER__ > 4
+ .Case(4, HasAnyBits64(0x0))
+ .Case(5, HasAnyBits64(0x1))
+ .Case(6, HasAnyBits64(0x3))
+ .Case(7, HasAnyBits64(0x80000000))
+ .Case(8, HasAnyBits64(0x100000000ULL))
+ .Case(9, HasAnyBits64(0x300000000ULL))
+ .Case(10, HasAnyBits64(0x100000001ULL))
+#endif
+ .Default(Kill("Invalid test case number"));
+ }
+ return Allow();
+}
+
+BPF_TEST_C(SandboxBPF, AnyBitTests, AnyBitTestPolicy) {
+ // 32bit test: any of 0x0 (should always be false)
+ BITMASK_TEST( 0, 0, ANYBITS32, 0x0, EXPECT_FAILURE);
+ BITMASK_TEST( 0, 1, ANYBITS32, 0x0, EXPECT_FAILURE);
+ 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);
+
+#if __SIZEOF_POINTER__ > 4
+ // 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);
+#endif
+}
+
+class MaskedEqualTestPolicy : public Policy {
+ public:
+ MaskedEqualTestPolicy() {}
+ ~MaskedEqualTestPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static ResultExpr MaskedEqual32(uint32_t mask, uint32_t value);
+ static ResultExpr MaskedEqual64(uint64_t mask, uint64_t value);
+
+ DISALLOW_COPY_AND_ASSIGN(MaskedEqualTestPolicy);
+};
+
+ResultExpr MaskedEqualTestPolicy::MaskedEqual32(uint32_t mask, uint32_t value) {
+ const Arg<uint32_t> arg(1);
+ return If((arg & mask) == value, Error(1)).Else(Error(0));
+}
+
+ResultExpr MaskedEqualTestPolicy::MaskedEqual64(uint64_t mask, uint64_t value) {
+ const Arg<uint64_t> arg(1);
+ return If((arg & mask) == value, Error(1)).Else(Error(0));
+}
+
+ResultExpr MaskedEqualTestPolicy::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+
+ if (sysno == __NR_uname) {
+ const Arg<int> option(0);
+ return Switch(option)
+ .Case(0, MaskedEqual32(0x00ff00ff, 0x005500aa))
+#if __SIZEOF_POINTER__ > 4
+ .Case(1, MaskedEqual64(0x00ff00ff00000000, 0x005500aa00000000))
+ .Case(2, MaskedEqual64(0x00ff00ff00ff00ff, 0x005500aa005500aa))
+#endif
+ .Default(Kill("Invalid test case number"));
+ }
+
+ return Allow();
+}
+
+#define MASKEQ_TEST(rulenum, arg, expected_result) \
+ BPF_ASSERT(Syscall::Call(__NR_uname, (rulenum), (arg)) == (expected_result))
+
+BPF_TEST_C(SandboxBPF, MaskedEqualTests, MaskedEqualTestPolicy) {
+ // Allowed: 0x__55__aa
+ MASKEQ_TEST(0, 0x00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000001, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000003, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000100, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x00000300, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x005500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0x005500ab, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x005600aa, EXPECT_FAILURE);
+ MASKEQ_TEST(0, 0x005501aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0x005503aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0x555500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(0, 0xaa5500aa, EXPECT_SUCCESS);
+
+#if __SIZEOF_POINTER__ > 4
+ // Allowed: 0x__55__aa________
+ MASKEQ_TEST(1, 0x0000000000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000000000010, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000000000050, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000100000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000000300000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000010000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x0000030000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x005500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0x005500ab00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x005600aa00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(1, 0x005501aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0x005503aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0x555500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0xaa5500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0xaa5500aa00000000, EXPECT_SUCCESS);
+ MASKEQ_TEST(1, 0xaa5500aa0000cafe, EXPECT_SUCCESS);
+
+ // Allowed: 0x__55__aa__55__aa
+ MASKEQ_TEST(2, 0x0000000000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000000000010, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000000000050, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000100000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000000300000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000010000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x0000030000000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x00000000005500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005500aa00000000, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005500aa005500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(2, 0x005500aa005700aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005700aa005500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005500aa004500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x004500aa005500aa, EXPECT_FAILURE);
+ MASKEQ_TEST(2, 0x005512aa005500aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(2, 0x005500aa005534aa, EXPECT_SUCCESS);
+ MASKEQ_TEST(2, 0xff5500aa0055ffaa, EXPECT_SUCCESS);
+#endif
+}
+
+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;
+}
+
+class PthreadPolicyEquality : public Policy {
+ public:
+ PthreadPolicyEquality() {}
+ ~PthreadPolicyEquality() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PthreadPolicyEquality);
+};
+
+ResultExpr PthreadPolicyEquality::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // This policy allows creating threads with pthread_create(). But it
+ // doesn't allow any other uses of clone(). Most notably, it does not
+ // allow callers to implement fork() or vfork() by passing suitable flags
+ // to the clone() system call.
+ if (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.
+ // More recent versions of Android don't set CLONE_DETACHED anymore, so
+ // the last case accounts for that.
+ // The following policy is very strict. It only allows the exact masks
+ // that we have seen in known implementations. It is probably somewhat
+ // stricter than what we would want to do.
+ const uint64_t kGlibcCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS |
+ CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const uint64_t kBaseAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const Arg<unsigned long> flags(0);
+ return If(flags == kGlibcCloneMask ||
+ flags == (kBaseAndroidCloneMask | CLONE_DETACHED) ||
+ flags == kBaseAndroidCloneMask,
+ Allow()).Else(Trap(PthreadTrapHandler, "Unknown mask"));
+ }
+
+ return Allow();
+}
+
+class PthreadPolicyBitMask : public Policy {
+ public:
+ PthreadPolicyBitMask() {}
+ ~PthreadPolicyBitMask() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override;
+
+ private:
+ static BoolExpr HasAnyBits(const Arg<unsigned long>& arg, unsigned long bits);
+ static BoolExpr HasAllBits(const Arg<unsigned long>& arg, unsigned long bits);
+
+ DISALLOW_COPY_AND_ASSIGN(PthreadPolicyBitMask);
+};
+
+BoolExpr PthreadPolicyBitMask::HasAnyBits(const Arg<unsigned long>& arg,
+ unsigned long bits) {
+ return (arg & bits) != 0;
+}
+
+BoolExpr PthreadPolicyBitMask::HasAllBits(const Arg<unsigned long>& arg,
+ unsigned long bits) {
+ return (arg & bits) == bits;
+}
+
+ResultExpr PthreadPolicyBitMask::EvaluateSyscall(int sysno) const {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // This policy allows creating threads with pthread_create(). But it
+ // doesn't allow any other uses of clone(). Most notably, it does not
+ // allow callers to implement fork() or vfork() by passing suitable flags
+ // to the clone() system call.
+ if (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()).
+ const unsigned long kMandatoryFlags = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const unsigned long kFutexFlags =
+ CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const unsigned long kNoopFlags = CLONE_DETACHED;
+ const unsigned long kKnownFlags =
+ kMandatoryFlags | kFutexFlags | kNoopFlags;
+
+ const Arg<unsigned long> flags(0);
+ return If(HasAnyBits(flags, ~kKnownFlags),
+ Trap(PthreadTrapHandler, "Unexpected CLONE_XXX flag found"))
+ .ElseIf(!HasAllBits(flags, kMandatoryFlags),
+ Trap(PthreadTrapHandler,
+ "Missing mandatory CLONE_XXX flags "
+ "when creating new thread"))
+ .ElseIf(
+ !HasAllBits(flags, kFutexFlags) && HasAnyBits(flags, kFutexFlags),
+ Trap(PthreadTrapHandler,
+ "Must set either all or none of the TLS and futex bits in "
+ "call to clone()"))
+ .Else(Allow());
+ }
+
+ return Allow();
+}
+
+static void* ThreadFnc(void* arg) {
+ ++*reinterpret_cast<int*>(arg);
+ Syscall::Call(__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 (Syscall::Call(__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(Syscall::Call(__NR_clone,
+ CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID | SIGCHLD,
+ 0,
+ 0,
+ &pid) == -EPERM);
+}
+
+BPF_TEST_C(SandboxBPF, PthreadEquality, PthreadPolicyEquality) {
+ PthreadTest();
+}
+
+BPF_TEST_C(SandboxBPF, PthreadBitMask, PthreadPolicyBitMask) {
+ PthreadTest();
+}
+
+// libc might not define these even though the kernel supports it.
+#ifndef PTRACE_O_TRACESECCOMP
+#define PTRACE_O_TRACESECCOMP 0x00000080
+#endif
+
+#ifdef PTRACE_EVENT_SECCOMP
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP)
+#else
+// When Debian/Ubuntu backported seccomp-bpf support into earlier kernels, they
+// changed the value of PTRACE_EVENT_SECCOMP from 7 to 8, since 7 was taken by
+// PTRACE_EVENT_STOP (upstream chose to renumber PTRACE_EVENT_STOP to 128). If
+// PTRACE_EVENT_SECCOMP isn't defined, we have no choice but to consider both
+// values here.
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == 7 || (status >> 16) == 8)
+#endif
+
+#if defined(__arm__)
+#ifndef PTRACE_SET_SYSCALL
+#define PTRACE_SET_SYSCALL 23
+#endif
+#endif
+
+#if defined(__aarch64__)
+#ifndef PTRACE_GETREGS
+#define PTRACE_GETREGS 12
+#endif
+#endif
+
+#if defined(__aarch64__)
+#ifndef PTRACE_SETREGS
+#define PTRACE_SETREGS 13
+#endif
+#endif
+
+// Changes the syscall to run for a child being sandboxed using seccomp-bpf with
+// PTRACE_O_TRACESECCOMP. Should only be called when the child is stopped on
+// PTRACE_EVENT_SECCOMP.
+//
+// regs should contain the current set of registers of the child, obtained using
+// PTRACE_GETREGS.
+//
+// Depending on the architecture, this may modify regs, so the caller is
+// responsible for committing these changes using PTRACE_SETREGS.
+long SetSyscall(pid_t pid, regs_struct* regs, int syscall_number) {
+#if defined(__arm__)
+ // On ARM, the syscall is changed using PTRACE_SET_SYSCALL. We cannot use the
+ // libc ptrace call as the request parameter is an enum, and
+ // PTRACE_SET_SYSCALL may not be in the enum.
+ return syscall(__NR_ptrace, PTRACE_SET_SYSCALL, pid, NULL, syscall_number);
+#endif
+
+ SECCOMP_PT_SYSCALL(*regs) = syscall_number;
+ return 0;
+}
+
+const uint16_t kTraceData = 0xcc;
+
+class TraceAllPolicy : public Policy {
+ public:
+ TraceAllPolicy() {}
+ ~TraceAllPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int system_call_number) const override {
+ return Trace(kTraceData);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TraceAllPolicy);
+};
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) {
+ if (!SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
+ return;
+ }
+
+// This test is disabled on arm due to a kernel bug.
+// See https://code.google.com/p/chromium/issues/detail?id=383977
+#if defined(__arm__) || defined(__aarch64__)
+ printf("This test is currently disabled on ARM32/64 due to a kernel bug.");
+ return;
+#endif
+
+#if defined(__mips__)
+ // TODO: Figure out how to support specificity of handling indirect syscalls
+ // in this test and enable it.
+ printf("This test is currently disabled on MIPS.");
+ return;
+#endif
+
+ pid_t pid = fork();
+ BPF_ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ pid_t my_pid = getpid();
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_TRACEME, -1, NULL, NULL));
+ BPF_ASSERT_EQ(0, raise(SIGSTOP));
+ SandboxBPF sandbox(new TraceAllPolicy);
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
+
+ // getpid is allowed.
+ BPF_ASSERT_EQ(my_pid, sys_getpid());
+
+ // write to stdout is skipped and returns a fake value.
+ BPF_ASSERT_EQ(kExpectedReturnValue,
+ syscall(__NR_write, STDOUT_FILENO, "A", 1));
+
+ // kill is rewritten to exit(kExpectedReturnValue).
+ syscall(__NR_kill, my_pid, SIGKILL);
+
+ // Should not be reached.
+ BPF_ASSERT(false);
+ }
+
+ int status;
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, WUNTRACED)) != -1);
+ BPF_ASSERT(WIFSTOPPED(status));
+
+ BPF_ASSERT_NE(-1,
+ ptrace(PTRACE_SETOPTIONS,
+ pid,
+ NULL,
+ reinterpret_cast<void*>(PTRACE_O_TRACESECCOMP)));
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ while (true) {
+ BPF_ASSERT(HANDLE_EINTR(waitpid(pid, &status, 0)) != -1);
+ if (WIFEXITED(status) || WIFSIGNALED(status)) {
+ BPF_ASSERT(WIFEXITED(status));
+ BPF_ASSERT_EQ(kExpectedReturnValue, WEXITSTATUS(status));
+ break;
+ }
+
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
+ !IS_SECCOMP_EVENT(status)) {
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ continue;
+ }
+
+ unsigned long data;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data));
+ BPF_ASSERT_EQ(kTraceData, data);
+
+ regs_struct regs;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_GETREGS, pid, NULL, &regs));
+ switch (SECCOMP_PT_SYSCALL(regs)) {
+ case __NR_write:
+ // Skip writes to stdout, make it return kExpectedReturnValue. Allow
+ // writes to stderr so that BPF_ASSERT messages show up.
+ if (SECCOMP_PT_PARM1(regs) == STDOUT_FILENO) {
+ BPF_ASSERT_NE(-1, SetSyscall(pid, &regs, -1));
+ SECCOMP_PT_RESULT(regs) = kExpectedReturnValue;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, &regs));
+ }
+ break;
+
+ case __NR_kill:
+ // Rewrite to exit(kExpectedReturnValue).
+ BPF_ASSERT_NE(-1, SetSyscall(pid, &regs, __NR_exit));
+ SECCOMP_PT_PARM1(regs) = kExpectedReturnValue;
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_SETREGS, pid, NULL, &regs));
+ break;
+
+ default:
+ // Allow all other syscalls.
+ break;
+ }
+
+ BPF_ASSERT_NE(-1, ptrace(PTRACE_CONT, pid, NULL, NULL));
+ }
+}
+
+// Android does not expose pread64 nor pwrite64.
+#if !defined(OS_ANDROID)
+
+bool FullPwrite64(int fd, const char* buffer, size_t count, off64_t offset) {
+ while (count > 0) {
+ const ssize_t transfered =
+ HANDLE_EINTR(pwrite64(fd, buffer, count, offset));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ offset += transfered;
+ }
+ return true;
+}
+
+bool FullPread64(int fd, char* buffer, size_t count, off64_t offset) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(pread64(fd, buffer, count, offset));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ offset += transfered;
+ }
+ return true;
+}
+
+bool pread_64_was_forwarded = false;
+
+class TrapPread64Policy : public Policy {
+ public:
+ TrapPread64Policy() {}
+ ~TrapPread64Policy() override {}
+
+ ResultExpr EvaluateSyscall(int system_call_number) const override {
+ // Set the global environment for unsafe traps once.
+ if (system_call_number == MIN_SYSCALL) {
+ EnableUnsafeTraps();
+ }
+
+ if (system_call_number == __NR_pread64) {
+ return UnsafeTrap(ForwardPreadHandler, NULL);
+ }
+ return Allow();
+ }
+
+ private:
+ static intptr_t ForwardPreadHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ BPF_ASSERT(args.nr == __NR_pread64);
+ pread_64_was_forwarded = true;
+
+ return SandboxBPF::ForwardSyscall(args);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(TrapPread64Policy);
+};
+
+// pread(2) takes a 64 bits offset. On 32 bits systems, it will be split
+// between two arguments. In this test, we make sure that ForwardSyscall() can
+// forward it properly.
+BPF_TEST_C(SandboxBPF, Pread64, TrapPread64Policy) {
+ ScopedTemporaryFile temp_file;
+ const uint64_t kLargeOffset = (static_cast<uint64_t>(1) << 32) | 0xBEEF;
+ const char kTestString[] = "This is a test!";
+ BPF_ASSERT(FullPwrite64(
+ temp_file.fd(), kTestString, sizeof(kTestString), kLargeOffset));
+
+ char read_test_string[sizeof(kTestString)] = {0};
+ BPF_ASSERT(FullPread64(temp_file.fd(),
+ read_test_string,
+ sizeof(read_test_string),
+ kLargeOffset));
+ BPF_ASSERT_EQ(0, memcmp(kTestString, read_test_string, sizeof(kTestString)));
+ BPF_ASSERT(pread_64_was_forwarded);
+}
+
+#endif // !defined(OS_ANDROID)
+
+void* TsyncApplyToTwoThreadsFunc(void* cond_ptr) {
+ base::WaitableEvent* event = static_cast<base::WaitableEvent*>(cond_ptr);
+
+ // Wait for the main thread to signal that the filter has been applied.
+ if (!event->IsSignaled()) {
+ event->Wait();
+ }
+
+ BPF_ASSERT(event->IsSignaled());
+
+ BlacklistNanosleepPolicy::AssertNanosleepFails();
+
+ return NULL;
+}
+
+SANDBOX_TEST(SandboxBPF, Tsync) {
+ const bool supports_multi_threaded = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::MULTI_THREADED);
+// On Chrome OS tsync is mandatory.
+#if defined(OS_CHROMEOS)
+ if (base::SysInfo::IsRunningOnChromeOS()) {
+ BPF_ASSERT_EQ(true, supports_multi_threaded);
+ }
+// else a Chrome OS build not running on a Chrome OS device e.g. Chrome bots.
+// In this case fall through.
+#endif
+ if (!supports_multi_threaded) {
+ return;
+ }
+
+ base::WaitableEvent event(true, false);
+
+ // Create a thread on which to invoke the blocked syscall.
+ pthread_t thread;
+ BPF_ASSERT_EQ(
+ 0, pthread_create(&thread, NULL, &TsyncApplyToTwoThreadsFunc, &event));
+
+ // Test that nanoseelp success.
+ const struct timespec ts = {0, 0};
+ BPF_ASSERT_EQ(0, HANDLE_EINTR(syscall(__NR_nanosleep, &ts, NULL)));
+
+ // Engage the sandbox.
+ SandboxBPF sandbox(new BlacklistNanosleepPolicy());
+ BPF_ASSERT(sandbox.StartSandbox(SandboxBPF::SeccompLevel::MULTI_THREADED));
+
+ // This thread should have the filter applied as well.
+ BlacklistNanosleepPolicy::AssertNanosleepFails();
+
+ // Signal the condition to invoke the system call.
+ event.Signal();
+
+ // Wait for the thread to finish.
+ BPF_ASSERT_EQ(0, pthread_join(thread, NULL));
+}
+
+class AllowAllPolicy : public Policy {
+ public:
+ AllowAllPolicy() {}
+ ~AllowAllPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override { return Allow(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
+};
+
+SANDBOX_DEATH_TEST(
+ SandboxBPF,
+ StartMultiThreadedAsSingleThreaded,
+ DEATH_MESSAGE(
+ ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests())) {
+ base::Thread thread("sandbox.linux.StartMultiThreadedAsSingleThreaded");
+ BPF_ASSERT(thread.Start());
+
+ SandboxBPF sandbox(new AllowAllPolicy());
+ BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::SeccompLevel::SINGLE_THREADED));
+}
+
+// http://crbug.com/407357
+#if !defined(THREAD_SANITIZER)
+SANDBOX_DEATH_TEST(
+ SandboxBPF,
+ StartSingleThreadedAsMultiThreaded,
+ DEATH_MESSAGE(
+ "Cannot start sandbox; process may be single-threaded when "
+ "reported as not")) {
+ SandboxBPF sandbox(new AllowAllPolicy());
+ BPF_ASSERT(!sandbox.StartSandbox(SandboxBPF::SeccompLevel::MULTI_THREADED));
+}
+#endif // !defined(THREAD_SANITIZER)
+
+// A stub handler for the UnsafeTrap. Never called.
+intptr_t NoOpHandler(const struct arch_seccomp_data& args, void*) {
+ return -1;
+}
+
+class UnsafeTrapWithCondPolicy : public Policy {
+ public:
+ UnsafeTrapWithCondPolicy() {}
+ ~UnsafeTrapWithCondPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ setenv(kSandboxDebuggingEnv, "t", 0);
+ Die::SuppressInfoMessages(true);
+
+ if (SandboxBPF::IsRequiredForUnsafeTrap(sysno))
+ return Allow();
+
+ switch (sysno) {
+ case __NR_uname: {
+ const Arg<uint32_t> arg(0);
+ return If(arg == 0, Allow()).Else(Error(EPERM));
+ }
+ case __NR_setgid: {
+ const Arg<uint32_t> arg(0);
+ return Switch(arg)
+ .Case(100, Error(ENOMEM))
+ .Case(200, Error(ENOSYS))
+ .Default(Error(EPERM));
+ }
+ case __NR_close:
+ case __NR_exit_group:
+ case __NR_write:
+ return Allow();
+ case __NR_getppid:
+ return UnsafeTrap(NoOpHandler, NULL);
+ default:
+ return Error(EPERM);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(UnsafeTrapWithCondPolicy);
+};
+
+BPF_TEST_C(SandboxBPF, UnsafeTrapWithCond, UnsafeTrapWithCondPolicy) {
+ BPF_ASSERT_EQ(-1, syscall(__NR_uname, 0));
+ BPF_ASSERT_EQ(EFAULT, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_uname, 1));
+ BPF_ASSERT_EQ(EPERM, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_setgid, 100));
+ BPF_ASSERT_EQ(ENOMEM, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_setgid, 200));
+ BPF_ASSERT_EQ(ENOSYS, errno);
+
+ BPF_ASSERT_EQ(-1, syscall(__NR_setgid, 300));
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+} // namespace
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/sandbox/linux/integration_tests/namespace_unix_domain_socket_unittest.cc b/sandbox/linux/integration_tests/namespace_unix_domain_socket_unittest.cc
new file mode 100644
index 0000000000..9d79bff1c6
--- /dev/null
+++ b/sandbox/linux/integration_tests/namespace_unix_domain_socket_unittest.cc
@@ -0,0 +1,267 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/process/process.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+// Additional tests for base's UnixDomainSocket to make sure it behaves
+// correctly in the presence of sandboxing functionality (e.g., receiving
+// PIDs across namespaces).
+
+namespace sandbox {
+
+namespace {
+
+const char kHello[] = "hello";
+
+// If the calling process isn't root, then try using unshare(CLONE_NEWUSER)
+// to fake it.
+void FakeRoot() {
+ // If we're already root, then allow test to proceed.
+ if (geteuid() == 0)
+ return;
+
+ // Otherwise hope the kernel supports unprivileged namespaces.
+ if (unshare(CLONE_NEWUSER) == 0)
+ return;
+
+ printf("Permission to use CLONE_NEWPID missing; skipping test.\n");
+ UnitTests::IgnoreThisTest();
+}
+
+void WaitForExit(pid_t pid) {
+ int status;
+ CHECK_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(0, WEXITSTATUS(status));
+}
+
+base::ProcessId GetParentProcessId(base::ProcessId pid) {
+ // base::GetParentProcessId() is defined as taking a ProcessHandle instead of
+ // a ProcessId, even though it's a POSIX-only function and IDs and Handles
+ // are both simply pid_t on POSIX... :/
+ base::Process process = base::Process::Open(pid);
+ CHECK(process.IsValid());
+ base::ProcessId ret = base::GetParentProcessId(process.Handle());
+ return ret;
+}
+
+// SendHello sends a "hello" to socket fd, and then blocks until the recipient
+// acknowledges it by calling RecvHello.
+void SendHello(int fd) {
+ int pipe_fds[2];
+ CHECK_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_pipe(pipe_fds[0]);
+ base::ScopedFD write_pipe(pipe_fds[1]);
+
+ std::vector<int> send_fds;
+ send_fds.push_back(write_pipe.get());
+ CHECK(base::UnixDomainSocket::SendMsg(fd, kHello, sizeof(kHello), send_fds));
+
+ write_pipe.reset();
+
+ // Block until receiver closes their end of the pipe.
+ char ch;
+ CHECK_EQ(0, HANDLE_EINTR(read(read_pipe.get(), &ch, 1)));
+}
+
+// RecvHello receives and acknowledges a "hello" on socket fd, and returns the
+// process ID of the sender in sender_pid. Optionally, write_pipe can be used
+// to return a file descriptor, and the acknowledgement will be delayed until
+// the descriptor is closed.
+// (Implementation details: SendHello allocates a new pipe, sends us the writing
+// end alongside the "hello" message, and then blocks until we close the writing
+// end of the pipe.)
+void RecvHello(int fd,
+ base::ProcessId* sender_pid,
+ base::ScopedFD* write_pipe = NULL) {
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ ScopedVector<base::ScopedFD> message_fds;
+ ssize_t n = base::UnixDomainSocket::RecvMsgWithPid(
+ fd, buf, sizeof(buf), &message_fds, sender_pid);
+ CHECK_EQ(sizeof(kHello), static_cast<size_t>(n));
+ CHECK_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ CHECK_EQ(1U, message_fds.size());
+ if (write_pipe)
+ write_pipe->swap(*message_fds[0]);
+}
+
+// Check that receiving PIDs works across a fork().
+SANDBOX_TEST(UnixDomainSocketTest, Fork) {
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(base::UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = fork();
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Similar to Fork above, but forking the child into a new pid namespace.
+SANDBOX_TEST(UnixDomainSocketTest, Namespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(base::UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ // Check that we think we're pid 1 in our new namespace.
+ CHECK_EQ(1, sys_getpid());
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(pid, sender_pid);
+
+ WaitForExit(pid);
+}
+
+// Again similar to Fork, but now with nested PID namespaces.
+SANDBOX_TEST(UnixDomainSocketTest, DoubleNamespace) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ CHECK(base::UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ recv_sock.reset();
+
+ const pid_t pid2 = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
+ CHECK_NE(-1, pid2);
+
+ if (pid2 != 0) {
+ // Wait for grandchild to run to completion; see comments below.
+ WaitForExit(pid2);
+
+ // Fallthrough once grandchild has sent its hello and exited.
+ }
+
+ // Check that we think we're pid 1.
+ CHECK_EQ(1, sys_getpid());
+
+ SendHello(send_sock.get());
+ _exit(0);
+ }
+
+ // Parent process.
+ send_sock.reset();
+
+ // We have two messages to receive: first from the grand-child,
+ // then from the child.
+ for (unsigned iteration = 0; iteration < 2; ++iteration) {
+ base::ProcessId sender_pid;
+ base::ScopedFD pipe_fd;
+ RecvHello(recv_sock.get(), &sender_pid, &pipe_fd);
+
+ // We need our child and grandchild processes to both be alive for
+ // GetParentProcessId() to return a valid pid, hence the pipe trickery.
+ // (On the first iteration, grandchild is blocked reading from the pipe
+ // until we close it, and child is blocked waiting for grandchild to exit.)
+ switch (iteration) {
+ case 0: // Grandchild's message
+ // Check that sender_pid refers to our grandchild by checking that pid
+ // (our child) is its parent.
+ CHECK_EQ(pid, GetParentProcessId(sender_pid));
+ break;
+ case 1: // Child's message
+ CHECK_EQ(pid, sender_pid);
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+
+ WaitForExit(pid);
+}
+
+// Tests that GetPeerPid() returns 0 if the peer does not exist in caller's
+// namespace.
+SANDBOX_TEST(UnixDomainSocketTest, ImpossiblePid) {
+ FakeRoot();
+
+ int fds[2];
+ CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD send_sock(fds[0]);
+ base::ScopedFD recv_sock(fds[1]);
+
+ CHECK(base::UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ const pid_t pid = sys_clone(CLONE_NEWPID | SIGCHLD, 0, 0, 0, 0);
+ CHECK_NE(-1, pid);
+ if (pid == 0) {
+ // Child process.
+ send_sock.reset();
+
+ base::ProcessId sender_pid;
+ RecvHello(recv_sock.get(), &sender_pid);
+ CHECK_EQ(0, sender_pid);
+ _exit(0);
+ }
+
+ // Parent process.
+ recv_sock.reset();
+ SendHello(send_sock.get());
+ WaitForExit(pid);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
new file mode 100644
index 0000000000..9aa320997b
--- /dev/null
+++ b/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright 2015 The Chromium 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/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+#include "sandbox/linux/syscall_broker/broker_process.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+using bpf_dsl::Allow;
+using bpf_dsl::ResultExpr;
+using bpf_dsl::Trap;
+
+bool NoOpCallback() {
+ return true;
+}
+
+// Test a trap handler that makes use of a broker process to open().
+
+class InitializedOpenBroker {
+ public:
+ InitializedOpenBroker() : initialized_(false) {
+ std::vector<syscall_broker::BrokerFilePermission> permissions;
+ permissions.push_back(
+ syscall_broker::BrokerFilePermission::ReadOnly("/proc/allowed"));
+ permissions.push_back(
+ syscall_broker::BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+
+ broker_process_.reset(
+ new syscall_broker::BrokerProcess(EPERM, permissions));
+ BPF_ASSERT(broker_process() != NULL);
+ BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback)));
+
+ initialized_ = true;
+ }
+ bool initialized() { return initialized_; }
+ class syscall_broker::BrokerProcess* broker_process() {
+ return broker_process_.get();
+ }
+
+ private:
+ bool initialized_;
+ scoped_ptr<class syscall_broker::BrokerProcess> broker_process_;
+ DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
+};
+
+intptr_t BrokerOpenTrapHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ BPF_ASSERT(aux);
+ syscall_broker::BrokerProcess* broker_process =
+ static_cast<syscall_broker::BrokerProcess*>(aux);
+ switch (args.nr) {
+ case __NR_faccessat: // access is a wrapper of faccessat in android
+ BPF_ASSERT(static_cast<int>(args.args[0]) == AT_FDCWD);
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#if defined(__NR_access)
+ case __NR_access:
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+#if defined(__NR_open)
+ case __NR_open:
+ return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+ 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;
+ }
+}
+
+class DenyOpenPolicy : public bpf_dsl::Policy {
+ public:
+ explicit DenyOpenPolicy(InitializedOpenBroker* iob) : iob_(iob) {}
+ ~DenyOpenPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+
+ switch (sysno) {
+ case __NR_faccessat:
+#if defined(__NR_access)
+ case __NR_access:
+#endif
+#if defined(__NR_open)
+ case __NR_open:
+#endif
+ case __NR_openat:
+ // We get a InitializedOpenBroker class, but our trap handler wants
+ // the syscall_broker::BrokerProcess object.
+ return Trap(BrokerOpenTrapHandler, iob_->broker_process());
+ default:
+ return Allow();
+ }
+ }
+
+ private:
+ InitializedOpenBroker* iob_;
+
+ DISALLOW_COPY_AND_ASSIGN(DenyOpenPolicy);
+};
+
+// 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());
+ syscall_broker::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);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi
new file mode 100644
index 0000000000..a7bd259d8a
--- /dev/null
+++ b/sandbox/linux/sandbox_linux.gypi
@@ -0,0 +1,416 @@
+# 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_credentials': 1,
+ 'use_base_test_suite': 1,
+ }, {
+ 'compile_suid_client': 0,
+ 'compile_credentials': 0,
+ 'use_base_test_suite': 0,
+ }],
+ ['OS=="linux" and (target_arch=="ia32" or target_arch=="x64" or '
+ 'target_arch=="mipsel")', {
+ 'compile_seccomp_bpf_demo': 1,
+ }, {
+ 'compile_seccomp_bpf_demo': 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.
+ # There is one notable exception: for historical reasons, chrome_sandbox 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.
+ [ 'use_seccomp_bpf==1', {
+ 'dependencies': [
+ 'seccomp_bpf',
+ 'seccomp_bpf_helpers',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sandbox_linux_test_utils',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'tests/sandbox_test_runner.cc',
+ 'tests/sandbox_test_runner.h',
+ 'tests/sandbox_test_runner_function_pointer.cc',
+ 'tests/sandbox_test_runner_function_pointer.h',
+ 'tests/test_utils.cc',
+ 'tests/test_utils.h',
+ 'tests/unit_tests.cc',
+ 'tests/unit_tests.h',
+ ],
+ 'conditions': [
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'seccomp-bpf/bpf_tester_compatibility_delegate.h',
+ 'seccomp-bpf/bpf_tests.h',
+ 'seccomp-bpf/sandbox_bpf_test_runner.cc',
+ 'seccomp-bpf/sandbox_bpf_test_runner.h',
+ ],
+ 'dependencies': [
+ 'seccomp_bpf',
+ ]
+ }],
+ [ 'use_base_test_suite==1', {
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ ],
+ 'defines': [
+ 'SANDBOX_USES_BASE_TEST_SUITE',
+ ],
+ }],
+ ],
+ },
+ {
+ # 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"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'seccomp_bpf',
+ 'type': '<(component)',
+ 'sources': [
+ 'bpf_dsl/bpf_dsl.cc',
+ 'bpf_dsl/bpf_dsl.h',
+ 'bpf_dsl/bpf_dsl_forward.h',
+ 'bpf_dsl/bpf_dsl_impl.h',
+ 'bpf_dsl/codegen.cc',
+ 'bpf_dsl/codegen.h',
+ 'bpf_dsl/cons.h',
+ 'bpf_dsl/dump_bpf.cc',
+ 'bpf_dsl/dump_bpf.h',
+ 'bpf_dsl/linux_syscall_ranges.h',
+ 'bpf_dsl/policy.cc',
+ 'bpf_dsl/policy.h',
+ 'bpf_dsl/policy_compiler.cc',
+ 'bpf_dsl/policy_compiler.h',
+ 'bpf_dsl/seccomp_macros.h',
+ 'bpf_dsl/seccomp_macros.h',
+ 'bpf_dsl/syscall_set.cc',
+ 'bpf_dsl/syscall_set.h',
+ 'bpf_dsl/trap_registry.h',
+ 'bpf_dsl/verifier.cc',
+ 'bpf_dsl/verifier.h',
+ 'seccomp-bpf/die.cc',
+ 'seccomp-bpf/die.h',
+ 'seccomp-bpf/errorcode.cc',
+ 'seccomp-bpf/errorcode.h',
+ 'seccomp-bpf/sandbox_bpf.cc',
+ 'seccomp-bpf/sandbox_bpf.h',
+ 'seccomp-bpf/syscall.cc',
+ 'seccomp-bpf/syscall.h',
+ 'seccomp-bpf/trap.cc',
+ 'seccomp-bpf/trap.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ 'sandbox_services_headers',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'includes': [
+ # Disable LTO due to compiler bug
+ # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57703
+ '../../build/android/disable_lto.gypi',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ {
+ 'target_name': 'seccomp_bpf_helpers',
+ 'type': '<(component)',
+ 'sources': [
+ 'seccomp-bpf-helpers/baseline_policy.cc',
+ 'seccomp-bpf-helpers/baseline_policy.h',
+ 'seccomp-bpf-helpers/sigsys_handlers.cc',
+ 'seccomp-bpf-helpers/sigsys_handlers.h',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.h',
+ 'seccomp-bpf-helpers/syscall_sets.cc',
+ 'seccomp-bpf-helpers/syscall_sets.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ 'seccomp_bpf',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ '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/process_util.h',
+ 'suid/process_util_linux.c',
+ 'suid/sandbox.c',
+ ],
+ 'cflags': [
+ # For ULLONG_MAX
+ '-std=gnu99',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ # Do not use any sanitizer tools with this binary. http://crbug.com/382766
+ 'cflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ 'ldflags/': [
+ ['exclude', '-fsanitize'],
+ ],
+ },
+ { 'target_name': 'sandbox_services',
+ 'type': '<(component)',
+ 'sources': [
+ 'services/init_process_reaper.cc',
+ 'services/init_process_reaper.h',
+ 'services/proc_util.cc',
+ 'services/proc_util.h',
+ 'services/resource_limits.cc',
+ 'services/resource_limits.h',
+ 'services/scoped_process.cc',
+ 'services/scoped_process.h',
+ 'services/syscall_wrappers.cc',
+ 'services/syscall_wrappers.h',
+ 'services/thread_helpers.cc',
+ 'services/thread_helpers.h',
+ 'services/yama.cc',
+ 'services/yama.h',
+ 'syscall_broker/broker_channel.cc',
+ 'syscall_broker/broker_channel.h',
+ 'syscall_broker/broker_client.cc',
+ 'syscall_broker/broker_client.h',
+ 'syscall_broker/broker_common.h',
+ 'syscall_broker/broker_file_permission.cc',
+ 'syscall_broker/broker_file_permission.h',
+ 'syscall_broker/broker_host.cc',
+ 'syscall_broker/broker_host.h',
+ 'syscall_broker/broker_policy.cc',
+ 'syscall_broker/broker_policy.h',
+ 'syscall_broker/broker_process.cc',
+ 'syscall_broker/broker_process.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'conditions': [
+ ['compile_credentials==1', {
+ 'sources': [
+ 'services/credentials.cc',
+ 'services/credentials.h',
+ 'services/namespace_sandbox.cc',
+ 'services/namespace_sandbox.h',
+ 'services/namespace_utils.cc',
+ 'services/namespace_utils.h',
+ ],
+ 'dependencies': [
+ # for capability.h.
+ 'sandbox_services_headers',
+ ],
+ }],
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ { 'target_name': 'sandbox_services_headers',
+ 'type': 'none',
+ 'sources': [
+ 'system_headers/arm64_linux_syscalls.h',
+ 'system_headers/arm64_linux_ucontext.h',
+ 'system_headers/arm_linux_syscalls.h',
+ 'system_headers/arm_linux_ucontext.h',
+ 'system_headers/capability.h',
+ 'system_headers/i386_linux_ucontext.h',
+ 'system_headers/linux_futex.h',
+ 'system_headers/linux_seccomp.h',
+ 'system_headers/linux_syscalls.h',
+ 'system_headers/linux_time.h',
+ 'system_headers/linux_ucontext.h',
+ 'system_headers/mips_linux_syscalls.h',
+ 'system_headers/mips_linux_ucontext.h',
+ 'system_headers/x86_32_linux_syscalls.h',
+ 'system_headers/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': '<(component)',
+ 'sources': [
+ 'suid/common/sandbox.h',
+ 'suid/common/suid_unsafe_environment_variables.h',
+ 'suid/client/setuid_sandbox_client.cc',
+ 'suid/client/setuid_sandbox_client.h',
+ 'suid/client/setuid_sandbox_host.cc',
+ 'suid/client/setuid_sandbox_host.h',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'sandbox_services',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ ],
+ 'conditions': [
+ [ 'OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_stripped',
+ 'type': 'none',
+ 'dependencies': [ 'sandbox_linux_unittests' ],
+ 'actions': [{
+ 'action_name': 'strip sandbox_linux_unittests',
+ 'inputs': [ '<(PRODUCT_DIR)/sandbox_linux_unittests' ],
+ 'outputs': [ '<(PRODUCT_DIR)/sandbox_linux_unittests_stripped' ],
+ 'action': [ '<(android_strip)', '<@(_inputs)', '-o', '<@(_outputs)' ],
+ }],
+ },
+ {
+ 'target_name': 'sandbox_linux_unittests_deps',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests_stripped',
+ ],
+ # For the component build, ensure dependent shared libraries are
+ # stripped and put alongside sandbox_linux_unittests to simplify pushing
+ # to the device.
+ 'variables': {
+ 'output_dir': '<(PRODUCT_DIR)/sandbox_linux_unittests_deps/',
+ 'native_binary': '<(PRODUCT_DIR)/sandbox_linux_unittests_stripped',
+ 'include_main_binary': 0,
+ },
+ 'includes': [
+ '../../build/android/native_app_dependencies.gypi'
+ ],
+ }],
+ }],
+ [ 'OS=="android"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_jni_unittests_apk',
+ 'type': 'none',
+ 'variables': {
+ 'test_suite_name': 'sandbox_linux_jni_unittests',
+ },
+ 'dependencies': [
+ 'sandbox_linux_jni_unittests',
+ ],
+ 'includes': [ '../../build/apk_test.gypi' ],
+ }
+ ],
+ }],
+ ['test_isolation_mode != "noop"', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_unittests_run',
+ 'type': 'none',
+ 'dependencies': [
+ 'sandbox_linux_unittests',
+ ],
+ 'includes': [
+ '../../build/isolate.gypi',
+ ],
+ 'sources': [
+ '../sandbox_linux_unittests.isolate',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/sandbox_linux_nacl_nonsfi.gyp b/sandbox/linux/sandbox_linux_nacl_nonsfi.gyp
new file mode 100644
index 0000000000..87ad06ccdc
--- /dev/null
+++ b/sandbox/linux/sandbox_linux_nacl_nonsfi.gyp
@@ -0,0 +1,88 @@
+# Copyright 2015 The Chromium 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,
+ },
+ 'includes': [
+ '../../build/common_untrusted.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_nacl_nonsfi',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libsandbox_linux_nacl_nonsfi.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
+
+ 'sources': [
+ # This is the subset of linux build target, needed for
+ # nacl_helper_nonsfi's sandbox implementation.
+ 'bpf_dsl/bpf_dsl.cc',
+ 'bpf_dsl/codegen.cc',
+ 'bpf_dsl/dump_bpf.cc',
+ 'bpf_dsl/policy.cc',
+ 'bpf_dsl/policy_compiler.cc',
+ 'bpf_dsl/syscall_set.cc',
+ 'bpf_dsl/verifier.cc',
+ 'seccomp-bpf-helpers/sigsys_handlers.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions.cc',
+ 'seccomp-bpf/die.cc',
+ 'seccomp-bpf/errorcode.cc',
+ 'seccomp-bpf/sandbox_bpf.cc',
+ 'seccomp-bpf/syscall.cc',
+ 'seccomp-bpf/trap.cc',
+ 'services/credentials.cc',
+ 'services/namespace_sandbox.cc',
+ 'services/namespace_utils.cc',
+ 'services/proc_util.cc',
+ 'services/resource_limits.cc',
+ 'services/syscall_wrappers.cc',
+ 'services/thread_helpers.cc',
+ 'suid/client/setuid_sandbox_client.cc',
+ ],
+ },
+ 'dependencies': [
+ '../../base/base_nacl.gyp:base_nacl_nonsfi',
+ ],
+ },
+ ],
+ }],
+
+ ['disable_nacl==0 and disable_nacl_untrusted==0 and enable_nacl_nonsfi_test==1', {
+ 'targets': [
+ {
+ 'target_name': 'sandbox_linux_test_utils_nacl_nonsfi',
+ 'type': 'none',
+ 'variables': {
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libsandbox_linux_test_utils_nacl_nonsfi.a',
+ 'build_glibc': 0,
+ 'build_newlib': 0,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 0,
+ 'build_nonsfi_helper': 1,
+
+ 'sources': [
+ 'seccomp-bpf/sandbox_bpf_test_runner.cc',
+ 'tests/sandbox_test_runner.cc',
+ 'tests/unit_tests.cc',
+ ],
+ },
+ 'dependencies': [
+ '../../testing/gtest_nacl.gyp:gtest_nacl',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/sandbox_linux_test_sources.gypi b/sandbox/linux/sandbox_linux_test_sources.gypi
new file mode 100644
index 0000000000..82d7532056
--- /dev/null
+++ b/sandbox/linux/sandbox_linux_test_sources.gypi
@@ -0,0 +1,84 @@
+# 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',
+ 'sandbox_linux_test_utils',
+ 'sandbox_services',
+ '../base/base.gyp:base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'sources': [
+ 'services/proc_util_unittest.cc',
+ 'services/scoped_process_unittest.cc',
+ 'services/resource_limits_unittests.cc',
+ 'services/syscall_wrappers_unittest.cc',
+ 'services/thread_helpers_unittests.cc',
+ 'services/yama_unittests.cc',
+ 'syscall_broker/broker_file_permission_unittest.cc',
+ 'syscall_broker/broker_process_unittest.cc',
+ 'tests/main.cc',
+ 'tests/scoped_temporary_file.cc',
+ 'tests/scoped_temporary_file.h',
+ 'tests/scoped_temporary_file_unittest.cc',
+ 'tests/test_utils_unittest.cc',
+ 'tests/unit_tests_unittest.cc',
+ ],
+ 'conditions': [
+ [ 'compile_suid_client==1', {
+ 'sources': [
+ 'suid/client/setuid_sandbox_client_unittest.cc',
+ 'suid/client/setuid_sandbox_host_unittest.cc',
+ ],
+ }],
+ [ 'use_seccomp_bpf==1', {
+ 'sources': [
+ 'bpf_dsl/bpf_dsl_unittest.cc',
+ 'bpf_dsl/codegen_unittest.cc',
+ 'bpf_dsl/cons_unittest.cc',
+ 'bpf_dsl/syscall_set_unittest.cc',
+ 'integration_tests/bpf_dsl_seccomp_unittest.cc',
+ 'integration_tests/seccomp_broker_process_unittest.cc',
+ 'seccomp-bpf-helpers/baseline_policy_unittest.cc',
+ 'seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc',
+ 'seccomp-bpf/bpf_tests_unittest.cc',
+ 'seccomp-bpf/errorcode_unittest.cc',
+ 'seccomp-bpf/sandbox_bpf_unittest.cc',
+ 'seccomp-bpf/syscall_unittest.cc',
+ 'seccomp-bpf/trap_unittest.cc',
+ ],
+ }],
+ [ 'compile_credentials==1', {
+ 'sources': [
+ 'integration_tests/namespace_unix_domain_socket_unittest.cc',
+ 'services/credentials_unittest.cc',
+ 'services/namespace_utils_unittest.cc',
+ ],
+ 'dependencies': [
+ '../build/linux/system.gyp:libcap'
+ ],
+ 'conditions': [
+ [ 'use_base_test_suite==1', {
+ 'sources': [
+ 'services/namespace_sandbox_unittest.cc',
+ ]
+ }]
+ ],
+ }],
+ [ 'use_base_test_suite==1', {
+ 'dependencies': [
+ '../base/base.gyp:test_support_base',
+ ],
+ 'defines': [
+ 'SANDBOX_USES_BASE_TEST_SUITE',
+ ],
+ }],
+ ],
+}
diff --git a/sandbox/linux/seccomp-bpf-helpers/DEPS b/sandbox/linux/seccomp-bpf-helpers/DEPS
new file mode 100644
index 0000000000..4419fd1da3
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/DEPS
@@ -0,0 +1,7 @@
+include_rules = [
+ "+sandbox/linux/bpf_dsl",
+ "+sandbox/linux/seccomp-bpf",
+ "+sandbox/linux/services",
+ "+sandbox/linux/system_headers",
+ "+third_party/lss/linux_syscall_support.h",
+]
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
new file mode 100644
index 0000000000..8c679a3d41
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+// Changing this implementation will have an effect on *all* policies.
+// Currently this means: Renderer/Worker, GPU, Flash and NaCl.
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Arg;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::If;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+namespace {
+
+bool IsBaselinePolicyAllowed(int sysno) {
+ return SyscallSets::IsAllowedAddressSpaceAccess(sysno) ||
+ SyscallSets::IsAllowedBasicScheduler(sysno) ||
+ SyscallSets::IsAllowedEpoll(sysno) ||
+ SyscallSets::IsAllowedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsAllowedFutex(sysno) ||
+ SyscallSets::IsAllowedGeneralIo(sysno) ||
+ SyscallSets::IsAllowedGetOrModifySocket(sysno) ||
+ SyscallSets::IsAllowedGettime(sysno) ||
+ SyscallSets::IsAllowedProcessStartOrDeath(sysno) ||
+ SyscallSets::IsAllowedSignalHandling(sysno) ||
+ SyscallSets::IsGetSimpleId(sysno) ||
+ SyscallSets::IsKernelInternalApi(sysno) ||
+#if defined(__arm__)
+ SyscallSets::IsArmPrivate(sysno) ||
+#endif
+#if defined(__mips__)
+ SyscallSets::IsMipsPrivate(sysno) ||
+#endif
+ SyscallSets::IsAllowedOperationOnFd(sysno);
+}
+
+// System calls that will trigger the crashing SIGSYS handler.
+bool IsBaselinePolicyWatched(int sysno) {
+ return SyscallSets::IsAdminOperation(sysno) ||
+ SyscallSets::IsAdvancedScheduler(sysno) ||
+ SyscallSets::IsAdvancedTimer(sysno) ||
+ SyscallSets::IsAsyncIo(sysno) ||
+ SyscallSets::IsDebug(sysno) ||
+ SyscallSets::IsEventFd(sysno) ||
+ SyscallSets::IsExtendedAttributes(sysno) ||
+ SyscallSets::IsFaNotify(sysno) ||
+ SyscallSets::IsFsControl(sysno) ||
+ SyscallSets::IsGlobalFSViewChange(sysno) ||
+ SyscallSets::IsGlobalProcessEnvironment(sysno) ||
+ SyscallSets::IsGlobalSystemStatus(sysno) ||
+ SyscallSets::IsInotify(sysno) ||
+ SyscallSets::IsKernelModule(sysno) ||
+ SyscallSets::IsKeyManagement(sysno) ||
+ SyscallSets::IsKill(sysno) ||
+ SyscallSets::IsMessageQueue(sysno) ||
+ SyscallSets::IsMisc(sysno) ||
+#if defined(__x86_64__)
+ SyscallSets::IsNetworkSocketInformation(sysno) ||
+#endif
+ SyscallSets::IsNuma(sysno) ||
+ SyscallSets::IsPrctl(sysno) ||
+ SyscallSets::IsProcessGroupOrSession(sysno) ||
+#if defined(__i386__) || defined(__mips__)
+ SyscallSets::IsSocketCall(sysno) ||
+#endif
+#if defined(__arm__)
+ SyscallSets::IsArmPciConfig(sysno) ||
+#endif
+#if defined(__mips__)
+ SyscallSets::IsMipsMisc(sysno) ||
+#endif
+ SyscallSets::IsTimer(sysno);
+}
+
+// |fs_denied_errno| is the errno return for denied filesystem access.
+ResultExpr EvaluateSyscallImpl(int fs_denied_errno,
+ pid_t current_pid,
+ int sysno) {
+#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
+ defined(MEMORY_SANITIZER)
+ // TCGETS is required by the sanitizers on failure.
+ if (sysno == __NR_ioctl) {
+ return RestrictIoctl();
+ }
+
+ if (sysno == __NR_sched_getaffinity) {
+ return Allow();
+ }
+
+ // Used when RSS limiting is enabled in sanitizers.
+ if (sysno == __NR_getrusage) {
+ return RestrictGetrusage();
+ }
+
+ if (sysno == __NR_sigaltstack) {
+ // Required for better stack overflow detection in ASan. Disallowed in
+ // non-ASan builds.
+ return Allow();
+ }
+#endif // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
+ // defined(MEMORY_SANITIZER)
+
+ if (IsBaselinePolicyAllowed(sysno)) {
+ return Allow();
+ }
+
+#if defined(OS_ANDROID)
+ // Needed for thread creation.
+ if (sysno == __NR_sigaltstack)
+ return Allow();
+#endif
+
+ if (sysno == __NR_clock_gettime) {
+ return RestrictClockID();
+ }
+
+ if (sysno == __NR_clone) {
+ return RestrictCloneToThreadsAndEPERMFork();
+ }
+
+ if (sysno == __NR_fcntl)
+ return RestrictFcntlCommands();
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ if (sysno == __NR_fcntl64)
+ return RestrictFcntlCommands();
+#endif
+
+#if !defined(__aarch64__)
+ // fork() is never used as a system call (clone() is used instead), but we
+ // have seen it in fallback code on Android.
+ if (sysno == __NR_fork) {
+ return Error(EPERM);
+ }
+#endif
+
+ if (sysno == __NR_futex)
+ return RestrictFutex();
+
+ if (sysno == __NR_set_robust_list)
+ return Error(EPERM);
+
+ if (sysno == __NR_getpriority || sysno ==__NR_setpriority)
+ return RestrictGetSetpriority(current_pid);
+
+ if (sysno == __NR_madvise) {
+ // Only allow MADV_DONTNEED (aka MADV_FREE).
+ const Arg<int> advice(2);
+ return If(advice == MADV_DONTNEED, Allow()).Else(Error(EPERM));
+ }
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ if (sysno == __NR_mmap)
+ return RestrictMmapFlags();
+#endif
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ if (sysno == __NR_mmap2)
+ return RestrictMmapFlags();
+#endif
+
+ if (sysno == __NR_mprotect)
+ return RestrictMprotectFlags();
+
+ if (sysno == __NR_prctl)
+ return RestrictPrctl();
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ if (sysno == __NR_socketpair) {
+ // Only allow AF_UNIX, PF_UNIX. Crash if anything else is seen.
+ static_assert(AF_UNIX == PF_UNIX,
+ "af_unix and pf_unix should not be different");
+ const Arg<int> domain(0);
+ return If(domain == AF_UNIX, Allow()).Else(CrashSIGSYS());
+ }
+#endif
+
+ if (SyscallSets::IsKill(sysno)) {
+ return RestrictKillTarget(current_pid, sysno);
+ }
+
+ if (SyscallSets::IsFileSystem(sysno) ||
+ SyscallSets::IsCurrentDirectory(sysno)) {
+ return Error(fs_denied_errno);
+ }
+
+ if (SyscallSets::IsSeccomp(sysno))
+ return Error(EPERM);
+
+ if (SyscallSets::IsAnySystemV(sysno)) {
+ return Error(EPERM);
+ }
+
+ if (SyscallSets::IsUmask(sysno) ||
+ SyscallSets::IsDeniedFileSystemAccessViaFd(sysno) ||
+ SyscallSets::IsDeniedGetOrModifySocket(sysno) ||
+ SyscallSets::IsProcessPrivilegeChange(sysno)) {
+ return Error(EPERM);
+ }
+
+#if defined(__i386__) || defined(__mips__)
+ if (SyscallSets::IsSocketCall(sysno))
+ return RestrictSocketcallCommand();
+#endif
+
+ if (IsBaselinePolicyWatched(sysno)) {
+ // Previously unseen syscalls. TODO(jln): some of these should
+ // be denied gracefully right away.
+ return CrashSIGSYS();
+ }
+
+ // In any other case crash the program with our SIGSYS handler.
+ return CrashSIGSYS();
+}
+
+} // namespace.
+
+// Unfortunately C++03 doesn't allow delegated constructors.
+// Call other constructor when C++11 lands.
+BaselinePolicy::BaselinePolicy() : BaselinePolicy(EPERM) {}
+
+BaselinePolicy::BaselinePolicy(int fs_denied_errno)
+ : fs_denied_errno_(fs_denied_errno), policy_pid_(sys_getpid()) {
+}
+
+BaselinePolicy::~BaselinePolicy() {
+ // Make sure that this policy is created, used and destroyed by a single
+ // process.
+ DCHECK_EQ(sys_getpid(), policy_pid_);
+}
+
+ResultExpr BaselinePolicy::EvaluateSyscall(int sysno) const {
+ // Sanity check that we're only called with valid syscall numbers.
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ // Make sure that this policy is used in the creating process.
+ if (1 == sysno) {
+ DCHECK_EQ(sys_getpid(), policy_pid_);
+ }
+ return EvaluateSyscallImpl(fs_denied_errno_, policy_pid_, sysno);
+}
+
+ResultExpr BaselinePolicy::InvalidSyscall() const {
+ return CrashSIGSYS();
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
new file mode 100644
index 0000000000..4169d9c3e2
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is a helper to build seccomp-bpf policies, i.e. policies for a sandbox
+// that reduces the Linux kernel's attack surface. Given its nature, it doesn't
+// have a clear semantics and is mostly "implementation-defined".
+//
+// This class implements the Policy interface with a "baseline"
+// policy for use within Chromium.
+// The "baseline" policy is somewhat arbitrary. All Chromium policies are an
+// alteration of it, and it represents a reasonable common ground to run most
+// code in a sandboxed environment.
+// A baseline policy is only valid for the process for which this object was
+// instantiated (so do not fork() and use it in a child).
+class SANDBOX_EXPORT BaselinePolicy : public bpf_dsl::Policy {
+ public:
+ BaselinePolicy();
+ // |fs_denied_errno| is the errno returned when a filesystem access system
+ // call is denied.
+ explicit BaselinePolicy(int fs_denied_errno);
+ ~BaselinePolicy() override;
+
+ bpf_dsl::ResultExpr EvaluateSyscall(int system_call_number) const override;
+ bpf_dsl::ResultExpr InvalidSyscall() const override;
+ pid_t policy_pid() const { return policy_pid_; }
+
+ private:
+ int fs_denied_errno_;
+
+ // The PID that the policy applies to (should be equal to the current pid).
+ pid_t policy_pid_;
+
+ DISALLOW_COPY_AND_ASSIGN(BaselinePolicy);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_BASELINE_POLICY_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
new file mode 100644
index 0000000000..614849f61c
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -0,0 +1,334 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/linux_futex.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// This also tests that read(), write() and fstat() are allowed.
+void TestPipeOrSocketPair(base::ScopedFD read_end, base::ScopedFD write_end) {
+ BPF_ASSERT_LE(0, read_end.get());
+ BPF_ASSERT_LE(0, write_end.get());
+ struct stat stat_buf;
+ int sys_ret = fstat(read_end.get(), &stat_buf);
+ BPF_ASSERT_EQ(0, sys_ret);
+ BPF_ASSERT(S_ISFIFO(stat_buf.st_mode) || S_ISSOCK(stat_buf.st_mode));
+
+ const ssize_t kTestTransferSize = 4;
+ static const char kTestString[kTestTransferSize] = {'T', 'E', 'S', 'T'};
+ ssize_t transfered = 0;
+
+ transfered =
+ HANDLE_EINTR(write(write_end.get(), kTestString, kTestTransferSize));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ char read_buf[kTestTransferSize + 1] = {0};
+ transfered = HANDLE_EINTR(read(read_end.get(), read_buf, sizeof(read_buf)));
+ BPF_ASSERT_EQ(kTestTransferSize, transfered);
+ BPF_ASSERT_EQ(0, memcmp(kTestString, read_buf, kTestTransferSize));
+}
+
+// Test that a few easy-to-test system calls are allowed.
+BPF_TEST_C(BaselinePolicy, BaselinePolicyBasicAllowed, BaselinePolicy) {
+ BPF_ASSERT_EQ(0, sched_yield());
+
+ int pipefd[2];
+ int sys_ret = pipe(pipefd);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(pipefd[0]), base::ScopedFD(pipefd[1]));
+
+ BPF_ASSERT_LE(1, getpid());
+ BPF_ASSERT_LE(0, getuid());
+}
+
+BPF_TEST_C(BaselinePolicy, FchmodErrno, BaselinePolicy) {
+ int ret = fchmod(-1, 07777);
+ BPF_ASSERT_EQ(-1, ret);
+ // Without the sandbox, this would EBADF instead.
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkErrno, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = fork();
+ const int fork_errno = errno;
+ TestUtils::HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkX86Glibc() {
+ static pid_t ptid;
+ return sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid, nullptr,
+ nullptr);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkX86Eperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkX86Glibc();
+ const int fork_errno = errno;
+ TestUtils::HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+pid_t ForkARMGlibc() {
+ static pid_t ctid;
+ return sys_clone(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, nullptr,
+ nullptr, &ctid, nullptr);
+}
+
+BPF_TEST_C(BaselinePolicy, ForkArmEperm, BaselinePolicy) {
+ errno = 0;
+ pid_t pid = ForkARMGlibc();
+ const int fork_errno = errno;
+ TestUtils::HandlePostForkReturn(pid);
+
+ BPF_ASSERT_EQ(-1, pid);
+ BPF_ASSERT_EQ(EPERM, fork_errno);
+}
+
+BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) {
+ base::Thread thread("sandbox_tests");
+ BPF_ASSERT(thread.Start());
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedCloneFlagCrashes,
+ DEATH_SEGV_MESSAGE(GetCloneErrorMessageContentForTests()),
+ BaselinePolicy) {
+ pid_t pid = sys_clone(CLONE_THREAD | SIGCHLD);
+ TestUtils::HandlePostForkReturn(pid);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ DisallowedKillCrashes,
+ DEATH_SEGV_MESSAGE(GetKillErrorMessageContentForTests()),
+ BaselinePolicy) {
+ BPF_ASSERT_NE(1, getpid());
+ kill(1, 0);
+ _exit(0);
+}
+
+BPF_TEST_C(BaselinePolicy, CanKillSelf, BaselinePolicy) {
+ int sys_ret = kill(getpid(), 0);
+ BPF_ASSERT_EQ(0, sys_ret);
+}
+
+BPF_TEST_C(BaselinePolicy, Socketpair, BaselinePolicy) {
+ int sv[2];
+ int sys_ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+
+ sys_ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sv);
+ BPF_ASSERT_EQ(0, sys_ret);
+ TestPipeOrSocketPair(base::ScopedFD(sv[0]), base::ScopedFD(sv[1]));
+}
+
+// Not all architectures can restrict the domain for socketpair().
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SocketpairWrongDomain,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ int sv[2];
+ ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv));
+ _exit(1);
+}
+#endif // defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+
+BPF_TEST_C(BaselinePolicy, EPERM_open, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = open("/proc/cpuinfo", O_RDONLY);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_access, BaselinePolicy) {
+ errno = 0;
+ int sys_ret = access("/proc/cpuinfo", R_OK);
+ BPF_ASSERT_EQ(-1, sys_ret);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_TEST_C(BaselinePolicy, EPERM_getcwd, BaselinePolicy) {
+ errno = 0;
+ char buf[1024];
+ char* cwd = getcwd(buf, sizeof(buf));
+ BPF_ASSERT_EQ(NULL, cwd);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ SIGSYS_InvalidSyscall,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ Syscall::InvalidCall();
+}
+
+// A failing test using this macro could be problematic since we perform
+// system calls by passing "0" as every argument.
+// The kernel could SIGSEGV the process or the system call itself could reboot
+// the machine. Some thoughts have been given when hand-picking the system
+// calls below to limit any potential side effects outside of the current
+// process.
+#define TEST_BASELINE_SIGSYS(sysno) \
+ BPF_DEATH_TEST_C(BaselinePolicy, \
+ SIGSYS_##sysno, \
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), \
+ BaselinePolicy) { \
+ syscall(sysno, 0, 0, 0, 0, 0, 0); \
+ _exit(1); \
+ }
+
+TEST_BASELINE_SIGSYS(__NR_acct);
+TEST_BASELINE_SIGSYS(__NR_chroot);
+TEST_BASELINE_SIGSYS(__NR_fanotify_init);
+TEST_BASELINE_SIGSYS(__NR_fgetxattr);
+TEST_BASELINE_SIGSYS(__NR_getcpu);
+TEST_BASELINE_SIGSYS(__NR_getitimer);
+TEST_BASELINE_SIGSYS(__NR_init_module);
+TEST_BASELINE_SIGSYS(__NR_io_cancel);
+TEST_BASELINE_SIGSYS(__NR_keyctl);
+TEST_BASELINE_SIGSYS(__NR_mq_open);
+TEST_BASELINE_SIGSYS(__NR_ptrace);
+TEST_BASELINE_SIGSYS(__NR_sched_setaffinity);
+TEST_BASELINE_SIGSYS(__NR_setpgid);
+TEST_BASELINE_SIGSYS(__NR_swapon);
+TEST_BASELINE_SIGSYS(__NR_sysinfo);
+TEST_BASELINE_SIGSYS(__NR_syslog);
+TEST_BASELINE_SIGSYS(__NR_timer_create);
+
+#if !defined(__aarch64__)
+TEST_BASELINE_SIGSYS(__NR_eventfd);
+TEST_BASELINE_SIGSYS(__NR_inotify_init);
+TEST_BASELINE_SIGSYS(__NR_vserver);
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritence,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithRequeuePriorityInheritencePrivate,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_CMP_REQUEUE_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ FutexWithUnlockPIPrivate,
+ DEATH_SEGV_MESSAGE(GetFutexErrorMessageContentForTests()),
+ BaselinePolicy) {
+ syscall(__NR_futex, NULL, FUTEX_UNLOCK_PI_PRIVATE, 0, NULL, NULL, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, PrctlDumpable, BaselinePolicy) {
+ const int is_dumpable = prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+ BPF_ASSERT(is_dumpable == 1 || is_dumpable == 0);
+ const int prctl_ret = prctl(PR_SET_DUMPABLE, is_dumpable, 0, 0, 0, 0);
+ BPF_ASSERT_EQ(0, prctl_ret);
+}
+
+// Workaround incomplete Android headers.
+#if !defined(PR_CAPBSET_READ)
+#define PR_CAPBSET_READ 23
+#endif
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ PrctlSigsys,
+ DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()),
+ BaselinePolicy) {
+ prctl(PR_CAPBSET_READ, 0, 0, 0, 0);
+ _exit(1);
+}
+
+BPF_TEST_C(BaselinePolicy, GetOrSetPriority, BaselinePolicy) {
+ errno = 0;
+ const int original_prio = getpriority(PRIO_PROCESS, 0);
+ // Check errno instead of the return value since this system call can return
+ // -1 as a valid value.
+ BPF_ASSERT_EQ(0, errno);
+
+ errno = 0;
+ int rc = getpriority(PRIO_PROCESS, getpid());
+ BPF_ASSERT_EQ(0, errno);
+
+ rc = getpriority(PRIO_PROCESS, getpid() + 1);
+ BPF_ASSERT_EQ(-1, rc);
+ BPF_ASSERT_EQ(EPERM, errno);
+
+ rc = setpriority(PRIO_PROCESS, 0, original_prio);
+ BPF_ASSERT_EQ(0, rc);
+
+ rc = setpriority(PRIO_PROCESS, getpid(), original_prio);
+ BPF_ASSERT_EQ(0, rc);
+
+ errno = 0;
+ rc = setpriority(PRIO_PROCESS, getpid() + 1, original_prio);
+ BPF_ASSERT_EQ(-1, rc);
+ BPF_ASSERT_EQ(EPERM, errno);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ GetPrioritySigsys,
+ DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ getpriority(PRIO_USER, 0);
+ _exit(1);
+}
+
+BPF_DEATH_TEST_C(BaselinePolicy,
+ ClockGettimeWithDisallowedClockCrashes,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ BaselinePolicy) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
new file mode 100644
index 0000000000..05250d147f
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: any code in this file MUST be async-signal safe.
+
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+#if defined(__mips__)
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+#endif
+
+#define SECCOMP_MESSAGE_COMMON_CONTENT "seccomp-bpf failure"
+#define SECCOMP_MESSAGE_CLONE_CONTENT "clone() failure"
+#define SECCOMP_MESSAGE_PRCTL_CONTENT "prctl() failure"
+#define SECCOMP_MESSAGE_IOCTL_CONTENT "ioctl() failure"
+#define SECCOMP_MESSAGE_KILL_CONTENT "(tg)kill() failure"
+#define SECCOMP_MESSAGE_FUTEX_CONTENT "futex() failure"
+
+namespace {
+
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+// Write |error_message| to stderr. Similar to RawLog(), but a bit more careful
+// about async-signal safety. |size| is the size to write and should typically
+// not include a terminating \0.
+void WriteToStdErr(const char* error_message, size_t size) {
+ while (size > 0) {
+ // TODO(jln): query the current policy to check if send() is available and
+ // use it to perform a non-blocking write.
+ const int ret = HANDLE_EINTR(write(STDERR_FILENO, error_message, size));
+ // We can't handle any type of error here.
+ if (ret <= 0 || static_cast<size_t>(ret) > size) break;
+ size -= ret;
+ error_message += ret;
+ }
+}
+
+// Invalid syscall values are truncated to zero.
+// On architectures where base value is zero (Intel and Arm),
+// syscall number is the same as offset from base.
+// This function returns values between 0 and 1023 on all architectures.
+// On architectures where base value is different than zero (currently only
+// Mips), we are truncating valid syscall values to offset from base.
+uint32_t SyscallNumberToOffsetFromBase(uint32_t sysno) {
+#if defined(__mips__)
+ // On MIPS syscall numbers are in different range than on x86 and ARM.
+ // Valid MIPS O32 ABI syscall __NR_syscall will be truncated to zero for
+ // simplicity.
+ sysno = sysno - __NR_Linux;
+#endif
+
+ if (sysno >= 1024)
+ sysno = 0;
+
+ return sysno;
+}
+
+// Print a seccomp-bpf failure to handle |sysno| to stderr in an
+// async-signal safe way.
+void PrintSyscallError(uint32_t sysno) {
+ if (sysno >= 1024)
+ sysno = 0;
+ // TODO(markus): replace with async-signal safe snprintf when available.
+ const size_t kNumDigits = 4;
+ char sysno_base10[kNumDigits];
+ uint32_t rem = sysno;
+ uint32_t mod = 0;
+ for (int i = kNumDigits - 1; i >= 0; i--) {
+ mod = rem % 10;
+ rem /= 10;
+ sysno_base10[i] = '0' + mod;
+ }
+#if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32)
+ static const char kSeccompErrorPrefix[] = __FILE__
+ ":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall 4000 + ";
+#else
+ static const char kSeccompErrorPrefix[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_COMMON_CONTENT " in syscall ";
+#endif
+ static const char kSeccompErrorPostfix[] = "\n";
+ WriteToStdErr(kSeccompErrorPrefix, sizeof(kSeccompErrorPrefix) - 1);
+ WriteToStdErr(sysno_base10, sizeof(sysno_base10));
+ WriteToStdErr(kSeccompErrorPostfix, sizeof(kSeccompErrorPostfix) - 1);
+}
+
+} // namespace.
+
+namespace sandbox {
+
+intptr_t CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux) {
+ uint32_t syscall = SyscallNumberToOffsetFromBase(args.nr);
+
+ PrintSyscallError(syscall);
+
+ // Encode 8-bits of the 1st two arguments too, so we can discern which socket
+ // type, which fcntl, ... etc., without being likely to hit a mapped
+ // address.
+ // Do not encode more bits here without thinking about increasing the
+ // likelihood of collision with mapped pages.
+ syscall |= ((args.args[0] & 0xffUL) << 12);
+ syscall |= ((args.args[1] & 0xffUL) << 20);
+ // Purposefully dereference the syscall as an address so it'll show up very
+ // clearly and easily in crash dumps.
+ volatile char* addr = reinterpret_cast<volatile char*>(syscall);
+ *addr = '\0';
+ // In case we hit a mapped address, hit the null page with just the syscall,
+ // for paranoia.
+ syscall &= 0xfffUL;
+ addr = reinterpret_cast<volatile char*>(syscall);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+// TODO(jln): refactor the reporting functions.
+
+intptr_t SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux) {
+ static const char kSeccompCloneError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_CLONE_CONTENT "\n";
+ WriteToStdErr(kSeccompCloneError, sizeof(kSeccompCloneError) - 1);
+ // "flags" is the first argument in the kernel's clone().
+ // Mark as volatile to be able to find the value on the stack in a minidump.
+ volatile uint64_t clone_flags = args.args[0];
+ volatile char* addr;
+ if (IsArchitectureX86_64()) {
+ addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFFFFF);
+ *addr = '\0';
+ }
+ // Hit the NULL page if this fails to fault.
+ addr = reinterpret_cast<volatile char*>(clone_flags & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSPrctlFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompPrctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_PRCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompPrctlError, sizeof(kSeccompPrctlError) - 1);
+ // Mark as volatile to be able to find the value on the stack in a minidump.
+ volatile uint64_t option = args.args[0];
+ volatile char* addr =
+ reinterpret_cast<volatile char*>(option & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSIoctlFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompIoctlError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_IOCTL_CONTENT "\n";
+ WriteToStdErr(kSeccompIoctlError, sizeof(kSeccompIoctlError) - 1);
+ // Make "request" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t request = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(request & 0xFFFF);
+ *addr = '\0';
+ // Hit the NULL page if this fails.
+ addr = reinterpret_cast<volatile char*>(request & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSKillFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompKillError[] =
+ __FILE__":**CRASHING**:" SECCOMP_MESSAGE_KILL_CONTENT "\n";
+ WriteToStdErr(kSeccompKillError, sizeof(kSeccompKillError) - 1);
+ // Make "pid" volatile so that we can see it on the stack in a minidump.
+ volatile uint64_t my_pid = sys_getpid();
+ volatile char* addr = reinterpret_cast<volatile char*>(my_pid & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSFutexFailure(const struct arch_seccomp_data& args,
+ void* /* aux */) {
+ static const char kSeccompFutexError[] =
+ __FILE__ ":**CRASHING**:" SECCOMP_MESSAGE_FUTEX_CONTENT "\n";
+ WriteToStdErr(kSeccompFutexError, sizeof(kSeccompFutexError) - 1);
+ volatile int futex_op = args.args[1];
+ volatile char* addr = reinterpret_cast<volatile char*>(futex_op & 0xFFF);
+ *addr = '\0';
+ for (;;)
+ _exit(1);
+}
+
+intptr_t SIGSYSSchedHandler(const struct arch_seccomp_data& args,
+ void* aux) {
+ switch (args.nr) {
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler:
+ const pid_t tid = sys_gettid();
+ // The first argument is the pid. If is our thread id, then replace it
+ // with 0, which is equivalent and allowed by the policy.
+ if (args.args[0] == static_cast<uint64_t>(tid)) {
+ return Syscall::Call(args.nr,
+ 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]));
+ }
+ break;
+ }
+
+ CrashSIGSYS_Handler(args, aux);
+
+ // Should never be reached.
+ RAW_CHECK(false);
+ return -ENOSYS;
+}
+
+bpf_dsl::ResultExpr CrashSIGSYS() {
+ return bpf_dsl::Trap(CrashSIGSYS_Handler, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSClone() {
+ return bpf_dsl::Trap(SIGSYSCloneFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSPrctl() {
+ return bpf_dsl::Trap(SIGSYSPrctlFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSIoctl() {
+ return bpf_dsl::Trap(SIGSYSIoctlFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSKill() {
+ return bpf_dsl::Trap(SIGSYSKillFailure, NULL);
+}
+
+bpf_dsl::ResultExpr CrashSIGSYSFutex() {
+ return bpf_dsl::Trap(SIGSYSFutexFailure, NULL);
+}
+
+bpf_dsl::ResultExpr RewriteSchedSIGSYS() {
+ return bpf_dsl::Trap(SIGSYSSchedHandler, NULL);
+}
+
+const char* GetErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_COMMON_CONTENT;
+}
+
+const char* GetCloneErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_CLONE_CONTENT;
+}
+
+const char* GetPrctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_PRCTL_CONTENT;
+}
+
+const char* GetIoctlErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_IOCTL_CONTENT;
+}
+
+const char* GetKillErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_KILL_CONTENT;
+}
+
+const char* GetFutexErrorMessageContentForTests() {
+ return SECCOMP_MESSAGE_FUTEX_CONTENT;
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
new file mode 100644
index 0000000000..c64e994172
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
+
+#include <stdint.h>
+
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+// The handlers are suitable for use in Trap() error codes. They are
+// guaranteed to be async-signal safe.
+// See sandbox/linux/seccomp-bpf/trap.h to see how they work.
+
+namespace sandbox {
+
+struct arch_seccomp_data;
+
+// This handler will crash the currently running process. The crashing address
+// will be the number of the current system call, extracted from |args|.
+// This handler will also print to stderr the number of the crashing syscall.
+SANDBOX_EXPORT intptr_t
+ CrashSIGSYS_Handler(const struct arch_seccomp_data& args, void* aux);
+
+// The following three handlers are suitable to report failures with the
+// clone(), prctl() and ioctl() system calls respectively.
+
+// The crashing address will be (clone_flags & 0xFFFFFF), where clone_flags is
+// the clone(2) argument, extracted from |args|.
+SANDBOX_EXPORT intptr_t
+ SIGSYSCloneFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (option & 0xFFF), where option is the prctl(2)
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSPrctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be request & 0xFFFF, where request is the ioctl(2)
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSIoctlFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (pid & 0xFFF), where pid is the first
+// argument (and can be a tid).
+SANDBOX_EXPORT intptr_t
+ SIGSYSKillFailure(const struct arch_seccomp_data& args, void* aux);
+// The crashing address will be (op & 0xFFF), where op is the second
+// argument.
+SANDBOX_EXPORT intptr_t
+ SIGSYSFutexFailure(const struct arch_seccomp_data& args, void* aux);
+// If the syscall is not being called on the current tid, crashes in the same
+// way as CrashSIGSYS_Handler. Otherwise, returns the result of calling the
+// syscall with the pid argument set to 0 (which for these calls means the
+// current thread). The following syscalls are supported:
+//
+// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
+// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
+// sched_setparam(), sched_setscheduler()
+SANDBOX_EXPORT intptr_t
+ SIGSYSSchedHandler(const struct arch_seccomp_data& args, void* aux);
+
+// Variants of the above functions for use with bpf_dsl.
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYS();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSClone();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSPrctl();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSIoctl();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill();
+SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex();
+SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS();
+
+// Following four functions return substrings of error messages used
+// in the above four functions. They are useful in death tests.
+SANDBOX_EXPORT const char* GetErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetCloneErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetPrctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetIoctlErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetKillErrorMessageContentForTests();
+SANDBOX_EXPORT const char* GetFutexErrorMessageContentForTests();
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SIGSYS_HANDLERS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
new file mode 100644
index 0000000000..58ffb843a8
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <fcntl.h>
+#include <linux/net.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/system_headers/linux_futex.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/system_headers/linux_time.h"
+
+// PNaCl toolchain does not provide sys/ioctl.h header.
+#if !defined(OS_NACL_NONSFI)
+#include <sys/ioctl.h>
+#endif
+
+#if defined(OS_ANDROID)
+
+#if !defined(F_DUPFD_CLOEXEC)
+#define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6)
+#endif
+
+// https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h
+#if !defined(PR_SET_VMA)
+#define PR_SET_VMA 0x53564d41
+#endif
+
+// https://android.googlesource.com/platform/system/core/+/lollipop-release/libcutils/sched_policy.c
+#if !defined(PR_SET_TIMERSLACK_PID)
+#define PR_SET_TIMERSLACK_PID 41
+#endif
+
+#endif // defined(OS_ANDROID)
+
+#if defined(__arm__) && !defined(MAP_STACK)
+#define MAP_STACK 0x20000 // Daisy build environment has old headers.
+#endif
+
+#if defined(__mips__) && !defined(MAP_STACK)
+#define MAP_STACK 0x40000
+#endif
+namespace {
+
+inline bool IsArchitectureX86_64() {
+#if defined(__x86_64__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsArchitectureI386() {
+#if defined(__i386__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsAndroid() {
+#if defined(OS_ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+
+inline bool IsArchitectureMips() {
+#if defined(__mips__)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // namespace.
+
+#define CASES SANDBOX_BPF_DSL_CASES
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Arg;
+using sandbox::bpf_dsl::BoolExpr;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::If;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+#if !defined(OS_NACL_NONSFI)
+// Allow Glibc's and Android pthread creation flags, crash on any other
+// thread creation attempts and EPERM attempts to use neither
+// CLONE_VM, nor CLONE_THREAD, which includes all fork() implementations.
+ResultExpr RestrictCloneToThreadsAndEPERMFork() {
+ const Arg<unsigned long> flags(0);
+
+ // TODO(mdempsky): Extend DSL to support (flags & ~mask1) == mask2.
+ const uint64_t kAndroidCloneMask = CLONE_VM | CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM;
+ const uint64_t kObsoleteAndroidCloneMask = kAndroidCloneMask | CLONE_DETACHED;
+
+ const uint64_t kGlibcPthreadFlags =
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID;
+ const BoolExpr glibc_test = flags == kGlibcPthreadFlags;
+
+ const BoolExpr android_test = flags == kAndroidCloneMask ||
+ flags == kObsoleteAndroidCloneMask ||
+ flags == kGlibcPthreadFlags;
+
+ return If(IsAndroid() ? android_test : glibc_test, Allow())
+ .ElseIf((flags & (CLONE_VM | CLONE_THREAD)) == 0, Error(EPERM))
+ .Else(CrashSIGSYSClone());
+}
+
+ResultExpr RestrictPrctl() {
+ // Will need to add seccomp compositing in the future. PR_SET_PTRACER is
+ // used by breakpad but not needed anymore.
+ const Arg<int> option(0);
+ return Switch(option)
+ .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE),
+ Allow())
+#if defined(OS_ANDROID)
+ .CASES((PR_SET_VMA, PR_SET_TIMERSLACK_PID), Allow())
+#endif
+ .Default(CrashSIGSYSPrctl());
+}
+
+ResultExpr RestrictIoctl() {
+ const Arg<int> request(1);
+ return Switch(request).CASES((TCGETS, FIONREAD), Allow()).Default(
+ CrashSIGSYSIoctl());
+}
+
+ResultExpr RestrictMmapFlags() {
+ // The flags you see are actually the allowed ones, and the variable is a
+ // "denied" mask because of the negation operator.
+ // Significantly, we don't permit MAP_HUGETLB, or the newer flags such as
+ // MAP_POPULATE.
+ // TODO(davidung), remove MAP_DENYWRITE with updated Tegra libraries.
+ const uint64_t kAllowedMask = MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
+ MAP_STACK | MAP_NORESERVE | MAP_FIXED |
+ MAP_DENYWRITE;
+ const Arg<int> flags(3);
+ return If((flags & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictMprotectFlags() {
+ // The flags you see are actually the allowed ones, and the variable is a
+ // "denied" mask because of the negation operator.
+ // Significantly, we don't permit weird undocumented flags such as
+ // PROT_GROWSDOWN.
+ const uint64_t kAllowedMask = PROT_READ | PROT_WRITE | PROT_EXEC;
+ const Arg<int> prot(2);
+ return If((prot & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictFcntlCommands() {
+ // We also restrict the flags in F_SETFL. We don't want to permit flags with
+ // a history of trouble such as O_DIRECT. The flags you see are actually the
+ // allowed ones, and the variable is a "denied" mask because of the negation
+ // operator.
+ // Glibc overrides the kernel's O_LARGEFILE value. Account for this.
+ uint64_t kOLargeFileFlag = O_LARGEFILE;
+ if (IsArchitectureX86_64() || IsArchitectureI386() || IsArchitectureMips())
+ kOLargeFileFlag = 0100000;
+
+ const Arg<int> cmd(1);
+ const Arg<long> long_arg(2);
+
+ const uint64_t kAllowedMask = O_ACCMODE | O_APPEND | O_NONBLOCK | O_SYNC |
+ kOLargeFileFlag | O_CLOEXEC | O_NOATIME;
+ return Switch(cmd)
+ .CASES((F_GETFL,
+ F_GETFD,
+ F_SETFD,
+ F_SETLK,
+ F_SETLKW,
+ F_GETLK,
+ F_DUPFD,
+ F_DUPFD_CLOEXEC),
+ Allow())
+ .Case(F_SETFL,
+ If((long_arg & ~kAllowedMask) == 0, Allow()).Else(CrashSIGSYS()))
+ .Default(CrashSIGSYS());
+}
+
+#if defined(__i386__) || defined(__mips__)
+ResultExpr RestrictSocketcallCommand() {
+ // Unfortunately, we are unable to restrict the first parameter to
+ // socketpair(2). Whilst initially sounding bad, it's noteworthy that very
+ // few protocols actually support socketpair(2). The scary call that we're
+ // worried about, socket(2), remains blocked.
+ const Arg<int> call(0);
+ return Switch(call)
+ .CASES((SYS_SOCKETPAIR,
+ SYS_SHUTDOWN,
+ SYS_RECV,
+ SYS_SEND,
+ SYS_RECVFROM,
+ SYS_SENDTO,
+ SYS_RECVMSG,
+ SYS_SENDMSG),
+ Allow())
+ .Default(Error(EPERM));
+}
+#endif
+
+ResultExpr RestrictKillTarget(pid_t target_pid, int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill: {
+ const Arg<pid_t> pid(0);
+ return If(pid == target_pid, Allow()).Else(CrashSIGSYSKill());
+ }
+ case __NR_tkill:
+ return CrashSIGSYSKill();
+ default:
+ NOTREACHED();
+ return CrashSIGSYS();
+ }
+}
+
+ResultExpr RestrictFutex() {
+ const uint64_t kAllowedFutexFlags = FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME;
+ const Arg<int> op(1);
+ return Switch(op & ~kAllowedFutexFlags)
+ .CASES((FUTEX_WAIT,
+ FUTEX_WAKE,
+ FUTEX_REQUEUE,
+ FUTEX_CMP_REQUEUE,
+ FUTEX_WAKE_OP,
+ FUTEX_WAIT_BITSET,
+ FUTEX_WAKE_BITSET),
+ Allow())
+ .Default(CrashSIGSYSFutex());
+}
+
+ResultExpr RestrictGetSetpriority(pid_t target_pid) {
+ const Arg<int> which(0);
+ const Arg<int> who(1);
+ return If(which == PRIO_PROCESS,
+ If(who == 0 || who == target_pid, Allow()).Else(Error(EPERM)))
+ .Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictSchedTarget(pid_t target_pid, int sysno) {
+ switch (sysno) {
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler: {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0 || pid == target_pid, Allow())
+ .Else(RewriteSchedSIGSYS());
+ }
+ default:
+ NOTREACHED();
+ return CrashSIGSYS();
+ }
+}
+
+ResultExpr RestrictPrlimit64(pid_t target_pid) {
+ const Arg<pid_t> pid(0);
+ return If(pid == 0 || pid == target_pid, Allow()).Else(CrashSIGSYS());
+}
+
+ResultExpr RestrictGetrusage() {
+ const Arg<int> who(0);
+ return If(who == RUSAGE_SELF, Allow()).Else(CrashSIGSYS());
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+ResultExpr RestrictClockID() {
+ static_assert(4 == sizeof(clockid_t), "clockid_t is not 32bit");
+ const Arg<clockid_t> clockid(0);
+ return If(
+#if defined(OS_CHROMEOS)
+ // Allow the special clock for Chrome OS used by Chrome tracing.
+ clockid == base::TraceTicks::kClockSystemTrace ||
+#endif
+ clockid == CLOCK_MONOTONIC ||
+ clockid == CLOCK_MONOTONIC_COARSE ||
+ clockid == CLOCK_PROCESS_CPUTIME_ID ||
+ clockid == CLOCK_REALTIME ||
+ clockid == CLOCK_REALTIME_COARSE ||
+ clockid == CLOCK_THREAD_CPUTIME_ID,
+ Allow()).Else(CrashSIGSYS());
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
new file mode 100644
index 0000000000..9eb35d10e0
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
+
+#include <unistd.h>
+
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+// These are helpers to build seccomp-bpf policies, i.e. policies for a
+// sandbox that reduces the Linux kernel's attack surface. They return a
+// bpf_dsl::ResultExpr suitable to restrict certain system call parameters.
+
+namespace sandbox {
+
+// Allow clone(2) for threads.
+// Reject fork(2) attempts with EPERM.
+// Don't restrict on ASAN.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictCloneToThreadsAndEPERMFork();
+
+// Allow PR_SET_NAME, PR_SET_DUMPABLE, PR_GET_DUMPABLE.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrctl();
+
+// Allow TCGETS and FIONREAD.
+// Crash if anything else is attempted.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictIoctl();
+
+// Restrict the flags argument in mmap(2).
+// Only allow: MAP_SHARED | MAP_PRIVATE | MAP_ANONYMOUS |
+// MAP_STACK | MAP_NORESERVE | MAP_FIXED | MAP_DENYWRITE.
+// Crash if any other flag is used.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMmapFlags();
+
+// Restrict the prot argument in mprotect(2).
+// Only allow: PROT_READ | PROT_WRITE | PROT_EXEC.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictMprotectFlags();
+
+// Restrict fcntl(2) cmd argument to:
+// We allow F_GETFL, F_SETFL, F_GETFD, F_SETFD, F_DUPFD, F_DUPFD_CLOEXEC,
+// F_SETLK, F_SETLKW and F_GETLK.
+// Also, in F_SETFL, restrict the allowed flags to: O_ACCMODE | O_APPEND |
+// O_NONBLOCK | O_SYNC | O_LARGEFILE | O_CLOEXEC | O_NOATIME.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictFcntlCommands();
+
+#if defined(__i386__) || defined(__mips__)
+// Restrict socketcall(2) to only allow socketpair(2), send(2), recv(2),
+// sendto(2), recvfrom(2), shutdown(2), sendmsg(2) and recvmsg(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSocketcallCommand();
+#endif
+
+// Restrict |sysno| (which must be kill, tkill or tgkill) by allowing tgkill or
+// kill iff the first parameter is |target_pid|, crashing otherwise or if
+// |sysno| is tkill.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictKillTarget(pid_t target_pid,
+ int sysno);
+
+// Crash if FUTEX_CMP_REQUEUE_PI is used in the second argument of futex(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictFutex();
+
+// Crash if |which| is not PRIO_PROCESS. EPERM if |who| is not 0, neither
+// |target_pid| while calling setpriority(2) / getpriority(2).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetSetpriority(pid_t target_pid);
+
+// Restricts |pid| for sched_* syscalls which take a pid as the first argument.
+// We only allow calling these syscalls if the pid argument is equal to the pid
+// of the sandboxed process or 0 (indicating the current thread). The following
+// syscalls are supported:
+//
+// sched_getaffinity(), sched_getattr(), sched_getparam(), sched_getscheduler(),
+// sched_rr_get_interval(), sched_setaffinity(), sched_setattr(),
+// sched_setparam(), sched_setscheduler()
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictSchedTarget(pid_t target_pid,
+ int sysno);
+
+// Restricts the |pid| argument of prlimit64 to 0 (meaning the calling process)
+// or target_pid.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictPrlimit64(pid_t target_pid);
+
+// Restricts the |who| argument of getrusage to RUSAGE_SELF (meaning the calling
+// process).
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictGetrusage();
+
+// Restrict |clk_id| for clock_getres(), clock_gettime() and clock_settime().
+// We allow accessing only CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID,
+// CLOCK_REALTIME, and CLOCK_THREAD_CPUTIME_ID. In particular, this disallows
+// access to arbitrary per-{process,thread} CPU-time clock IDs (such as those
+// returned by {clock,pthread}_getcpuclockid), which can leak information
+// about the state of the host OS.
+// On Chrome OS, base::TraceTicks::kClockSystemTrace is also allowed.
+SANDBOX_EXPORT bpf_dsl::ResultExpr RestrictClockID();
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_PARAMETERS_RESTRICTIONS_H_
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
new file mode 100644
index 0000000000..aaed480d69
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -0,0 +1,282 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/sys_info.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/system_headers/linux_time.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+#if !defined(OS_ANDROID)
+#include "third_party/lss/linux_syscall_support.h" // for MAKE_PROCESS_CPUCLOCK
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// NOTE: most of the parameter restrictions are tested in
+// baseline_policy_unittest.cc as a more end-to-end test.
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+
+class RestrictClockIdPolicy : public bpf_dsl::Policy {
+ public:
+ RestrictClockIdPolicy() {}
+ ~RestrictClockIdPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_clock_gettime:
+ case __NR_clock_getres:
+ return RestrictClockID();
+ default:
+ return Allow();
+ }
+ }
+};
+
+void CheckClock(clockid_t clockid) {
+ struct timespec ts;
+ ts.tv_sec = -1;
+ ts.tv_nsec = -1;
+ BPF_ASSERT_EQ(0, clock_getres(clockid, &ts));
+ BPF_ASSERT_EQ(0, ts.tv_sec);
+ BPF_ASSERT_LE(0, ts.tv_nsec);
+ ts.tv_sec = -1;
+ ts.tv_nsec = -1;
+ BPF_ASSERT_EQ(0, clock_gettime(clockid, &ts));
+ BPF_ASSERT_LE(0, ts.tv_sec);
+ BPF_ASSERT_LE(0, ts.tv_nsec);
+}
+
+BPF_TEST_C(ParameterRestrictions,
+ clock_gettime_allowed,
+ RestrictClockIdPolicy) {
+ CheckClock(CLOCK_MONOTONIC);
+ CheckClock(CLOCK_MONOTONIC_COARSE);
+ CheckClock(CLOCK_PROCESS_CPUTIME_ID);
+ CheckClock(CLOCK_REALTIME);
+ CheckClock(CLOCK_REALTIME_COARSE);
+ CheckClock(CLOCK_THREAD_CPUTIME_ID);
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_monotonic_raw,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+}
+
+#if defined(OS_CHROMEOS)
+
+// A custom BPF tester delegate to run IsRunningOnChromeOS() before
+// the sandbox is enabled because we cannot run it with non-SFI BPF
+// sandbox enabled.
+class ClockSystemTesterDelegate : public sandbox::BPFTesterDelegate {
+ public:
+ ClockSystemTesterDelegate()
+ : is_running_on_chromeos_(base::SysInfo::IsRunningOnChromeOS()) {}
+ ~ClockSystemTesterDelegate() override {}
+
+ scoped_ptr<sandbox::bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return scoped_ptr<sandbox::bpf_dsl::Policy>(new RestrictClockIdPolicy());
+ }
+ void RunTestFunction() override {
+ if (is_running_on_chromeos_) {
+ CheckClock(base::TraceTicks::kClockSystemTrace);
+ } else {
+ struct timespec ts;
+ // kClockSystemTrace is 11, which is CLOCK_THREAD_CPUTIME_ID of
+ // the init process (pid=1). If kernel supports this feature,
+ // this may succeed even if this is not running on Chrome OS. We
+ // just check this clock_gettime call does not crash.
+ clock_gettime(base::TraceTicks::kClockSystemTrace, &ts);
+ }
+ }
+
+ private:
+ const bool is_running_on_chromeos_;
+ DISALLOW_COPY_AND_ASSIGN(ClockSystemTesterDelegate);
+};
+
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, ClockSystemTesterDelegate);
+
+#elif defined(OS_LINUX)
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_system_trace,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ struct timespec ts;
+ clock_gettime(base::TraceTicks::kClockSystemTrace, &ts);
+}
+
+#endif // defined(OS_CHROMEOS)
+
+#if !defined(OS_ANDROID)
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ clock_gettime_crash_cpu_clock,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictClockIdPolicy) {
+ // We can't use clock_getcpuclockid() because it's not implemented in newlib,
+ // and it might not work inside the sandbox anyway.
+ const pid_t kInitPID = 1;
+ const clockid_t kInitCPUClockID =
+ MAKE_PROCESS_CPUCLOCK(kInitPID, CPUCLOCK_SCHED);
+
+ struct timespec ts;
+ clock_gettime(kInitCPUClockID, &ts);
+}
+#endif // !defined(OS_ANDROID)
+
+class RestrictSchedPolicy : public bpf_dsl::Policy {
+ public:
+ RestrictSchedPolicy() {}
+ ~RestrictSchedPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_sched_getparam:
+ return RestrictSchedTarget(getpid(), sysno);
+ default:
+ return Allow();
+ }
+ }
+};
+
+void CheckSchedGetParam(pid_t pid, struct sched_param* param) {
+ BPF_ASSERT_EQ(0, sched_getparam(pid, param));
+}
+
+void SchedGetParamThread(base::WaitableEvent* thread_run) {
+ const pid_t pid = getpid();
+ const pid_t tid = sys_gettid();
+ BPF_ASSERT_NE(pid, tid);
+
+ struct sched_param current_pid_param;
+ CheckSchedGetParam(pid, &current_pid_param);
+
+ struct sched_param zero_param;
+ CheckSchedGetParam(0, &zero_param);
+
+ struct sched_param tid_param;
+ CheckSchedGetParam(tid, &tid_param);
+
+ BPF_ASSERT_EQ(zero_param.sched_priority, tid_param.sched_priority);
+
+ // Verify that the SIGSYS handler sets errno properly.
+ errno = 0;
+ BPF_ASSERT_EQ(-1, sched_getparam(tid, NULL));
+ BPF_ASSERT_EQ(EINVAL, errno);
+
+ thread_run->Signal();
+}
+
+BPF_TEST_C(ParameterRestrictions,
+ sched_getparam_allowed,
+ RestrictSchedPolicy) {
+ base::WaitableEvent thread_run(true, false);
+ // Run the actual test in a new thread so that the current pid and tid are
+ // different.
+ base::Thread getparam_thread("sched_getparam_thread");
+ BPF_ASSERT(getparam_thread.Start());
+ getparam_thread.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&SchedGetParamThread, &thread_run));
+ BPF_ASSERT(thread_run.TimedWait(base::TimeDelta::FromMilliseconds(5000)));
+ getparam_thread.Stop();
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ sched_getparam_crash_non_zero,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictSchedPolicy) {
+ const pid_t kInitPID = 1;
+ struct sched_param param;
+ sched_getparam(kInitPID, &param);
+}
+
+class RestrictPrlimit64Policy : public bpf_dsl::Policy {
+ public:
+ RestrictPrlimit64Policy() {}
+ ~RestrictPrlimit64Policy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_prlimit64:
+ return RestrictPrlimit64(getpid());
+ default:
+ return Allow();
+ }
+ }
+};
+
+BPF_TEST_C(ParameterRestrictions, prlimit64_allowed, RestrictPrlimit64Policy) {
+ BPF_ASSERT_EQ(0, sys_prlimit64(0, RLIMIT_AS, NULL, NULL));
+ BPF_ASSERT_EQ(0, sys_prlimit64(getpid(), RLIMIT_AS, NULL, NULL));
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ prlimit64_crash_not_self,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictPrlimit64Policy) {
+ const pid_t kInitPID = 1;
+ BPF_ASSERT_NE(kInitPID, getpid());
+ sys_prlimit64(kInitPID, RLIMIT_AS, NULL, NULL);
+}
+
+class RestrictGetrusagePolicy : public bpf_dsl::Policy {
+ public:
+ RestrictGetrusagePolicy() {}
+ ~RestrictGetrusagePolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_getrusage:
+ return RestrictGetrusage();
+ default:
+ return Allow();
+ }
+ }
+};
+
+BPF_TEST_C(ParameterRestrictions, getrusage_allowed, RestrictGetrusagePolicy) {
+ struct rusage usage;
+ BPF_ASSERT_EQ(0, getrusage(RUSAGE_SELF, &usage));
+}
+
+BPF_DEATH_TEST_C(ParameterRestrictions,
+ getrusage_crash_not_self,
+ DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()),
+ RestrictGetrusagePolicy) {
+ struct rusage usage;
+ getrusage(RUSAGE_CHILDREN, &usage);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
new file mode 100644
index 0000000000..c217d47e2d
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.cc
@@ -0,0 +1,1060 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
+
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+
+// The functions below cover all existing i386, x86_64, and ARM system calls;
+// excluding syscalls made obsolete in ARM EABI.
+// The implicitly defined sets form a partition of the sets of
+// system calls.
+
+bool SyscallSets::IsKill(int sysno) {
+ switch (sysno) {
+ case __NR_kill:
+ case __NR_tgkill:
+ case __NR_tkill: // Deprecated.
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGettime(int sysno) {
+ switch (sysno) {
+ case __NR_gettimeofday:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_time:
+#endif
+ return true;
+ case __NR_adjtimex: // Privileged.
+ case __NR_clock_adjtime: // Privileged.
+ case __NR_clock_getres: // Could be allowed.
+ case __NR_clock_gettime:
+ case __NR_clock_nanosleep: // Could be allowed.
+ case __NR_clock_settime: // Privileged.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_ftime: // Obsolete.
+#endif
+ case __NR_settimeofday: // Privileged.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_stime:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsCurrentDirectory(int sysno) {
+ switch (sysno) {
+ case __NR_getcwd:
+ case __NR_chdir:
+ case __NR_fchdir:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsUmask(int sysno) {
+ switch (sysno) {
+ case __NR_umask:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// System calls that directly access the file system. They might acquire
+// a new file descriptor or otherwise perform an operation directly
+// via a path.
+// Both EPERM and ENOENT are valid errno unless otherwise noted in comment.
+bool SyscallSets::IsFileSystem(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_access: // EPERM not a valid errno.
+ case __NR_chmod:
+ case __NR_chown:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_chown32:
+#endif
+ case __NR_creat:
+ case __NR_futimesat: // Should be called utimesat ?
+ case __NR_lchown:
+ case __NR_link:
+ case __NR_lstat: // EPERM not a valid errno.
+ case __NR_mkdir:
+ case __NR_mknod:
+ case __NR_open:
+ case __NR_readlink: // EPERM not a valid errno.
+ case __NR_rename:
+ case __NR_rmdir:
+ case __NR_stat: // EPERM not a valid errno.
+ case __NR_symlink:
+ case __NR_unlink:
+ case __NR_uselib: // Neither EPERM, nor ENOENT are valid errno.
+ case __NR_ustat: // Same as above. Deprecated.
+ case __NR_utimes:
+#endif // !defined(__aarch64__)
+
+ case __NR_execve:
+ case __NR_faccessat: // EPERM not a valid errno.
+ case __NR_fchmodat:
+ case __NR_fchownat: // Should be called chownat ?
+#if defined(__x86_64__) || defined(__aarch64__)
+ case __NR_newfstatat: // fstatat(). EPERM not a valid errno.
+#elif defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstatat64:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_lchown32:
+#endif
+ case __NR_linkat:
+ case __NR_lookup_dcookie: // ENOENT not a valid errno.
+
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_lstat64:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__x86_64__)
+ case __NR_memfd_create:
+#endif
+ case __NR_mkdirat:
+ case __NR_mknodat:
+#if defined(__i386__)
+ case __NR_oldlstat:
+ case __NR_oldstat:
+#endif
+ case __NR_openat:
+ case __NR_readlinkat:
+ case __NR_renameat:
+ case __NR_renameat2:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_stat64:
+#endif
+ case __NR_statfs: // EPERM not a valid errno.
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_statfs64:
+#endif
+ case __NR_symlinkat:
+ case __NR_truncate:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_truncate64:
+#endif
+ case __NR_unlinkat:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_utime:
+#endif
+ case __NR_utimensat: // New.
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedFileSystemAccessViaFd(int sysno) {
+ switch (sysno) {
+ case __NR_fstat:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstat64:
+#endif
+ return true;
+// TODO(jln): these should be denied gracefully as well (moved below).
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_fadvise64: // EPERM not a valid errno.
+#endif
+#if defined(__i386__)
+ case __NR_fadvise64_64:
+#endif
+#if defined(__arm__)
+ case __NR_arm_fadvise64_64:
+#endif
+ case __NR_fdatasync: // EPERM not a valid errno.
+ case __NR_flock: // EPERM not a valid errno.
+ case __NR_fstatfs: // Give information about the whole filesystem.
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fstatfs64:
+#endif
+ case __NR_fsync: // EPERM not a valid errno.
+#if defined(__i386__)
+ case __NR_oldfstat:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_sync_file_range: // EPERM not a valid errno.
+#elif defined(__arm__)
+ case __NR_arm_sync_file_range: // EPERM not a valid errno.
+#endif
+ default:
+ return false;
+ }
+}
+
+// EPERM is a good errno for any of these.
+bool SyscallSets::IsDeniedFileSystemAccessViaFd(int sysno) {
+ switch (sysno) {
+ case __NR_fallocate:
+ case __NR_fchmod:
+ case __NR_fchown:
+ case __NR_ftruncate:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_fchown32:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_ftruncate64:
+#endif
+#if !defined(__aarch64__)
+ case __NR_getdents: // EPERM not a valid errno.
+#endif
+ case __NR_getdents64: // EPERM not a valid errno.
+#if defined(__i386__) || defined(__mips__)
+ case __NR_readdir:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGetSimpleId(int sysno) {
+ switch (sysno) {
+ case __NR_capget:
+ case __NR_getegid:
+ case __NR_geteuid:
+ case __NR_getgid:
+ case __NR_getgroups:
+ case __NR_getpid:
+ case __NR_getppid:
+ case __NR_getresgid:
+ case __NR_getsid:
+ case __NR_gettid:
+ case __NR_getuid:
+ case __NR_getresuid:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_getegid32:
+ case __NR_geteuid32:
+ case __NR_getgid32:
+ case __NR_getgroups32:
+ case __NR_getresgid32:
+ case __NR_getresuid32:
+ case __NR_getuid32:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsProcessPrivilegeChange(int sysno) {
+ switch (sysno) {
+ case __NR_capset:
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_ioperm: // Intel privilege.
+ case __NR_iopl: // Intel privilege.
+#endif
+ case __NR_setfsgid:
+ case __NR_setfsuid:
+ case __NR_setgid:
+ case __NR_setgroups:
+ case __NR_setregid:
+ case __NR_setresgid:
+ case __NR_setresuid:
+ case __NR_setreuid:
+ case __NR_setuid:
+#if defined(__i386__) || defined(__arm__)
+ case __NR_setfsgid32:
+ case __NR_setfsuid32:
+ case __NR_setgid32:
+ case __NR_setgroups32:
+ case __NR_setregid32:
+ case __NR_setresgid32:
+ case __NR_setresuid32:
+ case __NR_setreuid32:
+ case __NR_setuid32:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsProcessGroupOrSession(int sysno) {
+ switch (sysno) {
+ case __NR_setpgid:
+#if !defined(__aarch64__)
+ case __NR_getpgrp:
+#endif
+ case __NR_setsid:
+ case __NR_getpgid:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedSignalHandling(int sysno) {
+ switch (sysno) {
+ case __NR_rt_sigaction:
+ case __NR_rt_sigprocmask:
+ case __NR_rt_sigreturn:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sigaction:
+ case __NR_sigprocmask:
+ case __NR_sigreturn:
+#endif
+ return true;
+ case __NR_rt_sigpending:
+ case __NR_rt_sigqueueinfo:
+ case __NR_rt_sigsuspend:
+ case __NR_rt_sigtimedwait:
+ case __NR_rt_tgsigqueueinfo:
+ case __NR_sigaltstack:
+#if !defined(__aarch64__)
+ case __NR_signalfd:
+#endif
+ case __NR_signalfd4:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sigpending:
+ case __NR_sigsuspend:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_signal:
+ case __NR_sgetmask: // Obsolete.
+ case __NR_ssetmask:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedOperationOnFd(int sysno) {
+ switch (sysno) {
+ case __NR_close:
+ case __NR_dup:
+#if !defined(__aarch64__)
+ case __NR_dup2:
+#endif
+ case __NR_dup3:
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_shutdown:
+#endif
+ return true;
+ case __NR_fcntl:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_fcntl64:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKernelInternalApi(int sysno) {
+ switch (sysno) {
+ case __NR_restart_syscall:
+#if defined(__arm__)
+ case __ARM_NR_cmpxchg:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+// This should be thought through in conjunction with IsFutex().
+bool SyscallSets::IsAllowedProcessStartOrDeath(int sysno) {
+ switch (sysno) {
+ case __NR_exit:
+ case __NR_exit_group:
+ case __NR_wait4:
+ case __NR_waitid:
+#if defined(__i386__)
+ case __NR_waitpid:
+#endif
+ return true;
+ case __NR_clone: // Should be parameter-restricted.
+ case __NR_setns: // Privileged.
+#if !defined(__aarch64__)
+ case __NR_fork:
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_get_thread_area:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_set_thread_area:
+#endif
+ case __NR_set_tid_address:
+ case __NR_unshare:
+#if !defined(__mips__) && !defined(__aarch64__)
+ case __NR_vfork:
+#endif
+ default:
+ return false;
+ }
+}
+
+// It's difficult to restrict those, but there is attack surface here.
+bool SyscallSets::IsAllowedFutex(int sysno) {
+ switch (sysno) {
+ case __NR_get_robust_list:
+ case __NR_set_robust_list:
+ case __NR_futex:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedEpoll(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_epoll_create:
+ case __NR_epoll_wait:
+#endif
+ case __NR_epoll_create1:
+ case __NR_epoll_ctl:
+ return true;
+ default:
+#if defined(__x86_64__)
+ case __NR_epoll_ctl_old:
+#endif
+ case __NR_epoll_pwait:
+#if defined(__x86_64__)
+ case __NR_epoll_wait_old:
+#endif
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGetOrModifySocket(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_pipe:
+#endif
+ case __NR_pipe2:
+ return true;
+ default:
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_socketpair: // We will want to inspect its argument.
+#endif
+ return false;
+ }
+}
+
+bool SyscallSets::IsDeniedGetOrModifySocket(int sysno) {
+ switch (sysno) {
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_accept:
+ case __NR_accept4:
+ case __NR_bind:
+ case __NR_connect:
+ case __NR_socket:
+ case __NR_listen:
+ return true;
+#endif
+ default:
+ return false;
+ }
+}
+
+#if defined(__i386__) || defined(__mips__)
+// Big multiplexing system call for sockets.
+bool SyscallSets::IsSocketCall(int sysno) {
+ switch (sysno) {
+ case __NR_socketcall:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__)
+bool SyscallSets::IsNetworkSocketInformation(int sysno) {
+ switch (sysno) {
+ case __NR_getpeername:
+ case __NR_getsockname:
+ case __NR_getsockopt:
+ case __NR_setsockopt:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+bool SyscallSets::IsAllowedAddressSpaceAccess(int sysno) {
+ switch (sysno) {
+ case __NR_brk:
+ case __NR_mlock:
+ case __NR_munlock:
+ case __NR_munmap:
+ return true;
+ case __NR_madvise:
+ case __NR_mincore:
+ case __NR_mlockall:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_mmap:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_mmap2:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_modify_ldt:
+#endif
+ case __NR_mprotect:
+ case __NR_mremap:
+ case __NR_msync:
+ case __NR_munlockall:
+ case __NR_readahead:
+ case __NR_remap_file_pages:
+#if defined(__i386__)
+ case __NR_vm86:
+ case __NR_vm86old:
+#endif
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedGeneralIo(int sysno) {
+ switch (sysno) {
+ case __NR_lseek:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR__llseek:
+#endif
+#if !defined(__aarch64__)
+ case __NR_poll:
+#endif
+ case __NR_ppoll:
+ case __NR_pselect6:
+ case __NR_read:
+ case __NR_readv:
+#if defined(__arm__) || defined(__mips__)
+ case __NR_recv:
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_recvfrom: // Could specify source.
+ case __NR_recvmsg: // Could specify source.
+#endif
+#if defined(__i386__) || defined(__x86_64__)
+ case __NR_select:
+#endif
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR__newselect:
+#endif
+#if defined(__arm__)
+ case __NR_send:
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_sendmsg: // Could specify destination.
+ case __NR_sendto: // Could specify destination.
+#endif
+ case __NR_write:
+ case __NR_writev:
+ return true;
+ case __NR_ioctl: // Can be very powerful.
+ case __NR_pread64:
+ case __NR_preadv:
+ case __NR_pwrite64:
+ case __NR_pwritev:
+ case __NR_recvmmsg: // Could specify source.
+ case __NR_sendfile:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_sendfile64:
+#endif
+ case __NR_sendmmsg: // Could specify destination.
+ case __NR_splice:
+ case __NR_tee:
+ case __NR_vmsplice:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsPrctl(int sysno) {
+ switch (sysno) {
+#if defined(__x86_64__)
+ case __NR_arch_prctl:
+#endif
+ case __NR_prctl:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsSeccomp(int sysno) {
+ switch (sysno) {
+ case __NR_seccomp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAllowedBasicScheduler(int sysno) {
+ switch (sysno) {
+ case __NR_sched_yield:
+#if !defined(__aarch64__)
+ case __NR_pause:
+#endif
+ case __NR_nanosleep:
+ return true;
+ case __NR_getpriority:
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_nice:
+#endif
+ case __NR_setpriority:
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAdminOperation(int sysno) {
+ switch (sysno) {
+#if defined(__i386__) || defined(__arm__) || defined(__mips__)
+ case __NR_bdflush:
+#endif
+ case __NR_kexec_load:
+ case __NR_reboot:
+ case __NR_setdomainname:
+ case __NR_sethostname:
+ case __NR_syslog:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKernelModule(int sysno) {
+ switch (sysno) {
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_create_module:
+ case __NR_get_kernel_syms: // Should ENOSYS.
+ case __NR_query_module:
+#endif
+ case __NR_delete_module:
+ case __NR_init_module:
+ case __NR_finit_module:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalFSViewChange(int sysno) {
+ switch (sysno) {
+ case __NR_pivot_root:
+ case __NR_chroot:
+ case __NR_sync:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsFsControl(int sysno) {
+ switch (sysno) {
+ case __NR_mount:
+ case __NR_nfsservctl:
+ case __NR_quotactl:
+ case __NR_swapoff:
+ case __NR_swapon:
+#if defined(__i386__) || defined(__mips__)
+ case __NR_umount:
+#endif
+ case __NR_umount2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsNuma(int sysno) {
+ switch (sysno) {
+ case __NR_get_mempolicy:
+ case __NR_getcpu:
+ case __NR_mbind:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_migrate_pages:
+#endif
+ case __NR_move_pages:
+ case __NR_set_mempolicy:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsMessageQueue(int sysno) {
+ switch (sysno) {
+ case __NR_mq_getsetattr:
+ case __NR_mq_notify:
+ case __NR_mq_open:
+ case __NR_mq_timedreceive:
+ case __NR_mq_timedsend:
+ case __NR_mq_unlink:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalProcessEnvironment(int sysno) {
+ switch (sysno) {
+ case __NR_acct: // Privileged.
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__)
+ case __NR_getrlimit:
+#endif
+#if defined(__i386__) || defined(__arm__)
+ case __NR_ugetrlimit:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_ulimit:
+#endif
+ case __NR_getrusage:
+ case __NR_personality: // Can change its personality as well.
+ case __NR_prlimit64: // Like setrlimit / getrlimit.
+ case __NR_setrlimit:
+ case __NR_times:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsDebug(int sysno) {
+ switch (sysno) {
+ case __NR_ptrace:
+ case __NR_process_vm_readv:
+ case __NR_process_vm_writev:
+ case __NR_kcmp:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsGlobalSystemStatus(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR__sysctl:
+ case __NR_sysfs:
+#endif
+ case __NR_sysinfo:
+ case __NR_uname:
+#if defined(__i386__)
+ case __NR_olduname:
+ case __NR_oldolduname:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsEventFd(int sysno) {
+ switch (sysno) {
+#if !defined(__aarch64__)
+ case __NR_eventfd:
+#endif
+ case __NR_eventfd2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Asynchronous I/O API.
+bool SyscallSets::IsAsyncIo(int sysno) {
+ switch (sysno) {
+ case __NR_io_cancel:
+ case __NR_io_destroy:
+ case __NR_io_getevents:
+ case __NR_io_setup:
+ case __NR_io_submit:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsKeyManagement(int sysno) {
+ switch (sysno) {
+ case __NR_add_key:
+ case __NR_keyctl:
+ case __NR_request_key:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+bool SyscallSets::IsSystemVSemaphores(int sysno) {
+ switch (sysno) {
+ case __NR_semctl:
+ case __NR_semget:
+ case __NR_semop:
+ case __NR_semtimedop:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+// These give a lot of ambient authority and bypass the setuid sandbox.
+bool SyscallSets::IsSystemVSharedMemory(int sysno) {
+ switch (sysno) {
+ case __NR_shmat:
+ case __NR_shmctl:
+ case __NR_shmdt:
+ case __NR_shmget:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+bool SyscallSets::IsSystemVMessageQueue(int sysno) {
+ switch (sysno) {
+ case __NR_msgctl:
+ case __NR_msgget:
+ case __NR_msgrcv:
+ case __NR_msgsnd:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+#if defined(__i386__) || defined(__mips__)
+// Big system V multiplexing system call.
+bool SyscallSets::IsSystemVIpc(int sysno) {
+ switch (sysno) {
+ case __NR_ipc:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+bool SyscallSets::IsAnySystemV(int sysno) {
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ return IsSystemVMessageQueue(sysno) || IsSystemVSemaphores(sysno) ||
+ IsSystemVSharedMemory(sysno);
+#elif defined(__i386__) || defined(__mips__)
+ return IsSystemVIpc(sysno);
+#endif
+}
+
+bool SyscallSets::IsAdvancedScheduler(int sysno) {
+ switch (sysno) {
+ case __NR_ioprio_get: // IO scheduler.
+ case __NR_ioprio_set:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_getaffinity:
+ case __NR_sched_getattr:
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_rr_get_interval:
+ case __NR_sched_setaffinity:
+ case __NR_sched_setattr:
+ case __NR_sched_setparam:
+ case __NR_sched_setscheduler:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsInotify(int sysno) {
+ switch (sysno) {
+ case __NR_inotify_add_watch:
+#if !defined(__aarch64__)
+ case __NR_inotify_init:
+#endif
+ case __NR_inotify_init1:
+ case __NR_inotify_rm_watch:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsFaNotify(int sysno) {
+ switch (sysno) {
+ case __NR_fanotify_init:
+ case __NR_fanotify_mark:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsTimer(int sysno) {
+ switch (sysno) {
+ case __NR_getitimer:
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_alarm:
+#endif
+ case __NR_setitimer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsAdvancedTimer(int sysno) {
+ switch (sysno) {
+ case __NR_timer_create:
+ case __NR_timer_delete:
+ case __NR_timer_getoverrun:
+ case __NR_timer_gettime:
+ case __NR_timer_settime:
+ case __NR_timerfd_create:
+ case __NR_timerfd_gettime:
+ case __NR_timerfd_settime:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsExtendedAttributes(int sysno) {
+ switch (sysno) {
+ case __NR_fgetxattr:
+ case __NR_flistxattr:
+ case __NR_fremovexattr:
+ case __NR_fsetxattr:
+ case __NR_getxattr:
+ case __NR_lgetxattr:
+ case __NR_listxattr:
+ case __NR_llistxattr:
+ case __NR_lremovexattr:
+ case __NR_lsetxattr:
+ case __NR_removexattr:
+ case __NR_setxattr:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Various system calls that need to be researched.
+// TODO(jln): classify this better.
+bool SyscallSets::IsMisc(int sysno) {
+ switch (sysno) {
+#if !defined(__mips__)
+ case __NR_getrandom:
+#endif
+ case __NR_name_to_handle_at:
+ case __NR_open_by_handle_at:
+ case __NR_perf_event_open:
+ case __NR_syncfs:
+ case __NR_vhangup:
+// The system calls below are not implemented.
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_afs_syscall:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_break:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_getpmsg:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_gtty:
+ case __NR_idle:
+ case __NR_lock:
+ case __NR_mpx:
+ case __NR_prof:
+ case __NR_profil:
+#endif
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+ case __NR_putpmsg:
+#endif
+#if defined(__x86_64__)
+ case __NR_security:
+#endif
+#if defined(__i386__) || defined(__mips__)
+ case __NR_stty:
+#endif
+#if defined(__x86_64__)
+ case __NR_tuxcall:
+#endif
+#if !defined(__aarch64__)
+ case __NR_vserver:
+#endif
+ return true;
+ default:
+ return false;
+ }
+}
+
+#if defined(__arm__)
+bool SyscallSets::IsArmPciConfig(int sysno) {
+ switch (sysno) {
+ case __NR_pciconfig_iobase:
+ case __NR_pciconfig_read:
+ case __NR_pciconfig_write:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsArmPrivate(int sysno) {
+ switch (sysno) {
+ case __ARM_NR_breakpoint:
+ case __ARM_NR_cacheflush:
+ case __ARM_NR_set_tls:
+ case __ARM_NR_usr26:
+ case __ARM_NR_usr32:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // defined(__arm__)
+
+#if defined(__mips__)
+bool SyscallSets::IsMipsPrivate(int sysno) {
+ switch (sysno) {
+ case __NR_cacheflush:
+ case __NR_cachectl:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool SyscallSets::IsMipsMisc(int sysno) {
+ switch (sysno) {
+ case __NR_sysmips:
+ case __NR_unused150:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif // defined(__mips__)
+} // namespace sandbox.
diff --git a/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
new file mode 100644
index 0000000000..5ba6335a95
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf-helpers/syscall_sets.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/sandbox_export.h"
+
+// These are helpers to build seccomp-bpf policies, i.e. policies for a
+// sandbox that reduces the Linux kernel's attack surface. Given their
+// nature, they don't have any clear semantics and are completely
+// "implementation-defined".
+
+namespace sandbox {
+
+class SANDBOX_EXPORT SyscallSets {
+ public:
+ static bool IsKill(int sysno);
+ static bool IsAllowedGettime(int sysno);
+ static bool IsCurrentDirectory(int sysno);
+ static bool IsUmask(int sysno);
+ // System calls that directly access the file system. They might acquire
+ // a new file descriptor or otherwise perform an operation directly
+ // via a path.
+ static bool IsFileSystem(int sysno);
+ static bool IsAllowedFileSystemAccessViaFd(int sysno);
+ static bool IsDeniedFileSystemAccessViaFd(int sysno);
+ static bool IsGetSimpleId(int sysno);
+ static bool IsProcessPrivilegeChange(int sysno);
+ static bool IsProcessGroupOrSession(int sysno);
+ static bool IsAllowedSignalHandling(int sysno);
+ static bool IsAllowedOperationOnFd(int sysno);
+ static bool IsKernelInternalApi(int sysno);
+ // This should be thought through in conjunction with IsFutex().
+ static bool IsAllowedProcessStartOrDeath(int sysno);
+ // It's difficult to restrict those, but there is attack surface here.
+ static bool IsAllowedFutex(int sysno);
+ static bool IsAllowedEpoll(int sysno);
+ static bool IsAllowedGetOrModifySocket(int sysno);
+ static bool IsDeniedGetOrModifySocket(int sysno);
+
+#if defined(__i386__) || defined(__mips__)
+ // Big multiplexing system call for sockets.
+ static bool IsSocketCall(int sysno);
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__mips__) || \
+ defined(__aarch64__)
+ static bool IsNetworkSocketInformation(int sysno);
+#endif
+
+ static bool IsAllowedAddressSpaceAccess(int sysno);
+ static bool IsAllowedGeneralIo(int sysno);
+ static bool IsPrctl(int sysno);
+ static bool IsSeccomp(int sysno);
+ static bool IsAllowedBasicScheduler(int sysno);
+ static bool IsAdminOperation(int sysno);
+ static bool IsKernelModule(int sysno);
+ static bool IsGlobalFSViewChange(int sysno);
+ static bool IsFsControl(int sysno);
+ static bool IsNuma(int sysno);
+ static bool IsMessageQueue(int sysno);
+ static bool IsGlobalProcessEnvironment(int sysno);
+ static bool IsDebug(int sysno);
+ static bool IsGlobalSystemStatus(int sysno);
+ static bool IsEventFd(int sysno);
+ // Asynchronous I/O API.
+ static bool IsAsyncIo(int sysno);
+ static bool IsKeyManagement(int sysno);
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ static bool IsSystemVSemaphores(int sysno);
+#endif
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ // These give a lot of ambient authority and bypass the setuid sandbox.
+ static bool IsSystemVSharedMemory(int sysno);
+#endif
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+ static bool IsSystemVMessageQueue(int sysno);
+#endif
+
+#if defined(__i386__) || defined(__mips__)
+ // Big system V multiplexing system call.
+ static bool IsSystemVIpc(int sysno);
+#endif
+
+ static bool IsAnySystemV(int sysno);
+ static bool IsAdvancedScheduler(int sysno);
+ static bool IsInotify(int sysno);
+ static bool IsFaNotify(int sysno);
+ static bool IsTimer(int sysno);
+ static bool IsAdvancedTimer(int sysno);
+ static bool IsExtendedAttributes(int sysno);
+ static bool IsMisc(int sysno);
+#if defined(__arm__)
+ static bool IsArmPciConfig(int sysno);
+ static bool IsArmPrivate(int sysno);
+#endif // defined(__arm__)
+#if defined(__mips__)
+ static bool IsMipsPrivate(int sysno);
+ static bool IsMipsMisc(int sysno);
+#endif // defined(__mips__)
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SyscallSets);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_HELPERS_SYSCALL_SETS_H_
diff --git a/sandbox/linux/seccomp-bpf/DEPS b/sandbox/linux/seccomp-bpf/DEPS
new file mode 100644
index 0000000000..149c463b06
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+sandbox/linux/bpf_dsl",
+ "+sandbox/linux/services",
+ "+sandbox/linux/system_headers",
+]
diff --git a/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
new file mode 100644
index 0000000000..7736c1506f
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
@@ -0,0 +1,54 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+namespace sandbox {
+
+// This templated class allows building a BPFTesterDelegate from a
+// deprecated-style BPF policy (that is a SyscallEvaluator function pointer,
+// instead of a SandboxBPFPolicy class), specified in |policy_function| and a
+// function pointer to a test in |test_function|.
+// This allows both the policy and the test function to take a pointer to an
+// object of type "Aux" as a parameter. This is used to implement the BPF_TEST
+// macro and should generally not be used directly.
+template <class Policy, class Aux>
+class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
+ public:
+ typedef void (*TestFunction)(Aux*);
+
+ explicit BPFTesterCompatibilityDelegate(TestFunction test_function)
+ : aux_(), test_function_(test_function) {}
+
+ ~BPFTesterCompatibilityDelegate() override {}
+
+ scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ // The current method is guaranteed to only run in the child process
+ // running the test. In this process, the current object is guaranteed
+ // to live forever. So it's ok to pass aux_pointer_for_policy_ to
+ // the policy, which could in turn pass it to the kernel via Trap().
+ return scoped_ptr<bpf_dsl::Policy>(new Policy(&aux_));
+ }
+
+ void RunTestFunction() override {
+ // Run the actual test.
+ // The current object is guaranteed to live forever in the child process
+ // where this will run.
+ test_function_(&aux_);
+ }
+
+ private:
+ Aux aux_;
+ TestFunction test_function_;
+
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterCompatibilityDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h
new file mode 100644
index 0000000000..cc4debd4c3
--- /dev/null
+++ b/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 "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+// BPF_TEST_C() is a special version of SANDBOX_TEST(). It runs a test function
+// in a sub-process, under a seccomp-bpf policy specified in
+// |bpf_policy_class_name| without failing on configurations that are allowed
+// to not support seccomp-bpf in their kernels.
+// This is the preferred format for new BPF tests. |bpf_policy_class_name| is a
+// class name (which will be default-constructed) that implements the
+// Policy interface.
+// The test function's body can simply follow. Test functions should use
+// the BPF_ASSERT macros defined below, not GTEST's macros. The use of
+// CHECK* macros is supported but less robust.
+#define BPF_TEST_C(test_case_name, test_name, bpf_policy_class_name) \
+ BPF_DEATH_TEST_C( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_policy_class_name)
+
+// Identical to BPF_TEST_C but allows to specify the nature of death.
+#define BPF_DEATH_TEST_C( \
+ test_case_name, test_name, death, bpf_policy_class_name) \
+ void BPF_TEST_C_##test_name(); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterSimpleDelegate<bpf_policy_class_name>( \
+ BPF_TEST_C_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_C_##test_name()
+
+// This form of BPF_TEST is a little verbose and should be reserved for complex
+// tests where a lot of control is required.
+// |bpf_tester_delegate_class| must be a classname implementing the
+// BPFTesterDelegate interface.
+#define BPF_TEST_D(test_case_name, test_name, bpf_tester_delegate_class) \
+ BPF_DEATH_TEST_D( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_tester_delegate_class)
+
+// Identical to BPF_TEST_D but allows to specify the nature of death.
+#define BPF_DEATH_TEST_D( \
+ test_case_name, test_name, death, bpf_tester_delegate_class) \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new bpf_tester_delegate_class()); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ }
+
+// Assertions are handled exactly the same as with a normal SANDBOX_TEST()
+#define BPF_ASSERT SANDBOX_ASSERT
+#define BPF_ASSERT_EQ(x, y) BPF_ASSERT((x) == (y))
+#define BPF_ASSERT_NE(x, y) BPF_ASSERT((x) != (y))
+#define BPF_ASSERT_LT(x, y) BPF_ASSERT((x) < (y))
+#define BPF_ASSERT_GT(x, y) BPF_ASSERT((x) > (y))
+#define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y))
+#define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y))
+
+// This form of BPF_TEST is now discouraged (but still allowed) in favor of
+// BPF_TEST_D and BPF_TEST_C.
+// The |policy| parameter should be a Policy subclass.
+// BPF_TEST() takes a C++ data type as an fourth parameter. A variable
+// of this type will be allocated and a pointer to it will be
+// available within the test function as "BPF_AUX". The pointer will
+// also be passed as an argument to the policy's constructor. Policies
+// would typically use it as an argument to SandboxBPF::Trap(), if
+// they want to communicate data between the BPF_TEST() and a Trap()
+// function. The life-time of this object is the same as the life-time
+// of the process running under the seccomp-bpf policy.
+// |aux| must not be void.
+#define BPF_TEST(test_case_name, test_name, policy, aux) \
+ BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux)
+
+// A BPF_DEATH_TEST is just the same as a BPF_TEST, but it assumes that the
+// test will fail with a particular known error condition. Use the DEATH_XXX()
+// macros from unit_tests.h to specify the expected error condition.
+#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux) \
+ void BPF_TEST_##test_name(aux* BPF_AUX); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterCompatibilityDelegate<policy, aux>( \
+ BPF_TEST_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_##test_name(aux* BPF_AUX)
+
+// This class takes a simple function pointer as a constructor parameter and a
+// class name as a template parameter to implement the BPFTesterDelegate
+// interface which can be used to build BPF unittests with
+// the SandboxBPFTestRunner class.
+template <class PolicyClass>
+class BPFTesterSimpleDelegate : public BPFTesterDelegate {
+ public:
+ explicit BPFTesterSimpleDelegate(void (*test_function)(void))
+ : test_function_(test_function) {}
+ ~BPFTesterSimpleDelegate() override {}
+
+ scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return scoped_ptr<bpf_dsl::Policy>(new PolicyClass());
+ }
+ void RunTestFunction() override {
+ DCHECK(test_function_);
+ test_function_();
+ }
+
+ private:
+ void (*test_function_)(void);
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterSimpleDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
diff --git a/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
new file mode 100644
index 0000000000..63e1814c90
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+namespace {
+
+class FourtyTwo {
+ public:
+ static const int kMagicValue = 42;
+ FourtyTwo() : value_(kMagicValue) {}
+ int value() { return value_; }
+
+ private:
+ int value_;
+ DISALLOW_COPY_AND_ASSIGN(FourtyTwo);
+};
+
+class EmptyClassTakingPolicy : public bpf_dsl::Policy {
+ public:
+ explicit EmptyClassTakingPolicy(FourtyTwo* fourty_two) {
+ BPF_ASSERT(fourty_two);
+ BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
+ }
+ ~EmptyClassTakingPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ return Allow();
+ }
+};
+
+BPF_TEST(BPFTest,
+ BPFAUXPointsToClass,
+ EmptyClassTakingPolicy,
+ FourtyTwo /* *BPF_AUX */) {
+ // BPF_AUX should point to an instance of FourtyTwo.
+ BPF_ASSERT(BPF_AUX);
+ BPF_ASSERT(FourtyTwo::kMagicValue == BPF_AUX->value());
+}
+
+void DummyTestFunction(FourtyTwo *fourty_two) {
+}
+
+TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) {
+ // Don't do anything, simply gives dynamic tools an opportunity to detect
+ // leaks.
+ {
+ BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>
+ simple_delegate(DummyTestFunction);
+ }
+ {
+ // Test polymorphism.
+ scoped_ptr<BPFTesterDelegate> simple_delegate(
+ new BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>(
+ DummyTestFunction));
+ }
+}
+
+class EnosysPtracePolicy : public bpf_dsl::Policy {
+ public:
+ EnosysPtracePolicy() { my_pid_ = sys_getpid(); }
+ ~EnosysPtracePolicy() override {
+ // Policies should be able to bind with the process on which they are
+ // created. They should never be created in a parent process.
+ BPF_ASSERT_EQ(my_pid_, sys_getpid());
+ }
+
+ ResultExpr EvaluateSyscall(int system_call_number) const override {
+ CHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
+ if (system_call_number == __NR_ptrace) {
+ // The EvaluateSyscall function should run in the process that created
+ // the current object.
+ BPF_ASSERT_EQ(my_pid_, sys_getpid());
+ return Error(ENOSYS);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ pid_t my_pid_;
+ DISALLOW_COPY_AND_ASSIGN(EnosysPtracePolicy);
+};
+
+class BasicBPFTesterDelegate : public BPFTesterDelegate {
+ public:
+ BasicBPFTesterDelegate() {}
+ ~BasicBPFTesterDelegate() override {}
+
+ scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return scoped_ptr<bpf_dsl::Policy>(new EnosysPtracePolicy());
+ }
+ void RunTestFunction() override {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicBPFTesterDelegate);
+};
+
+// This is the most powerful and complex way to create a BPF test, but it
+// requires a full class definition (BasicBPFTesterDelegate).
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, BasicBPFTesterDelegate);
+
+// This is the simplest form of BPF tests.
+BPF_TEST_C(BPFTest, BPFTestWithInlineTest, EnosysPtracePolicy) {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+}
+
+const char kHelloMessage[] = "Hello";
+
+BPF_DEATH_TEST_C(BPFTest,
+ BPFDeathTestWithInlineTest,
+ DEATH_MESSAGE(kHelloMessage),
+ EnosysPtracePolicy) {
+ LOG(ERROR) << kHelloMessage;
+ _exit(1);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/die.cc b/sandbox/linux/seccomp-bpf/die.cc
new file mode 100644
index 0000000000..3baf1f13d9
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/die.cc
@@ -0,0 +1,93 @@
+// 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 <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace sandbox {
+
+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.
+ Syscall::Call(__NR_exit_group, 1);
+
+ // We have no idea what our run-time environment looks like. So, signal
+ // handlers might or might not do the right thing. Try to reset settings
+ // 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.
+ struct sigaction sa = {};
+ sa.sa_handler = LINUX_SIG_DFL;
+ sa.sa_flags = LINUX_SA_RESTART;
+ sys_sigaction(LINUX_SIGSEGV, &sa, nullptr);
+ Syscall::Call(__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 (;;) {
+ Syscall::Call(__NR_exit_group, 1);
+ }
+}
+
+void Die::SandboxDie(const char* msg, const char* file, int line) {
+ if (simple_exit_) {
+ LogToStderr(msg, file, line);
+ } else {
+ logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
+ }
+ ExitGroup();
+}
+
+void Die::RawSandboxDie(const char* msg) {
+ if (!msg)
+ msg = "";
+ RAW_LOG(FATAL, msg);
+ ExitGroup();
+}
+
+void Die::SandboxInfo(const char* msg, const char* file, int line) {
+ if (!suppress_info_) {
+ logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
+ }
+}
+
+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.
+ ignore_result(
+ HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length())));
+ }
+}
+
+bool Die::simple_exit_ = false;
+bool Die::suppress_info_ = false;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/die.h b/sandbox/linux/seccomp-bpf/die.h
new file mode 100644
index 0000000000..b3f3f72c2f
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/die.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_LINUX_SECCOMP_BPF_DIE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is the main API for using this file. Prints a error message and
+// exits with a fatal error. This is not async-signal safe.
+#define SANDBOX_DIE(m) sandbox::Die::SandboxDie(m, __FILE__, __LINE__)
+
+// An async signal safe version of the same API. Won't print the filename
+// and line numbers.
+#define RAW_SANDBOX_DIE(m) sandbox::Die::RawSandboxDie(m)
+
+// Adds an informational message to the log file or stderr as appropriate.
+#define SANDBOX_INFO(m) sandbox::Die::SandboxInfo(m, __FILE__, __LINE__)
+
+class SANDBOX_EXPORT Die {
+ public:
+ // Terminate the program, even if the current sandbox policy prevents some
+ // of the more commonly used functions used for exiting.
+ // 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));
+
+ static void RawSandboxDie(const char* msg) __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_INFO(). There is normally no reason
+ // to call it directly unless you are defining your own logging macro.
+ static void SandboxInfo(const char* msg, const char* file, int line);
+
+ // 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 sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
diff --git a/sandbox/linux/seccomp-bpf/errorcode.cc b/sandbox/linux/seccomp-bpf/errorcode.cc
new file mode 100644
index 0000000000..9bb3ddb648
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode.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 "sandbox/linux/seccomp-bpf/errorcode.h"
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+
+ErrorCode::ErrorCode() : error_type_(ET_INVALID), err_(SECCOMP_RET_INVALID) {
+}
+
+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:
+ if ((err & ~SECCOMP_RET_DATA) == ERR_TRACE) {
+ err_ = SECCOMP_RET_TRACE + (err & SECCOMP_RET_DATA);
+ error_type_ = ET_SIMPLE;
+ break;
+ }
+ SANDBOX_DIE("Invalid use of ErrorCode object");
+ }
+}
+
+ErrorCode::ErrorCode(uint16_t trap_id,
+ Trap::TrapFnc fnc,
+ const void* aux,
+ bool safe)
+ : error_type_(ET_TRAP),
+ fnc_(fnc),
+ aux_(const_cast<void*>(aux)),
+ safe_(safe),
+ err_(SECCOMP_RET_TRAP + trap_id) {
+}
+
+ErrorCode::ErrorCode(int argno,
+ ArgType width,
+ uint64_t mask,
+ uint64_t value,
+ const ErrorCode* passed,
+ const ErrorCode* failed)
+ : error_type_(ET_COND),
+ mask_(mask),
+ value_(value),
+ argno_(argno),
+ width_(width),
+ passed_(passed),
+ failed_(failed),
+ err_(SECCOMP_RET_INVALID) {
+}
+
+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 mask_ == err.mask_ && value_ == err.value_ && argno_ == err.argno_ &&
+ width_ == err.width_ && 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 (mask_ != err.mask_) {
+ return mask_ < err.mask_;
+ } else 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 (!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 sandbox
diff --git a/sandbox/linux/seccomp-bpf/errorcode.h b/sandbox/linux/seccomp-bpf/errorcode.h
new file mode 100644
index 0000000000..d88777313e
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode.h
@@ -0,0 +1,203 @@
+// 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/trap.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class PolicyCompiler;
+}
+
+// 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.
+//
+// TODO(mdempsky): Nuke from orbit. The only reason this class still
+// exists is for Verifier, which will eventually be replaced by a true
+// BPF symbolic evaluator and constraint solver.
+class SANDBOX_EXPORT 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,
+
+ // If the progress is being ptraced with PTRACE_O_TRACESECCOMP, then the
+ // tracer will be notified of a PTRACE_EVENT_SECCOMP and allowed to change
+ // or skip the system call. The lower 16 bits of err will be available to
+ // the tracer via PTRACE_GETEVENTMSG.
+ ERR_TRACE = 0x08000000,
+
+ // Deny the system call with a particular "errno" value.
+ // N.B.: It is also possible to return "0" here. That would normally
+ // indicate success, but it won't actually run the system call.
+ // This is very different from return ERR_ALLOWED.
+ ERR_MIN_ERRNO = 0,
+#if defined(__mips__)
+ // MIPS only supports errno up to 1133
+ ERR_MAX_ERRNO = 1133,
+#else
+ // TODO(markus): Android only supports errno up to 255
+ // (crbug.com/181647).
+ ERR_MAX_ERRNO = 4095,
+#endif
+ };
+
+ // 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 SandboxBPF::Cond(), TP_32BIT requests that
+ // the conditional test should operate on the 32bit part of the system call
+ // argument.
+ // On 64bit architectures, this verifies that user space did not pass
+ // 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 SandboxBPF::Cond(), TP_64BIT requests that
+ // the conditional test should operate on the full 64bit argument. It is
+ // generally harmless to perform a 64bit test on 32bit systems, as the
+ // kernel will always see the top 32 bits of all arguments as zero'd out.
+ // 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,
+ };
+
+ // Deprecated.
+ enum Operation {
+ // Test whether the system call argument is equal to the operand.
+ OP_EQUAL,
+
+ // 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,
+ };
+
+ 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();
+ 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 mask() const { return mask_; }
+ uint64_t value() const { return value_; }
+ int argno() const { return argno_; }
+ ArgType width() const { return width_; }
+ 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 bpf_dsl::PolicyCompiler;
+ friend class CodeGen;
+ friend class SandboxBPF;
+ friend class Trap;
+
+ // If we are wrapping a callback, we must assign a unique id. This id is
+ // how the kernel tells us which one of our different SECCOMP_RET_TRAP
+ // cases has been triggered.
+ ErrorCode(uint16_t trap_id, Trap::TrapFnc fnc, const void* aux, bool safe);
+
+ // Some system calls require inspection of arguments. This constructor
+ // allows us to specify additional constraints.
+ ErrorCode(int argno,
+ ArgType width,
+ uint64_t mask,
+ 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 mask_; // Mask that we are comparing under.
+ 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.
+ 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 sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_ERRORCODE_H__
diff --git a/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
new file mode 100644
index 0000000000..6b5491ee4a
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/errorcode_unittest.cc
@@ -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.
+
+#include "sandbox/linux/seccomp-bpf/errorcode.h"
+
+#include <errno.h>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+class DummyPolicy : public bpf_dsl::Policy {
+ public:
+ DummyPolicy() {}
+ ~DummyPolicy() override {}
+
+ bpf_dsl::ResultExpr EvaluateSyscall(int sysno) const override {
+ return bpf_dsl::Allow();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DummyPolicy);
+};
+
+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);
+
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e3 = compiler.Trap(NULL, NULL, true /* safe */);
+ SANDBOX_ASSERT((e3.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP);
+
+ uint16_t data = 0xdead;
+ ErrorCode e4(ErrorCode::ERR_TRACE + data);
+ SANDBOX_ASSERT(e4.err() == SECCOMP_RET_TRACE + data);
+}
+
+SANDBOX_DEATH_TEST(ErrorCode,
+ InvalidSeccompRetTrace,
+ DEATH_MESSAGE("Invalid use of ErrorCode object")) {
+ // Should die if the trace data does not fit in 16 bits.
+ ErrorCode e(ErrorCode::ERR_TRACE + (1 << 16));
+}
+
+SANDBOX_TEST(ErrorCode, Trap) {
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e0 = compiler.Trap(NULL, "a", true /* safe */);
+ ErrorCode e1 = compiler.Trap(NULL, "b", true /* safe */);
+ SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 ==
+ (e1.err() & SECCOMP_RET_DATA));
+
+ ErrorCode e2 = compiler.Trap(NULL, "a", true /* safe */);
+ 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));
+
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e4 = compiler.Trap(NULL, "a", true /* safe */);
+ ErrorCode e5 = compiler.Trap(NULL, "b", true /* safe */);
+ ErrorCode e6 = compiler.Trap(NULL, "a", true /* safe */);
+ 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));
+
+ DummyPolicy dummy_policy;
+ bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry());
+ ErrorCode e4 = compiler.Trap(NULL, "a", true /* safe */);
+ ErrorCode e5 = compiler.Trap(NULL, "b", true /* safe */);
+ ErrorCode e6 = compiler.Trap(NULL, "a", true /* safe */);
+ 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
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
new file mode 100644
index 0000000000..239043eb27
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -0,0 +1,279 @@
+// 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"
+
+// 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 <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+
+namespace {
+
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+bool IsSingleThreaded(int proc_fd) {
+ return ThreadHelpers::IsSingleThreaded(proc_fd);
+}
+
+// Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
+// prctl().
+bool KernelSupportsSeccompBPF() {
+ errno = 0;
+ const int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
+
+ if (rv == -1 && EFAULT == errno) {
+ return true;
+ }
+ return false;
+}
+
+// LG introduced a buggy syscall, sys_set_media_ext, with the same number as
+// seccomp. Return true if the current kernel has this buggy syscall.
+//
+// We want this to work with upcoming versions of seccomp, so we pass bogus
+// flags that are unlikely to ever be used by the kernel. A normal kernel would
+// return -EINVAL, but a buggy LG kernel would return 1.
+bool KernelHasLGBug() {
+#if defined(OS_ANDROID)
+ // sys_set_media will see this as NULL, which should be a safe (non-crashing)
+ // way to invoke it. A genuine seccomp syscall will see it as
+ // SECCOMP_SET_MODE_STRICT.
+ const unsigned int operation = 0;
+ // Chosen by fair dice roll. Guaranteed to be random.
+ const unsigned int flags = 0xf7a46a5c;
+ const int rv = sys_seccomp(operation, flags, nullptr);
+ // A genuine kernel would return -EINVAL (which would set rv to -1 and errno
+ // to EINVAL), or at the very least return some kind of error (which would
+ // set rv to -1). Any other behavior indicates that whatever code received
+ // our syscall was not the real seccomp.
+ if (rv != -1) {
+ return true;
+ }
+#endif // defined(OS_ANDROID)
+
+ return false;
+}
+
+// Check if the kernel supports seccomp-filter via the seccomp system call
+// and the TSYNC feature to enable seccomp on all threads.
+bool KernelSupportsSeccompTsync() {
+ if (KernelHasLGBug()) {
+ return false;
+ }
+
+ errno = 0;
+ const int rv =
+ sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr);
+
+ if (rv == -1 && errno == EFAULT) {
+ return true;
+ } else {
+ // TODO(jln): turn these into DCHECK after 417888 is considered fixed.
+ CHECK_EQ(-1, rv);
+ CHECK(ENOSYS == errno || EINVAL == errno);
+ return false;
+ }
+}
+
+uint64_t EscapePC() {
+ intptr_t rv = Syscall::Call(-1);
+ if (rv == -1 && errno == ENOSYS) {
+ return 0;
+ }
+ return static_cast<uint64_t>(static_cast<uintptr_t>(rv));
+}
+
+} // namespace
+
+SandboxBPF::SandboxBPF(bpf_dsl::Policy* policy)
+ : proc_fd_(), sandbox_has_started_(false), policy_(policy) {
+}
+
+SandboxBPF::~SandboxBPF() {
+}
+
+// static
+bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
+ // Never pretend to support seccomp with Valgrind, as it
+ // throws the tool off.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+ switch (level) {
+ case SeccompLevel::SINGLE_THREADED:
+ return KernelSupportsSeccompBPF();
+ case SeccompLevel::MULTI_THREADED:
+ return KernelSupportsSeccompTsync();
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
+ DCHECK(policy_);
+ CHECK(seccomp_level == SeccompLevel::SINGLE_THREADED ||
+ seccomp_level == SeccompLevel::MULTI_THREADED);
+
+ if (sandbox_has_started_) {
+ SANDBOX_DIE(
+ "Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
+ return false;
+ }
+
+ if (!proc_fd_.is_valid()) {
+ SetProcFd(ProcUtil::OpenProc());
+ }
+
+ const bool supports_tsync = KernelSupportsSeccompTsync();
+
+ if (seccomp_level == SeccompLevel::SINGLE_THREADED) {
+ // Wait for /proc/self/task/ to update if needed and assert the
+ // process is single threaded.
+ ThreadHelpers::AssertSingleThreaded(proc_fd_.get());
+ } else if (seccomp_level == SeccompLevel::MULTI_THREADED) {
+ if (IsSingleThreaded(proc_fd_.get())) {
+ SANDBOX_DIE("Cannot start sandbox; "
+ "process may be single-threaded when reported as not");
+ return false;
+ }
+ if (!supports_tsync) {
+ SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
+ "filters for a threadgroup");
+ return false;
+ }
+ }
+
+ // 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_.is_valid()) {
+ proc_fd_.reset();
+ }
+
+ // Install the filters.
+ InstallFilter(supports_tsync ||
+ seccomp_level == SeccompLevel::MULTI_THREADED);
+
+ return true;
+}
+
+void SandboxBPF::SetProcFd(base::ScopedFD proc_fd) {
+ proc_fd_.swap(proc_fd);
+}
+
+// static
+bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
+ return SyscallSet::IsValid(sysnum);
+}
+
+// static
+bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) {
+ return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno);
+}
+
+// static
+intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
+ return Syscall::Call(
+ args.nr, static_cast<intptr_t>(args.args[0]),
+ static_cast<intptr_t>(args.args[1]), static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]), static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
+}
+
+scoped_ptr<CodeGen::Program> SandboxBPF::AssembleFilter(
+ bool force_verification) {
+#if !defined(NDEBUG)
+ force_verification = true;
+#endif
+ DCHECK(policy_);
+
+ bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry());
+ if (Trap::SandboxDebuggingAllowedByUser()) {
+ compiler.DangerousSetEscapePC(EscapePC());
+ }
+ return compiler.Compile(force_verification);
+}
+
+void SandboxBPF::InstallFilter(bool must_sync_threads) {
+ // 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().
+ CodeGen::Program* program = AssembleFilter(false).release();
+
+ 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;
+
+ // Make an attempt to release memory that is no longer needed here, rather
+ // than in the destructor. Try to avoid as much as possible to presume of
+ // what will be possible to do in the new (sandboxed) execution environment.
+ policy_.reset();
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ SANDBOX_DIE("Kernel refuses to enable no-new-privs");
+ }
+
+ // Install BPF filter program. If the thread state indicates multi-threading
+ // support, then the kernel hass the seccomp system call. Otherwise, fall
+ // back on prctl, which requires the process to be single-threaded.
+ if (must_sync_threads) {
+ int rv =
+ sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
+ if (rv) {
+ SANDBOX_DIE(
+ "Kernel refuses to turn on and synchronize threads for BPF filters");
+ }
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ SANDBOX_DIE("Kernel refuses to turn on BPF filters");
+ }
+ }
+
+ sandbox_has_started_ = true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
new file mode 100644
index 0000000000..96cceb5648
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -0,0 +1,118 @@
+// 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 <stdint.h>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+struct arch_seccomp_data;
+namespace bpf_dsl {
+class Policy;
+}
+
+// This class can be used to apply a syscall sandboxing policy expressed in a
+// bpf_dsl::Policy object to the current process.
+// Syscall sandboxing policies get inherited by subprocesses and, once applied,
+// can never be removed for the lifetime of the process.
+class SANDBOX_EXPORT SandboxBPF {
+ public:
+ enum class SeccompLevel {
+ SINGLE_THREADED,
+ MULTI_THREADED,
+ };
+
+ // Ownership of |policy| is transfered here to the sandbox object.
+ // nullptr is allowed for unit tests.
+ explicit SandboxBPF(bpf_dsl::Policy* policy);
+ // 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. The
+ // sandbox remains engaged even when the object is destructed.
+ ~SandboxBPF();
+
+ // Detect if the kernel supports the specified seccomp level.
+ // See StartSandbox() for a description of these.
+ static bool SupportsSeccompSandbox(SeccompLevel level);
+
+ // This is the main public entry point. It sets up the resources needed by
+ // the sandbox, and enters Seccomp mode.
+ // The calling process must provide a |level| to tell the sandbox which type
+ // of kernel support it should engage.
+ // SINGLE_THREADED will only sandbox the calling thread. Since it would be a
+ // security risk, the sandbox will also check that the current process is
+ // single threaded and crash if it isn't the case.
+ // MULTI_THREADED requires more recent kernel support and allows to sandbox
+ // all the threads of the current process. Be mindful of potential races,
+ // with other threads using disallowed system calls either before or after
+ // the sandbox is engaged.
+ //
+ // 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.
+ bool StartSandbox(SeccompLevel level) WARN_UNUSED_RESULT;
+
+ // The sandbox needs to be able to access files in "/proc/self/". If
+ // this directory is not accessible when "StartSandbox()" gets called, the
+ // caller must provide an already opened file descriptor by calling
+ // "SetProcFd()".
+ // The sandbox becomes the new owner of this file descriptor and will
+ // close it when "StartSandbox()" executes or when the sandbox object
+ // disappears.
+ void SetProcFd(base::ScopedFD proc_fd);
+
+ // Checks whether a particular system call number is valid on the current
+ // architecture.
+ static bool IsValidSyscallNumber(int sysnum);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // 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);
+
+ // 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.
+ scoped_ptr<CodeGen::Program> AssembleFilter(bool force_verification);
+
+ private:
+ // Assembles and installs a filter based on the policy that has previously
+ // been configured with SetSandboxPolicy().
+ void InstallFilter(bool must_sync_threads);
+
+ base::ScopedFD proc_fd_;
+ bool sandbox_has_started_;
+ scoped_ptr<bpf_dsl::Policy> policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
new file mode 100644
index 0000000000..321ea9a8ee
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -0,0 +1,65 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+#include <fcntl.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+SandboxBPFTestRunner::SandboxBPFTestRunner(
+ BPFTesterDelegate* bpf_tester_delegate)
+ : bpf_tester_delegate_(bpf_tester_delegate) {
+}
+
+SandboxBPFTestRunner::~SandboxBPFTestRunner() {
+}
+
+void SandboxBPFTestRunner::Run() {
+ DCHECK(bpf_tester_delegate_);
+ sandbox::Die::EnableSimpleExit();
+
+ scoped_ptr<bpf_dsl::Policy> policy =
+ bpf_tester_delegate_->GetSandboxBPFPolicy();
+
+ if (sandbox::SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
+ // Initialize and then start the sandbox with our custom policy
+ sandbox::SandboxBPF sandbox(policy.release());
+ SANDBOX_ASSERT(sandbox.StartSandbox(
+ sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED));
+
+ // Run the actual test.
+ bpf_tester_delegate_->RunTestFunction();
+ } else {
+ printf("This BPF test is not fully running in this configuration!\n");
+ // Android and Valgrind are the only configurations where we accept not
+ // having kernel BPF support.
+ if (!IsAndroid() && !IsRunningOnValgrind()) {
+ const bool seccomp_bpf_is_supported = false;
+ SANDBOX_ASSERT(seccomp_bpf_is_supported);
+ }
+ // Call the compiler and verify the policy. That's the least we can do,
+ // if we don't have kernel support.
+ sandbox::SandboxBPF sandbox(policy.release());
+ sandbox.AssembleFilter(true /* force_verification */);
+ sandbox::UnitTests::IgnoreThisTest();
+ }
+}
+
+bool SandboxBPFTestRunner::ShouldCheckForLeaks() const {
+ // LSAN requires being able to use ptrace() and other system calls that could
+ // be denied.
+ return false;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
new file mode 100644
index 0000000000..fef6240d74
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class Policy;
+}
+
+// To create a SandboxBPFTestRunner object, one needs to implement this
+// interface and pass an instance to the SandboxBPFTestRunner constructor.
+// In the child process running the test, the BPFTesterDelegate object is
+// guaranteed to not be destroyed until the child process terminates.
+class BPFTesterDelegate {
+ public:
+ BPFTesterDelegate() {}
+ virtual ~BPFTesterDelegate() {}
+
+ // This will instanciate a policy suitable for the test we want to run. It is
+ // guaranteed to only be called from the child process that will run the
+ // test.
+ virtual scoped_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() = 0;
+ // This will be called from a child process with the BPF sandbox turned on.
+ virtual void RunTestFunction() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterDelegate);
+};
+
+// This class implements the SandboxTestRunner interface and Run() will
+// initialize a seccomp-bpf sandbox (specified by |bpf_tester_delegate|) and
+// run a test function (via |bpf_tester_delegate|) if the current kernel
+// configuration allows it. If it can not run the test under seccomp-bpf,
+// Run() will still compile the policy which should allow to get some coverage
+// under tools such as Valgrind.
+class SandboxBPFTestRunner : public SandboxTestRunner {
+ public:
+ // This constructor takes ownership of the |bpf_tester_delegate| object.
+ // (It doesn't take a scoped_ptr since they make polymorphism verbose).
+ explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate);
+ ~SandboxBPFTestRunner() override;
+
+ void Run() override;
+
+ bool ShouldCheckForLeaks() const override;
+
+ private:
+ scoped_ptr<BPFTesterDelegate> bpf_tester_delegate_;
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPFTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
new file mode 100644
index 0000000000..580cad2525
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include "base/files/scoped_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+// NOTE: most tests for the SandboxBPF class are currently in
+// integration_tests/.
+
+TEST(SandboxBPF, CreateDestroy) {
+ // Give an opportunity to dynamic tools to perform some simple testing.
+ SandboxBPF sandbox(nullptr);
+ SandboxBPF* sandbox_ptr = new SandboxBPF(nullptr);
+ delete sandbox_ptr;
+}
+
+// This test should execute no matter whether we have kernel support. So,
+// we make it a TEST() instead of a BPF_TEST().
+TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupports)) {
+ // We check that we don't crash, but it's ok if the kernel doesn't
+ // support it.
+ bool seccomp_bpf_supported = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED);
+ bool seccomp_bpf_tsync_supported = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::MULTI_THREADED);
+ // We want to log whether or not seccomp BPF is actually supported
+ // since actual test coverage depends on it.
+ std::cout << "Seccomp BPF supported (single thread): "
+ << (seccomp_bpf_supported ? "true." : "false.") << "\n";
+ std::cout << "Seccomp BPF supported (multi thread): "
+ << (seccomp_bpf_tsync_supported ? "true." : "false.") << "\n";
+ std::cout << "Pointer size: " << sizeof(void*) << "\n";
+}
+
+SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(CallSupportsTwice)) {
+ bool single1 = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED);
+ bool single2 = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED);
+ ASSERT_EQ(single1, single2);
+ bool multi1 = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::MULTI_THREADED);
+ bool multi2 = SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::MULTI_THREADED);
+ ASSERT_EQ(multi1, multi2);
+
+ // Multi threaded support implies single threaded support.
+ if (multi1) {
+ ASSERT_TRUE(single1);
+ }
+}
+
+TEST(SandboxBPF, ProcTaskFdDescriptorGetsClosed) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end(pipe_fds[0]);
+ base::ScopedFD write_end(pipe_fds[1]);
+
+ {
+ SandboxBPF sandbox(nullptr);
+ sandbox.SetProcFd(write_end.Pass());
+ }
+
+ ASSERT_EQ(0, fcntl(read_end.get(), F_SETFL, O_NONBLOCK));
+ char c;
+ // Check that the sandbox closed the write_end (read will EOF instead of
+ // returning EWOULDBLOCK).
+ ASSERT_EQ(0, read(read_end.get(), &c, 1));
+}
+
+} // namespace
+} // sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall.cc b/sandbox/linux/seccomp-bpf/syscall.cc
new file mode 100644
index 0000000000..bc6461f117
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.cc
@@ -0,0 +1,421 @@
+// 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/syscall.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY)
+// Number that's not currently used by any Linux kernel ABIs.
+const int kInvalidSyscallNumber = 0x351d3;
+#else
+#error Unrecognized architecture
+#endif
+
+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; .cfi_rel_offset esi, 0\n"
+ "push %edi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset edi, 0\n"
+ "push %ebx; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebx, 0\n"
+ "push %ebp; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebp, 0\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_restore ebp; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebx; .cfi_restore ebx; .cfi_adjust_cfa_offset -4\n"
+ "pop %edi; .cfi_restore edi; .cfi_adjust_cfa_offset -4\n"
+ "pop %esi; .cfi_restore 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 "%rdi" 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 %rdi, %rdi\n"
+ "jge 1f\n"
+ // Always make sure that our code is position-independent, or the
+ // linker will throw a hissy fit on x86-64.
+ "lea 2f(%rip), %rax\n"
+ "ret\n"
+ // Now we load the registers used to pass arguments to the system
+ // call: system call number in %rax, and arguments in %rdi, %rsi,
+ // %rdx, %r10, %r8, %r9. Note: These are all caller-save registers
+ // (only %rbx, %rbp, %rsp, and %r12-%r15 are callee-save), so no
+ // need to worry here about spilling registers or CFI directives.
+ "1:movq %rdi, %rax\n"
+ "movq 0(%rsi), %rdi\n"
+ "movq 16(%rsi), %rdx\n"
+ "movq 24(%rsi), %r10\n"
+ "movq 32(%rsi), %r8\n"
+ "movq 40(%rsi), %r9\n"
+ "movq 8(%rsi), %rsi\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:\n"
+#if !defined(__native_client_nonsfi__)
+ // .fnstart and .fnend pseudo operations creates unwind table.
+ // It also creates a reference to the symbol __aeabi_unwind_cpp_pr0, which
+ // is not provided by PNaCl toolchain. Disable it.
+ ".fnstart\n"
+#endif
+ "@ 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"
+ ".save {r7, lr}\n"
+ ".cfi_offset 14, -4\n"
+ ".cfi_offset 7, -8\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"
+ "adr r0, 2f\n"
+ "b 2f\n"
+ // We declared (almost) all clobbered registers to the compiler. On
+ // ARM there is no particular register pressure. So, we can go
+ // ahead and directly copy the entries from the arguments array
+ // into the appropriate CPU registers.
+ "1:ldr r5, [r6, #20]\n"
+ "ldr r4, [r6, #16]\n"
+ "ldr r3, [r6, #12]\n"
+ "ldr r2, [r6, #8]\n"
+ "ldr r1, [r6, #4]\n"
+ "mov r7, r0\n"
+ "ldr r0, [r6, #0]\n"
+ // Enter the kernel
+ "swi 0\n"
+// Restore the frame pointer. Also restore the program counter from
+// the link register; this makes us return to the caller.
+#if defined(__thumb__)
+ "2:pop {r7, pc}\n"
+ ".cfi_endproc\n"
+#else
+ "2:ldmfd sp!, {fp, pc}\n"
+#endif
+#if !defined(__native_client_nonsfi__)
+ // Do not use .fnstart and .fnend for PNaCl toolchain. See above comment,
+ // for more details.
+ ".fnend\n"
+#endif
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__mips__)
+ ".text\n"
+ ".align 4\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.ent SyscallAsm\n"
+ ".frame $sp, 40, $ra\n"
+ ".set push\n"
+ ".set noreorder\n"
+ "addiu $sp, $sp, -40\n"
+ "sw $ra, 36($sp)\n"
+ // Check if "v0" 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.
+ "bgez $v0, 1f\n"
+ " nop\n"
+ "la $v0, 2f\n"
+ "b 2f\n"
+ " nop\n"
+ // On MIPS first four arguments go to registers a0 - a3 and any
+ // argument after that goes to stack. We can go ahead and directly
+ // copy the entries from the arguments array into the appropriate
+ // CPU registers and on the stack.
+ "1:lw $a3, 28($a0)\n"
+ "lw $a2, 24($a0)\n"
+ "lw $a1, 20($a0)\n"
+ "lw $t0, 16($a0)\n"
+ "sw $a3, 28($sp)\n"
+ "sw $a2, 24($sp)\n"
+ "sw $a1, 20($sp)\n"
+ "sw $t0, 16($sp)\n"
+ "lw $a3, 12($a0)\n"
+ "lw $a2, 8($a0)\n"
+ "lw $a1, 4($a0)\n"
+ "lw $a0, 0($a0)\n"
+ // Enter the kernel
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ // Restore the return address from the stack.
+ "2:lw $ra, 36($sp)\n"
+ "jr $ra\n"
+ " addiu $sp, $sp, 40\n"
+ ".set pop\n"
+ ".end SyscallAsm\n"
+ ".size SyscallAsm,.-SyscallAsm\n"
+#elif defined(__aarch64__)
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+ "SyscallAsm:\n"
+ ".cfi_startproc\n"
+ "cmp x0, #0\n"
+ "b.ge 1f\n"
+ "adr x0,2f\n"
+ "b 2f\n"
+ "1:ldr x5, [x6, #40]\n"
+ "ldr x4, [x6, #32]\n"
+ "ldr x3, [x6, #24]\n"
+ "ldr x2, [x6, #16]\n"
+ "ldr x1, [x6, #8]\n"
+ "mov x8, x0\n"
+ "ldr x0, [x6, #0]\n"
+ // Enter the kernel
+ "svc 0\n"
+ "2:ret\n"
+ ".cfi_endproc\n"
+ ".size SyscallAsm, .-SyscallAsm\n"
+#endif
+ ); // asm
+
+#if defined(__x86_64__)
+extern "C" {
+intptr_t SyscallAsm(intptr_t nr, const intptr_t args[6]);
+}
+#endif
+
+} // namespace
+
+intptr_t Syscall::InvalidCall() {
+ // Explicitly pass eight zero arguments just in case.
+ return Call(kInvalidSyscallNumber, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+intptr_t Syscall::Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7) {
+ // 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.
+ static_assert(sizeof(void*) == sizeof(intptr_t),
+ "pointer types and intptr_t must be exactly the same size");
+
+ // TODO(nedeljko): Enable use of more than six parameters on architectures
+ // where that makes sense.
+#if defined(__mips__)
+ const intptr_t args[8] = {p0, p1, p2, p3, p4, p5, p6, p7};
+#else
+ DCHECK_EQ(p6, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ DCHECK_EQ(p7, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
+#endif // defined(__mips__)
+
+// Invoke our file-scope assembly code. The constraints have been picked
+// carefully to match what the rest of the assembly code expects in input,
+// output, and clobbered registers.
+#if defined(__i386__)
+ intptr_t ret = nr;
+ asm volatile(
+ "call SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=a"(ret)
+ : "0"(ret), "D"(args)
+ : "cc", "esp", "memory", "ecx", "edx");
+#elif defined(__x86_64__)
+ intptr_t ret = SyscallAsm(nr, args);
+#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)
+ : "cc",
+ "lr",
+ "memory",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5"
+#if !defined(__thumb__)
+ // In thumb mode, we cannot use "r7" as a general purpose register, as
+ // it is our frame pointer. We have to manually manage and preserve
+ // it.
+ // In ARM mode, we have a dedicated frame pointer register and "r7" is
+ // thus available as a general purpose register. We don't preserve it,
+ // but instead mark it as clobbered.
+ ,
+ "r7"
+#endif // !defined(__thumb__)
+ );
+ ret = inout;
+ }
+#elif defined(__mips__)
+ int err_status;
+ intptr_t ret = Syscall::SandboxSyscallRaw(nr, args, &err_status);
+
+ if (err_status) {
+ // On error, MIPS returns errno from syscall instead of -errno.
+ // The purpose of this negation is for SandboxSyscall() to behave
+ // more like it would on other architectures.
+ ret = -ret;
+ }
+#elif defined(__aarch64__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("x0") = nr;
+ register const intptr_t* data __asm__("x6") = args;
+ asm volatile("bl SyscallAsm\n"
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "memory", "x1", "x2", "x3", "x4", "x5", "x8", "x30");
+ ret = inout;
+ }
+
+#else
+#error "Unimplemented architecture"
+#endif
+ return ret;
+}
+
+void Syscall::PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx) {
+#if defined(__mips__)
+ // Mips ABI states that on error a3 CPU register has non zero value and if
+ // there is no error, it should be zero.
+ if (ret_val <= -1 && ret_val >= -4095) {
+ // |ret_val| followes the Syscall::Call() convention of being -errno on
+ // errors. In order to write correct value to return register this sign
+ // needs to be changed back.
+ ret_val = -ret_val;
+ SECCOMP_PARM4(ctx) = 1;
+ } else
+ SECCOMP_PARM4(ctx) = 0;
+#endif
+ SECCOMP_RESULT(ctx) = static_cast<greg_t>(ret_val);
+}
+
+#if defined(__mips__)
+intptr_t Syscall::SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_ret) {
+ register intptr_t ret __asm__("v0") = nr;
+ // a3 register becomes non zero on error.
+ register intptr_t err_stat __asm__("a3") = 0;
+ {
+ register const intptr_t* data __asm__("a0") = args;
+ asm volatile(
+ "la $t9, SyscallAsm\n"
+ "jalr $t9\n"
+ " nop\n"
+ : "=r"(ret), "=r"(err_stat)
+ : "0"(ret),
+ "r"(data)
+ // a2 is in the clober list so inline assembly can not change its
+ // value.
+ : "memory", "ra", "t9", "a2");
+ }
+
+ // Set an error status so it can be used outside of this function
+ *err_ret = err_stat;
+
+ return ret;
+}
+#endif // defined(__mips__)
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/syscall.h b/sandbox/linux/seccomp-bpf/syscall.h
new file mode 100644
index 0000000000..ccfc88dcb3
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall.h
@@ -0,0 +1,166 @@
+// 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 <signal.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This purely static class can be used to perform system calls with some
+// low-level control.
+class SANDBOX_EXPORT Syscall {
+ public:
+ // InvalidCall() invokes Call() with a platform-appropriate syscall
+ // number that is guaranteed to not be implemented (i.e., normally
+ // returns -ENOSYS).
+ // This is primarily meant to be useful for writing sandbox policy
+ // unit tests.
+ static intptr_t InvalidCall();
+
+ // System calls can take up to six parameters (up to eight on some
+ // architectures). Traditionally, glibc
+ // implements this property by using variadic argument lists. This works, but
+ // confuses modern tools such as valgrind, because we are nominally passing
+ // uninitialized data whenever we call through this function and pass less
+ // than the full six arguments.
+ // So, instead, we use C++'s template system to achieve a very similar
+ // effect. C++ automatically sets the unused parameters to zero for us, and
+ // it also does the correct type expansion (e.g. from 32bit to 64bit) where
+ // necessary.
+ // We have to use C-style cast operators as we want to be able to accept both
+ // integer and pointer types.
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6,
+ class T7>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ (intptr_t)p7);
+ }
+
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ 0,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
+ return Call(nr, p0, p1, p2, p3, p4, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2, class T3>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
+ return Call(nr, p0, p1, p2, p3, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2) {
+ return Call(nr, p0, p1, p2, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1) {
+ return Call(nr, p0, p1, 0, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0>
+ static inline intptr_t Call(int nr, T0 p0) {
+ return Call(nr, p0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ static inline intptr_t Call(int nr) {
+ return Call(nr, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ // Set the registers in |ctx| to match what they would be after a system call
+ // returning |ret_val|. |ret_val| must follow the Syscall::Call() convention
+ // of being -errno on errors.
+ static void PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx);
+
+ private:
+ // This performs system call |nr| with the arguments p0 to p7 from a constant
+ // userland address, which is for instance observable by seccomp-bpf filters.
+ // The constant userland address from which these system calls are made will
+ // be returned if |nr| is passed as -1.
+ // On error, this function will return a value between -1 and -4095 which
+ // should be interpreted as -errno.
+ static intptr_t Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7);
+
+#if defined(__mips__)
+ // This function basically does on MIPS what SandboxSyscall() is doing on
+ // other architectures. However, because of specificity of MIPS regarding
+ // handling syscall errors, SandboxSyscall() is made as a wrapper for this
+ // function in order for SandboxSyscall() to behave more like on other
+ // architectures on places where return value from SandboxSyscall() is used
+ // directly (like in most tests).
+ // The syscall "nr" is called with arguments that are set in an array on which
+ // pointer "args" points to and an information weather there is an error or no
+ // is returned to SandboxSyscall() by err_stat.
+ static intptr_t SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_stat);
+#endif // defined(__mips__)
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Syscall);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
diff --git a/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
new file mode 100644
index 0000000000..5fdee6c495
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -0,0 +1,240 @@
+// 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/syscall.h"
+
+#include <asm/unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::Trap;
+
+namespace sandbox {
+
+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, InvalidCallReturnsENOSYS) {
+ EXPECT_EQ(-ENOSYS, Syscall::InvalidCall());
+}
+
+TEST(Syscall, WellKnownEntryPoint) {
+// Test that Syscall::Call(-1) is handled specially. Don't do this on ARM,
+// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+// are still testing ARM code in the next set of tests.
+#if !defined(__arm__) && !defined(__aarch64__)
+ EXPECT_NE(Syscall::Call(-1), syscall(-1));
+#endif
+
+// If possible, test that Syscall::Call(-1) returns the address right
+// after
+// a kernel entry point.
+#if defined(__i386__)
+ EXPECT_EQ(0x80CDu, ((uint16_t*)Syscall::Call(-1))[-1]); // INT 0x80
+#elif defined(__x86_64__)
+ EXPECT_EQ(0x050Fu, ((uint16_t*)Syscall::Call(-1))[-1]); // SYSCALL
+#elif defined(__arm__)
+#if defined(__thumb__)
+ EXPECT_EQ(0xDF00u, ((uint16_t*)Syscall::Call(-1))[-1]); // SWI 0
+#else
+ EXPECT_EQ(0xEF000000u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#endif
+#elif defined(__mips__)
+ // Opcode for MIPS sycall is in the lower 16-bits
+ EXPECT_EQ(0x0cu, (((uint32_t*)Syscall::Call(-1))[-1]) & 0x0000FFFF);
+#elif defined(__aarch64__)
+ EXPECT_EQ(0xD4000001u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#else
+#warning Incomplete test case; need port for target platform
+#endif
+}
+
+TEST(Syscall, TrivialSyscallNoArgs) {
+ // Test that we can do basic system calls
+ EXPECT_EQ(Syscall::Call(__NR_getpid), syscall(__NR_getpid));
+}
+
+TEST(Syscall, TrivialSyscallOneArg) {
+ int new_fd;
+ // Duplicate standard error and close it.
+ ASSERT_GE(new_fd = Syscall::Call(__NR_dup, 2), 0);
+ int close_return_value = IGNORE_EINTR(Syscall::Call(__NR_close, new_fd));
+ ASSERT_EQ(close_return_value, 0);
+}
+
+TEST(Syscall, TrivialFailingSyscall) {
+ errno = -42;
+ int ret = Syscall::Call(__NR_dup, -1);
+ ASSERT_EQ(-EBADF, ret);
+ // Verify that Syscall::Call does not touch errno.
+ ASSERT_EQ(-42, errno);
+}
+
+// SIGSYS trap handler that will be called on __NR_uname.
+intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
+ // |aux| is our BPF_AUX pointer.
+ std::vector<uint64_t>* const seen_syscall_args =
+ static_cast<std::vector<uint64_t>*>(aux);
+ BPF_ASSERT(arraysize(args.args) == 6);
+ seen_syscall_args->assign(args.args, args.args + arraysize(args.args));
+ return -ENOMEM;
+}
+
+class CopyAllArgsOnUnamePolicy : public bpf_dsl::Policy {
+ public:
+ explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
+ ~CopyAllArgsOnUnamePolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return Trap(CopySyscallArgsToAux, aux_);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ std::vector<uint64_t>* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyAllArgsOnUnamePolicy);
+};
+
+// We are testing Syscall::Call() by making use of a BPF filter that
+// allows us
+// to inspect the system call arguments that the kernel saw.
+BPF_TEST(Syscall,
+ SyntheticSixArgs,
+ CopyAllArgsOnUnamePolicy,
+ std::vector<uint64_t> /* (*BPF_AUX) */) {
+ 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(Syscall::Call(__NR_uname,
+ syscall_args[0],
+ syscall_args[1],
+ syscall_args[2],
+ syscall_args[3],
+ syscall_args[4],
+ syscall_args[5]) == -ENOMEM);
+
+ // We expect the trap handler to have copied the 6 arguments.
+ BPF_ASSERT(BPF_AUX->size() == 6);
+
+ // 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 = Syscall::Call(__NR_openat, AT_FDCWD, "/dev/null", O_RDWR, 0L));
+
+ // Use mmap() to allocate some read-only memory
+ char* addr0;
+ ASSERT_NE(
+ (char*)NULL,
+ addr0 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 4096,
+ PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ fd,
+ 0L)));
+
+ // Try to replace the existing mapping with a read-write mapping
+ char* addr1;
+ ASSERT_EQ(addr0,
+ addr1 = reinterpret_cast<char*>(
+ Syscall::Call(kMMapNr,
+ addr0,
+ 4096L,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ fd,
+ 0L)));
+ ++*addr1; // This should not seg fault
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+
+ // Check that the offset argument (i.e. the sixth argument) is processed
+ // correctly.
+ ASSERT_GE(
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/proc/self/exe", O_RDONLY, 0L),
+ 0);
+ char* addr2, *addr3;
+ ASSERT_NE((char*)NULL,
+ addr2 = reinterpret_cast<char*>(Syscall::Call(
+ kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
+ ASSERT_NE((char*)NULL,
+ addr3 = reinterpret_cast<char*>(Syscall::Call(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, Syscall::Call(__NR_read, fd, buf, 8192L));
+ EXPECT_EQ(0, memcmp(addr2, buf, 8192));
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 8192L));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, 4096L));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/trap.cc b/sandbox/linux/seccomp-bpf/trap.cc
new file mode 100644
index 0000000000..8f559e53b1
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.cc
@@ -0,0 +1,390 @@
+// 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/trap.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace {
+
+struct arch_sigsys {
+ void* ip;
+ int nr;
+ unsigned int arch;
+};
+
+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), LINUX_SIGBUS);
+}
+
+void SetIsInSigHandler() {
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGBUS) ||
+ sandbox::sys_sigprocmask(LINUX_SIG_BLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to block SIGBUS");
+ }
+}
+
+bool IsDefaultSignalAction(const struct sigaction& sa) {
+ if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+Trap::Trap()
+ : trap_array_(NULL),
+ trap_array_size_(0),
+ trap_array_capacity_(0),
+ has_unsafe_traps_(false) {
+ // Set new SIGSYS handler
+ struct sigaction sa = {};
+ // In some toolchain, sa_sigaction is not declared in struct sigaction.
+ // So, here cast the pointer to the sa_handler's type. This works because
+ // |sa_handler| and |sa_sigaction| shares the same memory.
+ sa.sa_handler = reinterpret_cast<void (*)(int)>(SigSysAction);
+ sa.sa_flags = LINUX_SA_SIGINFO | LINUX_SA_NODEFER;
+ struct sigaction old_sa = {};
+ if (sys_sigaction(LINUX_SIGSYS, &sa, &old_sa) < 0) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+
+ if (!IsDefaultSignalAction(old_sa)) {
+ static const char kExistingSIGSYSMsg[] =
+ "Existing signal handler when trying to install SIGSYS. SIGSYS needs "
+ "to be reserved for seccomp-bpf.";
+ DLOG(FATAL) << kExistingSIGSYSMsg;
+ LOG(ERROR) << kExistingSIGSYSMsg;
+ }
+
+ // Unmask SIGSYS
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGSYS) ||
+ sys_sigprocmask(LINUX_SIG_UNBLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+}
+
+bpf_dsl::TrapRegistry* Trap::Registry() {
+ // 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, LinuxSigInfo* info, void* void_context) {
+ if (info) {
+ MSAN_UNPOISON(info, sizeof(*info));
+ }
+
+ // 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);
+ if (ctx) {
+ MSAN_UNPOISON(ctx, sizeof(*ctx));
+ }
+
+ if (!global_trap_) {
+ RAW_SANDBOX_DIE(
+ "This can't happen. Found no global singleton instance "
+ "for Trap() handling.");
+ }
+ global_trap_->SigSys(nr, info, ctx);
+}
+
+void Trap::SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx) {
+ // Signal handlers should always preserve "errno". Otherwise, we could
+ // trigger really subtle bugs.
+ const int old_errno = errno;
+
+ // 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 != LINUX_SIGSYS || info->si_code != SYS_SECCOMP || !ctx ||
+ info->si_errno <= 0 ||
+ static_cast<size_t>(info->si_errno) > trap_array_size_) {
+ // ATI drivers seem to send SIGSYS, so this cannot be FATAL.
+ // See crbug.com/178166.
+ // TODO(jln): add a DCHECK or move back to FATAL.
+ RAW_LOG(ERROR, "Unexpected SIGSYS received.");
+ errno = old_errno;
+ return;
+ }
+
+
+ // 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));
+
+#if defined(__mips__)
+ // When indirect syscall (syscall(__NR_foo, ...)) is made on Mips, the
+ // number in register SECCOMP_SYSCALL(ctx) is always __NR_syscall and the
+ // real number of a syscall (__NR_foo) is in SECCOMP_PARM1(ctx)
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) &&
+ sigsys.nr != static_cast<int>(SECCOMP_PARM1(ctx));
+#else
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx));
+#endif
+
+ // Some more sanity checks.
+ if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
+ sigsys_nr_is_bad || sigsys.arch != SECCOMP_ARCH) {
+ // TODO(markus):
+ // 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.
+ RAW_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) {
+ RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
+ }
+#if defined(__mips__)
+ // Mips supports up to eight arguments for syscall.
+ // However, seccomp bpf can filter only up to six arguments, so using eight
+ // arguments has sense only when using UnsafeTrap() handler.
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx),
+ SECCOMP_PARM7(ctx),
+ SECCOMP_PARM8(ctx));
+#else
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx));
+#endif // defined(__mips__)
+ } else {
+ const TrapKey& trap = trap_array_[info->si_errno - 1];
+ if (!trap.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 = {
+ static_cast<int>(SECCOMP_SYSCALL(ctx)),
+ 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 = trap.fnc(data, const_cast<void*>(trap.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.
+ Syscall::PutValueInUcontext(rc, ctx);
+ 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;
+ }
+}
+
+uint16_t Trap::Add(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 Syscall::Call(-1)).
+
+ // This SANDBOX_DIE() can optionally be removed. It won't break security,
+ // but it might make error messages from the BPF compiler a little harder
+ // to understand. Removing the SANDBOX_DIE() allows callers to easily 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 0;
+ }
+
+ // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
+ // of a SECCOMP_RET_TRAP.
+ TrapKey key(fnc, aux, safe);
+
+ // 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_.
+
+ TrapIds::const_iterator iter = trap_ids_.find(key);
+ if (iter != trap_ids_.end()) {
+ // We have seen this pair before. Return the same id that we assigned
+ // earlier.
+ return iter->second;
+ }
+
+ // 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<uint16_t>::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");
+ }
+
+ // 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 live 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;
+ TrapKey* old_trap_array = trap_array_;
+ TrapKey* new_trap_array = new TrapKey[trap_array_capacity_];
+ std::copy_n(old_trap_array, trap_array_size_, new_trap_array);
+
+ // 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.
+ //
+ // TODO(mdempsky): Try to clean this up using base/atomicops or C++11
+ // atomics; see crbug.com/414363.
+ 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;
+ }
+
+ uint16_t id = trap_array_size_ + 1;
+ trap_ids_[key] = id;
+ trap_array_[trap_array_size_] = key;
+ trap_array_size_++;
+ return id;
+}
+
+bool Trap::SandboxDebuggingAllowedByUser() {
+ const char* debug_flag = getenv(kSandboxDebuggingEnv);
+ return debug_flag && *debug_flag;
+}
+
+bool Trap::EnableUnsafeTraps() {
+ if (!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 (SandboxDebuggingAllowedByUser()) {
+ // We only ever print this message once, when we enable unsafe traps the
+ // first time.
+ SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
+ 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 has_unsafe_traps_;
+}
+
+Trap* Trap::global_trap_;
+
+} // namespace sandbox
diff --git a/sandbox/linux/seccomp-bpf/trap.h b/sandbox/linux/seccomp-bpf/trap.h
new file mode 100644
index 0000000000..50ac3fd1c3
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap.h
@@ -0,0 +1,85 @@
+// 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 <stdint.h>
+
+#include <map>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// 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 SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry {
+ public:
+ uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override;
+
+ bool EnableUnsafeTraps() override;
+
+ // Registry returns the trap registry used by Trap's SIGSYS handler,
+ // creating it if necessary.
+ static bpf_dsl::TrapRegistry* Registry();
+
+ // SandboxDebuggingAllowedByUser returns whether the
+ // "CHROME_SANDBOX_DEBUGGING" environment variable is set.
+ static bool SandboxDebuggingAllowedByUser();
+
+ private:
+ struct TrapKey {
+ TrapKey() : fnc(NULL), aux(NULL), safe(false) {}
+ 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;
+
+ // Our constructor is private. A shared global instance is created
+ // automatically as needed.
+ Trap();
+
+ // The destructor is unimplemented as destroying this object would
+ // break subsequent system calls that trigger a SIGSYS.
+ ~Trap() = delete;
+
+ static void SigSysAction(int nr, LinuxSigInfo* info, void* void_context);
+
+ // Make sure that SigSys is not inlined in order to get slightly better crash
+ // dumps.
+ void SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx)
+ __attribute__((noinline));
+ // 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
+ TrapKey* trap_array_; // Array of TrapKeys 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
+
+ // Copying and assigning is unimplemented. It doesn't make sense for a
+ // singleton.
+ DISALLOW_COPY_AND_ASSIGN(Trap);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
diff --git a/sandbox/linux/seccomp-bpf/trap_unittest.cc b/sandbox/linux/seccomp-bpf/trap_unittest.cc
new file mode 100644
index 0000000000..99f94bfb3a
--- /dev/null
+++ b/sandbox/linux/seccomp-bpf/trap_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2015 The Chromium 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/trap.h"
+
+#include <signal.h>
+
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+namespace {
+
+SANDBOX_TEST_ALLOW_NOISE(Trap, SigSysAction) {
+ // This creates a global Trap instance, and registers the signal handler
+ // (Trap::SigSysAction).
+ Trap::Registry();
+
+ // Send SIGSYS to self. If signal handler (SigSysAction) is not registered,
+ // the process will be terminated with status code -SIGSYS.
+ // Note that, SigSysAction handler would output an error message
+ // "Unexpected SIGSYS received." so it is necessary to allow the noise.
+ raise(SIGSYS);
+}
+
+} // namespace
+} // namespace sandbox
diff --git a/sandbox/linux/services/DEPS b/sandbox/linux/services/DEPS
new file mode 100644
index 0000000000..70d9b18aa1
--- /dev/null
+++ b/sandbox/linux/services/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/system_headers",
+]
diff --git a/sandbox/linux/services/credentials.cc b/sandbox/linux/services/credentials.cc
new file mode 100644
index 0000000000..35bb4dcbd7
--- /dev/null
+++ b/sandbox/linux/services/credentials.cc
@@ -0,0 +1,299 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/credentials.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/template_util.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/namespace_utils.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace sandbox {
+
+namespace {
+
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+// Checks that the set of RES-uids and the set of RES-gids have
+// one element each and return that element in |resuid| and |resgid|
+// respectively. It's ok to pass NULL as one or both of the ids.
+bool GetRESIds(uid_t* resuid, gid_t* resgid) {
+ uid_t ruid, euid, suid;
+ gid_t rgid, egid, sgid;
+ PCHECK(sys_getresuid(&ruid, &euid, &suid) == 0);
+ PCHECK(sys_getresgid(&rgid, &egid, &sgid) == 0);
+ const bool uids_are_equal = (ruid == euid) && (ruid == suid);
+ const bool gids_are_equal = (rgid == egid) && (rgid == sgid);
+ if (!uids_are_equal || !gids_are_equal) return false;
+ if (resuid) *resuid = euid;
+ if (resgid) *resgid = egid;
+ return true;
+}
+
+const int kExitSuccess = 0;
+
+int ChrootToSelfFdinfo(void*) {
+ RAW_CHECK(sys_chroot("/proc/self/fdinfo/") == 0);
+
+ // CWD is essentially an implicit file descriptor, so be careful to not
+ // leave it behind.
+ RAW_CHECK(chdir("/") == 0);
+ _exit(kExitSuccess);
+}
+
+// chroot() to an empty dir that is "safe". To be safe, it must not contain
+// any subdirectory (chroot-ing there would allow a chroot escape) and it must
+// be impossible to create an empty directory there.
+// We achieve this by doing the following:
+// 1. We create a new process sharing file system information.
+// 2. In the child, we chroot to /proc/self/fdinfo/
+// This is already "safe", since fdinfo/ does not contain another directory and
+// one cannot create another directory there.
+// 3. The process dies
+// After (3) happens, the directory is not available anymore in /proc.
+bool ChrootToSafeEmptyDir() {
+ // We need to chroot to a fdinfo that is unique to a process and have that
+ // process die.
+ // 1. We don't want to simply fork() because duplicating the page tables is
+ // slow with a big address space.
+ // 2. We do not use a regular thread (that would unshare CLONE_FILES) because
+ // when we are in a PID namespace, we cannot easily get a handle to the
+ // /proc/tid directory for the thread (since /proc may not be aware of the
+ // PID namespace). With a process, we can just use /proc/self.
+ pid_t pid = -1;
+ char stack_buf[PTHREAD_STACK_MIN];
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY)
+ // The stack grows downward.
+ void* stack = stack_buf + sizeof(stack_buf);
+#else
+#error "Unsupported architecture"
+#endif
+
+ pid = clone(ChrootToSelfFdinfo, stack,
+ CLONE_VM | CLONE_VFORK | CLONE_FS | LINUX_SIGCHLD, nullptr,
+ nullptr, nullptr, nullptr);
+ PCHECK(pid != -1);
+
+ int status = -1;
+ PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+
+ return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess;
+}
+
+// CHECK() that an attempt to move to a new user namespace raised an expected
+// errno.
+void CheckCloneNewUserErrno(int error) {
+ // EPERM can happen if already in a chroot. EUSERS if too many nested
+ // namespaces are used. EINVAL for kernels that don't support the feature.
+ // Valgrind will ENOSYS unshare().
+ PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
+ error == ENOSYS);
+}
+
+// Converts a Capability to the corresponding Linux CAP_XXX value.
+int CapabilityToKernelValue(Credentials::Capability cap) {
+ switch (cap) {
+ case Credentials::Capability::SYS_CHROOT:
+ return CAP_SYS_CHROOT;
+ case Credentials::Capability::SYS_ADMIN:
+ return CAP_SYS_ADMIN;
+ }
+
+ LOG(FATAL) << "Invalid Capability: " << static_cast<int>(cap);
+ return 0;
+}
+
+} // namespace.
+
+// static
+bool Credentials::DropAllCapabilities(int proc_fd) {
+ if (!SetCapabilities(proc_fd, std::vector<Capability>())) {
+ return false;
+ }
+
+ CHECK(!HasAnyCapability());
+ return true;
+}
+
+// static
+bool Credentials::DropAllCapabilities() {
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+ return Credentials::DropAllCapabilities(proc_fd.get());
+}
+
+// static
+bool Credentials::DropAllCapabilitiesOnCurrentThread() {
+ return SetCapabilitiesOnCurrentThread(std::vector<Capability>());
+}
+
+// static
+bool Credentials::SetCapabilitiesOnCurrentThread(
+ const std::vector<Capability>& caps) {
+ struct cap_hdr hdr = {};
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}};
+
+ // Initially, cap has no capability flags set. Enable the effective and
+ // permitted flags only for the requested capabilities.
+ for (const Capability cap : caps) {
+ const int cap_num = CapabilityToKernelValue(cap);
+ const size_t index = CAP_TO_INDEX(cap_num);
+ const uint32_t mask = CAP_TO_MASK(cap_num);
+ data[index].effective |= mask;
+ data[index].permitted |= mask;
+ }
+
+ return sys_capset(&hdr, data) == 0;
+}
+
+// static
+bool Credentials::SetCapabilities(int proc_fd,
+ const std::vector<Capability>& caps) {
+ DCHECK_LE(0, proc_fd);
+
+#if !defined(THREAD_SANITIZER)
+ // With TSAN, accept to break the security model as it is a testing
+ // configuration.
+ CHECK(ThreadHelpers::IsSingleThreaded(proc_fd));
+#endif
+
+ return SetCapabilitiesOnCurrentThread(caps);
+}
+
+bool Credentials::HasAnyCapability() {
+ struct cap_hdr hdr = {};
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}};
+
+ PCHECK(sys_capget(&hdr, data) == 0);
+
+ for (size_t i = 0; i < arraysize(data); ++i) {
+ if (data[i].effective || data[i].permitted || data[i].inheritable) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Credentials::HasCapability(Capability cap) {
+ struct cap_hdr hdr = {};
+ hdr.version = _LINUX_CAPABILITY_VERSION_3;
+ struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}};
+
+ PCHECK(sys_capget(&hdr, data) == 0);
+
+ const int cap_num = CapabilityToKernelValue(cap);
+ const size_t index = CAP_TO_INDEX(cap_num);
+ const uint32_t mask = CAP_TO_MASK(cap_num);
+
+ return (data[index].effective | data[index].permitted |
+ data[index].inheritable) &
+ mask;
+}
+
+// static
+bool Credentials::CanCreateProcessInNewUserNS() {
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+ // so always consider UserNS unsupported there.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+#if defined(THREAD_SANITIZER)
+ // With TSAN, processes will always have threads running and can never
+ // enter a new user namespace with MoveToNewUserNS().
+ return false;
+#endif
+
+ // This is roughly a fork().
+ const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0);
+
+ if (pid == -1) {
+ CheckCloneNewUserErrno(errno);
+ return false;
+ }
+
+ // The parent process could have had threads. In the child, these threads
+ // have disappeared. Make sure to not do anything in the child, as this is a
+ // fragile execution environment.
+ if (pid == 0) {
+ _exit(kExitSuccess);
+ }
+
+ // Always reap the child.
+ int status = -1;
+ PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kExitSuccess, WEXITSTATUS(status));
+
+ // clone(2) succeeded, we can use CLONE_NEWUSER.
+ return true;
+}
+
+bool Credentials::MoveToNewUserNS() {
+ uid_t uid;
+ gid_t gid;
+ if (!GetRESIds(&uid, &gid)) {
+ // If all the uids (or gids) are not equal to each other, the security
+ // model will most likely confuse the caller, abort.
+ DVLOG(1) << "uids or gids differ!";
+ return false;
+ }
+ int ret = sys_unshare(CLONE_NEWUSER);
+ if (ret) {
+ const int unshare_errno = errno;
+ VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available "
+ << "on this kernel.";
+ CheckCloneNewUserErrno(unshare_errno);
+ return false;
+ }
+
+ if (NamespaceUtils::KernelSupportsDenySetgroups()) {
+ PCHECK(NamespaceUtils::DenySetgroups());
+ }
+
+ // The current {r,e,s}{u,g}id is now an overflow id (c.f.
+ // /proc/sys/kernel/overflowuid). Setup the uid and gid maps.
+ DCHECK(GetRESIds(NULL, NULL));
+ const char kGidMapFile[] = "/proc/self/gid_map";
+ const char kUidMapFile[] = "/proc/self/uid_map";
+ PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid));
+ PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid));
+ DCHECK(GetRESIds(NULL, NULL));
+ return true;
+}
+
+bool Credentials::DropFileSystemAccess(int proc_fd) {
+ CHECK_LE(0, proc_fd);
+
+ CHECK(ChrootToSafeEmptyDir());
+ CHECK(!base::DirectoryExists(base::FilePath("/proc")));
+ CHECK(!ProcUtil::HasOpenDirectory(proc_fd));
+ // We never let this function fail.
+ return true;
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/credentials.h b/sandbox/linux/services/credentials.h
new file mode 100644
index 0000000000..0001dc7328
--- /dev/null
+++ b/sandbox/linux/services/credentials.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
+#define SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
+
+#include "build/build_config.h"
+// Link errors are tedious to track, raise a compile-time error instead.
+#if defined(OS_ANDROID)
+#error "Android is not supported."
+#endif // defined(OS_ANDROID).
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This class should be used to manipulate the current process' credentials.
+// It is currently a stub used to manipulate POSIX.1e capabilities as
+// implemented by the Linux kernel.
+class SANDBOX_EXPORT Credentials {
+ public:
+ // For brevity, we only expose enums for the subset of capabilities we use.
+ // This can be expanded as the need arises.
+ enum class Capability {
+ SYS_CHROOT,
+ SYS_ADMIN,
+ };
+
+ // Drop all capabilities in the effective, inheritable and permitted sets for
+ // the current thread. For security reasons, since capabilities are
+ // per-thread, the caller is responsible for ensuring it is single-threaded
+ // when calling this API.
+ // |proc_fd| must be a file descriptor to /proc/ and remains owned by
+ // the caller.
+ static bool DropAllCapabilities(int proc_fd) WARN_UNUSED_RESULT;
+ // A similar API which assumes that it can open /proc/self/ by itself.
+ static bool DropAllCapabilities() WARN_UNUSED_RESULT;
+ // Sets the effective and permitted capability sets for the current thread to
+ // the list of capabiltiies in |caps|. All other capability flags are cleared.
+ static bool SetCapabilities(int proc_fd,
+ const std::vector<Capability>& caps)
+ WARN_UNUSED_RESULT;
+
+ // Versions of the above functions which do not check that the process is
+ // single-threaded. After calling these functions, capabilities of other
+ // threads will not be changed. This is dangerous, do not use unless you nkow
+ // what you are doing.
+ static bool DropAllCapabilitiesOnCurrentThread() WARN_UNUSED_RESULT;
+ static bool SetCapabilitiesOnCurrentThread(
+ const std::vector<Capability>& caps) WARN_UNUSED_RESULT;
+
+ // Returns true if the current thread has either the effective, permitted, or
+ // inheritable flag set for the given capability.
+ static bool HasCapability(Capability cap);
+
+ // Return true iff there is any capability in any of the capabilities sets
+ // of the current thread.
+ static bool HasAnyCapability();
+
+ // Returns whether the kernel supports CLONE_NEWUSER and whether it would be
+ // possible to immediately move to a new user namespace. There is no point
+ // in using this method right before calling MoveToNewUserNS(), simply call
+ // MoveToNewUserNS() immediately. This method is only useful to test the
+ // ability to move to a user namespace ahead of time.
+ static bool CanCreateProcessInNewUserNS();
+
+ // Move the current process to a new "user namespace" as supported by Linux
+ // 3.8+ (CLONE_NEWUSER).
+ // The uid map will be set-up so that the perceived uid and gid will not
+ // change.
+ // If this call succeeds, the current process will be granted a full set of
+ // capabilities in the new namespace.
+ // This will fail if the process is not mono-threaded.
+ static bool MoveToNewUserNS() WARN_UNUSED_RESULT;
+
+ // Remove the ability of the process to access the file system. File
+ // descriptors which are already open prior to calling this API remain
+ // available.
+ // The implementation currently uses chroot(2) and requires CAP_SYS_CHROOT.
+ // CAP_SYS_CHROOT can be acquired by using the MoveToNewUserNS() API.
+ // |proc_fd| must be a file descriptor to /proc/ and must be the only open
+ // directory file descriptor of the process.
+ //
+ // CRITICAL:
+ // - the caller must close |proc_fd| eventually or access to the file
+ // system can be recovered.
+ // - DropAllCapabilities() must be called to prevent escapes.
+ static bool DropFileSystemAccess(int proc_fd) WARN_UNUSED_RESULT;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Credentials);
+};
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_CREDENTIALS_H_
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
new file mode 100644
index 0000000000..6b93c86c3e
--- /dev/null
+++ b/sandbox/linux/services/credentials_unittest.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/credentials.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+struct CapFreeDeleter {
+ inline void operator()(cap_t cap) const {
+ int ret = cap_free(cap);
+ CHECK_EQ(0, ret);
+ }
+};
+
+// Wrapper to manage libcap2's cap_t type.
+typedef scoped_ptr<typeof(*((cap_t)0)), CapFreeDeleter> ScopedCap;
+
+bool WorkingDirectoryIsRoot() {
+ char current_dir[PATH_MAX];
+ char* cwd = getcwd(current_dir, sizeof(current_dir));
+ PCHECK(cwd);
+ if (strcmp("/", cwd)) return false;
+
+ // The current directory is the root. Add a few paranoid checks.
+ struct stat current;
+ CHECK_EQ(0, stat(".", &current));
+ struct stat parrent;
+ CHECK_EQ(0, stat("..", &parrent));
+ CHECK_EQ(current.st_dev, parrent.st_dev);
+ CHECK_EQ(current.st_ino, parrent.st_ino);
+ CHECK_EQ(current.st_mode, parrent.st_mode);
+ CHECK_EQ(current.st_uid, parrent.st_uid);
+ CHECK_EQ(current.st_gid, parrent.st_gid);
+ return true;
+}
+
+SANDBOX_TEST(Credentials, DropAllCaps) {
+ CHECK(Credentials::DropAllCapabilities());
+ CHECK(!Credentials::HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, MoveToNewUserNS) {
+ CHECK(Credentials::DropAllCapabilities());
+ bool moved_to_new_ns = Credentials::MoveToNewUserNS();
+ fprintf(stdout,
+ "Unprivileged CLONE_NEWUSER supported: %s\n",
+ moved_to_new_ns ? "true." : "false.");
+ fflush(stdout);
+ if (!moved_to_new_ns) {
+ fprintf(stdout, "This kernel does not support unprivileged namespaces. "
+ "USERNS tests will succeed without running.\n");
+ fflush(stdout);
+ return;
+ }
+ CHECK(Credentials::HasAnyCapability());
+ CHECK(Credentials::DropAllCapabilities());
+ CHECK(!Credentials::HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, CanCreateProcessInNewUserNS) {
+ CHECK(Credentials::DropAllCapabilities());
+ bool user_ns_supported = Credentials::CanCreateProcessInNewUserNS();
+ bool moved_to_new_ns = Credentials::MoveToNewUserNS();
+ CHECK_EQ(user_ns_supported, moved_to_new_ns);
+}
+
+SANDBOX_TEST(Credentials, UidIsPreserved) {
+ CHECK(Credentials::DropAllCapabilities());
+ uid_t old_ruid, old_euid, old_suid;
+ gid_t old_rgid, old_egid, old_sgid;
+ PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
+ PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ uid_t new_ruid, new_euid, new_suid;
+ PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
+ CHECK(old_ruid == new_ruid);
+ CHECK(old_euid == new_euid);
+ CHECK(old_suid == new_suid);
+
+ gid_t new_rgid, new_egid, new_sgid;
+ PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
+ CHECK(old_rgid == new_rgid);
+ CHECK(old_egid == new_egid);
+ CHECK(old_sgid == new_sgid);
+}
+
+bool NewUserNSCycle() {
+ if (!Credentials::MoveToNewUserNS() ||
+ !Credentials::HasAnyCapability() ||
+ !Credentials::DropAllCapabilities() ||
+ Credentials::HasAnyCapability()) {
+ return false;
+ }
+ return true;
+}
+
+SANDBOX_TEST(Credentials, NestedUserNS) {
+ CHECK(Credentials::DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ CHECK(Credentials::DropAllCapabilities());
+ // As of 3.12, the kernel has a limit of 32. See create_user_ns().
+ const int kNestLevel = 10;
+ for (int i = 0; i < kNestLevel; ++i) {
+ CHECK(NewUserNSCycle()) << "Creating new user NS failed at iteration "
+ << i << ".";
+ }
+}
+
+// Test the WorkingDirectoryIsRoot() helper.
+SANDBOX_TEST(Credentials, CanDetectRoot) {
+ PCHECK(0 == chdir("/proc/"));
+ CHECK(!WorkingDirectoryIsRoot());
+ PCHECK(0 == chdir("/"));
+ CHECK(WorkingDirectoryIsRoot());
+}
+
+// Disabled on ASAN because of crbug.com/451603.
+SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(DropFileSystemAccessIsSafe)) {
+ CHECK(Credentials::DropAllCapabilities());
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ CHECK(Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
+ CHECK(!base::DirectoryExists(base::FilePath("/proc")));
+ CHECK(WorkingDirectoryIsRoot());
+ CHECK(base::IsDirectoryEmpty(base::FilePath("/")));
+ // We want the chroot to never have a subdirectory. A subdirectory
+ // could allow a chroot escape.
+ CHECK_NE(0, mkdir("/test", 0700));
+}
+
+// Check that after dropping filesystem access and dropping privileges
+// it is not possible to regain capabilities.
+SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(CannotRegainPrivileges)) {
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+ CHECK(Credentials::DropAllCapabilities(proc_fd.get()));
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS()) return;
+ CHECK(Credentials::DropFileSystemAccess(proc_fd.get()));
+ CHECK(Credentials::DropAllCapabilities(proc_fd.get()));
+
+ // The kernel should now prevent us from regaining capabilities because we
+ // are in a chroot.
+ CHECK(!Credentials::CanCreateProcessInNewUserNS());
+ CHECK(!Credentials::MoveToNewUserNS());
+}
+
+SANDBOX_TEST(Credentials, SetCapabilities) {
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS())
+ return;
+
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_ADMIN));
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
+
+ std::vector<Credentials::Capability> caps;
+ caps.push_back(Credentials::Capability::SYS_CHROOT);
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
+
+ CHECK(!Credentials::HasCapability(Credentials::Capability::SYS_ADMIN));
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
+
+ const std::vector<Credentials::Capability> no_caps;
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), no_caps));
+ CHECK(!Credentials::HasAnyCapability());
+}
+
+SANDBOX_TEST(Credentials, SetCapabilitiesAndChroot) {
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS())
+ return;
+
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+
+ CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
+ PCHECK(chroot("/") == 0);
+
+ std::vector<Credentials::Capability> caps;
+ caps.push_back(Credentials::Capability::SYS_CHROOT);
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
+ PCHECK(chroot("/") == 0);
+
+ CHECK(Credentials::DropAllCapabilities());
+ PCHECK(chroot("/") == -1 && errno == EPERM);
+}
+
+SANDBOX_TEST(Credentials, SetCapabilitiesMatchesLibCap2) {
+ // Probably missing kernel support.
+ if (!Credentials::MoveToNewUserNS())
+ return;
+
+ base::ScopedFD proc_fd(ProcUtil::OpenProc());
+
+ std::vector<Credentials::Capability> caps;
+ caps.push_back(Credentials::Capability::SYS_CHROOT);
+ CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
+
+ ScopedCap actual_cap(cap_get_proc());
+ PCHECK(actual_cap != nullptr);
+
+ ScopedCap expected_cap(cap_init());
+ PCHECK(expected_cap != nullptr);
+
+ const cap_value_t allowed_cap = CAP_SYS_CHROOT;
+ for (const cap_flag_t flag : {CAP_EFFECTIVE, CAP_PERMITTED}) {
+ PCHECK(cap_set_flag(expected_cap.get(), flag, 1, &allowed_cap, CAP_SET) ==
+ 0);
+ }
+
+ CHECK_EQ(0, cap_compare(expected_cap.get(), actual_cap.get()));
+}
+
+} // namespace.
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.cc b/sandbox/linux/services/init_process_reaper.cc
new file mode 100644
index 0000000000..2e0b90b7b5
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.cc
@@ -0,0 +1,101 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/init_process_reaper.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoNothingSignalHandler(int signal) {}
+
+} // namespace
+
+bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) {
+ int sync_fds[2];
+ // We want to use send, so we can't use a pipe
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) {
+ PLOG(ERROR) << "Failed to create socketpair";
+ return false;
+ }
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ return false;
+ }
+ if (child_pid) {
+ // In the parent, assuming the role of an init process.
+ // The disposition for SIGCHLD cannot be SIG_IGN or wait() will only return
+ // once all of our childs are dead. Since we're init we need to reap childs
+ // as they come.
+ struct sigaction action;
+ memset(&action, 0, sizeof(action));
+ action.sa_handler = &DoNothingSignalHandler;
+ CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
+
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[1], SHUT_RD);
+ DPCHECK(!close_ret);
+ if (post_fork_parent_callback)
+ post_fork_parent_callback->Run();
+ // Tell the child to continue
+ CHECK(HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) == 1);
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+
+ for (;;) {
+ // Loop until we have reaped our one natural child
+ siginfo_t reaped_child_info;
+ int wait_ret =
+ HANDLE_EINTR(waitid(P_ALL, 0, &reaped_child_info, WEXITED));
+ if (wait_ret)
+ _exit(1);
+ if (reaped_child_info.si_pid == child_pid) {
+ int exit_code = 0;
+ // We're done waiting
+ if (reaped_child_info.si_code == CLD_EXITED) {
+ exit_code = reaped_child_info.si_status;
+ }
+ // Exit with the same exit code as our parent. Exit with 0 if we got
+ // signaled.
+ _exit(exit_code);
+ }
+ }
+ } else {
+ // The child needs to wait for the parent to run the callback to avoid a
+ // race condition.
+ int close_ret;
+ close_ret = IGNORE_EINTR(close(sync_fds[1]));
+ DPCHECK(!close_ret);
+ close_ret = shutdown(sync_fds[0], SHUT_WR);
+ DPCHECK(!close_ret);
+ char should_continue;
+ int read_ret = HANDLE_EINTR(read(sync_fds[0], &should_continue, 1));
+ close_ret = IGNORE_EINTR(close(sync_fds[0]));
+ DPCHECK(!close_ret);
+ if (read_ret == 1)
+ return true;
+ else
+ return false;
+ }
+}
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/init_process_reaper.h b/sandbox/linux/services/init_process_reaper.h
new file mode 100644
index 0000000000..840f6fcda7
--- /dev/null
+++ b/sandbox/linux/services/init_process_reaper.h
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+#define SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
+
+#include "base/callback_forward.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// The current process will fork(). The parent will become a process reaper
+// like init(1). The child will continue normally (after this function
+// returns).
+// If not NULL, |post_fork_parent_callback| will run in the parent almost
+// immediately after fork().
+// Since this function calls fork(), it's very important that the caller has
+// only one thread running.
+SANDBOX_EXPORT bool CreateInitProcessReaper(
+ base::Closure* post_fork_parent_callback);
+
+} // namespace sandbox.
+
+#endif // SANDBOX_LINUX_SERVICES_INIT_PROCESS_REAPER_H_
diff --git a/sandbox/linux/services/libc_urandom_override.cc b/sandbox/linux/services/libc_urandom_override.cc
new file mode 100644
index 0000000000..33bb25d6b1
--- /dev/null
+++ b/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 or MSan - it crashes content_unittests. Make sure it works
+// properly and enable it here. http://crbug.com/123263
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+static void InitLibcFileIOFunctions();
+static pthread_once_t g_libc_file_io_funcs_guard = PTHREAD_ONCE_INIT;
+#endif
+
+void InitLibcUrandomOverrides() {
+ // Make sure /dev/urandom is open.
+ base::GetUrandomFD();
+ g_override_urandom = true;
+
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER))
+ CHECK_EQ(0, pthread_once(&g_libc_file_io_funcs_guard,
+ InitLibcFileIOFunctions));
+#endif
+}
+
+#if !(defined(ADDRESS_SANITIZER) || defined(MEMORY_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) || defined(MEMORY_SANITIZER))
+
+} // namespace content
diff --git a/sandbox/linux/services/libc_urandom_override.h b/sandbox/linux/services/libc_urandom_override.h
new file mode 100644
index 0000000000..86212f8bc4
--- /dev/null
+++ b/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/sandbox/linux/services/namespace_sandbox.cc b/sandbox/linux/services/namespace_sandbox.cc
new file mode 100644
index 0000000000..23796446f3
--- /dev/null
+++ b/sandbox/linux/services/namespace_sandbox.cc
@@ -0,0 +1,208 @@
+// Copyright 2015 The Chromium 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/namespace_sandbox.h"
+
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSandboxUSERNSEnvironmentVarName[] = "SBX_USER_NS";
+const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS";
+const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS";
+
+#if !defined(OS_NACL_NONSFI)
+class WriteUidGidMapDelegate : public base::LaunchOptions::PreExecDelegate {
+ public:
+ WriteUidGidMapDelegate()
+ : uid_(getuid()),
+ gid_(getgid()),
+ supports_deny_setgroups_(
+ NamespaceUtils::KernelSupportsDenySetgroups()) {}
+
+ ~WriteUidGidMapDelegate() override {}
+
+ void RunAsyncSafe() override {
+ if (supports_deny_setgroups_) {
+ RAW_CHECK(NamespaceUtils::DenySetgroups());
+ }
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid_));
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/gid_map", gid_));
+ }
+
+ private:
+ const uid_t uid_;
+ const gid_t gid_;
+ const bool supports_deny_setgroups_;
+ DISALLOW_COPY_AND_ASSIGN(WriteUidGidMapDelegate);
+};
+
+void SetEnvironForNamespaceType(base::EnvironmentMap* environ,
+ base::NativeEnvironmentString env_var,
+ bool value) {
+ // An empty string causes the env var to be unset in the child process.
+ (*environ)[env_var] = value ? "1" : "";
+}
+
+// Linux supports up to 64 signals. This should be updated if that ever changes.
+int g_signal_exit_codes[64];
+
+void TerminationSignalHandler(int sig) {
+ // Return a special exit code so that the process is detected as terminated by
+ // a signal.
+ const size_t sig_idx = static_cast<size_t>(sig);
+ if (sig_idx < arraysize(g_signal_exit_codes)) {
+ _exit(g_signal_exit_codes[sig_idx]);
+ }
+
+ _exit(NamespaceSandbox::kDefaultExitCode);
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+} // namespace
+
+#if !defined(OS_NACL_NONSFI)
+// static
+base::Process NamespaceSandbox::LaunchProcess(
+ const base::CommandLine& cmdline,
+ const base::LaunchOptions& options) {
+ return LaunchProcess(cmdline.argv(), options);
+}
+
+// static
+base::Process NamespaceSandbox::LaunchProcess(
+ const std::vector<std::string>& argv,
+ const base::LaunchOptions& options) {
+ int clone_flags = 0;
+ int ns_types[] = {CLONE_NEWUSER, CLONE_NEWPID, CLONE_NEWNET};
+ for (const int ns_type : ns_types) {
+ if (NamespaceUtils::KernelSupportsUnprivilegedNamespace(ns_type)) {
+ clone_flags |= ns_type;
+ }
+ }
+ CHECK(clone_flags & CLONE_NEWUSER);
+
+ // These fields may not be set by the caller.
+ CHECK(options.pre_exec_delegate == nullptr);
+ CHECK_EQ(0, options.clone_flags);
+
+ WriteUidGidMapDelegate write_uid_gid_map_delegate;
+
+ base::LaunchOptions launch_options = options;
+ launch_options.pre_exec_delegate = &write_uid_gid_map_delegate;
+ launch_options.clone_flags = clone_flags;
+
+ const std::pair<int, const char*> clone_flag_environ[] = {
+ std::make_pair(CLONE_NEWUSER, kSandboxUSERNSEnvironmentVarName),
+ std::make_pair(CLONE_NEWPID, kSandboxPIDNSEnvironmentVarName),
+ std::make_pair(CLONE_NEWNET, kSandboxNETNSEnvironmentVarName),
+ };
+
+ base::EnvironmentMap* environ = &launch_options.environ;
+ for (const auto& entry : clone_flag_environ) {
+ const int flag = entry.first;
+ const char* environ_name = entry.second;
+ SetEnvironForNamespaceType(environ, environ_name, clone_flags & flag);
+ }
+
+ return base::LaunchProcess(argv, launch_options);
+}
+
+// static
+pid_t NamespaceSandbox::ForkInNewPidNamespace(bool drop_capabilities_in_child) {
+ const pid_t pid =
+ base::ForkWithFlags(CLONE_NEWPID | SIGCHLD, nullptr, nullptr);
+ if (pid < 0) {
+ return pid;
+ }
+
+ if (pid == 0) {
+ DCHECK_EQ(1, getpid());
+ if (drop_capabilities_in_child) {
+ // Since we just forked, we are single-threaded, so this should be safe.
+ CHECK(Credentials::DropAllCapabilitiesOnCurrentThread());
+ }
+ return 0;
+ }
+
+ return pid;
+}
+
+// static
+void NamespaceSandbox::InstallDefaultTerminationSignalHandlers() {
+ static const int kDefaultTermSignals[] = {
+ SIGHUP, SIGINT, SIGABRT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2,
+ };
+
+ for (const int sig : kDefaultTermSignals) {
+ InstallTerminationSignalHandler(sig, kDefaultExitCode);
+ }
+}
+
+// static
+bool NamespaceSandbox::InstallTerminationSignalHandler(
+ int sig,
+ int exit_code) {
+ struct sigaction old_action;
+ PCHECK(sigaction(sig, nullptr, &old_action) == 0);
+
+ if (old_action.sa_flags & SA_SIGINFO &&
+ old_action.sa_sigaction != nullptr) {
+ return false;
+ } else if (old_action.sa_handler != SIG_DFL) {
+ return false;
+ }
+
+ const size_t sig_idx = static_cast<size_t>(sig);
+ CHECK_LT(sig_idx, arraysize(g_signal_exit_codes));
+
+ DCHECK_GE(exit_code, 0);
+ DCHECK_LT(exit_code, 256);
+
+ g_signal_exit_codes[sig_idx] = exit_code;
+
+ struct sigaction action = {};
+ action.sa_handler = &TerminationSignalHandler;
+ PCHECK(sigaction(sig, &action, nullptr) == 0);
+ return true;
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+// static
+bool NamespaceSandbox::InNewUserNamespace() {
+ return getenv(kSandboxUSERNSEnvironmentVarName) != nullptr;
+}
+
+// static
+bool NamespaceSandbox::InNewPidNamespace() {
+ return getenv(kSandboxPIDNSEnvironmentVarName) != nullptr;
+}
+
+// static
+bool NamespaceSandbox::InNewNetNamespace() {
+ return getenv(kSandboxNETNSEnvironmentVarName) != nullptr;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/namespace_sandbox.h b/sandbox/linux/services/namespace_sandbox.h
new file mode 100644
index 0000000000..80097fb16a
--- /dev/null
+++ b/sandbox/linux/services/namespace_sandbox.h
@@ -0,0 +1,101 @@
+// Copyright 2015 The Chromium 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_NAMESPACE_SANDBOX_H_
+#define SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
+
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Helper class for starting a process inside a new user, PID, and network
+// namespace. Before using a namespace sandbox, check for namespaces support
+// using Credentials::CanCreateProcessInNewUserNS.
+//
+// A typical use for "A" launching a sandboxed process "B" would be:
+// 1. A sets up a command line and launch options for process B.
+// 2. A launches B with LaunchProcess.
+// 3. B should be prepared to assume the role of init(1). In particular, apart
+// from SIGKILL and SIGSTOP, B cannot receive any signal for which it does
+// not have an explicit signal handler registered.
+// If B dies, all the processes in the namespace will die.
+// B can fork() and the parent can assume the role of init(1), by using
+// CreateInitProcessReaper().
+// 4. B chroots using Credentials::MoveToNewUserNS() and
+// Credentials::DropFileSystemAccess()
+// 5. B drops capabilities gained by entering the new user namespace with
+// Credentials::DropAllCapabilities().
+class SANDBOX_EXPORT NamespaceSandbox {
+ public:
+#if !defined(OS_NACL_NONSFI)
+ static const int kDefaultExitCode = 1;
+
+ // Launch a new process inside its own user/PID/network namespaces (depending
+ // on kernel support). Requires at a minimum that user namespaces are
+ // supported (use Credentials::CanCreateProcessInNewUserNS to check this).
+ //
+ // pre_exec_delegate and clone_flags fields of LaunchOptions should be nullptr
+ // and 0, respectively, since this function makes a copy of options and
+ // overrides them.
+ static base::Process LaunchProcess(const base::CommandLine& cmdline,
+ const base::LaunchOptions& options);
+ static base::Process LaunchProcess(const std::vector<std::string>& argv,
+ const base::LaunchOptions& options);
+
+ // Forks a process in its own PID namespace. The child process is the init
+ // process inside of the PID namespace, so if the child needs to fork further,
+ // it should call CreateInitProcessReaper, which turns the init process into a
+ // reaper process.
+ //
+ // Otherwise, the child should setup handlers for signals which should
+ // terminate the process using InstallDefaultTerminationSignalHandlers or
+ // InstallTerminationSignalHandler. This works around the fact that init
+ // processes ignore such signals unless they have an explicit handler set.
+ //
+ // This function requries CAP_SYS_ADMIN. If |drop_capabilities_in_child| is
+ // true, then capabilities are dropped in the child.
+ static pid_t ForkInNewPidNamespace(bool drop_capabilities_in_child);
+
+ // Installs a signal handler for:
+ //
+ // SIGHUP, SIGINT, SIGABRT, SIGQUIT, SIGPIPE, SIGTERM, SIGUSR1, SIGUSR2
+ //
+ // that exits with kDefaultExitCode. These are signals whose default action is
+ // to terminate the program (apart from SIGILL, SIGFPE, and SIGSEGV, which
+ // will still terminate the process if e.g. an illegal instruction is
+ // encountered, etc.).
+ //
+ // If any of these already had a signal handler installed, this function will
+ // not override them.
+ static void InstallDefaultTerminationSignalHandlers();
+
+ // Installs a signal handler for |sig| which exits with |exit_code|. If a
+ // signal handler was already present for |sig|, does nothing and returns
+ // false.
+ static bool InstallTerminationSignalHandler(int sig, int exit_code);
+#endif // !defined(OS_NACL_NONSFI)
+
+ // Returns whether the namespace sandbox created a new user, PID, and network
+ // namespace. In particular, InNewUserNamespace should return true iff the
+ // process was started via this class.
+ static bool InNewUserNamespace();
+ static bool InNewPidNamespace();
+ static bool InNewNetNamespace();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceSandbox);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_NAMESPACE_SANDBOX_H_
diff --git a/sandbox/linux/services/namespace_sandbox_unittest.cc b/sandbox/linux/services/namespace_sandbox_unittest.cc
new file mode 100644
index 0000000000..547ef6728c
--- /dev/null
+++ b/sandbox/linux/services/namespace_sandbox_unittest.cc
@@ -0,0 +1,217 @@
+// Copyright 2015 The Chromium 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/namespace_sandbox.h"
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_enumerator.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/test/multiprocess_test.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/services/namespace_utils.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace sandbox {
+
+namespace {
+
+bool RootDirectoryIsEmpty() {
+ base::FilePath root("/");
+ int file_type =
+ base::FileEnumerator::DIRECTORIES | base::FileEnumerator::FILES;
+ base::FileEnumerator enumerator_before(root, false, file_type);
+ return enumerator_before.Next().empty();
+}
+
+class NamespaceSandboxTest : public base::MultiProcessTest {
+ public:
+ void TestProc(const std::string& procname) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ base::FileHandleMappingVector fds_to_remap = {
+ std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
+ std::make_pair(STDERR_FILENO, STDERR_FILENO),
+ };
+ base::LaunchOptions launch_options;
+ launch_options.fds_to_remap = &fds_to_remap;
+
+ base::Process process =
+ NamespaceSandbox::LaunchProcess(MakeCmdLine(procname), launch_options);
+ ASSERT_TRUE(process.IsValid());
+
+ const int kDummyExitCode = 42;
+ int exit_code = kDummyExitCode;
+ EXPECT_TRUE(process.WaitForExit(&exit_code));
+ EXPECT_EQ(0, exit_code);
+ }
+};
+
+MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ bool in_user_ns = NamespaceSandbox::InNewUserNamespace();
+ bool in_pid_ns = NamespaceSandbox::InNewPidNamespace();
+ bool in_net_ns = NamespaceSandbox::InNewNetNamespace();
+ CHECK(in_user_ns);
+ CHECK_EQ(in_pid_ns,
+ NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWPID));
+ CHECK_EQ(in_net_ns,
+ NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWNET));
+ if (in_pid_ns) {
+ CHECK_EQ(1, getpid());
+ }
+ return 0;
+}
+
+TEST_F(NamespaceSandboxTest, BasicUsage) {
+ TestProc("SimpleChildProcess");
+}
+
+MULTIPROCESS_TEST_MAIN(ChrootMe) {
+ CHECK(!RootDirectoryIsEmpty());
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ CHECK(sandbox::Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
+ CHECK(RootDirectoryIsEmpty());
+ return 0;
+}
+
+// Temporarily disabled on ASAN due to crbug.com/451603.
+TEST_F(NamespaceSandboxTest, DISABLE_ON_ASAN(ChrootAndDropCapabilities)) {
+ TestProc("ChrootMe");
+}
+
+MULTIPROCESS_TEST_MAIN(NestedNamespaceSandbox) {
+ base::FileHandleMappingVector fds_to_remap = {
+ std::make_pair(STDOUT_FILENO, STDOUT_FILENO),
+ std::make_pair(STDERR_FILENO, STDERR_FILENO),
+ };
+ base::LaunchOptions launch_options;
+ launch_options.fds_to_remap = &fds_to_remap;
+ base::Process process = NamespaceSandbox::LaunchProcess(
+ base::CommandLine(base::FilePath("/bin/true")), launch_options);
+ CHECK(process.IsValid());
+
+ const int kDummyExitCode = 42;
+ int exit_code = kDummyExitCode;
+ CHECK(process.WaitForExit(&exit_code));
+ CHECK_EQ(0, exit_code);
+ return 0;
+}
+
+TEST_F(NamespaceSandboxTest, NestedNamespaceSandbox) {
+ TestProc("NestedNamespaceSandbox");
+}
+
+const int kNormalExitCode = 0;
+const int kSignalTerminationExitCode = 255;
+
+// Ensure that CHECK(false) is distinguishable from _exit(kNormalExitCode).
+// Allowing noise since CHECK(false) will write a stack trace to stderr.
+SANDBOX_TEST_ALLOW_NOISE(ForkInNewPidNamespace, CheckDoesNotReturnZero) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ CHECK_GE(pid, 0);
+
+ if (pid == 0) {
+ CHECK(false);
+ _exit(kNormalExitCode);
+ }
+
+ int status;
+ PCHECK(waitpid(pid, &status, 0) == pid);
+ if (WIFEXITED(status)) {
+ CHECK_NE(kNormalExitCode, WEXITSTATUS(status));
+ }
+}
+
+SANDBOX_TEST(ForkInNewPidNamespace, BasicUsage) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ CHECK_GE(pid, 0);
+
+ if (pid == 0) {
+ CHECK_EQ(1, getpid());
+ CHECK(!Credentials::HasAnyCapability());
+ _exit(kNormalExitCode);
+ }
+
+ int status;
+ PCHECK(waitpid(pid, &status, 0) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kNormalExitCode, WEXITSTATUS(status));
+}
+
+SANDBOX_TEST(ForkInNewPidNamespace, ExitWithSignal) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ CHECK(sandbox::Credentials::MoveToNewUserNS());
+ const pid_t pid = NamespaceSandbox::ForkInNewPidNamespace(
+ /*drop_capabilities_in_child=*/true);
+ CHECK_GE(pid, 0);
+
+ if (pid == 0) {
+ CHECK_EQ(1, getpid());
+ CHECK(!Credentials::HasAnyCapability());
+ CHECK(NamespaceSandbox::InstallTerminationSignalHandler(
+ SIGTERM, kSignalTerminationExitCode));
+ while (true) {
+ raise(SIGTERM);
+ }
+ }
+
+ int status;
+ PCHECK(waitpid(pid, &status, 0) == pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kSignalTerminationExitCode, WEXITSTATUS(status));
+}
+
+volatile sig_atomic_t signal_handler_called;
+void ExitSuccessfully(int sig) {
+ signal_handler_called = 1;
+}
+
+SANDBOX_TEST(InstallTerminationSignalHandler, DoesNotOverrideExistingHandlers) {
+ struct sigaction action = {};
+ action.sa_handler = &ExitSuccessfully;
+ PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
+
+ NamespaceSandbox::InstallDefaultTerminationSignalHandlers();
+ CHECK(!NamespaceSandbox::InstallTerminationSignalHandler(
+ SIGUSR1, kSignalTerminationExitCode));
+
+ raise(SIGUSR1);
+ CHECK_EQ(1, signal_handler_called);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/namespace_utils.cc b/sandbox/linux/services/namespace_utils.cc
new file mode 100644
index 0000000000..82a544453f
--- /dev/null
+++ b/sandbox/linux/services/namespace_utils.cc
@@ -0,0 +1,117 @@
+// Copyright 2015 The Chromium 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/namespace_utils.h"
+
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/strings/safe_sprintf.h"
+#include "base/third_party/valgrind/valgrind.h"
+
+namespace sandbox {
+
+namespace {
+bool IsRunningOnValgrind() {
+ return RUNNING_ON_VALGRIND;
+}
+
+const char kProcSelfSetgroups[] = "/proc/self/setgroups";
+} // namespace
+
+// static
+bool NamespaceUtils::WriteToIdMapFile(const char* map_file, generic_id_t id) {
+ // This function needs to be async-signal-safe, as it may be called in between
+ // fork and exec.
+
+ int fd = HANDLE_EINTR(open(map_file, O_WRONLY));
+ if (fd == -1) {
+ return false;
+ }
+
+ const generic_id_t inside_id = id;
+ const generic_id_t outside_id = id;
+
+ char mapping[64];
+ const ssize_t len =
+ base::strings::SafeSPrintf(mapping, "%d %d 1\n", inside_id, outside_id);
+ const ssize_t rc = HANDLE_EINTR(write(fd, mapping, len));
+ RAW_CHECK(IGNORE_EINTR(close(fd)) == 0);
+ return rc == len;
+}
+
+// static
+bool NamespaceUtils::KernelSupportsUnprivilegedNamespace(int type) {
+ // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
+ // so always consider namespaces unsupported there.
+ if (IsRunningOnValgrind()) {
+ return false;
+ }
+
+ // As of Linux 3.8, /proc/self/ns/* files exist for all namespace types. Since
+ // user namespaces were added in 3.8, it is OK to rely on the existence of
+ // /proc/self/ns/*.
+ if (!base::PathExists(base::FilePath("/proc/self/ns/user"))) {
+ return false;
+ }
+
+ const char* path;
+ switch (type) {
+ case CLONE_NEWUSER:
+ return true;
+ case CLONE_NEWIPC:
+ path = "/proc/self/ns/ipc";
+ break;
+ case CLONE_NEWNET:
+ path = "/proc/self/ns/net";
+ break;
+ case CLONE_NEWNS:
+ path = "/proc/self/ns/mnt";
+ break;
+ case CLONE_NEWPID:
+ path = "/proc/self/ns/pid";
+ break;
+ case CLONE_NEWUTS:
+ path = "/proc/self/ns/uts";
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ return base::PathExists(base::FilePath(path));
+}
+
+// static
+bool NamespaceUtils::KernelSupportsDenySetgroups() {
+ return base::PathExists(base::FilePath(kProcSelfSetgroups));
+}
+
+// static
+bool NamespaceUtils::DenySetgroups() {
+ // This function needs to be async-signal-safe.
+ int fd = HANDLE_EINTR(open(kProcSelfSetgroups, O_WRONLY));
+ if (fd == -1) {
+ return false;
+ }
+
+ static const char kDeny[] = "deny";
+ const ssize_t len = sizeof(kDeny) - 1;
+ const ssize_t rc = HANDLE_EINTR(write(fd, kDeny, len));
+ RAW_CHECK(IGNORE_EINTR(close(fd)) == 0);
+ return rc == len;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/namespace_utils.h b/sandbox/linux/services/namespace_utils.h
new file mode 100644
index 0000000000..f3c88a9452
--- /dev/null
+++ b/sandbox/linux/services/namespace_utils.h
@@ -0,0 +1,53 @@
+// Copyright 2015 The Chromium 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_NAMESPACE_UTILS_H_
+#define SANDBOX_LINUX_SERVICES_NAMESPACE_UTILS_H_
+
+#include <sys/types.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/template_util.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Utility functions for using Linux namepaces.
+class SANDBOX_EXPORT NamespaceUtils {
+ public:
+ COMPILE_ASSERT((base::is_same<uid_t, gid_t>::value), UidAndGidAreSameType);
+ // generic_id_t can be used for either uid_t or gid_t.
+ typedef uid_t generic_id_t;
+
+ // Write a uid or gid mapping from |id| to |id| in |map_file|. This function
+ // is async-signal-safe.
+ static bool WriteToIdMapFile(const char* map_file,
+ generic_id_t id) WARN_UNUSED_RESULT;
+
+ // Returns true if unprivileged namespaces of type |type| is supported
+ // (meaning that both CLONE_NEWUSER and type are are supported). |type| must
+ // be one of CLONE_NEWIPC, CLONE_NEWNET, CLONE_NEWNS, CLONE_NEWPID,
+ // CLONE_NEWUSER, or CLONE_NEWUTS. This relies on access to /proc, so it will
+ // not work from within a sandbox.
+ static bool KernelSupportsUnprivilegedNamespace(int type);
+
+ // Returns true if the kernel supports denying setgroups in a user namespace.
+ // On kernels where this is supported, DenySetgroups must be called before a
+ // gid mapping can be added.
+ static bool KernelSupportsDenySetgroups();
+
+ // Disables setgroups() within the current user namespace. On Linux 3.18.2 and
+ // later, this is required in order to write to /proc/self/gid_map without
+ // having CAP_SETGID. Callers can determine whether is this needed with
+ // KernelSupportsDenySetgroups. This function is async-signal-safe.
+ static bool DenySetgroups() WARN_UNUSED_RESULT;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceUtils);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_NAMESPACE_UTILS_H_
diff --git a/sandbox/linux/services/namespace_utils_unittest.cc b/sandbox/linux/services/namespace_utils_unittest.cc
new file mode 100644
index 0000000000..41ed7e89a6
--- /dev/null
+++ b/sandbox/linux/services/namespace_utils_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium 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/namespace_utils.h"
+
+#include <errno.h>
+#include <sched.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "sandbox/linux/services/credentials.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+SANDBOX_TEST(NamespaceUtils, KernelSupportsUnprivilegedNamespace) {
+ const bool can_create_user_ns = Credentials::CanCreateProcessInNewUserNS();
+ const bool supports_user_ns =
+ NamespaceUtils::KernelSupportsUnprivilegedNamespace(CLONE_NEWUSER);
+ // can_create_user_ns implies supports_user_ns, but the converse is not
+ // necessarily true, as creating a user namespace can fail for various
+ // reasons.
+ if (can_create_user_ns) {
+ SANDBOX_ASSERT(supports_user_ns);
+ }
+}
+
+SANDBOX_TEST(NamespaceUtils, WriteToIdMapFile) {
+ if (!Credentials::CanCreateProcessInNewUserNS()) {
+ return;
+ }
+
+ const uid_t uid = getuid();
+ const gid_t gid = getgid();
+
+ const bool supports_deny_setgroups =
+ NamespaceUtils::KernelSupportsDenySetgroups();
+
+ const pid_t pid =
+ base::ForkWithFlags(CLONE_NEWUSER | SIGCHLD, nullptr, nullptr);
+ ASSERT_NE(-1, pid);
+ if (pid == 0) {
+ if (supports_deny_setgroups) {
+ RAW_CHECK(NamespaceUtils::DenySetgroups());
+ }
+
+ RAW_CHECK(getuid() != uid);
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/uid_map", uid));
+ RAW_CHECK(getuid() == uid);
+
+ RAW_CHECK(getgid() != gid);
+ RAW_CHECK(NamespaceUtils::WriteToIdMapFile("/proc/self/gid_map", gid));
+ RAW_CHECK(getgid() == gid);
+
+ _exit(0);
+ }
+
+ int status = 42;
+ SANDBOX_ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ SANDBOX_ASSERT_EQ(0, status);
+}
+
+} // namespace.
+
+} // namespace sandbox.
diff --git a/sandbox/linux/services/proc_util.cc b/sandbox/linux/services/proc_util.cc
new file mode 100644
index 0000000000..d3f755f9a1
--- /dev/null
+++ b/sandbox/linux/services/proc_util.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/proc_util.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+
+namespace sandbox {
+namespace {
+
+struct DIRCloser {
+ void operator()(DIR* d) const {
+ DCHECK(d);
+ PCHECK(0 == closedir(d));
+ }
+};
+
+typedef scoped_ptr<DIR, DIRCloser> ScopedDIR;
+
+base::ScopedFD OpenDirectory(const char* path) {
+ DCHECK(path);
+ base::ScopedFD directory_fd(
+ HANDLE_EINTR(open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
+ PCHECK(directory_fd.is_valid());
+ return directory_fd.Pass();
+}
+
+} // namespace
+
+int ProcUtil::CountOpenFds(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ int proc_self_fd = HANDLE_EINTR(
+ openat(proc_fd, "self/fd/", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ PCHECK(0 <= proc_self_fd);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ int count = 0;
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ ++count;
+ }
+ return count;
+}
+
+bool ProcUtil::HasOpenDirectory(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ int proc_self_fd =
+ openat(proc_fd, "self/fd/", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+
+ PCHECK(0 <= proc_self_fd);
+
+ // Ownership of proc_self_fd is transferred here, it must not be closed
+ // or modified afterwards except via dir.
+ ScopedDIR dir(fdopendir(proc_self_fd));
+ CHECK(dir);
+
+ struct dirent e;
+ struct dirent* de;
+ while (!readdir_r(dir.get(), &e, &de) && de) {
+ if (strcmp(e.d_name, ".") == 0 || strcmp(e.d_name, "..") == 0) {
+ continue;
+ }
+
+ int fd_num;
+ CHECK(base::StringToInt(e.d_name, &fd_num));
+ if (fd_num == proc_fd || fd_num == proc_self_fd) {
+ continue;
+ }
+
+ struct stat s;
+ // It's OK to use proc_self_fd here, fstatat won't modify it.
+ CHECK(fstatat(proc_self_fd, e.d_name, &s, 0) == 0);
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+ }
+
+ // No open unmanaged directories found.
+ return false;
+}
+
+bool ProcUtil::HasOpenDirectory() {
+ base::ScopedFD proc_fd(
+ HANDLE_EINTR(open("/proc/", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));
+ return HasOpenDirectory(proc_fd.get());
+}
+
+//static
+base::ScopedFD ProcUtil::OpenProc() {
+ return OpenDirectory("/proc/");
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/proc_util.h b/sandbox/linux/services/proc_util.h
new file mode 100644
index 0000000000..bc14c5ef2a
--- /dev/null
+++ b/sandbox/linux/services/proc_util.h
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
+#define SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+class SANDBOX_EXPORT ProcUtil {
+ public:
+ // Returns the number of file descriptors in the current process's FD
+ // table, excluding |proc_fd|, which should be a file descriptor for
+ // /proc/.
+ static int CountOpenFds(int proc_fd);
+
+ // Checks whether the current process has any directory file descriptor open.
+ // Directory file descriptors are "capabilities" that would let a process use
+ // system calls such as openat() to bypass restrictions such as
+ // DropFileSystemAccess().
+ // Sometimes it's useful to call HasOpenDirectory() after file system access
+ // has been dropped. In this case, |proc_fd| should be a file descriptor to
+ // /proc/. The file descriptor in |proc_fd| will be ignored by
+ // HasOpenDirectory() and remains owned by the caller. It is very important
+ // for the caller to close it.
+ static bool HasOpenDirectory(int proc_fd) WARN_UNUSED_RESULT;
+ static bool HasOpenDirectory() WARN_UNUSED_RESULT;
+
+ // Open /proc/ or crash if not possible.
+ static base::ScopedFD OpenProc();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ProcUtil);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_PROC_UTIL_H_
diff --git a/sandbox/linux/services/proc_util_unittest.cc b/sandbox/linux/services/proc_util_unittest.cc
new file mode 100644
index 0000000000..bf25151956
--- /dev/null
+++ b/sandbox/linux/services/proc_util_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/proc_util.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "base/files/scoped_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(ProcUtil, CountOpenFds) {
+ base::ScopedFD proc_fd(open("/proc/", O_RDONLY | O_DIRECTORY));
+ ASSERT_TRUE(proc_fd.is_valid());
+ int fd_count = ProcUtil::CountOpenFds(proc_fd.get());
+ int fd = open("/dev/null", O_RDONLY);
+ ASSERT_LE(0, fd);
+ EXPECT_EQ(fd_count + 1, ProcUtil::CountOpenFds(proc_fd.get()));
+ ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
+ EXPECT_EQ(fd_count, ProcUtil::CountOpenFds(proc_fd.get()));
+}
+
+TEST(ProcUtil, HasOpenDirectory) {
+ // No open directory should exist at startup.
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory());
+ {
+ // Have a "/proc" file descriptor around.
+ int proc_fd = open("/proc/", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD proc_fd_closer(proc_fd);
+ EXPECT_TRUE(ProcUtil::HasOpenDirectory());
+ }
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory());
+}
+
+TEST(ProcUtil, HasOpenDirectoryWithFD) {
+ int proc_fd = open("/proc/", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD proc_fd_closer(proc_fd);
+ ASSERT_LE(0, proc_fd);
+
+ // Don't pass |proc_fd|, an open directory (proc_fd) should
+ // be detected.
+ EXPECT_TRUE(ProcUtil::HasOpenDirectory());
+ // Pass |proc_fd| and no open directory should be detected.
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
+
+ {
+ // Have a directory file descriptor around.
+ int open_directory_fd = open("/proc/self/", O_RDONLY | O_DIRECTORY);
+ base::ScopedFD open_directory_fd_closer(open_directory_fd);
+ EXPECT_TRUE(ProcUtil::HasOpenDirectory(proc_fd));
+ }
+
+ // The "/proc/" file descriptor should now be closed, |proc_fd| is the
+ // only directory file descriptor open.
+ EXPECT_FALSE(ProcUtil::HasOpenDirectory(proc_fd));
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/resource_limits.cc b/sandbox/linux/services/resource_limits.cc
new file mode 100644
index 0000000000..1ec11295d1
--- /dev/null
+++ b/sandbox/linux/services/resource_limits.cc
@@ -0,0 +1,26 @@
+// Copyright 2015 The Chromium 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/resource_limits.h"
+
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <algorithm>
+
+namespace sandbox {
+
+// static
+bool ResourceLimits::Lower(int resource, rlim_t limit) {
+ struct rlimit old_rlimit;
+ if (getrlimit(resource, &old_rlimit))
+ return false;
+ // Make sure we don't raise the existing limit.
+ const struct rlimit new_rlimit = {std::min(old_rlimit.rlim_cur, limit),
+ std::min(old_rlimit.rlim_max, limit)};
+ int rc = setrlimit(resource, &new_rlimit);
+ return rc == 0;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/resource_limits.h b/sandbox/linux/services/resource_limits.h
new file mode 100644
index 0000000000..3464dab679
--- /dev/null
+++ b/sandbox/linux/services/resource_limits.h
@@ -0,0 +1,29 @@
+// Copyright 2015 The Chromium 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_RESOURCE_LIMITS_H_
+#define SANDBOX_LINUX_SERVICES_RESOURCE_LIMITS_H_
+
+#include <sys/resource.h>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This class provides a small wrapper around setrlimit().
+class SANDBOX_EXPORT ResourceLimits {
+ public:
+ // Lower the soft and hard limit of |resource| to |limit|. If the current
+ // limit is lower than |limit|, keep it.
+ static bool Lower(int resource, rlim_t limit) WARN_UNUSED_RESULT;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ResourceLimits);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_RESOURCE_LIMITS_H_
diff --git a/sandbox/linux/services/resource_limits_unittests.cc b/sandbox/linux/services/resource_limits_unittests.cc
new file mode 100644
index 0000000000..910c740f7b
--- /dev/null
+++ b/sandbox/linux/services/resource_limits_unittests.cc
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium 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/resource_limits.h"
+
+#include <errno.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+// Fails on Android: crbug.com/459158
+#if !defined(OS_ANDROID)
+#define MAYBE_NoFork DISABLE_ON_ASAN(NoFork)
+#else
+#define MAYBE_NoFork DISABLED_NoFork
+#endif // OS_ANDROID
+
+// Not being able to fork breaks LeakSanitizer, so disable on
+// all ASAN builds.
+SANDBOX_TEST(ResourceLimits, MAYBE_NoFork) {
+ // Make sure that fork will fail with EAGAIN.
+ SANDBOX_ASSERT(ResourceLimits::Lower(RLIMIT_NPROC, 0));
+ errno = 0;
+ pid_t pid = fork();
+ // Reap any child if fork succeeded.
+ TestUtils::HandlePostForkReturn(pid);
+ SANDBOX_ASSERT_EQ(-1, pid);
+ CHECK_EQ(EAGAIN, errno);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/scoped_process.cc b/sandbox/linux/services/scoped_process.cc
new file mode 100644
index 0000000000..65af4873a4
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.cc
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/scoped_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kSynchronisationChar[] = "D";
+
+void WaitForever() {
+ while(true) {
+ pause();
+ }
+}
+
+} // namespace
+
+ScopedProcess::ScopedProcess(const base::Closure& child_callback)
+ : child_process_id_(-1), process_id_(getpid()) {
+ PCHECK(0 == pipe(pipe_fds_));
+#if !defined(THREAD_SANITIZER)
+ // Make sure that we can safely fork().
+ CHECK(ThreadHelpers::IsSingleThreaded());
+#endif
+ child_process_id_ = fork();
+ PCHECK(0 <= child_process_id_);
+
+ if (0 == child_process_id_) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ pipe_fds_[0] = -1;
+ child_callback.Run();
+ // Notify the parent that the closure has run.
+ CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds_[1], kSynchronisationChar, 1)));
+ WaitForever();
+ NOTREACHED();
+ _exit(1);
+ }
+
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ pipe_fds_[1] = -1;
+}
+
+ScopedProcess::~ScopedProcess() {
+ CHECK(IsOriginalProcess());
+ if (child_process_id_ >= 0) {
+ PCHECK(0 == kill(child_process_id_, SIGKILL));
+ siginfo_t process_info;
+
+ PCHECK(0 == HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED)));
+ }
+ if (pipe_fds_[0] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[0])));
+ }
+ if (pipe_fds_[1] >= 0) {
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds_[1])));
+ }
+}
+
+int ScopedProcess::WaitForExit(bool* got_signaled) {
+ DCHECK(got_signaled);
+ CHECK(IsOriginalProcess());
+ siginfo_t process_info;
+ // WNOWAIT to make sure that the destructor can wait on the child.
+ int ret = HANDLE_EINTR(
+ waitid(P_PID, child_process_id_, &process_info, WEXITED | WNOWAIT));
+ PCHECK(0 == ret) << "Did something else wait on the child?";
+
+ if (process_info.si_code == CLD_EXITED) {
+ *got_signaled = false;
+ } else if (process_info.si_code == CLD_KILLED ||
+ process_info.si_code == CLD_DUMPED) {
+ *got_signaled = true;
+ } else {
+ CHECK(false) << "ScopedProcess needs to be extended for si_code "
+ << process_info.si_code;
+ }
+ return process_info.si_status;
+}
+
+bool ScopedProcess::WaitForClosureToRun() {
+ char c = 0;
+ int ret = HANDLE_EINTR(read(pipe_fds_[0], &c, 1));
+ PCHECK(ret >= 0);
+ if (0 == ret)
+ return false;
+
+ CHECK_EQ(c, kSynchronisationChar[0]);
+ return true;
+}
+
+// It would be problematic if after a fork(), another process would start using
+// this object.
+// This method allows to assert it is not happening.
+bool ScopedProcess::IsOriginalProcess() {
+ // Make a direct syscall to bypass glibc caching of PIDs.
+ pid_t pid = sys_getpid();
+ return pid == process_id_;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/scoped_process.h b/sandbox/linux/services/scoped_process.h
new file mode 100644
index 0000000000..bddbd5529b
--- /dev/null
+++ b/sandbox/linux/services/scoped_process.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// fork() a child process that will run a Closure.
+// After the Closure has run, the child will pause forever. If this object
+// is detroyed, the child will be destroyed, even if the closure did not
+// finish running. It's ok to signal the child from outside of this class to
+// destroy it.
+// This class cannot be instanciated from a multi-threaded process, as it needs
+// to fork().
+class SANDBOX_EXPORT ScopedProcess {
+ public:
+ // A new process will be created and |child_callback| will run in the child
+ // process. This callback is allowed to terminate the process or to simply
+ // return. If the callback returns, the process will wait forever.
+ explicit ScopedProcess(const base::Closure& child_callback);
+ ~ScopedProcess();
+
+ // Wait for the process to exit.
+ // |got_signaled| tells how to interpret the return value: either as an exit
+ // code, or as a signal number.
+ // When this returns, the process will still not have been reaped and will
+ // survive as a zombie for the lifetime of this object. This method can be
+ // called multiple times.
+ int WaitForExit(bool* got_signaled);
+
+ // Wait for the |child_callback| passed at construction to run. Return false
+ // if |child_callback| did not finish running and we know it never will (for
+ // instance the child crashed or used _exit()).
+ bool WaitForClosureToRun();
+ base::ProcessId GetPid() { return child_process_id_; }
+
+ private:
+ bool IsOriginalProcess();
+
+ base::ProcessId child_process_id_;
+ base::ProcessId process_id_;
+ int pipe_fds_[2];
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcess);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_SCOPED_PROCESS_H_
diff --git a/sandbox/linux/services/scoped_process_unittest.cc b/sandbox/linux/services/scoped_process_unittest.cc
new file mode 100644
index 0000000000..8bd2847997
--- /dev/null
+++ b/sandbox/linux/services/scoped_process_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/scoped_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void DoExit() { _exit(0); }
+
+void ExitWithCode(int exit_code) { _exit(exit_code); }
+
+void RaiseAndExit(int signal) {
+ PCHECK(0 == raise(signal));
+ _exit(0);
+}
+
+void DoNothing() {}
+
+TEST(ScopedProcess, ScopedProcessNormalExit) {
+ const int kCustomExitCode = 12;
+ ScopedProcess process(base::Bind(&ExitWithCode, kCustomExitCode));
+ bool got_signaled = true;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_FALSE(got_signaled);
+ EXPECT_EQ(kCustomExitCode, exit_code);
+
+ // Verify that WaitForExit() can be called multiple times on the same
+ // process.
+ bool got_signaled2 = true;
+ int exit_code2 = process.WaitForExit(&got_signaled2);
+ EXPECT_FALSE(got_signaled2);
+ EXPECT_EQ(kCustomExitCode, exit_code2);
+}
+
+// Disable this test on Android, SIGABRT is funky there.
+TEST(ScopedProcess, DISABLE_ON_ANDROID(ScopedProcessAbort)) {
+ PCHECK(SIG_ERR != signal(SIGABRT, SIG_DFL));
+ ScopedProcess process(base::Bind(&RaiseAndExit, SIGABRT));
+ bool got_signaled = false;
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGABRT, exit_code);
+}
+
+TEST(ScopedProcess, ScopedProcessSignaled) {
+ ScopedProcess process(base::Bind(&DoNothing));
+ bool got_signaled = false;
+ ASSERT_EQ(0, kill(process.GetPid(), SIGKILL));
+ int exit_code = process.WaitForExit(&got_signaled);
+ EXPECT_TRUE(got_signaled);
+ EXPECT_EQ(SIGKILL, exit_code);
+}
+
+TEST(ScopedProcess, DiesForReal) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ { ScopedProcess process(base::Bind(&DoExit)); }
+
+ // Close writing end of the pipe.
+ write_end_closer.reset();
+ pipe_fds[1] = -1;
+
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c;
+ // If the child process is dead for real, there will be no writing end
+ // for this pipe left and read will EOF instead of returning EWOULDBLOCK.
+ ASSERT_EQ(0, read(pipe_fds[0], &c, 1));
+}
+
+TEST(ScopedProcess, SynchronizationBasic) {
+ ScopedProcess process1(base::Bind(&DoNothing));
+ EXPECT_TRUE(process1.WaitForClosureToRun());
+
+ ScopedProcess process2(base::Bind(&DoExit));
+ // The closure didn't finish running normally. This case is simple enough
+ // that process.WaitForClosureToRun() should return false, even though the
+ // API does not guarantees that it will return at all.
+ EXPECT_FALSE(process2.WaitForClosureToRun());
+}
+
+void SleepInMsAndWriteOneByte(int time_to_sleep, int fd) {
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(time_to_sleep));
+ CHECK(1 == write(fd, "1", 1));
+}
+
+TEST(ScopedProcess, SynchronizationWorks) {
+ int pipe_fds[2];
+ ASSERT_EQ(0, pipe(pipe_fds));
+ base::ScopedFD read_end_closer(pipe_fds[0]);
+ base::ScopedFD write_end_closer(pipe_fds[1]);
+
+ // Start a process with a closure that takes a little bit to run.
+ ScopedProcess process(
+ base::Bind(&SleepInMsAndWriteOneByte, 100, pipe_fds[1]));
+ EXPECT_TRUE(process.WaitForClosureToRun());
+
+ // Verify that the closure did, indeed, run.
+ ASSERT_EQ(0, fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK));
+ char c = 0;
+ EXPECT_EQ(1, read(pipe_fds[0], &c, 1));
+ EXPECT_EQ('1', c);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/syscall_wrappers.cc b/sandbox/linux/services/syscall_wrappers.cc
new file mode 100644
index 0000000000..264eb6842d
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers.cc
@@ -0,0 +1,246 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <cstring>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/capability.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+
+pid_t sys_getpid(void) {
+ return syscall(__NR_getpid);
+}
+
+pid_t sys_gettid(void) {
+ return syscall(__NR_gettid);
+}
+
+long sys_clone(unsigned long flags,
+ decltype(nullptr) child_stack,
+ pid_t* ptid,
+ pid_t* ctid,
+ decltype(nullptr) tls) {
+ const bool clone_tls_used = flags & CLONE_SETTLS;
+ const bool invalid_ctid =
+ (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) && !ctid;
+ const bool invalid_ptid = (flags & CLONE_PARENT_SETTID) && !ptid;
+
+ // We do not support CLONE_VM.
+ const bool clone_vm_used = flags & CLONE_VM;
+ if (clone_tls_used || invalid_ctid || invalid_ptid || clone_vm_used) {
+ RAW_LOG(FATAL, "Invalid usage of sys_clone");
+ }
+
+ if (ptid) MSAN_UNPOISON(ptid, sizeof(*ptid));
+ if (ctid) MSAN_UNPOISON(ctid, sizeof(*ctid));
+ // See kernel/fork.c in Linux. There is different ordering of sys_clone
+ // parameters depending on CONFIG_CLONE_BACKWARDS* configuration options.
+#if defined(ARCH_CPU_X86_64)
+ return syscall(__NR_clone, flags, child_stack, ptid, ctid, tls);
+#elif defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)
+ // CONFIG_CLONE_BACKWARDS defined.
+ return syscall(__NR_clone, flags, child_stack, ptid, tls, ctid);
+#endif
+}
+
+long sys_clone(unsigned long flags) {
+ return sys_clone(flags, nullptr, nullptr, nullptr, nullptr);
+}
+
+void sys_exit_group(int status) {
+ syscall(__NR_exit_group, status);
+}
+
+int sys_seccomp(unsigned int operation,
+ unsigned int flags,
+ const struct sock_fprog* args) {
+ return syscall(__NR_seccomp, operation, flags, args);
+}
+
+int sys_prlimit64(pid_t pid,
+ int resource,
+ const struct rlimit64* new_limit,
+ struct rlimit64* old_limit) {
+ int res = syscall(__NR_prlimit64, pid, resource, new_limit, old_limit);
+ if (res == 0 && old_limit) MSAN_UNPOISON(old_limit, sizeof(*old_limit));
+ return res;
+}
+
+int sys_capget(cap_hdr* hdrp, cap_data* datap) {
+ int res = syscall(__NR_capget, hdrp, datap);
+ if (res == 0) {
+ if (hdrp) MSAN_UNPOISON(hdrp, sizeof(*hdrp));
+ if (datap) MSAN_UNPOISON(datap, sizeof(*datap));
+ }
+ return res;
+}
+
+int sys_capset(cap_hdr* hdrp, const cap_data* datap) {
+ return syscall(__NR_capset, hdrp, datap);
+}
+
+int sys_getresuid(uid_t* ruid, uid_t* euid, uid_t* suid) {
+ int res;
+#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL)
+ // On 32-bit x86 or 32-bit arm, getresuid supports 16bit values only.
+ // Use getresuid32 instead.
+ res = syscall(__NR_getresuid32, ruid, euid, suid);
+#else
+ res = syscall(__NR_getresuid, ruid, euid, suid);
+#endif
+ if (res == 0) {
+ if (ruid) MSAN_UNPOISON(ruid, sizeof(*ruid));
+ if (euid) MSAN_UNPOISON(euid, sizeof(*euid));
+ if (suid) MSAN_UNPOISON(suid, sizeof(*suid));
+ }
+ return res;
+}
+
+int sys_getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid) {
+ int res;
+#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_ARMEL)
+ // On 32-bit x86 or 32-bit arm, getresgid supports 16bit values only.
+ // Use getresgid32 instead.
+ res = syscall(__NR_getresgid32, rgid, egid, sgid);
+#else
+ res = syscall(__NR_getresgid, rgid, egid, sgid);
+#endif
+ if (res == 0) {
+ if (rgid) MSAN_UNPOISON(rgid, sizeof(*rgid));
+ if (egid) MSAN_UNPOISON(egid, sizeof(*egid));
+ if (sgid) MSAN_UNPOISON(sgid, sizeof(*sgid));
+ }
+ return res;
+}
+
+int sys_chroot(const char* path) {
+ return syscall(__NR_chroot, path);
+}
+
+int sys_unshare(int flags) {
+ return syscall(__NR_unshare, flags);
+}
+
+int sys_sigprocmask(int how, const sigset_t* set, decltype(nullptr) oldset) {
+ // In some toolchain (in particular Android and PNaCl toolchain),
+ // sigset_t is 32 bits, but Linux ABI requires 64 bits.
+ uint64_t linux_value = 0;
+ std::memcpy(&linux_value, set, std::min(sizeof(sigset_t), sizeof(uint64_t)));
+ return syscall(__NR_rt_sigprocmask, how, &linux_value, nullptr,
+ sizeof(linux_value));
+}
+
+#if (defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
+ (defined(ARCH_CPU_X86_64) && !defined(__clang__))) && \
+ !defined(OS_NACL_NONSFI)
+// If MEMORY_SANITIZER or THREAD_SANITIZER is enabled, it is necessary to call
+// sigaction() here, rather than the direct syscall (sys_sigaction() defined
+// by ourselves).
+// It is because, if MEMORY_SANITIZER or THREAD_SANITIZER is enabled, sigaction
+// is wrapped, and |act->sa_handler| is injected in order to unpoisonize the
+// memory passed via callback's arguments for MEMORY_SANITIZER, or handle
+// signals to check thread consistency for THREAD_SANITIZER. Please see
+// msan_interceptors.cc and tsan_interceptors.cc for more details.
+// So, specifically, if MEMORY_SANITIZER is enabled while the direct syscall is
+// used, as MEMORY_SANITIZER does not know about it, sigaction() invocation in
+// other places would be broken (in more precise, returned |oldact| would have
+// a broken |sa_handler| callback).
+// Practically, it would break NaCl's signal handler installation.
+// cf) native_client/src/trusted/service_runtime/linux/nacl_signal.c.
+// As for THREAD_SANITIZER, the intercepted signal handlers are processed more
+// in other libc functions' interceptors (such as for raise()), so that it
+// would not work properly.
+//
+// Also on x86_64 architecture, we need naked function for rt_sigreturn.
+// However, there is no simple way to define it with GCC. Note that the body
+// of function is actually very small (only two instructions), but we need to
+// define much debug information in addition, otherwise backtrace() used by
+// base::StackTrace would not work so that some tests would fail.
+//
+// When this is built with PNaCl toolchain, we should always use sys_sigaction
+// below, because sigaction() provided by the toolchain is incompatible with
+// Linux's ABI. So, otherwise, it would just fail. Note that it is not
+// necessary to think about sigaction() invocation in other places even with
+// MEMORY_SANITIZER or THREAD_SANITIZER, because it would just fail there.
+int sys_sigaction(int signum,
+ const struct sigaction* act,
+ struct sigaction* oldact) {
+ return sigaction(signum, act, oldact);
+}
+#else
+// struct sigaction is different ABI from the Linux's.
+struct KernelSigAction {
+ void (*kernel_handler)(int);
+ uint32_t sa_flags;
+ void (*sa_restorer)(void);
+ uint64_t sa_mask;
+};
+
+// On X86_64 arch, it is necessary to set sa_restorer always.
+#if defined(ARCH_CPU_X86_64)
+#if !defined(SA_RESTORER)
+#define SA_RESTORER 0x04000000
+#endif
+
+// rt_sigreturn is a special system call that interacts with the user land
+// stack. Thus, here prologue must not be created, which implies syscall()
+// does not work properly, too. Note that rt_sigreturn will never return.
+static __attribute__((naked)) void sys_rt_sigreturn() {
+ // Just invoke rt_sigreturn system call.
+ asm volatile ("syscall\n"
+ :: "a"(__NR_rt_sigreturn));
+}
+#endif
+
+int sys_sigaction(int signum,
+ const struct sigaction* act,
+ struct sigaction* oldact) {
+ KernelSigAction kernel_act = {};
+ if (act) {
+ kernel_act.kernel_handler = act->sa_handler;
+ std::memcpy(&kernel_act.sa_mask, &act->sa_mask,
+ std::min(sizeof(kernel_act.sa_mask), sizeof(act->sa_mask)));
+ kernel_act.sa_flags = act->sa_flags;
+
+#if defined(ARCH_CPU_X86_64)
+ if (!(kernel_act.sa_flags & SA_RESTORER)) {
+ kernel_act.sa_flags |= SA_RESTORER;
+ kernel_act.sa_restorer = sys_rt_sigreturn;
+ }
+#endif
+ }
+
+ KernelSigAction kernel_oldact = {};
+ int result = syscall(__NR_rt_sigaction, signum, act ? &kernel_act : nullptr,
+ oldact ? &kernel_oldact : nullptr, sizeof(uint64_t));
+ if (result == 0 && oldact) {
+ oldact->sa_handler = kernel_oldact.kernel_handler;
+ sigemptyset(&oldact->sa_mask);
+ std::memcpy(&oldact->sa_mask, &kernel_oldact.sa_mask,
+ std::min(sizeof(kernel_act.sa_mask), sizeof(act->sa_mask)));
+ oldact->sa_flags = kernel_oldact.sa_flags;
+ }
+ return result;
+}
+
+#endif // defined(MEMORY_SANITIZER)
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/syscall_wrappers.h b/sandbox/linux/services/syscall_wrappers.h
new file mode 100644
index 0000000000..581425a367
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers.h
@@ -0,0 +1,83 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+#define SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "sandbox/sandbox_export.h"
+
+struct sock_fprog;
+struct rlimit64;
+struct cap_hdr;
+struct cap_data;
+
+namespace sandbox {
+
+// Provide direct system call wrappers for a few common system calls.
+// These are guaranteed to perform a system call and do not rely on things such
+// as caching the current pid (c.f. getpid()) unless otherwise specified.
+
+SANDBOX_EXPORT pid_t sys_getpid(void);
+
+SANDBOX_EXPORT pid_t sys_gettid(void);
+
+SANDBOX_EXPORT long sys_clone(unsigned long flags);
+
+// |regs| is not supported and must be passed as nullptr. |child_stack| must be
+// nullptr, since otherwise this function cannot safely return. As a
+// consequence, this function does not support CLONE_VM.
+SANDBOX_EXPORT long sys_clone(unsigned long flags,
+ decltype(nullptr) child_stack,
+ pid_t* ptid,
+ pid_t* ctid,
+ decltype(nullptr) regs);
+
+SANDBOX_EXPORT void sys_exit_group(int status);
+
+// The official system call takes |args| as void* (in order to be extensible),
+// but add more typing for the cases that are currently used.
+SANDBOX_EXPORT int sys_seccomp(unsigned int operation,
+ unsigned int flags,
+ const struct sock_fprog* args);
+
+// Some libcs do not expose a prlimit64 wrapper.
+SANDBOX_EXPORT int sys_prlimit64(pid_t pid,
+ int resource,
+ const struct rlimit64* new_limit,
+ struct rlimit64* old_limit);
+
+// Some libcs do not expose capget/capset wrappers. We want to use these
+// directly in order to avoid pulling in libcap2.
+SANDBOX_EXPORT int sys_capget(struct cap_hdr* hdrp, struct cap_data* datap);
+SANDBOX_EXPORT int sys_capset(struct cap_hdr* hdrp,
+ const struct cap_data* datap);
+
+// Some libcs do not expose getresuid/getresgid wrappers.
+SANDBOX_EXPORT int sys_getresuid(uid_t* ruid, uid_t* euid, uid_t* suid);
+SANDBOX_EXPORT int sys_getresgid(gid_t* rgid, gid_t* egid, gid_t* sgid);
+
+// Some libcs do not expose a chroot wrapper.
+SANDBOX_EXPORT int sys_chroot(const char* path);
+
+// Some libcs do not expose a unshare wrapper.
+SANDBOX_EXPORT int sys_unshare(int flags);
+
+// Some libcs do not expose a sigprocmask. Note that oldset must be a nullptr,
+// because of some ABI gap between toolchain's and Linux's.
+SANDBOX_EXPORT int sys_sigprocmask(int how,
+ const sigset_t* set,
+ decltype(nullptr) oldset);
+
+// Some libcs do not expose a sigaction().
+SANDBOX_EXPORT int sys_sigaction(int signum,
+ const struct sigaction* act,
+ struct sigaction* oldact);
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
diff --git a/sandbox/linux/services/syscall_wrappers_unittest.cc b/sandbox/linux/services/syscall_wrappers_unittest.cc
new file mode 100644
index 0000000000..1878ff3fe7
--- /dev/null
+++ b/sandbox/linux/services/syscall_wrappers_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <cstring>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+TEST(SyscallWrappers, BasicSyscalls) {
+ EXPECT_EQ(getpid(), sys_getpid());
+}
+
+TEST(SyscallWrappers, CloneBasic) {
+ pid_t child = sys_clone(SIGCHLD);
+ TestUtils::HandlePostForkReturn(child);
+ EXPECT_LT(0, child);
+}
+
+TEST(SyscallWrappers, CloneParentSettid) {
+ pid_t ptid = 0;
+ pid_t child = sys_clone(CLONE_PARENT_SETTID | SIGCHLD, nullptr, &ptid,
+ nullptr, nullptr);
+ TestUtils::HandlePostForkReturn(child);
+ EXPECT_LT(0, child);
+ EXPECT_EQ(child, ptid);
+}
+
+TEST(SyscallWrappers, CloneChildSettid) {
+ pid_t ctid = 0;
+ pid_t pid =
+ sys_clone(CLONE_CHILD_SETTID | SIGCHLD, nullptr, nullptr, &ctid, nullptr);
+
+ const int kSuccessExit = 0;
+ if (0 == pid) {
+ // In child.
+ if (sys_getpid() == ctid)
+ _exit(kSuccessExit);
+ _exit(1);
+ }
+
+ ASSERT_NE(-1, pid);
+ int status = 0;
+ ASSERT_EQ(pid, HANDLE_EINTR(waitpid(pid, &status, 0)));
+ ASSERT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(kSuccessExit, WEXITSTATUS(status));
+}
+
+TEST(SyscallWrappers, GetRESUid) {
+ uid_t ruid, euid, suid;
+ uid_t sys_ruid, sys_euid, sys_suid;
+ ASSERT_EQ(0, getresuid(&ruid, &euid, &suid));
+ ASSERT_EQ(0, sys_getresuid(&sys_ruid, &sys_euid, &sys_suid));
+ EXPECT_EQ(ruid, sys_ruid);
+ EXPECT_EQ(euid, sys_euid);
+ EXPECT_EQ(suid, sys_suid);
+}
+
+TEST(SyscallWrappers, GetRESGid) {
+ gid_t rgid, egid, sgid;
+ gid_t sys_rgid, sys_egid, sys_sgid;
+ ASSERT_EQ(0, getresgid(&rgid, &egid, &sgid));
+ ASSERT_EQ(0, sys_getresgid(&sys_rgid, &sys_egid, &sys_sgid));
+ EXPECT_EQ(rgid, sys_rgid);
+ EXPECT_EQ(egid, sys_egid);
+ EXPECT_EQ(sgid, sys_sgid);
+}
+
+TEST(SyscallWrappers, LinuxSigSet) {
+ sigset_t sigset;
+ ASSERT_EQ(0, sigemptyset(&sigset));
+ ASSERT_EQ(0, sigaddset(&sigset, LINUX_SIGSEGV));
+ ASSERT_EQ(0, sigaddset(&sigset, LINUX_SIGBUS));
+ uint64_t linux_sigset = 0;
+ std::memcpy(&linux_sigset, &sigset,
+ std::min(sizeof(sigset), sizeof(linux_sigset)));
+ EXPECT_EQ((1ULL << (LINUX_SIGSEGV - 1)) | (1ULL << (LINUX_SIGBUS - 1)),
+ linux_sigset);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.cc b/sandbox/linux/services/thread_helpers.cc
new file mode 100644
index 0000000000..80766a9bc5
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/thread_helpers.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "sandbox/linux/services/proc_util.h"
+
+namespace sandbox {
+
+namespace {
+
+const char kAssertSingleThreadedError[] =
+ "Current process is not mono-threaded!";
+
+bool IsSingleThreadedImpl(int proc_fd) {
+ CHECK_LE(0, proc_fd);
+ struct stat task_stat;
+ int fstat_ret = fstatat(proc_fd, "self/task/", &task_stat, 0);
+ PCHECK(0 == fstat_ret);
+
+ // At least "..", "." and the current thread should be present.
+ CHECK_LE(3UL, task_stat.st_nlink);
+ // Counting threads via /proc/self/task could be racy. For the purpose of
+ // determining if the current proces is monothreaded it works: if at any
+ // time it becomes monothreaded, it'll stay so.
+ return task_stat.st_nlink == 3;
+}
+
+bool IsThreadPresentInProcFS(int proc_fd,
+ const std::string& thread_id_dir_str) {
+ struct stat task_stat;
+ const int fstat_ret =
+ fstatat(proc_fd, thread_id_dir_str.c_str(), &task_stat, 0);
+ if (fstat_ret < 0) {
+ PCHECK(ENOENT == errno);
+ return false;
+ }
+ return true;
+}
+
+// Run |cb| in a loop until it returns false. Every time |cb| runs, sleep
+// for an exponentially increasing amount of time. |cb| is expected to return
+// false very quickly and this will crash if it doesn't happen within ~64ms on
+// Debug builds (2s on Release builds).
+// This is guaranteed to not sleep more than twice as much as the bare minimum
+// amount of time.
+void RunWhileTrue(const base::Callback<bool(void)>& cb) {
+#if defined(NDEBUG)
+ // In Release mode, crash after 30 iterations, which means having spent
+ // roughly 2s in
+ // nanosleep(2) cumulatively.
+ const unsigned int kMaxIterations = 30U;
+#else
+ // In practice, this never goes through more than a couple iterations. In
+ // debug mode, crash after 64ms (+ eventually 25 times the granularity of
+ // the clock) in nanosleep(2). This ensures that this is not becoming too
+ // slow.
+ const unsigned int kMaxIterations = 25U;
+#endif
+
+ // Run |cb| with an exponential back-off, sleeping 2^iterations nanoseconds
+ // in nanosleep(2).
+ // Note: the clock may not allow for nanosecond granularity, in this case the
+ // first iterations would sleep a tiny bit more instead, which would not
+ // change the calculations significantly.
+ for (unsigned int i = 0; i < kMaxIterations; ++i) {
+ if (!cb.Run()) {
+ return;
+ }
+
+ // Increase the waiting time exponentially.
+ struct timespec ts = {0, 1L << i /* nanoseconds */};
+ PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts)));
+ }
+
+ LOG(FATAL) << kAssertSingleThreadedError << " (iterations: " << kMaxIterations
+ << ")";
+
+ NOTREACHED();
+}
+
+bool IsMultiThreaded(int proc_fd) {
+ return !ThreadHelpers::IsSingleThreaded(proc_fd);
+}
+
+} // namespace
+
+// static
+bool ThreadHelpers::IsSingleThreaded(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ return IsSingleThreadedImpl(proc_fd);
+}
+
+// static
+bool ThreadHelpers::IsSingleThreaded() {
+ base::ScopedFD task_fd(ProcUtil::OpenProc());
+ return IsSingleThreaded(task_fd.get());
+}
+
+// static
+void ThreadHelpers::AssertSingleThreaded(int proc_fd) {
+ DCHECK_LE(0, proc_fd);
+ const base::Callback<bool(void)> cb = base::Bind(&IsMultiThreaded, proc_fd);
+ RunWhileTrue(cb);
+}
+
+void ThreadHelpers::AssertSingleThreaded() {
+ base::ScopedFD task_fd(ProcUtil::OpenProc());
+ AssertSingleThreaded(task_fd.get());
+}
+
+// static
+bool ThreadHelpers::StopThreadAndWatchProcFS(int proc_fd,
+ base::Thread* thread) {
+ DCHECK_LE(0, proc_fd);
+ DCHECK(thread);
+ const base::PlatformThreadId thread_id = thread->thread_id();
+ const std::string thread_id_dir_str =
+ "self/task/" + base::IntToString(thread_id) + "/";
+
+ // The kernel is at liberty to wake the thread id futex before updating
+ // /proc. Following Stop(), the thread is joined, but entries in /proc may
+ // not have been updated.
+ thread->Stop();
+
+ const base::Callback<bool(void)> cb =
+ base::Bind(&IsThreadPresentInProcFS, proc_fd, thread_id_dir_str);
+
+ RunWhileTrue(cb);
+
+ return true;
+}
+
+// static
+const char* ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests() {
+ return kAssertSingleThreadedError;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/thread_helpers.h b/sandbox/linux/services/thread_helpers.h
new file mode 100644
index 0000000000..f4abdffd03
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers.h
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
+#define SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace base { class Thread; }
+
+namespace sandbox {
+
+class SANDBOX_EXPORT ThreadHelpers {
+ public:
+ // Check whether the current process is single threaded. |proc_fd|
+ // must be a file descriptor to /proc/ and remains owned by the
+ // caller.
+ static bool IsSingleThreaded(int proc_fd);
+ static bool IsSingleThreaded();
+
+ // Crash if the current process is not single threaded. This will wait
+ // on /proc to be updated. In the case where this doesn't crash, this will
+ // return promptly. In the case where this does crash, this will first wait
+ // for a few ms in Debug mode, a few seconds in Release mode.
+ static void AssertSingleThreaded(int proc_fd);
+ static void AssertSingleThreaded();
+
+ // Stop |thread| and ensure that it does not have an entry in
+ // /proc/self/task/ from the point of view of the current thread. This is
+ // the way to stop threads before calling IsSingleThreaded().
+ static bool StopThreadAndWatchProcFS(int proc_fd, base::Thread* thread);
+
+ static const char* GetAssertSingleThreadedErrorMessageForTests();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadHelpers);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_THREAD_HELPERS_H_
diff --git a/sandbox/linux/services/thread_helpers_unittests.cc b/sandbox/linux/services/thread_helpers_unittests.cc
new file mode 100644
index 0000000000..7357a0cfa7
--- /dev/null
+++ b/sandbox/linux/services/thread_helpers_unittests.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/thread_helpers.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::PlatformThread;
+
+namespace sandbox {
+
+namespace {
+
+// These tests fail under ThreadSanitizer, see http://crbug.com/342305
+#if !defined(THREAD_SANITIZER)
+
+int GetRaceTestIterations() {
+ if (IsRunningOnValgrind()) {
+ return 2;
+ } else {
+ return 1000;
+ }
+}
+
+class ScopedProc {
+ public:
+ ScopedProc() : fd_(-1) {
+ fd_ = open("/proc/", O_RDONLY | O_DIRECTORY);
+ CHECK_LE(0, fd_);
+ }
+
+ ~ScopedProc() { PCHECK(0 == IGNORE_EINTR(close(fd_))); }
+
+ int fd() { return fd_; }
+
+ private:
+ int fd_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedProc);
+};
+
+TEST(ThreadHelpers, IsSingleThreadedBasic) {
+ ScopedProc proc_fd;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded());
+
+ base::Thread thread("sandbox_tests");
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded());
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread));
+}
+
+SANDBOX_TEST(ThreadHelpers, AssertSingleThreaded) {
+ ScopedProc proc_fd;
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded());
+
+ ThreadHelpers::AssertSingleThreaded(proc_fd.fd());
+ ThreadHelpers::AssertSingleThreaded();
+}
+
+TEST(ThreadHelpers, IsSingleThreadedIterated) {
+ ScopedProc proc_fd;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+
+ // Iterate to check for race conditions.
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ base::Thread thread("sandbox_tests");
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ // Explicitly stop the thread here to not pollute the next test.
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread));
+ }
+}
+
+TEST(ThreadHelpers, IsSingleThreadedStartAndStop) {
+ ScopedProc proc_fd;
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+
+ base::Thread thread("sandbox_tests");
+ // This is testing for a race condition, so iterate.
+ // Manually, this has been tested with more that 1M iterations.
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ ASSERT_TRUE(thread.Start());
+ ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+
+ ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread));
+ ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
+ ASSERT_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+ }
+}
+
+SANDBOX_TEST(ThreadHelpers, AssertSingleThreadedAfterThreadStopped) {
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded());
+
+ base::Thread thread1("sandbox_tests");
+ base::Thread thread2("sandbox_tests");
+
+ for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ SANDBOX_ASSERT(thread1.Start());
+ SANDBOX_ASSERT(thread2.Start());
+ SANDBOX_ASSERT(!ThreadHelpers::IsSingleThreaded());
+
+ thread1.Stop();
+ thread2.Stop();
+ // This will wait on /proc/ to reflect the state of threads in the
+ // process.
+ ThreadHelpers::AssertSingleThreaded();
+ SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded());
+ }
+}
+
+// Only run this test in Debug mode, where AssertSingleThreaded() will return
+// in less than 64ms.
+#if !defined(NDEBUG)
+SANDBOX_DEATH_TEST(
+ ThreadHelpers,
+ AssertSingleThreadedDies,
+ DEATH_MESSAGE(
+ ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests())) {
+ base::Thread thread1("sandbox_tests");
+ SANDBOX_ASSERT(thread1.Start());
+ ThreadHelpers::AssertSingleThreaded();
+}
+#endif // !defined(NDEBUG)
+
+#endif // !defined(THREAD_SANITIZER)
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/yama.cc b/sandbox/linux/services/yama.cc
new file mode 100644
index 0000000000..151f4bd340
--- /dev/null
+++ b/sandbox/linux/services/yama.cc
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/yama.h"
+
+#include <fcntl.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+#if !defined(PR_SET_PTRACER_ANY)
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#if !defined(PR_SET_PTRACER)
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+namespace sandbox {
+
+namespace {
+
+// Enable or disable the Yama ptracers restrictions.
+// Return false if Yama is not present on this kernel.
+bool SetYamaPtracersRestriction(bool enable_restrictions) {
+ unsigned long set_ptracer_arg;
+ if (enable_restrictions) {
+ set_ptracer_arg = 0;
+ } else {
+ set_ptracer_arg = PR_SET_PTRACER_ANY;
+ }
+
+ const int ret = prctl(PR_SET_PTRACER, set_ptracer_arg);
+ const int prctl_errno = errno;
+
+ if (0 == ret) {
+ return true;
+ } else {
+ // ENOSYS or EINVAL means Yama is not in the current kernel.
+ CHECK(ENOSYS == prctl_errno || EINVAL == prctl_errno);
+ return false;
+ }
+}
+
+bool CanAccessProcFS() {
+ static const char kProcfsKernelSysPath[] = "/proc/sys/kernel/";
+ int ret = access(kProcfsKernelSysPath, F_OK);
+ if (ret) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+// static
+bool Yama::RestrictPtracersToAncestors() {
+ return SetYamaPtracersRestriction(true /* enable_restrictions */);
+}
+
+// static
+bool Yama::DisableYamaRestrictions() {
+ return SetYamaPtracersRestriction(false /* enable_restrictions */);
+}
+
+// static
+int Yama::GetStatus() {
+ if (!CanAccessProcFS()) {
+ return 0;
+ }
+
+ static const char kPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope";
+
+ base::ScopedFD yama_scope(HANDLE_EINTR(open(kPtraceScopePath, O_RDONLY)));
+
+ if (!yama_scope.is_valid()) {
+ const int open_errno = errno;
+ DCHECK(ENOENT == open_errno);
+ // The status is known, yama is not present.
+ return STATUS_KNOWN;
+ }
+
+ char yama_scope_value = 0;
+ ssize_t num_read = HANDLE_EINTR(read(yama_scope.get(), &yama_scope_value, 1));
+ PCHECK(1 == num_read);
+
+ switch (yama_scope_value) {
+ case '0':
+ return STATUS_KNOWN | STATUS_PRESENT;
+ case '1':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING;
+ case '2':
+ case '3':
+ return STATUS_KNOWN | STATUS_PRESENT | STATUS_ENFORCING |
+ STATUS_STRICT_ENFORCING;
+ default:
+ NOTREACHED();
+ return 0;
+ }
+}
+
+// static
+bool Yama::IsPresent() { return GetStatus() & STATUS_PRESENT; }
+
+// static
+bool Yama::IsEnforcing() { return GetStatus() & STATUS_ENFORCING; }
+
+} // namespace sandbox
diff --git a/sandbox/linux/services/yama.h b/sandbox/linux/services/yama.h
new file mode 100644
index 0000000000..e6c5c45b2a
--- /dev/null
+++ b/sandbox/linux/services/yama.h
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_YAMA_H_
+#define SANDBOX_LINUX_SERVICES_YAMA_H_
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Yama is a LSM kernel module which can restrict ptrace().
+// This class provides ways to detect if Yama is present and enabled
+// and to restrict which processes can ptrace the current process.
+class SANDBOX_EXPORT Yama {
+ public:
+ // This enum should be used to set or check a bitmask.
+ // A value of 0 would indicate that the status is not known.
+ enum GlobalStatus {
+ STATUS_KNOWN = 1 << 0,
+ STATUS_PRESENT = 1 << 1,
+ STATUS_ENFORCING = 1 << 2,
+ // STATUS_STRICT_ENFORCING corresponds to either mode 2 or mode 3 of Yama.
+ // Ptrace could be entirely denied, or restricted to CAP_SYS_PTRACE
+ // and PTRACE_TRACEME.
+ STATUS_STRICT_ENFORCING = 1 << 3
+ };
+
+ // Restrict who can ptrace() the current process to its ancestors.
+ // If this succeeds, then Yama is available on this kernel.
+ // However, Yama may not be enforcing at this time.
+ static bool RestrictPtracersToAncestors();
+
+ // Disable Yama restrictions for the current process.
+ // This will fail if Yama is not available on this kernel.
+ // This is meant for testing only. If you need this, implement
+ // a per-pid authorization instead.
+ static bool DisableYamaRestrictions();
+
+ // Checks if Yama is currently in enforcing mode for the machine (not the
+ // current process). This requires access to the filesystem and will use
+ // /proc/sys/kernel/yama/ptrace_scope.
+ static int GetStatus();
+
+ // Helper for checking for STATUS_PRESENT in GetStatus().
+ static bool IsPresent();
+ // Helper for checkking for STATUS_ENFORCING in GetStatus().
+ static bool IsEnforcing();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Yama);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_YAMA_H_
diff --git a/sandbox/linux/services/yama_unittests.cc b/sandbox/linux/services/yama_unittests.cc
new file mode 100644
index 0000000000..204cfd6a44
--- /dev/null
+++ b/sandbox/linux/services/yama_unittests.cc
@@ -0,0 +1,172 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_util.h"
+#include "base/sys_info.h"
+#include "sandbox/linux/services/scoped_process.h"
+#include "sandbox/linux/services/yama.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool HasLinux32Bug() {
+#if defined(__i386__)
+ // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels.
+ // This is fixed in 3.4.
+ bool is_kernel_64bit =
+ base::SysInfo::OperatingSystemArchitecture() == "x86_64";
+ bool is_linux = base::SysInfo::OperatingSystemName() == "Linux";
+ bool is_3_dot_2 = base::StartsWithASCII(
+ base::SysInfo::OperatingSystemVersion(), "3.2", /*case_sensitive=*/false);
+ if (is_kernel_64bit && is_linux && is_3_dot_2)
+ return true;
+#endif // defined(__i386__)
+ return false;
+}
+
+bool CanPtrace(pid_t pid) {
+ int ret;
+ ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
+ if (ret == -1) {
+ CHECK_EQ(EPERM, errno);
+ return false;
+ }
+ // Wait for the process to be stopped so that it can be detached.
+ siginfo_t process_info;
+ int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED));
+ PCHECK(0 == wait_ret);
+ PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL));
+ return true;
+}
+
+// _exit(0) if pid can be ptraced by the current process.
+// _exit(1) otherwise.
+void ExitZeroIfCanPtrace(pid_t pid) {
+ if (CanPtrace(pid)) {
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+}
+
+bool CanSubProcessPtrace(pid_t pid) {
+ ScopedProcess process(base::Bind(&ExitZeroIfCanPtrace, pid));
+ bool signaled;
+ int exit_code = process.WaitForExit(&signaled);
+ CHECK(!signaled);
+ return 0 == exit_code;
+}
+
+// The tests below assume that the system-level configuration will not change
+// while they run.
+
+TEST(Yama, GetStatus) {
+ int status1 = Yama::GetStatus();
+
+ // Check that the value is a possible bitmask.
+ ASSERT_LE(0, status1);
+ ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING |
+ Yama::STATUS_STRICT_ENFORCING,
+ status1);
+
+ // The status should not just be a random value.
+ int status2 = Yama::GetStatus();
+ EXPECT_EQ(status1, status2);
+
+ // This test is not running sandboxed, there is no reason to not know the
+ // status.
+ EXPECT_NE(0, Yama::STATUS_KNOWN & status1);
+
+ if (status1 & Yama::STATUS_STRICT_ENFORCING) {
+ // If Yama is strictly enforcing, it is also enforcing.
+ EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING);
+ }
+
+ if (status1 & Yama::STATUS_ENFORCING) {
+ // If Yama is enforcing, Yama is present.
+ EXPECT_NE(0, status1 & Yama::STATUS_PRESENT);
+ }
+
+ // Verify that the helper functions work as intended.
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_ENFORCING),
+ Yama::IsEnforcing());
+ EXPECT_EQ(static_cast<bool>(status1 & Yama::STATUS_PRESENT),
+ Yama::IsPresent());
+
+ fprintf(stdout,
+ "Yama present: %s - enforcing: %s\n",
+ Yama::IsPresent() ? "Y" : "N",
+ Yama::IsEnforcing() ? "Y" : "N");
+}
+
+SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) {
+ // This call will succeed iff Yama is present.
+ bool restricted = Yama::RestrictPtracersToAncestors();
+ CHECK_EQ(restricted, Yama::IsPresent());
+}
+
+// Attempts to enable or disable Yama restrictions.
+void SetYamaRestrictions(bool enable_restriction) {
+ if (enable_restriction) {
+ Yama::RestrictPtracersToAncestors();
+ } else {
+ Yama::DisableYamaRestrictions();
+ }
+}
+
+TEST(Yama, RestrictPtraceWorks) {
+ if (HasLinux32Bug())
+ return;
+
+ ScopedProcess process1(base::Bind(&SetYamaRestrictions, true));
+ ASSERT_TRUE(process1.WaitForClosureToRun());
+
+ if (Yama::IsEnforcing()) {
+ // A sibling process cannot ptrace process1.
+ ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid()));
+ }
+
+ if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) {
+ // However, parent can ptrace process1.
+ ASSERT_TRUE(CanPtrace(process1.GetPid()));
+
+ // A sibling can ptrace process2 which disables any Yama protection.
+ ScopedProcess process2(base::Bind(&SetYamaRestrictions, false));
+ ASSERT_TRUE(process2.WaitForClosureToRun());
+ ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid()));
+ }
+}
+
+void DoNothing() {}
+
+SANDBOX_TEST(Yama, RestrictPtraceIsDefault) {
+ if (!Yama::IsPresent() || HasLinux32Bug())
+ return;
+
+ CHECK(Yama::DisableYamaRestrictions());
+ ScopedProcess process1(base::Bind(&DoNothing));
+
+ if (Yama::IsEnforcing()) {
+ // Check that process1 is protected by Yama, even though it has
+ // been created from a process that disabled Yama.
+ CHECK(!CanSubProcessPtrace(process1.GetPid()));
+ }
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/suid/client/DEPS b/sandbox/linux/suid/client/DEPS
new file mode 100644
index 0000000000..99a337d772
--- /dev/null
+++ b/sandbox/linux/suid/client/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/services",
+]
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc
new file mode 100644
index 0000000000..12ef7f9f40
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc
@@ -0,0 +1,151 @@
+// 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/suid/client/setuid_sandbox_client.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/environment.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+
+namespace {
+
+bool IsFileSystemAccessDenied() {
+ base::ScopedFD root_dir(HANDLE_EINTR(open("/", O_RDONLY)));
+ return !root_dir.is_valid();
+}
+
+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());
+ CHECK(environment);
+ return new SetuidSandboxClient(environment);
+}
+
+SetuidSandboxClient::SetuidSandboxClient(base::Environment* env)
+ : env_(env), sandboxed_(false) {
+}
+
+SetuidSandboxClient::~SetuidSandboxClient() {
+}
+
+void SetuidSandboxClient::CloseDummyFile() {
+ // When we're launched through the setuid sandbox, SetupLaunchOptions
+ // arranges for kZygoteIdFd to be a dummy file descriptor to satisfy an
+ // ancient setuid sandbox ABI requirement. However, the descriptor is no
+ // longer needed, so we can simply close it right away now.
+ CHECK(IsSuidSandboxChild());
+
+ // Sanity check that kZygoteIdFd refers to a pipe.
+ struct stat st;
+ PCHECK(0 == fstat(kZygoteIdFd, &st));
+ CHECK(S_ISFIFO(st.st_mode));
+
+ PCHECK(0 == IGNORE_EINTR(close(kZygoteIdFd)));
+}
+
+bool SetuidSandboxClient::ChrootMe() {
+ int ipc_fd = GetIPCDescriptor(env_.get());
+
+ 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_.get());
+ // If helper_pid is -1 we wait for any child.
+ if (HANDLE_EINTR(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.
+ CHECK(IsFileSystemAccessDenied());
+ sandboxed_ = true;
+ return true;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxUpToDate() const {
+ return GetHelperApi(env_.get()) == kSUIDSandboxApiNumber;
+}
+
+bool SetuidSandboxClient::IsSuidSandboxChild() const {
+ return GetIPCDescriptor(env_.get()) >= 0;
+}
+
+bool SetuidSandboxClient::IsInNewPIDNamespace() const {
+ return env_->HasVar(kSandboxPIDNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsInNewNETNamespace() const {
+ return env_->HasVar(kSandboxNETNSEnvironmentVarName);
+}
+
+bool SetuidSandboxClient::IsSandboxed() const {
+ return sandboxed_;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h
new file mode 100644
index 0000000000..026894fc27
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client.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_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
+
+#include "base/environment.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Helper class to use the setuid sandbox. This class is to be used
+// after being executed through the setuid helper.
+// This class is difficult to use. It has been created by refactoring very old
+// code scathered through the Chromium code base.
+//
+// A typical use for "A" launching a sandboxed process "B" would be:
+// (Steps 1 through 4 are described in setuid_sandbox_host.h.)
+// 5. B uses CloseDummyFile() to close the dummy file descriptor.
+// 6. B performs various initializations that require access to the file
+// system.
+// 6.b (optional) B uses sandbox::Credentials::HasOpenDirectory() to verify
+// that no directory is kept open (which would allow bypassing the setuid
+// sandbox).
+// 7. B should be prepared to assume the role of init(1). In particular, B
+// cannot receive any signal from any other process, excluding SIGKILL.
+// If B dies, all the processes in the namespace will die.
+// B can fork() and the parent can assume the role of init(1), by using
+// sandbox::CreateInitProcessReaper().
+// 8. B requests being chroot-ed through ChrootMe() and
+// requests other sandboxing status via the status functions.
+class SANDBOX_EXPORT SetuidSandboxClient {
+ public:
+ // All instantation should go through this factory method.
+ static SetuidSandboxClient* Create();
+ ~SetuidSandboxClient();
+
+ // Close the dummy file descriptor leftover from the sandbox ABI.
+ void CloseDummyFile();
+ // Ask the setuid helper over the setuid sandbox IPC channel to chroot() us
+ // to an empty directory.
+ // Will only work if we have been launched through the setuid helper.
+ 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;
+
+ private:
+ explicit SetuidSandboxClient(base::Environment* env);
+
+ // Holds the environment. Will never be NULL.
+ scoped_ptr<base::Environment> env_;
+ bool sandboxed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetuidSandboxClient);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
new file mode 100644
index 0000000000..2acd8fb5fc
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
@@ -0,0 +1,46 @@
+// 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/suid/client/setuid_sandbox_client.h"
+
+#include "base/environment.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+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/sandbox/linux/suid/client/setuid_sandbox_host.cc b/sandbox/linux/suid/client/setuid_sandbox_host.cc
new file mode 100644
index 0000000000..71171ebd4f
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_host.cc
@@ -0,0 +1,195 @@
+// Copyright 2015 The Chromium 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/suid/client/setuid_sandbox_host.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/launch.h"
+#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.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));
+}
+
+// Unset environment variables that are expected to be set by the setuid
+// sandbox. This is to allow nesting of one instance of the SUID sandbox
+// inside another.
+void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) {
+ DCHECK(env_map);
+ const base::NativeEnvironmentString environment_vars[] = {
+ sandbox::kSandboxDescriptorEnvironmentVarName,
+ sandbox::kSandboxHelperPidEnvironmentVarName,
+ sandbox::kSandboxEnvironmentApiProvides,
+ sandbox::kSandboxPIDNSEnvironmentVarName,
+ sandbox::kSandboxNETNSEnvironmentVarName,
+ };
+
+ for (size_t i = 0; i < arraysize(environment_vars); ++i) {
+ // Setting values in EnvironmentMap to an empty-string will make
+ // sure that they get unset from the environment via AlterEnvironment().
+ (*env_map)[environment_vars[i]] = base::NativeEnvironmentString();
+ }
+}
+
+// Wrapper around a shared C function.
+// Returns the "saved" environment variable name corresponding to |envvar|
+// in a new string or NULL.
+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());
+ }
+}
+
+const char* GetDevelSandboxPath() {
+ return getenv("CHROME_DEVEL_SANDBOX");
+}
+
+} // namespace
+
+namespace sandbox {
+
+SetuidSandboxHost* SetuidSandboxHost::Create() {
+ base::Environment* environment(base::Environment::Create());
+ CHECK(environment);
+ return new SetuidSandboxHost(environment);
+}
+
+SetuidSandboxHost::SetuidSandboxHost(base::Environment* env) : env_(env) {
+}
+
+SetuidSandboxHost::~SetuidSandboxHost() {
+}
+
+// Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables
+// the setuid sandbox. TODO(jln): fix this (crbug.com/245376).
+bool SetuidSandboxHost::IsDisabledViaEnvironment() {
+ const char* devel_sandbox_path = GetDevelSandboxPath();
+ if (devel_sandbox_path && '\0' == *devel_sandbox_path) {
+ return true;
+ }
+ return false;
+}
+
+base::FilePath SetuidSandboxHost::GetSandboxBinaryPath() {
+ base::FilePath sandbox_binary;
+ base::FilePath exe_dir;
+ if (PathService::Get(base::DIR_EXE, &exe_dir)) {
+ base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox");
+ if (base::PathExists(sandbox_candidate))
+ sandbox_binary = sandbox_candidate;
+ }
+
+ // In user-managed builds, including development builds, an environment
+ // variable is required to enable the sandbox. See
+ // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
+ struct stat st;
+ if (sandbox_binary.empty() && stat(base::kProcSelfExe, &st) == 0 &&
+ st.st_uid == getuid()) {
+ const char* devel_sandbox_path = GetDevelSandboxPath();
+ if (devel_sandbox_path) {
+ sandbox_binary = base::FilePath(devel_sandbox_path);
+ }
+ }
+
+ return sandbox_binary;
+}
+
+void SetuidSandboxHost::PrependWrapper(base::CommandLine* cmd_line) {
+ std::string sandbox_binary(GetSandboxBinaryPath().value());
+ struct stat st;
+ if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) {
+ LOG(FATAL) << "The SUID sandbox helper binary is missing: "
+ << sandbox_binary << " Aborting now. See "
+ "https://code.google.com/p/chromium/wiki/"
+ "LinuxSUIDSandboxDevelopment.";
+ }
+
+ if (access(sandbox_binary.c_str(), X_OK) != 0 || (st.st_uid != 0) ||
+ ((st.st_mode & S_ISUID) == 0) || ((st.st_mode & S_IXOTH)) == 0) {
+ LOG(FATAL) << "The SUID sandbox helper binary was found, but is not "
+ "configured correctly. Rather than run without sandboxing "
+ "I'm aborting now. You need to make sure that "
+ << sandbox_binary << " is owned by root and has mode 4755.";
+ }
+
+ cmd_line->PrependWrapper(sandbox_binary);
+}
+
+void SetuidSandboxHost::SetupLaunchOptions(
+ base::LaunchOptions* options,
+ base::FileHandleMappingVector* fds_to_remap,
+ base::ScopedFD* dummy_fd) {
+ DCHECK(options);
+ DCHECK(fds_to_remap);
+
+ // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used.
+ options->allow_new_privs = true;
+ UnsetExpectedEnvironmentVariables(&options->environ);
+
+ // Set dummy_fd to the reading end of a closed pipe.
+ int pipe_fds[2];
+ PCHECK(0 == pipe(pipe_fds));
+ PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1])));
+ dummy_fd->reset(pipe_fds[0]);
+
+ // We no longer need a dummy socket for discovering the child's PID,
+ // but the sandbox is still hard-coded to expect a file descriptor at
+ // kZygoteIdFd. Fixing this requires a sandbox API change. :(
+ fds_to_remap->push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd));
+}
+
+void SetuidSandboxHost::SetupLaunchEnvironment() {
+ SaveSUIDUnsafeEnvironmentVariables(env_.get());
+ SetSandboxAPIEnvironmentVariable(env_.get());
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/suid/client/setuid_sandbox_host.h b/sandbox/linux/suid/client/setuid_sandbox_host.h
new file mode 100644
index 0000000000..6788892441
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_host.h
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium 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_HOST_H_
+#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_HOST_H_
+
+#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/launch.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Helper class to use the setuid sandbox. This class is to be used
+// before launching the setuid helper.
+// This class is difficult to use. It has been created by refactoring very old
+// code scathered through the Chromium code base.
+//
+// A typical use for "A" launching a sandboxed process "B" would be:
+// 1. A calls SetupLaunchEnvironment()
+// 2. A sets up a base::CommandLine and then amends it with
+// PrependWrapper() (or manually, by relying on GetSandboxBinaryPath()).
+// 3. A uses SetupLaunchOptions() to arrange for a dummy descriptor for the
+// setuid sandbox ABI.
+// 4. A launches B with base::LaunchProcess, using the amended
+// base::CommandLine.
+// (The remaining steps are described within setuid_sandbox_client.h.)
+class SANDBOX_EXPORT SetuidSandboxHost {
+ public:
+ // All instantation should go through this factory method.
+ static SetuidSandboxHost* Create();
+ ~SetuidSandboxHost();
+
+ // The setuid sandbox may still be disabled via the environment.
+ // This is tracked in crbug.com/245376.
+ bool IsDisabledViaEnvironment();
+ // Get the sandbox binary path. This method knows about the
+ // CHROME_DEVEL_SANDBOX environment variable used for user-managed builds. If
+ // the sandbox binary cannot be found, it will return an empty FilePath.
+ base::FilePath GetSandboxBinaryPath();
+ // Modify |cmd_line| to launch via the setuid sandbox. Crash if the setuid
+ // sandbox binary cannot be found. |cmd_line| must not be NULL.
+ void PrependWrapper(base::CommandLine* cmd_line);
+ // Set-up the launch options for launching via the setuid sandbox. Caller is
+ // responsible for keeping |dummy_fd| alive until LaunchProcess() completes.
+ // |options| and |fds_to_remap| must not be NULL.
+ // (Keeping |dummy_fd| alive is an unfortunate historical artifact of the
+ // chrome-sandbox ABI.)
+ void SetupLaunchOptions(base::LaunchOptions* options,
+ base::FileHandleMappingVector* fds_to_remap,
+ base::ScopedFD* dummy_fd);
+ // Set-up the environment. This should be done prior to launching the setuid
+ // helper.
+ void SetupLaunchEnvironment();
+
+ private:
+ explicit SetuidSandboxHost(base::Environment* env);
+
+ // Holds the environment. Will never be NULL.
+ scoped_ptr<base::Environment> env_;
+
+ DISALLOW_COPY_AND_ASSIGN(SetuidSandboxHost);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_HOST_H_
diff --git a/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc
new file mode 100644
index 0000000000..8415abb064
--- /dev/null
+++ b/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Chromium 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/suid/client/setuid_sandbox_host.h"
+
+#include <string>
+
+#include "base/environment.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "sandbox/linux/suid/common/sandbox.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(SetuidSandboxHost, 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<SetuidSandboxHost> sandbox_host(SetuidSandboxHost::Create());
+ EXPECT_TRUE(sandbox_host != NULL);
+
+ // Make sure the environment is clean.
+ EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiRequest));
+ EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiProvides));
+
+ sandbox_host->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"));
+ }
+}
+
+// This test doesn't accomplish much, but will make sure that analysis tools
+// will run this codepath.
+TEST(SetuidSandboxHost, GetSandboxBinaryPath) {
+ scoped_ptr<SetuidSandboxHost> setuid_sandbox_host(
+ SetuidSandboxHost::Create());
+ ignore_result(setuid_sandbox_host->GetSandboxBinaryPath());
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/suid/common/sandbox.h b/sandbox/linux/suid/common/sandbox.h
new file mode 100644
index 0000000000..99eb7b5120
--- /dev/null
+++ b/sandbox/linux/suid/common/sandbox.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_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 kSuidSandboxGetApiSwitch[] = "--get-api";
+static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score";
+
+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/sandbox/linux/suid/common/suid_unsafe_environment_variables.h b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h
new file mode 100644
index 0000000000..33ba4b6ab7
--- /dev/null
+++ b/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/sandbox/linux/suid/process_util.h b/sandbox/linux/suid/process_util.h
new file mode 100644
index 0000000000..9fb9a8791a
--- /dev/null
+++ b/sandbox/linux/suid/process_util.h
@@ -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.
+
+// 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>
+
+// 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.
+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.
+bool AdjustLowMemoryMargin(int64_t margin_mb);
+
+#endif // SANDBOX_LINUX_SUID_PROCESS_UTIL_H_
diff --git a/sandbox/linux/suid/process_util_linux.c b/sandbox/linux/suid/process_util_linux.c
new file mode 100644
index 0000000000..8d9a53c3a4
--- /dev/null
+++ b/sandbox/linux/suid/process_util_linux.c
@@ -0,0 +1,78 @@
+// 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.
+
+// Needed for O_DIRECTORY, must be defined before fcntl.h is included
+// (and it can be included earlier than the explicit #include below
+// in some versions of glibc).
+#define _GNU_SOURCE
+
+#include "sandbox/linux/suid/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;
+
+// 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 == (ssize_t)len);
+}
diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c
new file mode 100644
index 0000000000..3049ae5211
--- /dev/null
+++ b/sandbox/linux/suid/sandbox.c
@@ -0,0 +1,480 @@
+// 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 "sandbox/linux/suid/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 "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
+#include "sandbox/linux/suid/process_util.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);
+}
+
+static void ExitWithErrorSignalHandler(int signal) {
+ const char msg[] = "\nThe setuid sandbox got signaled, exiting.\n";
+ if (-1 == write(2, msg, sizeof(msg) - 1)) {
+ // Do nothing.
+ }
+
+ _exit(1);
+}
+
+// We will chroot() to the helper's /proc/self directory. Anything there will
+// not exist anymore if we make sure to wait() for the helper.
+//
+// /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;
+
+ // Don't "Core" on SIGABRT. SIGABRT is sent by the Chrome OS session manager
+ // when things are hanging.
+ // Here, the current process is going to waitid() and _exit(), so there is no
+ // point in generating a crash report. The child process is the one
+ // blocking us.
+ if (signal(SIGABRT, ExitWithErrorSignalHandler) == SIG_ERR) {
+ FatalError("Failed to change signal handler");
+ }
+
+ int wait_ret =
+ HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED));
+
+ 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);
+ const int clone_errno = errno;
+
+ 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 EINVAL then the system doesn't support the requested flags, so
+ // continue to try a different set.
+ // On any other errno value the system *does* support these flags but
+ // something went wrong, hence we bail with an error message rather then
+ // provide less security.
+ if (errno != EINVAL) {
+ fprintf(stderr, "Failed to move to new namespace:");
+ if (kCloneExtraFlags[i] & CLONE_NEWPID) {
+ fprintf(stderr, " PID namespaces supported,");
+ }
+ if (kCloneExtraFlags[i] & CLONE_NEWNET) {
+ fprintf(stderr, " Network namespace supported,");
+ }
+ fprintf(stderr, " but failed: errno = %s\n", strerror(clone_errno));
+ return false;
+ }
+ }
+
+ // 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;
+ }
+
+ // We cannot adjust /proc/pid/oom_adj for sandboxed renderers
+ // because those files are owned by root. So we need a 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);
+ }
+
+ // Protect the core setuid sandbox functionality with an API version
+ if (!CheckAndExportApiVersion()) {
+ return 1;
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr,
+ "The setuid sandbox is not running as root. Common causes:\n"
+ " * An unprivileged process using ptrace on it, like a debugger.\n"
+ " * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n");
+ }
+
+ if (!MoveToNewNamespaces())
+ return 1;
+ if (!SpawnChrootHelper())
+ return 1;
+ if (!DropRoot())
+ return 1;
+ if (!SetupChildEnvironment())
+ return 1;
+
+ execv(argv[1], &argv[1]);
+ FatalError("execv failed");
+
+ return 1;
+}
diff --git a/sandbox/linux/syscall_broker/DEPS b/sandbox/linux/syscall_broker/DEPS
new file mode 100644
index 0000000000..70d9b18aa1
--- /dev/null
+++ b/sandbox/linux/syscall_broker/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+sandbox/linux/system_headers",
+]
diff --git a/sandbox/linux/syscall_broker/broker_channel.cc b/sandbox/linux/syscall_broker/broker_channel.cc
new file mode 100644
index 0000000000..fa0f7615fc
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_channel.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "base/logging.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// static
+void BrokerChannel::CreatePair(EndPoint* reader, EndPoint* writer) {
+ DCHECK(reader);
+ DCHECK(writer);
+ int socket_pair[2];
+ // Use SOCK_SEQPACKET, to preserve message boundaries but we also want to be
+ // notified (recvmsg should return and not block) when the connection has
+ // been broken which could mean that the other end has been closed.
+ PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, socket_pair));
+
+ reader->reset(socket_pair[0]);
+ PCHECK(0 == shutdown(reader->get(), SHUT_WR));
+
+ writer->reset(socket_pair[1]);
+ PCHECK(0 == shutdown(writer->get(), SHUT_RD));
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_channel.h b/sandbox/linux/syscall_broker/broker_channel.h
new file mode 100644
index 0000000000..2abdba413a
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_channel.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// A small class to create a pipe-like communication channel. It is based on a
+// SOCK_SEQPACKET unix socket, which is connection-based and guaranteed to
+// preserve message boundaries.
+class BrokerChannel {
+ public:
+ typedef base::ScopedFD EndPoint;
+ static void CreatePair(EndPoint* reader, EndPoint* writer);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BrokerChannel);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CHANNEL_H_
diff --git a/sandbox/linux/syscall_broker/broker_client.cc b/sandbox/linux/syscall_broker/broker_client.cc
new file mode 100644
index 0000000000..760cf59b3c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_client.cc
@@ -0,0 +1,144 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_client.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "build/build_config.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+
+#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
+#define MSG_CMSG_CLOEXEC 0x40000000
+#endif
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// 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 BrokerClient::PathAndFlagsSyscall(IPCCommand syscall_type,
+ const char* pathname,
+ int flags) const {
+ int recvmsg_flags = 0;
+ RAW_CHECK(syscall_type == COMMAND_OPEN || syscall_type == COMMAND_ACCESS);
+ 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 == COMMAND_OPEN && (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;
+ flags &= ~O_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 == COMMAND_OPEN &&
+ !broker_policy_.GetFileNameIfAllowedToOpen(
+ pathname, flags, NULL /* file_to_open */,
+ NULL /* unlink_after_open */)) {
+ return -broker_policy_.denied_errno();
+ }
+ if (syscall_type == COMMAND_ACCESS &&
+ !broker_policy_.GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
+ return -broker_policy_.denied_errno();
+ }
+ }
+
+ base::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 = base::UnixDomainSocket::SendRecvMsgWithFlags(
+ ipc_channel_.get(), 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;
+ }
+
+ base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ base::PickleIterator iter(read_pickle);
+ int return_value = -1;
+ // Now deserialize the return value and eventually return the file
+ // descriptor.
+ if (iter.ReadInt(&return_value)) {
+ switch (syscall_type) {
+ case COMMAND_ACCESS:
+ // We should never have a fd to return.
+ RAW_CHECK(returned_fd == -1);
+ return return_value;
+ case COMMAND_OPEN:
+ 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 -ENOMEM;
+ }
+}
+
+BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
+ BrokerChannel::EndPoint ipc_channel,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : broker_policy_(broker_policy),
+ ipc_channel_(ipc_channel.Pass()),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests) {
+}
+
+BrokerClient::~BrokerClient() {
+}
+
+int BrokerClient::Access(const char* pathname, int mode) const {
+ return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
+}
+
+int BrokerClient::Open(const char* pathname, int flags) const {
+ return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_client.h b/sandbox/linux/syscall_broker/broker_client.h
new file mode 100644
index 0000000000..2dfef8150c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_client.h
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerPolicy;
+
+// This class can be embedded in a sandboxed process and can be
+// used to perform certain system calls in another, presumably
+// non-sandboxed process (which embeds BrokerHost).
+// A key feature of this class is the ability to use some of its methods in a
+// thread-safe and async-signal safe way. The goal is to be able to use it to
+// replace the open() or access() system calls happening anywhere in a process
+// (as allowed for instance by seccomp-bpf's SIGSYS mechanism).
+class BrokerClient {
+ public:
+ // |policy| needs to match the policy used by BrokerHost. This
+ // allows to predict some of the requests which will be denied
+ // and save an IPC round trip.
+ // |ipc_channel| needs to be a suitable SOCK_SEQPACKET unix socket.
+ // |fast_check_in_client| should be set to true and
+ // |quiet_failures_for_tests| to false unless you are writing tests.
+ BrokerClient(const BrokerPolicy& policy,
+ BrokerChannel::EndPoint ipc_channel,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests);
+ ~BrokerClient();
+
+ // Can be used in place of access().
+ // X_OK will always return an error in practice since the broker process
+ // doesn't support execute permissions.
+ // It's similar to the access() system call and will return -errno on errors.
+ // This is async signal safe.
+ int Access(const char* pathname, int mode) const;
+ // Can be used in place of open().
+ // 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.
+ // This is async signal safe.
+ int Open(const char* pathname, int flags) const;
+
+ // Get the file descriptor used for IPC. This is used for tests.
+ int GetIPCDescriptor() const { return ipc_channel_.get(); }
+
+ private:
+ const BrokerPolicy& broker_policy_;
+ const BrokerChannel::EndPoint ipc_channel_;
+ const bool fast_check_in_client_; // Whether to forward a request that we
+ // know will be denied to the broker. (Used
+ // for tests).
+ const bool quiet_failures_for_tests_; // Disable certain error message when
+ // testing for failures.
+
+ int PathAndFlagsSyscall(IPCCommand syscall_type,
+ const char* pathname,
+ int flags) const;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerClient);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
diff --git a/sandbox/linux/syscall_broker/broker_common.h b/sandbox/linux/syscall_broker/broker_common.h
new file mode 100644
index 0000000000..25aafa7ed2
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_common.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMON_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMON_H_
+
+#include <fcntl.h>
+#include <stddef.h>
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+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().
+const int kCurrentProcessOpenFlagsMask = O_CLOEXEC;
+
+enum IPCCommand {
+ COMMAND_INVALID = 0,
+ COMMAND_OPEN,
+ COMMAND_ACCESS,
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMON_H_
diff --git a/sandbox/linux/syscall_broker/broker_file_permission.cc b/sandbox/linux/syscall_broker/broker_file_permission.cc
new file mode 100644
index 0000000000..beceda93f5
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission.cc
@@ -0,0 +1,243 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+#include <fcntl.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// Async signal safe
+bool BrokerFilePermission::ValidatePath(const char* path) {
+ if (!path)
+ return false;
+
+ const size_t len = strlen(path);
+ // No empty paths
+ if (len == 0)
+ return false;
+ // Paths must be absolute and not relative
+ if (path[0] != '/')
+ return false;
+ // No trailing / (but "/" is valid)
+ if (len > 1 && path[len - 1] == '/')
+ return false;
+ // No trailing /..
+ if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' &&
+ path[len - 1] == '.')
+ return false;
+ // No /../ anywhere
+ for (size_t i = 0; i < len; i++) {
+ if (path[i] == '/' && (len - i) > 3) {
+ if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Async signal safe
+// Calls std::string::c_str(), strncmp and strlen. All these
+// methods are async signal safe in common standard libs.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::MatchPath(const char* requested_filename) const {
+ const char* path = path_.c_str();
+ if ((recursive_ && strncmp(requested_filename, path, strlen(path)) == 0)) {
+ // Note: This prefix match will allow any path under the whitelisted
+ // path, for any number of directory levels. E.g. if the whitelisted
+ // path is /good/ then the following will be permitted by the policy.
+ // /good/file1
+ // /good/folder/file2
+ // /good/folder/folder2/file3
+ // If an attacker could make 'folder' a symlink to ../../ they would have
+ // access to the entire filesystem.
+ // Whitelisting with multiple depths is useful, e.g /proc/ but
+ // the system needs to ensure symlinks can not be created!
+ // That said if an attacker can convert any of the absolute paths
+ // to a symlink they can control any file on the system also.
+ return true;
+ } else if (strcmp(requested_filename, path) == 0) {
+ return true;
+ }
+ return false;
+}
+
+// Async signal safe.
+// External call to std::string::c_str() is
+// called in MatchPath.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::CheckAccess(const char* requested_filename,
+ int mode,
+ const char** file_to_access) const {
+ // First, check if |mode| is existence, ability to read or ability
+ // to write. We do not support X_OK.
+ if (mode != F_OK && mode & ~(R_OK | W_OK)) {
+ return false;
+ }
+
+ if (!ValidatePath(requested_filename))
+ return false;
+
+ if (!MatchPath(requested_filename)) {
+ return false;
+ }
+ bool allowed = false;
+ switch (mode) {
+ case F_OK:
+ if (allow_read_ || allow_write_)
+ allowed = true;
+ break;
+ case R_OK:
+ if (allow_read_)
+ allowed = true;
+ break;
+ case W_OK:
+ if (allow_write_)
+ allowed = true;
+ break;
+ case R_OK | W_OK:
+ if (allow_read_ && allow_write_)
+ allowed = true;
+ break;
+ default:
+ return false;
+ }
+
+ if (allowed && file_to_access) {
+ if (!recursive_)
+ *file_to_access = path_.c_str();
+ else
+ *file_to_access = requested_filename;
+ }
+ return allowed;
+}
+
+// Async signal safe.
+// External call to std::string::c_str() is
+// called in MatchPath.
+// TODO(leecam): remove dependency on std::string
+bool BrokerFilePermission::CheckOpen(const char* requested_filename,
+ int flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const {
+ if (!ValidatePath(requested_filename))
+ return false;
+
+ if (!MatchPath(requested_filename)) {
+ return false;
+ }
+
+ // First, check the access mode is valid.
+ const int access_mode = flags & O_ACCMODE;
+ if (access_mode != O_RDONLY && access_mode != O_WRONLY &&
+ access_mode != O_RDWR) {
+ return false;
+ }
+
+ // Check if read is allowed
+ if (!allow_read_ && (access_mode == O_RDONLY || access_mode == O_RDWR)) {
+ return false;
+ }
+
+ // Check if write is allowed
+ if (!allow_write_ && (access_mode == O_WRONLY || access_mode == O_RDWR)) {
+ return false;
+ }
+
+ // Check if file creation is allowed.
+ if (!allow_create_ && (flags & O_CREAT)) {
+ return false;
+ }
+
+ // If O_CREAT is present, ensure O_EXCL
+ if ((flags & O_CREAT) && !(flags & O_EXCL)) {
+ return false;
+ }
+
+ // If this file is to be unlinked, ensure it's created.
+ if (unlink_ && !(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) {
+ 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;
+
+ if (has_unknown_flags)
+ return false;
+
+ if (file_to_open) {
+ if (!recursive_)
+ *file_to_open = path_.c_str();
+ else
+ *file_to_open = requested_filename;
+ }
+ if (unlink_after_open)
+ *unlink_after_open = unlink_;
+
+ return true;
+}
+
+const char* BrokerFilePermission::GetErrorMessageForTests() {
+ static char kInvalidBrokerFileString[] = "Invalid BrokerFilePermission";
+ return kInvalidBrokerFileString;
+}
+
+BrokerFilePermission::BrokerFilePermission(const std::string& path,
+ bool recursive,
+ bool unlink,
+ bool allow_read,
+ bool allow_write,
+ bool allow_create)
+ : path_(path),
+ recursive_(recursive),
+ unlink_(unlink),
+ allow_read_(allow_read),
+ allow_write_(allow_write),
+ allow_create_(allow_create) {
+ // Validate this permission and die if invalid!
+
+ // Must have enough length for a '/'
+ CHECK(path_.length() > 0) << GetErrorMessageForTests();
+ // Whitelisted paths must be absolute.
+ CHECK(path_[0] == '/') << GetErrorMessageForTests();
+
+ // Don't allow unlinking on creation without create permission
+ if (unlink_) {
+ CHECK(allow_create) << GetErrorMessageForTests();
+ }
+ const char last_char = *(path_.rbegin());
+ // Recursive paths must have a trailing slash
+ if (recursive_) {
+ CHECK(last_char == '/') << GetErrorMessageForTests();
+ } else {
+ CHECK(last_char != '/') << GetErrorMessageForTests();
+ }
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox \ No newline at end of file
diff --git a/sandbox/linux/syscall_broker/broker_file_permission.h b/sandbox/linux/syscall_broker/broker_file_permission.h
new file mode 100644
index 0000000000..03300d1d74
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission.h
@@ -0,0 +1,119 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+// BrokerFilePermission defines a path for whitelisting.
+// Pick the correct static factory method to create a permission.
+// CheckOpen and CheckAccess are async signal safe.
+// Constuction and Destruction are not async signal safe.
+// |path| is the path to be whitelisted.
+class SANDBOX_EXPORT BrokerFilePermission {
+ public:
+ ~BrokerFilePermission() {}
+ BrokerFilePermission(const BrokerFilePermission&) = default;
+ BrokerFilePermission& operator=(const BrokerFilePermission&) = default;
+
+ static BrokerFilePermission ReadOnly(const std::string& path) {
+ return BrokerFilePermission(path, false, false, true, false, false);
+ }
+
+ static BrokerFilePermission ReadOnlyRecursive(const std::string& path) {
+ return BrokerFilePermission(path, true, false, true, false, false);
+ }
+
+ static BrokerFilePermission WriteOnly(const std::string& path) {
+ return BrokerFilePermission(path, false, false, false, true, false);
+ }
+
+ static BrokerFilePermission ReadWrite(const std::string& path) {
+ return BrokerFilePermission(path, false, false, true, true, false);
+ }
+
+ static BrokerFilePermission ReadWriteCreate(const std::string& path) {
+ return BrokerFilePermission(path, false, false, true, true, true);
+ }
+
+ static BrokerFilePermission ReadWriteCreateUnlink(const std::string& path) {
+ return BrokerFilePermission(path, false, true, true, true, true);
+ }
+
+ static BrokerFilePermission ReadWriteCreateUnlinkRecursive(
+ const std::string& path) {
+ return BrokerFilePermission(path, true, true, true, true, true);
+ }
+
+ // Returns true if |requested_filename| is allowed to be opened
+ // by this permission.
+ // If |file_to_open| is not NULL it is set to point to either
+ // the |requested_filename| in the case of a recursive match,
+ // or a pointer the matched path in the whitelist if an absolute
+ // match.
+ // If not NULL |unlink_after_open| is set to point to true if the
+ // caller should unlink the path after openning.
+ // Async signal safe if |file_to_open| is NULL.
+ bool CheckOpen(const char* requested_filename,
+ int flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const;
+ // Returns true if |requested_filename| is allowed to be accessed
+ // by this permission as per access(2).
+ // If |file_to_open| is not NULL it is set to point to either
+ // the |requested_filename| in the case of a recursive match,
+ // or a pointer to the matched path in the whitelist if an absolute
+ // match.
+ // |mode| is per mode argument of access(2).
+ // Async signal safe if |file_to_access| is NULL
+ bool CheckAccess(const char* requested_filename,
+ int mode,
+ const char** file_to_access) const;
+
+ private:
+ friend class BrokerFilePermissionTester;
+ BrokerFilePermission(const std::string& path,
+ bool recursive,
+ bool unlink,
+ bool allow_read,
+ bool allow_write,
+ bool allow_create);
+
+ // ValidatePath checks |path| and returns true if these conditions are met
+ // * Greater than 0 length
+ // * Is an absolute path
+ // * No trailing slash
+ // * No /../ path traversal
+ static bool ValidatePath(const char* path);
+
+ // MatchPath returns true if |requested_filename| is covered by this instance
+ bool MatchPath(const char* requested_filename) const;
+
+ // Used in by BrokerFilePermissionTester for tests.
+ static const char* GetErrorMessageForTests();
+
+ // These are not const as std::vector requires copy-assignment and this class
+ // is stored in vectors. All methods are marked const so
+ // the compiler will still enforce no changes outside of the constructor.
+ std::string path_;
+ bool recursive_; // Allow everything under this path. |path| must be a dir.
+ bool unlink_; // unlink after opening.
+ bool allow_read_;
+ bool allow_write_;
+ bool allow_create_;
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_ \ No newline at end of file
diff --git a/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
new file mode 100644
index 0000000000..b58a901cde
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerFilePermissionTester {
+ public:
+ static bool ValidatePath(const char* path) {
+ return BrokerFilePermission::ValidatePath(path);
+ }
+ static const char* GetErrorMessage() {
+ return BrokerFilePermission::GetErrorMessageForTests();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BrokerFilePermissionTester);
+};
+
+namespace {
+
+// Creation tests are DEATH tests as a bad permission causes termination.
+SANDBOX_TEST(BrokerFilePermission, CreateGood) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_TEST(BrokerFilePermission, CreateGoodRecursive) {
+ const char kPath[] = "/tmp/good/";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBad,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "/tmp/bad/";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBadRecursive,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "/tmp/bad";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBadNotAbs,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "tmp/bad";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+SANDBOX_DEATH_TEST(
+ BrokerFilePermission,
+ CreateBadEmpty,
+ DEATH_MESSAGE(BrokerFilePermissionTester::GetErrorMessage())) {
+ const char kPath[] = "";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+}
+
+// CheckPerm tests |path| against |perm| given |access_flags|.
+// If |create| is true then file creation is tested for success.
+void CheckPerm(const BrokerFilePermission& perm,
+ const char* path,
+ int access_flags,
+ bool create) {
+ const char* file_to_open = NULL;
+
+ ASSERT_FALSE(perm.CheckAccess(path, X_OK, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, F_OK, NULL));
+ // check bad perms
+ switch (access_flags) {
+ case O_RDONLY:
+ ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
+ ASSERT_FALSE(perm.CheckAccess(path, W_OK, NULL));
+ break;
+ case O_WRONLY:
+ ASSERT_FALSE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+ ASSERT_FALSE(perm.CheckAccess(path, R_OK, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
+ break;
+ case O_RDWR:
+ ASSERT_TRUE(perm.CheckOpen(path, O_RDONLY, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckOpen(path, O_WRONLY, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckOpen(path, O_RDWR, &file_to_open, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, R_OK, NULL));
+ ASSERT_TRUE(perm.CheckAccess(path, W_OK, NULL));
+ break;
+ default:
+ // Bad test case
+ NOTREACHED();
+ }
+
+// O_SYNC can be defined as (__O_SYNC|O_DSYNC)
+#ifdef O_DSYNC
+ const int kSyncFlag = O_SYNC & ~O_DSYNC;
+#else
+ const int kSyncFlag = O_SYNC;
+#endif
+
+ const int kNumberOfBitsInOAccMode = 2;
+ static_assert(O_ACCMODE == ((1 << kNumberOfBitsInOAccMode) - 1),
+ "incorrect number of bits");
+ // check every possible flag and act accordingly.
+ // Skipping AccMode bits as they are present in every case.
+ for (int i = kNumberOfBitsInOAccMode; i < 32; i++) {
+ int flag = 1 << i;
+ switch (flag) {
+ case O_APPEND:
+ case O_ASYNC:
+ case O_DIRECT:
+ case O_DIRECTORY:
+#ifdef O_DSYNC
+ case O_DSYNC:
+#endif
+ case O_EXCL:
+ case O_LARGEFILE:
+ case O_NOATIME:
+ case O_NOCTTY:
+ case O_NOFOLLOW:
+ case O_NONBLOCK:
+#if (O_NONBLOCK != O_NDELAY)
+ case O_NDELAY:
+#endif
+ case kSyncFlag:
+ case O_TRUNC:
+ ASSERT_TRUE(
+ perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
+ break;
+ case O_CLOEXEC:
+ case O_CREAT:
+ default:
+ ASSERT_FALSE(
+ perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
+ }
+ }
+ if (create) {
+ bool unlink;
+ ASSERT_TRUE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
+ &file_to_open, &unlink));
+ ASSERT_FALSE(unlink);
+ } else {
+ ASSERT_FALSE(perm.CheckOpen(path, O_CREAT | O_EXCL | access_flags,
+ &file_to_open, NULL));
+ }
+}
+
+TEST(BrokerFilePermission, ReadOnly) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnly(kPath);
+ CheckPerm(perm, kPath, O_RDONLY, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadOnlyRecursive) {
+ const char kPath[] = "/tmp/good/";
+ const char kPathFile[] = "/tmp/good/file";
+ BrokerFilePermission perm = BrokerFilePermission::ReadOnlyRecursive(kPath);
+ CheckPerm(perm, kPathFile, O_RDONLY, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, WriteOnly) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::WriteOnly(kPath);
+ CheckPerm(perm, kPath, O_WRONLY, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadWrite) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadWrite(kPath);
+ CheckPerm(perm, kPath, O_RDWR, false);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadWriteCreate) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm = BrokerFilePermission::ReadWriteCreate(kPath);
+ CheckPerm(perm, kPath, O_RDWR, true);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+void CheckUnlink(BrokerFilePermission& perm,
+ const char* path,
+ int access_flags) {
+ bool unlink;
+ ASSERT_FALSE(perm.CheckOpen(path, access_flags, NULL, &unlink));
+ ASSERT_FALSE(perm.CheckOpen(path, access_flags | O_CREAT, NULL, &unlink));
+ ASSERT_TRUE(
+ perm.CheckOpen(path, access_flags | O_CREAT | O_EXCL, NULL, &unlink));
+ ASSERT_TRUE(unlink);
+}
+
+TEST(BrokerFilePermission, ReadWriteCreateUnlink) {
+ const char kPath[] = "/tmp/good";
+ BrokerFilePermission perm =
+ BrokerFilePermission::ReadWriteCreateUnlink(kPath);
+ CheckUnlink(perm, kPath, O_RDWR);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ReadWriteCreateUnlinkRecursive) {
+ const char kPath[] = "/tmp/good/";
+ const char kPathFile[] = "/tmp/good/file";
+ BrokerFilePermission perm =
+ BrokerFilePermission::ReadWriteCreateUnlinkRecursive(kPath);
+ CheckUnlink(perm, kPathFile, O_RDWR);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerFilePermission, ValidatePath) {
+ EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/path"));
+ EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/"));
+ EXPECT_TRUE(BrokerFilePermissionTester::ValidatePath("/..path"));
+
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath(""));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("bad/"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/.."));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/bad/../bad"));
+ EXPECT_FALSE(BrokerFilePermissionTester::ValidatePath("/../bad"));
+}
+
+} // namespace
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_host.cc b/sandbox/linux/syscall_broker/broker_host.cc
new file mode 100644
index 0000000000..e5957ed224
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_host.cc
@@ -0,0 +1,231 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_host.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+namespace {
+
+bool IsRunningOnValgrind() {
+ return RUNNING_ON_VALGRIND;
+}
+
+// A little open(2) wrapper to handle some oddities for us. In the general case
+// make a direct system call since we want to keep in control of the broker
+// process' system calls profile to be able to loosely sandbox it.
+int sys_open(const char* pathname, int flags) {
+ // Hardcode mode to rw------- when creating files.
+ int mode;
+ if (flags & O_CREAT) {
+ mode = 0600;
+ } else {
+ mode = 0;
+ }
+ if (IsRunningOnValgrind()) {
+ // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
+ return open(pathname, flags, mode);
+ } else {
+ return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
+ }
+}
+
+// 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 OpenFileForIPC(const BrokerPolicy& policy,
+ const std::string& requested_filename,
+ int flags,
+ base::Pickle* write_pickle,
+ std::vector<int>* opened_files) {
+ DCHECK(write_pickle);
+ DCHECK(opened_files);
+ const char* file_to_open = NULL;
+ bool unlink_after_open = false;
+ const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen(
+ requested_filename.c_str(), flags, &file_to_open, &unlink_after_open);
+
+ if (safe_to_open_file) {
+ CHECK(file_to_open);
+ int opened_fd = sys_open(file_to_open, flags);
+ if (opened_fd < 0) {
+ write_pickle->WriteInt(-errno);
+ } else {
+ // Success.
+ if (unlink_after_open) {
+ unlink(file_to_open);
+ }
+ opened_files->push_back(opened_fd);
+ write_pickle->WriteInt(0);
+ }
+ } else {
+ write_pickle->WriteInt(-policy.denied_errno());
+ }
+}
+
+// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
+// policy. Write the syscall return value (-errno) to |write_pickle|.
+void AccessFileForIPC(const BrokerPolicy& policy,
+ const std::string& requested_filename,
+ int mode,
+ base::Pickle* write_pickle) {
+ DCHECK(write_pickle);
+ const char* file_to_access = NULL;
+ const bool safe_to_access_file = policy.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(-policy.denied_errno());
+ }
+}
+
+// Handle a |command_type| request contained in |iter| and send the reply
+// on |reply_ipc|.
+// Currently COMMAND_OPEN and COMMAND_ACCESS are supported.
+bool HandleRemoteCommand(const BrokerPolicy& policy,
+ IPCCommand command_type,
+ int reply_ipc,
+ base::PickleIterator iter) {
+ // Currently all commands have two arguments: filename and flags.
+ std::string requested_filename;
+ int flags = 0;
+ if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ return false;
+
+ base::Pickle write_pickle;
+ std::vector<int> opened_files;
+
+ switch (command_type) {
+ case COMMAND_ACCESS:
+ AccessFileForIPC(policy, requested_filename, flags, &write_pickle);
+ break;
+ case COMMAND_OPEN:
+ OpenFileForIPC(
+ policy, requested_filename, flags, &write_pickle, &opened_files);
+ break;
+ default:
+ LOG(ERROR) << "Invalid IPC command";
+ break;
+ }
+
+ CHECK_LE(write_pickle.size(), kMaxMessageLength);
+ ssize_t sent = base::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 = IGNORE_EINTR(close(*it));
+ DCHECK(!ret) << "Could not close file descriptor";
+ }
+
+ if (sent <= 0) {
+ LOG(ERROR) << "Could not send IPC reply";
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+BrokerHost::BrokerHost(const BrokerPolicy& broker_policy,
+ BrokerChannel::EndPoint ipc_channel)
+ : broker_policy_(broker_policy), ipc_channel_(ipc_channel.Pass()) {
+}
+
+BrokerHost::~BrokerHost() {
+}
+
+// Handle a request on the IPC channel ipc_channel_.
+// 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.
+BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
+ ScopedVector<base::ScopedFD> fds;
+ char buf[kMaxMessageLength];
+ errno = 0;
+ const ssize_t msg_len = base::UnixDomainSocket::RecvMsg(
+ ipc_channel_.get(), buf, sizeof(buf), &fds);
+
+ if (msg_len == 0 || (msg_len == -1 && errno == ECONNRESET)) {
+ // EOF from the client, or the client died, we should die.
+ return RequestStatus::LOST_CLIENT;
+ }
+
+ // The client should send exactly one file descriptor, on which we
+ // will write the reply.
+ // TODO(mdempsky): ScopedVector doesn't have 'at()', only 'operator[]'.
+ if (msg_len < 0 || fds.size() != 1 || fds[0]->get() < 0) {
+ PLOG(ERROR) << "Error reading message from the client";
+ return RequestStatus::FAILURE;
+ }
+
+ base::ScopedFD temporary_ipc(fds[0]->Pass());
+
+ base::Pickle pickle(buf, msg_len);
+ base::PickleIterator iter(pickle);
+ int command_type;
+ if (iter.ReadInt(&command_type)) {
+ bool command_handled = false;
+ // Go through all the possible IPC messages.
+ switch (command_type) {
+ case COMMAND_ACCESS:
+ case COMMAND_OPEN:
+ // We reply on the file descriptor sent to us via the IPC channel.
+ command_handled = HandleRemoteCommand(
+ broker_policy_, static_cast<IPCCommand>(command_type),
+ temporary_ipc.get(), iter);
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ if (command_handled) {
+ return RequestStatus::SUCCESS;
+ } else {
+ return RequestStatus::FAILURE;
+ }
+
+ NOTREACHED();
+ }
+
+ LOG(ERROR) << "Error parsing IPC request";
+ return RequestStatus::FAILURE;
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_host.h b/sandbox/linux/syscall_broker/broker_host.h
new file mode 100644
index 0000000000..9866507d1c
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_host.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerPolicy;
+
+// The BrokerHost class should be embedded in a (presumably not sandboxed)
+// process. It will honor IPC requests from a BrokerClient sent over
+// |ipc_channel| according to |broker_policy|.
+class BrokerHost {
+ public:
+ enum class RequestStatus { LOST_CLIENT = 0, SUCCESS, FAILURE };
+
+ BrokerHost(const BrokerPolicy& broker_policy,
+ BrokerChannel::EndPoint ipc_channel);
+ ~BrokerHost();
+
+ RequestStatus HandleRequest() const;
+
+ private:
+ const BrokerPolicy& broker_policy_;
+ const BrokerChannel::EndPoint ipc_channel_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerHost);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_HOST_H_
diff --git a/sandbox/linux/syscall_broker/broker_policy.cc b/sandbox/linux/syscall_broker/broker_policy.cc
new file mode 100644
index 0000000000..d9f69e3b81
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_policy.cc
@@ -0,0 +1,99 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "sandbox/linux/syscall_broker/broker_common.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+BrokerPolicy::BrokerPolicy(int denied_errno,
+ const std::vector<BrokerFilePermission>& permissions)
+ : denied_errno_(denied_errno),
+ permissions_(permissions),
+ num_of_permissions_(permissions.size()) {
+ // The spec guarantees vectors store their elements contiguously
+ // so set up a pointer to array of element so it can be used
+ // in async signal safe code instead of vector operations.
+ if (num_of_permissions_ > 0) {
+ permissions_array_ = &permissions_[0];
+ } else {
+ permissions_array_ = NULL;
+ }
+}
+
+BrokerPolicy::~BrokerPolicy() {
+}
+
+// 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() for 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 BrokerPolicy::GetFileNameIfAllowedToAccess(
+ const char* requested_filename,
+ int requested_mode,
+ const char** file_to_access) const {
+ if (file_to_access && *file_to_access) {
+ // 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_access should be NULL");
+ return false;
+ }
+ for (size_t i = 0; i < num_of_permissions_; i++) {
+ if (permissions_array_[i].CheckAccess(requested_filename, requested_mode,
+ file_to_access)) {
+ return true;
+ }
+ }
+ 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 BrokerPolicy::GetFileNameIfAllowedToOpen(const char* requested_filename,
+ int requested_flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const {
+ 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;
+ }
+ for (size_t i = 0; i < num_of_permissions_; i++) {
+ if (permissions_array_[i].CheckOpen(requested_filename, requested_flags,
+ file_to_open, unlink_after_open)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/syscall_broker/broker_policy.h b/sandbox/linux/syscall_broker/broker_policy.h
new file mode 100644
index 0000000000..d5146edc06
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_policy.h
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
+
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+
+#include "sandbox/linux/syscall_broker/broker_file_permission.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+// BrokerPolicy allows to define the security policy enforced by a
+// BrokerHost. The BrokerHost will evaluate requests sent over its
+// IPC channel according to the BrokerPolicy.
+// Some of the methods of this class can be used in an async-signal safe
+// way.
+class BrokerPolicy {
+ public:
+ // |denied_errno| is the error code returned when IPC requests for system
+ // calls such as open() or access() are denied because a file is not in the
+ // whitelist. EACCESS would be a typical value.
+ // |permissions| is a list of BrokerPermission objects that define
+ // what the broker will allow.
+ BrokerPolicy(int denied_errno,
+ const std::vector<BrokerFilePermission>& permissions);
+
+ ~BrokerPolicy();
+
+ // 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
+ // If |file_to_open| is not NULL, a pointer to the path will be returned.
+ // In the case of a recursive match, this will be the requested_filename,
+ // otherwise it will return the matching pointer from the
+ // whitelist. For paranoia a caller should then use |file_to_access|. See
+ // GetFileNameIfAllowedToOpen() for 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 GetFileNameIfAllowedToAccess(const char* requested_filename,
+ int requested_mode,
+ const char** file_to_access) const;
+
+ // Check if |requested_filename| can be opened with flags |requested_flags|.
+ // If |file_to_open| is not NULL, a pointer to the path will be returned.
+ // In the case of a recursive match, this will be the requested_filename,
+ // otherwise it 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.
+ // |unlink_after_open| if not NULL will be set to point to true if the
+ // policy requests the caller unlink the path after opening.
+ // Return true if opening should be allowed, false otherwise.
+ // Async signal safe if and only if |file_to_open| is NULL.
+ bool GetFileNameIfAllowedToOpen(const char* requested_filename,
+ int requested_flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const;
+ int denied_errno() const { return denied_errno_; }
+
+ private:
+ const int denied_errno_;
+ // The permissions_ vector is used as storage for the BrokerFilePermission
+ // objects but is not referenced outside of the constructor as
+ // vectors are unfriendly in async signal safe code.
+ const std::vector<BrokerFilePermission> permissions_;
+ // permissions_array_ is set up to point to the backing store of
+ // permissions_ and is used in async signal safe methods.
+ const BrokerFilePermission* permissions_array_;
+ const size_t num_of_permissions_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerPolicy);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
diff --git a/sandbox/linux/syscall_broker/broker_process.cc b/sandbox/linux/syscall_broker/broker_process.cc
new file mode 100644
index 0000000000..81131cc4e0
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_process.cc
@@ -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.
+
+#include "sandbox/linux/syscall_broker/broker_process.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "build/build_config.h"
+#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
+#include "sandbox/linux/syscall_broker/broker_host.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+BrokerProcess::BrokerProcess(
+ int denied_errno,
+ const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : initialized_(false),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests),
+ broker_pid_(-1),
+ policy_(denied_errno, permissions) {
+}
+
+BrokerProcess::~BrokerProcess() {
+ if (initialized_) {
+ if (broker_client_.get()) {
+ // Closing the socket should be enough to notify the child to die,
+ // unless it has been duplicated.
+ CloseChannel();
+ }
+ PCHECK(0 == kill(broker_pid_, SIGKILL));
+ siginfo_t process_info;
+ // Reap the child.
+ int ret = HANDLE_EINTR(waitid(P_PID, broker_pid_, &process_info, WEXITED));
+ PCHECK(0 == ret);
+ }
+}
+
+bool BrokerProcess::Init(
+ const base::Callback<bool(void)>& broker_process_init_callback) {
+ CHECK(!initialized_);
+ BrokerChannel::EndPoint ipc_reader;
+ BrokerChannel::EndPoint ipc_writer;
+ BrokerChannel::CreatePair(&ipc_reader, &ipc_writer);
+
+#if !defined(THREAD_SANITIZER)
+ DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
+#endif
+ int child_pid = fork();
+ if (child_pid == -1) {
+ return false;
+ }
+ if (child_pid) {
+ // We are the parent and we have just forked our broker process.
+ ipc_reader.reset();
+ broker_pid_ = child_pid;
+ broker_client_.reset(new BrokerClient(policy_, ipc_writer.Pass(),
+ fast_check_in_client_,
+ quiet_failures_for_tests_));
+ initialized_ = true;
+ return true;
+ } else {
+ // We are the broker process. Make sure to close the writer's end so that
+ // we get notified if the client disappears.
+ ipc_writer.reset();
+ CHECK(broker_process_init_callback.Run());
+ BrokerHost broker_host(policy_, ipc_reader.Pass());
+ for (;;) {
+ switch (broker_host.HandleRequest()) {
+ case BrokerHost::RequestStatus::LOST_CLIENT:
+ _exit(1);
+ case BrokerHost::RequestStatus::SUCCESS:
+ case BrokerHost::RequestStatus::FAILURE:
+ continue;
+ }
+ }
+ _exit(1);
+ }
+ NOTREACHED();
+ return false;
+}
+
+void BrokerProcess::CloseChannel() {
+ broker_client_.reset();
+}
+
+int BrokerProcess::Access(const char* pathname, int mode) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Access(pathname, mode);
+}
+
+int BrokerProcess::Open(const char* pathname, int flags) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Open(pathname, flags);
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox.
diff --git a/sandbox/linux/syscall_broker/broker_process.h b/sandbox/linux/syscall_broker/broker_process.h
new file mode 100644
index 0000000000..8a512a0c12
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_process.h
@@ -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.
+
+#ifndef SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+#define SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/pickle.h"
+#include "base/process/process.h"
+#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerClient;
+class BrokerFilePermission;
+
+// Create a new "broker" process to which we can send requests via an IPC
+// channel by forking the current process.
+// 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 SANDBOX_EXPORT BrokerProcess {
+ public:
+ // |denied_errno| is the error code returned when methods such as Open()
+ // or Access() are invoked on a file which is not in the whitelist. EACCESS
+ // would be a typical value.
+ // |allowed_r_files| and |allowed_w_files| are white lists of files that can
+ // be opened later via the Open() API, respectively for reading and writing.
+ // A file available read-write should be listed in both.
+ // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
+ // unit tests, don't use it.
+
+ BrokerProcess(
+ int denied_errno,
+ const std::vector<syscall_broker::BrokerFilePermission>& permissions,
+ 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().
+ // broker_process_init_callback will be called in the new broker process,
+ // after fork() returns.
+ bool Init(const base::Callback<bool(void)>& broker_process_init_callback);
+
+ // Can be used in place of access(). Will be async signal safe.
+ // X_OK will always return an error in practice since the broker process
+ // 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:
+ friend class BrokerProcessTestHelper;
+
+ // Close the IPC channel with the other party. This should only be used
+ // by tests an none of the class methods should be used afterwards.
+ void CloseChannel();
+
+ bool initialized_; // Whether we've been through Init() yet.
+ const bool fast_check_in_client_;
+ const bool quiet_failures_for_tests_;
+ pid_t broker_pid_; // The PID of the broker (child).
+ syscall_broker::BrokerPolicy policy_; // The sandboxing policy.
+ scoped_ptr<syscall_broker::BrokerClient> broker_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerProcess);
+};
+
+} // namespace syscall_broker
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SERVICES_BROKER_PROCESS_H_
diff --git a/sandbox/linux/syscall_broker/broker_process_unittest.cc b/sandbox/linux/syscall_broker/broker_process_unittest.cc
new file mode 100644
index 0000000000..9ad0e719de
--- /dev/null
+++ b/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -0,0 +1,656 @@
+// 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/syscall_broker/broker_process.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/posix/unix_domain_socket_linux.h"
+#include "sandbox/linux/syscall_broker/broker_client.h"
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace syscall_broker {
+
+class BrokerProcessTestHelper {
+ public:
+ static void CloseChannel(BrokerProcess* broker) { broker->CloseChannel(); }
+ // Get the client's IPC descriptor to send IPC requests directly.
+ // TODO(jln): refator tests to get rid of this.
+ static int GetIPCDescriptor(const BrokerProcess* broker) {
+ return broker->broker_client_->GetIPCDescriptor();
+ }
+};
+
+namespace {
+
+bool NoOpCallback() {
+ return true;
+}
+
+} // namespace
+
+TEST(BrokerProcess, CreateAndDestroy) {
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+
+ scoped_ptr<BrokerProcess> open_broker(new BrokerProcess(EPERM, permissions));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ // Destroy the broker and check it has exited properly.
+ open_broker.reset();
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+}
+
+TEST(BrokerProcess, TestOpenAccessNull) {
+ std::vector<BrokerFilePermission> empty;
+ BrokerProcess open_broker(EPERM, empty);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ 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, int denied_errno) {
+ const char kR_WhiteListed[] = "/proc/DOESNOTEXIST1";
+ // We can't debug the init process, and shouldn't be able to access
+ // its auxv file.
+ 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<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kR_WhiteListed));
+ permissions.push_back(
+ BrokerFilePermission::ReadOnly(kR_WhiteListedButDenied));
+ permissions.push_back(BrokerFilePermission::WriteOnly(kW_WhiteListed));
+ permissions.push_back(BrokerFilePermission::ReadWrite(kRW_WhiteListed));
+
+ BrokerProcess open_broker(denied_errno, permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ 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, -denied_errno);
+ fd = open_broker.Open(kR_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ int ret = -1;
+ ret = open_broker.Access(kR_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kR_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ // 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, -denied_errno);
+ fd = open_broker.Open(kR_WhiteListedButDenied, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, F_OK);
+ // The normal permission system will let us check that the file exists.
+ ASSERT_EQ(ret, 0);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK);
+ ASSERT_EQ(ret, -EACCES);
+ ret = open_broker.Access(kR_WhiteListedButDenied, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kR_WhiteListedButDenied, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ }
+
+ fd = open_broker.Open(kW_WhiteListed, O_RDONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(kW_WhiteListed, O_WRONLY);
+ ASSERT_EQ(fd, -ENOENT);
+ fd = open_broker.Open(kW_WhiteListed, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, F_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, W_OK);
+ ASSERT_EQ(ret, -ENOENT);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(kW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ 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, -denied_errno);
+ ret = open_broker.Access(kRW_WhiteListed, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ fd = open_broker.Open(k_NotWhitelisted, O_RDONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(k_NotWhitelisted, O_WRONLY);
+ ASSERT_EQ(fd, -denied_errno);
+ fd = open_broker.Open(k_NotWhitelisted, O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, F_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | W_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+ ret = open_broker.Access(k_NotWhitelisted, R_OK | X_OK);
+ ASSERT_EQ(ret, -denied_errno);
+
+ // We have some extra sanity check for clearly wrong values.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDONLY | O_WRONLY | O_RDWR);
+ ASSERT_EQ(fd, -denied_errno);
+
+ // It makes no sense to allow O_CREAT in a 2-parameters open. Ensure this
+ // is denied.
+ fd = open_broker.Open(kRW_WhiteListed, O_RDWR | O_CREAT);
+ ASSERT_EQ(fd, -denied_errno);
+}
+
+// Run the same thing twice. The second time, we make sure that no security
+// check is performed on the client.
+TEST(BrokerProcess, OpenFilePermsWithClientCheck) {
+ TestOpenFilePerms(true /* fast_check_in_client */, EPERM);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheck) {
+ TestOpenFilePerms(false /* fast_check_in_client */, EPERM);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+// Run the same twice again, but with ENOENT instead of EPERM.
+TEST(BrokerProcess, OpenFilePermsWithClientCheckNoEnt) {
+ TestOpenFilePerms(true /* fast_check_in_client */, ENOENT);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenOpenFilePermsNoClientCheckNoEnt) {
+ TestOpenFilePerms(false /* fast_check_in_client */, ENOENT);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+void TestBadPaths(bool fast_check_in_client) {
+ const char kFileCpuInfo[] = "/proc/cpuinfo";
+ const char kNotAbsPath[] = "proc/cpuinfo";
+ const char kDotDotStart[] = "/../proc/cpuinfo";
+ const char kDotDotMiddle[] = "/proc/self/../cpuinfo";
+ const char kDotDotEnd[] = "/proc/..";
+ const char kTrailingSlash[] = "/proc/";
+
+ std::vector<BrokerFilePermission> permissions;
+
+ permissions.push_back(BrokerFilePermission::ReadOnlyRecursive("/proc/"));
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, permissions, fast_check_in_client));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ ASSERT_GE(cpuinfo_fd, 0);
+
+ int fd = -1;
+ int can_access;
+
+ can_access = open_broker->Access(kNotAbsPath, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kNotAbsPath, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kDotDotStart, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kDotDotStart, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kDotDotMiddle, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kDotDotMiddle, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kDotDotEnd, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kDotDotEnd, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+
+ can_access = open_broker->Access(kTrailingSlash, R_OK);
+ ASSERT_EQ(can_access, -EPERM);
+ fd = open_broker->Open(kTrailingSlash, O_RDONLY);
+ ASSERT_EQ(fd, -EPERM);
+}
+
+TEST(BrokerProcess, BadPathsClientCheck) {
+ TestBadPaths(true /* fast_check_in_client */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, BadPathsNoClientCheck) {
+ TestBadPaths(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, bool recursive) {
+ const char kFileCpuInfo[] = "/proc/cpuinfo";
+ const char kDirProc[] = "/proc/";
+
+ std::vector<BrokerFilePermission> permissions;
+ if (recursive)
+ permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(kDirProc));
+ else
+ permissions.push_back(BrokerFilePermission::ReadOnly(kFileCpuInfo));
+
+ scoped_ptr<BrokerProcess> open_broker(
+ new BrokerProcess(EPERM, permissions, fast_check_in_client));
+ ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+ fd = open_broker->Open(kFileCpuInfo, O_RDWR);
+ base::ScopedFD fd_closer(fd);
+ 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);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ 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);
+ base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
+ 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);
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ open_broker.reset();
+ ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
+}
+
+// Run this test 4 times. With and without the check in client
+// and using a recursive path.
+TEST(BrokerProcess, OpenCpuinfoWithClientCheck) {
+ TestOpenCpuinfo(true /* fast_check_in_client */, false /* not recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheck) {
+ TestOpenCpuinfo(false /* fast_check_in_client */, false /* not recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoWithClientCheckRecursive) {
+ TestOpenCpuinfo(true /* fast_check_in_client */, true /* recursive */);
+ // Don't do anything here, so that ASSERT works in the subfunction as
+ // expected.
+}
+
+TEST(BrokerProcess, OpenCpuinfoNoClientCheckRecursive) {
+ TestOpenCpuinfo(false /* fast_check_in_client */, true /* recursive */);
+ // 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<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadWrite(tempfile_name));
+
+ BrokerProcess open_broker(EPERM, permissions);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ // Check we can access that file with read or write.
+ int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
+ 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 the process could die with a SIGPIPE
+// and we want this to happen in a subprocess.
+SANDBOX_TEST(BrokerProcess, BrokerDied) {
+ const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
+
+ BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+ true /* quiet_failures_for_tests */);
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+ const pid_t broker_pid = open_broker.broker_pid();
+ SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
+
+ // Now we check that the broker has been signaled, but do not reap it.
+ siginfo_t process_info;
+ SANDBOX_ASSERT(HANDLE_EINTR(waitid(
+ P_PID, broker_pid, &process_info, WEXITED | WNOWAIT)) ==
+ 0);
+ SANDBOX_ASSERT(broker_pid == process_info.si_pid);
+ SANDBOX_ASSERT(CLD_KILLED == process_info.si_code);
+ SANDBOX_ASSERT(SIGKILL == process_info.si_status);
+
+ // Check that doing Open with a dead broker won't SIGPIPE us.
+ SANDBOX_ASSERT(open_broker.Open(kCpuInfo, O_RDONLY) == -ENOMEM);
+ SANDBOX_ASSERT(open_broker.Access(kCpuInfo, O_RDONLY) == -ENOMEM);
+}
+
+void TestOpenComplexFlags(bool fast_check_in_client) {
+ const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
+
+ BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ // Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
+ int fd = -1;
+ int ret = 0;
+ 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);
+ 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.
+}
+
+// We need to allow noise because the broker will log when it receives our
+// bogus IPCs.
+SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, RecvMsgDescriptorLeak) {
+ // Android creates a socket on first use of the LOG call.
+ // We need to ensure this socket is open before we
+ // begin the test.
+ LOG(INFO) << "Ensure Android LOG socket is allocated";
+
+ // Find the four lowest available file descriptors.
+ int available_fds[4];
+ SANDBOX_ASSERT(0 == pipe(available_fds));
+ SANDBOX_ASSERT(0 == pipe(available_fds + 2));
+
+ // Save one FD to send to the broker later, and close the others.
+ base::ScopedFD message_fd(available_fds[0]);
+ for (size_t i = 1; i < arraysize(available_fds); i++) {
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(available_fds[i])));
+ }
+
+ // Lower our file descriptor limit to just allow three more file descriptors
+ // to be allocated. (N.B., RLIMIT_NOFILE doesn't limit the number of file
+ // descriptors a process can have: it only limits the highest value that can
+ // be assigned to newly-created descriptors allocated by the process.)
+ const rlim_t fd_limit =
+ 1 +
+ *std::max_element(available_fds,
+ available_fds + arraysize(available_fds));
+
+ // Valgrind doesn't allow changing the hard descriptor limit, so we only
+ // change the soft descriptor limit here.
+ struct rlimit rlim;
+ SANDBOX_ASSERT(0 == getrlimit(RLIMIT_NOFILE, &rlim));
+ SANDBOX_ASSERT(fd_limit <= rlim.rlim_cur);
+ rlim.rlim_cur = fd_limit;
+ SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
+
+ static const char kCpuInfo[] = "/proc/cpuinfo";
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
+
+ BrokerProcess open_broker(EPERM, permissions);
+ SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker);
+ SANDBOX_ASSERT(ipc_fd >= 0);
+
+ static const char kBogus[] = "not a pickle";
+ std::vector<int> fds;
+ fds.push_back(message_fd.get());
+
+ // The broker process should only have a couple spare file descriptors
+ // available, but for good measure we send it fd_limit bogus IPCs anyway.
+ for (rlim_t i = 0; i < fd_limit; ++i) {
+ SANDBOX_ASSERT(
+ base::UnixDomainSocket::SendMsg(ipc_fd, kBogus, sizeof(kBogus), fds));
+ }
+
+ const int fd = open_broker.Open(kCpuInfo, O_RDONLY);
+ SANDBOX_ASSERT(fd >= 0);
+ SANDBOX_ASSERT(0 == IGNORE_EINTR(close(fd)));
+}
+
+bool CloseFD(int fd) {
+ PCHECK(0 == IGNORE_EINTR(close(fd)));
+ return true;
+}
+
+// Return true if the other end of the |reader| pipe was closed,
+// false if |timeout_in_seconds| was reached or another event
+// or error occured.
+bool WaitForClosedPipeWriter(int reader, int timeout_in_ms) {
+ struct pollfd poll_fd = {reader, POLLIN | POLLRDHUP, 0};
+ const int num_events = HANDLE_EINTR(poll(&poll_fd, 1, timeout_in_ms));
+ if (1 == num_events && poll_fd.revents | POLLHUP)
+ return true;
+ return false;
+}
+
+// Closing the broker client's IPC channel should terminate the broker
+// process.
+TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
+
+ // Get the writing end of a pipe into the broker (child) process so
+ // that we can reliably detect when it dies.
+ int lifeline_fds[2];
+ PCHECK(0 == pipe(lifeline_fds));
+
+ BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+ false /* quiet_failures_for_tests */);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&CloseFD, lifeline_fds[0])));
+ // Make sure the writing end only exists in the broker process.
+ CloseFD(lifeline_fds[1]);
+ base::ScopedFD reader(lifeline_fds[0]);
+
+ const pid_t broker_pid = open_broker.broker_pid();
+
+ // This should cause the broker process to exit.
+ BrokerProcessTestHelper::CloseChannel(&open_broker);
+
+ const int kTimeoutInMilliseconds = 5000;
+ const bool broker_lifeline_closed =
+ WaitForClosedPipeWriter(reader.get(), kTimeoutInMilliseconds);
+ // If the broker exited, its lifeline fd should be closed.
+ ASSERT_TRUE(broker_lifeline_closed);
+ // Now check that the broker has exited, but do not reap it.
+ siginfo_t process_info;
+ ASSERT_EQ(0, HANDLE_EINTR(waitid(P_PID, broker_pid, &process_info,
+ WEXITED | WNOWAIT)));
+ EXPECT_EQ(broker_pid, process_info.si_pid);
+ EXPECT_EQ(CLD_EXITED, process_info.si_code);
+ EXPECT_EQ(1, process_info.si_status);
+}
+
+TEST(BrokerProcess, CreateFile) {
+ std::string temp_str;
+ {
+ ScopedTemporaryFile tmp_file;
+ temp_str = tmp_file.full_file_name();
+ }
+ const char* tempfile_name = temp_str.c_str();
+
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadWriteCreate(tempfile_name));
+
+ BrokerProcess open_broker(EPERM, permissions);
+ ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+
+ int fd = -1;
+
+ // Try without O_EXCL
+ fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT);
+ ASSERT_EQ(fd, -EPERM);
+
+ const char kTestText[] = "TESTTESTTEST";
+ // Create a file
+ fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ ASSERT_GE(fd, 0);
+ {
+ base::ScopedFD scoped_fd(fd);
+
+ // Confirm fail if file exists
+ int bad_fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ ASSERT_EQ(bad_fd, -EEXIST);
+
+ // Write to the descriptor opened by the broker.
+
+ ssize_t len = HANDLE_EINTR(write(fd, kTestText, sizeof(kTestText)));
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+ }
+
+ int fd_check = open(tempfile_name, O_RDONLY);
+ ASSERT_GE(fd_check, 0);
+ {
+ base::ScopedFD scoped_fd(fd_check);
+ char buf[1024];
+ ssize_t len = HANDLE_EINTR(read(fd_check, buf, sizeof(buf)));
+
+ ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
+ ASSERT_EQ(memcmp(kTestText, buf, sizeof(kTestText)), 0);
+ }
+}
+
+} // namespace syscall_broker
+
+} // namespace sandbox
diff --git a/sandbox/linux/system_headers/arm64_linux_syscalls.h b/sandbox/linux/system_headers/arm64_linux_syscalls.h
new file mode 100644
index 0000000000..8acb2d1000
--- /dev/null
+++ b/sandbox/linux/system_headers/arm64_linux_syscalls.h
@@ -0,0 +1,1062 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
+
+#include <asm-generic/unistd.h>
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 0
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 1
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 2
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 3
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 4
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 5
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 6
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 7
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 8
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 9
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 10
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 11
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 12
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 13
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 14
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 15
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 16
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 17
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 18
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 19
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 20
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 21
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 22
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 23
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 24
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 25
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 26
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 27
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 28
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 29
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 30
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 31
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 32
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 33
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 34
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 35
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 36
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 37
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 38
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 39
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 40
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 41
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 42
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 43
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 44
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 45
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 46
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 47
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 48
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 49
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 50
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 51
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 52
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 53
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 54
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 55
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 56
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 57
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 58
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 59
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 60
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 61
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 62
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 63
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 64
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 65
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 66
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 67
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 68
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 69
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 70
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 71
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 72
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 73
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 74
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 75
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 76
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 77
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 78
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat 79
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 80
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 81
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 82
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 83
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 84
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 85
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 86
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 87
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 88
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 89
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 90
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 91
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 92
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 93
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 94
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 95
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 96
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 97
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 98
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 99
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 100
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 101
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 102
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 103
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 104
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 105
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 106
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 107
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 108
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 109
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 110
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 111
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 112
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 113
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 114
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 115
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 116
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 117
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 118
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 119
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 120
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 121
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 122
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 123
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 124
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 125
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 126
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 127
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 128
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 129
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 130
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 131
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 132
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 133
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 134
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 135
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 136
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 137
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 138
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 139
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 140
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 141
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 142
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 143
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 144
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 145
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 146
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 147
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 148
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 149
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 150
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 151
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 152
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 153
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 154
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 155
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 156
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 157
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 158
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 159
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 160
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 161
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 162
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 163
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 164
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 165
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 166
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 167
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 168
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 169
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 170
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 171
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 172
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 173
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 174
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 175
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 176
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 177
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 178
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 179
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 180
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 181
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 182
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 183
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 184
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 185
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 186
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 187
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 188
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 189
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 190
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 191
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop 192
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop 193
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 194
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 195
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 196
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 197
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 198
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 199
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 200
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 201
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept 202
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 203
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 204
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 205
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 206
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 207
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 208
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 209
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 210
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 211
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 212
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 213
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 214
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 215
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 216
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 217
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 218
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 219
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 220
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 221
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 222
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 223
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 224
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 225
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 226
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 227
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 228
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 229
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 230
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 231
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 232
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 233
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 234
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 235
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 236
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 237
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 238
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 239
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 240
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 241
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 242
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 243
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 260
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 261
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 262
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 263
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 264
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 265
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 266
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 267
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 268
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 269
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 270
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 271
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 272
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 273
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 274
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 275
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 276
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 277
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 278
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/system_headers/arm64_linux_ucontext.h b/sandbox/linux/system_headers/arm64_linux_ucontext.h
new file mode 100644
index 0000000000..46e0407599
--- /dev/null
+++ b/sandbox/linux/system_headers/arm64_linux_ucontext.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_UCONTEXT_H_
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+#include <signal.h>
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef uint64_t greg_t;
+
+struct ucontext_t {
+ unsigned long uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ sigset_t uc_sigmask;
+ /* glibc uses a 1024-bit sigset_t */
+ uint8_t unused[1024 / 8 - sizeof(sigset_t)];
+ /* last for future expansion */
+ struct sigcontext uc_mcontext;
+};
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_UCONTEXT_H_
diff --git a/sandbox/linux/system_headers/arm_linux_syscalls.h b/sandbox/linux/system_headers/arm_linux_syscalls.h
new file mode 100644
index 0000000000..1addd53843
--- /dev/null
+++ b/sandbox/linux/system_headers/arm_linux_syscalls.h
@@ -0,0 +1,1418 @@
+// 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_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_
+
+#if !defined(__arm__) || !defined(__ARM_EABI__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_SYSCALL_BASE)
+// On ARM EABI arch, __NR_SYSCALL_BASE is 0.
+#define __NR_SYSCALL_BASE 0
+#endif
+
+// 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_sync_file_range2)
+#define __NR_sync_file_range2 (__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
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_SYSCALL_BASE+378)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_SYSCALL_BASE+379)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_SYSCALL_BASE+380)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_SYSCALL_BASE+381)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_SYSCALL_BASE+382)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_SYSCALL_BASE+383)
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom (__NR_SYSCALL_BASE+384)
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create (__NR_SYSCALL_BASE+385)
+#endif
+
+// ARM private syscalls.
+#if !defined(__ARM_NR_BASE)
+#define __ARM_NR_BASE (__NR_SYSCALL_BASE + 0xF0000)
+#endif
+
+#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_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/system_headers/arm_linux_ucontext.h b/sandbox/linux/system_headers/arm_linux_ucontext.h
new file mode 100644
index 0000000000..0eb723a236
--- /dev/null
+++ b/sandbox/linux/system_headers/arm_linux_ucontext.h
@@ -0,0 +1,67 @@
+// 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_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#if !defined(__native_client_nonsfi__)
+#include <asm/sigcontext.h>
+#else
+// In PNaCl toolchain, sigcontext and stack_t is not defined. So here declare
+// them.
+struct sigcontext {
+ unsigned long trap_no;
+ unsigned long error_code;
+ unsigned long oldmask;
+ unsigned long arm_r0;
+ unsigned long arm_r1;
+ unsigned long arm_r2;
+ unsigned long arm_r3;
+ unsigned long arm_r4;
+ unsigned long arm_r5;
+ unsigned long arm_r6;
+ unsigned long arm_r7;
+ unsigned long arm_r8;
+ unsigned long arm_r9;
+ unsigned long arm_r10;
+ unsigned long arm_fp;
+ unsigned long arm_ip;
+ unsigned long arm_sp;
+ unsigned long arm_lr;
+ unsigned long arm_pc;
+ unsigned long arm_cpsr;
+ unsigned long fault_address;
+};
+
+typedef struct sigaltstack {
+ void* ss_sp;
+ int ss_flags;
+ size_t ss_size;
+} stack_t;
+
+#endif
+
+// 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_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_
diff --git a/sandbox/linux/system_headers/capability.h b/sandbox/linux/system_headers/capability.h
new file mode 100644
index 0000000000..f91fcf78ac
--- /dev/null
+++ b/sandbox/linux/system_headers/capability.h
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium 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_SYSTEM_HEADERS_LINUX_CAPABILITY_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_CAPABILITY_H_
+
+#include <stdint.h>
+
+// The following macros are taken from linux/capability.h.
+// We only support capability version 3, which was introduced in Linux 2.6.26.
+#ifndef _LINUX_CAPABILITY_VERSION_3
+#define _LINUX_CAPABILITY_VERSION_3 0x20080522
+#endif
+#ifndef _LINUX_CAPABILITY_U32S_3
+#define _LINUX_CAPABILITY_U32S_3 2
+#endif
+#ifndef CAP_TO_INDEX
+#define CAP_TO_INDEX(x) ((x) >> 5) // 1 << 5 == bits in __u32
+#endif
+#ifndef CAP_TO_MASK
+#define CAP_TO_MASK(x) (1 << ((x) & 31)) // mask for indexed __u32
+#endif
+#ifndef CAP_SYS_CHROOT
+#define CAP_SYS_CHROOT 18
+#endif
+#ifndef CAP_SYS_ADMIN
+#define CAP_SYS_ADMIN 21
+#endif
+
+struct cap_hdr {
+ uint32_t version;
+ int pid;
+};
+
+struct cap_data {
+ uint32_t effective;
+ uint32_t permitted;
+ uint32_t inheritable;
+};
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_CAPABILITY_H_
diff --git a/sandbox/linux/system_headers/i386_linux_ucontext.h b/sandbox/linux/system_headers/i386_linux_ucontext.h
new file mode 100644
index 0000000000..61d9f7a9b8
--- /dev/null
+++ b/sandbox/linux/system_headers/i386_linux_ucontext.h
@@ -0,0 +1,93 @@
+// 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_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_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)
+#if !defined(__native_client_nonsfi__)
+#include <asm/sigcontext.h>
+#else
+// In PNaCl toolchain, sigcontext is not defined. So here declare it.
+typedef struct sigaltstack {
+ void* ss_sp;
+ int ss_flags;
+ size_t ss_size;
+} stack_t;
+#endif
+
+/* 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;
+ // Android and PNaCl toolchain's sigset_t has only 32 bits, though Linux
+ // ABI requires 64 bits.
+ union {
+ sigset_t uc_sigmask;
+ uint32_t kernel_sigmask[2];
+ };
+ struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_
diff --git a/sandbox/linux/system_headers/linux_filter.h b/sandbox/linux/system_headers/linux_filter.h
new file mode 100644
index 0000000000..b23b6eb0c1
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_filter.h
@@ -0,0 +1,140 @@
+// Copyright 2015 The Chromium 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_SYSTEM_HEADERS_LINUX_FILTER_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
+
+#include <stdint.h>
+
+// The following structs and macros are taken from linux/filter.h,
+// as some toolchain does not expose them.
+struct sock_filter {
+ uint16_t code;
+ uint8_t jt;
+ uint8_t jf;
+ uint32_t k;
+};
+
+struct sock_fprog {
+ uint16_t len;
+ struct sock_filter *filter;
+};
+
+#ifndef BPF_CLASS
+#define BPF_CLASS(code) ((code) & 0x07)
+#endif
+
+#ifndef BPF_LD
+#define BPF_LD 0x00
+#endif
+
+#ifndef BPF_ALU
+#define BPF_ALU 0x04
+#endif
+
+#ifndef BPF_JMP
+#define BPF_JMP 0x05
+#endif
+
+#ifndef BPF_RET
+#define BPF_RET 0x06
+#endif
+
+#ifndef BPF_SIZE
+#define BPF_SIZE(code) ((code) & 0x18)
+#endif
+
+#ifndef BPF_W
+#define BPF_W 0x00
+#endif
+
+#ifndef BPF_MODE
+#define BPF_MODE(code) ((code) & 0xe0)
+#endif
+
+#ifndef BPF_ABS
+#define BPF_ABS 0x20
+#endif
+
+#ifndef BPF_OP
+#define BPF_OP(code) ((code) & 0xf0)
+#endif
+
+#ifndef BPF_ADD
+#define BPF_ADD 0x00
+#endif
+
+#ifndef BPF_SUB
+#define BPF_SUB 0x10
+#endif
+
+#ifndef BPF_MUL
+#define BPF_MUL 0x20
+#endif
+
+#ifndef BPF_DIV
+#define BPF_DIV 0x30
+#endif
+
+#ifndef BPF_OR
+#define BPF_OR 0x40
+#endif
+
+#ifndef BPF_AND
+#define BPF_AND 0x50
+#endif
+
+#ifndef BPF_LSH
+#define BPF_LSH 0x60
+#endif
+
+#ifndef BPF_RSH
+#define BPF_RSH 0x70
+#endif
+
+#ifndef BPF_NEG
+#define BPF_NEG 0x80
+#endif
+
+#ifndef BPF_MOD
+#define BPF_MOD 0x90
+#endif
+
+#ifndef BPF_XOR
+#define BPF_XOR 0xA0
+#endif
+
+#ifndef BPF_JA
+#define BPF_JA 0x00
+#endif
+
+#ifndef BPF_JEQ
+#define BPF_JEQ 0x10
+#endif
+
+#ifndef BPF_JGT
+#define BPF_JGT 0x20
+#endif
+
+#ifndef BPF_JGE
+#define BPF_JGE 0x30
+#endif
+
+#ifndef BPF_JSET
+#define BPF_JSET 0x40
+#endif
+
+#ifndef BPF_SRC
+#define BPF_SRC(code) ((code) & 0x08)
+#endif
+
+#ifndef BPF_K
+#define BPF_K 0x00
+#endif
+
+#ifndef BPF_MAXINSNS
+#define BPF_MAXINSNS 4096
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
diff --git a/sandbox/linux/system_headers/linux_futex.h b/sandbox/linux/system_headers/linux_futex.h
new file mode 100644
index 0000000000..4e28403336
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_futex.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
+
+#if !defined(__native_client_nonsfi__)
+#include <linux/futex.h>
+#endif // !defined(__native_client_nonsfi__)
+
+#if !defined(FUTEX_WAIT)
+#define FUTEX_WAIT 0
+#endif
+
+#if !defined(FUTEX_WAKE)
+#define FUTEX_WAKE 1
+#endif
+
+#if !defined(FUTEX_FD)
+#define FUTEX_FD 2
+#endif
+
+#if !defined(FUTEX_REQUEUE)
+#define FUTEX_REQUEUE 3
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE)
+#define FUTEX_CMP_REQUEUE 4
+#endif
+
+#if !defined(FUTEX_WAKE_OP)
+#define FUTEX_WAKE_OP 5
+#endif
+
+#if !defined(FUTEX_LOCK_PI)
+#define FUTEX_LOCK_PI 6
+#endif
+
+#if !defined(FUTEX_UNLOCK_PI)
+#define FUTEX_UNLOCK_PI 7
+#endif
+
+#if !defined(FUTEX_TRYLOCK_PI)
+#define FUTEX_TRYLOCK_PI 8
+#endif
+
+#if !defined(FUTEX_WAIT_BITSET)
+#define FUTEX_WAIT_BITSET 9
+#endif
+
+#if !defined(FUTEX_WAKE_BITSET)
+#define FUTEX_WAKE_BITSET 10
+#endif
+
+#if !defined(FUTEX_WAIT_REQUEUE_PI)
+#define FUTEX_WAIT_REQUEUE_PI 11
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI)
+#define FUTEX_CMP_REQUEUE_PI 12
+#endif
+
+#if !defined(FUTEX_PRIVATE_FLAG)
+#define FUTEX_PRIVATE_FLAG 128
+#endif
+
+#if !defined FUTEX_CLOCK_REALTIME
+#define FUTEX_CLOCK_REALTIME 256
+#endif
+
+#if !defined(FUTEX_CMD_MASK)
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI_PRIVATE)
+#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#if !defined(FUTEX_UNLOCK_PI_PRIVATE)
+#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
diff --git a/sandbox/linux/system_headers/linux_seccomp.h b/sandbox/linux/system_headers/linux_seccomp.h
new file mode 100644
index 0000000000..3deb3d2253
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_seccomp.h
@@ -0,0 +1,107 @@
+// Copyright 2015 The Chromium 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_SYSTEM_HEADERS_LINUX_SECCOMP_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_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>
+
+// 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 EM_MIPS
+#define EM_MIPS 8
+#endif
+#ifndef EM_AARCH64
+#define EM_AARCH64 183
+#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
+#ifndef AUDIT_ARCH_MIPSEL
+#define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_AARCH64
+#define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __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
+
+// 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_SET_MODE_STRICT
+#define SECCOMP_SET_MODE_STRICT 0
+#endif
+#ifndef SECCOMP_SET_MODE_FILTER
+#define SECCOMP_SET_MODE_FILTER 1
+#endif
+#ifndef SECCOMP_FILTER_FLAG_TSYNC
+#define SECCOMP_FILTER_FLAG_TSYNC 1
+#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
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SECCOMP_H_
diff --git a/sandbox/linux/system_headers/linux_signal.h b/sandbox/linux/system_headers/linux_signal.h
new file mode 100644
index 0000000000..5db7fc5ea1
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_signal.h
@@ -0,0 +1,73 @@
+// Copyright 2015 The Chromium 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_SYSTEM_HEADERS_LINUX_SIGNAL_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SIGNAL_H_
+
+// NOTE: On some toolchains, signal related ABI is incompatible with Linux's
+// (not undefined, but defined different values and in different memory
+// layouts). So, fill the gap here.
+
+#if defined(__native_client_nonsfi__)
+#if !defined(__i386__) && !defined(__arm__)
+#error "Unsupported platform"
+#endif
+
+#include <signal.h>
+
+#define LINUX_SIGBUS 7 // 10 in PNaCl toolchain.
+#define LINUX_SIGSEGV 11 // 11 in PNaCl toolchain. Defined for consistency.
+#define LINUX_SIGCHLD 17 // 20 in PNaCl toolchain.
+#define LINUX_SIGSYS 31 // 12 in PNaCl toolchain.
+
+#define LINUX_SIG_BLOCK 0 // 1 in PNaCl toolchain.
+#define LINUX_SIG_UNBLOCK 1 // 2 in PNaCl toolchain.
+
+#define LINUX_SA_SIGINFO 4 // 2 in PNaCl toolchain.
+#define LINUX_SA_NODEFER 0x40000000 // Undefined in PNaCl toolchain.
+#define LINUX_SA_RESTART 0x10000000 // Undefined in PNaCl toolchain.
+
+#define LINUX_SIG_DFL 0 // In PNaCl toolchain, unneeded cast is applied.
+
+struct LinuxSigInfo {
+ int si_signo;
+ int si_errno;
+ int si_code;
+
+ // Extra data is followed by the |si_code|. The length depends on the
+ // signal number.
+ char _sifields[1];
+};
+
+#include "sandbox/linux/system_headers/linux_ucontext.h"
+
+#else // !defined(__native_client_nonsfi__)
+
+// Just alias the toolchain's value.
+#include <signal.h>
+
+#define LINUX_SIGBUS SIGBUS
+#define LINUX_SIGSEGV SIGSEGV
+#define LINUX_SIGCHLD SIGCHLD
+#define LINUX_SIGSYS SIGSYS
+
+#define LINUX_SIG_BLOCK SIG_BLOCK
+#define LINUX_SIG_UNBLOCK SIG_UNBLOCK
+
+#define LINUX_SA_SIGINFO SA_SIGINFO
+#define LINUX_SA_NODEFER SA_NODEFER
+#define LINUX_SA_RESTART SA_RESTART
+
+#define LINUX_SIG_DFL SIG_DFL
+
+typedef siginfo_t LinuxSigInfo;
+
+#if defined(__ANDROID__)
+// Android's signal.h doesn't define ucontext etc.
+#include "sandbox/linux/system_headers/linux_ucontext.h"
+#endif // defined(__ANDROID__)
+
+#endif // !defined(__native_client_nonsfi__)
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SIGNAL_H_
diff --git a/sandbox/linux/system_headers/linux_syscalls.h b/sandbox/linux/system_headers/linux_syscalls.h
new file mode 100644
index 0000000000..2b441e47ea
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_syscalls.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.
+
+// 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_SYSTEM_HEADERS_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SYSCALLS_H_
+
+#if defined(__x86_64__)
+#include "sandbox/linux/system_headers/x86_64_linux_syscalls.h"
+#endif
+
+#if defined(__i386__)
+#include "sandbox/linux/system_headers/x86_32_linux_syscalls.h"
+#endif
+
+#if defined(__arm__) && defined(__ARM_EABI__)
+#include "sandbox/linux/system_headers/arm_linux_syscalls.h"
+#endif
+
+#if defined(__mips__) && (_MIPS_SIM == _ABIO32)
+#include "sandbox/linux/system_headers/mips_linux_syscalls.h"
+#endif
+
+#if defined(__mips__) && (_MIPS_SIM == _ABI64)
+#include "sandbox/linux/system_headers/mips64_linux_syscalls.h"
+#endif
+
+#if defined(__aarch64__)
+#include "sandbox/linux/system_headers/arm64_linux_syscalls.h"
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/system_headers/linux_time.h b/sandbox/linux/system_headers/linux_time.h
new file mode 100644
index 0000000000..e6c8112b86
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_time.h
@@ -0,0 +1,18 @@
+// Copyright 2015 The Chromium 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_SYSTEM_HEADERS_LINUX_TIME_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_TIME_H_
+
+#include <time.h>
+
+#if !defined(CLOCK_REALTIME_COARSE)
+#define CLOCK_REALTIME_COARSE 5
+#endif
+
+#if !defined(CLOCK_MONOTONIC_COARSE)
+#define CLOCK_MONOTONIC_COARSE 6
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_TIME_H_
diff --git a/sandbox/linux/system_headers/linux_ucontext.h b/sandbox/linux/system_headers/linux_ucontext.h
new file mode 100644
index 0000000000..ea4d8a6c1f
--- /dev/null
+++ b/sandbox/linux/system_headers/linux_ucontext.h
@@ -0,0 +1,28 @@
+// 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_SYSTEM_HEADERS_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_UCONTEXT_H_
+
+#if defined(__ANDROID__) || defined(__native_client_nonsfi__)
+
+#if defined(__arm__)
+#include "sandbox/linux/system_headers/arm_linux_ucontext.h"
+#elif defined(__i386__)
+#include "sandbox/linux/system_headers/i386_linux_ucontext.h"
+#elif defined(__x86_64__)
+#include "sandbox/linux/system_headers/x86_64_linux_ucontext.h"
+#elif defined(__mips__)
+#include "sandbox/linux/system_headers/mips_linux_ucontext.h"
+#elif defined(__aarch64__)
+#include "sandbox/linux/system_headers/arm64_linux_ucontext.h"
+#else
+#error "No support for your architecture in Android or PNaCl header"
+#endif
+
+#else // defined(__ANDROID__) || defined(__native_client_nonsfi__)
+#error "The header file included on non Android and non PNaCl."
+#endif // defined(__ANDROID__) || defined(__native_client_nonsfi__)
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_UCONTEXT_H_
diff --git a/sandbox/linux/system_headers/mips64_linux_syscalls.h b/sandbox/linux/system_headers/mips64_linux_syscalls.h
new file mode 100644
index 0000000000..d003124284
--- /dev/null
+++ b/sandbox/linux/system_headers/mips64_linux_syscalls.h
@@ -0,0 +1,1266 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's calls.S.
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_MIPS64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_MIPS64_LINUX_SYSCALLS_H_
+
+#if !defined(__mips__) || (_MIPS_SIM != _ABI64)
+#error "Including header on wrong architecture"
+#endif
+
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_Linux + 0)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_Linux + 1)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_Linux + 2)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_Linux + 3)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_Linux + 4)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_Linux + 5)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_Linux + 6)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_Linux + 7)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_Linux + 8)
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap (__NR_Linux + 9)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_Linux + 10)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_Linux + 11)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_Linux + 12)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_Linux + 13)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_Linux + 14)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_Linux + 15)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_Linux + 16)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_Linux + 17)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_Linux + 18)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_Linux + 19)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_Linux + 20)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_Linux + 21)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_Linux + 22)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_Linux + 23)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_Linux + 24)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_Linux + 25)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_Linux + 26)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_Linux + 27)
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget (__NR_Linux + 28)
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat (__NR_Linux + 29)
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl (__NR_Linux + 30)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_Linux + 31)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_Linux + 32)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_Linux + 33)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_Linux + 34)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_Linux + 35)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_Linux + 36)
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm (__NR_Linux + 37)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_Linux + 38)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_Linux + 39)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_Linux + 40)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_Linux + 41)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_Linux + 42)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_Linux + 43)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_Linux + 44)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_Linux + 45)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_Linux + 46)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_Linux + 47)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_Linux + 48)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_Linux + 49)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_Linux + 50)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_Linux + 51)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_Linux + 52)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_Linux + 53)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_Linux + 54)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_Linux + 55)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_Linux + 56)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_Linux + 57)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_Linux + 58)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_Linux + 59)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_Linux + 60)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_Linux + 61)
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget (__NR_Linux + 62)
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop (__NR_Linux + 63)
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl (__NR_Linux + 64)
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt (__NR_Linux + 65)
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget (__NR_Linux + 66)
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd (__NR_Linux + 67)
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv (__NR_Linux + 68)
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl (__NR_Linux + 69)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_Linux + 70)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_Linux + 71)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_Linux + 72)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_Linux + 73)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_Linux + 74)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_Linux + 75)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_Linux + 76)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_Linux + 77)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_Linux + 78)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_Linux + 79)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_Linux + 80)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_Linux + 81)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_Linux + 82)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_Linux + 83)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_Linux + 84)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_Linux + 85)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_Linux + 86)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_Linux + 87)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_Linux + 88)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_Linux + 89)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_Linux + 90)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_Linux + 91)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_Linux + 92)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_Linux + 93)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_Linux + 94)
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit (__NR_Linux + 95)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_Linux + 96)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_Linux + 97)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_Linux + 98)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_Linux + 99)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_Linux + 100)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_Linux + 101)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_Linux + 102)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_Linux + 103)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_Linux + 104)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_Linux + 105)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_Linux + 106)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_Linux + 107)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_Linux + 108)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_Linux + 109)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_Linux + 110)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_Linux + 111)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_Linux + 112)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_Linux + 113)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_Linux + 114)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_Linux + 115)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_Linux + 116)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_Linux + 117)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_Linux + 118)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_Linux + 119)
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_Linux + 120)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_Linux + 121)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_Linux + 122)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_Linux + 123)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_Linux + 124)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_Linux + 125)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_Linux + 126)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_Linux + 127)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_Linux + 128)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_Linux + 129)
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime (__NR_Linux + 130)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_Linux + 131)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_Linux + 132)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_Linux + 133)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_Linux + 134)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_Linux + 135)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_Linux + 136)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_Linux + 137)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_Linux + 138)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_Linux + 139)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_Linux + 140)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_Linux + 141)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_Linux + 142)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_Linux + 143)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_Linux + 144)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_Linux + 145)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_Linux + 146)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_Linux + 147)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_Linux + 148)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_Linux + 149)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_Linux + 150)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_Linux + 151)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_Linux + 152)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_Linux + 153)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_Linux + 154)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_Linux + 155)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_Linux + 156)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_Linux + 157)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_Linux + 158)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_Linux + 159)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_Linux + 160)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_Linux + 161)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_Linux + 162)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_Linux + 163)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_Linux + 164)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_Linux + 165)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_Linux + 166)
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module (__NR_Linux + 167)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_Linux + 168)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_Linux + 169)
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms (__NR_Linux + 170)
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module (__NR_Linux + 171)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_Linux + 172)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_Linux + 173)
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg (__NR_Linux + 174)
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg (__NR_Linux + 175)
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall (__NR_Linux + 176)
+#endif
+
+#if !defined(__NR_reserved177)
+#define __NR_reserved177 (__NR_Linux + 177)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_Linux + 178)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_Linux + 179)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_Linux + 180)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_Linux + 181)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_Linux + 182)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_Linux + 183)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_Linux + 184)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_Linux + 185)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_Linux + 186)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_Linux + 187)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_Linux + 188)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_Linux + 189)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_Linux + 190)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_Linux + 191)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_Linux + 192)
+#endif
+
+#if !defined(__NR_reserved193)
+#define __NR_reserved193 (__NR_Linux + 193)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_Linux + 194)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_Linux + 195)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_Linux + 196)
+#endif
+
+#if !defined(__NR_cacheflush)
+#define __NR_cacheflush (__NR_Linux + 197)
+#endif
+
+#if !defined(__NR_cachectl)
+#define __NR_cachectl (__NR_Linux + 198)
+#endif
+
+#if !defined(__NR_sysmips)
+#define __NR_sysmips (__NR_Linux + 199)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_Linux + 200)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_Linux + 201)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_Linux + 202)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_Linux + 203)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_Linux + 204)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_Linux + 205)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_Linux + 206)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_Linux + 207)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_Linux + 208)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_Linux + 209)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_Linux + 210)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_Linux + 211)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_Linux + 212)
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_Linux + 213)
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop (__NR_Linux + 214)
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 (__NR_Linux + 215)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_Linux + 216)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_Linux + 217)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_Linux + 218)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_Linux + 219)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_Linux + 220)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_Linux + 221)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_Linux + 222)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_Linux + 223)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_Linux + 224)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_Linux + 225)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_Linux + 226)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_Linux + 227)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_Linux + 228)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_Linux + 229)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_Linux + 230)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_Linux + 231)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_Linux + 232)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_Linux + 233)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_Linux + 234)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_Linux + 235)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_Linux + 236)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_Linux + 237)
+#endif
+
+/* #define __NR_sys_setaltroot (__NR_Linux + 238) */
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_Linux + 239)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_Linux + 240)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_Linux + 241)
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area (__NR_Linux + 242)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_Linux + 243)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_Linux + 244)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_Linux + 245)
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages (__NR_Linux + 246)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_Linux + 247)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_Linux + 248)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_Linux + 249)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_Linux + 250)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_Linux + 251)
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat (__NR_Linux + 252)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_Linux + 253)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_Linux + 254)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_Linux + 255)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_Linux + 256)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_Linux + 257)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_Linux + 258)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_Linux + 259)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_Linux + 260)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_Linux + 261)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_Linux + 262)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_Linux + 263)
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range (__NR_Linux + 264)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_Linux + 265)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_Linux + 266)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_Linux + 267)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_Linux + 268)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_Linux + 269)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_Linux + 270)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_Linux + 271)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_Linux + 272)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_Linux + 273)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_Linux + 274)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_Linux + 275)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_Linux + 276)
+#endif
+
+#if !defined(__NR_timerfd)
+#define __NR_timerfd (__NR_Linux + 277)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_Linux + 278)
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate (__NR_Linux + 279)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_Linux + 280)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_Linux + 281)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_Linux + 282)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_Linux + 283)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_Linux + 284)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_Linux + 285)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_Linux + 286)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_Linux + 287)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_Linux + 288)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_Linux + 289)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_Linux + 290)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_Linux + 291)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_Linux + 292)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_Linux + 293)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_Linux + 294)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_Linux + 295)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_Linux + 296)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_Linux + 297)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_Linux + 298)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_Linux + 299)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_Linux + 300)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_Linux + 301)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_Linux + 302)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_Linux + 303)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_Linux + 304)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_Linux + 305)
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_Linux + 306)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_Linux + 307)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_Linux + 308)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_Linux + 309)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_Linux + 310)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_Linux + 311)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_Linux + 312)
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS64_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/system_headers/mips_linux_syscalls.h b/sandbox/linux/system_headers/mips_linux_syscalls.h
new file mode 100644
index 0000000000..eb1717aad9
--- /dev/null
+++ b/sandbox/linux/system_headers/mips_linux_syscalls.h
@@ -0,0 +1,1428 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Generated from the Linux kernel's calls.S.
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_SYSCALLS_H_
+
+#if !defined(__mips__) || (_MIPS_SIM != _ABIO32)
+#error "Including header on wrong architecture"
+#endif
+
+// __NR_Linux, is defined in <asm/unistd.h>.
+#include <asm/unistd.h>
+
+#if !defined(__NR_syscall)
+#define __NR_syscall (__NR_Linux + 0)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_Linux + 1)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_Linux + 2)
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_Linux + 3)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_Linux + 4)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_Linux + 5)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_Linux + 6)
+#endif
+
+#if !defined(__NR_waitpid)
+#define __NR_waitpid (__NR_Linux + 7)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_Linux + 8)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_Linux + 9)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_Linux + 10)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_Linux + 11)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_Linux + 12)
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time (__NR_Linux + 13)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_Linux + 14)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_Linux + 15)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_Linux + 16)
+#endif
+
+#if !defined(__NR_break)
+#define __NR_break (__NR_Linux + 17)
+#endif
+
+#if !defined(__NR_unused18)
+#define __NR_unused18 (__NR_Linux + 18)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_Linux + 19)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_Linux + 20)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_Linux + 21)
+#endif
+
+#if !defined(__NR_umount)
+#define __NR_umount (__NR_Linux + 22)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_Linux + 23)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_Linux + 24)
+#endif
+
+#if !defined(__NR_stime)
+#define __NR_stime (__NR_Linux + 25)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_Linux + 26)
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm (__NR_Linux + 27)
+#endif
+
+#if !defined(__NR_unused28)
+#define __NR_unused28 (__NR_Linux + 28)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_Linux + 29)
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime (__NR_Linux + 30)
+#endif
+
+#if !defined(__NR_stty)
+#define __NR_stty (__NR_Linux + 31)
+#endif
+
+#if !defined(__NR_gtty)
+#define __NR_gtty (__NR_Linux + 32)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_Linux + 33)
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice (__NR_Linux + 34)
+#endif
+
+#if !defined(__NR_ftime)
+#define __NR_ftime (__NR_Linux + 35)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_Linux + 36)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_Linux + 37)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_Linux + 38)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_Linux + 39)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_Linux + 40)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_Linux + 41)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_Linux + 42)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_Linux + 43)
+#endif
+
+#if !defined(__NR_prof)
+#define __NR_prof (__NR_Linux + 44)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_Linux + 45)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_Linux + 46)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_Linux + 47)
+#endif
+
+#if !defined(__NR_signal)
+#define __NR_signal (__NR_Linux + 48)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_Linux + 49)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_Linux + 50)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_Linux + 51)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_Linux + 52)
+#endif
+
+#if !defined(__NR_lock)
+#define __NR_lock (__NR_Linux + 53)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_Linux + 54)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_Linux + 55)
+#endif
+
+#if !defined(__NR_mpx)
+#define __NR_mpx (__NR_Linux + 56)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_Linux + 57)
+#endif
+
+#if !defined(__NR_ulimit)
+#define __NR_ulimit (__NR_Linux + 58)
+#endif
+
+#if !defined(__NR_unused59)
+#define __NR_unused59 (__NR_Linux + 59)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_Linux + 60)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_Linux + 61)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_Linux + 62)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_Linux + 63)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_Linux + 64)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_Linux + 65)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_Linux + 66)
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction (__NR_Linux + 67)
+#endif
+
+#if !defined(__NR_sgetmask)
+#define __NR_sgetmask (__NR_Linux + 68)
+#endif
+
+#if !defined(__NR_ssetmask)
+#define __NR_ssetmask (__NR_Linux + 69)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_Linux + 70)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_Linux + 71)
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend (__NR_Linux + 72)
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending (__NR_Linux + 73)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_Linux + 74)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_Linux + 75)
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit (__NR_Linux + 76)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_Linux + 77)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_Linux + 78)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_Linux + 79)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_Linux + 80)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_Linux + 81)
+#endif
+
+#if !defined(__NR_reserved82)
+#define __NR_reserved82 (__NR_Linux + 82)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_Linux + 83)
+#endif
+
+#if !defined(__NR_unused84)
+#define __NR_unused84 (__NR_Linux + 84)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_Linux + 85)
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib (__NR_Linux + 86)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_Linux + 87)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_Linux + 88)
+#endif
+
+#if !defined(__NR_readdir)
+#define __NR_readdir (__NR_Linux + 89)
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap (__NR_Linux + 90)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_Linux + 91)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_Linux + 92)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_Linux + 93)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_Linux + 94)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_Linux + 95)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_Linux + 96)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_Linux + 97)
+#endif
+
+#if !defined(__NR_profil)
+#define __NR_profil (__NR_Linux + 98)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_Linux + 99)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_Linux + 100)
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm (__NR_Linux + 101)
+#endif
+
+#if !defined(__NR_socketcall)
+#define __NR_socketcall (__NR_Linux + 102)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_Linux + 103)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_Linux + 104)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_Linux + 105)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_Linux + 106)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_Linux + 107)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_Linux + 108)
+#endif
+
+#if !defined(__NR_unused109)
+#define __NR_unused109 (__NR_Linux + 109)
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl (__NR_Linux + 110)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_Linux + 111)
+#endif
+
+#if !defined(__NR_idle)
+#define __NR_idle (__NR_Linux + 112)
+#endif
+
+#if !defined(__NR_vm86)
+#define __NR_vm86 (__NR_Linux + 113)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_Linux + 114)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_Linux + 115)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_Linux + 116)
+#endif
+
+#if !defined(__NR_ipc)
+#define __NR_ipc (__NR_Linux + 117)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_Linux + 118)
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn (__NR_Linux + 119)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_Linux + 120)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_Linux + 121)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_Linux + 122)
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt (__NR_Linux + 123)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_Linux + 124)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_Linux + 125)
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask (__NR_Linux + 126)
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module (__NR_Linux + 127)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_Linux + 128)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_Linux + 129)
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms (__NR_Linux + 130)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_Linux + 131)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_Linux + 132)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_Linux + 133)
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush (__NR_Linux + 134)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_Linux + 135)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_Linux + 136)
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall \
+ (__NR_Linux + 137) /* Syscall for Andrew File System \
+ */
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_Linux + 138)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_Linux + 139)
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek (__NR_Linux + 140)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_Linux + 141)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_Linux + 142)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_Linux + 143)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_Linux + 144)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_Linux + 145)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_Linux + 146)
+#endif
+
+#if !defined(__NR_cacheflush)
+#define __NR_cacheflush (__NR_Linux + 147)
+#endif
+
+#if !defined(__NR_cachectl)
+#define __NR_cachectl (__NR_Linux + 148)
+#endif
+
+#if !defined(__NR_sysmips)
+#define __NR_sysmips (__NR_Linux + 149)
+#endif
+
+#if !defined(__NR_unused150)
+#define __NR_unused150 (__NR_Linux + 150)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_Linux + 151)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_Linux + 152)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_Linux + 153)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_Linux + 154)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_Linux + 155)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_Linux + 156)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_Linux + 157)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_Linux + 158)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_Linux + 159)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_Linux + 160)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_Linux + 161)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_Linux + 162)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_Linux + 163)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_Linux + 164)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_Linux + 165)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_Linux + 166)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_Linux + 167)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_Linux + 168)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_Linux + 169)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_Linux + 170)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_Linux + 171)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_Linux + 172)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_Linux + 173)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_Linux + 174)
+#endif
+
+#if !defined(__NR_recv)
+#define __NR_recv (__NR_Linux + 175)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_Linux + 176)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_Linux + 177)
+#endif
+
+#if !defined(__NR_send)
+#define __NR_send (__NR_Linux + 178)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_Linux + 179)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_Linux + 180)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_Linux + 181)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_Linux + 182)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_Linux + 183)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_Linux + 184)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_Linux + 185)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_Linux + 186)
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module (__NR_Linux + 187)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_Linux + 188)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_Linux + 189)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_Linux + 190)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_Linux + 191)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_Linux + 192)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_Linux + 193)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_Linux + 194)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_Linux + 195)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_Linux + 196)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_Linux + 197)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_Linux + 198)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_Linux + 199)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_Linux + 200)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_Linux + 201)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_Linux + 202)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_Linux + 203)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_Linux + 204)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_Linux + 205)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_Linux + 206)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_Linux + 207)
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg (__NR_Linux + 208)
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg (__NR_Linux + 209)
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 (__NR_Linux + 210)
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 (__NR_Linux + 211)
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 (__NR_Linux + 212)
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 (__NR_Linux + 213)
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 (__NR_Linux + 214)
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 (__NR_Linux + 215)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_Linux + 216)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_Linux + 217)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_Linux + 218)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_Linux + 219)
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 (__NR_Linux + 220)
+#endif
+
+#if !defined(__NR_reserved221)
+#define __NR_reserved221 (__NR_Linux + 221)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_Linux + 222)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_Linux + 223)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_Linux + 224)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_Linux + 225)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_Linux + 226)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_Linux + 227)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_Linux + 228)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_Linux + 229)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_Linux + 230)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_Linux + 231)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_Linux + 232)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_Linux + 233)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_Linux + 234)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_Linux + 235)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_Linux + 236)
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 (__NR_Linux + 237)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_Linux + 238)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_Linux + 239)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_Linux + 240)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_Linux + 241)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_Linux + 242)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_Linux + 243)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_Linux + 244)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_Linux + 245)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_Linux + 246)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_Linux + 247)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_Linux + 248)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_Linux + 249)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_Linux + 250)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_Linux + 251)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_Linux + 252)
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_Linux + 253)
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 (__NR_Linux + 254)
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 (__NR_Linux + 255)
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 (__NR_Linux + 256)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_Linux + 257)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_Linux + 258)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_Linux + 259)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_Linux + 260)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_Linux + 261)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_Linux + 262)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_Linux + 263)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_Linux + 264)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_Linux + 265)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_Linux + 266)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_Linux + 267)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_Linux + 268)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_Linux + 269)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_Linux + 270)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_Linux + 271)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_Linux + 272)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_Linux + 273)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_Linux + 274)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_Linux + 275)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_Linux + 276)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_Linux + 277)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_Linux + 278)
+#endif
+
+/* #define __NR_sys_setaltroot (__NR_Linux + 279) */
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_Linux + 280)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_Linux + 281)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_Linux + 282)
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area (__NR_Linux + 283)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_Linux + 284)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_Linux + 285)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_Linux + 286)
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages (__NR_Linux + 287)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_Linux + 288)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_Linux + 289)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_Linux + 290)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_Linux + 291)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_Linux + 292)
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 (__NR_Linux + 293)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_Linux + 294)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_Linux + 295)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_Linux + 296)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_Linux + 297)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_Linux + 298)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_Linux + 299)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_Linux + 300)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_Linux + 301)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_Linux + 302)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_Linux + 303)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_Linux + 304)
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range (__NR_Linux + 305)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_Linux + 306)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_Linux + 307)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_Linux + 308)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_Linux + 309)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_Linux + 310)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_Linux + 311)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_Linux + 312)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_Linux + 313)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_Linux + 314)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_Linux + 315)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_Linux + 316)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_Linux + 317)
+#endif
+
+#if !defined(__NR_timerfd)
+#define __NR_timerfd (__NR_Linux + 318)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_Linux + 319)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_Linux + 320)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_Linux + 321)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_Linux + 322)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_Linux + 323)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_Linux + 324)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_Linux + 325)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_Linux + 326)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_Linux + 327)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_Linux + 328)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_Linux + 329)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_Linux + 330)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_Linux + 331)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_Linux + 332)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_Linux + 333)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_Linux + 334)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_Linux + 335)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_Linux + 336)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_Linux + 337)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_Linux + 338)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_Linux + 339)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_Linux + 340)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_Linux + 341)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_Linux + 342)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_Linux + 343)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_Linux + 344)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_Linux + 345)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_Linux + 346)
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_Linux + 347)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_Linux + 348)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_Linux + 349)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_Linux + 350)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_Linux + 351)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_Linux + 352)
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_SYSCALLS_H_
diff --git a/sandbox/linux/system_headers/mips_linux_ucontext.h b/sandbox/linux/system_headers/mips_linux_ucontext.h
new file mode 100644
index 0000000000..27b3763522
--- /dev/null
+++ b/sandbox/linux/system_headers/mips_linux_ucontext.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_UCONTEXT_H_
+
+// 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)
+// Ensure that 'stack_t' is defined.
+#include <asm/signal.h>
+
+// We also need greg_t for the sandbox, include it in this header as well.
+typedef unsigned long greg_t;
+
+typedef struct {
+ uint32_t regmask;
+ uint32_t status;
+ uint64_t pc;
+ uint64_t gregs[32];
+ uint64_t fpregs[32];
+ uint32_t acx;
+ uint32_t fpc_csr;
+ uint32_t fpc_eir;
+ uint32_t used_math;
+ uint32_t dsp;
+ uint64_t mdhi;
+ uint64_t mdlo;
+ uint32_t hi1;
+ uint32_t lo1;
+ uint32_t hi2;
+ uint32_t lo2;
+ uint32_t hi3;
+ uint32_t lo3;
+} mcontext_t;
+
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ // Other fields are not used by Google Breakpad. Don't define them.
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_MIPS_LINUX_UCONTEXT_H_
diff --git a/sandbox/linux/system_headers/x86_32_linux_syscalls.h b/sandbox/linux/system_headers/x86_32_linux_syscalls.h
new file mode 100644
index 0000000000..a6afc62d99
--- /dev/null
+++ b/sandbox/linux/system_headers/x86_32_linux_syscalls.h
@@ -0,0 +1,1426 @@
+// 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_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_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
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 350
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 351
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 352
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 353
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 354
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 355
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 356
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/system_headers/x86_64_linux_syscalls.h b/sandbox/linux/system_headers/x86_64_linux_syscalls.h
new file mode 100644
index 0000000000..349504aee4
--- /dev/null
+++ b/sandbox/linux/system_headers/x86_64_linux_syscalls.h
@@ -0,0 +1,1294 @@
+// 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_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_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
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 313
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 314
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 315
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 316
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 317
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 318
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 319
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+
diff --git a/sandbox/linux/system_headers/x86_64_linux_ucontext.h b/sandbox/linux/system_headers/x86_64_linux_ucontext.h
new file mode 100644
index 0000000000..57b8919a9c
--- /dev/null
+++ b/sandbox/linux/system_headers/x86_64_linux_ucontext.h
@@ -0,0 +1,88 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_UCONTEXT_H_
+
+// We do something compatible with glibc. Hopefully, at some point Android will
+// provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined.
+// Spec:
+// http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-AMD64/LSB-Core-AMD64/libc-ddefs.html#AEN5668
+
+#if !defined(__BIONIC_HAVE_UCONTEXT_T)
+#include <asm/sigcontext.h>
+
+struct _libc_fpxreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+ unsigned short padding[3];
+};
+
+struct _libc_xmmreg {
+ uint32_t element[4];
+};
+
+struct _libc_fpstate {
+ uint16_t cwd;
+ uint16_t swd;
+ uint16_t twd;
+ uint16_t fop;
+ uint64_t rip;
+ uint64_t rdp;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ struct _libc_fpxreg _st[8];
+ struct _libc_xmmreg _xmm[16];
+ uint32_t padding[24];
+};
+
+typedef uint64_t greg_t;
+
+typedef struct {
+ greg_t gregs[23];
+ struct _libc_fpstate* fpregs;
+ unsigned long __reserved1[8];
+} mcontext_t;
+
+enum {
+ REG_R8 = 0,
+ REG_R9,
+ REG_R10,
+ REG_R11,
+ REG_R12,
+ REG_R13,
+ REG_R14,
+ REG_R15,
+ REG_RDI,
+ REG_RSI,
+ REG_RBP,
+ REG_RBX,
+ REG_RDX,
+ REG_RAX,
+ REG_RCX,
+ REG_RSP,
+ REG_RIP,
+ REG_EFL,
+ REG_CSGSFS,
+ REG_ERR,
+ REG_TRAPNO,
+ REG_OLDMASK,
+ REG_CR2,
+ NGREG,
+};
+
+typedef struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ sigset_t uc_sigmask;
+ struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#else
+#include <sys/ucontext.h>
+#endif // __BIONIC_HAVE_UCONTEXT_T
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_UCONTEXT_H_
diff --git a/sandbox/linux/tests/main.cc b/sandbox/linux/tests/main.cc
new file mode 100644
index 0000000000..caeddee32c
--- /dev/null
+++ b/sandbox/linux/tests/main.cc
@@ -0,0 +1,82 @@
+// 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/at_exit.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/test/test_suite.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/test_utils.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace sandbox {
+namespace {
+
+// Check for leaks in our tests.
+void RunPostTestsChecks(const base::FilePath& orig_cwd) {
+ if (TestUtils::CurrentProcessHasChildren()) {
+ LOG(FATAL) << "One of the tests created a child that was not waited for. "
+ << "Please, clean up after your tests!";
+ }
+
+ base::FilePath cwd;
+ CHECK(GetCurrentDirectory(&cwd));
+ if (orig_cwd != cwd) {
+ LOG(FATAL) << "One of the tests changed the current working directory. "
+ << "Please, clean up after your tests!";
+ }
+}
+
+} // namespace
+} // namespace sandbox
+
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+void UnitTestAssertHandler(const std::string& str) {
+ _exit(1);
+}
+#endif
+
+int main(int argc, char* argv[]) {
+ base::CommandLine::Init(argc, argv);
+ std::string client_func;
+#if defined(SANDBOX_USES_BASE_TEST_SUITE)
+ client_func = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTestChildProcess);
+#endif
+ if (!client_func.empty()) {
+ base::AtExitManager exit_manager;
+ return multi_process_function_list::InvokeChildProcessTest(client_func);
+ }
+
+ base::FilePath orig_cwd;
+ CHECK(GetCurrentDirectory(&orig_cwd));
+
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+ // The use of Callbacks requires an AtExitManager.
+ base::AtExitManager exit_manager;
+ testing::InitGoogleTest(&argc, argv);
+ // Death tests rely on LOG(FATAL) triggering an exit (the default behavior is
+ // SIGABRT). The normal test launcher does this at initialization, but since
+ // we still do not use this on Android, we must install the handler ourselves.
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+#endif
+ // Always go through re-execution for death tests.
+ // This makes gtest only marginally slower for us and has the
+ // additional side effect of getting rid of gtest warnings about fork()
+ // safety.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+ int tests_result = RUN_ALL_TESTS();
+#else
+ int tests_result = base::RunUnitTestsUsingBaseTestSuite(argc, argv);
+#endif
+
+ sandbox::RunPostTestsChecks(orig_cwd);
+ return tests_result;
+}
diff --git a/sandbox/linux/tests/sandbox_test_runner.cc b/sandbox/linux/tests/sandbox_test_runner.cc
new file mode 100644
index 0000000000..b099b97289
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+SandboxTestRunner::SandboxTestRunner() {
+}
+
+SandboxTestRunner::~SandboxTestRunner() {
+}
+
+bool SandboxTestRunner::ShouldCheckForLeaks() const {
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/sandbox_test_runner.h b/sandbox/linux/tests/sandbox_test_runner.h
new file mode 100644
index 0000000000..3155b74008
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
+#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
+
+#include "base/macros.h"
+
+namespace sandbox {
+
+// A simple "runner" class to implement tests.
+class SandboxTestRunner {
+ public:
+ SandboxTestRunner();
+ virtual ~SandboxTestRunner();
+
+ virtual void Run() = 0;
+
+ // Override to decide whether or not to check for leaks with LSAN
+ // (if built with LSAN and LSAN is enabled).
+ virtual bool ShouldCheckForLeaks() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SandboxTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_H_
diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc
new file mode 100644
index 0000000000..69e05ac4e0
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/sandbox_test_runner_function_pointer.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+namespace sandbox {
+
+SandboxTestRunnerFunctionPointer::SandboxTestRunnerFunctionPointer(
+ void (*function_to_run)(void))
+ : function_to_run_(function_to_run) {
+}
+
+SandboxTestRunnerFunctionPointer::~SandboxTestRunnerFunctionPointer() {
+}
+
+void SandboxTestRunnerFunctionPointer::Run() {
+ DCHECK(function_to_run_);
+ function_to_run_();
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/sandbox_test_runner_function_pointer.h b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h
new file mode 100644
index 0000000000..cadd07c248
--- /dev/null
+++ b/sandbox/linux/tests/sandbox_test_runner_function_pointer.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_
+#define SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER_FUNCTION_POINTER_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+
+class SandboxTestRunnerFunctionPointer : public SandboxTestRunner {
+ public:
+ SandboxTestRunnerFunctionPointer(void (*function_to_run)(void));
+ ~SandboxTestRunnerFunctionPointer() override;
+ void Run() override;
+
+ private:
+ void (*function_to_run_)(void);
+ DISALLOW_COPY_AND_ASSIGN(SandboxTestRunnerFunctionPointer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SANDBOX_TEST_RUNNER__FUNCTION_POINTER_H_
diff --git a/sandbox/linux/tests/scoped_temporary_file.cc b/sandbox/linux/tests/scoped_temporary_file.cc
new file mode 100644
index 0000000000..1f2d66fd6b
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "build/build_config.h"
+
+namespace sandbox {
+
+ScopedTemporaryFile::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)
+ static_assert(sizeof(full_file_name_) >= sizeof(file_template),
+ "full_file_name is not large enough");
+ memcpy(full_file_name_, file_template, sizeof(file_template));
+ fd_ = mkstemp(full_file_name_);
+ CHECK_LE(0, fd_);
+}
+
+ScopedTemporaryFile::~ScopedTemporaryFile() {
+ CHECK_EQ(0, unlink(full_file_name_));
+ CHECK_EQ(0, IGNORE_EINTR(close(fd_)));
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/scoped_temporary_file.h b/sandbox/linux/tests/scoped_temporary_file.h
new file mode 100644
index 0000000000..0734130055
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file.h
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
+#define SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
+
+#include "base/macros.h"
+
+namespace sandbox {
+// 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();
+ ~ScopedTemporaryFile();
+
+ 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 sandbox
+
+#endif // SANDBOX_LINUX_TESTS_SCOPED_TEMPORARY_FILE_H_
diff --git a/sandbox/linux/tests/scoped_temporary_file_unittest.cc b/sandbox/linux/tests/scoped_temporary_file_unittest.cc
new file mode 100644
index 0000000000..44a2ecb1ae
--- /dev/null
+++ b/sandbox/linux/tests/scoped_temporary_file_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/scoped_temporary_file.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool FullWrite(int fd, const char* buffer, size_t count) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(write(fd, buffer, count));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ }
+ return true;
+}
+
+bool FullRead(int fd, char* buffer, size_t count) {
+ while (count > 0) {
+ const ssize_t transfered = HANDLE_EINTR(read(fd, buffer, count));
+ if (transfered <= 0 || static_cast<size_t>(transfered) > count) {
+ return false;
+ }
+ count -= transfered;
+ buffer += transfered;
+ }
+ return true;
+}
+
+TEST(ScopedTemporaryFile, Basics) {
+ std::string temp_file_name;
+ {
+ ScopedTemporaryFile temp_file_1;
+ const char kTestString[] = "This is a test";
+ ASSERT_LE(0, temp_file_1.fd());
+
+ temp_file_name = temp_file_1.full_file_name();
+ base::ScopedFD temp_file_2(open(temp_file_1.full_file_name(), O_RDONLY));
+ ASSERT_TRUE(temp_file_2.is_valid());
+
+ ASSERT_TRUE(FullWrite(temp_file_1.fd(), kTestString, sizeof(kTestString)));
+
+ char test_string_read[sizeof(kTestString)] = {0};
+ ASSERT_TRUE(FullRead(
+ temp_file_2.get(), test_string_read, sizeof(test_string_read)));
+ ASSERT_EQ(0, memcmp(kTestString, test_string_read, sizeof(kTestString)));
+ }
+
+ errno = 0;
+ struct stat buf;
+ ASSERT_EQ(-1, stat(temp_file_name.c_str(), &buf));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.cc b/sandbox/linux/tests/test_utils.cc
new file mode 100644
index 0000000000..747bad27a5
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.cc
@@ -0,0 +1,42 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/test_utils.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+
+namespace sandbox {
+
+bool TestUtils::CurrentProcessHasChildren() {
+ siginfo_t process_info;
+ int ret = HANDLE_EINTR(
+ waitid(P_ALL, 0, &process_info, WEXITED | WNOHANG | WNOWAIT));
+ if (-1 == ret) {
+ PCHECK(ECHILD == errno);
+ return false;
+ } else {
+ return true;
+ }
+}
+
+void TestUtils::HandlePostForkReturn(pid_t pid) {
+ const int kChildExitCode = 1;
+ if (pid > 0) {
+ int status = 0;
+ PCHECK(pid == HANDLE_EINTR(waitpid(pid, &status, 0)));
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(kChildExitCode, WEXITSTATUS(status));
+ } else if (pid == 0) {
+ _exit(kChildExitCode);
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/test_utils.h b/sandbox/linux/tests/test_utils.h
new file mode 100644
index 0000000000..7cf9749fe4
--- /dev/null
+++ b/sandbox/linux/tests/test_utils.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+#define SANDBOX_LINUX_TESTS_TEST_UTILS_H_
+
+#include <sys/types.h>
+
+#include "base/macros.h"
+
+namespace sandbox {
+
+// This class provide small helpers to help writing tests.
+class TestUtils {
+ public:
+ static bool CurrentProcessHasChildren();
+ // |pid| is the return value of a fork()-like call. This
+ // makes sure that if fork() succeeded the child exits
+ // and the parent waits for it.
+ static void HandlePostForkReturn(pid_t pid);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TestUtils);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_TESTS_TEST_UTILS_H_
diff --git a/sandbox/linux/tests/test_utils_unittest.cc b/sandbox/linux/tests/test_utils_unittest.cc
new file mode 100644
index 0000000000..0f86e616e9
--- /dev/null
+++ b/sandbox/linux/tests/test_utils_unittest.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/tests/test_utils.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+// Check that HandlePostForkReturn works.
+TEST(TestUtils, HandlePostForkReturn) {
+ pid_t pid = fork();
+ TestUtils::HandlePostForkReturn(pid);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/linux/tests/unit_tests.cc b/sandbox/linux/tests/unit_tests.cc
new file mode 100644
index 0000000000..4973c41fbd
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests.cc
@@ -0,0 +1,354 @@
+// 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 <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "base/debug/leak_annotations.h"
+#include "base/files/file_util.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/third_party/valgrind/valgrind.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+// Specifically, PNaCl toolchain does not have this flag.
+#if !defined(POLLRDHUP)
+#define POLLRDHUP 0x2000
+#endif
+
+namespace {
+std::string TestFailedMessage(const std::string& msg) {
+ return msg.empty() ? std::string() : "Actual test failure: " + msg;
+}
+
+int GetSubProcessTimeoutTimeInSeconds() {
+ // Previously 10s, but that timed out (just) on Chromecast.
+ return 12;
+}
+
+// Returns the number of threads of the current process or -1.
+int CountThreads() {
+ struct stat task_stat;
+ int task_d = stat("/proc/self/task", &task_stat);
+ // task_stat.st_nlink should be the number of tasks + 2 (accounting for
+ // "." and "..".
+ if (task_d != 0 || task_stat.st_nlink < 3)
+ return -1;
+ const int num_threads = task_stat.st_nlink - 2;
+ return num_threads;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool IsAndroid() {
+#if defined(OS_ANDROID)
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool IsArchitectureArm() {
+#if defined(ARCH_CPU_ARM_FAMILY)
+ return true;
+#else
+ return false;
+#endif
+}
+
+// TODO(jln): figure out why base/.../dynamic_annotations.h's
+// RunningOnValgrind() cannot link.
+bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
+
+static const int kExpectedValue = 42;
+static const int kIgnoreThisTest = 43;
+static const int kExitWithAssertionFailure = 1;
+#if !defined(OS_NACL_NONSFI)
+static const int kExitForTimeout = 2;
+#endif
+
+#if defined(SANDBOX_USES_BASE_TEST_SUITE)
+// This is due to StackDumpSignalHandler() performing _exit(1).
+// TODO(jln): get rid of the collision with kExitWithAssertionFailure.
+const int kExitAfterSIGSEGV = 1;
+#endif
+
+// PNaCl toolchain's signal ABIs are incompatible with Linux's.
+// So, for simplicity, just drop the "timeout" feature from unittest framework
+// with relying on the buildbot's timeout feature.
+#if !defined(OS_NACL_NONSFI)
+static void SigAlrmHandler(int) {
+ const char failure_message[] = "Timeout reached!\n";
+ // Make sure that we never block here.
+ if (!fcntl(2, F_SETFL, O_NONBLOCK)) {
+ ignore_result(write(2, failure_message, sizeof(failure_message) - 1));
+ }
+ _exit(kExitForTimeout);
+}
+
+// Set a timeout with a handler that will automatically fail the
+// test.
+static void SetProcessTimeout(int time_in_seconds) {
+ struct sigaction act = {};
+ act.sa_handler = SigAlrmHandler;
+ SANDBOX_ASSERT(sigemptyset(&act.sa_mask) == 0);
+ act.sa_flags = 0;
+
+ struct sigaction old_act;
+ SANDBOX_ASSERT(sigaction(SIGALRM, &act, &old_act) == 0);
+
+ // We don't implemenet signal chaining, so make sure that nothing else
+ // is expecting to handle SIGALRM.
+ SANDBOX_ASSERT((old_act.sa_flags & SA_SIGINFO) == 0);
+ SANDBOX_ASSERT(old_act.sa_handler == SIG_DFL);
+ sigset_t sigalrm_set;
+ SANDBOX_ASSERT(sigemptyset(&sigalrm_set) == 0);
+ SANDBOX_ASSERT(sigaddset(&sigalrm_set, SIGALRM) == 0);
+ SANDBOX_ASSERT(sigprocmask(SIG_UNBLOCK, &sigalrm_set, NULL) == 0);
+ SANDBOX_ASSERT(alarm(time_in_seconds) == 0); // There should be no previous
+ // alarm.
+}
+#endif // !defined(OS_NACL_NONSFI)
+
+// Runs a test in a sub-process. This is necessary for most of the code
+// in the BPF sandbox, as it potentially makes global state changes and as
+// it also tends to raise fatal errors, if the code has been used in an
+// insecure manner.
+void UnitTests::RunTestInProcess(SandboxTestRunner* test_runner,
+ DeathCheck death,
+ const void* death_aux) {
+ CHECK(test_runner);
+ // We need to fork(), so we can't be multi-threaded, as threads could hold
+ // locks.
+ int num_threads = CountThreads();
+#if !defined(THREAD_SANITIZER)
+ const int kNumExpectedThreads = 1;
+#else
+ // Under TSAN, there is a special helper thread. It should be completely
+ // invisible to our testing, so we ignore it. It should be ok to fork()
+ // with this thread. It's currently buggy, but it's the best we can do until
+ // there is a way to delay the start of the thread
+ // (https://code.google.com/p/thread-sanitizer/issues/detail?id=19).
+ const int kNumExpectedThreads = 2;
+#endif
+
+ // The kernel is at liberty to wake a thread id futex before updating /proc.
+ // If another test running in the same process has stopped a thread, it may
+ // appear as still running in /proc.
+ // We poll /proc, with an exponential back-off. At most, we'll sleep around
+ // 2^iterations nanoseconds in nanosleep().
+ for (unsigned int iteration = 0; iteration < 30; iteration++) {
+ struct timespec ts = {0, 1L << iteration /* nanoseconds */};
+ PCHECK(0 == HANDLE_EINTR(nanosleep(&ts, &ts)));
+ num_threads = CountThreads();
+ if (kNumExpectedThreads == num_threads)
+ break;
+ }
+
+ ASSERT_EQ(kNumExpectedThreads, num_threads)
+ << "Running sandbox tests with multiple threads "
+ << "is not supported and will make the tests flaky.";
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+ // Check that our pipe is not on one of the standard file descriptor.
+ SANDBOX_ASSERT(fds[0] > 2 && fds[1] > 2);
+
+ pid_t pid;
+ ASSERT_LE(0, (pid = fork()));
+ if (!pid) {
+ // In child process
+ // Redirect stderr to our pipe. This way, we can capture all error
+ // messages, if we decide we want to do so in our tests.
+ SANDBOX_ASSERT(dup2(fds[1], 2) == 2);
+ SANDBOX_ASSERT(!close(fds[0]));
+ SANDBOX_ASSERT(!close(fds[1]));
+
+ // Don't set a timeout if running on Valgrind, since it's generally much
+ // slower.
+ if (!IsRunningOnValgrind()) {
+#if !defined(OS_NACL_NONSFI)
+ SetProcessTimeout(GetSubProcessTimeoutTimeInSeconds());
+#endif
+ }
+
+ // Disable core files. They are not very useful for our individual test
+ // cases.
+ struct rlimit no_core = {0};
+ setrlimit(RLIMIT_CORE, &no_core);
+
+ test_runner->Run();
+ if (test_runner->ShouldCheckForLeaks()) {
+#if defined(LEAK_SANITIZER)
+ __lsan_do_leak_check();
+#endif
+ }
+ _exit(kExpectedValue);
+ }
+
+ close(fds[1]);
+ std::vector<char> msg_buf;
+ ssize_t rc;
+
+ // Make sure read() will never block as we'll use poll() to
+ // block with a timeout instead.
+ const int fcntl_ret = fcntl(fds[0], F_SETFL, O_NONBLOCK);
+ ASSERT_EQ(0, fcntl_ret);
+ struct pollfd poll_fd = {fds[0], POLLIN | POLLRDHUP, 0};
+
+ int poll_ret;
+ // We prefer the SIGALRM timeout to trigger in the child than this timeout
+ // so we double the common value here.
+ int poll_timeout = GetSubProcessTimeoutTimeInSeconds() * 2 * 1000;
+ while ((poll_ret = poll(&poll_fd, 1, poll_timeout) > 0)) {
+ const size_t kCapacity = 256;
+ const size_t len = msg_buf.size();
+ msg_buf.resize(len + kCapacity);
+ rc = HANDLE_EINTR(read(fds[0], &msg_buf[len], kCapacity));
+ msg_buf.resize(len + std::max(rc, static_cast<ssize_t>(0)));
+ if (rc <= 0)
+ break;
+ }
+ ASSERT_NE(poll_ret, -1) << "poll() failed";
+ ASSERT_NE(poll_ret, 0) << "Timeout while reading child state";
+ close(fds[0]);
+ std::string msg(msg_buf.begin(), msg_buf.end());
+
+ int status = 0;
+ int waitpid_returned = HANDLE_EINTR(waitpid(pid, &status, 0));
+ ASSERT_EQ(pid, waitpid_returned) << TestFailedMessage(msg);
+
+ // At run-time, we sometimes decide that a test shouldn't actually
+ // run (e.g. when testing sandbox features on a kernel that doesn't
+ // have sandboxing support). When that happens, don't attempt to
+ // call the "death" function, as it might be looking for a
+ // death-test condition that would never have triggered.
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != kIgnoreThisTest ||
+ !msg.empty()) {
+ // We use gtest's ASSERT_XXX() macros instead of the DeathCheck
+ // functions. This means, on failure, "return" is called. This
+ // only works correctly, if the call of the "death" callback is
+ // the very last thing in our function.
+ death(status, msg, death_aux);
+ }
+}
+
+void UnitTests::DeathSuccess(int status, const std::string& msg, const void*) {
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details;
+ bool subprocess_exited_but_printed_messages = !msg.empty();
+ EXPECT_FALSE(subprocess_exited_but_printed_messages) << details;
+}
+
+void UnitTests::DeathSuccessAllowNoise(int status,
+ const std::string& msg,
+ const void*) {
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(kExpectedValue, subprocess_exit_status) << details;
+}
+
+void UnitTests::DeathMessage(int status,
+ const std::string& msg,
+ const void* aux) {
+ std::string details(TestFailedMessage(msg));
+ const char* expected_msg = static_cast<const char*>(aux);
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << "Exit status: " << status
+ << " " << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(1, subprocess_exit_status) << details;
+
+ bool subprocess_exited_without_matching_message =
+ msg.find(expected_msg) == std::string::npos;
+
+// In official builds CHECK messages are dropped, so look for SIGABRT.
+// See https://code.google.com/p/chromium/issues/detail?id=437312
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID)
+ if (subprocess_exited_without_matching_message) {
+ static const char kSigAbortMessage[] = "Received signal 6";
+ subprocess_exited_without_matching_message =
+ msg.find(kSigAbortMessage) == std::string::npos;
+ }
+#endif
+ EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
+}
+
+void UnitTests::DeathSEGVMessage(int status,
+ const std::string& msg,
+ const void* aux) {
+ std::string details(TestFailedMessage(msg));
+ const char* expected_msg = static_cast<const char*>(aux);
+
+#if !defined(SANDBOX_USES_BASE_TEST_SUITE)
+ const bool subprocess_got_sigsegv =
+ WIFSIGNALED(status) && (SIGSEGV == WTERMSIG(status));
+#else
+ // This hack is required when a signal handler is installed
+ // for SEGV that will _exit(1).
+ const bool subprocess_got_sigsegv =
+ WIFEXITED(status) && (kExitAfterSIGSEGV == WEXITSTATUS(status));
+#endif
+
+ ASSERT_TRUE(subprocess_got_sigsegv) << "Exit status: " << status
+ << " " << details;
+
+ bool subprocess_exited_without_matching_message =
+ msg.find(expected_msg) == std::string::npos;
+ EXPECT_FALSE(subprocess_exited_without_matching_message) << details;
+}
+
+void UnitTests::DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux) {
+ int expected_exit_code = static_cast<int>(reinterpret_cast<intptr_t>(aux));
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_normally = WIFEXITED(status);
+ ASSERT_TRUE(subprocess_terminated_normally) << details;
+ int subprocess_exit_status = WEXITSTATUS(status);
+ ASSERT_EQ(expected_exit_code, subprocess_exit_status) << details;
+}
+
+void UnitTests::DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux) {
+ int expected_signo = static_cast<int>(reinterpret_cast<intptr_t>(aux));
+ std::string details(TestFailedMessage(msg));
+
+ bool subprocess_terminated_by_signal = WIFSIGNALED(status);
+ ASSERT_TRUE(subprocess_terminated_by_signal) << details;
+ int subprocess_signal_number = WTERMSIG(status);
+ ASSERT_EQ(expected_signo, subprocess_signal_number) << details;
+}
+
+void UnitTests::AssertionFailure(const char* expr, const char* file, int line) {
+ fprintf(stderr, "%s:%d:%s", file, line, expr);
+ fflush(stderr);
+ _exit(kExitWithAssertionFailure);
+}
+
+void UnitTests::IgnoreThisTest() {
+ fflush(stderr);
+ _exit(kIgnoreThisTest);
+}
+
+} // namespace
diff --git a/sandbox/linux/tests/unit_tests.h b/sandbox/linux/tests/unit_tests.h
new file mode 100644
index 0000000000..5a7116e932
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests.h
@@ -0,0 +1,201 @@
+// 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_TESTS_UNIT_TESTS_H_
+#define SANDBOX_LINUX_TESTS_UNIT_TESTS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/tests/sandbox_test_runner_function_pointer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Has this been compiled to run on Android?
+bool IsAndroid();
+
+bool IsArchitectureArm();
+
+// Is Valgrind currently being used?
+bool IsRunningOnValgrind();
+
+#if defined(ADDRESS_SANITIZER)
+#define DISABLE_ON_ASAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ASAN(test_name) test_name
+#endif // defined(ADDRESS_SANITIZER)
+
+#if defined(LEAK_SANITIZER)
+#define DISABLE_ON_LSAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_LSAN(test_name) test_name
+#endif
+
+#if defined(THREAD_SANITIZER)
+#define DISABLE_ON_TSAN(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_TSAN(test_name) test_name
+#endif // defined(THREAD_SANITIZER)
+
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
+ defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) || \
+ defined(UNDEFINED_SANITIZER) || defined(SANITIZER_COVERAGE)
+#define DISABLE_ON_SANITIZERS(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_SANITIZERS(test_name) test_name
+#endif
+
+#if defined(OS_ANDROID)
+#define DISABLE_ON_ANDROID(test_name) DISABLED_##test_name
+#else
+#define DISABLE_ON_ANDROID(test_name) test_name
+#endif
+
+// While it is perfectly OK for a complex test to provide its own DeathCheck
+// function. Most death tests have very simple requirements. These tests should
+// use one of the predefined DEATH_XXX macros as an argument to
+// SANDBOX_DEATH_TEST(). You can check for a (sub-)string in the output of the
+// test, for a particular exit code, or for a particular death signal.
+// NOTE: If you do decide to write your own DeathCheck, make sure to use
+// gtests's ASSERT_XXX() macros instead of SANDBOX_ASSERT(). See
+// unit_tests.cc for examples.
+#define DEATH_SUCCESS() sandbox::UnitTests::DeathSuccess, NULL
+#define DEATH_SUCCESS_ALLOW_NOISE() \
+ sandbox::UnitTests::DeathSuccessAllowNoise, NULL
+#define DEATH_MESSAGE(msg) \
+ sandbox::UnitTests::DeathMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_SEGV_MESSAGE(msg) \
+ sandbox::UnitTests::DeathSEGVMessage, \
+ static_cast<const void*>(static_cast<const char*>(msg))
+#define DEATH_EXIT_CODE(rc) \
+ sandbox::UnitTests::DeathExitCode, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(rc))
+#define DEATH_BY_SIGNAL(s) \
+ sandbox::UnitTests::DeathBySignal, \
+ reinterpret_cast<void*>(static_cast<intptr_t>(s))
+
+// A SANDBOX_DEATH_TEST is just like a SANDBOX_TEST (see below), but it assumes
+// that the test actually dies. The death test only passes if the death occurs
+// in the expected fashion, as specified by "death" and "death_aux". These two
+// parameters are typically set to one of the DEATH_XXX() macros.
+#define SANDBOX_DEATH_TEST(test_case_name, test_name, death) \
+ void TEST_##test_name(void); \
+ TEST(test_case_name, test_name) { \
+ SandboxTestRunnerFunctionPointer sandbox_test_runner(TEST_##test_name); \
+ sandbox::UnitTests::RunTestInProcess(&sandbox_test_runner, death); \
+ } \
+ void TEST_##test_name(void)
+
+// Define a new test case that runs inside of a GTest death test. This is
+// necessary, as most of our tests by definition make global and irreversible
+// changes to the system (i.e. they install a sandbox). GTest provides death
+// tests as a tool to isolate global changes from the rest of the tests.
+#define SANDBOX_TEST(test_case_name, test_name) \
+ SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS())
+
+// SANDBOX_TEST_ALLOW_NOISE is just like SANDBOX_TEST, except it does not
+// consider log error messages printed by the test to be test failures.
+#define SANDBOX_TEST_ALLOW_NOISE(test_case_name, test_name) \
+ SANDBOX_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS_ALLOW_NOISE())
+
+// Simple assertion macro that is compatible with running inside of a death
+// test. We unfortunately cannot use any of the GTest macros.
+#define SANDBOX_STR(x) #x
+#define SANDBOX_ASSERT(expr) \
+ ((expr) ? static_cast<void>(0) : sandbox::UnitTests::AssertionFailure( \
+ SANDBOX_STR(expr), __FILE__, __LINE__))
+
+#define SANDBOX_ASSERT_EQ(x, y) SANDBOX_ASSERT((x) == (y))
+#define SANDBOX_ASSERT_NE(x, y) SANDBOX_ASSERT((x) != (y))
+#define SANDBOX_ASSERT_LT(x, y) SANDBOX_ASSERT((x) < (y))
+#define SANDBOX_ASSERT_GT(x, y) SANDBOX_ASSERT((x) > (y))
+#define SANDBOX_ASSERT_LE(x, y) SANDBOX_ASSERT((x) <= (y))
+#define SANDBOX_ASSERT_GE(x, y) SANDBOX_ASSERT((x) >= (y))
+
+// This class allows to run unittests in their own process. The main method is
+// RunTestInProcess().
+class UnitTests {
+ public:
+ typedef void (*DeathCheck)(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // Runs a test inside a short-lived process. Do not call this function
+ // directly. It is automatically invoked by SANDBOX_TEST(). Most sandboxing
+ // functions make global irreversible changes to the execution environment
+ // and must therefore execute in their own isolated process.
+ // |test_runner| must implement the SandboxTestRunner interface and will run
+ // in a subprocess.
+ // Note: since the child process (created with fork()) will never return from
+ // RunTestInProcess(), |test_runner| is guaranteed to exist for the lifetime
+ // of the child process.
+ static void RunTestInProcess(SandboxTestRunner* test_runner,
+ DeathCheck death,
+ const void* death_aux);
+
+ // Report a useful error message and terminate the current SANDBOX_TEST().
+ // Calling this function from outside a SANDBOX_TEST() is unlikely to do
+ // anything useful.
+ static void AssertionFailure(const char* expr, const char* file, int line);
+
+ // Sometimes we determine at run-time that a test should be disabled.
+ // Call this method if we want to return from a test and completely
+ // ignore its results.
+ // You should not call this method, if the test already ran any test-relevant
+ // code. Most notably, you should not call it, you already wrote any messages
+ // to stderr.
+ static void IgnoreThisTest();
+
+ // A DeathCheck method that verifies that the test completed succcessfully.
+ // This is the default test mode for SANDBOX_TEST(). The "aux" parameter
+ // of this DeathCheck is unused (and thus unnamed)
+ static void DeathSuccess(int status, const std::string& msg, const void*);
+
+ // A DeathCheck method that verifies that the test completed succcessfully
+ // allowing for log error messages.
+ static void DeathSuccessAllowNoise(int status,
+ const std::string& msg,
+ const void*);
+
+ // A DeathCheck method that verifies that the test completed with error
+ // code "1" and printed a message containing a particular substring. The
+ // "aux" pointer should point to a C-string containing the expected error
+ // message. This method is useful for checking assertion failures such as
+ // in SANDBOX_ASSERT() and/or SANDBOX_DIE().
+ static void DeathMessage(int status, const std::string& msg, const void* aux);
+
+ // Like DeathMessage() but the process must be terminated with a segmentation
+ // fault.
+ // Implementation detail: On Linux (but not on Android), this does check for
+ // the return value of our default signal handler rather than for the actual
+ // reception of a SIGSEGV.
+ // TODO(jln): make this more robust.
+ static void DeathSEGVMessage(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // A DeathCheck method that verifies that the test completed with a
+ // particular exit code. If the test output any messages to stderr, they are
+ // silently ignored. The expected exit code should be passed in by
+ // casting the its "int" value to a "void *", which is then used for "aux".
+ static void DeathExitCode(int status,
+ const std::string& msg,
+ const void* aux);
+
+ // A DeathCheck method that verifies that the test was terminated by a
+ // particular signal. If the test output any messages to stderr, they are
+ // silently ignore. The expected signal number should be passed in by
+ // casting the its "int" value to a "void *", which is then used for "aux".
+ static void DeathBySignal(int status,
+ const std::string& msg,
+ const void* aux);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UnitTests);
+};
+
+} // namespace
+
+#endif // SANDBOX_LINUX_TESTS_UNIT_TESTS_H_
diff --git a/sandbox/linux/tests/unit_tests_unittest.cc b/sandbox/linux/tests/unit_tests_unittest.cc
new file mode 100644
index 0000000000..57799b14c0
--- /dev/null
+++ b/sandbox/linux/tests/unit_tests_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+namespace {
+
+// Let's not use any of the "magic" values used internally in unit_tests.cc,
+// such as kExpectedValue.
+const int kExpectedExitCode = 100;
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathExitCode,
+ DEATH_EXIT_CODE(kExpectedExitCode)) {
+ _exit(kExpectedExitCode);
+}
+
+const int kExpectedSignalNumber = SIGKILL;
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathBySignal,
+ DEATH_BY_SIGNAL(kExpectedSignalNumber)) {
+ raise(kExpectedSignalNumber);
+}
+
+SANDBOX_DEATH_TEST(UnitTests,
+ DeathWithMessage,
+ DEATH_MESSAGE("Hello")) {
+ LOG(ERROR) << "Hello";
+ _exit(1);
+}
+
+SANDBOX_DEATH_TEST(UnitTests,
+ SEGVDeathWithMessage,
+ DEATH_SEGV_MESSAGE("Hello")) {
+ LOG(ERROR) << "Hello";
+ while (1) {
+ volatile char* addr = reinterpret_cast<volatile char*>(NULL);
+ *addr = '\0';
+ }
+
+ _exit(2);
+}
+
+SANDBOX_TEST_ALLOW_NOISE(UnitTests, NoisyTest) {
+ LOG(ERROR) << "The cow says moo!";
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/sandbox/mac/BUILD.gn b/sandbox/mac/BUILD.gn
new file mode 100644
index 0000000000..13960bfaef
--- /dev/null
+++ b/sandbox/mac/BUILD.gn
@@ -0,0 +1,101 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/mac/mac_sdk.gni")
+import("//testing/test.gni")
+
+generate_stubs_script = "//tools/generate_stubs/generate_stubs.py"
+generate_stubs_header = "xpc_stubs_header.fragment"
+generate_stubs_sig_public = "xpc_stubs.sig"
+generate_stubs_sig_private = "xpc_private_stubs.sig"
+generate_stubs_project = "sandbox/mac"
+generate_stubs_output_stem = "xpc_stubs"
+
+action("generate_stubs") {
+ script = generate_stubs_script
+ sources = [
+ generate_stubs_sig_private,
+ generate_stubs_sig_public,
+ ]
+ inputs = [
+ generate_stubs_header,
+ ]
+ outputs = [
+ "$target_gen_dir/$generate_stubs_output_stem.cc",
+ "$target_gen_dir/$generate_stubs_output_stem.h",
+ ]
+ args = [
+ "-i",
+ rebase_path(target_gen_dir, root_build_dir),
+ "-o",
+ rebase_path(target_gen_dir, root_build_dir),
+ "-t",
+ "posix_stubs",
+ "-e",
+ rebase_path(generate_stubs_header, root_build_dir),
+ "-s",
+ generate_stubs_output_stem,
+ "-p",
+ generate_stubs_project,
+ "-x",
+ "SANDBOX_EXPORT",
+ ]
+ args += rebase_path(sources, root_build_dir)
+}
+
+component("sandbox") {
+ sources = [
+ "bootstrap_sandbox.cc",
+ "bootstrap_sandbox.h",
+ "launchd_interception_server.cc",
+ "launchd_interception_server.h",
+ "mach_message_server.cc",
+ "mach_message_server.h",
+ "message_server.h",
+ "os_compatibility.cc",
+ "os_compatibility.h",
+ "policy.cc",
+ "policy.h",
+ "xpc.cc",
+ "xpc.h",
+ "xpc_message_server.cc",
+ "xpc_message_server.h",
+ ]
+
+ defines = [ "SANDBOX_IMPLEMENTATION" ]
+ libs = [ "bsm" ]
+
+ deps = [
+ "//base",
+ ]
+
+ # When the build SDK is 10.6, generate a dynamic stub loader. When the
+ # SDK is higher, then libxpc.dylib will be loaded automatically as part
+ # of libSystem, and only forward declarations of private symbols are
+ # necessary.
+ if (mac_sdk_version == "10.6") {
+ deps += [ ":generate_stubs" ]
+ sources += get_target_outputs(":generate_stubs")
+ }
+}
+
+test("sandbox_mac_unittests") {
+ sources = [
+ "bootstrap_sandbox_unittest.mm",
+ "policy_unittest.cc",
+ "xpc_message_server_unittest.cc",
+ ]
+
+ libs = [
+ "CoreFoundation.framework",
+ "Foundation.framework",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//base",
+ "//base/test:run_all_unittests",
+ "//testing/gtest",
+ ]
+}
diff --git a/sandbox/mac/OWNERS b/sandbox/mac/OWNERS
new file mode 100644
index 0000000000..163563f967
--- /dev/null
+++ b/sandbox/mac/OWNERS
@@ -0,0 +1,2 @@
+mark@chromium.org
+rsesek@chromium.org
diff --git a/sandbox/mac/bootstrap_sandbox.cc b/sandbox/mac/bootstrap_sandbox.cc
new file mode 100644
index 0000000000..a90f570eb4
--- /dev/null
+++ b/sandbox/mac/bootstrap_sandbox.cc
@@ -0,0 +1,133 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/bootstrap_sandbox.h"
+
+#include <servers/bootstrap.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "sandbox/mac/launchd_interception_server.h"
+
+namespace sandbox {
+
+const int kNotAPolicy = -1;
+
+// static
+scoped_ptr<BootstrapSandbox> BootstrapSandbox::Create() {
+ scoped_ptr<BootstrapSandbox> null; // Used for early returns.
+ scoped_ptr<BootstrapSandbox> sandbox(new BootstrapSandbox());
+ sandbox->server_.reset(new LaunchdInterceptionServer(sandbox.get()));
+
+ // Check in with launchd to get the receive right for the server that is
+ // published in the bootstrap namespace.
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_check_in(bootstrap_port,
+ sandbox->server_bootstrap_name().c_str(), &port);
+ if (kr != KERN_SUCCESS) {
+ BOOTSTRAP_LOG(ERROR, kr)
+ << "Failed to bootstrap_check_in the sandbox server.";
+ return null.Pass();
+ }
+ base::mac::ScopedMachReceiveRight scoped_port(port);
+
+ // Start the sandbox server.
+ if (sandbox->server_->Initialize(scoped_port.get()))
+ ignore_result(scoped_port.release()); // Transferred to server_.
+ else
+ return null.Pass();
+
+ return sandbox.Pass();
+}
+
+BootstrapSandbox::~BootstrapSandbox() {
+}
+
+void BootstrapSandbox::RegisterSandboxPolicy(
+ int sandbox_policy_id,
+ const BootstrapSandboxPolicy& policy) {
+ CHECK(IsPolicyValid(policy));
+ CHECK_GT(sandbox_policy_id, kNotAPolicy);
+ base::AutoLock lock(lock_);
+ DCHECK(policies_.find(sandbox_policy_id) == policies_.end());
+ policies_.insert(std::make_pair(sandbox_policy_id, policy));
+}
+
+void BootstrapSandbox::PrepareToForkWithPolicy(int sandbox_policy_id) {
+ base::AutoLock lock(lock_);
+
+ // Verify that this is a real policy.
+ CHECK(policies_.find(sandbox_policy_id) != policies_.end());
+ CHECK_EQ(kNotAPolicy, effective_policy_id_)
+ << "Cannot nest calls to PrepareToForkWithPolicy()";
+
+ // Store the policy for the process we're about to create.
+ effective_policy_id_ = sandbox_policy_id;
+}
+
+// TODO(rsesek): The |lock_| needs to be taken twice because
+// base::LaunchProcess handles both fork+exec, and holding the lock for the
+// duration would block servicing of other bootstrap messages. If a better
+// LaunchProcess existed (do arbitrary work without layering violations), this
+// could be avoided.
+
+void BootstrapSandbox::FinishedFork(base::ProcessHandle handle) {
+ base::AutoLock lock(lock_);
+
+ CHECK_NE(kNotAPolicy, effective_policy_id_)
+ << "Must PrepareToForkWithPolicy() before FinishedFork()";
+
+ // Apply the policy to the new process.
+ if (handle != base::kNullProcessHandle) {
+ const auto& existing_process = sandboxed_processes_.find(handle);
+ CHECK(existing_process == sandboxed_processes_.end());
+ sandboxed_processes_.insert(std::make_pair(handle, effective_policy_id_));
+ VLOG(3) << "Bootstrap sandbox enforced for pid " << handle;
+ }
+
+ effective_policy_id_ = kNotAPolicy;
+}
+
+void BootstrapSandbox::ChildDied(base::ProcessHandle handle) {
+ base::AutoLock lock(lock_);
+ const auto& it = sandboxed_processes_.find(handle);
+ if (it != sandboxed_processes_.end())
+ sandboxed_processes_.erase(it);
+}
+
+const BootstrapSandboxPolicy* BootstrapSandbox::PolicyForProcess(
+ pid_t pid) const {
+ base::AutoLock lock(lock_);
+ const auto& process = sandboxed_processes_.find(pid);
+
+ // The new child could send bootstrap requests before the parent calls
+ // FinishedFork().
+ int policy_id = effective_policy_id_;
+ if (process != sandboxed_processes_.end()) {
+ policy_id = process->second;
+ }
+
+ if (policy_id == kNotAPolicy)
+ return NULL;
+
+ return &policies_.find(policy_id)->second;
+}
+
+BootstrapSandbox::BootstrapSandbox()
+ : server_bootstrap_name_(
+ base::StringPrintf("%s.sandbox.%d", base::mac::BaseBundleID(),
+ getpid())),
+ real_bootstrap_port_(MACH_PORT_NULL),
+ effective_policy_id_(kNotAPolicy) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = task_get_special_port(
+ mach_task_self(), TASK_BOOTSTRAP_PORT, &port);
+ MACH_CHECK(kr == KERN_SUCCESS, kr);
+ real_bootstrap_port_.reset(port);
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/bootstrap_sandbox.h b/sandbox/mac/bootstrap_sandbox.h
new file mode 100644
index 0000000000..fd808cdf61
--- /dev/null
+++ b/sandbox/mac/bootstrap_sandbox.h
@@ -0,0 +1,114 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_BOOTSTRAP_SANDBOX_H_
+#define SANDBOX_MAC_BOOTSTRAP_SANDBOX_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <string>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/process/process_handle.h"
+#include "base/synchronization/lock.h"
+#include "sandbox/mac/policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+class LaunchdInterceptionServer;
+
+// The BootstrapSandbox is a second-layer sandbox for Mac. It is used to limit
+// the bootstrap namespace attack surface of child processes. The parent
+// process creates an instance of this class and registers policies that it
+// can enforce on its children.
+//
+// With this sandbox, the parent process must replace the bootstrap port prior
+// to the sandboxed target's execution. This should be done by setting the
+// base::LaunchOptions.replacement_bootstrap_name to the
+// server_bootstrap_name() of this class. Requests from the child that would
+// normally go to launchd are filtered based on the specified per-process
+// policies. If a request is permitted by the policy, it is forwarded on to
+// launchd for servicing. If it is not, then the sandbox will reply with a
+// primitive that does not grant additional capabilities to the receiver.
+//
+// Clients that which to use the sandbox must inform it of the creation and
+// death of child processes for which the sandbox should be enforced. The
+// client of the sandbox is intended to be an unsandboxed parent process that
+// fork()s sandboxed (and other unsandboxed) child processes.
+//
+// When the parent is ready to fork a new child process with this sandbox
+// being enforced, it should use the pair of methods PrepareToForkWithPolicy()
+// and FinishedFork(), and call fork() between them. The first method will
+// set the policy for the new process, and the second will finialize the
+// association between the process ID and sandbox policy ID.
+//
+// All methods of this class may be called from any thread, but
+// PrepareToForkWithPolicy() and FinishedFork() must be non-nested and balanced.
+class SANDBOX_EXPORT BootstrapSandbox {
+ public:
+ // Creates a new sandbox manager. Returns NULL on failure.
+ static scoped_ptr<BootstrapSandbox> Create();
+
+ ~BootstrapSandbox();
+
+ // Registers a bootstrap policy associated it with an identifier. The
+ // |sandbox_policy_id| must be greater than 0.
+ void RegisterSandboxPolicy(int sandbox_policy_id,
+ const BootstrapSandboxPolicy& policy);
+
+ // Called in the parent prior to fork()ing a child. The policy registered
+ // to |sandbox_policy_id| will be enforced on the new child. This must be
+ // followed by a call to FinishedFork().
+ void PrepareToForkWithPolicy(int sandbox_policy_id);
+
+ // Called in the parent after fork()ing a child. It records the |handle|
+ // and associates it with the specified-above |sandbox_policy_id|.
+ // If fork() failed and a new child was not created, pass kNullProcessHandle.
+ void FinishedFork(base::ProcessHandle handle);
+
+ // Called in the parent when a process has died. It cleans up the references
+ // to the process.
+ void ChildDied(base::ProcessHandle handle);
+
+ // Looks up the policy for a given process ID. If no policy is associated
+ // with the |pid|, this returns NULL.
+ const BootstrapSandboxPolicy* PolicyForProcess(pid_t pid) const;
+
+ std::string server_bootstrap_name() const { return server_bootstrap_name_; }
+ mach_port_t real_bootstrap_port() const { return real_bootstrap_port_; }
+
+ private:
+ BootstrapSandbox();
+
+ // The name in the system bootstrap server by which the |server_|'s port
+ // is known.
+ const std::string server_bootstrap_name_;
+
+ // The original bootstrap port of the process, which is connected to the
+ // real launchd server.
+ base::mac::ScopedMachSendRight real_bootstrap_port_;
+
+ // The |lock_| protects all the following variables.
+ mutable base::Lock lock_;
+
+ // The sandbox_policy_id that will be enforced for the new child.
+ int effective_policy_id_;
+
+ // All the policies that have been registered with this sandbox manager.
+ std::map<int, const BootstrapSandboxPolicy> policies_;
+
+ // The association between process ID and sandbox policy ID.
+ std::map<base::ProcessHandle, int> sandboxed_processes_;
+
+ // A Mach IPC message server that is used to intercept and filter bootstrap
+ // requests.
+ scoped_ptr<LaunchdInterceptionServer> server_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_BOOTSTRAP_SANDBOX_H_
diff --git a/sandbox/mac/bootstrap_sandbox_unittest.mm b/sandbox/mac/bootstrap_sandbox_unittest.mm
new file mode 100644
index 0000000000..717f3f99f3
--- /dev/null
+++ b/sandbox/mac/bootstrap_sandbox_unittest.mm
@@ -0,0 +1,518 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/bootstrap_sandbox.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#import <Foundation/Foundation.h>
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
+
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/process/kill.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#import "testing/gtest_mac.h"
+#include "testing/multiprocess_func_list.h"
+
+NSString* const kTestNotification = @"org.chromium.bootstrap_sandbox_test";
+
+@interface DistributedNotificationObserver : NSObject {
+ @private
+ int receivedCount_;
+ base::scoped_nsobject<NSString> object_;
+}
+- (int)receivedCount;
+- (NSString*)object;
+- (void)waitForNotification;
+@end
+
+@implementation DistributedNotificationObserver
+- (id)init {
+ if ((self = [super init])) {
+ [[NSDistributedNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(observeNotification:)
+ name:kTestNotification
+ object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [[NSDistributedNotificationCenter defaultCenter]
+ removeObserver:self
+ name:kTestNotification
+ object:nil];
+ [super dealloc];
+}
+
+- (int)receivedCount {
+ return receivedCount_;
+}
+
+- (NSString*)object {
+ return object_.get();
+}
+
+- (void)waitForNotification {
+ object_.reset();
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode,
+ TestTimeouts::action_timeout().InSeconds(), false);
+}
+
+- (void)observeNotification:(NSNotification*)notification {
+ ++receivedCount_;
+ object_.reset([[notification object] copy]);
+ CFRunLoopStop(CFRunLoopGetCurrent());
+}
+@end
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace sandbox {
+
+class BootstrapSandboxTest : public base::MultiProcessTest {
+ public:
+ void SetUp() override {
+ base::MultiProcessTest::SetUp();
+
+ sandbox_ = BootstrapSandbox::Create();
+ ASSERT_TRUE(sandbox_.get());
+ }
+
+ BootstrapSandboxPolicy BaselinePolicy() {
+ BootstrapSandboxPolicy policy;
+ if (base::mac::IsOSSnowLeopard())
+ policy.rules["com.apple.SecurityServer"] = Rule(POLICY_ALLOW);
+ return policy;
+ }
+
+ void RunChildWithPolicy(int policy_id,
+ const char* child_name,
+ base::ProcessHandle* out_pid) {
+ sandbox_->PrepareToForkWithPolicy(policy_id);
+ base::LaunchOptions options;
+ options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
+ base::Process process = SpawnChildWithOptions(child_name, options);
+ ASSERT_TRUE(process.IsValid());
+ sandbox_->FinishedFork(process.Handle());
+ int code = 0;
+ EXPECT_TRUE(process.WaitForExit(&code));
+ EXPECT_EQ(0, code);
+ if (out_pid)
+ *out_pid = process.Pid();
+ }
+
+ protected:
+ scoped_ptr<BootstrapSandbox> sandbox_;
+};
+
+const char kNotificationTestMain[] = "PostNotification";
+
+// Run the test without the sandbox.
+TEST_F(BootstrapSandboxTest, DistributedNotifications_Unsandboxed) {
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ base::Process process = SpawnChild(kNotificationTestMain);
+ ASSERT_TRUE(process.IsValid());
+ int code = 0;
+ EXPECT_TRUE(process.WaitForExit(&code));
+ EXPECT_EQ(0, code);
+
+ [observer waitForNotification];
+ EXPECT_EQ(1, [observer receivedCount]);
+ EXPECT_EQ(process.Pid(), [[observer object] intValue]);
+}
+
+// Run the test with the sandbox enabled without notifications on the policy
+// whitelist.
+TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxDeny) {
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ sandbox_->RegisterSandboxPolicy(1, BaselinePolicy());
+ RunChildWithPolicy(1, kNotificationTestMain, NULL);
+
+ [observer waitForNotification];
+ EXPECT_EQ(0, [observer receivedCount]);
+ EXPECT_EQ(nil, [observer object]);
+}
+
+// Run the test with notifications permitted.
+TEST_F(BootstrapSandboxTest, DistributedNotifications_SandboxAllow) {
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ // 10.9:
+ policy.rules["com.apple.distributed_notifications@Uv3"] = Rule(POLICY_ALLOW);
+ policy.rules["com.apple.distributed_notifications@1v3"] = Rule(POLICY_ALLOW);
+ // 10.6:
+ policy.rules["com.apple.system.notification_center"] = Rule(POLICY_ALLOW);
+ policy.rules["com.apple.distributed_notifications.2"] = Rule(POLICY_ALLOW);
+ sandbox_->RegisterSandboxPolicy(2, policy);
+
+ base::ProcessHandle pid;
+ RunChildWithPolicy(2, kNotificationTestMain, &pid);
+
+ [observer waitForNotification];
+ EXPECT_EQ(1, [observer receivedCount]);
+ EXPECT_EQ(pid, [[observer object] intValue]);
+}
+
+MULTIPROCESS_TEST_MAIN(PostNotification) {
+ [[NSDistributedNotificationCenter defaultCenter]
+ postNotificationName:kTestNotification
+ object:[NSString stringWithFormat:@"%d", getpid()]];
+ return 0;
+}
+
+const char kTestServer[] = "org.chromium.test_bootstrap_server";
+
+TEST_F(BootstrapSandboxTest, PolicyDenyError) {
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules[kTestServer] = Rule(POLICY_DENY_ERROR);
+ sandbox_->RegisterSandboxPolicy(1, policy);
+
+ RunChildWithPolicy(1, "PolicyDenyError", NULL);
+}
+
+MULTIPROCESS_TEST_MAIN(PolicyDenyError) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
+ &port);
+ CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
+ CHECK(port == MACH_PORT_NULL);
+
+ kr = bootstrap_look_up(bootstrap_port, "org.chromium.some_other_server",
+ &port);
+ CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, kr);
+ CHECK(port == MACH_PORT_NULL);
+
+ return 0;
+}
+
+TEST_F(BootstrapSandboxTest, PolicyDenyDummyPort) {
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules[kTestServer] = Rule(POLICY_DENY_DUMMY_PORT);
+ sandbox_->RegisterSandboxPolicy(1, policy);
+
+ RunChildWithPolicy(1, "PolicyDenyDummyPort", NULL);
+}
+
+MULTIPROCESS_TEST_MAIN(PolicyDenyDummyPort) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer,
+ &port);
+ CHECK_EQ(KERN_SUCCESS, kr);
+ CHECK(port != MACH_PORT_NULL);
+ return 0;
+}
+
+struct SubstitutePortAckSend {
+ mach_msg_header_t header;
+ char buf[32];
+};
+
+struct SubstitutePortAckRecv : public SubstitutePortAckSend {
+ mach_msg_trailer_t trailer;
+};
+
+const char kSubstituteAck[] = "Hello, this is doge!";
+
+TEST_F(BootstrapSandboxTest, PolicySubstitutePort) {
+ mach_port_t task = mach_task_self();
+
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port(port);
+
+ mach_port_urefs_t send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(0u, send_rights);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules[kTestServer] = Rule(port);
+ sandbox_->RegisterSandboxPolicy(1, policy);
+
+ RunChildWithPolicy(1, "PolicySubstitutePort", NULL);
+
+ struct SubstitutePortAckRecv msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_local_port = port;
+ kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
+ msg.header.msgh_size, port,
+ TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
+ EXPECT_EQ(KERN_SUCCESS, kr);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
+}
+
+MULTIPROCESS_TEST_MAIN(PolicySubstitutePort) {
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port, kTestServer, &port);
+ CHECK_EQ(KERN_SUCCESS, kr);
+ CHECK(port != MACH_PORT_NULL);
+
+ struct SubstitutePortAckSend msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_remote_port = port;
+ msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
+ strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
+
+ CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
+
+ return 0;
+}
+
+TEST_F(BootstrapSandboxTest, ForwardMessageInProcess) {
+ mach_port_t task = mach_task_self();
+
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port_recv(port);
+
+ mach_port_urefs_t send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(0u, send_rights);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ mach_port_t bp;
+ ASSERT_EQ(KERN_SUCCESS, task_get_bootstrap_port(task, &bp));
+ base::mac::ScopedMachSendRight scoped_bp(bp);
+
+ char service_name[] = "org.chromium.sandbox.test.ForwardMessageInProcess";
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ kern_return_t kr = bootstrap_register(bp, service_name, port);
+#pragma GCC diagnostic pop
+ EXPECT_EQ(KERN_SUCCESS, kr);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ EXPECT_EQ(1u, send_rights);
+
+ mach_port_t service_port;
+ EXPECT_EQ(KERN_SUCCESS, bootstrap_look_up(bp, service_name, &service_port));
+ base::mac::ScopedMachSendRight scoped_service_port(service_port);
+
+ send_rights = 0;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_get_refs(task, port, MACH_PORT_RIGHT_SEND,
+ &send_rights));
+ // On 10.6, bootstrap_lookup2 may add an extra right to place it in a per-
+ // process cache.
+ if (base::mac::IsOSSnowLeopard())
+ EXPECT_TRUE(send_rights == 3u || send_rights == 2u) << send_rights;
+ else
+ EXPECT_EQ(2u, send_rights);
+}
+
+const char kDefaultRuleTestAllow[] =
+ "org.chromium.sandbox.test.DefaultRuleAllow";
+const char kDefaultRuleTestDeny[] =
+ "org.chromium.sandbox.test.DefaultRuleAllow.Deny";
+
+TEST_F(BootstrapSandboxTest, DefaultRuleAllow) {
+ mach_port_t task = mach_task_self();
+
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port_recv(port);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = Rule(POLICY_ALLOW);
+ policy.rules[kDefaultRuleTestAllow] = Rule(port);
+ policy.rules[kDefaultRuleTestDeny] = Rule(POLICY_DENY_ERROR);
+ sandbox_->RegisterSandboxPolicy(3, policy);
+
+ base::scoped_nsobject<DistributedNotificationObserver> observer(
+ [[DistributedNotificationObserver alloc] init]);
+
+ int pid = 0;
+ RunChildWithPolicy(3, "DefaultRuleAllow", &pid);
+ EXPECT_GT(pid, 0);
+
+ [observer waitForNotification];
+ EXPECT_EQ(1, [observer receivedCount]);
+ EXPECT_EQ(pid, [[observer object] intValue]);
+
+ struct SubstitutePortAckRecv msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_local_port = port;
+ kern_return_t kr = mach_msg(&msg.header, MACH_RCV_MSG, 0,
+ msg.header.msgh_size, port,
+ TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
+ EXPECT_EQ(KERN_SUCCESS, kr);
+
+ EXPECT_EQ(0, strncmp(kSubstituteAck, msg.buf, sizeof(msg.buf)));
+}
+
+MULTIPROCESS_TEST_MAIN(DefaultRuleAllow) {
+ [[NSDistributedNotificationCenter defaultCenter]
+ postNotificationName:kTestNotification
+ object:[NSString stringWithFormat:@"%d", getpid()]];
+
+ mach_port_t port = MACH_PORT_NULL;
+ CHECK_EQ(BOOTSTRAP_UNKNOWN_SERVICE, bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(kDefaultRuleTestDeny), &port));
+ CHECK(port == MACH_PORT_NULL);
+
+ CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port,
+ const_cast<char*>(kDefaultRuleTestAllow), &port));
+ CHECK(port != MACH_PORT_NULL);
+
+ struct SubstitutePortAckSend msg;
+ bzero(&msg, sizeof(msg));
+ msg.header.msgh_size = sizeof(msg);
+ msg.header.msgh_remote_port = port;
+ msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND);
+ strncpy(msg.buf, kSubstituteAck, sizeof(msg.buf));
+
+ CHECK_EQ(KERN_SUCCESS, mach_msg_send(&msg.header));
+
+ return 0;
+}
+
+TEST_F(BootstrapSandboxTest, ChildOutliveSandbox) {
+ const int kTestPolicyId = 1;
+ mach_port_t task = mach_task_self();
+
+ // Create a server port.
+ mach_port_t port;
+ ASSERT_EQ(KERN_SUCCESS, mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,
+ &port));
+ base::mac::ScopedMachReceiveRight scoped_port_recv(port);
+
+ ASSERT_EQ(KERN_SUCCESS, mach_port_insert_right(task, port, port,
+ MACH_MSG_TYPE_MAKE_SEND));
+ base::mac::ScopedMachSendRight scoped_port_send(port);
+
+ // Set up the policy and register the port.
+ BootstrapSandboxPolicy policy(BaselinePolicy());
+ policy.rules["sync"] = Rule(port);
+ sandbox_->RegisterSandboxPolicy(kTestPolicyId, policy);
+
+ // Launch the child.
+ sandbox_->PrepareToForkWithPolicy(kTestPolicyId);
+ base::LaunchOptions options;
+ options.replacement_bootstrap_name = sandbox_->server_bootstrap_name();
+ base::Process process = SpawnChildWithOptions("ChildOutliveSandbox", options);
+ ASSERT_TRUE(process.IsValid());
+ sandbox_->FinishedFork(process.Handle());
+
+ // Synchronize with the child.
+ mach_msg_empty_rcv_t rcv_msg;
+ bzero(&rcv_msg, sizeof(rcv_msg));
+ kern_return_t kr = mach_msg(&rcv_msg.header, MACH_RCV_MSG, 0,
+ sizeof(rcv_msg), port,
+ TestTimeouts::tiny_timeout().InMilliseconds(), MACH_PORT_NULL);
+ ASSERT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
+
+ // Destroy the sandbox.
+ sandbox_.reset();
+
+ // Synchronize again with the child.
+ mach_msg_empty_send_t send_msg;
+ bzero(&send_msg, sizeof(send_msg));
+ send_msg.header.msgh_size = sizeof(send_msg);
+ send_msg.header.msgh_remote_port = rcv_msg.header.msgh_remote_port;
+ send_msg.header.msgh_bits =
+ MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size, 0,
+ MACH_PORT_NULL, TestTimeouts::tiny_timeout().InMilliseconds(),
+ MACH_PORT_NULL);
+ EXPECT_EQ(KERN_SUCCESS, kr) << mach_error_string(kr);
+
+ int code = 0;
+ EXPECT_TRUE(process.WaitForExit(&code));
+ EXPECT_EQ(0, code);
+}
+
+MULTIPROCESS_TEST_MAIN(ChildOutliveSandbox) {
+ // Get the synchronization channel.
+ mach_port_t port = MACH_PORT_NULL;
+ CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, "sync", &port));
+
+ // Create a reply port.
+ mach_port_t reply_port;
+ CHECK_EQ(KERN_SUCCESS, mach_port_allocate(mach_task_self(),
+ MACH_PORT_RIGHT_RECEIVE, &reply_port));
+ base::mac::ScopedMachReceiveRight scoped_reply_port(reply_port);
+
+ // Send a message to shutdown the sandbox.
+ mach_msg_empty_send_t send_msg;
+ bzero(&send_msg, sizeof(send_msg));
+ send_msg.header.msgh_size = sizeof(send_msg);
+ send_msg.header.msgh_local_port = reply_port;
+ send_msg.header.msgh_remote_port = port;
+ send_msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ kern_return_t kr = mach_msg_send(&send_msg.header);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_send";
+
+ // Flood the server's message queue with messages. There should be some
+ // pending when the sandbox is destroyed.
+ for (int i = 0; i < 20; ++i) {
+ mach_port_t tmp = MACH_PORT_NULL;
+ std::string name = base::StringPrintf("test.%d", i);
+ bootstrap_look_up(bootstrap_port, const_cast<char*>(name.c_str()), &tmp);
+ }
+
+ // Ack that the sandbox has been shutdown.
+ mach_msg_empty_rcv_t rcv_msg;
+ bzero(&rcv_msg, sizeof(rcv_msg));
+ rcv_msg.header.msgh_size = sizeof(rcv_msg);
+ rcv_msg.header.msgh_local_port = reply_port;
+ kr = mach_msg_receive(&rcv_msg.header);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg_receive";
+
+ // Try to message the sandbox.
+ bootstrap_look_up(bootstrap_port, "test", &port);
+
+ return 0;
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/launchd_interception_server.cc b/sandbox/mac/launchd_interception_server.cc
new file mode 100644
index 0000000000..06f10812e4
--- /dev/null
+++ b/sandbox/mac/launchd_interception_server.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/launchd_interception_server.h"
+
+#include <servers/bootstrap.h>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "sandbox/mac/bootstrap_sandbox.h"
+#include "sandbox/mac/mach_message_server.h"
+
+namespace sandbox {
+
+// The buffer size for all launchd messages. This comes from
+// sizeof(union __RequestUnion__vproc_mig_job_subsystem) in launchd, and it
+// is larger than the __ReplyUnion.
+const mach_msg_size_t kBufferSize = 2096;
+
+LaunchdInterceptionServer::LaunchdInterceptionServer(
+ const BootstrapSandbox* sandbox)
+ : sandbox_(sandbox),
+ sandbox_port_(MACH_PORT_NULL),
+ compat_shim_(GetLaunchdCompatibilityShim()) {
+}
+
+LaunchdInterceptionServer::~LaunchdInterceptionServer() {
+}
+
+bool LaunchdInterceptionServer::Initialize(mach_port_t server_receive_right) {
+ mach_port_t task = mach_task_self();
+ kern_return_t kr;
+
+ // Allocate the dummy sandbox port.
+ mach_port_t port;
+ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
+ KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port.";
+ return false;
+ }
+ sandbox_port_.reset(port);
+ if ((kr = mach_port_insert_right(task, sandbox_port_, sandbox_port_,
+ MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate dummy sandbox port send right.";
+ return false;
+ }
+ sandbox_send_port_.reset(sandbox_port_);
+
+ message_server_.reset(
+ new MachMessageServer(this, server_receive_right, kBufferSize));
+ return message_server_->Initialize();
+}
+
+void LaunchdInterceptionServer::DemuxMessage(IPCMessage request) {
+ const uint64_t message_id = compat_shim_.ipc_message_get_id(request);
+ VLOG(3) << "Incoming message #" << message_id;
+
+ pid_t sender_pid = message_server_->GetMessageSenderPID(request);
+ const BootstrapSandboxPolicy* policy =
+ sandbox_->PolicyForProcess(sender_pid);
+ if (policy == NULL) {
+ // No sandbox policy is in place for the sender of this message, which
+ // means it came from the unknown. Reject it.
+ VLOG(3) << "Message from unknown pid " << sender_pid << " rejected.";
+ message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
+ return;
+ }
+
+ if (message_id == compat_shim_.msg_id_look_up2) {
+ // Filter messages sent via bootstrap_look_up to enforce the sandbox policy
+ // over the bootstrap namespace.
+ HandleLookUp(request, policy);
+ } else if (message_id == compat_shim_.msg_id_swap_integer) {
+ // Ensure that any vproc_swap_integer requests are safe.
+ HandleSwapInteger(request);
+ } else {
+ // All other messages are not permitted.
+ VLOG(1) << "Rejecting unhandled message #" << message_id;
+ message_server_->RejectMessage(request, MIG_REMOTE_ERROR);
+ }
+}
+
+void LaunchdInterceptionServer::HandleLookUp(
+ IPCMessage request,
+ const BootstrapSandboxPolicy* policy) {
+ const std::string request_service_name(
+ compat_shim_.look_up2_get_request_name(request));
+ VLOG(2) << "Incoming look_up2 request for " << request_service_name;
+
+ // Find the Rule for this service. If a named rule is not found, use the
+ // default specified by the policy.
+ const BootstrapSandboxPolicy::NamedRules::const_iterator it =
+ policy->rules.find(request_service_name);
+ Rule rule(policy->default_rule);
+ if (it != policy->rules.end())
+ rule = it->second;
+
+ if (rule.result == POLICY_ALLOW) {
+ // This service is explicitly allowed, so this message will not be
+ // intercepted by the sandbox.
+ VLOG(1) << "Permitting and forwarding look_up2: " << request_service_name;
+ ForwardMessage(request);
+ } else if (rule.result == POLICY_DENY_ERROR) {
+ // The child is not permitted to look up this service. Send a MIG error
+ // reply to the client. Returning a NULL or unserviced port for a look up
+ // can cause clients to crash or hang.
+ VLOG(1) << "Denying look_up2 with MIG error: " << request_service_name;
+ message_server_->RejectMessage(request, BOOTSTRAP_UNKNOWN_SERVICE);
+ } else if (rule.result == POLICY_DENY_DUMMY_PORT ||
+ rule.result == POLICY_SUBSTITUTE_PORT) {
+ // The policy result is to deny access to the real service port, replying
+ // with a sandboxed port in its stead. Use either the dummy sandbox_port_
+ // or the one specified in the policy.
+ VLOG(1) << "Intercepting look_up2 with a sandboxed service port: "
+ << request_service_name;
+
+ mach_port_t result_port;
+ if (rule.result == POLICY_DENY_DUMMY_PORT)
+ result_port = sandbox_port_.get();
+ else
+ result_port = rule.substitute_port;
+
+ IPCMessage reply = message_server_->CreateReply(request);
+ compat_shim_.look_up2_fill_reply(reply, result_port);
+ // If the message was sent successfully, clear the result_port out of the
+ // message so that it is not destroyed at the end of ReceiveMessage. The
+ // above-inserted right has been moved out of the process, and destroying
+ // the message will unref yet another right.
+ if (message_server_->SendReply(reply))
+ compat_shim_.look_up2_fill_reply(reply, MACH_PORT_NULL);
+ } else {
+ NOTREACHED();
+ }
+}
+
+void LaunchdInterceptionServer::HandleSwapInteger(IPCMessage request) {
+ // Only allow getting information out of launchd. Do not allow setting
+ // values. Two commonly observed values that are retrieved are
+ // VPROC_GSK_MGR_PID and VPROC_GSK_TRANSACTIONS_ENABLED.
+ if (compat_shim_.swap_integer_is_get_only(request)) {
+ VLOG(2) << "Forwarding vproc swap_integer message.";
+ ForwardMessage(request);
+ } else {
+ VLOG(2) << "Rejecting non-read-only swap_integer message.";
+ message_server_->RejectMessage(request, BOOTSTRAP_NOT_PRIVILEGED);
+ }
+}
+void LaunchdInterceptionServer::ForwardMessage(IPCMessage request) {
+ message_server_->ForwardMessage(request, sandbox_->real_bootstrap_port());
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/launchd_interception_server.h b/sandbox/mac/launchd_interception_server.h
new file mode 100644
index 0000000000..144d67d947
--- /dev/null
+++ b/sandbox/mac/launchd_interception_server.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_LAUNCHD_INTERCEPTION_SERVER_H_
+#define SANDBOX_MAC_LAUNCHD_INTERCEPTION_SERVER_H_
+
+#include <dispatch/dispatch.h>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/mac/message_server.h"
+#include "sandbox/mac/os_compatibility.h"
+
+namespace sandbox {
+
+class BootstrapSandbox;
+struct BootstrapSandboxPolicy;
+
+// This class is used to run a Mach IPC message server. This server can
+// hold the receive right for a bootstrap_port of a process, and it filters
+// a subset of the launchd/bootstrap IPC call set for sandboxing. It permits
+// or rejects requests based on the per-process policy specified in the
+// BootstrapSandbox.
+class LaunchdInterceptionServer : public MessageDemuxer {
+ public:
+ explicit LaunchdInterceptionServer(const BootstrapSandbox* sandbox);
+ ~LaunchdInterceptionServer() override;
+
+ // Initializes the class and starts running the message server. If the
+ // |server_receive_right| is non-NULL, this class will take ownership of
+ // the receive right and intercept messages sent to that port.
+ bool Initialize(mach_port_t server_receive_right);
+
+ // MessageDemuxer:
+ void DemuxMessage(IPCMessage request) override;
+
+ mach_port_t server_port() const { return message_server_->GetServerPort(); }
+
+ private:
+ // Given a look_up2 request message, this looks up the appropriate sandbox
+ // policy for the service name then formulates and sends the reply message.
+ void HandleLookUp(IPCMessage request, const BootstrapSandboxPolicy* policy);
+
+ // Given a swap_integer request message, this verifies that it is safe, and
+ // if so, forwards it on to launchd for servicing. If the request is unsafe,
+ // it replies with an error.
+ void HandleSwapInteger(IPCMessage request);
+
+ // Forwards the original |request| on to real bootstrap server for handling.
+ void ForwardMessage(IPCMessage request);
+
+ // The sandbox for which this message server is running.
+ const BootstrapSandbox* sandbox_;
+
+ // The Mach IPC server.
+ scoped_ptr<MessageServer> message_server_;
+
+ // The Mach port handed out in reply to denied look up requests. All denied
+ // requests share the same port, though nothing reads messages from it.
+ base::mac::ScopedMachReceiveRight sandbox_port_;
+ // The send right for the above |sandbox_port_|, used with
+ // MACH_MSG_TYPE_COPY_SEND when handing out references to the dummy port.
+ base::mac::ScopedMachSendRight sandbox_send_port_;
+
+ // The compatibility shim that handles differences in message header IDs and
+ // request/reply structures between different OS X versions.
+ const LaunchdCompatibilityShim compat_shim_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_LAUNCHD_INTERCEPTION_SERVER_H_
diff --git a/sandbox/mac/mach_message_server.cc b/sandbox/mac/mach_message_server.cc
new file mode 100644
index 0000000000..7cfcecc6cc
--- /dev/null
+++ b/sandbox/mac/mach_message_server.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/mach_message_server.h"
+
+#include <bsm/libbsm.h>
+#include <servers/bootstrap.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+
+namespace sandbox {
+
+MachMessageServer::MachMessageServer(
+ MessageDemuxer* demuxer,
+ mach_port_t server_receive_right,
+ mach_msg_size_t buffer_size)
+ : demuxer_(demuxer),
+ server_port_(server_receive_right),
+ buffer_size_(
+ mach_vm_round_page(buffer_size + sizeof(mach_msg_audit_trailer_t))),
+ did_forward_message_(false) {
+ DCHECK(demuxer_);
+}
+
+MachMessageServer::~MachMessageServer() {
+}
+
+bool MachMessageServer::Initialize() {
+ mach_port_t task = mach_task_self();
+ kern_return_t kr;
+
+ // Allocate a port for use as a new server port if one was not passed to the
+ // constructor.
+ if (!server_port_.is_valid()) {
+ mach_port_t port;
+ if ((kr = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &port)) !=
+ KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate new server port.";
+ return false;
+ }
+ server_port_.reset(port);
+ }
+
+ // Allocate the message request and reply buffers.
+ const int kMachMsgMemoryFlags = VM_MAKE_TAG(VM_MEMORY_MACH_MSG) |
+ VM_FLAGS_ANYWHERE;
+ vm_address_t buffer = 0;
+
+ kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate request buffer.";
+ return false;
+ }
+ request_buffer_.reset(buffer, buffer_size_);
+
+ kr = vm_allocate(task, &buffer, buffer_size_, kMachMsgMemoryFlags);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate reply buffer.";
+ return false;
+ }
+ reply_buffer_.reset(buffer, buffer_size_);
+
+ // Set up the dispatch queue to service the bootstrap port.
+ std::string label = base::StringPrintf(
+ "org.chromium.sandbox.MachMessageServer.%p", demuxer_);
+ dispatch_source_.reset(new base::DispatchSourceMach(
+ label.c_str(), server_port_.get(), ^{ ReceiveMessage(); }));
+ dispatch_source_->Resume();
+
+ return true;
+}
+
+pid_t MachMessageServer::GetMessageSenderPID(IPCMessage request) {
+ // Get the PID of the task that sent this request. This requires getting at
+ // the trailer of the message, from the header.
+ mach_msg_audit_trailer_t* trailer =
+ reinterpret_cast<mach_msg_audit_trailer_t*>(
+ reinterpret_cast<vm_address_t>(request.mach) +
+ round_msg(request.mach->msgh_size));
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t sender_pid;
+ audit_token_to_au32(trailer->msgh_audit,
+ NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
+ return sender_pid;
+}
+
+IPCMessage MachMessageServer::CreateReply(IPCMessage request_message) {
+ mach_msg_header_t* request = request_message.mach;
+
+ IPCMessage reply_message;
+ mach_msg_header_t* reply = reply_message.mach =
+ reinterpret_cast<mach_msg_header_t*>(reply_buffer_.address());
+ bzero(reply, buffer_size_);
+
+ reply->msgh_bits = MACH_MSGH_BITS_REMOTE(reply->msgh_bits);
+ // Since mach_msg will automatically swap the request and reply ports,
+ // undo that.
+ reply->msgh_remote_port = request->msgh_remote_port;
+ reply->msgh_local_port = MACH_PORT_NULL;
+ // MIG servers simply add 100 to the request ID to generate the reply ID.
+ reply->msgh_id = request->msgh_id + 100;
+
+ return reply_message;
+}
+
+bool MachMessageServer::SendReply(IPCMessage reply) {
+ kern_return_t kr = mach_msg(reply.mach, MACH_SEND_MSG,
+ reply.mach->msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+ << "Unable to send intercepted reply message.";
+ return kr == KERN_SUCCESS;
+}
+
+void MachMessageServer::ForwardMessage(IPCMessage message,
+ mach_port_t destination) {
+ mach_msg_header_t* request = message.mach;
+ request->msgh_local_port = request->msgh_remote_port;
+ request->msgh_remote_port = destination;
+ // Preserve the msgh_bits that do not deal with the local and remote ports.
+ request->msgh_bits = (request->msgh_bits & ~MACH_MSGH_BITS_PORTS_MASK) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ kern_return_t kr = mach_msg_send(request);
+ if (kr == KERN_SUCCESS) {
+ did_forward_message_ = true;
+ } else {
+ MACH_LOG(ERROR, kr) << "Unable to forward message to the real launchd.";
+ }
+}
+
+void MachMessageServer::RejectMessage(IPCMessage request, int error_code) {
+ IPCMessage reply = CreateReply(request);
+ mig_reply_error_t* error_reply =
+ reinterpret_cast<mig_reply_error_t*>(reply.mach);
+ error_reply->Head.msgh_size = sizeof(mig_reply_error_t);
+ error_reply->Head.msgh_bits =
+ MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ error_reply->NDR = NDR_record;
+ error_reply->RetCode = error_code;
+ SendReply(reply);
+}
+
+mach_port_t MachMessageServer::GetServerPort() const {
+ return server_port_.get();
+}
+
+void MachMessageServer::ReceiveMessage() {
+ const mach_msg_options_t kRcvOptions = MACH_RCV_MSG |
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+
+ mach_msg_header_t* request =
+ reinterpret_cast<mach_msg_header_t*>(request_buffer_.address());
+ mach_msg_header_t* reply =
+ reinterpret_cast<mach_msg_header_t*>(reply_buffer_.address());
+
+ // Zero out the buffers from handling any previous message.
+ bzero(request, buffer_size_);
+ bzero(reply, buffer_size_);
+ did_forward_message_ = false;
+
+ // A Mach message server-once. The system library to run a message server
+ // cannot be used here, because some requests are conditionally forwarded
+ // to another server.
+ kern_return_t kr = mach_msg(request, kRcvOptions, 0, buffer_size_,
+ server_port_.get(), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Unable to receive message.";
+ return;
+ }
+
+ // Process the message.
+ IPCMessage request_message = { request };
+ demuxer_->DemuxMessage(request_message);
+
+ // Free any descriptors in the message body. If the message was forwarded,
+ // any descriptors would have been moved out of the process on send. If the
+ // forwarded message was sent from the process hosting this sandbox server,
+ // destroying the message could also destroy rights held outside the scope of
+ // this message server.
+ if (!did_forward_message_) {
+ mach_msg_destroy(request);
+ mach_msg_destroy(reply);
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/mach_message_server.h b/sandbox/mac/mach_message_server.h
new file mode 100644
index 0000000000..20a543b3c7
--- /dev/null
+++ b/sandbox/mac/mach_message_server.h
@@ -0,0 +1,72 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_MACH_MESSAGE_SERVER_H_
+#define SANDBOX_MAC_MACH_MESSAGE_SERVER_H_
+
+#include <mach/mach.h>
+
+#include "base/mac/dispatch_source_mach.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/mac/message_server.h"
+
+namespace sandbox {
+
+// A Mach message server that operates a receive port. Messages are received
+// and then passed to the MessageDemuxer for handling. The Demuxer
+// can use the server class to send a reply, forward the message to a
+// different port, or reply to the message with a MIG error.
+class MachMessageServer : public MessageServer {
+ public:
+ // Creates a new Mach message server that will send messages to |demuxer|
+ // for handling. If the |server_receive_right| is non-NULL, this class will
+ // take ownership of the port and it will be used to receive messages.
+ // Otherwise the server will create a new receive right.
+ // The maximum size of messages is specified by |buffer_size|.
+ MachMessageServer(MessageDemuxer* demuxer,
+ mach_port_t server_receive_right,
+ mach_msg_size_t buffer_size);
+ ~MachMessageServer() override;
+
+ // MessageServer:
+ bool Initialize() override;
+ pid_t GetMessageSenderPID(IPCMessage request) override;
+ IPCMessage CreateReply(IPCMessage request) override;
+ bool SendReply(IPCMessage reply) override;
+ void ForwardMessage(IPCMessage request, mach_port_t destination) override;
+ // Replies to the message with the specified |error_code| as a MIG
+ // error_reply RetCode.
+ void RejectMessage(IPCMessage request, int error_code) override;
+ mach_port_t GetServerPort() const override;
+
+ private:
+ // Event handler for the |server_source_| that reads a message from the queue
+ // and processes it.
+ void ReceiveMessage();
+
+ // The demuxer delegate. Weak.
+ MessageDemuxer* demuxer_;
+
+ // The Mach port on which the server is receiving requests.
+ base::mac::ScopedMachReceiveRight server_port_;
+
+ // The size of the two message buffers below.
+ const mach_msg_size_t buffer_size_;
+
+ // Request and reply buffers used in ReceiveMessage.
+ base::mac::ScopedMachVM request_buffer_;
+ base::mac::ScopedMachVM reply_buffer_;
+
+ // MACH_RECV dispatch source that handles the |server_port_|.
+ scoped_ptr<base::DispatchSourceMach> dispatch_source_;
+
+ // Whether or not ForwardMessage() was called during ReceiveMessage().
+ bool did_forward_message_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_MACH_MESSAGE_SERVER_H_
diff --git a/sandbox/mac/message_server.h b/sandbox/mac/message_server.h
new file mode 100644
index 0000000000..1cd40b0a3d
--- /dev/null
+++ b/sandbox/mac/message_server.h
@@ -0,0 +1,71 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_MESSAGE_SERVER_H_
+#define SANDBOX_MAC_MESSAGE_SERVER_H_
+
+#include <mach/mach.h>
+#include <unistd.h>
+
+#include "sandbox/mac/xpc.h"
+
+namespace sandbox {
+
+// A message received by a MessageServer. Each concrete implementation of
+// that interface will handle the fields of this union appropriately.
+// Consumers should treat this as an opaque handle.
+union IPCMessage {
+ mach_msg_header_t* mach;
+ xpc_object_t xpc;
+};
+
+// A delegate interface for MessageServer that handles processing of
+// incoming intercepted IPC messages.
+class MessageDemuxer {
+ public:
+ // Handle a |request| message. The message is owned by the server. Use the
+ // server's methods to create and send a reply message.
+ virtual void DemuxMessage(IPCMessage request) = 0;
+
+ protected:
+ virtual ~MessageDemuxer() {}
+};
+
+// An interaface for an IPC server that implements Mach messaging semantics.
+// The concrete implementation may be powered by raw Mach messages, XPC, or
+// some other technology. This interface is the abstraction on top of those
+// that enables message interception.
+class MessageServer {
+ public:
+ virtual ~MessageServer() {}
+
+ // Initializes the class and starts running the message server. If this
+ // returns false, no other methods may be called on this class.
+ virtual bool Initialize() = 0;
+
+ // Given a received request message, returns the PID of the sending process.
+ virtual pid_t GetMessageSenderPID(IPCMessage request) = 0;
+
+ // Creates a reply message from a request message. The result is owned by
+ // the server.
+ virtual IPCMessage CreateReply(IPCMessage request) = 0;
+
+ // Sends a reply message. Returns true if the message was sent successfully.
+ virtual bool SendReply(IPCMessage reply) = 0;
+
+ // Forwards the original |request| to the |destination| for handling.
+ virtual void ForwardMessage(IPCMessage request, mach_port_t destination) = 0;
+
+ // Replies to the received |request| message by creating a reply and setting
+ // the specified |error_code| in a field that is interpreted by the
+ // underlying IPC system.
+ virtual void RejectMessage(IPCMessage request, int error_code) = 0;
+
+ // Returns the Mach port on which the MessageServer is listening.
+ virtual mach_port_t GetServerPort() const = 0;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_MESSAGE_SERVER_H_
diff --git a/sandbox/mac/os_compatibility.cc b/sandbox/mac/os_compatibility.cc
new file mode 100644
index 0000000000..6624f3a19b
--- /dev/null
+++ b/sandbox/mac/os_compatibility.cc
@@ -0,0 +1,135 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/os_compatibility.h"
+
+#include <servers/bootstrap.h>
+#include <unistd.h>
+
+#include "base/mac/mac_util.h"
+
+namespace sandbox {
+
+namespace {
+
+#pragma pack(push, 4)
+// Verified from launchd-329.3.3 (10.6.8).
+struct look_up2_request_10_6 {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ name_t servicename;
+ pid_t targetpid;
+ uint64_t flags;
+};
+
+struct look_up2_reply_10_6 {
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t service_port;
+};
+
+// Verified from:
+// launchd-392.39 (10.7.5)
+// launchd-442.26.2 (10.8.5)
+// launchd-842.1.4 (10.9.0)
+struct look_up2_request_10_7 {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ name_t servicename;
+ pid_t targetpid;
+ uuid_t instanceid;
+ uint64_t flags;
+};
+
+// look_up2_reply_10_7 is the same as the 10_6 version.
+
+// Verified from:
+// launchd-329.3.3 (10.6.8)
+// launchd-392.39 (10.7.5)
+// launchd-442.26.2 (10.8.5)
+// launchd-842.1.4 (10.9.0)
+typedef int vproc_gsk_t; // Defined as an enum in liblaunch/vproc_priv.h.
+struct swap_integer_request_10_6 {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ vproc_gsk_t inkey;
+ vproc_gsk_t outkey;
+ int64_t inval;
+};
+#pragma pack(pop)
+
+// TODO(rsesek): Libc provides strnlen() starting in 10.7.
+size_t strnlen(const char* str, size_t maxlen) {
+ size_t len = 0;
+ for (; len < maxlen; ++len, ++str) {
+ if (*str == '\0')
+ break;
+ }
+ return len;
+}
+
+uint64_t MachGetMessageID(const IPCMessage message) {
+ return message.mach->msgh_id;
+}
+
+template <typename R>
+std::string LaunchdLookUp2GetRequestName(const IPCMessage message) {
+ mach_msg_header_t* header = message.mach;
+ DCHECK_EQ(sizeof(R), header->msgh_size);
+ const R* request = reinterpret_cast<const R*>(header);
+ // Make sure the name is properly NUL-terminated.
+ const size_t name_length =
+ strnlen(request->servicename, BOOTSTRAP_MAX_NAME_LEN);
+ std::string name = std::string(request->servicename, name_length);
+ return name;
+}
+
+template <typename R>
+void LaunchdLookUp2FillReply(IPCMessage message, mach_port_t port) {
+ R* reply = reinterpret_cast<R*>(message.mach);
+ reply->Head.msgh_size = sizeof(R);
+ reply->Head.msgh_bits =
+ MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MOVE_SEND_ONCE) |
+ MACH_MSGH_BITS_COMPLEX;
+ reply->msgh_body.msgh_descriptor_count = 1;
+ reply->service_port.name = port;
+ reply->service_port.disposition = MACH_MSG_TYPE_COPY_SEND;
+ reply->service_port.type = MACH_MSG_PORT_DESCRIPTOR;
+}
+
+template <typename R>
+bool LaunchdSwapIntegerIsGetOnly(const IPCMessage message) {
+ const R* request = reinterpret_cast<const R*>(message.mach);
+ return request->inkey == 0 && request->inval == 0 && request->outkey != 0;
+}
+
+} // namespace
+
+const LaunchdCompatibilityShim GetLaunchdCompatibilityShim() {
+ LaunchdCompatibilityShim shim = {
+ .ipc_message_get_id = &MachGetMessageID,
+ .msg_id_look_up2 = 404,
+ .msg_id_swap_integer = 416,
+ .look_up2_fill_reply = &LaunchdLookUp2FillReply<look_up2_reply_10_6>,
+ .swap_integer_is_get_only =
+ &LaunchdSwapIntegerIsGetOnly<swap_integer_request_10_6>,
+ };
+
+ if (base::mac::IsOSSnowLeopard()) {
+ shim.look_up2_get_request_name =
+ &LaunchdLookUp2GetRequestName<look_up2_request_10_6>;
+ } else if (base::mac::IsOSLionOrLater() &&
+ !base::mac::IsOSYosemiteOrLater()) {
+ shim.look_up2_get_request_name =
+ &LaunchdLookUp2GetRequestName<look_up2_request_10_7>;
+ } else {
+ DLOG(ERROR) << "Unknown OS, using launchd compatibility shim from 10.7.";
+ shim.look_up2_get_request_name =
+ &LaunchdLookUp2GetRequestName<look_up2_request_10_7>;
+ }
+
+ return shim;
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/os_compatibility.h b/sandbox/mac/os_compatibility.h
new file mode 100644
index 0000000000..1205cd6868
--- /dev/null
+++ b/sandbox/mac/os_compatibility.h
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is used to handle differences in Mach message IDs and structures
+// that occur between different OS versions. The Mach messages that the sandbox
+// is interested in are decoded using information derived from the open-source
+// libraries, i.e. <http://www.opensource.apple.com/source/launchd/>. While
+// these messages definitions are open source, they are not considered part of
+// the stable OS API, and so differences do exist between OS versions.
+
+#ifndef SANDBOX_MAC_OS_COMPATIBILITY_H_
+#define SANDBOX_MAC_OS_COMPATIBILITY_H_
+
+#include <mach/mach.h>
+
+#include <string>
+
+#include "sandbox/mac/message_server.h"
+
+namespace sandbox {
+
+typedef uint64_t (*IPCMessageGetID)(const IPCMessage);
+
+typedef std::string (*LookUp2GetRequestName)(const IPCMessage);
+typedef void (*LookUp2FillReply)(IPCMessage, mach_port_t service_port);
+
+typedef bool (*SwapIntegerIsGetOnly)(const IPCMessage);
+
+struct LaunchdCompatibilityShim {
+ // Gets the message ID of an IPC message.
+ IPCMessageGetID ipc_message_get_id;
+
+ // The msgh_id for look_up2.
+ uint64_t msg_id_look_up2;
+
+ // The msgh_id for swap_integer.
+ uint64_t msg_id_swap_integer;
+
+ // A function to take a look_up2 message and return the string service name
+ // that was requested via the message.
+ LookUp2GetRequestName look_up2_get_request_name;
+
+ // A function to formulate a reply to a look_up2 message, given the reply
+ // message and the port to return as the service.
+ LookUp2FillReply look_up2_fill_reply;
+
+ // A function to take a swap_integer message and return true if the message
+ // is only getting the value of a key, neither setting it directly, nor
+ // swapping two keys.
+ SwapIntegerIsGetOnly swap_integer_is_get_only;
+};
+
+// Gets the compatibility shim for the launchd job subsystem.
+const LaunchdCompatibilityShim GetLaunchdCompatibilityShim();
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_OS_COMPATIBILITY_H_
diff --git a/sandbox/mac/policy.cc b/sandbox/mac/policy.cc
new file mode 100644
index 0000000000..293255adef
--- /dev/null
+++ b/sandbox/mac/policy.cc
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/policy.h"
+
+namespace sandbox {
+
+Rule::Rule()
+ : result(POLICY_DECISION_INVALID),
+ substitute_port(MACH_PORT_NULL) {
+}
+
+Rule::Rule(PolicyDecision result)
+ : result(result),
+ substitute_port(MACH_PORT_NULL) {
+}
+
+Rule::Rule(mach_port_t override_port)
+ : result(POLICY_SUBSTITUTE_PORT),
+ substitute_port(override_port) {
+}
+
+BootstrapSandboxPolicy::BootstrapSandboxPolicy()
+ : default_rule(POLICY_DENY_ERROR) {
+}
+
+BootstrapSandboxPolicy::~BootstrapSandboxPolicy() {}
+
+static bool IsRuleValid(const Rule& rule) {
+ if (!(rule.result > POLICY_DECISION_INVALID &&
+ rule.result < POLICY_DECISION_LAST)) {
+ return false;
+ }
+ if (rule.result == POLICY_SUBSTITUTE_PORT) {
+ if (rule.substitute_port == MACH_PORT_NULL)
+ return false;
+ } else {
+ if (rule.substitute_port != MACH_PORT_NULL)
+ return false;
+ }
+ return true;
+}
+
+bool IsPolicyValid(const BootstrapSandboxPolicy& policy) {
+ if (!IsRuleValid(policy.default_rule))
+ return false;
+
+ for (const auto& pair : policy.rules) {
+ if (!IsRuleValid(pair.second))
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/policy.h b/sandbox/mac/policy.h
new file mode 100644
index 0000000000..e500468237
--- /dev/null
+++ b/sandbox/mac/policy.h
@@ -0,0 +1,70 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_POLICY_H_
+#define SANDBOX_MAC_POLICY_H_
+
+#include <mach/mach.h>
+
+#include <map>
+#include <string>
+
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+enum PolicyDecision {
+ POLICY_DECISION_INVALID,
+ // Explicitly allows the real service to be looked up from launchd.
+ POLICY_ALLOW,
+ // Deny the look up request by replying with a MIG error. This is the
+ // default behavior for servers not given an explicit rule.
+ POLICY_DENY_ERROR,
+ // Deny the look up request with a well-formed reply containing a
+ // Mach port with a send right, messages to which will be ignored.
+ POLICY_DENY_DUMMY_PORT,
+ // Reply to the look up request with a send right to the substitute_port
+ // specified in the Rule.
+ POLICY_SUBSTITUTE_PORT,
+ POLICY_DECISION_LAST,
+};
+
+// A Rule expresses the action to take when a service port is requested via
+// bootstrap_look_up. If |result| is not POLICY_SUBSTITUTE_PORT, then
+// |substitute_port| must be NULL. If result is POLICY_SUBSTITUTE_PORT, then
+// |substitute_port| must not be NULL.
+struct SANDBOX_EXPORT Rule {
+ Rule();
+ explicit Rule(PolicyDecision result);
+ explicit Rule(mach_port_t override_port);
+
+ PolicyDecision result;
+
+ // The Rule does not take ownership of this port, but additional send rights
+ // will be allocated to it before it is sent to a client. This name must
+ // denote a send right that can duplicated with MACH_MSG_TYPE_COPY_SEND.
+ mach_port_t substitute_port;
+};
+
+// A policy object manages the rules enforced on a target sandboxed process.
+struct SANDBOX_EXPORT BootstrapSandboxPolicy {
+ typedef std::map<std::string, Rule> NamedRules;
+
+ BootstrapSandboxPolicy();
+ ~BootstrapSandboxPolicy();
+
+ // The default action to take if the server name being looked up is not
+ // present in |rules|.
+ Rule default_rule;
+
+ // A map of bootstrap server names to policy Rules.
+ NamedRules rules;
+};
+
+// Checks that a policy is well-formed.
+SANDBOX_EXPORT bool IsPolicyValid(const BootstrapSandboxPolicy& policy);
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_POLICY_H_
diff --git a/sandbox/mac/policy_unittest.cc b/sandbox/mac/policy_unittest.cc
new file mode 100644
index 0000000000..54e0e74895
--- /dev/null
+++ b/sandbox/mac/policy_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/policy.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+TEST(PolicyTest, ValidEmptyPolicy) {
+ EXPECT_TRUE(IsPolicyValid(BootstrapSandboxPolicy()));
+}
+
+TEST(PolicyTest, ValidPolicy) {
+ BootstrapSandboxPolicy policy;
+ policy.rules["allow"] = Rule(POLICY_ALLOW);
+ policy.rules["deny_error"] = Rule(POLICY_DENY_ERROR);
+ policy.rules["deny_dummy"] = Rule(POLICY_DENY_DUMMY_PORT);
+ policy.rules["substitue"] = Rule(mach_task_self());
+ EXPECT_TRUE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyEmptyRule) {
+ Rule rule;
+ BootstrapSandboxPolicy policy;
+ policy.rules["test"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicySubstitue) {
+ Rule rule(POLICY_SUBSTITUTE_PORT);
+ BootstrapSandboxPolicy policy;
+ policy.rules["test"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyWithPortAllow) {
+ Rule rule(POLICY_ALLOW);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.rules["allow"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyWithPortDenyError) {
+ Rule rule(POLICY_DENY_ERROR);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.rules["deny_error"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyWithPortDummy) {
+ Rule rule(POLICY_DENY_DUMMY_PORT);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.rules["deny_dummy"] = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRule) {
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = Rule();
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleSubstitue) {
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = Rule(POLICY_SUBSTITUTE_PORT);
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleWithPortAllow) {
+ Rule rule(POLICY_ALLOW);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleWithPortDenyError) {
+ Rule rule(POLICY_DENY_ERROR);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+TEST(PolicyTest, InvalidPolicyDefaultRuleWithPortDummy) {
+ Rule rule(POLICY_DENY_DUMMY_PORT);
+ rule.substitute_port = mach_task_self();
+ BootstrapSandboxPolicy policy;
+ policy.default_rule = rule;
+ EXPECT_FALSE(IsPolicyValid(policy));
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/sandbox_mac.gypi b/sandbox/mac/sandbox_mac.gypi
new file mode 100644
index 0000000000..d5013f832e
--- /dev/null
+++ b/sandbox/mac/sandbox_mac.gypi
@@ -0,0 +1,114 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': '<(component)',
+ 'sources': [
+ 'bootstrap_sandbox.cc',
+ 'bootstrap_sandbox.h',
+ 'launchd_interception_server.cc',
+ 'launchd_interception_server.h',
+ 'mach_message_server.cc',
+ 'mach_message_server.h',
+ 'message_server.h',
+ 'os_compatibility.cc',
+ 'os_compatibility.h',
+ 'policy.cc',
+ 'policy.h',
+ 'xpc.cc',
+ 'xpc.h',
+ 'xpc_message_server.cc',
+ 'xpc_message_server.h',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '..',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'defines': [
+ 'SANDBOX_IMPLEMENTATION',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/usr/lib/libbsm.dylib',
+ ],
+ },
+ 'conditions': [
+ # When the build SDK is 10.6, generate a dynamic stub loader. When the
+ # SDK is higher, then libxpc.dylib will be loaded automatically as part
+ # of libSystem, and only forward declarations of private symbols are
+ # necessary.
+ ['mac_sdk == "10.6"', {
+ 'actions': [
+ {
+ 'variables': {
+ 'generate_stubs_script': '../tools/generate_stubs/generate_stubs.py',
+ 'generate_stubs_header_path': 'xpc_stubs_header.fragment',
+ 'generate_stubs_sig_public_path': 'xpc_stubs.sig',
+ 'generate_stubs_sig_private_path': 'xpc_private_stubs.sig',
+ 'generate_stubs_project': 'sandbox/mac',
+ 'generate_stubs_output_stem': 'xpc_stubs',
+ },
+ 'action_name': 'generate_stubs',
+ 'inputs': [
+ '<(generate_stubs_script)',
+ '<(generate_stubs_header_path)',
+ '<(generate_stubs_sig_public_path)',
+ '<(generate_stubs_sig_private_path)',
+ ],
+ 'outputs': [
+ '<(INTERMEDIATE_DIR)/<(generate_stubs_output_stem).cc',
+ '<(SHARED_INTERMEDIATE_DIR)/<(generate_stubs_project)/<(generate_stubs_output_stem).h',
+ ],
+ 'action': [
+ 'python',
+ '<(generate_stubs_script)',
+ '-i', '<(INTERMEDIATE_DIR)',
+ '-o', '<(SHARED_INTERMEDIATE_DIR)/<(generate_stubs_project)',
+ '-t', 'posix_stubs',
+ '-e', '<(generate_stubs_header_path)',
+ '-s', '<(generate_stubs_output_stem)',
+ '-p', '<(generate_stubs_project)',
+ '-x', 'SANDBOX_EXPORT',
+ '<(generate_stubs_sig_public_path)',
+ '<(generate_stubs_sig_private_path)',
+ ],
+ 'process_outputs_as_sources': 1,
+ 'message': 'Generating XPC stubs for 10.6 compatability.',
+ },
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'sandbox_mac_unittests',
+ 'type': 'executable',
+ 'sources': [
+ 'bootstrap_sandbox_unittest.mm',
+ 'policy_unittest.cc',
+ 'xpc_message_server_unittest.cc',
+ ],
+ 'dependencies': [
+ 'sandbox',
+ '../base/base.gyp:base',
+ '../base/base.gyp:run_all_unittests',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+ '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+ ],
+ },
+ },
+ ],
+}
diff --git a/sandbox/mac/xpc.cc b/sandbox/mac/xpc.cc
new file mode 100644
index 0000000000..b8d526bdad
--- /dev/null
+++ b/sandbox/mac/xpc.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/xpc.h"
+
+namespace sandbox {
+
+bool InitializeXPC() {
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+ std::vector<std::string> path_list;
+ path_list.push_back("/usr/lib/system/libxpc.dylib");
+
+ sandbox_mac::StubPathMap path_map;
+ path_map[sandbox_mac::kModuleXpc_stubs] = path_list;
+ path_map[sandbox_mac::kModuleXpc_private_stubs] = path_list;
+
+ return sandbox_mac::InitializeStubs(path_map);
+#else
+ return true;
+#endif
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/xpc.h b/sandbox/mac/xpc.h
new file mode 100644
index 0000000000..33d3945e38
--- /dev/null
+++ b/sandbox/mac/xpc.h
@@ -0,0 +1,50 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides forward declarations for XPC symbols that are not
+// present in the 10.6 SDK. It uses generate_stubs to produce code to
+// dynamically load the libxpc.dylib library and set up a stub table, with
+// the same names as the real XPC functions.
+
+#ifndef SANDBOX_MAC_XPC_H_
+#define SANDBOX_MAC_XPC_H_
+
+#include <AvailabilityMacros.h>
+#include <mach/mach.h>
+
+#include "sandbox/sandbox_export.h"
+
+// Declares XPC object types. This includes <xpc/xpc.h> if available.
+#include "sandbox/mac/xpc_stubs_header.fragment"
+
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+// C++ library loader.
+#include "sandbox/mac/xpc_stubs.h"
+
+extern "C" {
+// Signatures for XPC public functions that are loaded by xpc_stubs.h.
+#include "sandbox/mac/xpc_stubs.sig"
+// Signatures for private XPC functions.
+#include "sandbox/mac/xpc_private_stubs.sig"
+} // extern "C"
+
+#else
+
+// Signatures for private XPC functions.
+extern "C" {
+#include "sandbox/mac/xpc_private_stubs.sig"
+} // extern "C"
+
+#endif
+
+namespace sandbox {
+
+// Dynamically loads the XPC library.
+bool SANDBOX_EXPORT InitializeXPC();
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_XPC_H_
diff --git a/sandbox/mac/xpc_message_server.cc b/sandbox/mac/xpc_message_server.cc
new file mode 100644
index 0000000000..1cd5c63f38
--- /dev/null
+++ b/sandbox/mac/xpc_message_server.cc
@@ -0,0 +1,126 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/xpc_message_server.h"
+
+#include <bsm/libbsm.h>
+
+#include <string>
+
+#include "base/mac/mach_logging.h"
+#include "base/strings/stringprintf.h"
+#include "sandbox/mac/xpc.h"
+
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+// Redeclare methods that only exist on 10.7+ to suppress
+// -Wpartial-availability warnings.
+extern "C" {
+XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT XPC_NONNULL_ALL
+ xpc_object_t
+ xpc_dictionary_create_reply(xpc_object_t original);
+} // extern "C"
+#endif
+
+namespace sandbox {
+
+XPCMessageServer::XPCMessageServer(MessageDemuxer* demuxer,
+ mach_port_t server_receive_right)
+ : demuxer_(demuxer),
+ server_port_(server_receive_right),
+ reply_message_(NULL) {
+}
+
+XPCMessageServer::~XPCMessageServer() {
+}
+
+bool XPCMessageServer::Initialize() {
+ // Allocate a port for use as a new server port if one was not passed to the
+ // constructor.
+ if (!server_port_.is_valid()) {
+ mach_port_t port;
+ kern_return_t kr;
+ if ((kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
+ &port)) != KERN_SUCCESS) {
+ MACH_LOG(ERROR, kr) << "Failed to allocate new server port.";
+ return false;
+ }
+ server_port_.reset(port);
+ }
+
+ std::string label = base::StringPrintf(
+ "org.chromium.sandbox.XPCMessageServer.%p", demuxer_);
+ dispatch_source_.reset(new base::DispatchSourceMach(
+ label.c_str(), server_port_.get(), ^{ ReceiveMessage(); }));
+ dispatch_source_->Resume();
+
+ return true;
+}
+
+pid_t XPCMessageServer::GetMessageSenderPID(IPCMessage request) {
+ audit_token_t token;
+ xpc_dictionary_get_audit_token(request.xpc, &token);
+ // TODO(rsesek): In the 10.7 SDK, there's audit_token_to_pid().
+ pid_t sender_pid;
+ audit_token_to_au32(token,
+ NULL, NULL, NULL, NULL, NULL, &sender_pid, NULL, NULL);
+ return sender_pid;
+}
+
+IPCMessage XPCMessageServer::CreateReply(IPCMessage request) {
+ if (!reply_message_)
+ reply_message_ = xpc_dictionary_create_reply(request.xpc);
+
+ IPCMessage reply;
+ reply.xpc = reply_message_;
+ return reply;
+}
+
+bool XPCMessageServer::SendReply(IPCMessage reply) {
+ int rv = xpc_pipe_routine_reply(reply.xpc);
+ if (rv) {
+ LOG(ERROR) << "Failed to xpc_pipe_routine_reply(): " << rv;
+ return false;
+ }
+ return true;
+}
+
+void XPCMessageServer::ForwardMessage(IPCMessage request,
+ mach_port_t destination) {
+ xpc_pipe_t pipe = xpc_pipe_create_from_port(destination, 0);
+ int rv = xpc_pipe_routine_forward(pipe, request.xpc);
+ if (rv) {
+ LOG(ERROR) << "Failed to xpc_pipe_routine_forward(): " << rv;
+ }
+ xpc_release(pipe);
+}
+
+void XPCMessageServer::RejectMessage(IPCMessage request, int error_code) {
+ IPCMessage reply = CreateReply(request);
+ xpc_dictionary_set_int64(reply.xpc, "error", error_code);
+ SendReply(reply);
+}
+
+mach_port_t XPCMessageServer::GetServerPort() const {
+ return server_port_.get();
+}
+
+void XPCMessageServer::ReceiveMessage() {
+ IPCMessage request;
+ int rv = xpc_pipe_receive(server_port_, &request.xpc);
+ if (rv) {
+ LOG(ERROR) << "Failed to xpc_pipe_receive(): " << rv;
+ return;
+ }
+
+ demuxer_->DemuxMessage(request);
+
+ xpc_release(request.xpc);
+ if (reply_message_) {
+ xpc_release(reply_message_);
+ reply_message_ = NULL;
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/xpc_message_server.h b/sandbox/mac/xpc_message_server.h
new file mode 100644
index 0000000000..5f5a9fa24a
--- /dev/null
+++ b/sandbox/mac/xpc_message_server.h
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_XPC_MESSAGE_SERVER_H_
+#define SANDBOX_MAC_XPC_MESSAGE_SERVER_H_
+
+#include <AvailabilityMacros.h>
+
+#include "base/mac/dispatch_source_mach.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/mac/message_server.h"
+#include "sandbox/mac/xpc.h"
+#include "sandbox/sandbox_export.h"
+
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+// Redeclare methods that only exist on 10.7+ to suppress
+// -Wpartial-availability warnings.
+extern "C" {
+XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2 void
+xpc_dictionary_set_int64(xpc_object_t xdict, const char* key, int64_t value);
+
+XPC_EXPORT XPC_NONNULL1 void xpc_release(xpc_object_t object);
+} // extern "C"
+#endif
+
+namespace sandbox {
+
+// An implementation of MessageServer that uses XPC pipes to read and write XPC
+// messages from a Mach port.
+class SANDBOX_EXPORT XPCMessageServer : public MessageServer {
+ public:
+ // Creates a new XPC message server that will send messages to |demuxer|
+ // for handling. If the |server_receive_right| is non-NULL, this class will
+ // take ownership of the port and it will be used to receive messages.
+ // Otherwise the server will create a new receive right on which to listen.
+ XPCMessageServer(MessageDemuxer* demuxer,
+ mach_port_t server_receive_right);
+ ~XPCMessageServer() override;
+
+ // MessageServer:
+ bool Initialize() override;
+ pid_t GetMessageSenderPID(IPCMessage request) override;
+ IPCMessage CreateReply(IPCMessage request) override;
+ bool SendReply(IPCMessage reply) override;
+ void ForwardMessage(IPCMessage request, mach_port_t destination) override;
+ // Creates an error reply message with a field "error" set to |error_code|.
+ void RejectMessage(IPCMessage request, int error_code) override;
+ mach_port_t GetServerPort() const override;
+
+ private:
+ // Reads a message from the XPC pipe.
+ void ReceiveMessage();
+
+ // The demuxer delegate. Weak.
+ MessageDemuxer* demuxer_;
+
+ // The Mach port on which the server is receiving requests.
+ base::mac::ScopedMachReceiveRight server_port_;
+
+ // MACH_RECV dispatch source that handles the |server_port_|.
+ scoped_ptr<base::DispatchSourceMach> dispatch_source_;
+
+ // The reply message, if one has been created.
+ xpc_object_t reply_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(XPCMessageServer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_MAC_XPC_MESSAGE_SERVER_H_
diff --git a/sandbox/mac/xpc_message_server_unittest.cc b/sandbox/mac/xpc_message_server_unittest.cc
new file mode 100644
index 0000000000..4c4fcf9c94
--- /dev/null
+++ b/sandbox/mac/xpc_message_server_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/mac/xpc_message_server.h"
+
+#include <Block.h>
+#include <mach/mach.h>
+#include <servers/bootstrap.h>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/process/kill.h"
+#include "base/test/multiprocess_test.h"
+#include "sandbox/mac/xpc.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/multiprocess_func_list.h"
+
+#if defined(MAC_OS_X_VERSION_10_7) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+// Redeclare methods that only exist on 10.7+ to suppress
+// -Wpartial-availability warnings.
+extern "C" {
+XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL_ALL int64_t
+xpc_dictionary_get_int64(xpc_object_t xdict, const char* key);
+
+XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL_ALL uint64_t
+xpc_dictionary_get_uint64(xpc_object_t xdict, const char* key);
+
+XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2 void
+xpc_dictionary_set_uint64(xpc_object_t xdict, const char* key, uint64_t value);
+
+XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT xpc_object_t
+xpc_dictionary_create(const char* const* keys,
+ const xpc_object_t* values,
+ size_t count);
+} // extern "C"
+#endif
+
+namespace sandbox {
+
+class XPCMessageServerTest : public testing::Test {
+ public:
+ void SetUp() override {
+ if (!RunXPCTest())
+ return;
+ ASSERT_TRUE(InitializeXPC());
+ }
+
+ bool RunXPCTest() {
+ return base::mac::IsOSMountainLionOrLater();
+ }
+};
+
+// A MessageDemuxer that manages a test server and executes a block for every
+// message.
+class BlockDemuxer : public MessageDemuxer {
+ public:
+ BlockDemuxer()
+ : demux_block_(NULL),
+ server_(this, MACH_PORT_NULL),
+ pipe_(NULL) {
+ }
+
+ ~BlockDemuxer() override {
+ if (pipe_)
+ xpc_release(pipe_);
+ if (demux_block_)
+ Block_release(demux_block_);
+ }
+
+ // Starts running the server, given a block to handle incoming IPC messages.
+ bool Initialize(void (^demux_block)(IPCMessage request)) {
+ if (!server_.Initialize())
+ return false;
+
+ // Create a send right on the port so that the XPC pipe can be created.
+ if (mach_port_insert_right(mach_task_self(), server_.GetServerPort(),
+ server_.GetServerPort(), MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
+ return false;
+ }
+ scoped_send_right_.reset(server_.GetServerPort());
+
+ demux_block_ = Block_copy(demux_block);
+ pipe_ = xpc_pipe_create_from_port(server_.GetServerPort(), 0);
+
+ return true;
+ }
+
+ void DemuxMessage(IPCMessage request) override {
+ demux_block_(request);
+ }
+
+ xpc_pipe_t pipe() { return pipe_; }
+
+ XPCMessageServer* server() { return &server_; }
+
+ private:
+ void (^demux_block_)(IPCMessage request);
+
+ XPCMessageServer server_;
+
+ base::mac::ScopedMachSendRight scoped_send_right_;
+
+ xpc_pipe_t pipe_;
+};
+
+#define XPC_TEST_F(name) TEST_F(XPCMessageServerTest, name) { \
+ if (!RunXPCTest()) \
+ return; \
+
+XPC_TEST_F(ReceiveMessage) // {
+ BlockDemuxer fixture;
+ XPCMessageServer* server = fixture.server();
+
+ uint64_t __block value = 0;
+ ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
+ value = xpc_dictionary_get_uint64(request.xpc, "test_value");
+ server->SendReply(server->CreateReply(request));
+ }));
+
+ xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_uint64(request, "test_value", 42);
+
+ xpc_object_t reply;
+ EXPECT_EQ(0, xpc_pipe_routine(fixture.pipe(), request, &reply));
+
+ EXPECT_EQ(42u, value);
+
+ xpc_release(request);
+ xpc_release(reply);
+}
+
+XPC_TEST_F(RejectMessage) // {
+ BlockDemuxer fixture;
+ XPCMessageServer* server = fixture.server();
+ ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
+ server->RejectMessage(request, EPERM);
+ }));
+
+ xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_object_t reply;
+ EXPECT_EQ(0, xpc_pipe_routine(fixture.pipe(), request, &reply));
+
+ EXPECT_EQ(EPERM, xpc_dictionary_get_int64(reply, "error"));
+
+ xpc_release(request);
+ xpc_release(reply);
+}
+
+char kGetSenderPID[] = "org.chromium.sandbox.test.GetSenderPID";
+
+XPC_TEST_F(GetSenderPID) // {
+ BlockDemuxer fixture;
+ XPCMessageServer* server = fixture.server();
+
+ pid_t __block sender_pid = 0;
+ int64_t __block child_pid = 0;
+ ASSERT_TRUE(fixture.Initialize(^(IPCMessage request) {
+ sender_pid = server->GetMessageSenderPID(request);
+ child_pid = xpc_dictionary_get_int64(request.xpc, "child_pid");
+ }));
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ kern_return_t kr = bootstrap_register(bootstrap_port, kGetSenderPID,
+ server->GetServerPort());
+#pragma GCC diagnostic pop
+ ASSERT_EQ(KERN_SUCCESS, kr);
+
+ base::Process child = base::SpawnMultiProcessTestChild(
+ "GetSenderPID",
+ base::GetMultiProcessTestChildBaseCommandLine(),
+ base::LaunchOptions());
+ ASSERT_TRUE(child.IsValid());
+
+ int exit_code = -1;
+ ASSERT_TRUE(child.WaitForExit(&exit_code));
+ EXPECT_EQ(0, exit_code);
+
+ EXPECT_EQ(child.Pid(), sender_pid);
+ EXPECT_EQ(child.Pid(), child_pid);
+ EXPECT_EQ(sender_pid, child_pid);
+}
+
+MULTIPROCESS_TEST_MAIN(GetSenderPID) {
+ CHECK(sandbox::InitializeXPC());
+
+ mach_port_t port = MACH_PORT_NULL;
+ CHECK_EQ(KERN_SUCCESS, bootstrap_look_up(bootstrap_port, kGetSenderPID,
+ &port));
+ base::mac::ScopedMachSendRight scoped_port(port);
+
+ xpc_pipe_t pipe = xpc_pipe_create_from_port(port, 0);
+
+ xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_int64(message, "child_pid", getpid());
+ CHECK_EQ(0, xpc_pipe_simpleroutine(pipe, message));
+
+ xpc_release(message);
+ xpc_release(pipe);
+
+ return 0;
+}
+
+XPC_TEST_F(ForwardMessage) // {
+ BlockDemuxer first;
+ XPCMessageServer* first_server = first.server();
+
+ BlockDemuxer second;
+ XPCMessageServer* second_server = second.server();
+
+ ASSERT_TRUE(first.Initialize(^(IPCMessage request) {
+ xpc_dictionary_set_int64(request.xpc, "seen_by_first", 1);
+ first_server->ForwardMessage(request, second_server->GetServerPort());
+ }));
+ ASSERT_TRUE(second.Initialize(^(IPCMessage request) {
+ IPCMessage reply = second_server->CreateReply(request);
+ xpc_dictionary_set_int64(reply.xpc, "seen_by_first",
+ xpc_dictionary_get_int64(request.xpc, "seen_by_first"));
+ xpc_dictionary_set_int64(reply.xpc, "seen_by_second", 2);
+ second_server->SendReply(reply);
+ }));
+
+ xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_object_t reply;
+ ASSERT_EQ(0, xpc_pipe_routine(first.pipe(), request, &reply));
+
+ EXPECT_EQ(1, xpc_dictionary_get_int64(reply, "seen_by_first"));
+ EXPECT_EQ(2, xpc_dictionary_get_int64(reply, "seen_by_second"));
+
+ xpc_release(request);
+ xpc_release(reply);
+}
+
+} // namespace sandbox
diff --git a/sandbox/mac/xpc_private_stubs.sig b/sandbox/mac/xpc_private_stubs.sig
new file mode 100644
index 0000000000..7ab2934c52
--- /dev/null
+++ b/sandbox/mac/xpc_private_stubs.sig
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains declarations of private XPC functions. This file is
+// used for both forward declarations of private symbols and to use with
+// tools/generate_stubs for creating a dynamic library loader.
+
+// Dictionary manipulation.
+void xpc_dictionary_set_mach_send(xpc_object_t dictionary, const char* name, mach_port_t port);
+void xpc_dictionary_get_audit_token(xpc_object_t dictionary, audit_token_t* token);
+
+// Pipe methods.
+xpc_pipe_t xpc_pipe_create_from_port(mach_port_t port, int flags);
+int xpc_pipe_receive(mach_port_t port, xpc_object_t* message);
+int xpc_pipe_routine(xpc_pipe_t pipe, xpc_object_t request, xpc_object_t* reply);
+int xpc_pipe_routine_reply(xpc_object_t reply);
+int xpc_pipe_simpleroutine(xpc_pipe_t pipe, xpc_object_t message);
+int xpc_pipe_routine_forward(xpc_pipe_t forward_to, xpc_object_t request);
diff --git a/sandbox/mac/xpc_stubs.sig b/sandbox/mac/xpc_stubs.sig
new file mode 100644
index 0000000000..d20af58a6e
--- /dev/null
+++ b/sandbox/mac/xpc_stubs.sig
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains declarations of public XPC functions used in the sandbox.
+// This file is used with tools/generate_stubs for creating a dynamic library
+// loader.
+
+// XPC object management.
+void xpc_release(xpc_object_t object);
+
+// Dictionary manipulation.
+xpc_object_t xpc_dictionary_create(const char* const *keys, const xpc_object_t* values, size_t count);
+const char* xpc_dictionary_get_string(xpc_object_t dictionary, const char* key);
+uint64_t xpc_dictionary_get_uint64(xpc_object_t dictionary, const char* key);
+void xpc_dictionary_set_uint64(xpc_object_t dictionary, const char* key, uint64_t value);
+int64_t xpc_dictionary_get_int64(xpc_object_t dictionary, const char* key);
+void xpc_dictionary_set_int64(xpc_object_t dictionary, const char* key, int64_t value);
+xpc_object_t xpc_dictionary_create_reply(xpc_object_t request);
diff --git a/sandbox/mac/xpc_stubs_header.fragment b/sandbox/mac/xpc_stubs_header.fragment
new file mode 100644
index 0000000000..8197587fc7
--- /dev/null
+++ b/sandbox/mac/xpc_stubs_header.fragment
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
+#define SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
+
+#include <bsm/libbsm.h>
+
+#include "sandbox/sandbox_export.h"
+
+// Declare or include public types.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+extern "C" {
+typedef void* xpc_object_t;
+} // extern "C"
+
+#else
+
+#include <xpc/xpc.h>
+
+#endif
+
+// Declare private types.
+extern "C" {
+typedef struct _xpc_pipe_s* xpc_pipe_t;
+} // extern "C"
+
+#endif // SANDBOX_MAC_XPC_STUBS_HEADER_FRAGMENT_
diff --git a/sandbox/sandbox.gyp b/sandbox/sandbox.gyp
new file mode 100644
index 0000000000..f93fa1862a
--- /dev/null
+++ b/sandbox/sandbox.gyp
@@ -0,0 +1,35 @@
+# 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=="mac" and OS!="ios"', {
+ 'includes': [
+ 'mac/sandbox_mac.gypi',
+ ],
+ }],
+ [ 'OS!="win" and OS!="mac" and OS!="linux" and OS!="android"', {
+ # A 'default' to accomodate the "sandbox" target.
+ 'targets': [
+ {
+ 'target_name': 'sandbox',
+ 'type': 'none',
+ }
+ ]
+ }],
+ ],
+}
diff --git a/sandbox/sandbox_export.h b/sandbox/sandbox_export.h
new file mode 100644
index 0000000000..40a4036640
--- /dev/null
+++ b/sandbox/sandbox_export.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SANDBOX_EXPORT_H_
+#define SANDBOX_SANDBOX_EXPORT_H_
+
+#if defined(WIN32)
+#error "sandbox_export.h does not support WIN32."
+#endif
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(SANDBOX_IMPLEMENTATION)
+#define SANDBOX_EXPORT __attribute__((visibility("default")))
+#define SANDBOX_EXPORT_PRIVATE __attribute__((visibility("default")))
+#else
+#define SANDBOX_EXPORT
+#define SANDBOX_EXPORT_PRIVATE
+#endif // defined(SANDBOX_IMPLEMENTATION)
+
+#else // defined(COMPONENT_BUILD)
+
+#define SANDBOX_EXPORT
+#define SANDBOX_EXPORT_PRIVATE
+
+#endif // defined(COMPONENT_BUILD)
+
+#endif // SANDBOX_SANDBOX_EXPORT_H_
diff --git a/sandbox/sandbox_linux_unittests.isolate b/sandbox/sandbox_linux_unittests.isolate
new file mode 100644
index 0000000000..2dadddd098
--- /dev/null
+++ b/sandbox/sandbox_linux_unittests.isolate
@@ -0,0 +1,27 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Because of a limitation in isolate_driver.py, this file needs to be in
+# the same directory as the main .gyp file.
+
+{
+ 'conditions': [
+ ['OS=="android" or OS=="linux"', {
+ 'variables': {
+ 'command': [
+ '<(PRODUCT_DIR)/sandbox_linux_unittests',
+ ],
+ 'files': [
+ '<(PRODUCT_DIR)/sandbox_linux_unittests',
+ ],
+ 'read_only': 1,
+ },
+ }],
+ ],
+ 'includes': [
+ # This is needed because of base/ dependencies on
+ # icudtl.dat.
+ '../base/base.isolate',
+ ],
+}
diff --git a/sandbox/win/BUILD.gn b/sandbox/win/BUILD.gn
new file mode 100644
index 0000000000..3063f7f17d
--- /dev/null
+++ b/sandbox/win/BUILD.gn
@@ -0,0 +1,302 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//testing/test.gni")
+
+source_set("sandbox") {
+ sources = [
+ "src/acl.cc",
+ "src/acl.h",
+ "src/app_container.cc",
+ "src/app_container.h",
+ "src/broker_services.cc",
+ "src/broker_services.h",
+ "src/crosscall_client.h",
+ "src/crosscall_params.h",
+ "src/crosscall_server.cc",
+ "src/crosscall_server.h",
+ "src/eat_resolver.cc",
+ "src/eat_resolver.h",
+ "src/filesystem_dispatcher.cc",
+ "src/filesystem_dispatcher.h",
+ "src/filesystem_interception.cc",
+ "src/filesystem_interception.h",
+ "src/filesystem_policy.cc",
+ "src/filesystem_policy.h",
+ "src/handle_closer.cc",
+ "src/handle_closer.h",
+ "src/handle_closer_agent.cc",
+ "src/handle_closer_agent.h",
+ "src/handle_dispatcher.cc",
+ "src/handle_dispatcher.h",
+ "src/handle_interception.cc",
+ "src/handle_interception.h",
+ "src/handle_policy.cc",
+ "src/handle_policy.h",
+ "src/handle_table.cc",
+ "src/handle_table.h",
+ "src/interception.cc",
+ "src/interception.h",
+ "src/interception_agent.cc",
+ "src/interception_agent.h",
+ "src/interception_internal.h",
+ "src/interceptors.h",
+ "src/internal_types.h",
+ "src/ipc_tags.h",
+ "src/job.cc",
+ "src/job.h",
+ "src/named_pipe_dispatcher.cc",
+ "src/named_pipe_dispatcher.h",
+ "src/named_pipe_interception.cc",
+ "src/named_pipe_interception.h",
+ "src/named_pipe_policy.cc",
+ "src/named_pipe_policy.h",
+ "src/nt_internals.h",
+ "src/policy_broker.cc",
+ "src/policy_broker.h",
+ "src/policy_engine_opcodes.cc",
+ "src/policy_engine_opcodes.h",
+ "src/policy_engine_params.h",
+ "src/policy_engine_processor.cc",
+ "src/policy_engine_processor.h",
+ "src/policy_low_level.cc",
+ "src/policy_low_level.h",
+ "src/policy_params.h",
+ "src/policy_target.cc",
+ "src/policy_target.h",
+ "src/process_mitigations.cc",
+ "src/process_mitigations.h",
+ "src/process_mitigations_win32k_dispatcher.cc",
+ "src/process_mitigations_win32k_dispatcher.h",
+ "src/process_mitigations_win32k_interception.cc",
+ "src/process_mitigations_win32k_interception.h",
+ "src/process_mitigations_win32k_policy.cc",
+ "src/process_mitigations_win32k_policy.h",
+ "src/process_thread_dispatcher.cc",
+ "src/process_thread_dispatcher.h",
+ "src/process_thread_interception.cc",
+ "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.cc",
+ "src/restricted_token.h",
+ "src/restricted_token_utils.cc",
+ "src/restricted_token_utils.h",
+ "src/sandbox.cc",
+ "src/sandbox.h",
+ "src/sandbox_factory.h",
+ "src/sandbox_globals.cc",
+ "src/sandbox_nt_types.h",
+ "src/sandbox_nt_util.cc",
+ "src/sandbox_nt_util.h",
+ "src/sandbox_policy.h",
+ "src/sandbox_policy_base.cc",
+ "src/sandbox_policy_base.h",
+ "src/sandbox_types.h",
+ "src/sandbox_utils.cc",
+ "src/sandbox_utils.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/win2k_threadpool.cc",
+ "src/win2k_threadpool.h",
+ "src/win_utils.cc",
+ "src/win_utils.h",
+ "src/window.cc",
+ "src/window.h",
+ ]
+
+ if (current_cpu == "x64") {
+ sources += [
+ "src/Wow64_64.cc",
+ "src/interceptors_64.cc",
+ "src/interceptors_64.h",
+ "src/resolver_64.cc",
+ "src/service_resolver_64.cc",
+ ]
+ } else if (current_cpu == "x86") {
+ sources += [
+ "src/Wow64.cc",
+ "src/Wow64.h",
+ "src/resolver_32.cc",
+ "src/service_resolver_32.cc",
+ "src/sidestep/ia32_modrm_map.cpp",
+ "src/sidestep/ia32_opcode_map.cpp",
+ "src/sidestep/mini_disassembler.cpp",
+ "src/sidestep/mini_disassembler.h",
+ "src/sidestep/mini_disassembler_types.h",
+ "src/sidestep/preamble_patcher.h",
+ "src/sidestep/preamble_patcher_with_stub.cpp",
+ "src/sidestep_resolver.cc",
+ "src/sidestep_resolver.h",
+ ]
+ }
+
+ deps = [
+ "//base",
+ "//base:base_static",
+ ]
+ if (current_cpu == "x86") {
+ deps += [ ":copy_wow_helper" ]
+ }
+}
+
+if (current_cpu == "x86") {
+ # Make a target that copies the wow_helper files to the out dir.
+ #
+ # TODO(brettw) we can probably just build this now that we have proper
+ # toolchain support.
+ copy("copy_wow_helper") {
+ sources = [
+ "wow_helper/wow_helper.exe",
+ "wow_helper/wow_helper.pdb",
+ ]
+ outputs = [
+ "$root_out_dir/{{source_file_part}}",
+ ]
+ }
+}
+
+test("sbox_integration_tests") {
+ sources = [
+ "src/address_sanitizer_test.cc",
+ "src/app_container_test.cc",
+ "src/file_policy_test.cc",
+ "src/handle_closer_test.cc",
+ "src/handle_inheritance_test.cc",
+ "src/handle_policy_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",
+ "tests/integration_tests/integration_tests_test.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+}
+
+test("sbox_validation_tests") {
+ sources = [
+ "tests/common/controller.cc",
+ "tests/common/controller.h",
+ "tests/validation_tests/commands.cc",
+ "tests/validation_tests/commands.h",
+ "tests/validation_tests/suite.cc",
+ "tests/validation_tests/unit_tests.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+}
+
+test("sbox_unittests") {
+ sources = [
+ "src/app_container_unittest.cc",
+ "src/interception_unittest.cc",
+ "src/ipc_unittest.cc",
+ "src/job_unittest.cc",
+ "src/policy_engine_unittest.cc",
+ "src/policy_low_level_unittest.cc",
+ "src/policy_opcodes_unittest.cc",
+ "src/restricted_token_unittest.cc",
+ "src/service_resolver_unittest.cc",
+ "src/sid_unittest.cc",
+ "src/threadpool_unittest.cc",
+ "src/win_utils_unittest.cc",
+ "tests/common/test_utils.cc",
+ "tests/common/test_utils.h",
+ "tests/unit_tests/unit_tests.cc",
+ ]
+
+ deps = [
+ ":sandbox",
+ "//base/test:test_support",
+ "//testing/gtest",
+ ]
+}
+
+test("sandbox_poc") {
+ sources = [
+ "sandbox_poc/main_ui_window.cc",
+ "sandbox_poc/main_ui_window.h",
+ "sandbox_poc/resource.h",
+ "sandbox_poc/sandbox.cc",
+ "sandbox_poc/sandbox.h",
+ "sandbox_poc/sandbox.ico",
+ "sandbox_poc/sandbox.rc",
+ ]
+
+ configs -= [ "//build/config/win:console" ]
+ configs += [ "//build/config/win:windowed" ]
+
+ libs = [ "comctl32.lib" ]
+
+ deps = [
+ ":sandbox",
+ ":pocdll",
+ ]
+}
+
+shared_library("pocdll") {
+ sources = [
+ "sandbox_poc/pocdll/exports.h",
+ "sandbox_poc/pocdll/fs.cc",
+ "sandbox_poc/pocdll/handles.cc",
+ "sandbox_poc/pocdll/invasive.cc",
+ "sandbox_poc/pocdll/network.cc",
+ "sandbox_poc/pocdll/pocdll.cc",
+ "sandbox_poc/pocdll/processes_and_threads.cc",
+ "sandbox_poc/pocdll/registry.cc",
+ "sandbox_poc/pocdll/spyware.cc",
+ "sandbox_poc/pocdll/utils.h",
+ ]
+
+ defines = [ "POCDLL_EXPORTS" ]
+}
diff --git a/sandbox/win/OWNERS b/sandbox/win/OWNERS
new file mode 100644
index 0000000000..fd5dcfda2c
--- /dev/null
+++ b/sandbox/win/OWNERS
@@ -0,0 +1,4 @@
+cpu@chromium.org
+jschuh@chromium.org
+rvargas@chromium.org
+wfh@chromium.org
diff --git a/sandbox/win/sandbox_poc/main_ui_window.cc b/sandbox/win/sandbox_poc/main_ui_window.cc
new file mode 100644
index 0000000000..6c5d17727a
--- /dev/null
+++ b/sandbox/win/sandbox_poc/main_ui_window.cc
@@ -0,0 +1,670 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <CommCtrl.h>
+#include <commdlg.h>
+#include <stdarg.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)) {
+ base::string16 dll_path = base::string16(current_dir) +
+ base::string16(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
+ base::string16 dll_path = host->OnShowBrowseForDllDlg(dialog);
+ if (dll_path.length() > 0) {
+ // Initialize the window text for Log File edit box
+ HWND edit_box_dll_path = ::GetDlgItem(dialog, IDC_DLL_NAME);
+ ::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
+ base::string16 log_path = host->OnShowBrowseForLogFileDlg(dialog);
+ if (log_path.length() > 0) {
+ // Initialize the window text for Log File edit box
+ HWND edit_box_log_file = ::GetDlgItem(dialog, IDC_LOG_FILE);
+ ::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_ = base::string16(L"\"") + log_file + base::string16(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::AddKnownSidToObject(pipe_handle_, SE_KERNEL_OBJECT,
+ WinWorldSid, GRANT_ACCESS,
+ 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;
+}
+
+base::string16 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"";
+}
+
+base::string16 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;
+ 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/sandbox/win/sandbox_poc/main_ui_window.h b/sandbox/win/sandbox_poc/main_ui_window.h
new file mode 100644
index 0000000000..84fb9861c9
--- /dev/null
+++ b/sandbox/win/sandbox_poc/main_ui_window.h
@@ -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.
+
+#ifndef SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__
+#define SANDBOX_SANDBOX_POC_MAIN_UI_WINDOW_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.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).
+ base::string16 OnShowBrowseForDllDlg(HWND owner);
+
+ // Shows a standard Save As dialog and returns the log filename selected or
+ // blank string if the user cancelled (or an error occurred).
+ base::string16 OnShowBrowseForLogFileDlg(HWND owner);
+
+ // Formats a message using the supplied format string and prints it in the
+ // listview in the main UI window. Passing a NULL param in 'fmt' results in
+ // 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.
+ base::string16 spawn_target_;
+
+ // A handle to the current instance of the app. Passed in to this class
+ // through CreateMainWindowAndLoop.
+ HINSTANCE instance_handle_;
+
+ // A path to the DLL that the target should load once it executes.
+ base::string16 dll_path_;
+
+ // The name of the entry point the target should call after it loads the DLL.
+ base::string16 entry_point_;
+
+ // The name of the log file to use.
+ base::string16 log_file_;
+
+ // This is a static handle to the list view that fills up the entire main
+ // UI window. The list view is used to display debugging information to the
+ // 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/sandbox/win/sandbox_poc/pocdll/exports.h b/sandbox/win/sandbox_poc/pocdll/exports.h
new file mode 100644
index 0000000000..66a07d6b78
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/pocdll/fs.cc b/sandbox/win/sandbox_poc/pocdll/fs.cc
new file mode 100644
index 0000000000..40596af6dd
--- /dev/null
+++ b/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(const 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 %ld.\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 %ld.\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/sandbox/win/sandbox_poc/pocdll/handles.cc b/sandbox/win/sandbox_poc/pocdll/handles.cc
new file mode 100644
index 0000000000..a12d433411
--- /dev/null
+++ b/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 %ld\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 %ld\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%lX\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%lX\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.8lX "
+ "Type: %-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.8lX "
+ "Type: %-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/sandbox/win/sandbox_poc/pocdll/invasive.cc b/sandbox/win/sandbox_poc/pocdll/invasive.cc
new file mode 100644
index 0000000000..9ee13b0cb9
--- /dev/null
+++ b/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%lX\r\n", tid);
+ ::CloseHandle(thread);
+ number_errors = 0;
+ } else {
+ fprintf(output, "[BLOCKED] Creating thread. Error %ld\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(%ld) priority to Realtime\r\n",
+ tid);
+ } else {
+ fprintf(output, "[BLOCKED] Set thread(%ld) 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 %ld\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/sandbox/win/sandbox_poc/pocdll/network.cc b/sandbox/win/sandbox_poc/pocdll/network.cc
new file mode 100644
index 0000000000..56ab5aefb0
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/pocdll/pocdll.cc b/sandbox/win/sandbox_poc/pocdll/pocdll.cc
new file mode 100644
index 0000000000..e058f58b82
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj b/sandbox/win/sandbox_poc/pocdll/pocdll.vcproj
new file mode 100644
index 0000000000..8e4e31fc6a
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc b/sandbox/win/sandbox_poc/pocdll/processes_and_threads.cc
new file mode 100644
index 0000000000..03e12ba522
--- /dev/null
+++ b/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 %ld\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:%ld but cannot open it. "
+ "Error %ld\r\n",
+ process_entry.szExeFile,
+ process_entry.th32ProcessID,
+ ::GetLastError());
+ } else {
+ fprintf(output, "[GRANTED] Found process %S:%ld 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 %ld 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 %ld\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 %ld:%ld 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 %ld 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/sandbox/win/sandbox_poc/pocdll/registry.cc b/sandbox/win/sandbox_poc/pocdll/registry.cc
new file mode 100644
index 0000000000..5784db65d0
--- /dev/null
+++ b/sandbox/win/sandbox_poc/pocdll/registry.cc
@@ -0,0 +1,49 @@
+// 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.
+
+// 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* hive_name,
+ 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",
+ hive_name,
+ path,
+ key);
+ ::RegCloseKey(key);
+ } else {
+ fprintf(output,
+ "[BLOCKED] Opening key \"%S\\%S\". Error %ld\r\n",
+ hive_name,
+ path,
+ err_code);
+ }
+}
+
+void POCDLL_API TestRegistry(HANDLE log) {
+ HandleToFile handle2file;
+ FILE *output = handle2file.Translate(log, "w");
+
+ TryOpenKey(HKEY_LOCAL_MACHINE, L"HKEY_LOCAL_MACHINE", NULL, output);
+ TryOpenKey(HKEY_CURRENT_USER, L"HKEY_CURRENT_USER", NULL, output);
+ TryOpenKey(HKEY_USERS, L"HKEY_USERS", NULL, output);
+ TryOpenKey(HKEY_LOCAL_MACHINE,
+ L"HKEY_LOCAL_MACHINE",
+ L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon",
+ output);
+}
diff --git a/sandbox/win/sandbox_poc/pocdll/spyware.cc b/sandbox/win/sandbox_poc/pocdll/spyware.cc
new file mode 100644
index 0000000000..cf0bd4177f
--- /dev/null
+++ b/sandbox/win/sandbox_poc/pocdll/spyware.cc
@@ -0,0 +1,68 @@
+// 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 "base/strings/string16.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 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 = %ld\r\n",
+ ::GetLastError());
+ }
+
+ fprintf(output, "[INFO] Logging keystrokes for 15 seconds\r\n");
+ fflush(output);
+ base::string16 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 = %ld\r\n",
+ ::GetLastError());
+}
diff --git a/sandbox/win/sandbox_poc/pocdll/utils.h b/sandbox/win/sandbox_poc/pocdll/utils.h
new file mode 100644
index 0000000000..ae4286147a
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/resource.h b/sandbox/win/sandbox_poc/resource.h
new file mode 100644
index 0000000000..87ff920ca4
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/sandbox.cc b/sandbox/win/sandbox_poc/sandbox.cc
new file mode 100644
index 0000000000..e282075321
--- /dev/null
+++ b/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,
+ base::string16 * 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;
+ }
+
+ base::string16 entry_point_wide = arg_list[1];
+ base::string16 dll_name_wide = arg_list[2];
+ *entry_point = std::string(entry_point_wide.begin(), entry_point_wide.end());
+ *dll_name = std::string(dll_name_wide.begin(), dll_name_wide.end());
+ *log_file = arg_list[3];
+
+ // 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;
+ base::string16 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/sandbox/win/sandbox_poc/sandbox.h b/sandbox/win/sandbox_poc/sandbox.h
new file mode 100644
index 0000000000..65c09a119f
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/sandbox.ico b/sandbox/win/sandbox_poc/sandbox.ico
new file mode 100644
index 0000000000..916fa12fc2
--- /dev/null
+++ b/sandbox/win/sandbox_poc/sandbox.ico
Binary files differ
diff --git a/sandbox/win/sandbox_poc/sandbox.rc b/sandbox/win/sandbox_poc/sandbox.rc
new file mode 100644
index 0000000000..978c96f6f2
--- /dev/null
+++ b/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/sandbox/win/sandbox_poc/sandbox_poc.vcproj b/sandbox/win/sandbox_poc/sandbox_poc.vcproj
new file mode 100644
index 0000000000..5fde1cd407
--- /dev/null
+++ b/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/sandbox/win/sandbox_standalone.sln b/sandbox/win/sandbox_standalone.sln
new file mode 100644
index 0000000000..529d20e09e
--- /dev/null
+++ b/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/sandbox/win/sandbox_win.gypi b/sandbox/win/sandbox_win.gypi
new file mode 100644
index 0000000000..e085cfb5ef
--- /dev/null
+++ b/sandbox/win/sandbox_win.gypi
@@ -0,0 +1,373 @@
+# 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_mitigations_win32k_dispatcher.cc',
+ 'src/process_mitigations_win32k_dispatcher.h',
+ 'src/process_mitigations_win32k_interception.cc',
+ 'src/process_mitigations_win32k_interception.h',
+ 'src/process_mitigations_win32k_policy.cc',
+ 'src/process_mitigations_win32k_policy.h',
+ 'src/process_thread_dispatcher.cc',
+ 'src/process_thread_dispatcher.h',
+ 'src/process_thread_interception.cc',
+ 'src/process_thread_interception.h',
+ 'src/process_thread_policy.cc',
+ 'src/process_thread_policy.h',
+ 'src/registry_dispatcher.cc',
+ 'src/registry_dispatcher.h',
+ 'src/registry_interception.cc',
+ 'src/registry_interception.h',
+ 'src/registry_policy.cc',
+ 'src/registry_policy.h',
+ 'src/resolver.cc',
+ 'src/resolver.h',
+ 'src/restricted_token_utils.cc',
+ 'src/restricted_token_utils.h',
+ 'src/restricted_token.cc',
+ 'src/restricted_token.h',
+ 'src/sandbox_factory.h',
+ 'src/sandbox_globals.cc',
+ 'src/sandbox_nt_types.h',
+ 'src/sandbox_nt_util.cc',
+ 'src/sandbox_nt_util.h',
+ 'src/sandbox_policy_base.cc',
+ 'src/sandbox_policy_base.h',
+ 'src/sandbox_policy.h',
+ 'src/sandbox_types.h',
+ 'src/sandbox_utils.cc',
+ 'src/sandbox_utils.h',
+ 'src/sandbox.cc',
+ 'src/sandbox.h',
+ 'src/security_level.h',
+ 'src/service_resolver.cc',
+ 'src/service_resolver.h',
+ 'src/shared_handles.cc',
+ 'src/shared_handles.h',
+ 'src/sharedmem_ipc_client.cc',
+ 'src/sharedmem_ipc_client.h',
+ 'src/sharedmem_ipc_server.cc',
+ 'src/sharedmem_ipc_server.h',
+ 'src/sid.cc',
+ 'src/sid.h',
+ 'src/sync_dispatcher.cc',
+ 'src/sync_dispatcher.h',
+ 'src/sync_interception.cc',
+ 'src/sync_interception.h',
+ 'src/sync_policy.cc',
+ 'src/sync_policy.h',
+ 'src/target_interceptions.cc',
+ 'src/target_interceptions.h',
+ 'src/target_process.cc',
+ 'src/target_process.h',
+ 'src/target_services.cc',
+ 'src/target_services.h',
+ 'src/win_utils.cc',
+ 'src/win_utils.h',
+ 'src/win2k_threadpool.cc',
+ 'src/win2k_threadpool.h',
+ 'src/window.cc',
+ 'src/window.h',
+ ],
+ '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': [
+ '../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',
+ '../base/base.gyp:test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'src/address_sanitizer_test.cc',
+ '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',
+ '../base/base.gyp:test_support_base',
+ '../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',
+ '../base/base.gyp:test_support_base',
+ '../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': [
+ '../base/base.gyp:base_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/sandbox/win/src/Wow64.cc b/sandbox/win/src/Wow64.cc
new file mode 100644
index 0000000000..60ee13d258
--- /dev/null
+++ b/sandbox/win/src/Wow64.cc
@@ -0,0 +1,221 @@
+// 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) {
+ static_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);
+ base::string16 path(prog_name);
+ size_t name_pos = path.find_last_of(L"\\");
+ if (base::string16::npos == name_pos)
+ return false;
+ path.resize(name_pos + 1);
+
+ std::basic_stringstream<base::char16> command;
+ command << std::hex << std::showbase << L"\"" << path <<
+ L"wow_helper.exe\" " << child_->ProcessId() << " " <<
+ bit_cast<ULONG>(buffer);
+
+ scoped_ptr<wchar_t, base::FreeDeleter>
+ writable_command(_wcsdup(command.str().c_str()));
+
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcess(NULL, writable_command.get(), NULL, NULL, FALSE, 0, NULL,
+ NULL, &startup_info, &temp_process_info))
+ return false;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
+ 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/sandbox/win/src/Wow64.h b/sandbox/win/src/Wow64.h
new file mode 100644
index 0000000000..e9bbd53d9d
--- /dev/null
+++ b/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/sandbox/win/src/Wow64_64.cc b/sandbox/win/src/Wow64_64.cc
new file mode 100644
index 0000000000..f03831bd11
--- /dev/null
+++ b/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/sandbox/win/src/acl.cc b/sandbox/win/src/acl.cc
new file mode 100644
index 0000000000..f140c7e6c4
--- /dev/null
+++ b/sandbox/win/src/acl.cc
@@ -0,0 +1,125 @@
+// 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<TOKEN_DEFAULT_DACL, base::FreeDeleter>* 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_MODE access_mode,
+ ACCESS_MASK access, ACL** new_dacl) {
+ EXPLICIT_ACCESS new_access = {0};
+ new_access.grfAccessMode = access_mode;
+ 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<TOKEN_DEFAULT_DACL, base::FreeDeleter> default_dacl;
+ if (!GetDefaultDacl(token, &default_dacl))
+ return false;
+
+ ACL* new_dacl = NULL;
+ if (!AddSidToDacl(sid, default_dacl->DefaultDacl, GRANT_ACCESS, 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<TOKEN_USER, base::FreeDeleter> 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 AddKnownSidToObject(HANDLE object, SE_OBJECT_TYPE object_type,
+ const Sid& sid, ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ PSECURITY_DESCRIPTOR descriptor = NULL;
+ PACL old_dacl = NULL;
+ PACL new_dacl = NULL;
+
+ if (ERROR_SUCCESS != ::GetSecurityInfo(object, object_type,
+ DACL_SECURITY_INFORMATION, NULL, NULL,
+ &old_dacl, NULL, &descriptor))
+ return false;
+
+ if (!AddSidToDacl(sid.GetPSID(), old_dacl, access_mode, access, &new_dacl)) {
+ ::LocalFree(descriptor);
+ return false;
+ }
+
+ DWORD result = ::SetSecurityInfo(object, object_type,
+ 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/sandbox/win/src/acl.h b/sandbox/win/src/acl.h
new file mode 100644
index 0000000000..b5021e7be8
--- /dev/null
+++ b/sandbox/win/src/acl.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_ACL_H_
+#define SANDBOX_SRC_ACL_H_
+
+#include <AccCtrl.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<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_dacl);
+
+// Appends an ACE represented by |sid|, |access_mode|, and |access| to
+// |old_dacl|. If the function succeeds, new_dacl contains the new dacl and
+// must be freed using LocalFree.
+bool AddSidToDacl(const Sid& sid, ACL* old_dacl, ACCESS_MODE access_mode,
+ ACCESS_MASK access, ACL** new_dacl);
+
+// Adds and ACE represented by |sid| and |access| to the default dacl present
+// in the token.
+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|, |access_mode|, and |access| to
+// the dacl of the kernel object referenced by |object| and of |object_type|.
+bool AddKnownSidToObject(HANDLE object, SE_OBJECT_TYPE object_type,
+ const Sid& sid, ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_ACL_H_
diff --git a/sandbox/win/src/address_sanitizer_test.cc b/sandbox/win/src/address_sanitizer_test.cc
new file mode 100644
index 0000000000..b88aa81cd4
--- /dev/null
+++ b/sandbox/win/src/address_sanitizer_test.cc
@@ -0,0 +1,107 @@
+// Copyright 2015 The Chromium 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/environment.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+class AddressSanitizerTests : public ::testing::Test {
+ public:
+ void SetUp() override {
+ env_.reset(base::Environment::Create());
+ had_asan_options_ = env_->GetVar("ASAN_OPTIONS", &old_asan_options_);
+ }
+
+ void TearDown() override {
+ if (had_asan_options_)
+ ASSERT_TRUE(env_->SetVar("ASAN_OPTIONS", old_asan_options_));
+ else
+ env_->UnSetVar("ASAN_OPTIONS");
+ }
+
+ protected:
+ scoped_ptr<base::Environment> env_;
+ bool had_asan_options_;
+ std::string old_asan_options_;
+};
+
+SBOX_TESTS_COMMAND int AddressSanitizerTests_Report(int argc, wchar_t** argv) {
+ // AddressSanitizer should detect an out of bounds write (heap buffer
+ // overflow) in this code.
+ volatile int idx = 42;
+ int *blah = new int[42];
+ blah[idx] = 42;
+ delete [] blah;
+ return SBOX_TEST_FAILED;
+}
+
+TEST_F(AddressSanitizerTests, TestAddressSanitizer) {
+ // This test is only supposed to work when using AddressSanitizer.
+ // However, ASan/Win is not on the CQ yet, so compiler breakages may get into
+ // the code unnoticed. To avoid that, we compile this test in all Windows
+ // builds, but only run the AddressSanitizer-specific part of the test when
+ // compiled with AddressSanitizer.
+#if defined(ADDRESS_SANITIZER)
+ bool asan_build = true;
+#else
+ bool asan_build = false;
+#endif
+ base::ScopedTempDir temp_directory;
+ base::FilePath temp_file_name;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ ASSERT_TRUE(CreateTemporaryFileInDir(temp_directory.path(), &temp_file_name));
+
+ SECURITY_ATTRIBUTES attrs = {};
+ attrs.nLength = sizeof(attrs);
+ attrs.bInheritHandle = TRUE;
+ base::win::ScopedHandle tmp_handle(
+ CreateFile(temp_file_name.value().c_str(), GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
+ &attrs, OPEN_EXISTING, 0, NULL));
+ EXPECT_TRUE(tmp_handle.IsValid());
+
+ TestRunner runner;
+ ASSERT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStderrHandle(tmp_handle.Get()));
+
+ base::FilePath exe;
+ ASSERT_TRUE(PathService::Get(base::FILE_EXE, &exe));
+ base::FilePath pdb_path = exe.DirName().Append(L"*.pdb");
+ ASSERT_TRUE(runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ pdb_path.value().c_str()));
+
+ env_->SetVar("ASAN_OPTIONS", "exitcode=123");
+ if (asan_build) {
+ int result = runner.RunTest(L"AddressSanitizerTests_Report");
+ EXPECT_EQ(123, result);
+
+ std::string data;
+ ASSERT_TRUE(base::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) {
+ ASSERT_TRUE(
+ strstr(data.c_str(), "ERROR: AddressSanitizer: heap-buffer-overflow"))
+ << "There doesn't seem to be an ASan report:\n" << data;
+ ASSERT_TRUE(strstr(data.c_str(), "AddressSanitizerTests_Report"))
+ << "The ASan report doesn't appear to be symbolized:\n" << data;
+ ASSERT_TRUE(strstr(data.c_str(), strrchr(__FILE__, '\\')))
+ << "The stack trace doesn't have a correct filename:\n" << data;
+ } else {
+ LOG(WARNING) << "Pre-Vista versions are not supported.";
+ }
+ } else {
+ LOG(WARNING) << "Not an AddressSanitizer build, skipping the run.";
+ }
+}
+
+}
diff --git a/sandbox/win/src/app_container.cc b/sandbox/win/src/app_container.cc
new file mode 100644
index 0000000000..f8d7541915
--- /dev/null
+++ b/sandbox/win/src/app_container.cc
@@ -0,0 +1,183 @@
+// 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 base::string16& sid) {
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return NULL;
+ return local_sid;
+}
+
+template <typename T>
+T BindFunction(const char* name) {
+ HMODULE module = GetModuleHandle(sandbox::kKerneldllName);
+ void* function = GetProcAddress(module, name);
+ if (!function) {
+ module = GetModuleHandle(sandbox::kKernelBasedllName);
+ function = GetProcAddress(module, name);
+ }
+ return reinterpret_cast<T>(function);
+}
+
+} // 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 base::string16& app_container_sid,
+ const std::vector<base::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 base::string16& sid,
+ const base::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) {
+ AppContainerRegisterSid =
+ BindFunction<AppContainerRegisterSidPtr>("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 base::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) {
+ AppContainerUnregisterSid =
+ BindFunction<AppContainerUnregisterSidPtr>("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;
+}
+
+base::string16 LookupAppContainer(const base::string16& sid) {
+ PSID local_sid;
+ if (!ConvertStringSidToSid(sid.c_str(), &local_sid))
+ return base::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) {
+ AppContainerLookupMoniker =
+ BindFunction<AppContainerLookupMonikerPtr>("AppContainerLookupMoniker");
+ AppContainerFreeMemory =
+ BindFunction<AppContainerFreeMemoryPtr>("AppContainerFreeMemory");
+ }
+
+ if (!AppContainerLookupMoniker || !AppContainerFreeMemory)
+ return base::string16();
+
+ wchar_t* buffer = NULL;
+ HRESULT rv = AppContainerLookupMoniker(local_sid, &buffer);
+ if (FAILED(rv))
+ return base::string16();
+
+ base::string16 name(buffer);
+ if (!AppContainerFreeMemory(buffer))
+ NOTREACHED();
+ return name;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/app_container.h b/sandbox/win/src/app_container.h
new file mode 100644
index 0000000000..8125d706fb
--- /dev/null
+++ b/sandbox/win/src/app_container.h
@@ -0,0 +1,69 @@
+// 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 base::string16& app_container_sid,
+ const std::vector<base::string16>& capabilities);
+
+ // Updates the proc_thred attribute list of the provided startup_information
+ // with the app container related data.
+ // 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 base::string16& sid,
+ const base::string16& name);
+
+// Deletes an AppContainer previously created with a successfull call to
+// CreateAppContainer.
+ResultCode DeleteAppContainer(const base::string16& sid);
+
+// Retrieves the name associated with the provided AppContainer sid. Returns an
+// empty string if the AppContainer is not registered with the system.
+base::string16 LookupAppContainer(const base::string16& sid);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_APP_CONTAINER_H_
diff --git a/sandbox/win/src/app_container_test.cc b/sandbox/win/src/app_container_test.cc
new file mode 100644
index 0000000000..ced5cbde7c
--- /dev/null
+++ b/sandbox/win/src/app_container_test.cc
@@ -0,0 +1,161 @@
+// 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 base::string16& name,
+ const base::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));
+}
+
+TEST(AppContainerTest, DenyOpenEventForLowBox) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
+
+ base::win::ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, L"test"));
+ ASSERT_TRUE(event.IsValid());
+
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetLowBox(kAppContainerSid));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
+}
+
+// TODO(shrikant): Please add some tests to prove usage of lowbox token like
+// socket connection to local server in lock down mode.
+
+} // namespace sandbox
diff --git a/sandbox/win/src/app_container_unittest.cc b/sandbox/win/src/app_container_unittest.cc
new file mode 100644
index 0000000000..4bce16a42b
--- /dev/null
+++ b/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<base::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/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
new file mode 100644
index 0000000000..57aa51a52d
--- /dev/null
+++ b/sandbox/win/src/broker_services.cc
@@ -0,0 +1,623 @@
+// 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 <AclAPI.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();
+ }
+}
+
+// Utility function to determine whether a token for the specified policy can
+// be cached.
+bool IsTokenCacheable(const sandbox::PolicyBase* policy) {
+ const sandbox::AppContainerAttributes* app_container =
+ policy->GetAppContainer();
+
+ // We cannot cache tokens with an app container or lowbox.
+ if (app_container || policy->GetLowBoxSid())
+ return false;
+
+ return true;
+}
+
+// Utility function to pack token values into a key for the cache map.
+uint32_t GenerateTokenCacheKey(const sandbox::PolicyBase* policy) {
+ const size_t kTokenShift = 3;
+ uint32_t key;
+
+ DCHECK(IsTokenCacheable(policy));
+
+ // Make sure our token values aren't too large to pack into the key.
+ static_assert(sandbox::USER_LAST <= (1 << kTokenShift),
+ "TokenLevel too large");
+ static_assert(sandbox::INTEGRITY_LEVEL_LAST <= (1 << kTokenShift),
+ "IntegrityLevel too large");
+ static_assert(sizeof(key) < (kTokenShift * 3),
+ "Token key type too small");
+
+ // The key is the enum values shifted to avoid overlap and OR'd together.
+ key = policy->GetInitialTokenLevel();
+ key <<= kTokenShift;
+ key |= policy->GetLockdownTokenLevel();
+ key <<= kTokenShift;
+ key |= policy->GetIntegrityLevel();
+
+ return key;
+}
+
+} // namespace
+
+namespace sandbox {
+
+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_);
+
+ // Close any token in the cache.
+ for (TokenCacheMap::iterator it = token_cache_.begin();
+ it != token_cache_.end(); ++it) {
+ ::CloseHandle(it->second.first);
+ ::CloseHandle(it->second.second);
+ }
+}
+
+TargetPolicy* BrokerServicesBase::CreatePolicy() {
+ // 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(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ }
+ --target_counter;
+ if (0 == target_counter)
+ ::SetEvent(no_targets);
+
+ DCHECK(target_counter >= 0);
+ break;
+ }
+
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
+ break;
+ }
+
+ case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
+ BOOL res = ::TerminateJobObject(tracker->job,
+ SBOX_FATAL_MEMORY_EXCEEDED);
+ DCHECK(res);
+ 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(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(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);
+
+ if (policy_base->GetAppContainer() && policy_base->GetLowBoxSid())
+ return SBOX_ERROR_BAD_PARAMS;
+
+ // 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 = SBOX_ALL_OK;
+
+ if (IsTokenCacheable(policy_base)) {
+ // Create the master tokens only once and save them in a cache. That way
+ // can just duplicate them to avoid hammering LSASS on every sandboxed
+ // process launch.
+ uint32_t token_key = GenerateTokenCacheKey(policy_base);
+ TokenCacheMap::iterator it = token_cache_.find(token_key);
+ if (it != token_cache_.end()) {
+ initial_token_temp = it->second.first;
+ lockdown_token_temp = it->second.second;
+ } else {
+ result =
+ policy_base->MakeTokens(&initial_token_temp, &lockdown_token_temp);
+ if (SBOX_ALL_OK != result)
+ return result;
+ token_cache_[token_key] =
+ std::pair<HANDLE, HANDLE>(initial_token_temp, lockdown_token_temp);
+ }
+
+ if (!::DuplicateToken(initial_token_temp, SecurityImpersonation,
+ &initial_token_temp)) {
+ return SBOX_ERROR_GENERIC;
+ }
+
+ if (!::DuplicateTokenEx(lockdown_token_temp, TOKEN_ALL_ACCESS, 0,
+ SecurityIdentification, TokenPrimary,
+ &lockdown_token_temp)) {
+ return SBOX_ERROR_GENERIC;
+ }
+ } else {
+ 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;
+ // The liftime of |mitigations| and |inherit_handle_list| have to be at least
+ // as long as |startup_info| because |UpdateProcThreadAttribute| requires that
+ // its |lpValue| parameter persist until |DeleteProcThreadAttributeList| is
+ // called; StartupInformation's destructor makes such a call.
+ DWORD64 mitigations;
+
+ std::vector<HANDLE> inherited_handle_list;
+
+ base::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;
+
+ 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();
+
+ if (stdout_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stdout_handle);
+
+ // Handles in the list must be unique.
+ if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stderr_handle);
+
+ HandleList policy_handle_list = policy_base->GetHandlesBeingShared();
+
+ for (auto handle : policy_handle_list)
+ inherited_handle_list.push_back(handle);
+
+ if (inherited_handle_list.size())
+ ++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 (inherited_handle_list.size()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ &inherited_handle_list[0],
+ sizeof(HANDLE) * inherited_handle_list.size())) {
+ 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.Get(),
+ thread_pool_);
+
+ DWORD win_result = target->Create(exe_path, command_line, inherit_handles,
+ policy_base->GetLowBoxSid() ? true : false,
+ startup_info, &process_info);
+
+ policy_base->ClearSharedHandles();
+
+ if (ERROR_SUCCESS != win_result) {
+ SpawnCleanup(target, win_result);
+ return SBOX_ERROR_CREATE_PROCESS;
+ }
+
+ // 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));
+
+ // There is no obvious recovery after failure here. Previous version with
+ // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639
+ CHECK(AssociateCompletionPort(tracker->job, job_port_, tracker.get()));
+
+ // 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>(static_cast<uintptr_t>(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.Get(), 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;
+
+ base::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;
+
+ base::string16 name = LookupAppContainer(sid);
+ if (name.empty())
+ return SBOX_ERROR_INVALID_APP_CONTAINER;
+
+ return DeleteAppContainer(sid);
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/broker_services.h b/sandbox/win/src/broker_services.h
new file mode 100644
index 0000000000..3e7a1790ee
--- /dev/null
+++ b/sandbox/win/src/broker_services.h
@@ -0,0 +1,118 @@
+// 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 <utility>
+#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 final : public BrokerServices,
+ public SingletonBase<BrokerServicesBase> {
+ public:
+ BrokerServicesBase();
+
+ ~BrokerServicesBase();
+
+ // BrokerServices interface.
+ ResultCode Init() override;
+ TargetPolicy* CreatePolicy() override;
+ ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ TargetPolicy* policy,
+ PROCESS_INFORMATION* target) override;
+ ResultCode WaitForAllTargets() override;
+ ResultCode AddTargetPeer(HANDLE peer_process) override;
+ ResultCode InstallAppContainer(const wchar_t* sid,
+ const wchar_t* name) override;
+ 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_;
+
+ typedef std::map<uint32_t, std::pair<HANDLE, HANDLE>> TokenCacheMap;
+ TokenCacheMap token_cache_;
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_WIN_SRC_BROKER_SERVICES_H_
diff --git a/sandbox/win/src/crosscall_client.h b/sandbox/win/src/crosscall_client.h
new file mode 100644
index 0000000000..5b1bce71b2
--- /dev/null
+++ b/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 UINT32_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() {
+ static_assert(sizeof(T) == sizeof(uint32), "specialization needed");
+ return UINT32_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) \
+ static_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/sandbox/win/src/crosscall_params.h b/sandbox/win/src/crosscall_params.h
new file mode 100644
index 0000000000..6facb202da
--- /dev/null
+++ b/sandbox/win/src/crosscall_params.h
@@ -0,0 +1,296 @@
+// 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_ =
+ static_cast<uint32>(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_ =
+ static_cast<uint32>(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 ((size > sizeof(*this)) ||
+ (param_info_[index].offset_ > (sizeof(*this) - size))) {
+ // 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);
+};
+
+static_assert(sizeof(ActualCallParams<1, 1024>) == 1024, "bad size buffer");
+static_assert(sizeof(ActualCallParams<2, 1024>) == 1024, "bad size buffer");
+static_assert(sizeof(ActualCallParams<3, 1024>) == 1024, "bad size buffer");
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__
diff --git a/sandbox/win/src/crosscall_server.cc b/sandbox/win/src/crosscall_server.cc
new file mode 100644
index 0000000000..6f8bd744f2
--- /dev/null
+++ b/sandbox/win/src/crosscall_server.cc
@@ -0,0 +1,307 @@
+// 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/crosscall_server.h"
+
+#include <string>
+#include <vector>
+
+#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:
+ 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) || (UINT32_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, base::string16* 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;
+}
+
+Dispatcher::Dispatcher() {
+}
+
+Dispatcher::~Dispatcher() {
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/crosscall_server.h b/sandbox/win/src/crosscall_server.h
new file mode 100644
index 0000000000..41888c1203
--- /dev/null
+++ b/sandbox/win/src/crosscall_server.h
@@ -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.
+
+#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 "base/strings/string16.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, base::string16* string);
+
+ // Gets a parameter that is an in/out buffer. Returns false is the parameter
+ // does not exist or if the size of the actual parameter is not equal to the
+ // 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;
+
+ Dispatcher();
+ 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/sandbox/win/src/eat_resolver.cc b/sandbox/win/src/eat_resolver.cc
new file mode 100644
index 0000000000..1675ce84eb
--- /dev/null
+++ b/sandbox/win/src/eat_resolver.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/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;
+
+#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;
+
+ size_t thunk_bytes = GetInternalThunkSize();
+ 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.
+ *eat_entry_ = static_cast<DWORD>(reinterpret_cast<uintptr_t>(thunk_storage)) -
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(target_module));
+
+ 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/sandbox/win/src/eat_resolver.h b/sandbox/win/src/eat_resolver.h
new file mode 100644
index 0000000000..1d9d430672
--- /dev/null
+++ b/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) {}
+ ~EatResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ 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) override;
+
+ // Implementation of Resolver::ResolveTarget.
+ NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ private:
+ // The entry to patch.
+ DWORD* eat_entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(EatResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_EAT_RESOLVER_H__
diff --git a/sandbox/win/src/file_policy_test.cc b/sandbox/win/src/file_policy_test.cc
new file mode 100644
index 0000000000..8b5236251f
--- /dev/null
+++ b/sandbox/win/src/file_policy_test.cc
@@ -0,0 +1,674 @@
+// 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 "base/win/windows_version.h"
+#include "sandbox/win/src/filesystem_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 "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. The second argument
+// determines the type of access and the dispositino of the file.
+SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t **argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring operation(argv[0]);
+
+ if (operation == L"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.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+
+ } else if (operation == L"Write") {
+ 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.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+
+ } else if (operation == L"ReadCreate") {
+ base::win::ScopedHandle file2(CreateFile(
+ argv[1], GENERIC_READ, kSharing, NULL, CREATE_NEW, 0, NULL));
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_READ, kSharing, NULL, CREATE_ALWAYS, 0, NULL));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+ }
+
+ return SBOX_TEST_INVALID_PARAMETER;
+}
+
+SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) {
+ if (argc != 1) {
+ SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ base::string16 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;
+
+ base::string16 file(argv[0]);
+ if (0 != _wcsnicmp(file.c_str(), kNTDevicePrefix, kNTDevicePrefixLen))
+ 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;
+
+ base::string16 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) {
+ base::string16 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;
+ base::string16 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) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ base::string16 calc = MakePathToSys(L"calc.exe", false);
+ base::string16 nt_path;
+ ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path));
+ TestRunner runner;
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str());
+
+ 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_read_create[MAX_PATH + 20] = {0};
+ wsprintf(command_read_create, L"File_Create ReadCreate \"%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 cannot create the file after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_read_create));
+
+ // Verify that we don't have write access after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write));
+
+ // Verify that we have read access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read));
+
+ // 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);
+}
+
+// Tests support of "\\\\.\\DeviceName" kind of paths.
+TEST(FilePolicyTest, AllowImplicitDeviceName) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return;
+
+ TestRunner runner;
+
+ 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);
+
+ std::wstring path;
+ EXPECT_TRUE(ConvertToLongPath(temp_file_name, &path));
+ EXPECT_TRUE(GetNtPathFromWin32Path(path, &path));
+ path = path.substr(sandbox::kNTDevicePrefixLen);
+
+ wchar_t command[MAX_PATH + 20] = {0};
+ wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str());
+ path = std::wstring(kNTPrefix) + path;
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, path.c_str()));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+
+ 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"));
+}
+
+TEST(FilePolicyTest, 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.
+ base::string16 subfolder = temp_file_name;
+ base::string16 temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1);
+ base::string16 temp_file = subfolder + L"\\file_" + temp_file_title;
+
+ HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, NULL);
+ ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Create a temporary file in the temp directory.
+ base::string16 temp_dir = temp_directory;
+ base::string16 temp_file_in_temp = temp_dir + L"file_" + temp_file_title;
+ file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, NULL);
+ ASSERT_TRUE(file != NULL);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Give write access to the temp directory.
+ base::string16 temp_dir_wildcard = temp_dir + L"*";
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY,
+ temp_dir_wildcard.c_str()));
+
+ // Prepare the command to execute.
+ base::string16 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);
+
+ base::string16 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()));
+}
+
+TEST(FilePolicyTest, CheckExistingNTPrefixEscape) {
+ base::string16 name = L"\\??\\NAME";
+
+ base::string16 result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), L"\\/?/?\\NAME");
+}
+
+TEST(FilePolicyTest, CheckEscapedNTPrefixNoEscape) {
+ base::string16 name = L"\\/?/?\\NAME";
+
+ base::string16 result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), name.c_str());
+}
+
+TEST(FilePolicyTest, CheckMissingNTPrefixEscape) {
+ base::string16 name = L"C:\\NAME";
+
+ base::string16 result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), L"\\/?/?\\C:\\NAME");
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/filesystem_dispatcher.cc b/sandbox/win/src/filesystem_dispatcher.cc
new file mode 100644
index 0000000000..4561be4202
--- /dev/null
+++ b/sandbox/win/src/filesystem_dispatcher.cc
@@ -0,0 +1,314 @@
+// 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, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtCreateFile)
+ };
+
+ static const IPCCall open_file = {
+ {IPC_NTOPENFILE_TAG, WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtOpenFile)
+ };
+
+ static const IPCCall attribs = {
+ {IPC_NTQUERYATTRIBUTESFILE_TAG, WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryAttributesFile)
+ };
+
+ static const IPCCall full_attribs = {
+ {IPC_NTQUERYFULLATTRIBUTESFILE_TAG, WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryFullAttributesFile)
+ };
+
+ static const IPCCall set_info = {
+ {IPC_NTSETINFO_RENAME_TAG, VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE,
+ UINT32_TYPE, UINT32_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,
+ base::string16* name,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 file_attributes,
+ uint32 share_access,
+ uint32 create_disposition,
+ uint32 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();
+
+ uint32 broker = TRUE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition);
+ 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,
+ base::string16* name,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 share_access,
+ uint32 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();
+
+ uint32 broker = TRUE;
+ uint32 create_disposition = FILE_OPEN;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition);
+ 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,
+ base::string16* name,
+ uint32 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;
+ }
+
+ uint32 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,
+ base::string16* name,
+ uint32 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;
+ }
+
+ uint32 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,
+ uint32 length,
+ uint32 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;
+
+ base::string16 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;
+ }
+
+ uint32 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/sandbox/win/src/filesystem_dispatcher.h b/sandbox/win/src/filesystem_dispatcher.h
new file mode 100644
index 0000000000..192d36cab0
--- /dev/null
+++ b/sandbox/win/src/filesystem_dispatcher.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_SRC_FILESYSTEM_DISPATCHER_H__
+#define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles file system-related IPC calls.
+class FilesystemDispatcher : public Dispatcher {
+ public:
+ explicit FilesystemDispatcher(PolicyBase* policy_base);
+ ~FilesystemDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateFile in the target.
+ bool NtCreateFile(IPCInfo* ipc,
+ base::string16* name,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 file_attributes,
+ uint32 share_access,
+ uint32 create_disposition,
+ uint32 create_options);
+
+ // Processes IPC requests coming from calls to NtOpenFile in the target.
+ bool NtOpenFile(IPCInfo* ipc,
+ base::string16* name,
+ uint32 attributes,
+ uint32 desired_access,
+ uint32 share_access,
+ uint32 create_options);
+
+ // Processes IPC requests coming from calls to NtQueryAttributesFile in the
+ // target.
+ bool NtQueryAttributesFile(IPCInfo* ipc,
+ base::string16* name,
+ uint32 attributes,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtQueryFullAttributesFile in
+ // the target.
+ bool NtQueryFullAttributesFile(IPCInfo* ipc,
+ base::string16* name,
+ uint32 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,
+ uint32 length,
+ uint32 info_class);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
diff --git a/sandbox/win/src/filesystem_interception.cc b/sandbox/win/src/filesystem_interception.cc
new file mode 100644
index 0000000000..459f7ace2d
--- /dev/null
+++ b/sandbox/win/src/filesystem_interception.cc
@@ -0,0 +1,367 @@
+// 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;
+
+ wchar_t* name = NULL;
+ 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;
+
+ uint32 attributes = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ uint32 desired_access_uint32 = desired_access;
+ uint32 options_uint32 = options;
+ uint32 disposition_uint32 = disposition;
+ uint32 broker = FALSE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(name);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access_uint32);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(disposition_uint32);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options_uint32);
+ 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_uint32, file_attributes, sharing,
+ disposition, options_uint32, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ if (name)
+ operator delete(name, NT_ALLOC);
+
+ 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;
+
+ wchar_t* name = NULL;
+ 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;
+
+ uint32 attributes;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || NULL == name)
+ break;
+
+ uint32 desired_access_uint32 = desired_access;
+ uint32 options_uint32 = options;
+ uint32 disposition_uint32 = FILE_OPEN;
+ uint32 broker = FALSE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(name);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access_uint32);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(disposition_uint32);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options_uint32);
+ 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_uint32, sharing, options_uint32,
+ &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ if (name)
+ operator delete(name, NT_ALLOC);
+
+ 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;
+
+ wchar_t* name = NULL;
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ 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));
+
+ uint32 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);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ } while (false);
+
+ if (name)
+ operator delete(name, NT_ALLOC);
+
+ 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;
+
+ wchar_t* name = NULL;
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ break;
+
+ 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));
+
+ uint32 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);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+ } while (false);
+
+ if (name)
+ operator delete(name, NT_ALLOC);
+
+ 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;
+
+ wchar_t* name = NULL;
+ 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;
+ }
+
+ NTSTATUS ret = AllocAndCopyName(&object_attributes, &name, NULL, NULL);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32 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);
+
+ if (name)
+ operator delete(name, NT_ALLOC);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/filesystem_interception.h b/sandbox/win/src/filesystem_interception.h
new file mode 100644
index 0000000000..2fafb4499b
--- /dev/null
+++ b/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/sandbox/win/src/filesystem_policy.cc b/sandbox/win/src/filesystem_policy.cc
new file mode 100644
index 0000000000..0349ff304e
--- /dev/null
+++ b/sandbox/win/src/filesystem_policy.cc
@@ -0,0 +1,430 @@
+// 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 "base/win/windows_version.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;
+}
+
+// Get an initialized anonymous level Security QOS.
+SECURITY_QUALITY_OF_SERVICE GetAnonymousQOS() {
+ SECURITY_QUALITY_OF_SERVICE security_qos = {0};
+ security_qos.Length = sizeof(security_qos);
+ security_qos.ImpersonationLevel = SecurityAnonymous;
+ // Set dynamic tracking so that a pipe doesn't capture the broker's token
+ security_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ security_qos.EffectiveOnly = TRUE;
+
+ return security_qos;
+}
+
+} // namespace.
+
+namespace sandbox {
+
+bool FileSystemPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ base::string16 mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ if (!PreProcessName(mod_name, &mod_name)) {
+ // The path to be added might contain a reparse point.
+ NOTREACHED();
+ return false;
+ }
+
+ // 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 (_wcsnicmp(mod_name.c_str(), kNTDevicePrefix, kNTDevicePrefixLen)) {
+ mod_name = FixNTPrefixForMatch(mod_name);
+ 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);
+ open.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
+ create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+ create.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
+
+ // 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 base::string16 &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};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes,
+ &uni_name, IsPipe(file) ? &security_qos : NULL);
+ *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 base::string16 &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};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes,
+ &uni_name, IsPipe(file) ? &security_qos : NULL);
+ *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 base::string16 &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};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes,
+ &uni_name, IsPipe(file) ? &security_qos : NULL);
+ *nt_status = NtQueryAttributesFile(&obj_attributes, file_info);
+
+ return true;
+}
+
+bool FileSystemPolicy::QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::string16 &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};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, NULL, &obj_attributes,
+ &uni_name, IsPipe(file) ? &security_qos : NULL);
+ *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 base::string16& path, base::string16* 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;
+}
+
+base::string16 FixNTPrefixForMatch(const base::string16& name) {
+ base::string16 mod_name = name;
+
+ // NT prefix escaped for rule matcher
+ const wchar_t kNTPrefixEscaped[] = L"\\/?/?\\";
+ const int kNTPrefixEscapedLen = arraysize(kNTPrefixEscaped) - 1;
+
+ if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) {
+ if (0 != mod_name.compare(0, kNTPrefixEscapedLen, kNTPrefixEscaped)) {
+ // TODO(nsylvain): Find a better way to do name resolution. Right now we
+ // take the name and we expand it.
+ mod_name.insert(0, kNTPrefixEscaped);
+ }
+ } else {
+ // Start of name matches NT prefix, replace with escaped format
+ // Fixes bug: 334882
+ mod_name.replace(0, kNTPrefixLen, kNTPrefixEscaped);
+ }
+
+ return mod_name;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/filesystem_policy.h b/sandbox/win/src/filesystem_policy.h
new file mode 100644
index 0000000000..ce28344c53
--- /dev/null
+++ b/sandbox/win/src/filesystem_policy.h
@@ -0,0 +1,113 @@
+// 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 "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#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 base::string16 &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 base::string16 &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 base::string16 &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 base::string16 &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 base::string16& path, base::string16* new_path);
+
+// Corrects global paths to have a correctly escaped NT prefix at the
+// beginning. If the name has no NT prefix (either normal or escaped)
+// add the escaped form to the string
+base::string16 FixNTPrefixForMatch(const base::string16& name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_POLICY_H__
diff --git a/sandbox/win/src/handle_closer.cc b/sandbox/win/src/handle_closer.cc
new file mode 100644
index 0000000000..2e3a78223b
--- /dev/null
+++ b/sandbox/win/src/handle_closer.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 "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() {
+}
+
+HandleCloser::~HandleCloser() {
+}
+
+ResultCode HandleCloser::AddHandle(const base::char16* handle_type,
+ const base::char16* handle_name) {
+ if (!handle_type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ base::string16 resolved_name;
+ if (handle_name) {
+ resolved_name = handle_name;
+ if (handle_type == base::string16(L"Key"))
+ if (!ResolveRegistryName(resolved_name, &resolved_name))
+ 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(resolved_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(resolved_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(base::char16);
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ bytes_entry += ((*j).size() + 1) * sizeof(base::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();
+
+ base::char16* output = reinterpret_cast<base::char16*>(
+ &handle_info->handle_entries[0]);
+ base::char16* end = reinterpret_cast<base::char16*>(
+ reinterpret_cast<char*>(buffer) + buffer_bytes);
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ 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 GetHandleName(HANDLE handle, base::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/sandbox/win/src/handle_closer.h b/sandbox/win/src/handle_closer.h
new file mode 100644
index 0000000000..2b43a6eaf1
--- /dev/null
+++ b/sandbox/win/src/handle_closer.h
@@ -0,0 +1,74 @@
+// 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 base::string16, std::set<base::string16> > HandleMap;
+
+// Type and set of corresponding handle names to close.
+struct HandleListEntry {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t offset_to_names; // Nul terminated strings of name_count names.
+ size_t name_count;
+ base::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();
+ ~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 base::char16* handle_type,
+ const base::char16* handle_name);
+
+ // Serializes and copies the closer table into the target process.
+ bool InitializeTargetHandles(TargetProcess* target);
+
+ 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, base::string16* handle_name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_H_
diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc
new file mode 100644
index 0000000000..9a79d556de
--- /dev/null
+++ b/sandbox/win/src/handle_closer_agent.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/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;
+}
+
+HandleCloserAgent::HandleCloserAgent()
+ : dummy_handle_(::CreateEvent(NULL, FALSE, FALSE, NULL)) {
+}
+
+HandleCloserAgent::~HandleCloserAgent() {
+}
+
+// Attempts to stuff |closed_handle| with a duplicated handle for a dummy Event
+// with no access. This should allow the handle to be closed, to avoid
+// generating EXCEPTION_INVALID_HANDLE on shutdown, but nothing else. For now
+// the only supported |type| is Event or File.
+bool HandleCloserAgent::AttemptToStuffHandleSlot(HANDLE closed_handle,
+ const base::string16& type) {
+ // Only attempt to stuff Files and Events at the moment.
+ if (type != L"Event" && type != L"File") {
+ return true;
+ }
+
+ if (!dummy_handle_.IsValid())
+ return false;
+
+ // This should never happen, as g_dummy is created before closing to_stuff.
+ DCHECK(dummy_handle_.Get() != closed_handle);
+
+ std::vector<HANDLE> to_close;
+ HANDLE dup_dummy = NULL;
+ size_t count = 16;
+
+ do {
+ if (!::DuplicateHandle(::GetCurrentProcess(), dummy_handle_.Get(),
+ ::GetCurrentProcess(), &dup_dummy, 0, FALSE, 0))
+ break;
+ if (dup_dummy != closed_handle)
+ to_close.push_back(dup_dummy);
+ } while (count-- &&
+ reinterpret_cast<uintptr_t>(dup_dummy) <
+ reinterpret_cast<uintptr_t>(closed_handle));
+
+ for (auto h : to_close)
+ ::CloseHandle(h);
+
+ // Useful to know when we're not able to stuff handles.
+ DCHECK(dup_dummy == closed_handle);
+
+ return dup_dummy == closed_handle;
+}
+
+// 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.
+ base::char16* input = entry->handle_type;
+ HandleMap::mapped_type& handle_names = handles_to_close_[input];
+ input = reinterpret_cast<base::char16*>(reinterpret_cast<char*>(entry)
+ + entry->offset_to_names);
+ // Grab all the handle names.
+ for (size_t j = 0; j < entry->name_count; ++j) {
+ 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<base::char16*>(entry) >= input);
+ DCHECK(reinterpret_cast<base::char16*>(entry) - input <
+ sizeof(size_t) / sizeof(base::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 = 4; // Handles are always a multiple of 4.
+
+ 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]));
+ base::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;
+ // Attempt to stuff this handle with a new dummy Event.
+ AttemptToStuffHandleSlot(handle, result->first);
+ }
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/handle_closer_agent.h b/sandbox/win/src/handle_closer_agent.h
new file mode 100644
index 0000000000..a3e15024d4
--- /dev/null
+++ b/sandbox/win/src/handle_closer_agent.h
@@ -0,0 +1,44 @@
+// 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 "base/win/scoped_handle.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();
+ ~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:
+ // Attempt to stuff a closed handle with a dummy Event.
+ bool AttemptToStuffHandleSlot(HANDLE closed_handle,
+ const base::string16& type);
+
+ HandleMap handles_to_close_;
+ base::win::ScopedHandle dummy_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
diff --git a/sandbox/win/src/handle_closer_test.cc b/sandbox/win/src/handle_closer_test.cc
new file mode 100644
index 0000000000..f1f80e8888
--- /dev/null
+++ b/sandbox/win/src/handle_closer_test.cc
@@ -0,0 +1,297 @@
+// 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/nt_internals.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));
+ base::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.Get(), &timestamp, NULL, NULL));
+ marker_path += base::StringPrintf(L"%08x%08x%08x",
+ ::GetFileSize(module.Get(), 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);
+}
+
+// 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;
+}
+
+// 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)
+ CHECK_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 = 4; // Handles are always a multiple of 4.
+ HANDLE handle = NULL;
+ int invalid_count = 0;
+ base::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;
+}
+
+// Checks that supplied handle is an Event and it's not waitable.
+// Format: CheckForEventHandles
+SBOX_TESTS_COMMAND int CheckForEventHandles(int argc, wchar_t** argv) {
+ static int state = BEFORE_INIT;
+ static std::vector<HANDLE> to_check;
+
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+ HANDLE handle = GetMarkerFile(kFileExtensions[i]);
+ CHECK_NE(handle, INVALID_HANDLE_VALUE);
+ to_check.push_back(handle);
+ }
+ return SBOX_TEST_SUCCEEDED;
+
+ case AFTER_REVERT:
+ for (auto handle : to_check) {
+ // 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]));
+ 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;
+ }
+
+ CHECK(NT_SUCCESS(rc));
+ CHECK(type_info->Name.Buffer);
+
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] =
+ L'\0';
+
+ // Should be an Event now.
+ CHECK_EQ(wcslen(type_info->Name.Buffer), 5U);
+ CHECK_EQ(wcscmp(L"Event", type_info->Name.Buffer), 0);
+
+ // Should not be able to wait.
+ CHECK_EQ(WaitForSingleObject(handle, INFINITE), WAIT_FAILED);
+
+ // Should be able to close.
+ CHECK_EQ(TRUE, CloseHandle(handle));
+ }
+ return SBOX_TEST_SUCCEEDED;
+
+ default: // Do nothing.
+ break;
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, CheckForMarkerFiles) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+
+ base::string16 command = base::string16(L"CheckForFileHandles Y");
+ for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+ base::string16 handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &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();
+
+ base::string16 command = base::string16(L"CheckForFileHandles N");
+ for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+ base::string16 handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &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;
+}
+
+TEST(HandleCloserTest, CheckStuffedHandle) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ for (int i = 0; i < arraysize(kFileExtensions); ++i) {
+ base::string16 handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i]));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+ CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
+ SBOX_ALL_OK);
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckForEventHandles"));
+}
+
+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];
+ finish_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ CHECK(finish_event);
+
+ // 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/sandbox/win/src/handle_dispatcher.cc b/sandbox/win/src/handle_dispatcher.cc
new file mode 100644
index 0000000000..66308f4b4a
--- /dev/null
+++ b/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, UINT32_TYPE, UINT32_TYPE,
+ UINT32_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,
+ uint32 target_process_id,
+ uint32 desired_access,
+ uint32 options) {
+ 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 | options)) {
+ ipc->return_info.win32_result = ::GetLastError();
+ return false;
+ }
+ options &= ~DUPLICATE_CLOSE_SOURCE;
+ base::win::ScopedHandle handle(handle_temp);
+
+ // Get the object type (32 characters is safe; current max is 14).
+ 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);
+ NTSTATUS error =
+ QueryObject(handle.Get(), ObjectTypeInformation, type_info, size, &size);
+ if (!NT_SUCCESS(error)) {
+ ipc->return_info.nt_status = 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, handle.Get(),
+ target_process_id,
+ &ipc->return_info.handle,
+ desired_access, options);
+ return true;
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/handle_dispatcher.h b/sandbox/win/src/handle_dispatcher.h
new file mode 100644
index 0000000000..84a22e19b3
--- /dev/null
+++ b/sandbox/win/src/handle_dispatcher.h
@@ -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.
+
+#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() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+ private:
+ // Processes IPC requests coming from calls to
+ // TargetServices::DuplicateHandle() in the target.
+ bool DuplicateHandleProxy(IPCInfo* ipc,
+ HANDLE source_handle,
+ uint32 target_process_id,
+ uint32 desired_access,
+ uint32 options);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(HandleDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
diff --git a/sandbox/win/src/handle_inheritance_test.cc b/sandbox/win/src/handle_inheritance_test.cc
new file mode 100644
index 0000000000..37974e3106
--- /dev/null
+++ b/sandbox/win/src/handle_inheritance_test.cc
@@ -0,0 +1,52 @@
+// 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/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/win/scoped_handle.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) {
+ base::ScopedTempDir temp_directory;
+ base::FilePath temp_file_name;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ ASSERT_TRUE(CreateTemporaryFileInDir(temp_directory.path(), &temp_file_name));
+
+ SECURITY_ATTRIBUTES attrs = {};
+ attrs.nLength = sizeof(attrs);
+ attrs.bInheritHandle = TRUE;
+ base::win::ScopedHandle tmp_handle(
+ CreateFile(temp_file_name.value().c_str(), GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
+ &attrs, OPEN_EXISTING, 0, NULL));
+ ASSERT_TRUE(tmp_handle.IsValid());
+
+ TestRunner runner;
+ ASSERT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStdoutHandle(tmp_handle.Get()));
+ int result = runner.RunTest(L"HandleInheritanceTests_PrintToStdout");
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED, result);
+
+ std::string data;
+ ASSERT_TRUE(base::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) {
+ ASSERT_EQ("Example output to stdout\r\n", data);
+ } else {
+ ASSERT_EQ("", data);
+ }
+}
+
+}
diff --git a/sandbox/win/src/handle_interception.cc b/sandbox/win/src/handle_interception.cc
new file mode 100644
index 0000000000..a0df8d653d
--- /dev/null
+++ b/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.win32_result);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ *target_handle = answer.handle;
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/handle_interception.h b/sandbox/win/src/handle_interception.h
new file mode 100644
index 0000000000..6f60811f17
--- /dev/null
+++ b/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/sandbox/win/src/handle_policy.cc b/sandbox/win/src/handle_policy.cc
new file mode 100644
index 0000000000..1023030792
--- /dev/null
+++ b/sandbox/win/src/handle_policy.cc
@@ -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.
+
+#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,
+ 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();
+ if (!::DuplicateHandle(::GetCurrentProcess(), source_handle, target_process,
+ target_handle, desired_access, FALSE,
+ options)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/handle_policy.h b/sandbox/win/src/handle_policy.h
new file mode 100644
index 0000000000..ffe54b8ae2
--- /dev/null
+++ b/sandbox/win/src/handle_policy.h
@@ -0,0 +1,40 @@
+// 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,
+ 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/sandbox/win/src/handle_policy_test.cc b/sandbox/win/src/handle_policy_test.cc
new file mode 100644
index 0000000000..11382da811
--- /dev/null
+++ b/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.Get(), 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.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ 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.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ 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.
+ base::string16 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/sandbox/win/src/handle_table.cc b/sandbox/win/src/handle_table.cc
new file mode 100644
index 0000000000..5ebcf99460
--- /dev/null
+++ b/sandbox/win/src/handle_table.cc
@@ -0,0 +1,189 @@
+// 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 base::char16* HandleTable::kTypeProcess = L"Process";
+const base::char16* HandleTable::kTypeThread = L"Thread";
+const base::char16* HandleTable::kTypeFile = L"File";
+const base::char16* HandleTable::kTypeDirectory = L"Directory";
+const base::char16* HandleTable::kTypeKey = L"Key";
+const base::char16* HandleTable::kTypeWindowStation = L"WindowStation";
+const base::char16* HandleTable::kTypeDesktop = L"Desktop";
+const base::char16* HandleTable::kTypeService = L"Service";
+const base::char16* HandleTable::kTypeMutex = L"Mutex";
+const base::char16* HandleTable::kTypeSemaphore = L"Semaphore";
+const base::char16* HandleTable::kTypeEvent = L"Event";
+const base::char16* HandleTable::kTypeTimer = L"Timer";
+const base::char16* HandleTable::kTypeNamedPipe = L"NamedPipe";
+const base::char16* HandleTable::kTypeJobObject = L"JobObject";
+const base::char16* HandleTable::kTypeFileMap = L"FileMap";
+const base::char16* HandleTable::kTypeAlpcPort = L"ALPC Port";
+
+HandleTable::HandleTable() {
+ static NtQuerySystemInformation QuerySystemInformation = NULL;
+ 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::~HandleTable() {
+}
+
+HandleTable::Iterator HandleTable::HandlesForProcess(ULONG process_id) const {
+ SYSTEM_HANDLE_INFORMATION key;
+ key.ProcessId = static_cast<USHORT>(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) {
+}
+
+HandleTable::HandleEntry::~HandleEntry() {
+}
+
+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 base::string16& HandleTable::HandleEntry::Name() {
+ UpdateInfo(UPDATE_INFO_AND_NAME);
+ return handle_name_;
+}
+
+const base::string16& HandleTable::HandleEntry::Type() {
+ UpdateInfo(UPDATE_INFO_AND_TYPE_NAME);
+ return type_name_;
+}
+
+bool HandleTable::HandleEntry::IsType(const base::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/sandbox/win/src/handle_table.h b/sandbox/win/src/handle_table.h
new file mode 100644
index 0000000000..47f625d9e3
--- /dev/null
+++ b/sandbox/win/src/handle_table.h
@@ -0,0 +1,162 @@
+// 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 base::char16* HandleTable::kTypeProcess;
+ static const base::char16* HandleTable::kTypeThread;
+ static const base::char16* HandleTable::kTypeFile;
+ static const base::char16* HandleTable::kTypeDirectory;
+ static const base::char16* HandleTable::kTypeKey;
+ static const base::char16* HandleTable::kTypeWindowStation;
+ static const base::char16* HandleTable::kTypeDesktop;
+ static const base::char16* HandleTable::kTypeService;
+ static const base::char16* HandleTable::kTypeMutex;
+ static const base::char16* HandleTable::kTypeSemaphore;
+ static const base::char16* HandleTable::kTypeEvent;
+ static const base::char16* HandleTable::kTypeTimer;
+ static const base::char16* HandleTable::kTypeNamedPipe;
+ static const base::char16* HandleTable::kTypeJobObject;
+ static const base::char16* HandleTable::kTypeFileMap;
+ static const base::char16* HandleTable::kTypeAlpcPort;
+
+ class Iterator;
+
+ // Used by the iterator to provide simple caching accessors to handle data.
+ class HandleEntry {
+ public:
+ ~HandleEntry();
+
+ 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 base::string16& Name();
+
+ const base::string16& Type();
+
+ bool IsType(const base::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_;
+ base::string16 handle_name_;
+ base::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();
+ ~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/sandbox/win/src/integrity_level_test.cc b/sandbox/win/src/integrity_level_test.cc
new file mode 100644
index 0000000000..f962033ca5
--- /dev/null
+++ b/sandbox/win/src/integrity_level_test.cc
@@ -0,0 +1,91 @@
+// 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()->SetAlternateDesktop(true);
+ 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/sandbox/win/src/interception.cc b/sandbox/win/src/interception.cc
new file mode 100644
index 0000000000..60dd4400b8
--- /dev/null
+++ b/sandbox/win/src/interception.cc
@@ -0,0 +1,554 @@
+// 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/strings/string16.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/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::InterceptionData::InterceptionData() {
+}
+
+InterceptionManager::InterceptionData::~InterceptionData() {
+}
+
+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<base::string16> 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 base::string16 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;
+
+ base::string16 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 - 1);
+ 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 (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 base::string16 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/sandbox/win/src/interception.h b/sandbox/win/src/interception.h
new file mode 100644
index 0000000000..728dc74e45
--- /dev/null
+++ b/sandbox/win/src/interception.h
@@ -0,0 +1,284 @@
+// 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 "base/strings/string16.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 {
+ InterceptionData();
+ ~InterceptionData();
+
+ InterceptionType type; // Interception type.
+ InterceptorId id; // Interceptor id.
+ base::string16 dll; // Name of dll to intercept.
+ std::string function; // Name of function to intercept.
+ std::string interceptor; // Name of interceptor function.
+ const void* interceptor_address; // Interceptor's entry point.
+ };
+
+ // 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)
+
+// When intercepting the EAT it is important that the patched version of the
+// function not call any functions imported from system libraries unless
+// |TargetServices::InitCalled()| returns true, because it is only then that
+// we are guaranteed that our IAT has been initialized.
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ ((&Target##function) ? \
+ manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
+ 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)
+
+// When intercepting the EAT it is important that the patched version of the
+// function not call any functions imported from system libraries unless
+// |TargetServices::InitCalled()| returns true, because it is only then that
+// we are guaranteed that our IAT has been initialized.
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ manager->AddToPatchedFunctions(dll, #function, sandbox::INTERCEPTION_EAT, \
+ MAKE_SERVICE_NAME(function), id)
+#endif // SANDBOX_EXPORTS
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_H_
diff --git a/sandbox/win/src/interception_agent.cc b/sandbox/win/src/interception_agent.cc
new file mode 100644
index 0000000000..b2a66c471f
--- /dev/null
+++ b/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/sandbox/win/src/interception_agent.h b/sandbox/win/src/interception_agent.h
new file mode 100644
index 0000000000..2762c611fd
--- /dev/null
+++ b/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/sandbox/win/src/interception_internal.h b/sandbox/win/src/interception_internal.h
new file mode 100644
index 0000000000..810478addb
--- /dev/null
+++ b/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/sandbox/win/src/interception_unittest.cc b/sandbox/win/src/interception_unittest.cc
new file mode 100644
index 0000000000..0fc9b7cbe2
--- /dev/null
+++ b/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/sandbox/win/src/interceptors.h b/sandbox/win/src/interceptors.h
new file mode 100644
index 0000000000..a17447aa18
--- /dev/null
+++ b/sandbox/win/src/interceptors.h
@@ -0,0 +1,55 @@
+// 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,
+ // Process mitigations Win32k dispatcher:
+ GDIINITIALIZE_ID,
+ GETSTOCKOBJECT_ID,
+ REGISTERCLASSW_ID,
+ INTERCEPTOR_MAX_ID
+};
+
+typedef void* OriginalFunctions[INTERCEPTOR_MAX_ID];
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTORS_H_
diff --git a/sandbox/win/src/interceptors_64.cc b/sandbox/win/src/interceptors_64.cc
new file mode 100644
index 0000000000..ef0b5f0017
--- /dev/null
+++ b/sandbox/win/src/interceptors_64.cc
@@ -0,0 +1,278 @@
+// 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_mitigations_win32k_interception.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/registry_interception.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#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);
+}
+
+// -----------------------------------------------------------------------
+
+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 NTSTATUS WINAPI TargetNtCreateEvent64(
+ PHANDLE event_handle, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NtCreateEventFunction orig_fn = reinterpret_cast<
+ NtCreateEventFunction>(g_originals[CREATE_EVENT_ID]);
+ return TargetNtCreateEvent(orig_fn, event_handle, desired_access,
+ object_attributes, event_type, initial_state);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent64(
+ PHANDLE event_handle, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NtOpenEventFunction orig_fn = reinterpret_cast<
+ NtOpenEventFunction>(g_originals[OPEN_EVENT_ID]);
+ return TargetNtOpenEvent(orig_fn, event_handle, desired_access,
+ object_attributes);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(
+ HANDLE dll,
+ DWORD reason) {
+ GdiDllInitializeFunction orig_fn = reinterpret_cast<
+ GdiDllInitializeFunction>(g_originals[GDIINITIALIZE_ID]);
+ return TargetGdiDllInitialize(orig_fn, dll, reason);
+}
+
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object) {
+ GetStockObjectFunction orig_fn = reinterpret_cast<
+ GetStockObjectFunction>(g_originals[GETSTOCKOBJECT_ID]);
+ return TargetGetStockObject(orig_fn, object);
+}
+
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW64(
+ const WNDCLASS* wnd_class) {
+ RegisterClassWFunction orig_fn = reinterpret_cast<
+ RegisterClassWFunction>(g_originals[REGISTERCLASSW_ID]);
+ return TargetRegisterClassW(orig_fn, wnd_class);
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/interceptors_64.h b/sandbox/win/src/interceptors_64.h
new file mode 100644
index 0000000000..7368ceb845
--- /dev/null
+++ b/sandbox/win/src/interceptors_64.h
@@ -0,0 +1,175 @@
+// 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);
+
+// -----------------------------------------------------------------------
+// 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 NtCreateEvent/NtOpenEvent on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateEvent64(
+ PHANDLE event_handle, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, EVENT_TYPE event_type,
+ BOOLEAN initial_state);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent64(
+ PHANDLE event_handle, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the process mitigations win32k lockdown code.
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(
+ HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW64(const WNDCLASS* wnd_class);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTORS_64_H_
diff --git a/sandbox/win/src/internal_types.h b/sandbox/win/src/internal_types.h
new file mode 100644
index 0000000000..026bedb064
--- /dev/null
+++ b/sandbox/win/src/internal_types.h
@@ -0,0 +1,76 @@
+// 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_INTERNAL_TYPES_H_
+#define SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
+
+namespace sandbox {
+
+const wchar_t kNtdllName[] = L"ntdll.dll";
+const wchar_t kKerneldllName[] = L"kernel32.dll";
+const wchar_t kKernelBasedllName[] = L"kernelbase.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,
+ UINT32_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_WIN_SRC_INTERNAL_TYPES_H_
diff --git a/sandbox/win/src/ipc_ping_test.cc b/sandbox/win/src/ipc_ping_test.cc
new file mode 100644
index 0000000000..64e3de6c54
--- /dev/null
+++ b/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/sandbox/win/src/ipc_tags.h b/sandbox/win/src/ipc_tags.h
new file mode 100644
index 0000000000..d680411dac
--- /dev/null
+++ b/sandbox/win/src/ipc_tags.h
@@ -0,0 +1,40 @@
+// 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_GDI_GDIDLLINITIALIZE_TAG,
+ IPC_GDI_GETSTOCKOBJECT_TAG,
+ IPC_USER_REGISTERCLASSW_TAG,
+ IPC_LAST_TAG
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_IPC_TAGS_H__
diff --git a/sandbox/win/src/ipc_unittest.cc b/sandbox/win/src/ipc_unittest.cc
new file mode 100644
index 0000000000..b1e74abcfd
--- /dev/null
+++ b/sandbox/win/src/ipc_unittest.cc
@@ -0,0 +1,639 @@
+// 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";
+ base::string16 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;
+ base::string16 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(UINT32_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(UINT32_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, UINT32_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));
+
+ // Test that we handle integer overflow on the number of params
+ // correctly. We use a test-only ctor for ActualCallParams that
+ // allows to create malformed cross-call buffers.
+ const int32 kPtrDiffSz = sizeof(ptrdiff_t);
+ 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, UINT32_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);
+ }
+
+ ActualCallParams<1, kBufferSize> params_3(kTag, 1);
+ params_3.CopyParamIn(0, &value, sizeof(value), false, UINT32_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, UINT32_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() override{};
+
+ bool SetupService(InterceptionManager* manager, int service) override {
+ return true;
+ }
+
+ private:
+ bool CallOneHandler(IPCInfo* ipc, HANDLE p1, uint32 p2) {
+ ipc->return_info.extended[0].handle = p1;
+ ipc->return_info.extended[1].unsigned_int = p2;
+ return true;
+ }
+
+ bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, uint32 p2) {
+ return true;
+ }
+};
+
+UnitTestIPCDispatcher::UnitTestIPCDispatcher() {
+ static const IPCCall call_one = {
+ {CALL_ONE_TAG, VOIDPTR_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallOneHandler)
+ };
+ static const IPCCall call_two = {
+ {CALL_TWO_TAG, VOIDPTR_TYPE, UINT32_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/sandbox/win/src/job.cc b/sandbox/win/src/job.cc
new file mode 100644
index 0000000000..8852ab0c72
--- /dev/null
+++ b/sandbox/win/src/job.cc
@@ -0,0 +1,118 @@
+// 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,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit) {
+ 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: {
+ if (memory_limit) {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY;
+ jeli.ProcessMemoryLimit = memory_limit;
+ }
+
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ 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/sandbox/win/src/job.h b/sandbox/win/src/job.h
new file mode 100644
index 0000000000..60dc3146b7
--- /dev/null
+++ b/sandbox/win/src/job.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_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,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit);
+
+ // Assigns the process referenced by process_handle to the job.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // 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/sandbox/win/src/job_unittest.cc b/sandbox/win/src/job_unittest.cc
new file mode 100644
index 0000000000..a1b7acafe3
--- /dev/null
+++ b/sandbox/win/src/job_unittest.cc
@@ -0,0 +1,195 @@
+// 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, 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, 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, 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, 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, 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, 0));
+ ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0, 0));
+}
+
+// Tests the error case when we use a method and the object is not yet
+// 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, 0));
+
+ Job job2;
+ ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0, 0));
+
+ Job job3;
+ ASSERT_EQ(ERROR_SUCCESS, job3.Init(JOB_LIMITED_USER, L"job3", 0, 0));
+
+ Job job4;
+ ASSERT_EQ(ERROR_SUCCESS, job4.Init(JOB_INTERACTIVE, L"job4", 0, 0));
+
+ Job job5;
+ ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0, 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, 0));
+
+ Job job7;
+ ASSERT_EQ(ERROR_BAD_ARGUMENTS, job7.Init(
+ static_cast<JobLevel>(JOB_NONE+1), L"job7", 0, 0));
+}
+
+// Tests the method "AssignProcessToJob".
+TEST(JobTest, ProcessInJob) {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_UNPROTECTED, L"job_test_process", 0,
+ 0));
+
+ BOOL result = FALSE;
+
+ wchar_t notepad[] = L"notepad";
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION temp_process_info = {};
+ result = ::CreateProcess(NULL, notepad, NULL, NULL, FALSE, 0, NULL, NULL, &si,
+ &temp_process_info);
+ ASSERT_TRUE(result);
+ base::win::ScopedProcessInformation pi(temp_process_info);
+ ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle()));
+
+ // Get the job handle.
+ 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/sandbox/win/src/named_pipe_dispatcher.cc b/sandbox/win/src/named_pipe_dispatcher.cc
new file mode 100644
index 0000000000..5527319833
--- /dev/null
+++ b/sandbox/win/src/named_pipe_dispatcher.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_dispatcher.h"
+
+#include "base/basictypes.h"
+#include "base/strings/string_split.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#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, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_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, kKerneldllName, CreateNamedPipeW,
+ CREATE_NAMED_PIPE_ID, 36);
+
+ return false;
+}
+
+bool NamedPipeDispatcher::CreateNamedPipe(IPCInfo* ipc,
+ base::string16* name,
+ uint32 open_mode,
+ uint32 pipe_mode,
+ uint32 max_instances,
+ uint32 out_buffer_size,
+ uint32 in_buffer_size,
+ uint32 default_timeout) {
+ ipc->return_info.win32_result = ERROR_ACCESS_DENIED;
+ ipc->return_info.handle = INVALID_HANDLE_VALUE;
+
+ std::vector<base::string16> paths;
+ std::vector<base::string16> innerpaths;
+ base::SplitString(*name, '/', &paths);
+
+ for (std::vector<base::string16>::const_iterator iter = paths.begin();
+ iter != paths.end(); ++iter) {
+ base::SplitString(*iter, '\\', &innerpaths);
+ for (std::vector<base::string16>::const_iterator iter2 = innerpaths.begin();
+ iter2 != innerpaths.end(); ++iter2) {
+ if (*iter2 == L"..")
+ return true;
+ }
+ }
+
+ const wchar_t* pipe_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ EvalResult eval = policy_base_->EvalPolicy(IPC_CREATENAMEDPIPEW_TAG,
+ params.GetBase());
+
+ // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to
+ // disable all string parsing and to send the string that follows it straight
+ // to the file system."
+ // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ // This ensures even if there is a path traversal in the pipe name, and it is
+ // able to get past the checks above, it will still not be allowed to escape
+ // our whitelisted namespace.
+ if (name->compare(0, 4, L"\\\\.\\") == 0)
+ name->replace(0, 4, L"\\\\\?\\");
+
+ HANDLE pipe;
+ DWORD ret = NamedPipePolicy::CreateNamedPipeAction(eval, *ipc->client_info,
+ *name, open_mode,
+ 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/sandbox/win/src/named_pipe_dispatcher.h b/sandbox/win/src/named_pipe_dispatcher.h
new file mode 100644
index 0000000000..1c02199784
--- /dev/null
+++ b/sandbox/win/src/named_pipe_dispatcher.h
@@ -0,0 +1,42 @@
+// 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 "base/strings/string16.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() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateNamedPipeW() in the
+ // target.
+ bool CreateNamedPipe(IPCInfo* ipc,
+ base::string16* name,
+ uint32 open_mode,
+ uint32 pipe_mode,
+ uint32 max_instances,
+ uint32 out_buffer_size,
+ uint32 in_buffer_size,
+ uint32 default_timeout);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(NamedPipeDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
diff --git a/sandbox/win/src/named_pipe_interception.cc b/sandbox/win/src/named_pipe_interception.cc
new file mode 100644
index 0000000000..c62d0931d7
--- /dev/null
+++ b/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;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return INVALID_HANDLE_VALUE;
+
+ DWORD original_error = ::GetLastError();
+
+ // We don't support specific Security Attributes.
+ if (security_attributes)
+ return INVALID_HANDLE_VALUE;
+
+ 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/sandbox/win/src/named_pipe_interception.h b/sandbox/win/src/named_pipe_interception.h
new file mode 100644
index 0000000000..fdbee19766
--- /dev/null
+++ b/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/sandbox/win/src/named_pipe_policy.cc b/sandbox/win/src/named_pipe_policy.cc
new file mode 100644
index 0000000000..eee719e50a
--- /dev/null
+++ b/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 base::string16 &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/sandbox/win/src/named_pipe_policy.h b/sandbox/win/src/named_pipe_policy.h
new file mode 100644
index 0000000000..c904aa3618
--- /dev/null
+++ b/sandbox/win/src/named_pipe_policy.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_NAMED_PIPE_POLICY_H__
+#define SANDBOX_SRC_NAMED_PIPE_POLICY_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+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 base::string16 &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/sandbox/win/src/named_pipe_policy_test.cc b/sandbox/win/src/named_pipe_policy_test.cc
new file mode 100644
index 0000000000..813cf1f464
--- /dev/null
+++ b/sandbox/win/src/named_pipe_policy_test.cc
@@ -0,0 +1,140 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+
+SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t **argv) {
+ if (argc < 1 || argc > 2) {
+ 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;
+
+ // The second parameter allows us to enforce a whitelist for where the
+ // pipe should be in the object namespace after creation.
+ if (argc == 2) {
+ base::string16 handle_name;
+ if (GetHandleName(pipe, &handle_name)) {
+ if (handle_name.compare(0, wcslen(argv[1]), argv[1]) != 0)
+ return SBOX_TEST_FAILED;
+ } else {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ OVERLAPPED overlapped = {0};
+ overlapped.hEvent = ::CreateEvent(NULL, TRUE, TRUE, NULL);
+ BOOL result = ::ConnectNamedPipe(pipe, &overlapped);
+
+ 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.
+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"));
+
+ // On XP, the sandbox can create a pipe without any help but it fails on
+ // Vista+, this is why we do not test the "denied" case.
+ if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) {
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh"));
+ }
+}
+
+// Tests if we can create a pipe with a path traversal in the sandbox.
+TEST(NamedPipePolicyTest, CreatePipeTraversal) {
+ TestRunner runner;
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ // On XP, the sandbox can create a pipe without any help but it fails on
+ // Vista+, this is why we do not test the "denied" case.
+ if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) {
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\..\\bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/../bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\../bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/..\\bleh"));
+ }
+}
+
+// This tests that path canonicalization is actually disabled if we use \\?\
+// syntax.
+TEST(NamedPipePolicyTest, CreatePipeCanonicalization) {
+ // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to
+ // disable all string parsing and to send the string that follows it straight
+ // to the file system."
+ // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ const wchar_t* argv[2] = { L"\\\\?\\pipe\\test\\..\\bleh",
+ L"\\Device\\NamedPipe\\test" };
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ NamedPipe_Create(2, const_cast<wchar_t**>(argv)));
+}
+
+// The same test as CreatePipe but this time using strict interceptions.
+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"));
+
+ // On XP, the sandbox can create a pipe without any help but it fails on
+ // Vista+, this is why we do not test the "denied" case.
+ if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) {
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh"));
+ }
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h
new file mode 100644
index 0000000000..40b29c6beb
--- /dev/null
+++ b/sandbox/win/src/nt_internals.h
@@ -0,0 +1,690 @@
+// 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.
+
+// 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_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
+#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
+#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL)
+#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
+
+#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
+#define OBJ_OPENIF 0x00000080L
+
+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
+
+// Create/open result values. These are the disposition values returned on the
+// io status information.
+#define FILE_SUPERSEDED 0x00000000
+#define FILE_OPENED 0x00000001
+#define FILE_CREATED 0x00000002
+#define FILE_OVERWRITTEN 0x00000003
+#define FILE_EXISTS 0x00000004
+#define FILE_DOES_NOT_EXIST 0x00000005
+
+typedef NTSTATUS (WINAPI *NtCreateFileFunction)(
+ OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ 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 SIZE_T MemoryInformationLength,
+ OUT PSIZE_T 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 void* (__cdecl *memcpyFunction)(
+ IN void* dest,
+ IN const void* src,
+ IN size_t count);
+
+typedef NTSTATUS (WINAPI *RtlAnsiStringToUnicodeStringFunction)(
+ IN OUT PUNICODE_STRING DestinationString,
+ IN PANSI_STRING SourceString,
+ 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);
+
+typedef enum _EVENT_TYPE {
+ NotificationEvent,
+ SynchronizationEvent
+} EVENT_TYPE, *PEVENT_TYPE;
+
+typedef NTSTATUS (WINAPI* NtCreateDirectoryObjectFunction) (
+ PHANDLE DirectoryHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NtOpenDirectoryObjectFunction) (
+ PHANDLE DirectoryHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS (WINAPI* NtQuerySymbolicLinkObjectFunction) (
+ HANDLE LinkHandle,
+ PUNICODE_STRING LinkTarget,
+ PULONG ReturnedLength);
+
+typedef NTSTATUS (WINAPI* NtOpenSymbolicLinkObjectFunction) (
+ PHANDLE LinkHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+#define DIRECTORY_QUERY 0x0001
+#define DIRECTORY_TRAVERSE 0x0002
+#define DIRECTORY_CREATE_OBJECT 0x0004
+#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008
+#define DIRECTORY_ALL_ACCESS 0x000F
+
+typedef NTSTATUS (WINAPI* NtCreateLowBoxToken)(
+ OUT PHANDLE token,
+ IN HANDLE original_handle,
+ IN ACCESS_MASK access,
+ IN POBJECT_ATTRIBUTES object_attribute,
+ IN PSID appcontainer_sid,
+ IN DWORD capabilityCount,
+ IN PSID_AND_ATTRIBUTES capabilities,
+ IN DWORD handle_count,
+ IN PHANDLE handles);
+
+typedef NTSTATUS(WINAPI *NtSetInformationProcess)(
+ IN HANDLE process_handle,
+ IN ULONG info_class,
+ IN PVOID process_information,
+ IN ULONG information_length);
+
+struct PROCESS_ACCESS_TOKEN {
+ HANDLE token;
+ HANDLE thread;
+};
+
+const unsigned int NtProcessInformationAccessToken = 9;
+
+#endif // SANDBOX_WIN_SRC_NT_INTERNALS_H__
+
diff --git a/sandbox/win/src/policy_broker.cc b/sandbox/win/src/policy_broker.cc
new file mode 100644
index 0000000000..dc5e18c28b
--- /dev/null
+++ b/sandbox/win/src/policy_broker.cc
@@ -0,0 +1,116 @@
+// 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);
+ INIT_GLOBAL_RTL(memcpy);
+
+#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/sandbox/win/src/policy_broker.h b/sandbox/win/src/policy_broker.h
new file mode 100644
index 0000000000..1c5cc26c23
--- /dev/null
+++ b/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/sandbox/win/src/policy_engine_opcodes.cc b/sandbox/win/src/policy_engine_opcodes.cc
new file mode 100644
index 0000000000..24ba119195
--- /dev/null
+++ b/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 uint32 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,
+ uint32 match,
+ uint32 options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, UINT32_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);
+ uint32 value_uint32 = 0;
+ if (param->Get(&value_uint32)) {
+ uint32 match_uint32 = 0;
+ opcode->GetArgument(0, &match_uint32);
+ return (match_uint32 != value_uint32)? 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 OpNumberMatchRange
+// Requires a uint32 in selected_param.
+// Argument 0 is the stored lower bound to match.
+// Argument 1 is the stored upper bound to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberMatchRange(int16 selected_param,
+ uint32 lower_bound,
+ uint32 upper_bound,
+ uint32 options) {
+ if (lower_bound > upper_bound) {
+ return NULL;
+ }
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_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_NUMBER_MATCH_RANGE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(context);
+ uint32 value = 0;
+ if (!param->Get(&value)) return EVAL_ERROR;
+
+ uint32 lower_bound = 0;
+ uint32 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 OpNumberAndMatch:
+// Requires a uint32 in selected_param.
+// Argument 0 is the stored number to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberAndMatch(int16 selected_param,
+ uint32 match,
+ uint32 options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_AND_MATCH, options, selected_param);
+ if (NULL == opcode) return NULL;
+ opcode->SetArgument(0, match);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_AND_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ UNREFERENCED_PARAMETER(context);
+ uint32 value = 0;
+ if (!param->Get(&value)) return EVAL_ERROR;
+
+ uint32 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;
+ }
+
+ BOOLEAN 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->SetOptions(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_NUMBER_MATCH_RANGE, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_AND_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_ACTION, this, parameters, match);
+ default:
+ return EVAL_ERROR;
+ }
+}
+
+#undef OPCODE_EVAL
+
+} // namespace sandbox
diff --git a/sandbox/win/src/policy_engine_opcodes.h b/sandbox/win/src/policy_engine_opcodes.h
new file mode 100644
index 0000000000..17d1764b1c
--- /dev/null
+++ b/sandbox/win/src/policy_engine_opcodes.h
@@ -0,0 +1,386 @@
+// 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 "base/basictypes.h"
+#include "base/numerics/safe_conversions.h"
+#include "sandbox/win/src/policy_engine_params.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_NUMBER_MATCH_RANGE, // Match a 32-bit integer as a <= n <= b.
+ OP_NUMBER_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 {
+ static_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) {
+ static_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(uint32 options) {
+ options_ = base::checked_cast<uint16>(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_;
+ // TODO(cpu): Making |options_| a uint32 would avoid casting, but causes test
+ // failures. Somewhere code is relying on the size of this struct.
+ // http://crbug.com/420296
+ uint16 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 uint32 or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the number to compare against the selected_param.
+ PolicyOpcode* MakeOpNumberMatch(int16 selected_param,
+ uint32 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 OpNumberMatchRange opcode using the memory passed in the ctor.
+ // selected_param: index of the input argument. It must be a uint32 or the
+ // evaluation result will generate a EVAL_ERROR.
+ // lower_bound, upper_bound: the range to compare against selected_param.
+ PolicyOpcode* MakeOpNumberMatchRange(int16 selected_param,
+ uint32 lower_bound,
+ uint32 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 OpNumberAndMatch opcode using the raw memory passed in the ctor.
+ // selected_param: index of the input argument. It must be uint32 or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the value to bitwise AND against selected_param.
+ PolicyOpcode* MakeOpNumberAndMatch(int16 selected_param,
+ uint32 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/sandbox/win/src/policy_engine_params.h b/sandbox/win/src/policy_engine_params.h
new file mode 100644
index 0000000000..5b3c5ef11c
--- /dev/null
+++ b/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(uint32* destination) const {
+ if (real_type_ != UINT32_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<uint32>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match void* fail.
+ bool Get(const void** destination) const {
+ if (real_type_ != VOIDPTR_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 (real_type_ != WCHAR_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<const wchar_t*>();
+ return true;
+ }
+
+ // False if the parameter is not properly initialized.
+ bool IsValid() const {
+ return real_type_ != INVALID_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<uint32> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address)
+ : ParameterSet(UINT32_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/sandbox/win/src/policy_engine_processor.cc b/sandbox/win/src/policy_engine_processor.cc
new file mode 100644
index 0000000000..7ca25b2ed2
--- /dev/null
+++ b/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/sandbox/win/src/policy_engine_processor.h b/sandbox/win/src/policy_engine_processor.h
new file mode 100644
index 0000000000..9e416bd35f
--- /dev/null
+++ b/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 kStopOnErrors & kRankedEval.
+//
+// Stop evaluating as soon as an error is encountered.
+const uint32 kStopOnErrors = 1;
+// Ignore all non fatal opcode evaluation errors.
+const uint32 kIgnoreErrors = 2;
+// Short-circuit evaluation: Only evaluate until opcode group that
+// evaluated to true has been found.
+const uint32 kShortEval = 4;
+// Discussed briefly at the policy design meeting. It will evaluate
+// all rules and then return the 'best' rule that evaluated true.
+const uint32 kRankedEval = 8;
+
+// 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/sandbox/win/src/policy_engine_unittest.cc b/sandbox/win/src/policy_engine_unittest.cc
new file mode 100644
index 0000000000..325a101dac
--- /dev/null
+++ b/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;
+
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ uint32 creation_mode = OPEN_EXISTING;
+ uint32 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/sandbox/win/src/policy_low_level.cc b/sandbox/win/src/policy_low_level.cc
new file mode 100644
index 0000000000..739321ca07
--- /dev/null
+++ b/sandbox/win/src/policy_low_level.cc
@@ -0,0 +1,356 @@
+// 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_low_level.h"
+
+#include <string>
+#include <map>
+
+#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 {
+
+LowLevelPolicy::LowLevelPolicy(PolicyGlobal* policy_store)
+ : policy_store_(policy_store) {
+}
+
+// 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, base::string16* 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.
+ base::string16 fragment; // accumulates the non-wildcard part.
+
+ 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,
+ uint32 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_->MakeOpNumberAndMatch(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/sandbox/win/src/policy_low_level.h b/sandbox/win/src/policy_low_level.h
new file mode 100644
index 0000000000..6a62631311
--- /dev/null
+++ b/sandbox/win/src/policy_low_level.h
@@ -0,0 +1,184 @@
+// 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 "base/strings/string16.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+
+// 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;
+static_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);
+
+ // 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,
+ uint32 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, base::string16* fragment);
+
+ // Loop over all generated opcodes and copy them to increasing memory
+ // addresses from opcode_start and copy the extra data (strings usually) into
+ // 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/sandbox/win/src/policy_low_level_unittest.cc b/sandbox/win/src/policy_low_level_unittest.cc
new file mode 100644
index 0000000000..4081a5885d
--- /dev/null
+++ b/sandbox/win/src/policy_low_level_unittest.cc
@@ -0,0 +1,618 @@
+// 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());
+
+ const 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());
+
+ const 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());
+
+ const 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());
+
+ const 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());
+
+ const wchar_t* filename = NULL;
+ uint32 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());
+
+ const wchar_t* filename = NULL;
+ uint32 access = 0;
+ uint32 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());
+
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ uint32 creation_mode = OPEN_EXISTING;
+ uint32 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_NUMBER_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.
+
+ const wchar_t* filename = L"";
+ uint32 creation_mode = OPEN_EXISTING;
+ uint32 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());
+
+ const 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/sandbox/win/src/policy_opcodes_unittest.cc b/sandbox/win/src/policy_opcodes_unittest.cc
new file mode 100644
index 0000000000..c999348fa8
--- /dev/null
+++ b/sandbox/win/src/policy_opcodes_unittest.cc
@@ -0,0 +1,369 @@
+// 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 {
+
+const size_t kOpcodeMemory = 1024;
+
+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;
+ uint32 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 uint32:
+ uint32 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[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // This opcode always evaluates to true.
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_NE(nullptr, op1);
+ 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);
+ ASSERT_NE(nullptr, op2);
+ 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);
+ ASSERT_NE(nullptr, op3);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL));
+ PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval);
+ ASSERT_NE(nullptr, op4);
+ EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL));
+
+ // Test that we clear the match context
+ PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext);
+ ASSERT_NE(nullptr, op5);
+ 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[kOpcodeMemory];
+ 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_NE(nullptr, 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_EQ(nullptr, 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[kOpcodeMemory];
+ 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_NE(nullptr, 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_EQ(nullptr, op1);
+}
+
+TEST(PolicyEngineTest, IntegerOpcodes) {
+ const wchar_t* txt = L"abcdef";
+ uint32 num1 = 42;
+ uint32 num2 = 113377;
+
+ ParameterSet pp_wrong1 = ParamPickerMake(txt);
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+ ParameterSet pp_num2 = ParamPickerMake(num2);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // Test basic match for uint32s 42 == 42 and 42 != 113377.
+ PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, 42UL, kPolNone);
+ ASSERT_NE(nullptr, op_m42);
+ 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);
+ ASSERT_NE(nullptr, op_vp_null);
+ 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.MakeOpNumberMatchRange(0, 41, 43, kPolNone);
+ ASSERT_NE(nullptr, op_range1);
+ 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[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ uint32 num1 = 0x10100702;
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+
+ PolicyOpcode* op_and1 =
+ opcode_maker.MakeOpNumberAndMatch(0, 0x00100000, kPolNone);
+ ASSERT_NE(nullptr, op_and1);
+ EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL));
+ PolicyOpcode* op_and2 =
+ opcode_maker.MakeOpNumberAndMatch(0, 0x00000001, kPolNone);
+ ASSERT_NE(nullptr, op_and2);
+ 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[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+ ASSERT_NE(nullptr, op1);
+
+ // 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);
+ ASSERT_NE(nullptr, op3);
+ 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);
+ ASSERT_NE(nullptr, op4);
+ 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);
+ ASSERT_NE(nullptr, op4b);
+ 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);
+ ASSERT_NE(nullptr, op5);
+ 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);
+ ASSERT_NE(nullptr, op5b);
+ EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(24, mc1.position);
+
+ // 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);
+ ASSERT_NE(nullptr, op6);
+ EXPECT_EQ(EVAL_FALSE, op6->Evaluate(&pp_tc1, 1, &mc1));
+
+ // Testing matching against 'g' which should be the last char.
+ MatchContext mc2;
+ PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op7);
+ EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2));
+ EXPECT_EQ(37, mc2.position);
+
+ // 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));
+ EXPECT_EQ(37, mc2.position);
+}
+
+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[kOpcodeMemory];
+ 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);
+ ASSERT_NE(nullptr, op1s);
+ PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward,
+ CASE_INSENSITIVE,
+ kPolNone);
+ ASSERT_NE(nullptr, op1i);
+ 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[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+ ASSERT_NE(nullptr, op1);
+ EXPECT_TRUE(op1->IsAction());
+ EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1));
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/policy_params.h b/sandbox/win/src/policy_params.h
new file mode 100644
index 0000000000..e051d2b59d
--- /dev/null
+++ b/sandbox/win/src/policy_params.h
@@ -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.
+
+#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(DISPOSITION)
+ 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)
+
+static_assert(OpenFile::NAME == static_cast<int>(FileName::NAME),
+ "to simplify fs policies");
+static_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/sandbox/win/src/policy_target.cc b/sandbox/win/src/policy_target.cc
new file mode 100644
index 0000000000..fb464cc0cb
--- /dev/null
+++ b/sandbox/win/src/policy_target.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/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;
+ // 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/sandbox/win/src/policy_target.h b/sandbox/win/src/policy_target.h
new file mode 100644
index 0000000000..8a2b437095
--- /dev/null
+++ b/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/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc
new file mode 100644
index 0000000000..4395a4f0d2
--- /dev/null
+++ b/sandbox/win/src/policy_target_test.cc
@@ -0,0 +1,412 @@
+// 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/memory/shared_memory.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.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_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);
+ PROCESS_INFORMATION temp_process_info = {};
+ // Note: CreateProcessW() can write to its lpCommandLine, don't pass a
+ // raw string literal.
+ base::string16 writable_cmdline_str(L"foo.exe");
+ if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], NULL, NULL, FALSE,
+ 0, NULL, NULL, &startup_info, &temp_process_info))
+ return SBOX_TEST_SUCCEEDED;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+ 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);
+
+ base::string16 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);
+ PROCESS_INFORMATION temp_process_info = {};
+ result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
+ &temp_process_info);
+ base::string16 desktop_name = policy->GetAlternateDesktop();
+ policy->Release();
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ 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);
+
+ base::string16 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);
+ PROCESS_INFORMATION temp_process_info = {};
+ result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
+ &temp_process_info);
+ base::string16 desktop_name = policy->GetAlternateDesktop();
+ policy->Release();
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ ASSERT_FALSE(desktop_name.empty());
+
+ // Make sure there is a backslash, for the window station name.
+ EXPECT_NE(desktop_name.find_first_of(L'\\'), base::string16::npos);
+
+ // Isolate the desktop name.
+ desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
+
+ 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();
+}
+
+// Launches the app in the sandbox and share a handle with it. The app should
+// be able to use the handle.
+TEST(PolicyTargetTest, ShareHandleTest) {
+ // The way we share handles via STARTUPINFOEX does not work on XP.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return;
+
+ BrokerServices* broker = GetBroker();
+ ASSERT_TRUE(broker != NULL);
+
+ base::StringPiece contents = "Hello World";
+ std::string name = "TestSharedMemory";
+ base::SharedMemoryCreateOptions options;
+ options.size = contents.size();
+ options.share_read_only = true;
+ options.name_deprecated = &name;
+ base::SharedMemory writable_shmem;
+ ASSERT_TRUE(writable_shmem.Create(options));
+ ASSERT_TRUE(writable_shmem.Map(options.size));
+ memcpy(writable_shmem.memory(), contents.data(), contents.size());
+
+ base::SharedMemory read_only_view;
+ ASSERT_TRUE(read_only_view.Open(name, true));
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+
+ TargetPolicy* policy = broker->CreatePolicy();
+ void* shared_handle = policy->AddHandleToShare(
+ read_only_view.handle());
+
+ base::string16 arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 shared_memory_handle ";
+ arguments += base::UintToString16(
+ reinterpret_cast<unsigned int>(shared_handle));
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ result = broker->SpawnTarget(prog_name, arguments.c_str(), policy,
+ &temp_process_info);
+ policy->Release();
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(WAIT_TIMEOUT,
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/process_mitigations.cc b/sandbox/win/src/process_mitigations.cc
new file mode 100644
index 0000000000..d187c55e3e
--- /dev/null
+++ b/sandbox/win/src/process_mitigations.cc
@@ -0,0 +1,331 @@
+// 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 <algorithm>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/restricted_token_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;
+
+ 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 (version >= base::win::VERSION_WIN7 &&
+ (flags & MITIGATION_HARDEN_TOKEN_IL_POLICY)) {
+ DWORD error = HardenProcessIntegrityLevelPolicy();
+ if ((error != ERROR_SUCCESS) && (error != ERROR_ACCESS_DENIED))
+ 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
+ static const int MEM_EXECUTE_OPTION_ENABLE = 1;
+ static const int MEM_EXECUTE_OPTION_DISABLE = 2;
+ static const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4;
+ static 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) {
+ 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_BOTTOM_UP_ASLR |
+ MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HEAP_TERMINATE);
+ }
+
+ // Windows 7.
+ 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_EXTENSION_DLL_DISABLE |
+ MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HARDEN_TOKEN_IL_POLICY));
+}
+
+bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) {
+ // These mitigations cannot be enabled prior to startup.
+ return !(flags & (MITIGATION_STRICT_HANDLE_CHECKS |
+ MITIGATION_DLL_SEARCH_ORDER));
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/process_mitigations.h b/sandbox/win/src/process_mitigations.h
new file mode 100644
index 0000000000..9039ad677b
--- /dev/null
+++ b/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/sandbox/win/src/process_mitigations_test.cc b/sandbox/win/src/process_mitigations_test.cc
new file mode 100644
index 0000000000..4d2e9c6e95
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_test.cc
@@ -0,0 +1,248 @@
+// 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/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 (!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;
+
+ 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;
+ }
+
+ static const int MEM_EXECUTE_OPTION_ENABLE = 1;
+ static const int MEM_EXECUTE_OPTION_DISABLE = 2;
+ static const int MEM_EXECUTE_OPTION_ATL7_THUNK_EMULATION = 4;
+ static 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 (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
+
+SBOX_TESTS_COMMAND int CheckWin8Lockdown(int argc, wchar_t **argv) {
+ get_process_mitigation_policy =
+ reinterpret_cast<GetProcessMitigationPolicyFunction>(
+ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
+ "GetProcessMitigationPolicy"));
+ if (!get_process_mitigation_policy)
+ return SBOX_TEST_NOT_FOUND;
+
+ if (!CheckWin8Win32CallPolicy())
+ return SBOX_TEST_FIRST_ERROR;
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation on
+// the target process causes the launch to fail in process initialization.
+// The test process itself links against user32/gdi32.
+TEST(ProcessMitigationsTest, CheckWin8Win32KLockDownFailure) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE),
+ SBOX_ALL_OK);
+ EXPECT_NE(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown"));
+}
+
+// This test validates that setting the MITIGATION_WIN32K_DISABLE mitigation
+// along with the policy to fake user32 and gdi32 initialization successfully
+// launches the target process.
+// The test process itself links against user32/gdi32.
+TEST(ProcessMitigationsTest, CheckWin8Win32KLockDownSuccess) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_WIN32K_DISABLE),
+ SBOX_ALL_OK);
+ EXPECT_EQ(policy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN,
+ sandbox::TargetPolicy::FAKE_USER_GDI_INIT, NULL),
+ sandbox::SBOX_ALL_OK);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckWin8Lockdown"));
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/process_mitigations_win32k_dispatcher.cc b/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
new file mode 100644
index 0000000000..e426084f86
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
@@ -0,0 +1,57 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_dispatcher.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+namespace sandbox {
+
+ProcessMitigationsWin32KDispatcher::ProcessMitigationsWin32KDispatcher(
+ PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+}
+
+bool ProcessMitigationsWin32KDispatcher::SetupService(
+ InterceptionManager* manager, int service) {
+ if (!(policy_base_->GetProcessMitigations() &
+ sandbox::MITIGATION_WIN32K_DISABLE)) {
+ return false;
+ }
+
+ switch (service) {
+ case IPC_GDI_GDIDLLINITIALIZE_TAG: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GdiDllInitialize,
+ GDIINITIALIZE_ID, 12)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IPC_GDI_GETSTOCKOBJECT_TAG: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetStockObject,
+ GETSTOCKOBJECT_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IPC_USER_REGISTERCLASSW_TAG: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", RegisterClassW,
+ REGISTERCLASSW_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ default:
+ break;
+ }
+ return false;
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/process_mitigations_win32k_dispatcher.h b/sandbox/win/src/process_mitigations_win32k_dispatcher.h
new file mode 100644
index 0000000000..2e1e1a802f
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_win32k_dispatcher.h
@@ -0,0 +1,31 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class sets up intercepts for the Win32K lockdown policy which is set
+// on Windows 8 and beyond.
+class ProcessMitigationsWin32KDispatcher : public Dispatcher {
+ public:
+ explicit ProcessMitigationsWin32KDispatcher(PolicyBase* policy_base);
+ ~ProcessMitigationsWin32KDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+ private:
+ PolicyBase* policy_base_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMitigationsWin32KDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
diff --git a/sandbox/win/src/process_mitigations_win32k_interception.cc b/sandbox/win/src/process_mitigations_win32k_interception.cc
new file mode 100644
index 0000000000..ee24fbf434
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_win32k_interception.cc
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+namespace sandbox {
+
+BOOL WINAPI TargetGdiDllInitialize(
+ GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason) {
+ return TRUE;
+}
+
+HGDIOBJ WINAPI TargetGetStockObject(
+ GetStockObjectFunction orig_get_stock_object,
+ int object) {
+ return reinterpret_cast<HGDIOBJ>(NULL);
+}
+
+ATOM WINAPI TargetRegisterClassW(
+ RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class) {
+ return TRUE;
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/process_mitigations_win32k_interception.h b/sandbox/win/src/process_mitigations_win32k_interception.h
new file mode 100644
index 0000000000..bf7b551227
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_win32k_interception.h
@@ -0,0 +1,46 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+typedef BOOL (WINAPI* GdiDllInitializeFunction) (
+ HANDLE dll,
+ DWORD reason,
+ LPVOID reserved);
+
+typedef HGDIOBJ (WINAPI *GetStockObjectFunction) (int object);
+
+typedef ATOM (WINAPI *RegisterClassWFunction) (const WNDCLASS* wnd_class);
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize(
+ GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject(
+ GetStockObjectFunction orig_get_stock_object,
+ int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW(
+ RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+
diff --git a/sandbox/win/src/process_mitigations_win32k_policy.cc b/sandbox/win/src/process_mitigations_win32k_policy.cc
new file mode 100644
index 0000000000..af18c5413c
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_win32k_policy.cc
@@ -0,0 +1,24 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+
+namespace sandbox {
+
+bool ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule rule(FAKE_SUCCESS);
+ if (!policy->AddRule(IPC_GDI_GDIDLLINITIALIZE_TAG, &rule))
+ return false;
+ if (!policy->AddRule(IPC_GDI_GETSTOCKOBJECT_TAG, &rule))
+ return false;
+ if (!policy->AddRule(IPC_USER_REGISTERCLASSW_TAG, &rule))
+ return false;
+ return true;
+}
+
+} // namespace sandbox
+
diff --git a/sandbox/win/src/process_mitigations_win32k_policy.h b/sandbox/win/src/process_mitigations_win32k_policy.h
new file mode 100644
index 0000000000..078ed2bee1
--- /dev/null
+++ b/sandbox/win/src/process_mitigations_win32k_policy.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+
+#include "base/basictypes.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum EvalResult;
+
+// This class centralizes most of the knowledge related to the process
+// mitigations Win32K lockdown policy.
+class ProcessMitigationsWin32KLockdownPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for the Win32K process mitigation policy.
+ // name is the object name, semantics is the desired semantics for the
+ // open or create and policy is the policy generator to which the rules are
+ // going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+
+
diff --git a/sandbox/win/src/process_policy_test.cc b/sandbox/win/src/process_policy_test.cc
new file mode 100644
index 0000000000..44effa3635
--- /dev/null
+++ b/sandbox/win/src/process_policy_test.cc
@@ -0,0 +1,385 @@
+// 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.
+base::string16 MakeFullPathToSystem32(const wchar_t* name) {
+ wchar_t windows_path[MAX_PATH] = {0};
+ ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH);
+ base::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 base::string16& exe,
+ const base::string16& command) {
+ base::win::ScopedProcessInformation pi;
+ STARTUPINFOW si = {sizeof(si)};
+
+ const wchar_t *exe_name = NULL;
+ if (!exe.empty())
+ exe_name = exe.c_str();
+
+ base::string16 writable_command = command;
+
+ // Create the process with the unicode version of the API.
+ sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED;
+ PROCESS_INFORMATION temp_process_info = {};
+ if (::CreateProcessW(exe_name,
+ command.empty() ? NULL : &writable_command[0],
+ NULL,
+ NULL,
+ FALSE,
+ 0,
+ NULL,
+ NULL,
+ &si,
+ &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret1 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret1 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret1 = sandbox::SBOX_TEST_FAILED;
+ }
+ }
+
+ 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 =
+ base::SysWideToMultiByte(command.c_str(), CP_UTF8);
+ if (::CreateProcessA(
+ exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str() : NULL,
+ command.empty() ? NULL : &narrow_cmd_line[0],
+ NULL, NULL, FALSE, 0, NULL, NULL, &sia, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret2 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret2 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret2 = sandbox::SBOX_TEST_FAILED;
+ }
+ }
+
+ 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;
+ }
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
+
+ // TEST 1: Try with the path in the app_name.
+ return CreateProcessHelper(path, base::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;
+ }
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
+
+ // TEST 2: Try with the path in the cmd_line.
+ base::string16 cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\"";
+ return CreateProcessHelper(base::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(base::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.
+ base::string16 system32 = MakeFullPathToSystem32(L"");
+ wchar_t current_directory[MAX_PATH + 1];
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ if (!ret)
+ return SBOX_TEST_FIRST_ERROR;
+ if (ret >= MAX_PATH)
+ return SBOX_TEST_FAILED;
+
+ current_directory[ret] = L'\\';
+ current_directory[ret+1] = L'\0';
+ if (!::SetCurrentDirectory(system32.c_str())) {
+ return SBOX_TEST_SECOND_ERROR;
+ }
+
+ const int result4 = CreateProcessHelper(argv[0], base::string16());
+ return ::SetCurrentDirectory(current_directory) ? result4 : SBOX_TEST_FAILED;
+}
+
+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;
+ }
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
+
+ // TEST 5: Try with the path in the cmd_line and arguments.
+ base::string16 cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\" /I";
+ return CreateProcessHelper(base::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.
+ base::string16 cmd_line = argv[0];
+ cmd_line += L" /I";
+ return CreateProcessHelper(base::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;
+
+ base::string16 path = MakeFullPathToSystem32(argv[0]);
+
+ STARTUPINFOW si = {sizeof(si)};
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessW(path.c_str(), NULL, NULL, NULL, FALSE, CREATE_SUSPENDED,
+ NULL, NULL, &si, &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+
+ 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;
+ base::string16 exe_path = MakeFullPathToSystem32(L"findstr.exe");
+ base::string16 system32 = MakeFullPathToSystem32(L"");
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ 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;
+ base::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);
+ base::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);
+ base::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);
+ base::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/sandbox/win/src/process_thread_dispatcher.cc b/sandbox/win/src/process_thread_dispatcher.cc
new file mode 100644
index 0000000000..ca17d4920a
--- /dev/null
+++ b/sandbox/win/src/process_thread_dispatcher.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/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.
+base::string16 GetPathFromCmdLine(const base::string16 &cmd_line) {
+ base::string16 exe_name;
+ // Check if it starts with '"'.
+ if (cmd_line[0] == L'\"') {
+ // Find the position of the second '"', this terminates the path.
+ base::string16::size_type pos = cmd_line.find(L'\"', 1);
+ if (base::string16::npos == pos)
+ return cmd_line;
+ exe_name = cmd_line.substr(1, pos - 1);
+ } else {
+ // There is no '"', that means that the appname is terminated at the
+ // first space.
+ base::string16::size_type pos = cmd_line.find(L' ');
+ if (base::string16::npos == pos) {
+ // There is no space, the cmd_line contains only the app_name
+ exe_name = cmd_line;
+ } else {
+ 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 base::string16 &path) {
+ // A path is Relative if it's not a UNC path beginnning with \\ or a
+ // path beginning with a drive. (i.e. X:\)
+ if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
+ return false;
+ return true;
+}
+
+// Converts a relative path to an absolute path.
+bool ConvertToAbsolutePath(const base::string16& child_current_directory,
+ bool use_env_path, base::string16 *path) {
+ wchar_t file_buffer[MAX_PATH];
+ wchar_t *file_part = NULL;
+
+ // 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, UINT32_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenThread)
+ };
+
+ static const IPCCall open_process = {
+ {IPC_NTOPENPROCESS_TAG, UINT32_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcess)
+ };
+
+ static const IPCCall process_token = {
+ {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessToken)
+ };
+
+ static const IPCCall process_tokenex = {
+ {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, UINT32_TYPE, UINT32_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, kKerneldllName, CreateProcessW,
+ CREATE_PROCESSW_ID, 44) &&
+ INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
+ CREATE_PROCESSA_ID, 44);
+
+ default:
+ return false;
+ }
+}
+
+bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc,
+ uint32 desired_access,
+ uint32 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,
+ uint32 desired_access,
+ uint32 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,
+ uint32 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,
+ uint32 desired_access,
+ uint32 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, base::string16* name,
+ base::string16* cmd_line,
+ base::string16* cur_dir,
+ CountedBuffer* info) {
+ if (sizeof(PROCESS_INFORMATION) != info->Size())
+ return false;
+
+ // Check if there is an application name.
+ base::string16 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/sandbox/win/src/process_thread_dispatcher.h b/sandbox/win/src/process_thread_dispatcher.h
new file mode 100644
index 0000000000..2bb3b6ea6f
--- /dev/null
+++ b/sandbox/win/src/process_thread_dispatcher.h
@@ -0,0 +1,53 @@
+// 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 "base/strings/string16.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() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtOpenThread() in the target.
+ bool NtOpenThread(IPCInfo* ipc, uint32 desired_access, uint32 thread_id);
+
+ // Processes IPC requests coming from calls to NtOpenProcess() in the target.
+ bool NtOpenProcess(IPCInfo* ipc, uint32 desired_access, uint32 process_id);
+
+ // Processes IPC requests from calls to NtOpenProcessToken() in the target.
+ bool NtOpenProcessToken(IPCInfo* ipc, HANDLE process, uint32 desired_access);
+
+ // Processes IPC requests from calls to NtOpenProcessTokenEx() in the target.
+ bool NtOpenProcessTokenEx(IPCInfo* ipc,
+ HANDLE process,
+ uint32 desired_access,
+ uint32 attributes);
+
+ // Processes IPC requests coming from calls to CreateProcessW() in the target.
+ bool CreateProcessW(IPCInfo* ipc,
+ base::string16* name,
+ base::string16* cmd_line,
+ base::string16* cur_dir,
+ CountedBuffer* info);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
diff --git a/sandbox/win/src/process_thread_interception.cc b/sandbox/win/src/process_thread_interception.cc
new file mode 100644
index 0000000000..45926bc5f6
--- /dev/null
+++ b/sandbox/win/src/process_thread_interception.cc
@@ -0,0 +1,403 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/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;
+ }
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return FALSE;
+
+ DWORD original_error = ::GetLastError();
+
+ do {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ 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;
+ }
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return FALSE;
+
+ DWORD original_error = ::GetLastError();
+
+ do {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ 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;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/process_thread_interception.h b/sandbox/win/src/process_thread_interception.h
new file mode 100644
index 0000000000..31dc231543
--- /dev/null
+++ b/sandbox/win/src/process_thread_interception.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "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);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_INTERCEPTION_H__
diff --git a/sandbox/win/src/process_thread_policy.cc b/sandbox/win/src/process_thread_policy.cc
new file mode 100644
index 0000000000..065359161f
--- /dev/null
+++ b/sandbox/win/src/process_thread_policy.cc
@@ -0,0 +1,239 @@
+// 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)) {
+ 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)) {
+ 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)) {
+ 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)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::string16 &app_name,
+ const base::string16 &command_line,
+ PROCESS_INFORMATION* process_info) {
+ // The only action supported is ASK_BROKER which means create the process.
+ if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ scoped_ptr<wchar_t, base::FreeDeleter>
+ cmd_line(_wcsdup(command_line.c_str()));
+
+ BOOL should_give_full_access = (GIVE_ALLACCESS == eval_result);
+ if (!CreateProcessExWHelper(client_info.process, should_give_full_access,
+ 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/sandbox/win/src/process_thread_policy.h b/sandbox/win/src/process_thread_policy.h
new file mode 100644
index 0000000000..2871dcaa27
--- /dev/null
+++ b/sandbox/win/src/process_thread_policy.h
@@ -0,0 +1,83 @@
+// 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 "base/strings/string16.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 base::string16 &app_name,
+ const base::string16 &command_line,
+ PROCESS_INFORMATION* process_info);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
diff --git a/sandbox/win/src/registry_dispatcher.cc b/sandbox/win/src/registry_dispatcher.cc
new file mode 100644
index 0000000000..967fe652ad
--- /dev/null
+++ b/sandbox/win/src/registry_dispatcher.cc
@@ -0,0 +1,170 @@
+// 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 base::string16& name,
+ base::string16* 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, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtCreateKey)
+ };
+
+ static const IPCCall open_params = {
+ {IPC_NTOPENKEY_TAG, WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_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 ||
+ (base::win::GetVersion() == base::win::VERSION_VISTA &&
+ base::win::OSInfo::GetInstance()->version_type() ==
+ base::win::SUITE_SERVER))
+ result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20);
+ return result;
+ }
+
+ return false;
+}
+
+bool RegistryDispatcher::NtCreateKey(IPCInfo* ipc,
+ base::string16* name,
+ uint32 attributes,
+ HANDLE root,
+ uint32 desired_access,
+ uint32 title_index,
+ uint32 create_options) {
+ base::win::ScopedHandle root_handle;
+ base::string16 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,
+ base::string16* name,
+ uint32 attributes,
+ HANDLE root,
+ uint32 desired_access) {
+ base::win::ScopedHandle root_handle;
+ base::string16 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/sandbox/win/src/registry_dispatcher.h b/sandbox/win/src/registry_dispatcher.h
new file mode 100644
index 0000000000..83811a9cd6
--- /dev/null
+++ b/sandbox/win/src/registry_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_REGISTRY_DISPATCHER_H_
+#define SANDBOX_SRC_REGISTRY_DISPATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles registry-related IPC calls.
+class RegistryDispatcher : public Dispatcher {
+ public:
+ explicit RegistryDispatcher(PolicyBase* policy_base);
+ ~RegistryDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateKey in the target.
+ bool NtCreateKey(IPCInfo* ipc,
+ base::string16* name,
+ uint32 attributes,
+ HANDLE root,
+ uint32 desired_access,
+ uint32 title_index,
+ uint32 create_options);
+
+ // Processes IPC requests coming from calls to NtOpenKey in the target.
+ bool NtOpenKey(IPCInfo* ipc,
+ base::string16* name,
+ uint32 attributes,
+ HANDLE root,
+ uint32 desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_DISPATCHER_H_
diff --git a/sandbox/win/src/registry_interception.cc b/sandbox/win/src/registry_interception.cc
new file mode 100644
index 0000000000..4a1a846984
--- /dev/null
+++ b/sandbox/win/src/registry_interception.cc
@@ -0,0 +1,224 @@
+// 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/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 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;
+
+ uint32 desired_access_uint32 = desired_access;
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
+
+ wchar_t* full_name = NULL;
+
+ if (root_directory) {
+ ret = sandbox::AllocAndGetFullPath(root_directory, name, &full_name);
+ if (!NT_SUCCESS(ret) || NULL == full_name)
+ break;
+ params[OpenKey::NAME] = ParamPickerMake(full_name);
+ } else {
+ params[OpenKey::NAME] = ParamPickerMake(name);
+ }
+
+ bool query_broker = QueryBroker(IPC_NTCREATEKEY_TAG, params.GetBase());
+
+ if (full_name != NULL)
+ operator delete(full_name, NT_ALLOC);
+
+ if (!query_broker)
+ 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;
+
+ uint32 desired_access_uint32 = desired_access;
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
+
+ wchar_t* full_name = NULL;
+
+ if (root_directory) {
+ ret = sandbox::AllocAndGetFullPath(root_directory, name, &full_name);
+ if (!NT_SUCCESS(ret) || NULL == full_name)
+ break;
+ params[OpenKey::NAME] = ParamPickerMake(full_name);
+ } else {
+ params[OpenKey::NAME] = ParamPickerMake(name);
+ }
+
+ bool query_broker = QueryBroker(IPC_NTOPENKEY_TAG, params.GetBase());
+
+ if (full_name != NULL)
+ operator delete(full_name, NT_ALLOC);
+
+ if (!query_broker)
+ 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/sandbox/win/src/registry_interception.h b/sandbox/win/src/registry_interception.h
new file mode 100644
index 0000000000..c3cbde02e7
--- /dev/null
+++ b/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/sandbox/win/src/registry_policy.cc b/sandbox/win/src/registry_policy.cc
new file mode 100644
index 0000000000..b0f24a79ee
--- /dev/null
+++ b/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 uint32 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) {
+ base::string16 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.
+ uint32 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 base::string16 &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, NULL);
+ *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 base::string16 &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, NULL);
+ *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes,
+ client_info.process);
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/registry_policy.h b/sandbox/win/src/registry_policy.h
new file mode 100644
index 0000000000..69af8415d2
--- /dev/null
+++ b/sandbox/win/src/registry_policy.h
@@ -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.
+
+#ifndef SANDBOX_SRC_REGISTRY_POLICY_H__
+#define SANDBOX_SRC_REGISTRY_POLICY_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#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 base::string16 &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 base::string16 &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/sandbox/win/src/registry_policy_test.cc b/sandbox/win/src/registry_policy_test.cc
new file mode 100644
index 0000000000..d8ee34b06d
--- /dev/null
+++ b/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/sandbox/win/src/resolver.cc b/sandbox/win/src/resolver.cc
new file mode 100644
index 0000000000..6616fa52c3
--- /dev/null
+++ b/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/sandbox/win/src/resolver.h b/sandbox/win/src/resolver.h
new file mode 100644
index 0000000000..85f1e91990
--- /dev/null
+++ b/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/sandbox/win/src/resolver_32.cc b/sandbox/win/src/resolver_32.cc
new file mode 100644
index 0000000000..a591a8b1dd
--- /dev/null
+++ b/sandbox/win/src/resolver_32.cc
@@ -0,0 +1,92 @@
+// 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"
+
+// For placement new. This file must not depend on the CRT at runtime, but
+// placement operator new is inline.
+#include <new>
+
+#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) 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/sandbox/win/src/resolver_64.cc b/sandbox/win/src/resolver_64.cc
new file mode 100644
index 0000000000..8b2cc53c97
--- /dev/null
+++ b/sandbox/win/src/resolver_64.cc
@@ -0,0 +1,73 @@
+// 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"
+
+// For placement new. This file must not depend on the CRT at runtime, but
+// placement operator new is inline.
+#include <new>
+
+#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) 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/sandbox/win/src/restricted_token.cc b/sandbox/win/src/restricted_token.cc
new file mode 100644
index 0000000000..7ebef3de9a
--- /dev/null
+++ b/sandbox/win/src/restricted_token.cc
@@ -0,0 +1,481 @@
+// 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 {
+
+RestrictedToken::RestrictedToken()
+ : init_(false),
+ effective_token_(NULL),
+ integrity_level_(INTEGRITY_LEVEL_LAST) {
+}
+
+RestrictedToken::~RestrictedToken() {
+ if (effective_token_)
+ CloseHandle(effective_token_);
+}
+
+unsigned RestrictedToken::Init(const HANDLE effective_token) {
+ 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<base::string16> *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/sandbox/win/src/restricted_token.h b/sandbox/win/src/restricted_token.h
new file mode 100644
index 0000000000..565880e778
--- /dev/null
+++ b/sandbox/win/src/restricted_token.h
@@ -0,0 +1,193 @@
+// 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 "base/strings/string16.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/security_level.h"
+#include "sandbox/win/src/sid.h"
+
+// 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();
+ ~RestrictedToken();
+
+ // 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<base::string16> privilege_exceptions;
+ // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ // restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ unsigned DeleteAllPrivileges(
+ const std::vector<base::string16> *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/sandbox/win/src/restricted_token_unittest.cc b/sandbox/win/src/restricted_token_unittest.cc
new file mode 100644
index 0000000000..8186f9c77c
--- /dev/null
+++ b/sandbox/win/src/restricted_token_unittest.cc
@@ -0,0 +1,588 @@
+// 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<base::string16> 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 = 8192;
+ BYTE *memory = new BYTE[length];
+ 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 = 8192;
+ BYTE *memory = new BYTE[length];
+ 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);
+}
+
+// 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));
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/restricted_token_utils.cc b/sandbox/win/src/restricted_token_utils.cc
new file mode 100644
index 0000000000..5e06daa426
--- /dev/null
+++ b/sandbox/win/src/restricted_token_utils.cc
@@ -0,0 +1,408 @@
+// 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<base::string16> 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, 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};
+ PROCESS_INFORMATION temp_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,
+ &temp_process_info)) {
+ return ::GetLastError();
+ }
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
+ // Change the token of the main thread of the new process for the
+ // impersonation token with more rights.
+ {
+ 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.
+ base::string16 sddl = L"S:("; // SDDL for a SACL.
+ sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
+ sddl += L";;"; // No Ace Flags.
+ sddl += ace_access; // Add the ACE access.
+ 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);
+}
+
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return ERROR_SUCCESS;
+
+ DWORD last_error = 0;
+ DWORD length_needed = 0;
+
+ ::GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
+ NULL, 0, &length_needed);
+
+ last_error = ::GetLastError();
+ if (last_error != ERROR_INSUFFICIENT_BUFFER)
+ return last_error;
+
+ std::vector<char> security_desc_buffer(length_needed);
+ PSECURITY_DESCRIPTOR security_desc =
+ reinterpret_cast<PSECURITY_DESCRIPTOR>(&security_desc_buffer[0]);
+
+ if (!::GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
+ security_desc, length_needed,
+ &length_needed))
+ return ::GetLastError();
+
+ PACL sacl = NULL;
+ BOOL sacl_present = FALSE;
+ BOOL sacl_defaulted = FALSE;
+
+ if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present,
+ &sacl, &sacl_defaulted))
+ return ::GetLastError();
+
+ for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) {
+ PSYSTEM_MANDATORY_LABEL_ACE ace;
+
+ if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace))
+ && ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) {
+ ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP
+ | SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP;
+ break;
+ }
+ }
+
+ if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
+ security_desc))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD HardenProcessIntegrityLevelPolicy() {
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return ERROR_SUCCESS;
+
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return HardenTokenIntegrityLevelPolicy(token.Get());
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/restricted_token_utils.h b/sandbox/win/src/restricted_token_utils.h
new file mode 100644
index 0000000000..509feaf74b
--- /dev/null
+++ b/sandbox/win/src/restricted_token_utils.h
@@ -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.
+
+#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);
+
+// Returns the integrity level SDDL string associated with a given
+// IntegrityLevel value.
+const wchar_t* GetIntegrityLevelString(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);
+
+// Hardens the integrity level policy on a token. This is only valid on Win 7
+// and above. Specifically it sets the policy to block read and execute so
+// that a lower privileged process cannot open the token for impersonate or
+// duplicate permissions. This should limit potential security holes.
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token);
+
+// Hardens the integrity level policy on the current process. This is only
+// valid on Win 7 and above. Specifically it sets the policy to block read
+// and execute so that a lower privileged process cannot open the token for
+// impersonate or duplicate permissions. This should limit potential security
+// holes.
+DWORD HardenProcessIntegrityLevelPolicy();
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
diff --git a/sandbox/win/src/sandbox.cc b/sandbox/win/src/sandbox.cc
new file mode 100644
index 0000000000..984dfecec8
--- /dev/null
+++ b/sandbox/win/src/sandbox.cc
@@ -0,0 +1,48 @@
+// 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;
+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
+
+// Allows querying for whether the current process has been sandboxed.
+extern "C" bool __declspec(dllexport) IsSandboxedProcess() {
+ return sandbox::g_shared_section != NULL;
+}
diff --git a/sandbox/win/src/sandbox.h b/sandbox/win/src/sandbox.h
new file mode 100644
index 0000000000..e326194403
--- /dev/null
+++ b/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/sandbox/win/src/sandbox.vcproj b/sandbox/win/src/sandbox.vcproj
new file mode 100644
index 0000000000..f206e01a1f
--- /dev/null
+++ b/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/sandbox/win/src/sandbox_factory.h b/sandbox/win/src/sandbox_factory.h
new file mode 100644
index 0000000000..7a0280f908
--- /dev/null
+++ b/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/sandbox/win/src/sandbox_globals.cc b/sandbox/win/src/sandbox_globals.cc
new file mode 100644
index 0000000000..b4ab523921
--- /dev/null
+++ b/sandbox/win/src/sandbox_globals.cc
@@ -0,0 +1,18 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// The section for IPC and policy.
+SANDBOX_INTERCEPT HANDLE g_shared_section = NULL;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt = {};
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sandbox_nt_types.h b/sandbox/win/src/sandbox_nt_types.h
new file mode 100644
index 0000000000..a4a88bba7c
--- /dev/null
+++ b/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;
+ memcpyFunction memcpy;
+};
+
+// This is the value used for the ntdll level allocator.
+enum AllocationType {
+ NT_ALLOC,
+ NT_PAGE
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SANDBOX_NT_TYPES_H__
diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
new file mode 100644
index 0000000000..64fd1f1f6d
--- /dev/null
+++ b/sandbox/win/src/sandbox_nt_util.cc
@@ -0,0 +1,679 @@
+// 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;
+
+} // namespace sandbox
+
+namespace {
+
+#if defined(_WIN64)
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+
+ // Start with 1 GB above the source.
+ const size_t 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;
+
+ NTSTATUS ret;
+ int attempts = 0;
+ for (; attempts < 41; attempts++) {
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, zero_bits,
+ &actual_size, type, PAGE_READWRITE);
+ if (NT_SUCCESS(ret)) {
+ if (base < source ||
+ base >= reinterpret_cast<char*>(source) + 4 * kOneGB) {
+ // We won't be able to patch this dll.
+ VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size,
+ MEM_RELEASE));
+ return NULL;
+ }
+ break;
+ }
+
+ if (attempts == 30) {
+ // Try the first GB.
+ base = reinterpret_cast<char*>(source);
+ } else if (attempts == 40) {
+ // Try the highest available address.
+ base = NULL;
+ type |= MEM_TOP_DOWN;
+ }
+
+ // Try 100 MB higher.
+ base = reinterpret_cast<char*>(base) + 100 * 0x100000;
+ };
+
+ if (attempts == 41)
+ 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 {
+ g_nt.memcpy(destination, source, bytes);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+ return ret;
+}
+
+NTSTATUS AllocAndGetFullPath(HANDLE root,
+ wchar_t* path,
+ wchar_t** full_path) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(full_path);
+ DCHECK_NT(path);
+ *full_path = NULL;
+ OBJECT_NAME_INFORMATION* handle_name = NULL;
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ static NtQueryObjectFunction NtQueryObject = NULL;
+ if (!NtQueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ ULONG size = 0;
+ // Query the name information a first time to get the size of the name.
+ ret = NtQueryObject(root, ObjectNameInformation, NULL, 0, &size);
+
+ if (size) {
+ handle_name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(
+ new(NT_ALLOC) BYTE[size]);
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ ret = NtQueryObject(root, ObjectNameInformation, handle_name, size,
+ &size);
+ }
+
+ if (STATUS_SUCCESS != ret)
+ break;
+
+ // Space for path + '\' + name + '\0'.
+ size_t name_length = handle_name->ObjectName.Length +
+ (wcslen(path) + 2) * sizeof(wchar_t);
+ *full_path = new(NT_ALLOC) wchar_t[name_length/sizeof(wchar_t)];
+ if (NULL == *full_path)
+ break;
+ wchar_t* off = *full_path;
+ ret = CopyData(off, handle_name->ObjectName.Buffer,
+ handle_name->ObjectName.Length);
+ if (!NT_SUCCESS(ret))
+ break;
+ off += handle_name->ObjectName.Length / sizeof(wchar_t);
+ *off = L'\\';
+ off += 1;
+ ret = CopyData(off, path, wcslen(path) * sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+ off += wcslen(path);
+ *off = L'\0';
+ } while (false);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret)) {
+ if (*full_path) {
+ operator delete(*full_path, NT_ALLOC);
+ *full_path = NULL;
+ }
+ if (handle_name) {
+ operator delete(handle_name, NT_ALLOC);
+ handle_name = NULL;
+ }
+ }
+
+ 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) {
+ // PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable: 4509)
+ 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;
+#pragma warning(pop)
+}
+
+UNICODE_STRING* GetBackingFilePath(PVOID address) {
+ // We'll start with something close to max_path charactes for the name.
+ SIZE_T 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;
+
+ SIZE_T 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;
+
+ static const wchar_t kPathPrefix[] = { L'\\', L'?', L'?', L'\\'};
+
+ // Check if it starts with \\??\\. We don't support relative paths.
+ if (file_info->FileNameLength < sizeof(kPathPrefix) ||
+ file_info->FileNameLength > kuint16max)
+ return false;
+
+ if (file_info->FileName[0] != kPathPrefix[0] ||
+ file_info->FileName[1] != kPathPrefix[1] ||
+ file_info->FileName[2] != kPathPrefix[2] ||
+ file_info->FileName[3] != kPathPrefix[3])
+ return false;
+
+ return true;
+}
+
+} // namespace sandbox
+
+void* operator new(size_t size, sandbox::AllocationType type,
+ void* near_to) {
+ using namespace sandbox;
+
+ void* result = NULL;
+ if (NT_ALLOC == type) {
+ if (InitHeap()) {
+ // Use default flags for the allocation.
+ result = g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
+ }
+ } else if (NT_PAGE == type) {
+ result = AllocateNearTo(near_to, size);
+ } else {
+ NOTREACHED_NT();
+ }
+
+ // TODO: Returning NULL from operator new has undefined behavior, but
+ // the Allocate() functions called above can return NULL. Consider checking
+ // for NULL here and crashing or throwing.
+
+ return result;
+}
+
+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/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h
new file mode 100644
index 0000000000..83dd7c090e
--- /dev/null
+++ b/sandbox/win/src/sandbox_nt_util.h
@@ -0,0 +1,191 @@
+// 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 <intrin.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 CHECK_NT(condition) { (condition) ? (void)0 : __debugbreak(); }
+
+#define NOTREACHED_NT() DCHECK_NT(false)
+
+namespace sandbox {
+
+#if defined(_M_X64)
+#pragma intrinsic(_InterlockedCompareExchange)
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+
+#elif defined(_M_IX86)
+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));
+}
+
+#else
+#error Architecture not supported.
+
+#endif
+
+// 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);
+
+// Determine full path name from object root and path.
+NTSTATUS AllocAndGetFullPath(HANDLE root,
+ wchar_t* path,
+ wchar_t** full_path);
+
+// 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/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h
new file mode 100644
index 0000000000..54bb2a9096
--- /dev/null
+++ b/sandbox/win/src/sandbox_policy.h
@@ -0,0 +1,256 @@
+// 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 "base/strings/string16.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.
+ SUBSYS_WIN32K_LOCKDOWN // Win32K Lockdown related policy.
+ };
+
+ // 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.
+ FAKE_USER_GDI_INIT // Fakes user32 and gdi32 initialization. This can
+ // be used to allow the DLLs to load and initialize
+ // even if the process cannot access that subsystem.
+ };
+
+ // Increments the reference count of this object. The reference count must
+ // 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;
+
+ // Returns the initial token level.
+ virtual TokenLevel GetInitialTokenLevel() const = 0;
+
+ // Returns the lockdown token level.
+ virtual TokenLevel GetLockdownTokenLevel() const = 0;
+
+ // Sets the security level of the Job Object to which the target process will
+ // belong. This setting is permanent and cannot be changed once the target
+ // process is spawned. The job controls the global security settings which
+ // 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;
+
+ // Sets a hard limit on the size of the commit set for the sandboxed process.
+ // If the limit is reached, the process will be terminated with
+ // SBOX_FATAL_MEMORY_EXCEEDED (7012).
+ virtual ResultCode SetJobMemoryLimit(size_t memory_limit) = 0;
+
+ // Specifies the desktop on which the application is going to run. If the
+ // desktop does not exist, it will be created. If alternate_winstation is
+ // set to true, the desktop will be created on an alternate window station.
+ 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 base::string16 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;
+
+ // Returns the initial integrity level used.
+ virtual IntegrityLevel GetIntegrityLevel() const = 0;
+
+ // Sets the integrity level of the process in the sandbox. The integrity level
+ // will not take effect before you call LowerToken. User Interface Privilege
+ // Isolation is not affected by this setting and will remain off for the
+ // 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 LowBox token for sandboxed process. This is mutually exclusive
+ // with SetAppContainer method.
+ virtual ResultCode SetLowBox(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() const = 0;
+
+ // Sets the interceptions to operate in strict mode. By default, interceptions
+ // are performed in "relaxed" mode, where if something inside NTDLL.DLL is
+ // 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;
+
+ // Adds a handle that will be shared with the target process.
+ // Returns the handle which was actually shared with the target. This is
+ // achieved by duplicating the handle to ensure that it is inheritable by
+ // the target. The caller should treat this as an opaque value.
+ virtual void* AddHandleToShare(HANDLE handle) = 0;
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
new file mode 100644
index 0000000000..07a7d09581
--- /dev/null
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -0,0 +1,887 @@
+// 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 <sddl.h>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.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_mitigations_win32k_dispatcher.h"
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+#include "sandbox/win/src/process_thread_dispatcher.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/registry_dispatcher.h"
+#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/sandbox_utils.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;
+}
+
+HANDLE CreateLowBoxObjectDirectory(PSID lowbox_sid) {
+ DWORD session_id = 0;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id))
+ return NULL;
+
+ LPWSTR sid_string = NULL;
+ if (!::ConvertSidToStringSid(lowbox_sid, &sid_string))
+ return NULL;
+
+ base::string16 directory_path = base::StringPrintf(
+ L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls",
+ session_id, sid_string).c_str();
+ ::LocalFree(sid_string);
+
+ NtCreateDirectoryObjectFunction CreateObjectDirectory = NULL;
+ ResolveNTFunctionPtr("NtCreateDirectoryObject", &CreateObjectDirectory);
+
+ OBJECT_ATTRIBUTES obj_attr;
+ UNICODE_STRING obj_name;
+ sandbox::InitObjectAttribs(directory_path,
+ OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
+ NULL,
+ &obj_attr,
+ &obj_name,
+ NULL);
+
+ HANDLE handle = NULL;
+ NTSTATUS status = CreateObjectDirectory(&handle,
+ DIRECTORY_ALL_ACCESS,
+ &obj_attr);
+
+ if (!NT_SUCCESS(status))
+ return NULL;
+
+ return handle;
+}
+
+}
+
+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;
+IntegrityLevel PolicyBase::alternate_desktop_integrity_level_label_ =
+ INTEGRITY_LEVEL_SYSTEM;
+
+PolicyBase::PolicyBase()
+ : ref_count(1),
+ lockdown_level_(USER_LOCKDOWN),
+ initial_level_(USER_LOCKDOWN),
+ job_level_(JOB_LOCKDOWN),
+ ui_exceptions_(0),
+ memory_limit_(0),
+ use_alternate_desktop_(false),
+ use_alternate_winstation_(false),
+ file_system_init_(false),
+ 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),
+ lowbox_sid_(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;
+
+ dispatcher = new ProcessMitigationsWin32KDispatcher(this);
+ ipc_targets_[IPC_GDI_GDIDLLINITIALIZE_TAG] = dispatcher;
+ ipc_targets_[IPC_GDI_GETSTOCKOBJECT_TAG] = dispatcher;
+ ipc_targets_[IPC_USER_REGISTERCLASSW_TAG] = dispatcher;
+}
+
+PolicyBase::~PolicyBase() {
+ ClearSharedHandles();
+
+ 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_;
+
+ if (lowbox_sid_)
+ ::LocalFree(lowbox_sid_);
+
+ ::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;
+}
+
+TokenLevel PolicyBase::GetInitialTokenLevel() const {
+ return initial_level_;
+}
+
+TokenLevel PolicyBase::GetLockdownTokenLevel() const{
+ return lockdown_level_;
+}
+
+ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) {
+ if (memory_limit_ && job_level == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ job_level_ = job_level;
+ ui_exceptions_ = ui_exceptions;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) {
+ if (memory_limit && job_level_ == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ memory_limit_ = memory_limit;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
+ use_alternate_desktop_ = true;
+ use_alternate_winstation_ = alternate_winstation;
+ return CreateAlternateDesktop(alternate_winstation);
+}
+
+base::string16 PolicyBase::GetAlternateDesktop() const {
+ // No alternate desktop or winstation. Return an empty string.
+ if (!use_alternate_desktop_ && !use_alternate_winstation_) {
+ return base::string16();
+ }
+
+ // The desktop and winstation should have been created by now.
+ // If we hit this scenario, it means that the user ignored the failure
+ // during SetAlternateDesktop, so we ignore it here too.
+ if (use_alternate_desktop_ && !alternate_desktop_handle_) {
+ return base::string16();
+ }
+ if (use_alternate_winstation_ && (!alternate_desktop_handle_ ||
+ !alternate_winstation_handle_)) {
+ return base::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;
+}
+
+IntegrityLevel PolicyBase::GetIntegrityLevel() const {
+ return integrity_level_;
+}
+
+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;
+
+ // SetLowBox and SetAppContainer are mutually exclusive.
+ if (lowbox_sid_)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ // 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::SetLowBox(const wchar_t* sid) {
+ if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ // SetLowBox and SetAppContainer are mutually exclusive.
+ if (appcontainer_list_.get())
+ return SBOX_ERROR_UNSUPPORTED;
+
+ DCHECK(sid);
+
+ if (lowbox_sid_)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!ConvertStringSidToSid(sid, &lowbox_sid_))
+ return SBOX_ERROR_GENERIC;
+
+ 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() const {
+ 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) {
+ ResultCode result = AddRuleInternal(subsystem, semantics, pattern);
+ LOG_IF(ERROR, result != SBOX_ALL_OK) << "Failed to add sandbox rule."
+ << " error = " << result
+ << ", subsystem = " << subsystem
+ << ", semantics = " << semantics
+ << ", pattern = '" << pattern << "'";
+ return result;
+}
+
+ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
+ blacklisted_dlls_.push_back(dll_name);
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddKernelObjectToClose(const base::char16* handle_type,
+ const base::char16* handle_name) {
+ return handle_closer_.AddHandle(handle_type, handle_name);
+}
+
+void* PolicyBase::AddHandleToShare(HANDLE handle) {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return NULL;
+
+ if (!handle)
+ return NULL;
+
+ HANDLE duped_handle = NULL;
+ ::DuplicateHandle(::GetCurrentProcess(),
+ handle,
+ ::GetCurrentProcess(),
+ &duped_handle,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS);
+ DCHECK(duped_handle);
+ handles_to_share_.push_back(duped_handle);
+ return duped_handle;
+}
+
+HandleList PolicyBase::GetHandlesBeingShared() {
+ return handles_to_share_;
+}
+
+void PolicyBase::ClearSharedHandles() {
+ for (auto handle : handles_to_share_) {
+ ::CloseHandle(handle);
+ }
+ handles_to_share_.clear();
+}
+
+// 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, UINT32_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_,
+ memory_limit_);
+ 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) {
+ if (appcontainer_list_.get() && appcontainer_list_->HasAppContainer() &&
+ lowbox_sid_) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ // 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 we're launching on the alternate desktop we need to make sure the
+ // integrity label on the object is no higher than the sandboxed process's
+ // integrity level. So, we lower the label on the desktop process if it's
+ // not already low enough for our process.
+ if (alternate_desktop_handle_ && use_alternate_desktop_ &&
+ integrity_level_ != INTEGRITY_LEVEL_LAST &&
+ alternate_desktop_integrity_level_label_ < integrity_level_ &&
+ base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_VISTA) {
+ // Integrity label enum is reversed (higher level is a lower value).
+ static_assert(INTEGRITY_LEVEL_SYSTEM < INTEGRITY_LEVEL_UNTRUSTED,
+ "Integrity level ordering reversed.");
+ result = SetObjectIntegrityLabel(alternate_desktop_handle_,
+ SE_WINDOW_OBJECT,
+ L"",
+ GetIntegrityLevelString(integrity_level_));
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_GENERIC;
+
+ alternate_desktop_integrity_level_label_ = integrity_level_;
+ }
+
+ // We are maintaining two mutually exclusive approaches. One is to start an
+ // AppContainer process through StartupInfoEx and other is replacing
+ // existing token with LowBox token after process creation.
+ 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;
+ } else if (lowbox_sid_) {
+ NtCreateLowBoxToken CreateLowBoxToken = NULL;
+ ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken);
+ OBJECT_ATTRIBUTES obj_attr;
+ InitializeObjectAttributes(&obj_attr, NULL, 0, NULL, NULL);
+ HANDLE token_lowbox = NULL;
+
+ if (!lowbox_directory_.IsValid())
+ lowbox_directory_.Set(CreateLowBoxObjectDirectory(lowbox_sid_));
+ DCHECK(lowbox_directory_.IsValid());
+
+ // The order of handles isn't important in the CreateLowBoxToken call.
+ // The kernel will maintain a reference to the object directory handle.
+ HANDLE saved_handles[1] = {lowbox_directory_.Get()};
+ DWORD saved_handles_count = lowbox_directory_.IsValid() ? 1 : 0;
+
+ NTSTATUS status = CreateLowBoxToken(&token_lowbox, *lockdown,
+ TOKEN_ALL_ACCESS, &obj_attr,
+ lowbox_sid_, 0, NULL,
+ saved_handles_count, saved_handles);
+ if (!NT_SUCCESS(status))
+ return SBOX_ERROR_GENERIC;
+
+ DCHECK(token_lowbox);
+ ::CloseHandle(*lockdown);
+ *lockdown = token_lowbox;
+ }
+
+ // 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() const {
+ if (!appcontainer_list_.get() || !appcontainer_list_->HasAppContainer())
+ return NULL;
+
+ return appcontainer_list_.get();
+}
+
+const PSID PolicyBase::GetLowBoxSid() const {
+ return lowbox_sid_;
+}
+
+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<base::string16>::iterator it = blacklisted_dlls_.begin();
+ for (; it != blacklisted_dlls_.end(); ++it) {
+ manager.AddToUnloadModules(it->c_str());
+ }
+ }
+
+ 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);
+}
+
+ResultCode PolicyBase::AddRuleInternal(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;
+ }
+
+ case SUBSYS_WIN32K_LOCKDOWN: {
+ if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+
+ default: { return SBOX_ERROR_UNSUPPORTED; }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h
new file mode 100644
index 0000000000..1de5cf8c36
--- /dev/null
+++ b/sandbox/win/src/sandbox_policy_base.h
@@ -0,0 +1,187 @@
+// 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 "base/win/scoped_handle.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;
+
+typedef std::vector<HANDLE> HandleList;
+
+// 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:
+ void AddRef() override;
+ void Release() override;
+ ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) override;
+ TokenLevel GetInitialTokenLevel() const override;
+ TokenLevel GetLockdownTokenLevel() const override;
+ ResultCode SetJobLevel(JobLevel job_level, uint32 ui_exceptions) override;
+ ResultCode SetJobMemoryLimit(size_t memory_limit) override;
+ ResultCode SetAlternateDesktop(bool alternate_winstation) override;
+ base::string16 GetAlternateDesktop() const override;
+ ResultCode CreateAlternateDesktop(bool alternate_winstation) override;
+ void DestroyAlternateDesktop() override;
+ ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) override;
+ IntegrityLevel GetIntegrityLevel() const override;
+ ResultCode SetDelayedIntegrityLevel(IntegrityLevel integrity_level) override;
+ ResultCode SetAppContainer(const wchar_t* sid) override;
+ ResultCode SetCapability(const wchar_t* sid) override;
+ ResultCode SetLowBox(const wchar_t* sid) override;
+ ResultCode SetProcessMitigations(MitigationFlags flags) override;
+ MitigationFlags GetProcessMitigations() override;
+ ResultCode SetDelayedProcessMitigations(MitigationFlags flags) override;
+ MitigationFlags GetDelayedProcessMitigations() const override;
+ void SetStrictInterceptions() override;
+ ResultCode SetStdoutHandle(HANDLE handle) override;
+ ResultCode SetStderrHandle(HANDLE handle) override;
+ ResultCode AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) override;
+ ResultCode AddDllToUnload(const wchar_t* dll_name) override;
+ ResultCode AddKernelObjectToClose(const base::char16* handle_type,
+ const base::char16* handle_name) override;
+ void* AddHandleToShare(HANDLE handle) override;
+
+ // Dispatcher:
+ Dispatcher* OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) override;
+ 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() const;
+
+ const PSID GetLowBoxSid() const;
+
+ // 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();
+
+ // Returns the list of handles being shared with the target process.
+ HandleList GetHandlesBeingShared();
+
+ // Closes the handles being shared with the target and clears out the list.
+ void ClearSharedHandles();
+
+ private:
+ ~PolicyBase() override;
+
+ // 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);
+
+ ResultCode AddRuleInternal(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern);
+
+ // 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_;
+ size_t memory_limit_;
+ 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<base::string16> blacklisted_dlls_;
+ // This is a map of handle-types to names that we need to close in the
+ // target process. A null set means we need to close all handles of the
+ // given type.
+ HandleCloser handle_closer_;
+ std::vector<base::string16> capabilities_;
+ scoped_ptr<AppContainerAttributes> appcontainer_list_;
+ PSID lowbox_sid_;
+ base::win::ScopedHandle lowbox_directory_;
+
+ static HDESK alternate_desktop_handle_;
+ static HWINSTA alternate_winstation_handle_;
+ static IntegrityLevel alternate_desktop_integrity_level_label_;
+
+ // Contains the list of handles being shared with the target process.
+ // This list contains handles other than the stderr/stdout handles which are
+ // shared with the target at times.
+ std::vector<HANDLE> handles_to_share_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
diff --git a/sandbox/win/src/sandbox_types.h b/sandbox/win/src/sandbox_types.h
new file mode 100644
index 0000000000..3e531be4f4
--- /dev/null
+++ b/sandbox/win/src/sandbox_types.h
@@ -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.
+
+#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,
+ // Error in creating process.
+ SBOX_ERROR_CREATE_PROCESS = 18,
+ // 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_MEMORY_EXCEEDED = 7012, // Exceeded the job memory limit.
+ 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/sandbox/win/src/sandbox_utils.cc b/sandbox/win/src/sandbox_utils.cc
new file mode 100644
index 0000000000..6057caffc1
--- /dev/null
+++ b/sandbox/win/src/sandbox_utils.cc
@@ -0,0 +1,32 @@
+// 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 "sandbox/win/src/internal_types.h"
+
+namespace sandbox {
+
+void InitObjectAttribs(const base::string16& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name,
+ SECURITY_QUALITY_OF_SERVICE* security_qos) {
+ 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);
+ obj_attr->SecurityQualityOfService = security_qos;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sandbox_utils.h b/sandbox/win/src/sandbox_utils.h
new file mode 100644
index 0000000000..fc3100dcb9
--- /dev/null
+++ b/sandbox/win/src/sandbox_utils.h
@@ -0,0 +1,26 @@
+// 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 "base/strings/string16.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+void InitObjectAttribs(const base::string16& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name,
+ SECURITY_QUALITY_OF_SERVICE* security_qos);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_UTILS_H_
diff --git a/sandbox/win/src/security_level.h b/sandbox/win/src/security_level.h
new file mode 100644
index 0000000000..c89bbb4e24
--- /dev/null
+++ b/sandbox/win/src/security_level.h
@@ -0,0 +1,209 @@
+// 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,
+ USER_LAST
+};
+
+// 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;
+
+// Changes the mandatory integrity level policy on the current process' token
+// to enable no-read and no-execute up. This prevents a lower IL process from
+// opening the process token for impersonate/duplicate/assignment.
+const MitigationFlags MITIGATION_HARDEN_TOKEN_IL_POLICY = 0x00000001ULL << 33;
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SECURITY_LEVEL_H_
diff --git a/sandbox/win/src/service_resolver.cc b/sandbox/win/src/service_resolver.cc
new file mode 100644
index 0000000000..92f21a7c2c
--- /dev/null
+++ b/sandbox/win/src/service_resolver.cc
@@ -0,0 +1,46 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/sandbox_nt_util.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) {
+ if (NULL == module)
+ return STATUS_UNSUCCESSFUL;
+
+ base::win::PEImage module_image(module);
+ *address = module_image.GetProcAddress(function_name);
+
+ if (NULL == *address) {
+ NOTREACHED_NT();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void ServiceResolverThunk::AllowLocalPatches() {
+ ntdll_base_ = ::GetModuleHandle(kNtdllName);
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/service_resolver.h b/sandbox/win/src/service_resolver.h
new file mode 100644
index 0000000000..ab125fbe5a
--- /dev/null
+++ b/sandbox/win/src/service_resolver.h
@@ -0,0 +1,139 @@
+// 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),
+ relaxed_(relaxed), relative_jump_(0) {}
+ ~ServiceResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ 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) override;
+
+ // Implementation of Resolver::ResolveInterceptor.
+ NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address) override;
+
+ // Implementation of Resolver::ResolveTarget.
+ NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ // Call this to set up ntdll_base_ which will allow for local patches.
+ virtual void AllowLocalPatches();
+
+ // Verifies that the function specified by |target_name| in |target_module| is
+ // a service and copies the data from that function into |thunk_storage|. If
+ // |storage_bytes| is too small, then the method fails.
+ virtual NTSTATUS CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ protected:
+ // The unit test will use this member to allow local patch on a buffer.
+ HMODULE ntdll_base_;
+
+ // 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);
+
+ // 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) {}
+ ~Wow64ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ 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) {}
+ ~Wow64W8ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk);
+};
+
+// 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) {}
+ ~Win8ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SERVICE_RESOLVER_H__
diff --git a/sandbox/win/src/service_resolver_32.cc b/sandbox/win/src/service_resolver_32.cc
new file mode 100644
index 0000000000..ab69ab8538
--- /dev/null
+++ b/sandbox/win/src/service_resolver_32.cc
@@ -0,0 +1,427 @@
+// 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);
+static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
+ "wrong service length");
+static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
+static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
+
+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();
+}
+
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceEntry function_code;
+ SIZE_T read;
+ 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 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/sandbox/win/src/service_resolver_64.cc b/sandbox/win/src/service_resolver_64.cc
new file mode 100644
index 0000000000..984cb384e2
--- /dev/null
+++ b/sandbox/win/src/service_resolver_64.cc
@@ -0,0 +1,207 @@
+// 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/sandbox_nt_util.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; // = C3
+ 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);
+}
+
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_UNSUCCESSFUL;
+
+ if (NULL != storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceFullThunk function_code;
+ SIZE_T read;
+ 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) {
+ // Patch the original code.
+ ServiceEntry local_service;
+ DCHECK_NT(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_NT();
+ return false;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/service_resolver_unittest.cc b/sandbox/win/src/service_resolver_unittest.cc
new file mode 100644
index 0000000000..c7ac7eab16
--- /dev/null
+++ b/sandbox/win/src/service_resolver_unittest.cc
@@ -0,0 +1,262 @@
+// 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_;
+
+ 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::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;
+
+ resolver->AllowLocalPatches();
+
+ 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 (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
+}
+
+TEST(ServiceResolverTest, LocalPatchesAllowed) {
+ sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
+
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ ASSERT_TRUE(NULL != ntdll_base);
+
+ const char kFunctionName[] = "NtClose";
+
+ void* target = ::GetProcAddress(ntdll_base, kFunctionName);
+ ASSERT_TRUE(NULL != target);
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+ static_cast<WinXpResolverTest*>(resolver)->set_target(service);
+
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ scoped_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+
+ // First try patching without having allowed local patches.
+ ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL,
+ function_entry, thunk.get(), thunk_size,
+ &used);
+ EXPECT_FALSE(NT_SUCCESS(ret));
+
+ // Now allow local patches and check that things work.
+ resolver->AllowLocalPatches();
+ ret = resolver->Setup(ntdll_base, NULL, kFunctionName, NULL,
+ function_entry, thunk.get(), thunk_size,
+ &used);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+}
+
+} // namespace
diff --git a/sandbox/win/src/shared_handles.cc b/sandbox/win/src/shared_handles.cc
new file mode 100644
index 0000000000..423b67bab3
--- /dev/null
+++ b/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/sandbox/win/src/shared_handles.h b/sandbox/win/src/shared_handles.h
new file mode 100644
index 0000000000..2c76bfb280
--- /dev/null
+++ b/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/sandbox/win/src/sharedmem_ipc_client.cc b/sandbox/win/src/sharedmem_ipc_client.cc
new file mode 100644
index 0000000000..fa6a8779aa
--- /dev/null
+++ b/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_NE(kFreeChannel, static_cast<ChannelState>(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, kAbandonedChannel);
+ 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_LT(num, control_->channels_count);
+ return (num);
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sharedmem_ipc_client.h b/sandbox/win/src/sharedmem_ipc_client.h
new file mode 100644
index 0000000000..9eec74ac85
--- /dev/null
+++ b/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
+ kAbandonedChannel
+};
+
+// 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/sandbox/win/src/sharedmem_ipc_server.cc b/sandbox/win/src/sharedmem_ipc_server.cc
new file mode 100644
index 0000000000..5ce7da5d58
--- /dev/null
+++ b/sandbox/win/src/sharedmem_ipc_server.cc
@@ -0,0 +1,423 @@
+// 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 {
+// This handle must not be closed.
+volatile HANDLE g_alive_mutex = NULL;
+}
+
+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) {
+ // We create a initially owned mutex. If the server dies unexpectedly,
+ // the thread that owns it will fail to release the lock and windows will
+ // report to the target (when it tries to acquire it) that the wait was
+ // abandoned. Note: We purposely leak the local handle because we want it to
+ // be closed by Windows itself so it is properly marked as abandoned if the
+ // server dies.
+ if (!g_alive_mutex) {
+ HANDLE mutex = ::CreateMutexW(NULL, TRUE, NULL);
+ if (::InterlockedCompareExchangePointer(&g_alive_mutex, mutex, NULL)) {
+ // We lost the race to create the mutex.
+ ::CloseHandle(mutex);
+ }
+ }
+}
+
+SharedMemIPCServer::~SharedMemIPCServer() {
+ // 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;
+ }
+
+ if (client_control_)
+ ::UnmapViewOfFile(client_control_);
+}
+
+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);
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), g_alive_mutex,
+ 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<base::string16*>(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<base::string16> data(new base::string16);
+ if (!params->GetParameterStr(i, data.get())) {
+ args[i] = 0;
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data.release();
+ break;
+ }
+ case UINT32_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();
+ static_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/sandbox/win/src/sharedmem_ipc_server.h b/sandbox/win/src/sharedmem_ipc_server.h
new file mode 100644
index 0000000000..94d6959b81
--- /dev/null
+++ b/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/sandbox/win/src/sid.cc b/sandbox/win/src/sid.cc
new file mode 100644
index 0000000000..261605d547
--- /dev/null
+++ b/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/sandbox/win/src/sid.h b/sandbox/win/src/sid.h
new file mode 100644
index 0000000000..4656859bec
--- /dev/null
+++ b/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/sandbox/win/src/sid_unittest.cc b/sandbox/win/src/sid_unittest.cc
new file mode 100644
index 0000000000..76d61e82f5
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/ia32_modrm_map.cpp b/sandbox/win/src/sidestep/ia32_modrm_map.cpp
new file mode 100644
index 0000000000..89bc1895eb
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/ia32_opcode_map.cpp b/sandbox/win/src/sidestep/ia32_opcode_map.cpp
new file mode 100644
index 0000000000..b7d8a60bc1
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/mini_disassembler.cpp b/sandbox/win/src/sidestep/mini_disassembler.cpp
new file mode 100644
index 0000000000..1e8e0bd972
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/mini_disassembler.h b/sandbox/win/src/sidestep/mini_disassembler.h
new file mode 100644
index 0000000000..202c4ecc20
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/mini_disassembler_types.h b/sandbox/win/src/sidestep/mini_disassembler_types.h
new file mode 100644
index 0000000000..1c10626313
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/preamble_patcher.h b/sandbox/win/src/sidestep/preamble_patcher.h
new file mode 100644
index 0000000000..3a0985ce9f
--- /dev/null
+++ b/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/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp b/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp
new file mode 100644
index 0000000000..999d76bb21
--- /dev/null
+++ b/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/sandbox/win/src/sidestep_resolver.cc b/sandbox/win/src/sidestep_resolver.cc
new file mode 100644
index 0000000000..828c000a7c
--- /dev/null
+++ b/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/sandbox/win/src/sidestep_resolver.h b/sandbox/win/src/sidestep_resolver.h
new file mode 100644
index 0000000000..cf03d6e812
--- /dev/null
+++ b/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() {}
+ ~SidestepResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ 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) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ 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() {}
+ ~SmartSidestepResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ 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) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ 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/sandbox/win/src/sync_dispatcher.cc b/sandbox/win/src/sync_dispatcher.cc
new file mode 100644
index 0000000000..b83055dc30
--- /dev/null
+++ b/sandbox/win/src/sync_dispatcher.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_dispatcher.h"
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#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, UINT32_TYPE, UINT32_TYPE},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::CreateEvent)
+ };
+
+ static const IPCCall open_params = {
+ {IPC_OPENEVENT_TAG, WCHAR_TYPE, UINT32_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 (service == IPC_CREATEEVENT_TAG) {
+ return INTERCEPT_NT(manager, NtCreateEvent, CREATE_EVENT_ID, 24);
+ }
+ return (service == IPC_OPENEVENT_TAG) &&
+ INTERCEPT_NT(manager, NtOpenEvent, OPEN_EVENT_ID, 16);
+}
+
+bool SyncDispatcher::CreateEvent(IPCInfo* ipc,
+ base::string16* name,
+ uint32 event_type,
+ uint32 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;
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = SyncPolicy::CreateEventAction(
+ result, *ipc->client_info, *name, event_type, initial_state, &handle);
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool SyncDispatcher::OpenEvent(IPCInfo* ipc,
+ base::string16* name,
+ uint32 desired_access) {
+ 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;
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = SyncPolicy::OpenEventAction(
+ result, *ipc->client_info, *name, desired_access, &handle);
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sync_dispatcher.h b/sandbox/win/src/sync_dispatcher.h
new file mode 100644
index 0000000000..29c6c1e08b
--- /dev/null
+++ b/sandbox/win/src/sync_dispatcher.h
@@ -0,0 +1,40 @@
+// 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 "base/strings/string16.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() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, int service) override;
+
+private:
+ // Processes IPC requests coming from calls to CreateEvent in the target.
+ bool CreateEvent(IPCInfo* ipc,
+ base::string16* name,
+ uint32 event_type,
+ uint32 initial_state);
+
+ // Processes IPC requests coming from calls to OpenEvent in the target.
+ bool OpenEvent(IPCInfo* ipc, base::string16* name, uint32 desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(SyncDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_DISPATCHER_H_
diff --git a/sandbox/win/src/sync_interception.cc b/sandbox/win/src/sync_interception.cc
new file mode 100644
index 0000000000..da612a5b35
--- /dev/null
+++ b/sandbox/win/src/sync_interception.cc
@@ -0,0 +1,161 @@
+// 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 {
+
+ResultCode ProxyCreateEvent(LPCWSTR name,
+ uint32 initial_state,
+ EVENT_TYPE event_type,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(name);
+
+ if (!QueryBroker(IPC_CREATEEVENT_TAG, params.GetBase()))
+ return SBOX_ERROR_GENERIC;
+
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code = CrossCall(ipc, IPC_CREATEEVENT_TAG, name, event_type,
+ initial_state, answer);
+ return code;
+}
+
+ResultCode ProxyOpenEvent(LPCWSTR name,
+ uint32 desired_access,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ if (!QueryBroker(IPC_OPENEVENT_TAG, params.GetBase()))
+ return SBOX_ERROR_GENERIC;
+
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code = CrossCall(ipc, IPC_OPENEVENT_TAG, name, desired_access,
+ answer);
+
+ return code;
+}
+
+NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NTSTATUS status = orig_CreateEvent(event_handle, desired_access,
+ object_attributes, event_type,
+ initial_state);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (memory == NULL)
+ break;
+
+ OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes;
+ // The RootDirectory points to BaseNamedObjects. We can ignore it.
+ object_attribs_copy.RootDirectory = NULL;
+
+ wchar_t* name = NULL;
+ uint32 attributes = 0;
+ NTSTATUS ret = AllocAndCopyName(&object_attribs_copy, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || name == NULL)
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = status;
+ ResultCode code = ProxyCreateEvent(name, initial_state, event_type, memory,
+ &answer);
+ operator delete(name, NT_ALLOC);
+
+ if (code != SBOX_ALL_OK) {
+ status = answer.nt_status;
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NTSTATUS status = orig_OpenEvent(event_handle, desired_access,
+ object_attributes);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (memory == NULL)
+ break;
+
+ OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes;
+ // The RootDirectory points to BaseNamedObjects. We can ignore it.
+ object_attribs_copy.RootDirectory = NULL;
+
+ wchar_t* name = NULL;
+ uint32 attributes = 0;
+ NTSTATUS ret = AllocAndCopyName(&object_attribs_copy, &name, &attributes,
+ NULL);
+ if (!NT_SUCCESS(ret) || name == NULL)
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = status;
+ ResultCode code = ProxyOpenEvent(name, desired_access, memory, &answer);
+ operator delete(name, NT_ALLOC);
+
+ if (code != SBOX_ALL_OK) {
+ status = answer.nt_status;
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sync_interception.h b/sandbox/win/src/sync_interception.h
new file mode 100644
index 0000000000..0f985a8edc
--- /dev/null
+++ b/sandbox/win/src/sync_interception.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.
+
+#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 NTSTATUS (WINAPI* NtCreateEventFunction) (
+ PHANDLE EventHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ EVENT_TYPE EventType,
+ BOOLEAN InitialState);
+
+typedef NTSTATUS (WINAPI *NtOpenEventFunction) (
+ PHANDLE EventHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+// Interceptors for NtCreateEvent/NtOpenEvent
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateEvent(
+ NtCreateEventFunction orig_CreateEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenEvent(
+ NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_INTERCEPTION_H__
diff --git a/sandbox/win/src/sync_policy.cc b/sandbox/win/src/sync_policy.cc
new file mode 100644
index 0000000000..607b4465d6
--- /dev/null
+++ b/sandbox/win/src/sync_policy.cc
@@ -0,0 +1,254 @@
+// 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 "base/strings/stringprintf.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+// Provides functionality to resolve a symbolic link within the object
+// directory passed in.
+NTSTATUS ResolveSymbolicLink(const base::string16& directory_name,
+ const base::string16& name,
+ base::string16* target) {
+ NtOpenDirectoryObjectFunction NtOpenDirectoryObject = NULL;
+ ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject);
+
+ NtQuerySymbolicLinkObjectFunction NtQuerySymbolicLinkObject = NULL;
+ ResolveNTFunctionPtr("NtQuerySymbolicLinkObject",
+ &NtQuerySymbolicLinkObject);
+
+ NtOpenSymbolicLinkObjectFunction NtOpenSymbolicLinkObject = NULL;
+ ResolveNTFunctionPtr("NtOpenSymbolicLinkObject", &NtOpenSymbolicLinkObject);
+
+ NtCloseFunction NtClose = NULL;
+ ResolveNTFunctionPtr("NtClose", &NtClose);
+
+ OBJECT_ATTRIBUTES symbolic_link_directory_attributes = {};
+ UNICODE_STRING symbolic_link_directory_string = {};
+ InitObjectAttribs(directory_name, OBJ_CASE_INSENSITIVE, NULL,
+ &symbolic_link_directory_attributes,
+ &symbolic_link_directory_string, NULL);
+
+ HANDLE symbolic_link_directory = NULL;
+ NTSTATUS status = NtOpenDirectoryObject(&symbolic_link_directory,
+ DIRECTORY_QUERY,
+ &symbolic_link_directory_attributes);
+ if (status != STATUS_SUCCESS) {
+ DLOG(ERROR) << "Failed to open symbolic link directory. Error: "
+ << status;
+ return status;
+ }
+
+ OBJECT_ATTRIBUTES symbolic_link_attributes = {};
+ UNICODE_STRING name_string = {};
+ InitObjectAttribs(name, OBJ_CASE_INSENSITIVE, symbolic_link_directory,
+ &symbolic_link_attributes, &name_string, NULL);
+
+ HANDLE symbolic_link = NULL;
+ status = NtOpenSymbolicLinkObject(&symbolic_link, GENERIC_READ,
+ &symbolic_link_attributes);
+ NtClose(symbolic_link_directory);
+ if (status != STATUS_SUCCESS) {
+ DLOG(ERROR) << "Failed to open symbolic link Error: " << status;
+ return status;
+ }
+
+ UNICODE_STRING target_path = {};
+ unsigned long target_length = 0;
+ status = NtQuerySymbolicLinkObject(symbolic_link, &target_path,
+ &target_length);
+ if (status != STATUS_BUFFER_TOO_SMALL) {
+ NtClose(symbolic_link);
+ DLOG(ERROR) << "Failed to get length for symbolic link target. Error: "
+ << status;
+ return status;
+ }
+
+ target_path.Length = 0;
+ target_path.MaximumLength = static_cast<USHORT>(target_length);
+ target_path.Buffer = new wchar_t[target_path.MaximumLength + 1];
+ status = NtQuerySymbolicLinkObject(symbolic_link, &target_path,
+ &target_length);
+ if (status == STATUS_SUCCESS) {
+ target->assign(target_path.Buffer, target_length);
+ } else {
+ DLOG(ERROR) << "Failed to resolve symbolic link. Error: " << status;
+ }
+
+ NtClose(symbolic_link);
+ delete[] target_path.Buffer;
+ return status;
+}
+
+NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) {
+ static HANDLE base_named_objects_handle = NULL;
+ if (base_named_objects_handle) {
+ *directory = base_named_objects_handle;
+ return STATUS_SUCCESS;
+ }
+
+ NtOpenDirectoryObjectFunction NtOpenDirectoryObject = NULL;
+ ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject);
+
+ DWORD session_id = 0;
+ ProcessIdToSessionId(::GetCurrentProcessId(), &session_id);
+
+ base::string16 base_named_objects_path;
+
+ NTSTATUS status = ResolveSymbolicLink(L"\\Sessions\\BNOLINKS",
+ base::StringPrintf(L"%d", session_id),
+ &base_named_objects_path);
+ if (status != STATUS_SUCCESS) {
+ DLOG(ERROR) << "Failed to resolve BaseNamedObjects path. Error: "
+ << status;
+ return status;
+ }
+
+ UNICODE_STRING directory_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(base_named_objects_path, OBJ_CASE_INSENSITIVE, NULL,
+ &object_attributes, &directory_name, NULL);
+ status = NtOpenDirectoryObject(&base_named_objects_handle,
+ DIRECTORY_ALL_ACCESS,
+ &object_attributes);
+ if (status == STATUS_SUCCESS)
+ *directory = base_named_objects_handle;
+ return status;
+}
+
+bool SyncPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ base::string16 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.
+ uint32 allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL;
+ uint32 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;
+}
+
+NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::string16 &event_name,
+ uint32 event_type,
+ uint32 initial_state,
+ HANDLE *handle) {
+ NtCreateEventFunction NtCreateEvent = NULL;
+ ResolveNTFunctionPtr("NtCreateEvent", &NtCreateEvent);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE object_directory = NULL;
+ NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory);
+ if (status != STATUS_SUCCESS)
+ return status;
+
+ UNICODE_STRING unicode_event_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory,
+ &object_attributes, &unicode_event_name, NULL);
+
+ HANDLE local_handle = NULL;
+ status = NtCreateEvent(&local_handle, EVENT_ALL_ACCESS, &object_attributes,
+ static_cast<EVENT_TYPE>(event_type),
+ static_cast<BOOLEAN>(initial_state));
+ if (NULL == local_handle)
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+NTSTATUS SyncPolicy::OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::string16 &event_name,
+ uint32 desired_access,
+ HANDLE *handle) {
+ NtOpenEventFunction NtOpenEvent = NULL;
+ ResolveNTFunctionPtr("NtOpenEvent", &NtOpenEvent);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // event as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE object_directory = NULL;
+ NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory);
+ if (status != STATUS_SUCCESS)
+ return status;
+
+ UNICODE_STRING unicode_event_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory,
+ &object_attributes, &unicode_event_name, NULL);
+
+ HANDLE local_handle = NULL;
+ status = NtOpenEvent(&local_handle, desired_access, &object_attributes);
+ if (NULL == local_handle)
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, FALSE,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/sync_policy.h b/sandbox/win/src/sync_policy.h
new file mode 100644
index 0000000000..e370e4bdac
--- /dev/null
+++ b/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 "base/strings/string16.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#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 NTSTATUS CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::string16 &event_name,
+ uint32 event_type,
+ uint32 initial_state,
+ HANDLE *handle);
+ static NTSTATUS OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::string16 &event_name,
+ uint32 desired_access,
+ HANDLE *handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_POLICY_H__
diff --git a/sandbox/win/src/sync_policy_test.cc b/sandbox/win/src/sync_policy_test.cc
new file mode 100644
index 0000000000..9fc08f42bd
--- /dev/null
+++ b/sandbox/win/src/sync_policy_test.cc
@@ -0,0 +1,148 @@
+// 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.IsValid())
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error_open ||
+ ERROR_BAD_PATHNAME == error_open ||
+ ERROR_FILE_NOT_FOUND == 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.IsValid()) {
+ 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.IsValid() && event_create.IsValid())
+ return SBOX_TEST_SUCCEEDED;
+
+ if (event_open.IsValid() && !event_create.IsValid() ||
+ !event_open.IsValid() && event_create.IsValid())
+ 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, DISABLED_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, DISABLED_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/sandbox/win/src/sync_policy_test.h b/sandbox/win/src/sync_policy_test.h
new file mode 100644
index 0000000000..4f354b35be
--- /dev/null
+++ b/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/sandbox/win/src/target_interceptions.cc b/sandbox/win/src/target_interceptions.cc
new file mode 100644
index 0000000000..e6b0dcf780
--- /dev/null
+++ b/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/sandbox/win/src/target_interceptions.h b/sandbox/win/src/target_interceptions.h
new file mode 100644
index 0000000000..f4805fec5c
--- /dev/null
+++ b/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/sandbox/win/src/target_process.cc b/sandbox/win/src/target_process.cc
new file mode 100644
index 0000000000..e0284c3924
--- /dev/null
+++ b/sandbox/win/src/target_process.cc
@@ -0,0 +1,385 @@
+// 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"
+#include "sandbox/win/src/win_utils.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);
+ // At this point, the target process should have been killed. Check.
+ if (!::GetExitCodeProcess(sandbox_process_info_.process_handle(),
+ &exit_code) || (STILL_ACTIVE == exit_code)) {
+ // Something went wrong. We don't know if the target is in a state where
+ // it can manage to do another IPC call. If it can, and we've destroyed
+ // the |ipc_server_|, it will crash the broker. So we intentionally leak
+ // that.
+ if (shared_section_.IsValid())
+ shared_section_.Take();
+ 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,
+ bool set_lockdown_token_after_create,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info) {
+ if (set_lockdown_token_after_create &&
+ base::win::GetVersion() < base::win::VERSION_WIN8) {
+ // We don't allow set_lockdown_token_after_create below Windows 8.
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ exe_name_.reset(_wcsdup(exe_path));
+
+ // the command line needs to be writable by CreateProcess().
+ scoped_ptr<wchar_t, base::FreeDeleter> 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::ScopedHandle scoped_lockdown_token(lockdown_token_.Take());
+ PROCESS_INFORMATION temp_process_info = {};
+ if (set_lockdown_token_after_create) {
+ // First create process with a default token and then replace it later,
+ // after setting primary thread token. This is required for setting
+ // an AppContainer token along with an impersonation token.
+ if (!::CreateProcess(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(),
+ &temp_process_info)) {
+ return ::GetLastError();
+ }
+ } else {
+ if (!::CreateProcessAsUserW(scoped_lockdown_token.Get(),
+ 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(),
+ &temp_process_info)) {
+ return ::GetLastError();
+ }
+ }
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
+ 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_.Get())) {
+ 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();
+ }
+
+ if (set_lockdown_token_after_create) {
+ PROCESS_ACCESS_TOKEN process_access_token;
+ process_access_token.thread = process_info.thread_handle();
+ process_access_token.token = scoped_lockdown_token.Get();
+
+ NtSetInformationProcess SetInformationProcess = NULL;
+ ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess);
+
+ NTSTATUS status = SetInformationProcess(
+ process_info.process_handle(),
+ static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken),
+ &process_access_token,
+ sizeof(process_access_token));
+ if (!NT_SUCCESS(status)) {
+ win_result = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0); // exit code
+ return win_result;
+ }
+ }
+
+ 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_.Get(),
+ sandbox_process_info_.process_handle(),
+ &target_shared_section, access, FALSE, 0)) {
+ return ::GetLastError();
+ }
+
+ void* shared_memory = ::MapViewOfFile(shared_section_.Get(),
+ 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/sandbox/win/src/target_process.h b/sandbox/win/src/target_process.h
new file mode 100644
index 0000000000..cf5ad9f3c8
--- /dev/null
+++ b/sandbox/win/src/target_process.h
@@ -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.
+
+#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.
+ // When |set_lockdown_token_after_create| is set, the lockdown token
+ // is replaced after the process is created
+ DWORD Create(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ bool set_lockdown_token_after_create,
+ 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<wchar_t, base::FreeDeleter> 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/sandbox/win/src/target_services.cc b/sandbox/win/src/target_services.cc
new file mode 100644
index 0000000000..7c0afc210a
--- /dev/null
+++ b/sandbox/win/src/target_services.cc
@@ -0,0 +1,209 @@
+// 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 <new>
+
+#include <process.h>
+
+#include "base/basictypes.h"
+#include "base/win/windows_version.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;
+}
+
+// Used as storage for g_target_services, because other allocation facilities
+// are not available early. We can't use a regular function static because on
+// VS2015, because the CRT tries to acquire a lock to guard initialization, but
+// this code runs before the CRT is initialized.
+char g_target_services_memory[sizeof(sandbox::TargetServicesBase)];
+sandbox::TargetServicesBase* g_target_services = nullptr;
+
+} // 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() {
+ // Leak on purpose TargetServicesBase.
+ if (!g_target_services)
+ g_target_services = new (g_target_services_memory) TargetServicesBase;
+ return g_target_services;
+}
+
+// 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;
+}
+
+ProcessState::ProcessState() : process_state_(0) {
+}
+
+bool ProcessState::IsKernel32Loaded() const {
+ return process_state_ != 0;
+}
+
+bool ProcessState::InitCalled() const {
+ return process_state_ > 1;
+}
+
+bool ProcessState::RevertedToSelf() const {
+ 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/sandbox/win/src/target_services.h b/sandbox/win/src/target_services.h
new file mode 100644
index 0000000000..867a44a478
--- /dev/null
+++ b/sandbox/win/src/target_services.h
@@ -0,0 +1,67 @@
+// 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();
+ // Returns true if kernel32.dll has been loaded.
+ bool IsKernel32Loaded() const;
+ // Returns true if main has been called.
+ bool InitCalled() const;
+ // Returns true if LowerToken has been called.
+ bool RevertedToSelf() const;
+ // Set the current state.
+ void SetKernel32Loaded();
+ void SetInitCalled();
+ void SetRevertedToSelf();
+
+ private:
+ 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.
+ ResultCode Init() override;
+ void LowerToken() override;
+ ProcessState* GetState() override;
+ ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) override;
+
+ // 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/sandbox/win/src/threadpool_unittest.cc b/sandbox/win/src/threadpool_unittest.cc
new file mode 100644
index 0000000000..f439810851
--- /dev/null
+++ b/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/sandbox/win/src/unload_dll_test.cc b/sandbox/win/src/unload_dll_test.cc
new file mode 100644
index 0000000000..620016c6bd
--- /dev/null
+++ b/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/sandbox/win/src/win2k_threadpool.cc b/sandbox/win/src/win2k_threadpool.cc
new file mode 100644
index 0000000000..051cfc1c89
--- /dev/null
+++ b/sandbox/win/src/win2k_threadpool.cc
@@ -0,0 +1,64 @@
+// 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 {
+
+Win2kThreadPool::Win2kThreadPool() {
+ ::InitializeCriticalSection(&lock_);
+}
+
+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/sandbox/win/src/win2k_threadpool.h b/sandbox/win/src/win2k_threadpool.h
new file mode 100644
index 0000000000..be2791f309
--- /dev/null
+++ b/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();
+ ~Win2kThreadPool() override;
+
+ bool RegisterWait(const void* cookie,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) override;
+
+ bool UnRegisterWaits(void* cookie) override;
+
+ // 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/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc
new file mode 100644
index 0000000000..2ff1b7343c
--- /dev/null
+++ b/sandbox/win/src/win_utils.cc
@@ -0,0 +1,432 @@
+// 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/memory/scoped_ptr.h"
+#include "base/strings/string_util.h"
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+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}
+};
+
+// These functions perform case independent path comparisons.
+bool EqualPath(const base::string16& first, const base::string16& second) {
+ return _wcsicmp(first.c_str(), second.c_str()) == 0;
+}
+
+bool EqualPath(const base::string16& first, size_t first_offset,
+ const base::string16& second, size_t second_offset) {
+ return _wcsicmp(first.c_str() + first_offset,
+ second.c_str() + second_offset) == 0;
+}
+
+bool EqualPath(const base::string16& first,
+ const wchar_t* second, size_t second_len) {
+ return _wcsnicmp(first.c_str(), second, second_len) == 0;
+}
+
+bool EqualPath(const base::string16& first, size_t first_offset,
+ const wchar_t* second, size_t second_len) {
+ return _wcsnicmp(first.c_str() + first_offset, second, second_len) == 0;
+}
+
+// Returns true if |path| starts with "\??\" and returns a path without that
+// component.
+bool IsNTPath(const base::string16& path, base::string16* trimmed_path ) {
+ if ((path.size() < sandbox::kNTPrefixLen) ||
+ (0 != path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))) {
+ *trimmed_path = path;
+ return false;
+ }
+
+ *trimmed_path = path.substr(sandbox::kNTPrefixLen);
+ return true;
+}
+
+// Returns true if |path| starts with "\Device\" and returns a path without that
+// component.
+bool IsDevicePath(const base::string16& path, base::string16* trimmed_path ) {
+ if ((path.size() < sandbox::kNTDevicePrefixLen) ||
+ (!EqualPath(path, sandbox::kNTDevicePrefix,
+ sandbox::kNTDevicePrefixLen))) {
+ *trimmed_path = path;
+ return false;
+ }
+
+ *trimmed_path = path.substr(sandbox::kNTDevicePrefixLen);
+ return true;
+}
+
+bool StartsWithDriveLetter(const base::string16& path) {
+ if (path.size() < 3)
+ return false;
+
+ if (path[1] != L':' || path[2] != L'\\')
+ return false;
+
+ return (path[0] >= 'a' && path[0] <= 'z') ||
+ (path[0] >= 'A' && path[0] <= 'Z');
+}
+
+const wchar_t kNTDotPrefix[] = L"\\\\.\\";
+const size_t kNTDotPrefixLen = arraysize(kNTDotPrefix) - 1;
+
+// Removes "\\\\.\\" from the path.
+void RemoveImpliedDevice(base::string16* path) {
+ if (0 == path->compare(0, kNTDotPrefixLen, kNTDotPrefix))
+ *path = path->substr(kNTDotPrefixLen);
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const base::string16& path) {
+ size_t start = 0;
+ if (0 == path.compare(0, sandbox::kNTPrefixLen, sandbox::kNTPrefix))
+ start = sandbox::kNTPrefixLen;
+
+ const wchar_t kPipe[] = L"pipe\\";
+ if (path.size() < start + arraysize(kPipe) - 1)
+ return false;
+
+ return EqualPath(path, start, kPipe, arraysize(kPipe) - 1);
+}
+
+HKEY GetReservedKeyFromName(const base::string16& name) {
+ for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
+ if (name == kKnownKey[i].name)
+ return kKnownKey[i].key;
+ }
+
+ return NULL;
+}
+
+bool ResolveRegistryName(base::string16 name, base::string16* resolved_name) {
+ for (size_t i = 0; i < arraysize(kKnownKey); ++i) {
+ if (name.find(kKnownKey[i].name) == 0) {
+ HKEY key;
+ 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;
+}
+
+// |full_path| can have any of the following forms:
+// \??\c:\some\foo\bar
+// \Device\HarddiskVolume0\some\foo\bar
+// \??\HarddiskVolume0\some\foo\bar
+DWORD IsReparsePoint(const base::string16& full_path, bool* result) {
+ // Check if it's a pipe. We can't query the attributes of a pipe.
+ if (IsPipe(full_path)) {
+ *result = FALSE;
+ return ERROR_SUCCESS;
+ }
+
+ base::string16 path;
+ bool nt_path = IsNTPath(full_path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+ bool is_device_path = IsDevicePath(path, &path);
+
+ if (!has_drive && !is_device_path && !nt_path)
+ return ERROR_INVALID_NAME;
+
+ bool added_implied_device = false;
+ if (!has_drive) {
+ path = base::string16(kNTDotPrefix) + path;
+ added_implied_device = true;
+ }
+
+ base::string16::size_type last_pos = base::string16::npos;
+ bool passed_once = false;
+
+ 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.
+ if (passed_once && added_implied_device &&
+ (path.rfind(L'\\') == kNTDotPrefixLen - 1)) {
+ break;
+ }
+ NOTREACHED_NT();
+ return error;
+ }
+ } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
+ // This is a reparse point.
+ *result = true;
+ return ERROR_SUCCESS;
+ }
+
+ passed_once = true;
+ last_pos = path.rfind(L'\\');
+ } while (last_pos > 2); // Skip root dir.
+
+ *result = false;
+ return ERROR_SUCCESS;
+}
+
+// We get a |full_path| of the forms accepted by IsReparsePoint(), and the name
+// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
+bool SameObject(HANDLE handle, const wchar_t* full_path) {
+ // Check if it's a pipe.
+ if (IsPipe(full_path))
+ return true;
+
+ base::string16 actual_path;
+ if (!GetPathFromHandle(handle, &actual_path))
+ return false;
+
+ base::string16 path(full_path);
+ DCHECK_NT(!path.empty());
+
+ // 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 (EqualPath(actual_path, path))
+ return true;
+
+ bool nt_path = IsNTPath(path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+
+ if (!has_drive && nt_path) {
+ base::string16 simple_actual_path;
+ if (!IsDevicePath(actual_path, &simple_actual_path))
+ return false;
+
+ // Perfect match (case-insesitive check).
+ return (EqualPath(simple_actual_path, path));
+ }
+
+ if (!has_drive)
+ 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[0], 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() - 2 != actual_path.size())
+ return false;
+
+ // Check up to the drive letter.
+ if (!EqualPath(actual_path, vol_name, vol_length))
+ return false;
+
+ // Check the path after the drive letter.
+ if (!EqualPath(actual_path, vol_length, path, 2))
+ return false;
+
+ return true;
+}
+
+// Paths like \Device\HarddiskVolume0\some\foo\bar are assumed to be already
+// expanded.
+bool ConvertToLongPath(const base::string16& short_path,
+ base::string16* long_path) {
+ if (IsPipe(short_path)) {
+ // TODO(rvargas): Change the signature to use a single argument.
+ long_path->assign(short_path);
+ return true;
+ }
+
+ base::string16 path;
+ if (IsDevicePath(short_path, &path))
+ return false;
+
+ bool is_nt_path = IsNTPath(path, &path);
+ bool added_implied_device = false;
+ if (!StartsWithDriveLetter(path) && is_nt_path) {
+ path = base::string16(kNTDotPrefix) + path;
+ added_implied_device = 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.
+ base::string16::size_type last_slash = path.rfind(L'\\');
+ if (base::string16::npos == last_slash)
+ return false;
+
+ base::string16 begin = path.substr(0, last_slash);
+ base::string16 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 (added_implied_device)
+ RemoveImpliedDevice(&path);
+
+ if (is_nt_path) {
+ *long_path = kNTPrefix;
+ *long_path += path;
+ } else {
+ *long_path = path;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GetPathFromHandle(HANDLE handle, base::string16* 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.
+ // Windows XP requires that the size of the buffer passed in here be != 0.
+ NTSTATUS status = NtQueryObject(handle, ObjectNameInformation, name, size,
+ &size);
+
+ scoped_ptr<BYTE[]> name_ptr;
+ if (size) {
+ name_ptr.reset(new BYTE[size]);
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
+
+ // 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 base::string16& path,
+ base::string16* nt_path) {
+ HANDLE file = ::CreateFileW(path.c_str(), 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ 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
+
+void ResolveNTFunctionPtr(const char* name, void* ptr) {
+ static volatile HMODULE ntdll = NULL;
+
+ if (!ntdll) {
+ HMODULE ntdll_local = ::GetModuleHandle(sandbox::kNtdllName);
+ // Use PEImage to sanity-check that we have a valid ntdll handle.
+ base::win::PEImage ntdll_peimage(ntdll_local);
+ CHECK_NT(ntdll_peimage.VerifyMagic());
+ // Race-safe way to set static ntdll.
+ ::InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, NULL);
+
+ }
+
+ CHECK_NT(ntdll);
+ FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
+ *function_ptr = ::GetProcAddress(ntdll, name);
+ CHECK_NT(*function_ptr);
+}
diff --git a/sandbox/win/src/win_utils.h b/sandbox/win/src/win_utils.h
new file mode 100644
index 0000000000..3e4565f46f
--- /dev/null
+++ b/sandbox/win/src/win_utils.h
@@ -0,0 +1,117 @@
+// 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"
+#include "base/strings/string16.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 kNTDevicePrefix[] = L"\\Device\\";
+const size_t kNTDevicePrefixLen = arraysize(kNTDevicePrefix) - 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 base::string16& short_path,
+ base::string16* long_path);
+
+// Sets result to true if the path contains a reparse point. The return value
+// is ERROR_SUCCESS when the function succeeds or the appropriate error code
+// when the function fails.
+// This function is not smart. It looks for each element in the path and
+// returns true if any of them is a reparse point.
+DWORD IsReparsePoint(const base::string16& full_path, bool* result);
+
+// Returns true if the handle corresponds to the object pointed by this path.
+bool SameObject(HANDLE handle, const wchar_t* full_path);
+
+// Resolves a handle to an nt path. Returns true if the handle can be resolved.
+bool GetPathFromHandle(HANDLE handle, base::string16* path);
+
+// Resolves a win32 path to an nt path using GetPathFromHandle. The path must
+// exist. Returs true if the translation was succesful.
+bool GetNtPathFromWin32Path(const base::string16& path,
+ base::string16* nt_path);
+
+// Translates a reserved key name to its handle.
+// For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE.
+// Returns NULL if the name does not represent any reserved key name.
+HKEY GetReservedKeyFromName(const base::string16& name);
+
+// Resolves a user-readable registry path to a system-readable registry path.
+// For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to
+// \\registry\\machine\\software\\microsoft. Returns false if the path
+// cannot be resolved.
+bool ResolveRegistryName(base::string16 name, base::string16* resolved_name);
+
+// Writes |length| bytes from the provided |buffer| into the address space of
+// |child_process|, at the specified |address|, preserving the original write
+// protection attributes. Returns true on success.
+bool WriteProtectedChildMemory(HANDLE child_process, void* address,
+ const void* buffer, size_t length);
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const base::string16& path);
+
+} // 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/sandbox/win/src/win_utils_unittest.cc b/sandbox/win/src/win_utils_unittest.cc
new file mode 100644
index 0000000000..4cf59b85eb
--- /dev/null
+++ b/sandbox/win/src/win_utils_unittest.cc
@@ -0,0 +1,113 @@
+// 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.
+ base::string16 not_found = base::string16(my_folder) + L"\\foo\\bar";
+ // EXPECT_EQ(ERROR_PATH_NOT_FOUND, IsReparsePoint(not_found, &result));
+
+ base::string16 new_file = base::string16(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);
+
+ base::string16 temp_dir_nt = base::string16(L"\\??\\") + temp_directory;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+
+ EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result));
+ 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));
+
+ base::string16 folder(my_folder);
+ base::string16 file_name = folder + L"\\foo.txt";
+ const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+ base::win::ScopedHandle file(CreateFile(
+ file_name.c_str(), GENERIC_WRITE, kSharing, NULL, CREATE_ALWAYS,
+ FILE_FLAG_DELETE_ON_CLOSE, NULL));
+
+ EXPECT_TRUE(file.IsValid());
+ base::string16 file_name_nt1 = base::string16(L"\\??\\") + file_name;
+ base::string16 file_name_nt2 =
+ base::string16(L"\\??\\") + folder + L"\\FOO.txT";
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str()));
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str()));
+
+ file.Close();
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
+
+TEST(WinUtils, IsPipe) {
+ using sandbox::IsPipe;
+
+ base::string16 pipe_name = L"\\??\\pipe\\mypipe";
+ EXPECT_TRUE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\PiPe\\mypipe";
+ EXPECT_TRUE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\pipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\_pipe_\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\ABCD\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+
+ // Written as two strings to prevent trigraph '?' '?' '/'.
+ pipe_name = L"/?" L"?/pipe/mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\XX\\pipe\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\Device\\NamedPipe\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+}
diff --git a/sandbox/win/src/window.cc b/sandbox/win/src/window.cc
new file mode 100644
index 0000000000..cfbf280d9c
--- /dev/null
+++ b/sandbox/win/src/window.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/window.h"
+
+#include <aclapi.h>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/sid.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.
+ *winsta = ::CreateWindowStationW(
+ NULL, 0, GENERIC_READ | WINSTA_CREATEDESKTOP, &attributes);
+ LocalFree(attributes.lpSecurityDescriptor);
+
+ if (*winsta)
+ return SBOX_ALL_OK;
+
+ return SBOX_ERROR_CANNOT_CREATE_WINSTATION;
+}
+
+ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) {
+ base::string16 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.
+ *desktop = ::CreateDesktop(desktop_name.c_str(),
+ NULL,
+ NULL,
+ 0,
+ DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
+ READ_CONTROL | WRITE_DAC | WRITE_OWNER,
+ &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) {
+ // Replace the DACL on the new Desktop with a reduced privilege version.
+ // We can soft fail on this for now, as it's just an extra mitigation.
+ static const ACCESS_MASK kDesktopDenyMask = WRITE_DAC | WRITE_OWNER |
+ DELETE |
+ DESKTOP_CREATEMENU |
+ DESKTOP_CREATEWINDOW |
+ DESKTOP_HOOKCONTROL |
+ DESKTOP_JOURNALPLAYBACK |
+ DESKTOP_JOURNALRECORD |
+ DESKTOP_SWITCHDESKTOP;
+ AddKnownSidToObject(*desktop, SE_WINDOW_OBJECT, Sid(WinRestrictedCodeSid),
+ DENY_ACCESS, kDesktopDenyMask);
+ return SBOX_ALL_OK;
+ }
+
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+}
+
+base::string16 GetWindowObjectName(HANDLE handle) {
+ // Get the size of the name.
+ DWORD size = 0;
+ ::GetUserObjectInformation(handle, UOI_NAME, NULL, 0, &size);
+
+ if (!size) {
+ NOTREACHED();
+ return base::string16();
+ }
+
+ // 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 base::string16();
+ }
+
+ return base::string16(name_buffer.get());
+}
+
+base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
+ if (!desktop) {
+ NOTREACHED();
+ return base::string16();
+ }
+
+ base::string16 name;
+ if (winsta) {
+ name = GetWindowObjectName(winsta);
+ name += L'\\';
+ }
+
+ name += GetWindowObjectName(desktop);
+ return name;
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/src/window.h b/sandbox/win/src/window.h
new file mode 100644
index 0000000000..62fe7c4742
--- /dev/null
+++ b/sandbox/win/src/window.h
@@ -0,0 +1,40 @@
+// 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 "base/strings/string16.h"
+#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.
+ base::string16 GetWindowObjectName(HANDLE handle);
+
+ // Returns the name of the desktop referenced by |desktop|. If a window
+ // station is specified, the name is prepended with the window station name,
+ // followed by a backslash. This name can be used as the lpDesktop parameter
+ // to CreateProcess.
+ base::string16 GetFullDesktopName(HWINSTA winsta, HDESK desktop);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WINDOW_H_
diff --git a/sandbox/win/tests/common/controller.cc b/sandbox/win/tests/common/controller.cc
new file mode 100644
index 0000000000..bdada4b478
--- /dev/null
+++ b/sandbox/win/tests/common/controller.cc
@@ -0,0 +1,363 @@
+// 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/tests/common/controller.h"
+
+#include <string>
+
+#include "base/memory/shared_memory.h"
+#include "base/process/process.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox_factory.h"
+
+namespace {
+
+static const int kDefaultTimeout = 60000;
+
+// Constructs a full path to a file inside the system32 folder.
+base::string16 MakePathToSys32(const wchar_t* name, bool is_obj_man_path) {
+ wchar_t windows_path[MAX_PATH] = {0};
+ if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH))
+ return base::string16();
+
+ base::string16 full_path(windows_path);
+ if (full_path.empty())
+ return full_path;
+
+ if (is_obj_man_path)
+ full_path.insert(0, L"\\??\\");
+
+ full_path += L"\\system32\\";
+ full_path += name;
+ return full_path;
+}
+
+// Constructs a full path to a file inside the syswow64 folder.
+base::string16 MakePathToSysWow64(const wchar_t* name, bool is_obj_man_path) {
+ wchar_t windows_path[MAX_PATH] = {0};
+ if (0 == ::GetSystemWindowsDirectoryW(windows_path, MAX_PATH))
+ return base::string16();
+
+ base::string16 full_path(windows_path);
+ if (full_path.empty())
+ return full_path;
+
+ if (is_obj_man_path)
+ full_path.insert(0, L"\\??\\");
+
+ full_path += L"\\SysWOW64\\";
+ full_path += name;
+ return full_path;
+}
+
+bool IsProcessRunning(HANDLE process) {
+ DWORD exit_code = 0;
+ if (::GetExitCodeProcess(process, &exit_code))
+ return exit_code == STILL_ACTIVE;
+ return false;
+}
+
+} // namespace
+
+namespace sandbox {
+
+base::string16 MakePathToSys(const wchar_t* name, bool is_obj_man_path) {
+ return (base::win::OSInfo::GetInstance()->wow64_status() ==
+ base::win::OSInfo::WOW64_ENABLED) ?
+ MakePathToSysWow64(name, is_obj_man_path) :
+ MakePathToSys32(name, is_obj_man_path);
+}
+
+BrokerServices* GetBroker() {
+ static BrokerServices* broker = SandboxFactory::GetBrokerServices();
+ static bool is_initialized = false;
+
+ if (!broker) {
+ return NULL;
+ }
+
+ if (!is_initialized) {
+ if (SBOX_ALL_OK != broker->Init())
+ return NULL;
+
+ is_initialized = true;
+ }
+
+ return broker;
+}
+
+TestRunner::TestRunner(JobLevel job_level, TokenLevel startup_token,
+ TokenLevel main_token)
+ : is_init_(false), is_async_(false), no_sandbox_(false),
+ target_process_id_(0) {
+ Init(job_level, startup_token, main_token);
+}
+
+TestRunner::TestRunner()
+ : is_init_(false), is_async_(false), no_sandbox_(false),
+ target_process_id_(0) {
+ Init(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+}
+
+void TestRunner::Init(JobLevel job_level, TokenLevel startup_token,
+ TokenLevel main_token) {
+ broker_ = NULL;
+ policy_ = NULL;
+ timeout_ = kDefaultTimeout;
+ state_ = AFTER_REVERT;
+ is_async_= false;
+ kill_on_destruction_ = true;
+ target_process_id_ = 0;
+
+ broker_ = GetBroker();
+ if (!broker_)
+ return;
+
+ policy_ = broker_->CreatePolicy();
+ if (!policy_)
+ return;
+
+ policy_->SetJobLevel(job_level, 0);
+ policy_->SetTokenLevel(startup_token, main_token);
+
+ is_init_ = true;
+}
+
+TargetPolicy* TestRunner::GetPolicy() {
+ return policy_;
+}
+
+TestRunner::~TestRunner() {
+ if (target_process_.IsValid() && kill_on_destruction_)
+ ::TerminateProcess(target_process_.Get(), 0);
+
+ if (policy_)
+ policy_->Release();
+}
+
+bool TestRunner::AddRule(TargetPolicy::SubSystem subsystem,
+ TargetPolicy::Semantics semantics,
+ const wchar_t* pattern) {
+ if (!is_init_)
+ return false;
+
+ return (SBOX_ALL_OK == policy_->AddRule(subsystem, semantics, pattern));
+}
+
+bool TestRunner::AddRuleSys32(TargetPolicy::Semantics semantics,
+ const wchar_t* pattern) {
+ if (!is_init_)
+ return false;
+
+ base::string16 win32_path = MakePathToSys32(pattern, false);
+ if (win32_path.empty())
+ return false;
+
+ if (!AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str()))
+ return false;
+
+ if (base::win::OSInfo::GetInstance()->wow64_status() !=
+ base::win::OSInfo::WOW64_ENABLED)
+ return true;
+
+ win32_path = MakePathToSysWow64(pattern, false);
+ if (win32_path.empty())
+ return false;
+
+ return AddRule(TargetPolicy::SUBSYS_FILES, semantics, win32_path.c_str());
+}
+
+bool TestRunner::AddFsRule(TargetPolicy::Semantics semantics,
+ const wchar_t* pattern) {
+ if (!is_init_)
+ return false;
+
+ return AddRule(TargetPolicy::SUBSYS_FILES, semantics, pattern);
+}
+
+int TestRunner::RunTest(const wchar_t* command) {
+ if (MAX_STATE > 10)
+ return SBOX_TEST_INVALID_PARAMETER;
+
+ wchar_t state_number[2];
+ state_number[0] = static_cast<wchar_t>(L'0' + state_);
+ state_number[1] = L'\0';
+ base::string16 full_command(state_number);
+ full_command += L" ";
+ full_command += command;
+
+ return InternalRunTest(full_command.c_str());
+}
+
+int TestRunner::InternalRunTest(const wchar_t* command) {
+ if (!is_init_)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ // For simplicity TestRunner supports only one process per instance.
+ if (target_process_.IsValid()) {
+ if (IsProcessRunning(target_process_.Get()))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ target_process_.Close();
+ target_process_id_ = 0;
+ }
+
+ // Get the path to the sandboxed process.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+
+ // Launch the sandboxed process.
+ ResultCode result = SBOX_ALL_OK;
+ PROCESS_INFORMATION target = {0};
+
+ base::string16 arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child";
+ arguments += no_sandbox_ ? L"-no-sandbox " : L" ";
+ arguments += command;
+
+ if (no_sandbox_) {
+ STARTUPINFO startup_info = {sizeof(STARTUPINFO)};
+ if (!::CreateProcessW(prog_name, &arguments[0], NULL, NULL, FALSE, 0,
+ NULL, NULL, &startup_info, &target)) {
+ return SBOX_ERROR_GENERIC;
+ }
+ broker_->AddTargetPeer(target.hProcess);
+ } else {
+ result = broker_->SpawnTarget(prog_name, arguments.c_str(), policy_,
+ &target);
+ }
+
+ if (SBOX_ALL_OK != result)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ ::ResumeThread(target.hThread);
+
+ // For an asynchronous run we don't bother waiting.
+ if (is_async_) {
+ target_process_.Set(target.hProcess);
+ target_process_id_ = target.dwProcessId;
+ ::CloseHandle(target.hThread);
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ if (::IsDebuggerPresent()) {
+ // Don't kill the target process on a time-out while we are debugging.
+ timeout_ = INFINITE;
+ }
+
+ if (WAIT_TIMEOUT == ::WaitForSingleObject(target.hProcess, timeout_)) {
+ ::TerminateProcess(target.hProcess, static_cast<UINT>(SBOX_TEST_TIMED_OUT));
+ ::CloseHandle(target.hProcess);
+ ::CloseHandle(target.hThread);
+ return SBOX_TEST_TIMED_OUT;
+ }
+
+ DWORD exit_code = static_cast<DWORD>(SBOX_TEST_LAST_RESULT);
+ if (!::GetExitCodeProcess(target.hProcess, &exit_code)) {
+ ::CloseHandle(target.hProcess);
+ ::CloseHandle(target.hThread);
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ }
+
+ ::CloseHandle(target.hProcess);
+ ::CloseHandle(target.hThread);
+
+ return exit_code;
+}
+
+void TestRunner::SetTimeout(DWORD timeout_ms) {
+ timeout_ = timeout_ms;
+}
+
+void TestRunner::SetTestState(SboxTestsState desired_state) {
+ state_ = desired_state;
+}
+
+// This is the main procedure for the target (child) application. We'll find out
+// the target test and call it.
+// We expect the arguments to be:
+// argv[1] = "-child"
+// argv[2] = SboxTestsState when to run the command
+// argv[3] = command to run
+// argv[4...] = command arguments.
+int DispatchCall(int argc, wchar_t **argv) {
+ if (argc < 4)
+ return SBOX_TEST_INVALID_PARAMETER;
+
+ // We hard code two tests to avoid dispatch failures.
+ if (0 == _wcsicmp(argv[3], L"wait")) {
+ Sleep(INFINITE);
+ return SBOX_TEST_TIMED_OUT;
+ }
+
+ if (0 == _wcsicmp(argv[3], L"ping"))
+ return SBOX_TEST_PING_OK;
+
+ // If the caller shared a shared memory handle with us attempt to open it
+ // in read only mode and sleep infinitely if we succeed.
+ if (0 == _wcsicmp(argv[3], L"shared_memory_handle")) {
+ base::SharedMemoryHandle shared_handle = NULL;
+ base::StringToUint(
+ argv[4], reinterpret_cast<unsigned int*>(&shared_handle));
+ if (shared_handle == NULL)
+ return SBOX_TEST_INVALID_PARAMETER;
+ base::SharedMemory read_only_view(shared_handle, true);
+ if (!read_only_view.Map(0))
+ return SBOX_TEST_INVALID_PARAMETER;
+ std::string contents(reinterpret_cast<char*>(read_only_view.memory()));
+ if (contents != "Hello World")
+ return SBOX_TEST_INVALID_PARAMETER;
+ Sleep(INFINITE);
+ return SBOX_TEST_TIMED_OUT;
+ }
+
+ SboxTestsState state = static_cast<SboxTestsState>(_wtoi(argv[2]));
+ if ((state <= MIN_STATE) || (state >= MAX_STATE))
+ return SBOX_TEST_INVALID_PARAMETER;
+
+ HMODULE module;
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<wchar_t*>(&DispatchCall), &module))
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::string command_name = base::SysWideToMultiByte(argv[3], CP_UTF8);
+ CommandFunction command = reinterpret_cast<CommandFunction>(
+ ::GetProcAddress(module, command_name.c_str()));
+ if (!command)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (BEFORE_INIT == state)
+ return command(argc - 4, argv + 4);
+ else if (EVERY_STATE == state)
+ command(argc - 4, argv + 4);
+
+ TargetServices* target = SandboxFactory::GetTargetServices();
+ if (target) {
+ if (SBOX_ALL_OK != target->Init())
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (BEFORE_REVERT == state)
+ return command(argc - 4, argv + 4);
+ else if (EVERY_STATE == state)
+ command(argc - 4, argv + 4);
+
+#if defined(ADDRESS_SANITIZER)
+ // Bind and leak dbghelp.dll before the token is lowered, otherwise
+ // AddressSanitizer will crash when trying to symbolize a report.
+ if (!LoadLibraryA("dbghelp.dll"))
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+#endif
+
+ target->LowerToken();
+ } else if (0 != _wcsicmp(argv[1], L"-child-no-sandbox")) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ return command(argc - 4, argv + 4);
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/tests/common/controller.h b/sandbox/win/tests/common/controller.h
new file mode 100644
index 0000000000..a6498ed12e
--- /dev/null
+++ b/sandbox/win/tests/common/controller.h
@@ -0,0 +1,159 @@
+// 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_TESTS_COMMON_CONTROLLER_H_
+#define SANDBOX_WIN_TESTS_COMMON_CONTROLLER_H_
+
+#include <windows.h>
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/sandbox.h"
+
+namespace sandbox {
+
+// See winerror.h for details.
+#define SEVERITY_INFO_FLAGS 0x40000000
+#define SEVERITY_ERROR_FLAGS 0xC0000000
+#define CUSTOMER_CODE 0x20000000
+#define SBOX_TESTS_FACILITY 0x05B10000
+
+// All the possible error codes returned by the child process in
+// the sandbox.
+enum SboxTestResult {
+ SBOX_TEST_FIRST_RESULT = CUSTOMER_CODE | SBOX_TESTS_FACILITY,
+ SBOX_TEST_SUCCEEDED,
+ SBOX_TEST_PING_OK,
+ SBOX_TEST_FIRST_INFO = SBOX_TEST_FIRST_RESULT | SEVERITY_INFO_FLAGS,
+ SBOX_TEST_DENIED, // Access was denied.
+ SBOX_TEST_NOT_FOUND, // The resource was not found.
+ SBOX_TEST_FIRST_ERROR = SBOX_TEST_FIRST_RESULT | SEVERITY_ERROR_FLAGS,
+ SBOX_TEST_SECOND_ERROR,
+ SBOX_TEST_THIRD_ERROR,
+ SBOX_TEST_FOURTH_ERROR,
+ SBOX_TEST_FIFTH_ERROR,
+ SBOX_TEST_SIXTH_ERROR,
+ SBOX_TEST_SEVENTH_ERROR,
+ SBOX_TEST_INVALID_PARAMETER,
+ SBOX_TEST_FAILED_TO_RUN_TEST,
+ SBOX_TEST_FAILED_TO_EXECUTE_COMMAND,
+ SBOX_TEST_TIMED_OUT,
+ SBOX_TEST_FAILED,
+ SBOX_TEST_LAST_RESULT
+};
+
+inline bool IsSboxTestsResult(SboxTestResult result) {
+ unsigned int code = static_cast<unsigned int>(result);
+ unsigned int first = static_cast<unsigned int>(SBOX_TEST_FIRST_RESULT);
+ unsigned int last = static_cast<unsigned int>(SBOX_TEST_LAST_RESULT);
+ return (code > first) && (code < last);
+}
+
+enum SboxTestsState {
+ MIN_STATE = 1,
+ BEFORE_INIT,
+ BEFORE_REVERT,
+ AFTER_REVERT,
+ EVERY_STATE,
+ MAX_STATE
+};
+
+#define SBOX_TESTS_API __declspec(dllexport)
+#define SBOX_TESTS_COMMAND extern "C" SBOX_TESTS_API
+
+extern "C" {
+typedef int (*CommandFunction)(int argc, wchar_t **argv);
+}
+
+// Class to facilitate the launch of a test inside the sandbox.
+class TestRunner {
+ public:
+ TestRunner(JobLevel job_level, TokenLevel startup_token,
+ TokenLevel main_token);
+
+ TestRunner();
+
+ ~TestRunner();
+
+ // Adds a rule to the policy. The parameters are the same as the AddRule
+ // function in the sandbox.
+ bool AddRule(TargetPolicy::SubSystem subsystem,
+ TargetPolicy::Semantics semantics,
+ const wchar_t* pattern);
+
+ // Adds a filesystem rules with the path of a file in system32. The function
+ // appends "pattern" to "system32" and then call AddRule. Return true if the
+ // function succeeds.
+ bool AddRuleSys32(TargetPolicy::Semantics semantics, const wchar_t* pattern);
+
+ // Adds a filesystem rules to the policy. Returns true if the functions
+ // succeeds.
+ bool AddFsRule(TargetPolicy::Semantics semantics, const wchar_t* pattern);
+
+ // Starts a child process in the sandbox and ask it to run |command|. Returns
+ // a SboxTestResult. By default, the test runs AFTER_REVERT.
+ int RunTest(const wchar_t* command);
+
+ // Sets the timeout value for the child to run the command and return.
+ void SetTimeout(DWORD timeout_ms);
+
+ // Sets TestRunner to return without waiting for the process to exit.
+ void SetAsynchronous(bool is_async) { is_async_ = is_async; }
+
+ // Sets TestRunner to return without waiting for the process to exit.
+ void SetUnsandboxed(bool is_no_sandbox) { no_sandbox_ = is_no_sandbox; }
+
+ // Sets the desired state for the test to run.
+ void SetTestState(SboxTestsState desired_state);
+
+ // Sets a flag whether the process should be killed when the TestRunner is
+ // destroyed.
+ void SetKillOnDestruction(bool value) { kill_on_destruction_ = value; }
+
+ // Returns the pointers to the policy object. It can be used to modify
+ // the policy manually.
+ TargetPolicy* GetPolicy();
+
+ BrokerServices* broker() { return broker_; }
+
+ // Returns the process handle for an asynchronous test.
+ HANDLE process() { return target_process_.Get(); }
+
+ // Returns the process ID for an asynchronous test.
+ DWORD process_id() { return target_process_id_; }
+
+ private:
+ // Initializes the data in the object. Sets is_init_ to tree if the
+ // function succeeds. This is meant to be called from the constructor.
+ void Init(JobLevel job_level, TokenLevel startup_token,
+ TokenLevel main_token);
+
+ // The actual runner.
+ int InternalRunTest(const wchar_t* command);
+
+ BrokerServices* broker_;
+ TargetPolicy* policy_;
+ DWORD timeout_;
+ SboxTestsState state_;
+ bool is_init_;
+ bool is_async_;
+ bool no_sandbox_;
+ bool kill_on_destruction_;
+ base::win::ScopedHandle target_process_;
+ DWORD target_process_id_;
+};
+
+// Returns the broker services.
+BrokerServices* GetBroker();
+
+// Constructs a full path to a file inside the system32 (or syswow64) folder.
+base::string16 MakePathToSys(const wchar_t* name, bool is_obj_man_path);
+
+// Runs the given test on the target process.
+int DispatchCall(int argc, wchar_t **argv);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_TESTS_COMMON_CONTROLLER_H_
diff --git a/sandbox/win/tests/common/test_utils.cc b/sandbox/win/tests/common/test_utils.cc
new file mode 100644
index 0000000000..cdd86de2db
--- /dev/null
+++ b/sandbox/win/tests/common/test_utils.cc
@@ -0,0 +1,72 @@
+// 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/tests/common/test_utils.h"
+
+#include <winioctl.h>
+
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+// Sets a reparse point. |source| will now point to |target|. Returns true if
+// the call succeeds, false otherwise.
+bool SetReparsePoint(HANDLE source, const wchar_t* target) {
+ USHORT size_target = static_cast<USHORT>(wcslen(target)) * sizeof(target[0]);
+
+ char buffer[2000] = {0};
+ DWORD returned;
+
+ REPARSE_DATA_BUFFER* data = reinterpret_cast<REPARSE_DATA_BUFFER*>(buffer);
+
+ data->ReparseTag = 0xa0000003;
+ memcpy(data->MountPointReparseBuffer.PathBuffer, target, size_target + 2);
+ data->MountPointReparseBuffer.SubstituteNameLength = size_target;
+ data->MountPointReparseBuffer.PrintNameOffset = size_target + 2;
+ data->ReparseDataLength = size_target + 4 + 8;
+
+ int data_size = data->ReparseDataLength + 8;
+
+ if (!DeviceIoControl(source, FSCTL_SET_REPARSE_POINT, &buffer, data_size,
+ NULL, 0, &returned, NULL)) {
+ return false;
+ }
+ return true;
+}
+
+// Delete the reparse point referenced by |source|. Returns true if the call
+// succeeds, false otherwise.
+bool DeleteReparsePoint(HANDLE source) {
+ DWORD returned;
+ REPARSE_DATA_BUFFER data = {0};
+ data.ReparseTag = 0xa0000003;
+ if (!DeviceIoControl(source, FSCTL_DELETE_REPARSE_POINT, &data, 8, NULL, 0,
+ &returned, NULL)) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/sandbox/win/tests/common/test_utils.h b/sandbox/win/tests/common/test_utils.h
new file mode 100644
index 0000000000..9e1766076e
--- /dev/null
+++ b/sandbox/win/tests/common/test_utils.h
@@ -0,0 +1,19 @@
+// 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_TESTS_COMMON_TEST_UTILS_H_
+#define SANDBOX_TESTS_COMMON_TEST_UTILS_H_
+
+#include <windows.h>
+
+// Sets a reparse point. |source| will now point to |target|. Returns true if
+// the call succeeds, false otherwise.
+bool SetReparsePoint(HANDLE source, const wchar_t* target);
+
+// Delete the reparse point referenced by |source|. Returns true if the call
+// succeeds, false otherwise.
+bool DeleteReparsePoint(HANDLE source);
+
+#endif // SANDBOX_TESTS_COMMON_TEST_UTILS_H_
+
diff --git a/sandbox/win/tests/integration_tests/integration_tests.cc b/sandbox/win/tests/integration_tests/integration_tests.cc
new file mode 100644
index 0000000000..3920da198b
--- /dev/null
+++ b/sandbox/win/tests/integration_tests/integration_tests.cc
@@ -0,0 +1,25 @@
+// 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/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/tests/common/controller.h"
+
+int wmain(int argc, wchar_t **argv) {
+ if (argc >= 2) {
+ if (0 == _wcsicmp(argv[1], L"-child") ||
+ 0 == _wcsicmp(argv[1], L"-child-no-sandbox"))
+ // This instance is a child, not the test.
+ return sandbox::DispatchCall(argc, argv);
+ }
+
+ base::TestSuite test_suite(argc, argv);
+ return base::LaunchUnitTests(
+ argc,
+ argv,
+ false,
+ base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/sandbox/win/tests/integration_tests/integration_tests_test.cc b/sandbox/win/tests/integration_tests/integration_tests_test.cc
new file mode 100644
index 0000000000..44055d3d53
--- /dev/null
+++ b/sandbox/win/tests/integration_tests/integration_tests_test.cc
@@ -0,0 +1,306 @@
+// 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.
+
+// Some tests for the framework itself.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+// Returns the current process state.
+SBOX_TESTS_COMMAND int IntegrationTestsTest_state(int argc, wchar_t **argv) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return BEFORE_INIT;
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ return BEFORE_REVERT;
+
+ return AFTER_REVERT;
+}
+
+// Returns the current process state, keeping track of it.
+SBOX_TESTS_COMMAND int IntegrationTestsTest_state2(int argc, wchar_t **argv) {
+ static SboxTestsState state = MIN_STATE;
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) {
+ if (MIN_STATE == state)
+ state = BEFORE_INIT;
+ return state;
+ }
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
+ if (BEFORE_INIT == state)
+ state = BEFORE_REVERT;
+ return state;
+ }
+
+ if (BEFORE_REVERT == state)
+ state = AFTER_REVERT;
+ return state;
+}
+
+// Blocks the process for argv[0] milliseconds simulating stuck child.
+SBOX_TESTS_COMMAND int IntegrationTestsTest_stuck(int argc, wchar_t **argv) {
+ int timeout = 500;
+ if (argc > 0) {
+ timeout = _wtoi(argv[0]);
+ }
+
+ ::Sleep(timeout);
+ return 1;
+}
+
+// Returns the number of arguments
+SBOX_TESTS_COMMAND int IntegrationTestsTest_args(int argc, wchar_t **argv) {
+ for (int i = 0; i < argc; i++) {
+ wchar_t argument[20];
+ size_t argument_bytes = wcslen(argv[i]) * sizeof(wchar_t);
+ memcpy(argument, argv[i], __min(sizeof(argument), argument_bytes));
+ }
+
+ return argc;
+}
+
+// Creates a job and tries to run a process inside it. The function can be
+// called with up to two parameters. The first one if set to "none" means that
+// the child process should be run with the JOB_NONE JobLevel else it is run
+// with JOB_LOCKDOWN level. The second if present specifies that the
+// JOB_OBJECT_LIMIT_BREAKAWAY_OK flag should be set on the job object created
+// in this function. The return value is either SBOX_TEST_SUCCEEDED if the test
+// has passed or a value between 0 and 4 indicating which part of the test has
+// failed.
+SBOX_TESTS_COMMAND int IntegrationTestsTest_job(int argc, wchar_t **argv) {
+ HANDLE job = ::CreateJobObject(NULL, NULL);
+ if (!job)
+ return 0;
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limits;
+ if (!::QueryInformationJobObject(job, JobObjectExtendedLimitInformation,
+ &job_limits, sizeof(job_limits), NULL)) {
+ return 1;
+ }
+ // We cheat here and assume no 2-nd parameter means no breakaway flag and any
+ // value for the second param means with breakaway flag.
+ if (argc > 1) {
+ job_limits.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+ } else {
+ job_limits.BasicLimitInformation.LimitFlags &=
+ ~JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+ }
+ if (!::SetInformationJobObject(job, JobObjectExtendedLimitInformation,
+ &job_limits, sizeof(job_limits))) {
+ return 2;
+ }
+ if (!::AssignProcessToJobObject(job, ::GetCurrentProcess()))
+ return 3;
+
+ JobLevel job_level = JOB_LOCKDOWN;
+ if (argc > 0 && wcscmp(argv[0], L"none") == 0)
+ job_level = JOB_NONE;
+
+ TestRunner runner(job_level, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.SetTimeout(2000);
+
+ if (1 != runner.RunTest(L"IntegrationTestsTest_args 1"))
+ return 4;
+
+ // Terminate the job now.
+ ::TerminateJobObject(job, SBOX_TEST_SUCCEEDED);
+ // We should not make it to here but it doesn't mean our test failed.
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(IntegrationTestsTest, CallsBeforeInit) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(BEFORE_INIT);
+ ASSERT_EQ(BEFORE_INIT, runner.RunTest(L"IntegrationTestsTest_state"));
+}
+
+TEST(IntegrationTestsTest, CallsBeforeRevert) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(BEFORE_REVERT);
+ ASSERT_EQ(BEFORE_REVERT, runner.RunTest(L"IntegrationTestsTest_state"));
+}
+
+TEST(IntegrationTestsTest, CallsAfterRevert) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(AFTER_REVERT);
+ ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state"));
+}
+
+TEST(IntegrationTestsTest, CallsEveryState) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ ASSERT_EQ(AFTER_REVERT, runner.RunTest(L"IntegrationTestsTest_state2"));
+}
+
+TEST(IntegrationTestsTest, ForwardsArguments) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(BEFORE_INIT);
+ ASSERT_EQ(1, runner.RunTest(L"IntegrationTestsTest_args first"));
+ ASSERT_EQ(4, runner.RunTest(L"IntegrationTestsTest_args first second third "
+ L"fourth"));
+}
+
+TEST(IntegrationTestsTest, WaitForStuckChild) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetAsynchronous(true);
+ runner.SetKillOnDestruction(false);
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_stuck 100"));
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+}
+
+TEST(IntegrationTestsTest, NoWaitForStuckChildNoJob) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.SetTimeout(2000);
+ runner.SetAsynchronous(true);
+ runner.SetKillOnDestruction(false);
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_stuck 2000"));
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+ // In this case the processes are not tracked by the broker and should be
+ // still active.
+ DWORD exit_code;
+ ASSERT_TRUE(::GetExitCodeProcess(runner.process(), &exit_code));
+ ASSERT_EQ(STILL_ACTIVE, exit_code);
+ // Terminate the test process now.
+ ::TerminateProcess(runner.process(), 0);
+}
+
+TEST(IntegrationTestsTest, TwoStuckChildrenSecondOneHasNoJob) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetAsynchronous(true);
+ runner.SetKillOnDestruction(false);
+ TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner2.SetTimeout(2000);
+ runner2.SetAsynchronous(true);
+ runner2.SetKillOnDestruction(false);
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_stuck 100"));
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner2.RunTest(L"IntegrationTestsTest_stuck 2000"));
+ // Actually both runners share the same singleton broker.
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+ // In this case the processes are not tracked by the broker and should be
+ // still active.
+ DWORD exit_code;
+ // Checking the exit code for |runner| is flaky on the slow bots but at
+ // least we know that the wait above has succeeded if we are here.
+ ASSERT_TRUE(::GetExitCodeProcess(runner2.process(), &exit_code));
+ ASSERT_EQ(STILL_ACTIVE, exit_code);
+ // Terminate the test process now.
+ ::TerminateProcess(runner2.process(), 0);
+}
+
+TEST(IntegrationTestsTest, TwoStuckChildrenFirstOneHasNoJob) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetAsynchronous(true);
+ runner.SetKillOnDestruction(false);
+ TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner2.SetTimeout(2000);
+ runner2.SetAsynchronous(true);
+ runner2.SetKillOnDestruction(false);
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner2.RunTest(L"IntegrationTestsTest_stuck 2000"));
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_stuck 100"));
+ // Actually both runners share the same singleton broker.
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+ // In this case the processes are not tracked by the broker and should be
+ // still active.
+ DWORD exit_code;
+ // Checking the exit code for |runner| is flaky on the slow bots but at
+ // least we know that the wait above has succeeded if we are here.
+ ASSERT_TRUE(::GetExitCodeProcess(runner2.process(), &exit_code));
+ ASSERT_EQ(STILL_ACTIVE, exit_code);
+ // Terminate the test process now.
+ ::TerminateProcess(runner2.process(), 0);
+}
+
+TEST(IntegrationTestsTest, MultipleStuckChildrenSequential) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetAsynchronous(true);
+ runner.SetKillOnDestruction(false);
+ TestRunner runner2(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner2.SetTimeout(2000);
+ runner2.SetAsynchronous(true);
+ runner2.SetKillOnDestruction(false);
+
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_stuck 100"));
+ // Actually both runners share the same singleton broker.
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner2.RunTest(L"IntegrationTestsTest_stuck 2000"));
+ // Actually both runners share the same singleton broker.
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+
+ DWORD exit_code;
+ // Checking the exit code for |runner| is flaky on the slow bots but at
+ // least we know that the wait above has succeeded if we are here.
+ ASSERT_TRUE(::GetExitCodeProcess(runner2.process(), &exit_code));
+ ASSERT_EQ(STILL_ACTIVE, exit_code);
+ // Terminate the test process now.
+ ::TerminateProcess(runner2.process(), 0);
+
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_stuck 100"));
+ // Actually both runners share the same singleton broker.
+ ASSERT_EQ(SBOX_ALL_OK, runner.broker()->WaitForAllTargets());
+}
+
+// Running from inside job that allows us to escape from it should be ok.
+TEST(IntegrationTestsTest, RunChildFromInsideJob) {
+ TestRunner runner;
+ runner.SetUnsandboxed(true);
+ runner.SetTimeout(2000);
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_job with_job escape_flag"));
+}
+
+// Running from inside job that doesn't allow us to escape from it should fail
+// on any windows prior to 8.
+TEST(IntegrationTestsTest, RunChildFromInsideJobNoEscape) {
+ int expect_result = 4; // Means the runner has failed to execute the child.
+ // Check if we are on Win8 or newer and expect a success as newer windows
+ // versions support nested jobs.
+ OSVERSIONINFOEX version_info = { sizeof version_info };
+ ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
+ if (version_info.dwMajorVersion > 6 ||
+ (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion >= 2)) {
+ expect_result = SBOX_TEST_SUCCEEDED;
+ }
+
+ TestRunner runner;
+ runner.SetUnsandboxed(true);
+ runner.SetTimeout(2000);
+ ASSERT_EQ(expect_result,
+ runner.RunTest(L"IntegrationTestsTest_job with_job"));
+}
+
+// Running without a job object should be ok regardless of the fact that we are
+// running inside an outter job.
+TEST(IntegrationTestsTest, RunJoblessChildFromInsideJob) {
+ TestRunner runner;
+ runner.SetUnsandboxed(true);
+ runner.SetTimeout(2000);
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"IntegrationTestsTest_job none"));
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj b/sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj
new file mode 100644
index 0000000000..53816e73e2
--- /dev/null
+++ b/sandbox/win/tests/integration_tests/sbox_integration_tests.vcproj
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sbox_integration_tests"
+ ProjectGUID="{542D4B3B-98D4-4233-B68D-0103891508C6}"
+ RootNamespace="unit_tests"
+ 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;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_CONSOLE"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/safeseh /dynamicbase /ignore:4199 $(NoInherit)"
+ />
+ <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;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_CONSOLE"
+ UsePrecompiledHeader="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/safeseh /dynamicbase /ignore:4199 $(NoInherit)"
+ />
+ <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="Common"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{49F2D231-E141-4455-B241-7D37C09B6EEB}"
+ >
+ <File
+ RelativePath="..\common\controller.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\common\controller.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\testing\gtest\src\gtest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\integration_tests.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>
+ </Filter>
+ <File
+ RelativePath="..\..\src\dep_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\file_policy_test.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\integration_tests_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\integrity_level_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\ipc_ping_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\named_pipe_policy_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\policy_target_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\process_policy_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\registry_policy_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\sync_policy_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\unload_dll_test.cc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/sandbox/win/tests/unit_tests/sbox_unittests.vcproj b/sandbox/win/tests/unit_tests/sbox_unittests.vcproj
new file mode 100644
index 0000000000..a2df79210c
--- /dev/null
+++ b/sandbox/win/tests/unit_tests/sbox_unittests.vcproj
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sbox_unittests"
+ ProjectGUID="{883553BE-2A9D-418C-A121-61FE1DFBC562}"
+ RootNamespace="unit_tests"
+ 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;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_CONSOLE"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ 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;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_CONSOLE"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ />
+ <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>
+ <Filter
+ Name="Common"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\..\..\testing\gtest\src\gtest.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=".\unit_tests.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="TestInterception"
+ >
+ <File
+ RelativePath="..\..\src\interception_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\pe_image_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\service_resolver_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="TestRestrictedToken"
+ >
+ <File
+ RelativePath="..\..\src\restricted_token_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="TestJob"
+ >
+ <File
+ RelativePath="..\..\src\job_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Sid"
+ >
+ <File
+ RelativePath="..\..\src\sid_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy"
+ >
+ <File
+ RelativePath="..\..\src\policy_engine_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\policy_low_level_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\policy_opcodes_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="IPC"
+ >
+ <File
+ RelativePath="..\..\src\ipc_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\threadpool_unittest.cc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/sandbox/win/tests/unit_tests/unit_tests.cc b/sandbox/win/tests/unit_tests/unit_tests.cc
new file mode 100644
index 0000000000..bd56ab0067
--- /dev/null
+++ b/sandbox/win/tests/unit_tests/unit_tests.cc
@@ -0,0 +1,23 @@
+// 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/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+int wmain(int argc, wchar_t **argv) {
+ if (argc >= 2) {
+ if (0 == _wcsicmp(argv[1], L"-child"))
+ // This instance is a child, not the test.
+ return 0;
+ }
+
+ base::TestSuite test_suite(argc, argv);
+ return base::LaunchUnitTests(
+ argc,
+ argv,
+ false,
+ base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/sandbox/win/tests/validation_tests/commands.cc b/sandbox/win/tests/validation_tests/commands.cc
new file mode 100644
index 0000000000..10a4a13761
--- /dev/null
+++ b/sandbox/win/tests/validation_tests/commands.cc
@@ -0,0 +1,289 @@
+// 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 <windows.h>
+#include <string>
+
+#include "sandbox/win/tests/validation_tests/commands.h"
+
+#include "sandbox/win/tests/common/controller.h"
+
+namespace {
+
+// Returns the HKEY corresponding to name. If there is no HKEY corresponding
+// to the name it returns NULL.
+HKEY GetHKEYFromString(const base::string16 &name) {
+ if (name == L"HKLM")
+ return HKEY_LOCAL_MACHINE;
+ if (name == L"HKCR")
+ return HKEY_CLASSES_ROOT;
+ if (name == L"HKCC")
+ return HKEY_CURRENT_CONFIG;
+ if (name == L"HKCU")
+ return HKEY_CURRENT_USER;
+ if (name == L"HKU")
+ return HKEY_USERS;
+
+ return NULL;
+}
+
+// Modifies string to remove the leading and trailing quotes.
+void trim_quote(base::string16* string) {
+ base::string16::size_type pos1 = string->find_first_not_of(L'"');
+ base::string16::size_type pos2 = string->find_last_not_of(L'"');
+
+ if (pos1 == base::string16::npos || pos2 == base::string16::npos)
+ string->clear();
+ else
+ (*string) = string->substr(pos1, pos2 + 1);
+}
+
+int TestOpenFile(base::string16 path, bool for_write) {
+ wchar_t path_expanded[MAX_PATH + 1] = {0};
+ DWORD size = ::ExpandEnvironmentStrings(path.c_str(), path_expanded,
+ MAX_PATH);
+ if (!size)
+ return sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE file;
+ file = ::CreateFile(path_expanded,
+ for_write ? GENERIC_READ | GENERIC_WRITE : 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) {
+ ::CloseHandle(file);
+ return sandbox::SBOX_TEST_SUCCEEDED;
+ }
+ return (::GetLastError() == ERROR_ACCESS_DENIED) ?
+ sandbox::SBOX_TEST_DENIED : sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int ValidWindow(int argc, wchar_t **argv) {
+ return (argc == 1) ?
+ TestValidWindow(
+ reinterpret_cast<HWND>(static_cast<ULONG_PTR>(_wtoi(argv[0])))) :
+ SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+int TestValidWindow(HWND window) {
+ return ::IsWindow(window) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+}
+
+SBOX_TESTS_COMMAND int OpenProcessCmd(int argc, wchar_t **argv) {
+ return (argc == 2) ?
+ TestOpenProcess(_wtol(argv[0]), _wtol(argv[1])) :
+ SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+int TestOpenProcess(DWORD process_id, DWORD access_mask) {
+ HANDLE process = ::OpenProcess(access_mask,
+ FALSE, // Do not inherit handle.
+ process_id);
+ if (process != NULL) {
+ ::CloseHandle(process);
+ return SBOX_TEST_SUCCEEDED;
+ }
+ return (::GetLastError() == ERROR_ACCESS_DENIED) ?
+ sandbox::SBOX_TEST_DENIED : sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+SBOX_TESTS_COMMAND int OpenThreadCmd(int argc, wchar_t **argv) {
+ return (argc == 1) ?
+ TestOpenThread(_wtoi(argv[0])) : SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+int TestOpenThread(DWORD thread_id) {
+ HANDLE thread = ::OpenThread(THREAD_QUERY_INFORMATION,
+ FALSE, // Do not inherit handles.
+ thread_id);
+ if (thread != NULL) {
+ ::CloseHandle(thread);
+ return SBOX_TEST_SUCCEEDED;
+ }
+ return (::GetLastError() == ERROR_ACCESS_DENIED) ?
+ sandbox::SBOX_TEST_DENIED : sandbox::SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+SBOX_TESTS_COMMAND int OpenFileCmd(int argc, wchar_t **argv) {
+ if (1 != argc)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ base::string16 path = argv[0];
+ trim_quote(&path);
+
+ return TestOpenReadFile(path);
+}
+
+int TestOpenReadFile(const base::string16& path) {
+ return TestOpenFile(path, false);
+}
+
+int TestOpenWriteFile(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ base::string16 path = argv[0];
+ trim_quote(&path);
+ return TestOpenWriteFile(path);
+}
+
+int TestOpenWriteFile(const base::string16& path) {
+ return TestOpenFile(path, true);
+}
+
+SBOX_TESTS_COMMAND int OpenKey(int argc, wchar_t **argv) {
+ if (argc != 1 && argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // Get the hive.
+ HKEY base_key = GetHKEYFromString(argv[0]);
+
+ // Get the subkey.
+ base::string16 subkey;
+ if (argc == 2) {
+ subkey = argv[1];
+ trim_quote(&subkey);
+ }
+
+ return TestOpenKey(base_key, subkey);
+}
+
+int TestOpenKey(HKEY base_key, base::string16 subkey) {
+ HKEY key;
+ LONG err_code = ::RegOpenKeyEx(base_key,
+ subkey.c_str(),
+ 0, // Reserved, must be 0.
+ MAXIMUM_ALLOWED,
+ &key);
+ if (err_code == ERROR_SUCCESS) {
+ ::RegCloseKey(key);
+ return SBOX_TEST_SUCCEEDED;
+ }
+ return (err_code == ERROR_INVALID_HANDLE || err_code == ERROR_ACCESS_DENIED) ?
+ SBOX_TEST_DENIED : SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+}
+
+// Returns true if the current's thread desktop is the interactive desktop.
+// In Vista there is a more direct test but for XP and w2k we need to check
+// the object name.
+bool IsInteractiveDesktop(bool* is_interactive) {
+ HDESK current_desk = ::GetThreadDesktop(::GetCurrentThreadId());
+ if (current_desk == NULL)
+ return false;
+ wchar_t current_desk_name[256] = {0};
+ if (!::GetUserObjectInformationW(current_desk, UOI_NAME, current_desk_name,
+ sizeof(current_desk_name), NULL))
+ return false;
+ *is_interactive = (0 == _wcsicmp(L"default", current_desk_name));
+ return true;
+}
+
+SBOX_TESTS_COMMAND int OpenInteractiveDesktop(int, wchar_t **) {
+ return TestOpenInputDesktop();
+}
+
+int TestOpenInputDesktop() {
+ bool is_interactive = false;
+ if (IsInteractiveDesktop(&is_interactive) && is_interactive)
+ return SBOX_TEST_SUCCEEDED;
+ HDESK desk = ::OpenInputDesktop(0, FALSE, DESKTOP_CREATEWINDOW);
+ if (desk) {
+ ::CloseDesktop(desk);
+ return SBOX_TEST_SUCCEEDED;
+ }
+ return SBOX_TEST_DENIED;
+}
+
+SBOX_TESTS_COMMAND int SwitchToSboxDesktop(int, wchar_t **) {
+ return TestSwitchDesktop();
+}
+
+int TestSwitchDesktop() {
+ HDESK desktop = ::GetThreadDesktop(::GetCurrentThreadId());
+ if (desktop == NULL)
+ return SBOX_TEST_FAILED;
+ return ::SwitchDesktop(desktop) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+}
+
+SBOX_TESTS_COMMAND int OpenAlternateDesktop(int, wchar_t **argv) {
+ return TestOpenAlternateDesktop(argv[0]);
+}
+
+int TestOpenAlternateDesktop(wchar_t *desktop_name) {
+ // Test for WRITE_DAC permission on the handle.
+ HDESK desktop = ::GetThreadDesktop(::GetCurrentThreadId());
+ if (desktop) {
+ HANDLE test_handle;
+ if (::DuplicateHandle(::GetCurrentProcess(), desktop,
+ ::GetCurrentProcess(), &test_handle,
+ WRITE_DAC, FALSE, 0)) {
+ DWORD result = ::SetSecurityInfo(test_handle, SE_WINDOW_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL,
+ NULL, NULL);
+ ::CloseHandle(test_handle);
+ if (result == ERROR_SUCCESS)
+ return SBOX_TEST_SUCCEEDED;
+ } else if (::GetLastError() != ERROR_ACCESS_DENIED) {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ // Open by name with WRITE_DAC.
+ desktop = ::OpenDesktop(desktop_name, 0, FALSE, WRITE_DAC);
+ if (!desktop && ::GetLastError() == ERROR_ACCESS_DENIED)
+ return SBOX_TEST_DENIED;
+ ::CloseDesktop(desktop);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+BOOL CALLBACK DesktopTestEnumProc(LPTSTR desktop_name, LPARAM result) {
+ return TRUE;
+}
+
+SBOX_TESTS_COMMAND int EnumAlternateWinsta(int, wchar_t **) {
+ return TestEnumAlternateWinsta();
+}
+
+int TestEnumAlternateWinsta() {
+ // Try to enumerate the destops on the alternate windowstation.
+ return ::EnumDesktopsW(NULL, DesktopTestEnumProc, 0) ?
+ SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+}
+
+SBOX_TESTS_COMMAND int SleepCmd(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ ::Sleep(_wtoi(argv[0]));
+ return SBOX_TEST_SUCCEEDED;
+}
+
+SBOX_TESTS_COMMAND int AllocateCmd(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ size_t mem_size = static_cast<size_t>(_wtoll(argv[0]));
+ void* memory = ::VirtualAlloc(NULL, mem_size, MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE);
+ if (!memory) {
+ // We need to give the broker a chance to kill our process on failure.
+ ::Sleep(5000);
+ return SBOX_TEST_DENIED;
+ }
+
+ return ::VirtualFree(memory, 0, MEM_RELEASE) ?
+ SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+}
+
+
+} // namespace sandbox
diff --git a/sandbox/win/tests/validation_tests/commands.h b/sandbox/win/tests/validation_tests/commands.h
new file mode 100644
index 0000000000..f9b61990ed
--- /dev/null
+++ b/sandbox/win/tests/validation_tests/commands.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_TESTS_VALIDATION_TESTS_COMMANDS_H__
+#define SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__
+
+#include <windows.h>
+
+#include "base/strings/string16.h"
+
+namespace sandbox {
+
+// Checks if window is a real window. Returns a SboxTestResult.
+int TestValidWindow(HWND window);
+
+// Tries to open the process_id. Returns a SboxTestResult.
+int TestOpenProcess(DWORD process_id, DWORD access_mask);
+
+// Tries to open thread_id. Returns a SboxTestResult.
+int TestOpenThread(DWORD thread_id);
+
+// Tries to open path for read access. Returns a SboxTestResult.
+int TestOpenReadFile(const base::string16& path);
+
+// Tries to open path for write access. Returns a SboxTestResult.
+int TestOpenWriteFile(const base::string16& path);
+
+// Tries to open a registry key.
+int TestOpenKey(HKEY base_key, base::string16 subkey);
+
+// Tries to open the workstation's input desktop as long as the
+// current desktop is not the interactive one. Returns a SboxTestResult.
+int TestOpenInputDesktop();
+
+// Tries to switch the interactive desktop. Returns a SboxTestResult.
+int TestSwitchDesktop();
+
+// Tries to open the alternate desktop. Returns a SboxTestResult.
+int TestOpenAlternateDesktop(wchar_t *desktop_name);
+
+// Tries to enumerate desktops on the alternate windowstation.
+// Returns a SboxTestResult.
+int TestEnumAlternateWinsta();
+
+} // namespace sandbox
+
+#endif // SANDBOX_TESTS_VALIDATION_TESTS_COMMANDS_H__
diff --git a/sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj b/sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj
new file mode 100644
index 0000000000..9b7b599295
--- /dev/null
+++ b/sandbox/win/tests/validation_tests/sbox_validation_tests.vcproj
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sbox_validation_tests"
+ ProjectGUID="{B9CC7B0D-145A-49C2-B887-84E43CFA0F27}"
+ RootNamespace="unit_tests"
+ 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;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_CONSOLE"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.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;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_CONSOLE"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="shlwapi.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>
+ <Filter
+ Name="Common"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{2E6C7E35-7538-4883-B80C-C89961A80D66}"
+ >
+ <File
+ RelativePath="..\common\controller.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\common\controller.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\testing\gtest\src\gtest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\unit_tests.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Suite"
+ >
+ <File
+ RelativePath=".\commands.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\commands.h"
+ >
+ </File>
+ <File
+ RelativePath=".\suite.cc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/sandbox/win/tests/validation_tests/suite.cc b/sandbox/win/tests/validation_tests/suite.cc
new file mode 100644
index 0000000000..35fb5ddc23
--- /dev/null
+++ b/sandbox/win/tests/validation_tests/suite.cc
@@ -0,0 +1,241 @@
+// 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 the validation tests for the sandbox.
+// It includes the tests that need to be performed inside the
+// sandbox.
+
+#include <shlwapi.h>
+
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/tests/common/controller.h"
+
+#pragma comment(lib, "shlwapi.lib")
+
+namespace {
+
+void TestProcessAccess(sandbox::TestRunner* runner, DWORD target) {
+ const wchar_t *kCommandTemplate = L"OpenProcessCmd %d %d";
+ wchar_t command[1024] = {0};
+
+ // Test all the scary process permissions.
+ wsprintf(command, kCommandTemplate, target, PROCESS_CREATE_THREAD);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, PROCESS_DUP_HANDLE);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, PROCESS_SET_INFORMATION);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, PROCESS_VM_OPERATION);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, PROCESS_VM_READ);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, PROCESS_VM_WRITE);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, PROCESS_QUERY_INFORMATION);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, WRITE_DAC);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, WRITE_OWNER);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+ wsprintf(command, kCommandTemplate, target, READ_CONTROL);
+ EXPECT_EQ(sandbox::SBOX_TEST_DENIED, runner->RunTest(command));
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Returns true if the volume that contains any_path supports ACL security. The
+// input path can contain unexpanded environment strings. Returns false on any
+// failure or if the file system does not support file security (such as FAT).
+bool VolumeSupportsACLs(const wchar_t* any_path) {
+ wchar_t expand[MAX_PATH +1];
+ DWORD len =::ExpandEnvironmentStringsW(any_path, expand, _countof(expand));
+ if (0 == len) return false;
+ if (len > _countof(expand)) return false;
+ if (!::PathStripToRootW(expand)) return false;
+ DWORD fs_flags = 0;
+ if (!::GetVolumeInformationW(expand, NULL, 0, 0, NULL, &fs_flags, NULL, 0))
+ return false;
+ if (fs_flags & FILE_PERSISTENT_ACLS) return true;
+ return false;
+}
+
+// Tests if the suite is working properly.
+TEST(ValidationSuite, TestSuite) {
+ TestRunner runner;
+ ASSERT_EQ(SBOX_TEST_PING_OK, runner.RunTest(L"ping"));
+}
+
+// Tests if the file system is correctly protected by the sandbox.
+TEST(ValidationSuite, TestFileSystem) {
+ // Do not perform the test if the system is using FAT or any other
+ // file system that does not have file security.
+ ASSERT_TRUE(VolumeSupportsACLs(L"%SystemDrive%\\"));
+ ASSERT_TRUE(VolumeSupportsACLs(L"%SystemRoot%\\"));
+ ASSERT_TRUE(VolumeSupportsACLs(L"%ProgramFiles%\\"));
+ ASSERT_TRUE(VolumeSupportsACLs(L"%Temp%\\"));
+ ASSERT_TRUE(VolumeSupportsACLs(L"%AppData%\\"));
+
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFileCmd %SystemDrive%"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFileCmd %SystemRoot%"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFileCmd %ProgramFiles%"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"OpenFileCmd %SystemRoot%\\System32"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"OpenFileCmd %SystemRoot%\\explorer.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"OpenFileCmd %SystemRoot%\\Cursors\\arrow_i.cur"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"OpenFileCmd %AllUsersProfile%"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFileCmd %Temp%"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenFileCmd %AppData%"));
+}
+
+// Tests if the registry is correctly protected by the sandbox.
+TEST(ValidationSuite, TestRegistry) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKLM"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKCU"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenKey HKU"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"OpenKey HKLM "
+ L"\"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon\""));
+}
+
+// Tests that the permissions on the Windowstation does not allow the sandbox
+// to get to the interactive desktop or to make the sbox desktop interactive.
+TEST(ValidationSuite, TestDesktop) {
+ TestRunner runner;
+ runner.GetPolicy()->SetAlternateDesktop(true);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"OpenInteractiveDesktop NULL"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"SwitchToSboxDesktop NULL"));
+}
+
+// Tests that the permissions on the Windowstation does not allow the sandbox
+// to get to the interactive desktop or to make the sbox desktop interactive.
+TEST(ValidationSuite, TestAlternateDesktop) {
+ base::win::Version version = base::win::GetVersion();
+ if (version < base::win::VERSION_WIN7)
+ return;
+
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"EnumAlternateWinsta NULL"));
+
+ wchar_t command[1024] = {0};
+ runner.SetTimeout(3600000);
+ runner.GetPolicy()->SetAlternateDesktop(true);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ base::string16 desktop_name = runner.GetPolicy()->GetAlternateDesktop();
+ desktop_name = desktop_name.substr(desktop_name.find('\\') + 1);
+ wsprintf(command, L"OpenAlternateDesktop %lS", desktop_name.c_str());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+}
+
+// Tests if the windows are correctly protected by the sandbox.
+TEST(ValidationSuite, TestWindows) {
+ TestRunner runner;
+ wchar_t command[1024] = {0};
+
+ wsprintf(command, L"ValidWindow %Id",
+ reinterpret_cast<size_t>(::GetDesktopWindow()));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"ValidWindow %Id",
+ reinterpret_cast<size_t>(::FindWindow(NULL, NULL)));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+}
+
+// Tests that a locked-down process cannot open another locked-down process.
+TEST(ValidationSuite, TestProcessDenyLockdown) {
+ TestRunner runner;
+ TestRunner target;
+
+ target.SetAsynchronous(true);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000"));
+
+ TestProcessAccess(&runner, target.process_id());
+}
+
+// Tests that a low-integrity process cannot open a locked-down process (due
+// to the integrity label changing after startup via SetDelayedIntegrityLevel).
+TEST(ValidationSuite, TestProcessDenyLowIntegrity) {
+ // This test applies only to Vista and above.
+ if (base::win::Version() < base::win::VERSION_VISTA)
+ return;
+
+ TestRunner runner;
+ TestRunner target;
+
+ target.SetAsynchronous(true);
+ target.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS,
+ USER_INTERACTIVE);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000"));
+
+ TestProcessAccess(&runner, target.process_id());
+}
+
+// Tests that a locked-down process cannot open a low-integrity process.
+TEST(ValidationSuite, TestProcessDenyBelowLowIntegrity) {
+ // This test applies only to Vista and above.
+ if (base::win::Version() < base::win::VERSION_VISTA)
+ return;
+
+ TestRunner runner;
+ TestRunner target;
+
+ target.SetAsynchronous(true);
+ target.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ target.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS,
+ USER_INTERACTIVE);
+
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED);
+ runner.GetPolicy()->SetTokenLevel(USER_RESTRICTED_SAME_ACCESS,
+ USER_INTERACTIVE);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"SleepCmd 30000"));
+
+ TestProcessAccess(&runner, target.process_id());
+}
+
+// Tests if the threads are correctly protected by the sandbox.
+TEST(ValidationSuite, TestThread) {
+ TestRunner runner;
+ wchar_t command[1024] = {0};
+
+ wsprintf(command, L"OpenThreadCmd %d", ::GetCurrentThreadId());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+}
+
+// Tests if an over-limit allocation will be denied.
+TEST(ValidationSuite, TestMemoryLimit) {
+ TestRunner runner;
+ wchar_t command[1024] = {0};
+ const int kAllocationSize = 256 * 1024 * 1024;
+
+ wsprintf(command, L"AllocateCmd %d", kAllocationSize);
+ runner.GetPolicy()->SetJobMemoryLimit(kAllocationSize);
+ EXPECT_EQ(SBOX_FATAL_MEMORY_EXCEEDED, runner.RunTest(command));
+}
+
+// Tests a large allocation will succeed absent limits.
+TEST(ValidationSuite, TestMemoryNoLimit) {
+ TestRunner runner;
+ wchar_t command[1024] = {0};
+ const int kAllocationSize = 256 * 1024 * 1024;
+
+ wsprintf(command, L"AllocateCmd %d", kAllocationSize);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+}
+
+} // namespace sandbox
diff --git a/sandbox/win/tests/validation_tests/unit_tests.cc b/sandbox/win/tests/validation_tests/unit_tests.cc
new file mode 100644
index 0000000000..592b6c0a8d
--- /dev/null
+++ b/sandbox/win/tests/validation_tests/unit_tests.cc
@@ -0,0 +1,23 @@
+// 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/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "sandbox/win/tests/common/controller.h"
+
+int wmain(int argc, wchar_t **argv) {
+ if (argc >= 2) {
+ if (0 == _wcsicmp(argv[1], L"-child"))
+ return sandbox::DispatchCall(argc, argv);
+ }
+
+ base::TestSuite test_suite(argc, argv);
+ return base::LaunchUnitTests(
+ argc,
+ argv,
+ false,
+ base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/sandbox/win/tools/finder/finder.cc b/sandbox/win/tools/finder/finder.cc
new file mode 100644
index 0000000000..9b829628e3
--- /dev/null
+++ b/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/sandbox/win/tools/finder/finder.h b/sandbox/win/tools/finder/finder.h
new file mode 100644
index 0000000000..23255cea43
--- /dev/null
+++ b/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/sandbox/win/tools/finder/finder.vcproj b/sandbox/win/tools/finder/finder.vcproj
new file mode 100644
index 0000000000..787c8477c2
--- /dev/null
+++ b/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/sandbox/win/tools/finder/finder_fs.cc b/sandbox/win/tools/finder/finder_fs.cc
new file mode 100644
index 0000000000..ddcc4bec60
--- /dev/null
+++ b/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/sandbox/win/tools/finder/finder_kernel.cc b/sandbox/win/tools/finder/finder_kernel.cc
new file mode 100644
index 0000000000..430a4c08ab
--- /dev/null
+++ b/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/sandbox/win/tools/finder/finder_registry.cc b/sandbox/win/tools/finder/finder_registry.cc
new file mode 100644
index 0000000000..a84e413d32
--- /dev/null
+++ b/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/sandbox/win/tools/finder/main.cc b/sandbox/win/tools/finder/main.cc
new file mode 100644
index 0000000000..7cadbef8f6
--- /dev/null
+++ b/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/sandbox/win/tools/finder/ntundoc.h b/sandbox/win/tools/finder/ntundoc.h
new file mode 100644
index 0000000000..dc8c3a57cb
--- /dev/null
+++ b/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/sandbox/win/tools/launcher/launcher.cc b/sandbox/win/tools/launcher/launcher.cc
new file mode 100644
index 0000000000..efcc4a43cc
--- /dev/null
+++ b/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/sandbox/win/tools/launcher/launcher.vcproj b/sandbox/win/tools/launcher/launcher.vcproj
new file mode 100644
index 0000000000..71ed011d2b
--- /dev/null
+++ b/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/sandbox/win/wow_helper.sln b/sandbox/win/wow_helper.sln
new file mode 100644
index 0000000000..26d0da2526
--- /dev/null
+++ b/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/sandbox/win/wow_helper/service64_resolver.cc b/sandbox/win/wow_helper/service64_resolver.cc
new file mode 100644
index 0000000000..033b9d771e
--- /dev/null
+++ b/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/sandbox/win/wow_helper/service64_resolver.h b/sandbox/win/wow_helper/service64_resolver.h
new file mode 100644
index 0000000000..abd7efd813
--- /dev/null
+++ b/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/sandbox/win/wow_helper/target_code.cc b/sandbox/win/wow_helper/target_code.cc
new file mode 100644
index 0000000000..8da27cc576
--- /dev/null
+++ b/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/sandbox/win/wow_helper/target_code.h b/sandbox/win/wow_helper/target_code.h
new file mode 100644
index 0000000000..c198a852e2
--- /dev/null
+++ b/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/sandbox/win/wow_helper/wow_helper.cc b/sandbox/win/wow_helper/wow_helper.cc
new file mode 100644
index 0000000000..e3493375d6
--- /dev/null
+++ b/sandbox/win/wow_helper/wow_helper.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.
+
+// 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 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) {
+ static_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/sandbox/win/wow_helper/wow_helper.exe b/sandbox/win/wow_helper/wow_helper.exe
new file mode 100755
index 0000000000..f9bfb4bbdd
--- /dev/null
+++ b/sandbox/win/wow_helper/wow_helper.exe
Binary files differ
diff --git a/sandbox/win/wow_helper/wow_helper.pdb b/sandbox/win/wow_helper/wow_helper.pdb
new file mode 100644
index 0000000000..9cb67d001d
--- /dev/null
+++ b/sandbox/win/wow_helper/wow_helper.pdb
Binary files differ
diff --git a/sandbox/win/wow_helper/wow_helper.vcproj b/sandbox/win/wow_helper/wow_helper.vcproj
new file mode 100644
index 0000000000..5482fbddce
--- /dev/null
+++ b/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>