From d453628509668db4177afb88b7fbf318966e6ae2 Mon Sep 17 00:00:00 2001 From: Bob Owen Date: Tue, 6 Sep 2016 08:57:21 +0100 Subject: [PATCH] Bug 1287426 Part 3: Update security/sandbox/chromium/ to commit 4ec79b7f2379a60cdc15599e93255c0fa417f1ed. r=aklotz, r=jld MozReview-Commit-ID: 14eHMsYZznA --- CLOBBER | 2 +- .../base/debug/debugging_flags.h | 19 + .../base/file_version_info_win.h | 29 + .../chromium-shim/base/files/file_path.cpp | 19 + .../sandbox/chromium-shim/base/logging.cpp | 33 +- .../sandbox/chromium-shim/base/win/sdkdecls.h | 18 + security/sandbox/chromium/LICENSE | 2 +- security/sandbox/chromium/base/at_exit.h | 2 +- .../sandbox/chromium/base/atomic_ref_count.h | 3 - .../chromium/base/atomic_sequence_num.h | 2 +- security/sandbox/chromium/base/atomicops.h | 72 +- .../base/atomicops_internals_arm_gcc.h | 294 - .../base/atomicops_internals_portable.h | 6 +- .../base/atomicops_internals_x86_gcc.h | 228 - .../base/atomicops_internals_x86_msvc.h | 8 +- security/sandbox/chromium/base/base_export.h | 5 - .../sandbox/chromium/base/base_paths_win.h | 20 +- .../sandbox/chromium/base/base_switches.cc | 33 +- .../sandbox/chromium/base/base_switches.h | 10 +- security/sandbox/chromium/base/basictypes.h | 45 - security/sandbox/chromium/base/bind.h | 466 +- security/sandbox/chromium/base/bind_helpers.h | 242 +- .../sandbox/chromium/base/bind_internal.h | 2742 +--------- .../sandbox/chromium/base/bind_internal_win.h | 10 +- security/sandbox/chromium/base/bit_cast.h | 71 + security/sandbox/chromium/base/callback.h | 415 +- .../sandbox/chromium/base/callback_forward.h | 6 +- .../chromium/base/callback_internal.cc | 14 +- .../sandbox/chromium/base/callback_internal.h | 114 +- .../sandbox/chromium/base/compiler_specific.h | 53 +- .../chromium/base/containers/hash_tables.h | 121 +- security/sandbox/chromium/base/cpu.cc | 32 +- security/sandbox/chromium/base/cpu.h | 10 +- .../sandbox/chromium/base/debug/debugger.h | 6 +- .../chromium/base/debug/leak_annotations.h | 13 +- .../sandbox/chromium/base/debug/profiler.cc | 12 +- .../sandbox/chromium/base/debug/profiler.h | 9 +- .../chromium/base/file_descriptor_posix.h | 13 +- .../sandbox/chromium/base/files/file_path.h | 78 +- security/sandbox/chromium/base/float_util.h | 36 - security/sandbox/chromium/base/guid.h | 5 +- security/sandbox/chromium/base/hash.cc | 2 +- security/sandbox/chromium/base/hash.h | 10 +- .../sandbox/chromium/base/lazy_instance.cc | 1 - .../sandbox/chromium/base/lazy_instance.h | 1 - security/sandbox/chromium/base/location.cc | 7 + security/sandbox/chromium/base/location.h | 42 +- security/sandbox/chromium/base/logging.h | 125 +- security/sandbox/chromium/base/macros.h | 145 +- .../chromium/base/memory/aligned_memory.h | 43 +- .../raw_scoped_refptr_mismatch_checker.h | 75 +- .../chromium/base/memory/ref_counted.h | 59 +- .../sandbox/chromium/base/memory/scoped_ptr.h | 375 +- .../sandbox/chromium/base/memory/singleton.h | 66 +- .../sandbox/chromium/base/memory/weak_ptr.h | 16 +- security/sandbox/chromium/base/move.h | 256 +- .../chromium/base/numerics/safe_conversions.h | 117 +- .../base/numerics/safe_conversions_impl.h | 72 +- .../chromium/base/numerics/safe_math.h | 299 + .../chromium/base/numerics/safe_math_impl.h | 545 ++ security/sandbox/chromium/base/path_service.h | 17 +- security/sandbox/chromium/base/port.h | 48 - .../chromium/base/process/process_handle.h | 73 +- security/sandbox/chromium/base/rand_util.h | 13 +- .../chromium/base/safe_strerror_posix.cc | 119 - .../chromium/base/safe_strerror_posix.h | 38 - .../chromium/base/scoped_clear_errno.h | 2 +- .../chromium/base/sequence_checker_impl.h | 2 +- .../chromium/base/sequenced_task_runner.h | 6 +- .../base/sequenced_task_runner_helpers.h | 2 +- .../chromium/base/single_thread_task_runner.h | 3 +- security/sandbox/chromium/base/stl_util.h | 28 +- .../chromium/base/strings/nullable_string16.h | 2 +- .../chromium/base/strings/safe_sprintf.cc | 33 +- .../chromium/base/strings/safe_sprintf.h | 1 - .../base/strings/safe_sprintf_unittest.cc | 6 +- .../sandbox/chromium/base/strings/string16.h | 11 +- .../base/strings/string_number_conversions.cc | 178 +- .../base/strings/string_number_conversions.h | 34 +- .../chromium/base/strings/string_piece.cc | 15 + .../chromium/base/strings/string_piece.h | 32 +- .../chromium/base/strings/string_split.cc | 352 +- .../chromium/base/strings/string_split.h | 145 +- .../chromium/base/strings/string_util.cc | 793 +-- .../chromium/base/strings/string_util.h | 419 +- .../base/strings/string_util_constants.cc | 10 + .../chromium/base/strings/string_util_posix.h | 17 +- .../chromium/base/strings/string_util_win.h | 31 +- .../chromium/base/strings/stringprintf.cc | 19 +- .../chromium/base/strings/stringprintf.h | 44 +- .../strings/utf_string_conversion_utils.cc | 26 +- .../strings/utf_string_conversion_utils.h | 32 +- .../base/strings/utf_string_conversions.cc | 36 +- .../base/strings/utf_string_conversions.h | 17 +- .../base/synchronization/condition_variable.h | 10 +- .../condition_variable_posix.cc | 29 +- .../chromium/base/synchronization/lock.cc | 7 +- .../chromium/base/synchronization/lock.h | 11 +- .../chromium/base/synchronization/lock_impl.h | 5 +- .../base/synchronization/waitable_event.h | 14 +- .../synchronization/waitable_event_posix.cc | 4 +- security/sandbox/chromium/base/task_runner.h | 5 +- .../sandbox/chromium/base/template_util.h | 8 +- .../chromium/base/third_party/icu/icu_utf.cc | 57 +- .../chromium/base/third_party/icu/icu_utf.h | 163 +- .../base/third_party/valgrind/LICENSE | 39 + .../base/third_party/valgrind/valgrind.h | 4792 +++++++++++++++++ .../chromium/base/threading/platform_thread.h | 75 +- .../platform_thread_internal_posix.cc | 35 + .../platform_thread_internal_posix.h | 43 + .../base/threading/platform_thread_linux.cc | 111 +- .../base/threading/platform_thread_posix.cc | 163 +- .../base/threading/platform_thread_win.cc | 108 +- .../base/threading/sequenced_worker_pool.h | 59 +- .../base/threading/thread_checker_impl.h | 3 +- .../base/threading/thread_collision_warner.h | 2 +- .../base/threading/thread_id_name_manager.cc | 11 +- .../base/threading/thread_id_name_manager.h | 9 +- .../chromium/base/threading/thread_local.h | 3 +- .../base/threading/thread_local_posix.cc | 1 + .../base/threading/thread_local_storage.h | 16 +- .../base/threading/thread_restrictions.cc | 4 +- .../base/threading/thread_restrictions.h | 44 +- security/sandbox/chromium/base/time/time.cc | 94 +- security/sandbox/chromium/base/time/time.h | 665 +-- .../sandbox/chromium/base/time/time_posix.cc | 113 +- .../sandbox/chromium/base/time/time_win.cc | 432 +- security/sandbox/chromium/base/tuple.h | 1359 +---- security/sandbox/chromium/base/values.h | 61 +- security/sandbox/chromium/base/version.h | 8 +- .../sandbox/chromium/base/win/pe_image.cc | 159 +- security/sandbox/chromium/base/win/pe_image.h | 19 +- .../chromium/base/win/scoped_handle.cc | 227 +- .../sandbox/chromium/base/win/scoped_handle.h | 29 +- .../base/win/scoped_process_information.cc | 49 +- .../base/win/scoped_process_information.h | 2 +- .../chromium/base/win/startup_information.h | 10 +- .../chromium/base/win/windows_version.cc | 107 +- .../chromium/base/win/windows_version.h | 27 +- .../sandbox/chromium/build/build_config.h | 4 +- security/sandbox/chromium/build/buildflag.h | 47 + .../chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc | 228 +- .../chromium/sandbox/linux/bpf_dsl/bpf_dsl.h | 98 +- .../sandbox/linux/bpf_dsl/bpf_dsl_impl.h | 26 +- .../chromium/sandbox/linux/bpf_dsl/codegen.cc | 161 + .../chromium/sandbox/linux/bpf_dsl/codegen.h | 122 + .../chromium/sandbox/linux/bpf_dsl/cons.h | 1 + .../sandbox/linux/bpf_dsl/dump_bpf.cc | 228 +- .../chromium/sandbox/linux/bpf_dsl/dump_bpf.h | 8 +- .../sandbox/linux/bpf_dsl/errorcode.h | 37 + .../linux/bpf_dsl/linux_syscall_ranges.h | 57 + .../sandbox/linux/bpf_dsl/policy_compiler.cc | 316 +- .../sandbox/linux/bpf_dsl/policy_compiler.h | 145 +- .../seccomp_macros.h} | 159 +- .../syscall_set.cc} | 11 +- .../syscall_set.h} | 8 +- .../sandbox/linux/bpf_dsl/trap_registry.h | 9 + .../sandbox/linux/seccomp-bpf/basicblock.cc | 13 - .../sandbox/linux/seccomp-bpf/basicblock.h | 49 - .../bpf_tester_compatibility_delegate.h | 7 +- .../sandbox/linux/seccomp-bpf/bpf_tests.h | 6 +- .../linux/seccomp-bpf/bpf_tests_unittest.cc | 26 +- .../sandbox/linux/seccomp-bpf/codegen.cc | 609 --- .../sandbox/linux/seccomp-bpf/codegen.h | 147 - .../linux/seccomp-bpf/codegen_unittest.cc | 513 -- .../chromium/sandbox/linux/seccomp-bpf/die.cc | 7 +- .../sandbox/linux/seccomp-bpf/errorcode.cc | 115 - .../sandbox/linux/seccomp-bpf/errorcode.h | 199 - .../linux/seccomp-bpf/errorcode_unittest.cc | 120 - .../sandbox/linux/seccomp-bpf/instruction.h | 60 - .../sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 571 +- .../sandbox/linux/seccomp-bpf/sandbox_bpf.h | 156 +- .../seccomp-bpf/sandbox_bpf_test_runner.cc | 25 +- .../seccomp-bpf/sandbox_bpf_test_runner.h | 6 +- .../sandbox/linux/seccomp-bpf/syscall.cc | 24 +- .../sandbox/linux/seccomp-bpf/syscall.h | 6 +- .../seccomp-bpf/syscall_iterator_unittest.cc | 124 - .../linux/seccomp-bpf/syscall_unittest.cc | 9 +- .../sandbox/linux/seccomp-bpf/trap.cc | 75 +- .../chromium/sandbox/linux/seccomp-bpf/trap.h | 39 +- .../sandbox/linux/seccomp-bpf/verifier.cc | 401 -- .../sandbox/linux/seccomp-bpf/verifier.h | 55 - .../linux/services/android_arm_ucontext.h | 32 - .../sandbox/linux/services/android_ucontext.h | 28 - .../sandbox/linux/services/linux_syscalls.h | 33 - .../linux/services/syscall_wrappers.cc | 261 + .../sandbox/linux/services/syscall_wrappers.h | 85 + .../arm_linux_syscalls.h | 21 +- .../linux/system_headers/arm_linux_ucontext.h | 69 + .../sandbox/linux/system_headers/capability.h | 42 + .../i386_linux_ucontext.h} | 25 +- .../linux/system_headers/linux_filter.h | 140 + .../linux_futex.h} | 10 +- .../linux/system_headers/linux_seccomp.h | 107 + .../linux/system_headers/linux_signal.h | 146 + .../linux/system_headers/linux_syscalls.h | 37 + .../linux/system_headers/linux_ucontext.h | 28 + .../x86_32_linux_syscalls.h | 74 +- .../x86_64_linux_syscalls.h | 6 +- .../x86_64_linux_ucontext.h} | 8 +- .../sandbox/chromium/sandbox/sandbox_export.h | 3 - .../sandbox/chromium/sandbox/win/src/Wow64.cc | 37 +- .../sandbox/chromium/sandbox/win/src/Wow64.h | 12 +- .../chromium/sandbox/win/src/Wow64_64.cc | 4 + .../chromium/sandbox/win/src/app_container.cc | 1 + .../chromium/sandbox/win/src/app_container.h | 3 +- .../sandbox/win/src/app_container_test.cc | 17 + .../sandbox/win/src/broker_services.cc | 297 +- .../sandbox/win/src/broker_services.h | 40 +- .../sandbox/win/src/crosscall_client.h | 135 +- .../sandbox/win/src/crosscall_params.h | 83 +- .../sandbox/win/src/crosscall_server.cc | 60 +- .../sandbox/win/src/crosscall_server.h | 23 +- .../chromium/sandbox/win/src/eat_resolver.cc | 10 +- .../chromium/sandbox/win/src/eat_resolver.h | 30 +- .../sandbox/win/src/file_policy_test.cc | 39 +- .../sandbox/win/src/filesystem_dispatcher.cc | 97 +- .../sandbox/win/src/filesystem_dispatcher.h | 36 +- .../win/src/filesystem_interception.cc | 82 +- .../sandbox/win/src/filesystem_policy.cc | 119 +- .../sandbox/win/src/filesystem_policy.h | 46 +- .../chromium/sandbox/win/src/handle_closer.cc | 8 +- .../chromium/sandbox/win/src/handle_closer.h | 8 +- .../sandbox/win/src/handle_closer_agent.cc | 62 +- .../sandbox/win/src/handle_closer_agent.h | 18 +- .../sandbox/win/src/handle_closer_test.cc | 120 +- .../sandbox/win/src/handle_dispatcher.cc | 16 +- .../sandbox/win/src/handle_dispatcher.h | 14 +- .../win/src/handle_inheritance_test.cc | 36 +- .../sandbox/win/src/handle_interception.cc | 3 - .../chromium/sandbox/win/src/handle_policy.h | 1 - .../chromium/sandbox/win/src/handle_table.cc | 183 - .../chromium/sandbox/win/src/handle_table.h | 159 - .../chromium/sandbox/win/src/interception.cc | 30 +- .../chromium/sandbox/win/src/interception.h | 7 +- .../sandbox/win/src/interception_agent.cc | 6 +- .../sandbox/win/src/interception_agent.h | 2 +- .../sandbox/win/src/interception_internal.h | 2 + .../sandbox/win/src/interception_unittest.cc | 58 +- .../chromium/sandbox/win/src/internal_types.h | 10 +- .../chromium/sandbox/win/src/ipc_unittest.cc | 107 +- .../sandbox/chromium/sandbox/win/src/job.cc | 37 +- .../sandbox/chromium/sandbox/win/src/job.h | 16 +- .../chromium/sandbox/win/src/job_unittest.cc | 109 +- .../sandbox/win/src/named_pipe_dispatcher.cc | 45 +- .../sandbox/win/src/named_pipe_dispatcher.h | 20 +- .../win/src/named_pipe_interception.cc | 4 - .../sandbox/win/src/named_pipe_policy.h | 1 - .../chromium/sandbox/win/src/nt_internals.h | 57 +- .../chromium/sandbox/win/src/policy_broker.cc | 12 +- .../chromium/sandbox/win/src/policy_broker.h | 3 + .../sandbox/win/src/policy_engine_opcodes.cc | 92 +- .../sandbox/win/src/policy_engine_opcodes.h | 80 +- .../sandbox/win/src/policy_engine_params.h | 12 +- .../win/src/policy_engine_processor.cc | 7 +- .../sandbox/win/src/policy_engine_processor.h | 19 +- .../sandbox/win/src/policy_engine_unittest.cc | 7 +- .../sandbox/win/src/policy_low_level.cc | 49 +- .../sandbox/win/src/policy_low_level.h | 34 +- .../win/src/policy_low_level_unittest.cc | 47 +- .../win/src/policy_opcodes_unittest.cc | 37 +- .../chromium/sandbox/win/src/policy_params.h | 9 +- .../chromium/sandbox/win/src/policy_target.cc | 12 +- .../sandbox/win/src/policy_target_test.cc | 78 +- .../sandbox/win/src/process_mitigations.cc | 25 +- .../sandbox/win/src/process_mitigations.h | 2 +- .../win/src/process_mitigations_test.cc | 18 +- .../process_mitigations_win32k_dispatcher.h | 6 +- .../process_mitigations_win32k_interception.h | 1 - .../src/process_mitigations_win32k_policy.h | 1 - .../win/src/process_thread_dispatcher.cc | 54 +- .../win/src/process_thread_dispatcher.h | 22 +- .../win/src/process_thread_interception.cc | 34 +- .../sandbox/win/src/process_thread_policy.cc | 24 +- .../sandbox/win/src/process_thread_policy.h | 17 +- .../sandbox/win/src/registry_dispatcher.cc | 35 +- .../sandbox/win/src/registry_dispatcher.h | 20 +- .../sandbox/win/src/registry_interception.cc | 79 +- .../sandbox/win/src/registry_policy.cc | 30 +- .../sandbox/win/src/registry_policy.h | 27 +- .../chromium/sandbox/win/src/resolver.cc | 2 + .../chromium/sandbox/win/src/resolver.h | 4 +- .../chromium/sandbox/win/src/resolver_32.cc | 2 + .../chromium/sandbox/win/src/resolver_64.cc | 21 +- .../sandbox/win/src/restricted_token.cc | 306 +- .../sandbox/win/src/restricted_token.h | 61 +- .../win/src/restricted_token_unittest.cc | 272 +- .../sandbox/win/src/restricted_token_utils.cc | 176 +- .../sandbox/win/src/restricted_token_utils.h | 50 +- .../chromium/sandbox/win/src/sandbox.h | 1 - .../sandbox/win/src/sandbox_factory.h | 1 + .../sandbox/win/src/sandbox_nt_util.cc | 145 +- .../sandbox/win/src/sandbox_nt_util.h | 24 +- .../chromium/sandbox/win/src/sandbox_policy.h | 21 +- .../sandbox/win/src/sandbox_policy_base.cc | 287 +- .../sandbox/win/src/sandbox_policy_base.h | 118 +- .../chromium/sandbox/win/src/sandbox_rand.cc | 22 + .../chromium/sandbox/win/src/sandbox_rand.h | 16 + .../chromium/sandbox/win/src/sandbox_types.h | 17 +- .../chromium/sandbox/win/src/sandbox_utils.cc | 4 +- .../chromium/sandbox/win/src/sandbox_utils.h | 4 +- .../chromium/sandbox/win/src/security_level.h | 9 +- .../sandbox/win/src/service_resolver.h | 68 +- .../sandbox/win/src/service_resolver_32.cc | 86 +- .../sandbox/win/src/service_resolver_64.cc | 10 +- .../win/src/service_resolver_unittest.cc | 16 +- .../sandbox/win/src/shared_handles.cc | 67 - .../chromium/sandbox/win/src/shared_handles.h | 108 - .../sandbox/win/src/sharedmem_ipc_client.cc | 11 +- .../sandbox/win/src/sharedmem_ipc_client.h | 7 +- .../sandbox/win/src/sharedmem_ipc_server.cc | 75 +- .../sandbox/win/src/sharedmem_ipc_server.h | 33 +- .../sandbox/chromium/sandbox/win/src/sid.cc | 2 +- .../sidestep/preamble_patcher_with_stub.cpp | 2 + .../sandbox/win/src/sidestep_resolver.cc | 2 + .../sandbox/win/src/sidestep_resolver.h | 44 +- .../sandbox/win/src/sync_dispatcher.cc | 18 +- .../sandbox/win/src/sync_dispatcher.h | 18 +- .../sandbox/win/src/sync_interception.cc | 25 +- .../chromium/sandbox/win/src/sync_policy.cc | 56 +- .../chromium/sandbox/win/src/sync_policy.h | 17 +- .../sandbox/win/src/sync_policy_test.cc | 12 +- .../sandbox/win/src/target_process.cc | 91 +- .../chromium/sandbox/win/src/target_process.h | 21 +- .../sandbox/win/src/target_services.cc | 75 +- .../sandbox/win/src/target_services.h | 22 +- .../sandbox/win/src/threadpool_unittest.cc | 35 +- .../sandbox/win/src/top_level_dispatcher.cc | 146 + .../sandbox/win/src/top_level_dispatcher.h | 51 + .../sandbox/win/src/unload_dll_test.cc | 9 +- .../sandbox/win/src/win2k_threadpool.cc | 6 + .../sandbox/win/src/win2k_threadpool.h | 21 +- .../chromium/sandbox/win/src/win_utils.cc | 154 +- .../chromium/sandbox/win/src/win_utils.h | 16 +- .../sandbox/win/src/win_utils_unittest.cc | 46 +- .../win/wow_helper/service64_resolver.cc | 4 + .../win/wow_helper/service64_resolver.h | 3 + .../sandbox/win/wow_helper/wow_helper.cc | 3 +- security/sandbox/linux/Sandbox.cpp | 19 +- security/sandbox/linux/SandboxFilter.cpp | 10 +- security/sandbox/linux/SandboxFilterUtil.h | 2 +- security/sandbox/linux/SandboxLogging.cpp | 1 + security/sandbox/linux/SandboxUtil.cpp | 2 +- security/sandbox/linux/common/SandboxInfo.cpp | 4 +- security/sandbox/linux/moz.build | 11 +- ...romium-to-reapply-after-upstream-merge.txt | 3 - .../sandbox/moz-chromium-commit-status.txt | 12 +- security/sandbox/moz.build | 5 +- 348 files changed, 16073 insertions(+), 15694 deletions(-) create mode 100644 security/sandbox/chromium-shim/base/debug/debugging_flags.h create mode 100644 security/sandbox/chromium-shim/base/file_version_info_win.h create mode 100644 security/sandbox/chromium-shim/base/files/file_path.cpp delete mode 100644 security/sandbox/chromium/base/atomicops_internals_arm_gcc.h delete mode 100644 security/sandbox/chromium/base/atomicops_internals_x86_gcc.h delete mode 100644 security/sandbox/chromium/base/basictypes.h create mode 100644 security/sandbox/chromium/base/bit_cast.h delete mode 100644 security/sandbox/chromium/base/float_util.h create mode 100644 security/sandbox/chromium/base/numerics/safe_math.h create mode 100644 security/sandbox/chromium/base/numerics/safe_math_impl.h delete mode 100644 security/sandbox/chromium/base/port.h delete mode 100644 security/sandbox/chromium/base/safe_strerror_posix.cc delete mode 100644 security/sandbox/chromium/base/safe_strerror_posix.h create mode 100644 security/sandbox/chromium/base/third_party/valgrind/LICENSE create mode 100644 security/sandbox/chromium/base/third_party/valgrind/valgrind.h create mode 100644 security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc create mode 100644 security/sandbox/chromium/base/threading/platform_thread_internal_posix.h create mode 100644 security/sandbox/chromium/build/buildflag.h create mode 100644 security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.cc create mode 100644 security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.h create mode 100644 security/sandbox/chromium/sandbox/linux/bpf_dsl/errorcode.h create mode 100644 security/sandbox/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h rename security/sandbox/chromium/sandbox/linux/{seccomp-bpf/linux_seccomp.h => bpf_dsl/seccomp_macros.h} (70%) rename security/sandbox/chromium/sandbox/linux/{seccomp-bpf/syscall_iterator.cc => bpf_dsl/syscall_set.cc} (93%) rename security/sandbox/chromium/sandbox/linux/{seccomp-bpf/syscall_iterator.h => bpf_dsl/syscall_set.h} (93%) delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.h delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.h delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.h delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/instruction.h delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.cc delete mode 100644 security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.h delete mode 100644 security/sandbox/chromium/sandbox/linux/services/android_arm_ucontext.h delete mode 100644 security/sandbox/chromium/sandbox/linux/services/android_ucontext.h delete mode 100644 security/sandbox/chromium/sandbox/linux/services/linux_syscalls.h create mode 100644 security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc create mode 100644 security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.h rename security/sandbox/chromium/sandbox/linux/{services => system_headers}/arm_linux_syscalls.h (98%) create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_ucontext.h create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/capability.h rename security/sandbox/chromium/sandbox/linux/{services/android_i386_ucontext.h => system_headers/i386_linux_ucontext.h} (71%) create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/linux_filter.h rename security/sandbox/chromium/sandbox/linux/{services/android_futex.h => system_headers/linux_futex.h} (84%) create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/linux_seccomp.h create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/linux_signal.h create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/linux_syscalls.h create mode 100644 security/sandbox/chromium/sandbox/linux/system_headers/linux_ucontext.h rename security/sandbox/chromium/sandbox/linux/{services => system_headers}/x86_32_linux_syscalls.h (94%) rename security/sandbox/chromium/sandbox/linux/{services => system_headers}/x86_64_linux_syscalls.h (99%) rename security/sandbox/chromium/sandbox/linux/{services/android_x86_64_ucontext.h => system_headers/x86_64_linux_ucontext.h} (88%) delete mode 100644 security/sandbox/chromium/sandbox/win/src/handle_table.cc delete mode 100644 security/sandbox/chromium/sandbox/win/src/handle_table.h create mode 100644 security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc create mode 100644 security/sandbox/chromium/sandbox/win/src/sandbox_rand.h delete mode 100644 security/sandbox/chromium/sandbox/win/src/shared_handles.cc delete mode 100644 security/sandbox/chromium/sandbox/win/src/shared_handles.h create mode 100644 security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc create mode 100644 security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h diff --git a/CLOBBER b/CLOBBER index 888ceee4297c..19a244ab4270 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,4 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1289951 maybe needed a clobber to fix xpcshell tests? +Bug 1287426 - Clobber required because of Linux Chromium sandbox file moves. diff --git a/security/sandbox/chromium-shim/base/debug/debugging_flags.h b/security/sandbox/chromium-shim/base/debug/debugging_flags.h new file mode 100644 index 000000000000..ebb96dfca5c2 --- /dev/null +++ b/security/sandbox/chromium-shim/base/debug/debugging_flags.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This is a copy of a file that is generated by the chromium build. + +// Generated by build/write_buildflag_header.py +// From "//base:debugging_flags" + +#ifndef BASE_DEBUG_DEBUGGING_FLAGS_H_ +#define BASE_DEBUG_DEBUGGING_FLAGS_H_ + +#include "build/buildflag.h" + +#define BUILDFLAG_INTERNAL_ENABLE_PROFILING() (0) + +#endif // BASE_DEBUG_DEBUGGING_FLAGS_H_ diff --git a/security/sandbox/chromium-shim/base/file_version_info_win.h b/security/sandbox/chromium-shim/base/file_version_info_win.h new file mode 100644 index 000000000000..8276900bd452 --- /dev/null +++ b/security/sandbox/chromium-shim/base/file_version_info_win.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This is a dummy version of Chromium source file base/file_version_info_win.h +// Within our copy of Chromium files FileVersionInfoWin is only used in +// base/win/windows_version.cc in GetVersionFromKernel32, which we don't use. + +#ifndef BASE_FILE_VERSION_INFO_WIN_H_ +#define BASE_FILE_VERSION_INFO_WIN_H_ + +struct tagVS_FIXEDFILEINFO; +typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO; + +namespace base { +class FilePath; +} + +class FileVersionInfoWin { + public: + static FileVersionInfoWin* + CreateFileVersionInfo(const base::FilePath& file_path) { return nullptr; } + + VS_FIXEDFILEINFO* fixed_file_info() { return nullptr; } +}; + +#endif // BASE_FILE_VERSION_INFO_WIN_H_ diff --git a/security/sandbox/chromium-shim/base/files/file_path.cpp b/security/sandbox/chromium-shim/base/files/file_path.cpp new file mode 100644 index 000000000000..245118f9ee55 --- /dev/null +++ b/security/sandbox/chromium-shim/base/files/file_path.cpp @@ -0,0 +1,19 @@ +// 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 dummy version of Chromium source file base/file/file_path.cc. +// To provide the functions required in base/win/windows_version.cc +// GetVersionFromKernel32, which we don't use. + +#include "base/files/file_path.h" + +namespace base { + +FilePath::FilePath(FilePath::StringPieceType path) { +} + +FilePath::~FilePath() { +} + +} // namespace base diff --git a/security/sandbox/chromium-shim/base/logging.cpp b/security/sandbox/chromium-shim/base/logging.cpp index e9356e43a44e..9c2113dcbebe 100644 --- a/security/sandbox/chromium-shim/base/logging.cpp +++ b/security/sandbox/chromium-shim/base/logging.cpp @@ -26,16 +26,41 @@ #include "base/strings/utf_string_conversions.h" #endif +#include + namespace logging { namespace { -int min_log_level = 0; +int g_min_log_level = 0; + +LoggingDestination g_logging_destination = LOG_DEFAULT; + +// For LOG_ERROR and above, always print to stderr. +const int kAlwaysPrintErrorLevel = LOG_ERROR; + +// A log message handler that gets notified of every log message we process. +LogMessageHandlerFunction log_message_handler = nullptr; } // namespace +void SetMinLogLevel(int level) { + g_min_log_level = std::min(LOG_FATAL, level); +} + int GetMinLogLevel() { - return min_log_level; + return g_min_log_level; +} + +bool ShouldCreateLogMessage(int severity) { + if (severity < g_min_log_level) + return false; + + // Return true here unless we know ~LogMessage won't do anything. Note that + // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even + // when g_logging_destination is LOG_NONE. + return g_logging_destination != LOG_NONE || log_message_handler || + severity >= kAlwaysPrintErrorLevel; } int GetVlogLevelHelper(const char* file, size_t N) { @@ -67,6 +92,10 @@ LogMessage::LogMessage(const char* file, int line, LogSeverity severity) : severity_(severity), file_(file), line_(line) { } +LogMessage::LogMessage(const char* file, int line, const char* condition) + : severity_(LOG_FATAL), file_(file), line_(line) { +} + LogMessage::LogMessage(const char* file, int line, std::string* result) : severity_(LOG_FATAL), file_(file), line_(line) { delete result; diff --git a/security/sandbox/chromium-shim/base/win/sdkdecls.h b/security/sandbox/chromium-shim/base/win/sdkdecls.h index 562d418ff953..e999ab967b72 100644 --- a/security/sandbox/chromium-shim/base/win/sdkdecls.h +++ b/security/sandbox/chromium-shim/base/win/sdkdecls.h @@ -48,6 +48,24 @@ typedef LPSTARTUPINFOEXA LPSTARTUPINFOEX; #define PROCESS_DEP_ENABLE 0x00000001 #define PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION 0x00000002 +// They dynamically load these, but they still use the functions to describe the +// function pointers! +WINBASEAPI +int +WINAPI +GetUserDefaultLocaleName( + _Out_writes_(cchLocaleName) LPWSTR lpLocaleName, + _In_ int cchLocaleName +); + +WINBASEAPI +BOOL +WINAPI +QueryThreadCycleTime( + _In_ HANDLE ThreadHandle, + _Out_ PULONG64 CycleTime + ); + #endif // (_WIN32_WINNT >= 0x0600) #if (_WIN32_WINNT < 0x0601) diff --git a/security/sandbox/chromium/LICENSE b/security/sandbox/chromium/LICENSE index 972bb2edb099..a32e00ce6be3 100644 --- a/security/sandbox/chromium/LICENSE +++ b/security/sandbox/chromium/LICENSE @@ -1,4 +1,4 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. +// Copyright 2015 The Chromium Authors. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are diff --git a/security/sandbox/chromium/base/at_exit.h b/security/sandbox/chromium/base/at_exit.h index 6fe7361a850c..04e3f7642224 100644 --- a/security/sandbox/chromium/base/at_exit.h +++ b/security/sandbox/chromium/base/at_exit.h @@ -8,8 +8,8 @@ #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/callback.h" +#include "base/macros.h" #include "base/synchronization/lock.h" namespace base { diff --git a/security/sandbox/chromium/base/atomic_ref_count.h b/security/sandbox/chromium/base/atomic_ref_count.h index 553fab6a8c65..2ab724200203 100644 --- a/security/sandbox/chromium/base/atomic_ref_count.h +++ b/security/sandbox/chromium/base/atomic_ref_count.h @@ -4,9 +4,6 @@ // This is a low level implementation of atomic semantics for reference // counting. Please use base/memory/ref_counted.h directly instead. -// -// The implementation includes annotations to avoid some false positives -// when using data race detection tools. #ifndef BASE_ATOMIC_REF_COUNT_H_ #define BASE_ATOMIC_REF_COUNT_H_ diff --git a/security/sandbox/chromium/base/atomic_sequence_num.h b/security/sandbox/chromium/base/atomic_sequence_num.h index 7bf27789910a..59b0d2551a4c 100644 --- a/security/sandbox/chromium/base/atomic_sequence_num.h +++ b/security/sandbox/chromium/base/atomic_sequence_num.h @@ -6,7 +6,7 @@ #define BASE_ATOMIC_SEQUENCE_NUM_H_ #include "base/atomicops.h" -#include "base/basictypes.h" +#include "base/macros.h" namespace base { diff --git a/security/sandbox/chromium/base/atomicops.h b/security/sandbox/chromium/base/atomicops.h index 833e1704291b..3428fe87abb3 100644 --- a/security/sandbox/chromium/base/atomicops.h +++ b/security/sandbox/chromium/base/atomicops.h @@ -28,10 +28,14 @@ #ifndef BASE_ATOMICOPS_H_ #define BASE_ATOMICOPS_H_ -#include // Small C++ header which defines implementation specific - // macros used to identify the STL implementation. #include +// Small C++ header which defines implementation specific macros used to +// identify the STL implementation. +// - libc++: captures __config for _LIBCPP_VERSION +// - libstdc++: captures bits/c++config.h for __GLIBCXX__ +#include + #include "base/base_export.h" #include "build/build_config.h" @@ -140,65 +144,13 @@ Atomic64 Release_Load(volatile const Atomic64* ptr); } // namespace subtle } // namespace base -// The following x86 CPU features are used in atomicops_internals_x86_gcc.h, but -// this file is duplicated inside of Chrome: protobuf and tcmalloc rely on the -// struct being present at link time. Some parts of Chrome can currently use the -// portable interface whereas others still use GCC one. The include guards are -// the same as in atomicops_internals_x86_gcc.cc. -#if defined(__i386__) || defined(__x86_64__) -// This struct is not part of the public API of this module; clients may not -// use it. (However, it's exported via BASE_EXPORT because clients implicitly -// do use it at link time by inlining these functions.) -// Features of this x86. Values may not be correct before main() is run, -// but are set conservatively. -struct AtomicOps_x86CPUFeatureStruct { - bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence - // after acquire compare-and-swap. - // The following fields are unused by Chrome's base implementation but are - // still used by copies of the same code in other parts of the code base. This - // causes an ODR violation, and the other code is likely reading invalid - // memory. - // TODO(jfb) Delete these fields once the rest of the Chrome code base doesn't - // depend on them. - bool has_sse2; // Processor has SSE2. - bool has_cmpxchg16b; // Processor supports cmpxchg16b instruction. -}; -BASE_EXPORT extern struct AtomicOps_x86CPUFeatureStruct - AtomicOps_Internalx86CPUFeatures; -#endif - -// Try to use a portable implementation based on C++11 atomics. -// -// Some toolchains support C++11 language features without supporting library -// features (recent compiler, older STL). Whitelist libstdc++ and libc++ that we -// know will have when compiling C++11. -#if ((__cplusplus >= 201103L) && \ - ((defined(__GLIBCXX__) && (__GLIBCXX__ > 20110216)) || \ - (defined(_LIBCPP_VERSION) && (_LIBCPP_STD_VER >= 11)))) +#if defined(OS_WIN) +// TODO(jfb): The MSVC header includes windows.h, which other files end up +// relying on. Fix this as part of crbug.com/559247. +# include "base/atomicops_internals_x86_msvc.h" +#else # include "base/atomicops_internals_portable.h" -#else // Otherwise use a platform specific implementation. -# if defined(THREAD_SANITIZER) -# error "Thread sanitizer must use the portable atomic operations" -# elif (defined(OS_WIN) && defined(COMPILER_MSVC) && \ - defined(ARCH_CPU_X86_FAMILY)) -# include "base/atomicops_internals_x86_msvc.h" -# elif defined(OS_MACOSX) -# include "base/atomicops_internals_mac.h" -# elif defined(OS_NACL) -# include "base/atomicops_internals_gcc.h" -# elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL) -# include "base/atomicops_internals_arm_gcc.h" -# elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64) -# include "base/atomicops_internals_arm64_gcc.h" -# elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) -# include "base/atomicops_internals_x86_gcc.h" -# elif (defined(COMPILER_GCC) && \ - (defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY))) -# include "base/atomicops_internals_mips_gcc.h" -# else -# error "Atomic operations are not supported on your platform" -# endif -#endif // Portable / non-portable includes. +#endif // On some platforms we need additional declarations to make // AtomicWord compatible with our other Atomic* types. diff --git a/security/sandbox/chromium/base/atomicops_internals_arm_gcc.h b/security/sandbox/chromium/base/atomicops_internals_arm_gcc.h deleted file mode 100644 index e654afa7d270..000000000000 --- a/security/sandbox/chromium/base/atomicops_internals_arm_gcc.h +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is an internal atomic implementation, use base/atomicops.h instead. -// -// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. - -#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ -#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ - -#if defined(OS_QNX) -#include -#endif - -namespace base { -namespace subtle { - -// Memory barriers on ARM are funky, but the kernel is here to help: -// -// * ARMv5 didn't support SMP, there is no memory barrier instruction at -// all on this architecture, or when targeting its machine code. -// -// * Some ARMv6 CPUs support SMP. A full memory barrier can be produced by -// writing a random value to a very specific coprocessor register. -// -// * On ARMv7, the "dmb" instruction is used to perform a full memory -// barrier (though writing to the co-processor will still work). -// However, on single core devices (e.g. Nexus One, or Nexus S), -// this instruction will take up to 200 ns, which is huge, even though -// it's completely un-needed on these devices. -// -// * There is no easy way to determine at runtime if the device is -// single or multi-core. However, the kernel provides a useful helper -// function at a fixed memory address (0xffff0fa0), which will always -// perform a memory barrier in the most efficient way. I.e. on single -// core devices, this is an empty function that exits immediately. -// On multi-core devices, it implements a full memory barrier. -// -// * This source could be compiled to ARMv5 machine code that runs on a -// multi-core ARMv6 or ARMv7 device. In this case, memory barriers -// are needed for correct execution. Always call the kernel helper, even -// when targeting ARMv5TE. -// - -inline void MemoryBarrier() { -#if defined(OS_LINUX) || defined(OS_ANDROID) - // Note: This is a function call, which is also an implicit compiler barrier. - typedef void (*KernelMemoryBarrierFunc)(); - ((KernelMemoryBarrierFunc)0xffff0fa0)(); -#elif defined(OS_QNX) - __cpu_membarrier(); -#else -#error MemoryBarrier() is not implemented on this platform. -#endif -} - -// An ARM toolchain would only define one of these depending on which -// variant of the target architecture is being used. This tests against -// any known ARMv6 or ARMv7 variant, where it is possible to directly -// use ldrex/strex instructions to implement fast atomic operations. -#if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \ - defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \ - defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ - defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \ - defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev_value; - int reloop; - do { - // The following is equivalent to: - // - // prev_value = LDREX(ptr) - // reloop = 0 - // if (prev_value != old_value) - // reloop = STREX(ptr, new_value) - __asm__ __volatile__(" ldrex %0, [%3]\n" - " mov %1, #0\n" - " cmp %0, %4\n" -#ifdef __thumb2__ - " it eq\n" -#endif - " strexeq %1, %5, [%3]\n" - : "=&r"(prev_value), "=&r"(reloop), "+m"(*ptr) - : "r"(ptr), "r"(old_value), "r"(new_value) - : "cc", "memory"); - } while (reloop != 0); - return prev_value; -} - -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 result = NoBarrier_CompareAndSwap(ptr, old_value, new_value); - MemoryBarrier(); - return result; -} - -inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - MemoryBarrier(); - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - Atomic32 value; - int reloop; - do { - // Equivalent to: - // - // value = LDREX(ptr) - // value += increment - // reloop = STREX(ptr, value) - // - __asm__ __volatile__(" ldrex %0, [%3]\n" - " add %0, %0, %4\n" - " strex %1, %0, [%3]\n" - : "=&r"(value), "=&r"(reloop), "+m"(*ptr) - : "r"(ptr), "r"(increment) - : "cc", "memory"); - } while (reloop); - return value; -} - -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - // TODO(digit): Investigate if it's possible to implement this with - // a single MemoryBarrier() operation between the LDREX and STREX. - // See http://crbug.com/246514 - MemoryBarrier(); - Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment); - MemoryBarrier(); - return result; -} - -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, - Atomic32 new_value) { - Atomic32 old_value; - int reloop; - do { - // old_value = LDREX(ptr) - // reloop = STREX(ptr, new_value) - __asm__ __volatile__(" ldrex %0, [%3]\n" - " strex %1, %4, [%3]\n" - : "=&r"(old_value), "=&r"(reloop), "+m"(*ptr) - : "r"(ptr), "r"(new_value) - : "cc", "memory"); - } while (reloop != 0); - return old_value; -} - -// This tests against any known ARMv5 variant. -#elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \ - defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__) - -// The kernel also provides a helper function to perform an atomic -// compare-and-swap operation at the hard-wired address 0xffff0fc0. -// On ARMv5, this is implemented by a special code path that the kernel -// detects and treats specially when thread pre-emption happens. -// On ARMv6 and higher, it uses LDREX/STREX instructions instead. -// -// Note that this always perform a full memory barrier, there is no -// need to add calls MemoryBarrier() before or after it. It also -// returns 0 on success, and 1 on exit. -// -// Available and reliable since Linux 2.6.24. Both Android and ChromeOS -// use newer kernel revisions, so this should not be a concern. -namespace { - -inline int LinuxKernelCmpxchg(Atomic32 old_value, - Atomic32 new_value, - volatile Atomic32* ptr) { - typedef int (*KernelCmpxchgFunc)(Atomic32, Atomic32, volatile Atomic32*); - return ((KernelCmpxchgFunc)0xffff0fc0)(old_value, new_value, ptr); -} - -} // namespace - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev_value; - for (;;) { - prev_value = *ptr; - if (prev_value != old_value) - return prev_value; - if (!LinuxKernelCmpxchg(old_value, new_value, ptr)) - return old_value; - } -} - -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, - Atomic32 new_value) { - Atomic32 old_value; - do { - old_value = *ptr; - } while (LinuxKernelCmpxchg(old_value, new_value, ptr)); - return old_value; -} - -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - return Barrier_AtomicIncrement(ptr, increment); -} - -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - for (;;) { - // Atomic exchange the old value with an incremented one. - Atomic32 old_value = *ptr; - Atomic32 new_value = old_value + increment; - if (!LinuxKernelCmpxchg(old_value, new_value, ptr)) { - // The exchange took place as expected. - return new_value; - } - // Otherwise, *ptr changed mid-loop and we need to retry. - } -} - -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev_value; - for (;;) { - prev_value = *ptr; - if (prev_value != old_value) { - // Always ensure acquire semantics. - MemoryBarrier(); - return prev_value; - } - if (!LinuxKernelCmpxchg(old_value, new_value, ptr)) - return old_value; - } -} - -inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - // This could be implemented as: - // MemoryBarrier(); - // return NoBarrier_CompareAndSwap(); - // - // But would use 3 barriers per succesful CAS. To save performance, - // use Acquire_CompareAndSwap(). Its implementation guarantees that: - // - A succesful swap uses only 2 barriers (in the kernel helper). - // - An early return due to (prev_value != old_value) performs - // a memory barrier with no store, which is equivalent to the - // generic implementation above. - return Acquire_CompareAndSwap(ptr, old_value, new_value); -} - -#else -# error "Your CPU's ARM architecture is not supported yet" -#endif - -// NOTE: Atomicity of the following load and store operations is only -// guaranteed in case of 32-bit alignement of |ptr| values. - -inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; -} - -inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; - MemoryBarrier(); -} - -inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { - MemoryBarrier(); - *ptr = value; -} - -inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; } - -inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { - Atomic32 value = *ptr; - MemoryBarrier(); - return value; -} - -inline Atomic32 Release_Load(volatile const Atomic32* ptr) { - MemoryBarrier(); - return *ptr; -} - -} // namespace base::subtle -} // namespace base - -#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ diff --git a/security/sandbox/chromium/base/atomicops_internals_portable.h b/security/sandbox/chromium/base/atomicops_internals_portable.h index b25099fd2205..ee034dee1231 100644 --- a/security/sandbox/chromium/base/atomicops_internals_portable.h +++ b/security/sandbox/chromium/base/atomicops_internals_portable.h @@ -34,6 +34,8 @@ #include +#include "build/build_config.h" + namespace base { namespace subtle { @@ -221,7 +223,7 @@ inline Atomic64 Release_Load(volatile const Atomic64* ptr) { } #endif // defined(ARCH_CPU_64_BITS) -} -} // namespace base::subtle +} // namespace subtle +} // namespace base #endif // BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ diff --git a/security/sandbox/chromium/base/atomicops_internals_x86_gcc.h b/security/sandbox/chromium/base/atomicops_internals_x86_gcc.h deleted file mode 100644 index 69eacdb0873c..000000000000 --- a/security/sandbox/chromium/base/atomicops_internals_x86_gcc.h +++ /dev/null @@ -1,228 +0,0 @@ -// 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 is an internal atomic implementation, use base/atomicops.h instead. - -#ifndef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ -#define BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ - -#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory") - -namespace base { -namespace subtle { - -// 32-bit low-level operations on any platform. - -inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 prev; - __asm__ __volatile__("lock; cmpxchgl %1,%2" - : "=a" (prev) - : "q" (new_value), "m" (*ptr), "0" (old_value) - : "memory"); - return prev; -} - -inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, - Atomic32 new_value) { - __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg. - : "=r" (new_value) - : "m" (*ptr), "0" (new_value) - : "memory"); - return new_value; // Now it's the previous value. -} - -inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - Atomic32 temp = increment; - __asm__ __volatile__("lock; xaddl %0,%1" - : "+r" (temp), "+m" (*ptr) - : : "memory"); - // temp now holds the old value of *ptr - return temp + increment; -} - -inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, - Atomic32 increment) { - Atomic32 temp = increment; - __asm__ __volatile__("lock; xaddl %0,%1" - : "+r" (temp), "+m" (*ptr) - : : "memory"); - // temp now holds the old value of *ptr - if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { - __asm__ __volatile__("lfence" : : : "memory"); - } - return temp + increment; -} - -inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); - if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { - __asm__ __volatile__("lfence" : : : "memory"); - } - return x; -} - -inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, - Atomic32 old_value, - Atomic32 new_value) { - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - -inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; -} - -inline void MemoryBarrier() { - __asm__ __volatile__("mfence" : : : "memory"); -} - -inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { - *ptr = value; - MemoryBarrier(); -} - -inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { - ATOMICOPS_COMPILER_BARRIER(); - *ptr = value; // An x86 store acts as a release barrier. - // See comments in Atomic64 version of Release_Store(), below. -} - -inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { - return *ptr; -} - -inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { - Atomic32 value = *ptr; // An x86 load acts as a acquire barrier. - // See comments in Atomic64 version of Release_Store(), below. - ATOMICOPS_COMPILER_BARRIER(); - return value; -} - -inline Atomic32 Release_Load(volatile const Atomic32* ptr) { - MemoryBarrier(); - return *ptr; -} - -#if defined(__x86_64__) - -// 64-bit low-level operations on 64-bit platform. - -inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - Atomic64 prev; - __asm__ __volatile__("lock; cmpxchgq %1,%2" - : "=a" (prev) - : "q" (new_value), "m" (*ptr), "0" (old_value) - : "memory"); - return prev; -} - -inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, - Atomic64 new_value) { - __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg. - : "=r" (new_value) - : "m" (*ptr), "0" (new_value) - : "memory"); - return new_value; // Now it's the previous value. -} - -inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - Atomic64 temp = increment; - __asm__ __volatile__("lock; xaddq %0,%1" - : "+r" (temp), "+m" (*ptr) - : : "memory"); - // temp now contains the previous value of *ptr - return temp + increment; -} - -inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, - Atomic64 increment) { - Atomic64 temp = increment; - __asm__ __volatile__("lock; xaddq %0,%1" - : "+r" (temp), "+m" (*ptr) - : : "memory"); - // temp now contains the previous value of *ptr - if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { - __asm__ __volatile__("lfence" : : : "memory"); - } - return temp + increment; -} - -inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { - *ptr = value; -} - -inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { - *ptr = value; - MemoryBarrier(); -} - -inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { - ATOMICOPS_COMPILER_BARRIER(); - - *ptr = value; // An x86 store acts as a release barrier - // for current AMD/Intel chips as of Jan 2008. - // See also Acquire_Load(), below. - - // When new chips come out, check: - // IA-32 Intel Architecture Software Developer's Manual, Volume 3: - // System Programming Guide, Chatper 7: Multiple-processor management, - // Section 7.2, Memory Ordering. - // Last seen at: - // http://developer.intel.com/design/pentium4/manuals/index_new.htm - // - // x86 stores/loads fail to act as barriers for a few instructions (clflush - // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are - // not generated by the compiler, and are rare. Users of these instructions - // need to know about cache behaviour in any case since all of these involve - // either flushing cache lines or non-temporal cache hints. -} - -inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { - return *ptr; -} - -inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { - Atomic64 value = *ptr; // An x86 load acts as a acquire barrier, - // for current AMD/Intel chips as of Jan 2008. - // See also Release_Store(), above. - ATOMICOPS_COMPILER_BARRIER(); - return value; -} - -inline Atomic64 Release_Load(volatile const Atomic64* ptr) { - MemoryBarrier(); - return *ptr; -} - -inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); - if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { - __asm__ __volatile__("lfence" : : : "memory"); - } - return x; -} - -inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, - Atomic64 old_value, - Atomic64 new_value) { - return NoBarrier_CompareAndSwap(ptr, old_value, new_value); -} - -#endif // defined(__x86_64__) - -} // namespace base::subtle -} // namespace base - -#undef ATOMICOPS_COMPILER_BARRIER - -#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ diff --git a/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h b/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h index 0269d894a1fa..9f05b7e78d04 100644 --- a/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h +++ b/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h @@ -12,6 +12,7 @@ #include #include "base/macros.h" +#include "build/build_config.h" #if defined(ARCH_CPU_64_BITS) // windows.h #defines this (only on x64). This causes problems because the @@ -55,9 +56,6 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, return Barrier_AtomicIncrement(ptr, increment); } -#if !(defined(_MSC_VER) && _MSC_VER >= 1400) -#error "We require at least vs2005 for MemoryBarrier" -#endif inline void MemoryBarrier() { #if defined(ARCH_CPU_64_BITS) // See #undef and note at the top of this file. @@ -112,7 +110,7 @@ inline Atomic32 Release_Load(volatile const Atomic32* ptr) { // 64-bit low-level operations on 64-bit platform. -COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic); +static_assert(sizeof(Atomic64) == sizeof(PVOID), "atomic word is atomic"); inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, Atomic64 old_value, @@ -192,7 +190,7 @@ inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, #endif // defined(_WIN64) -} // namespace base::subtle +} // namespace subtle } // namespace base #endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ diff --git a/security/sandbox/chromium/base/base_export.h b/security/sandbox/chromium/base/base_export.h index 723b38a60fdc..cf7ebd78162d 100644 --- a/security/sandbox/chromium/base/base_export.h +++ b/security/sandbox/chromium/base/base_export.h @@ -10,25 +10,20 @@ #if defined(BASE_IMPLEMENTATION) #define BASE_EXPORT __declspec(dllexport) -#define BASE_EXPORT_PRIVATE __declspec(dllexport) #else #define BASE_EXPORT __declspec(dllimport) -#define BASE_EXPORT_PRIVATE __declspec(dllimport) #endif // defined(BASE_IMPLEMENTATION) #else // defined(WIN32) #if defined(BASE_IMPLEMENTATION) #define BASE_EXPORT __attribute__((visibility("default"))) -#define BASE_EXPORT_PRIVATE __attribute__((visibility("default"))) #else #define BASE_EXPORT -#define BASE_EXPORT_PRIVATE #endif // defined(BASE_IMPLEMENTATION) #endif #else // defined(COMPONENT_BUILD) #define BASE_EXPORT -#define BASE_EXPORT_PRIVATE #endif #endif // BASE_BASE_EXPORT_H_ diff --git a/security/sandbox/chromium/base/base_paths_win.h b/security/sandbox/chromium/base/base_paths_win.h index 032de348c5c8..d9dbc39f99ab 100644 --- a/security/sandbox/chromium/base/base_paths_win.h +++ b/security/sandbox/chromium/base/base_paths_win.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_BASE_PATHS_WIN_H__ -#define BASE_BASE_PATHS_WIN_H__ +#ifndef BASE_BASE_PATHS_WIN_H_ +#define BASE_BASE_PATHS_WIN_H_ // This file declares windows-specific path keys for the base module. // These can be used with the PathService to access various special @@ -16,8 +16,14 @@ enum { DIR_WINDOWS, // Windows directory, usually "c:\windows" DIR_SYSTEM, // Usually c:\windows\system32" - DIR_PROGRAM_FILES, // Usually c:\program files - DIR_PROGRAM_FILESX86, // Usually c:\program files or c:\program files (x86) + // 32-bit 32-bit on 64-bit 64-bit on 64-bit + // DIR_PROGRAM_FILES 1 2 1 + // DIR_PROGRAM_FILESX86 1 2 2 + // DIR_PROGRAM_FILES6432 1 1 1 + // 1 - C:\Program Files 2 - C:\Program Files (x86) + DIR_PROGRAM_FILES, // See table above. + DIR_PROGRAM_FILESX86, // See table above. + DIR_PROGRAM_FILES6432, // See table above. DIR_IE_INTERNET_CACHE, // Temporary Internet Files directory. DIR_COMMON_START_MENU, // Usually "C:\Documents and Settings\All Users\ @@ -36,8 +42,8 @@ enum { DIR_COMMON_DESKTOP, // Directory for the common desktop (visible // on all user's Desktop). DIR_USER_QUICK_LAUNCH, // Directory for the quick launch shortcuts. - DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar via - // base::win::TaskbarPinShortcutLink(). + DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar + // (Win7-8) via base::win::PinShortcutToTaskbar(). DIR_WINDOWS_FONTS, // Usually C:\Windows\Fonts. PATH_WIN_END @@ -45,4 +51,4 @@ enum { } // namespace base -#endif // BASE_BASE_PATHS_WIN_H__ +#endif // BASE_BASE_PATHS_WIN_H_ diff --git a/security/sandbox/chromium/base/base_switches.cc b/security/sandbox/chromium/base/base_switches.cc index 27f52cdb3302..02b22298850a 100644 --- a/security/sandbox/chromium/base/base_switches.cc +++ b/security/sandbox/chromium/base/base_switches.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/base_switches.h" +#include "build/build_config.h" namespace switches { @@ -14,13 +15,29 @@ const char kDisableBreakpad[] = "disable-breakpad"; // generated internally. const char kEnableCrashReporter[] = "enable-crash-reporter"; +// Makes memory allocators keep track of their allocations and context, so a +// detailed breakdown of memory usage can be presented in chrome://tracing when +// the memory-infra category is enabled. +const char kEnableHeapProfiling[] = "enable-heap-profiling"; + // Generates full memory crash dump. const char kFullMemoryCrashReport[] = "full-memory-crash-report"; -// Force low-end device when set to 1; -// Auto-detect low-end device when set to 2; -// Force non-low-end device when set to other values or empty; -const char kLowEndDeviceMode[] = "low-end-device-mode"; +// Force low-end device mode when set. +const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode"; + +// Force disabling of low-end device mode when set. +const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode"; + +// This option can be used to force field trials when testing changes locally. +// The argument is a list of name and value pairs, separated by slashes. If a +// trial name is prefixed with an asterisk, that trial will start activated. +// For example, the following argument defines two trials, with the second one +// activated: "GoogleNow/Enable/*MaterialDesignNTP/Default/" This option can +// also be used by the browser process to send the list of trials to a +// non-browser process, using the same format. See +// FieldTrialList::CreateTrialsFromString() in field_trial.h for details. +const char kForceFieldTrials[] = "force-fieldtrials"; // Suppresses all error dialogs when present. const char kNoErrorDialogs[] = "noerrdialogs"; @@ -47,9 +64,6 @@ const char kVModule[] = "vmodule"; // Will wait for 60 seconds for a debugger to come to attach to the process. const char kWaitForDebugger[] = "wait-for-debugger"; -// Sends a pretty-printed version of tracing info to the console. -const char kTraceToConsole[] = "trace-to-console"; - // Sends trace events from these categories to a file. // --trace-to-file on its own sends to default categories. const char kTraceToFile[] = "trace-to-file"; @@ -66,6 +80,11 @@ const char kProfilerTiming[] = "profiler-timing"; // chrome://profiler. const char kProfilerTimingDisabledValue[] = "0"; +#if defined(OS_WIN) +// Disables the USB keyboard detection for blocking the OSK on Win8+. +const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect"; +#endif + #if defined(OS_POSIX) // Used for turning on Breakpad crash reporting in a debug environment where // crash reporting is typically compiled but disabled. diff --git a/security/sandbox/chromium/base/base_switches.h b/security/sandbox/chromium/base/base_switches.h index 96cbd5bbb564..c97a629d9fb2 100644 --- a/security/sandbox/chromium/base/base_switches.h +++ b/security/sandbox/chromium/base/base_switches.h @@ -12,20 +12,26 @@ namespace switches { extern const char kDisableBreakpad[]; +extern const char kDisableLowEndDeviceMode[]; extern const char kEnableCrashReporter[]; +extern const char kEnableHeapProfiling[]; +extern const char kEnableLowEndDeviceMode[]; +extern const char kForceFieldTrials[]; extern const char kFullMemoryCrashReport[]; -extern const char kLowEndDeviceMode[]; extern const char kNoErrorDialogs[]; extern const char kProfilerTiming[]; extern const char kProfilerTimingDisabledValue[]; extern const char kTestChildProcess[]; -extern const char kTraceToConsole[]; extern const char kTraceToFile[]; extern const char kTraceToFileName[]; extern const char kV[]; extern const char kVModule[]; extern const char kWaitForDebugger[]; +#if defined(OS_WIN) +extern const char kDisableUsbKeyboardDetect[]; +#endif + #if defined(OS_POSIX) extern const char kEnableCrashReporterForTesting[]; #endif diff --git a/security/sandbox/chromium/base/basictypes.h b/security/sandbox/chromium/base/basictypes.h deleted file mode 100644 index bf75e6731572..000000000000 --- a/security/sandbox/chromium/base/basictypes.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains definitions of our old basic integral types -// ((u)int{8,16,32,64}) and further includes. I recommend that you use the C99 -// standard types instead, and include //etc. as needed. -// Note that the macros and macro-like constructs that were formerly defined in -// this file are now available separately in base/macros.h. - -#ifndef BASE_BASICTYPES_H_ -#define BASE_BASICTYPES_H_ - -#include // So we can set the bounds of our types. -#include // For size_t. -#include // For intptr_t. - -#include "base/macros.h" -#include "base/port.h" // Types that only need exist on certain systems. - -// DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include ). -typedef int8_t int8; -typedef uint8_t uint8; -typedef int16_t int16; -typedef uint16_t uint16; -typedef int32_t int32; -typedef uint32_t uint32; -typedef int64_t int64; -typedef uint64_t uint64; - -// DEPRECATED: Please use std::numeric_limits (from ) instead. -const uint8 kuint8max = 0xFF; -const uint16 kuint16max = 0xFFFF; -const uint32 kuint32max = 0xFFFFFFFF; -const uint64 kuint64max = 0xFFFFFFFFFFFFFFFFULL; -const int8 kint8min = -0x7F - 1; -const int8 kint8max = 0x7F; -const int16 kint16min = -0x7FFF - 1; -const int16 kint16max = 0x7FFF; -const int32 kint32min = -0x7FFFFFFF - 1; -const int32 kint32max = 0x7FFFFFFF; -const int64 kint64min = -0x7FFFFFFFFFFFFFFFLL - 1; -const int64 kint64max = 0x7FFFFFFFFFFFFFFFLL; - -#endif // BASE_BASICTYPES_H_ diff --git a/security/sandbox/chromium/base/bind.h b/security/sandbox/chromium/base/bind.h index b14f70c109f7..770e45706b02 100644 --- a/security/sandbox/chromium/base/bind.h +++ b/security/sandbox/chromium/base/bind.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py bind.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -46,464 +41,59 @@ // // TODO(ajwong): We might be able to avoid this now, but need to test. // -// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>, -// but it feels a little nicer to have the asserts here so people do not -// need to crack open bind_internal.h. On the other hand, it makes Bind() -// harder to read. +// It is possible to move most of the static_assert into BindState<>, but it +// feels a little nicer to have the asserts here so people do not need to crack +// open bind_internal.h. On the other hand, it makes Bind() harder to read. namespace base { -template +template base::Callback< typename internal::BindState< typename internal::FunctorTraits::RunnableType, typename internal::FunctorTraits::RunType, - void()> + typename internal::CallbackParamTraits::StorageType...> ::UnboundRunType> -Bind(Functor functor) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - typedef internal::BindState BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor))); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; +Bind(Functor functor, const Args&... args) { + // Type aliases for how to store and run the functor. + using RunnableType = typename internal::FunctorTraits::RunnableType; + using RunType = typename internal::FunctorTraits::RunType; // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual + // checks below for bound references need to know what the actual // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; + using BoundRunType = typename RunnableType::RunType; + + using BoundArgs = + internal::TakeTypeListItem>; // Do not allow binding a non-const reference parameter. Non-const reference // parameters are disallowed by the Google style guide. Also, binding a // non-const reference parameter can make for subtle bugs because the // invoked function will receive a reference to the stored copy of the // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); + static_assert(!internal::HasNonConstReferenceItem::value, + "do not bind functions with nonconst ref"); + + const bool is_method = internal::HasIsMethodTag::value; // For methods, we need to be careful for parameter 1. We do not require // a scoped_refptr because BindState<> itself takes care of AddRef() for // methods. We also disallow binding of an array as the method's target // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - typedef internal::BindState::StorageType)> BindState; + static_assert(!internal::BindsArrayToFirstArg::value, + "first bound argument to method cannot be array"); + static_assert( + !internal::HasRefCountedParamAsRawPtr::value, + "a parameter is a refcounted type and needs scoped_refptr"); + using BindState = internal::BindState< + RunnableType, RunType, + typename internal::CallbackParamTraits::StorageType...>; return Callback( - new BindState(internal::MakeRunnable(functor), p1)); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2)); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3)); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4)); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5)); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p6_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6)); -} - -template -base::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6, const P7& p7) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p6_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p7_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6, - p7)); + new BindState(internal::MakeRunnable(functor), args...)); } } // namespace base diff --git a/security/sandbox/chromium/base/bind_helpers.h b/security/sandbox/chromium/base/bind_helpers.h index f3efc31953c7..2add755b4260 100644 --- a/security/sandbox/chromium/base/bind_helpers.h +++ b/security/sandbox/chromium/base/bind_helpers.h @@ -111,7 +111,7 @@ // scoped_ptr f(new Foo()); // // // |cb| is given ownership of Foo(). |f| is now NULL. -// // You can use f.Pass() in place of &f, but it's more verbose. +// // You can use std::move(f) in place of &f, but it's more verbose. // Closure cb = Bind(&TakesOwnership, Passed(&f)); // // // Run was never called so |cb| still owns Foo() and deletes @@ -129,7 +129,7 @@ // Passed() is particularly useful with PostTask() when you are transferring // ownership of an argument into a task, but don't necessarily know if the // task will always be executed. This can happen if the task is cancellable -// or if it is posted to a MessageLoopProxy. +// or if it is posted to a TaskRunner. // // // SIMPLE FUNCTIONS AND UTILITIES. @@ -143,10 +143,15 @@ #ifndef BASE_BIND_HELPERS_H_ #define BASE_BIND_HELPERS_H_ -#include "base/basictypes.h" +#include + +#include +#include + #include "base/callback.h" #include "base/memory/weak_ptr.h" #include "base/template_util.h" +#include "build/build_config.h" namespace base { namespace internal { @@ -185,7 +190,7 @@ namespace internal { // want to probe for. Then we create a class Base that inherits from both T // (the class we wish to probe) and BaseMixin. Note that the function // signature in BaseMixin does not need to match the signature of the function -// we are probing for; thus it's easiest to just use void(void). +// we are probing for; thus it's easiest to just use void(). // // Now, if TargetFunc exists somewhere in T, then &Base::TargetFunc has an // ambiguous resolution between BaseMixin and T. This lets us write the @@ -222,8 +227,8 @@ namespace internal { // See http://crbug.com/82038. template class SupportsAddRefAndRelease { - typedef char Yes[1]; - typedef char No[2]; + using Yes = char[1]; + using No = char[2]; struct BaseMixin { void AddRef(); @@ -242,7 +247,7 @@ class SupportsAddRefAndRelease { #pragma warning(pop) #endif - template struct Helper {}; + template struct Helper {}; template static No& Check(Helper<&C::AddRef>*); @@ -276,8 +281,8 @@ struct UnsafeBindtoRefCountedArg template class HasIsMethodTag { - typedef char Yes[1]; - typedef char No[2]; + using Yes = char[1]; + using No = char[2]; template static Yes& Check(typename U::IsMethod*); @@ -359,22 +364,24 @@ class OwnedWrapper { // created when we are explicitly trying to do a destructive move. // // Two notes: -// 1) PassedWrapper supports any type that has a "Pass()" function. -// This is intentional. The whitelisting of which specific types we -// support is maintained by CallbackParamTraits<>. +// 1) PassedWrapper supports any type that has a move constructor, however +// the type will need to be specifically whitelisted in order for it to be +// bound to a Callback. We guard this explicitly at the call of Passed() +// to make for clear errors. Things not given to Passed() will be forwarded +// and stored by value which will not work for general move-only types. // 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL" // scoper to a Callback and allow the Callback to execute once. template class PassedWrapper { public: - explicit PassedWrapper(T scoper) : is_valid_(true), scoper_(scoper.Pass()) {} + explicit PassedWrapper(T&& scoper) + : is_valid_(true), scoper_(std::move(scoper)) {} PassedWrapper(const PassedWrapper& other) - : is_valid_(other.is_valid_), scoper_(other.scoper_.Pass()) { - } + : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {} T Pass() const { CHECK(is_valid_); is_valid_ = false; - return scoper_.Pass(); + return std::move(scoper_); } private: @@ -385,13 +392,13 @@ class PassedWrapper { // Unwrap the stored parameters for the wrappers above. template struct UnwrapTraits { - typedef const T& ForwardType; + using ForwardType = const T&; static ForwardType Unwrap(const T& o) { return o; } }; template struct UnwrapTraits > { - typedef T* ForwardType; + using ForwardType = T*; static ForwardType Unwrap(UnretainedWrapper unretained) { return unretained.get(); } @@ -399,7 +406,7 @@ struct UnwrapTraits > { template struct UnwrapTraits > { - typedef const T& ForwardType; + using ForwardType = const T&; static ForwardType Unwrap(ConstRefWrapper const_ref) { return const_ref.get(); } @@ -407,19 +414,19 @@ struct UnwrapTraits > { template struct UnwrapTraits > { - typedef T* ForwardType; + using ForwardType = T*; static ForwardType Unwrap(const scoped_refptr& o) { return o.get(); } }; template struct UnwrapTraits > { - typedef const WeakPtr& ForwardType; + using ForwardType = const WeakPtr&; static ForwardType Unwrap(const WeakPtr& o) { return o; } }; template struct UnwrapTraits > { - typedef T* ForwardType; + using ForwardType = T*; static ForwardType Unwrap(const OwnedWrapper& o) { return o.get(); } @@ -427,7 +434,7 @@ struct UnwrapTraits > { template struct UnwrapTraits > { - typedef T ForwardType; + using ForwardType = T; static T Unwrap(PassedWrapper& o) { return o.Pass(); } @@ -435,45 +442,46 @@ struct UnwrapTraits > { // Utility for handling different refcounting semantics in the Bind() // function. -template -struct MaybeRefcount; +template +struct MaybeScopedRefPtr; -template -struct MaybeRefcount { - static void AddRef(const T&) {} - static void Release(const T&) {} +template +struct MaybeScopedRefPtr { + MaybeScopedRefPtr() {} }; -template -struct MaybeRefcount { - static void AddRef(const T*) {} - static void Release(const T*) {} +template +struct MaybeScopedRefPtr { + MaybeScopedRefPtr(const T&, const Rest&...) {} }; -template -struct MaybeRefcount { - static void AddRef(const T&) {} - static void Release(const T&) {} +template +struct MaybeScopedRefPtr { + MaybeScopedRefPtr(const T*, const Rest&...) {} }; -template -struct MaybeRefcount { - static void AddRef(T* o) { o->AddRef(); } - static void Release(T* o) { o->Release(); } +template +struct MaybeScopedRefPtr { + MaybeScopedRefPtr(const T& o, const Rest&...) {} +}; + +template +struct MaybeScopedRefPtr { + MaybeScopedRefPtr(T* o, const Rest&...) : ref_(o) {} + scoped_refptr ref_; }; // No need to additionally AddRef() and Release() since we are storing a // scoped_refptr<> inside the storage object already. -template -struct MaybeRefcount > { - static void AddRef(const scoped_refptr& o) {} - static void Release(const scoped_refptr& o) {} +template +struct MaybeScopedRefPtr, Rest...> { + MaybeScopedRefPtr(const scoped_refptr&, const Rest&...) {} }; -template -struct MaybeRefcount { - static void AddRef(const T* o) { o->AddRef(); } - static void Release(const T* o) { o->Release(); } +template +struct MaybeScopedRefPtr { + MaybeScopedRefPtr(const T* o, const Rest&...) : ref_(o) {} + scoped_refptr ref_; }; // IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a @@ -481,15 +489,113 @@ struct MaybeRefcount { // InvokeHelper that will no-op itself in the event the WeakPtr<> for // the target object is invalidated. // -// P1 should be the type of the object that will be received of the method. -template +// The first argument should be the type of the object that will be received by +// the method. +template struct IsWeakMethod : public false_type {}; -template -struct IsWeakMethod > : public true_type {}; +template +struct IsWeakMethod, Args...> : public true_type {}; -template -struct IsWeakMethod > > : public true_type {}; +template +struct IsWeakMethod>, Args...> + : public true_type {}; + + +// Packs a list of types to hold them in a single type. +template +struct TypeList {}; + +// Used for DropTypeListItem implementation. +template +struct DropTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template +struct DropTypeListItemImpl> + : DropTypeListItemImpl> {}; + +template +struct DropTypeListItemImpl<0, TypeList> { + using Type = TypeList; +}; + +template <> +struct DropTypeListItemImpl<0, TypeList<>> { + using Type = TypeList<>; +}; + +// A type-level function that drops |n| list item from given TypeList. +template +using DropTypeListItem = typename DropTypeListItemImpl::Type; + +// Used for TakeTypeListItem implementation. +template +struct TakeTypeListItemImpl; + +// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure. +template +struct TakeTypeListItemImpl, Accum...> + : TakeTypeListItemImpl, Accum..., T> {}; + +template +struct TakeTypeListItemImpl<0, TypeList, Accum...> { + using Type = TypeList; +}; + +template +struct TakeTypeListItemImpl<0, TypeList<>, Accum...> { + using Type = TypeList; +}; + +// A type-level function that takes first |n| list item from given TypeList. +// E.g. TakeTypeListItem<3, TypeList> is evaluated to +// TypeList. +template +using TakeTypeListItem = typename TakeTypeListItemImpl::Type; + +// Used for ConcatTypeLists implementation. +template +struct ConcatTypeListsImpl; + +template +struct ConcatTypeListsImpl, TypeList> { + using Type = TypeList; +}; + +// A type-level function that concats two TypeLists. +template +using ConcatTypeLists = typename ConcatTypeListsImpl::Type; + +// Used for MakeFunctionType implementation. +template +struct MakeFunctionTypeImpl; + +template +struct MakeFunctionTypeImpl> { + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R Type(Args...); +}; + +// A type-level function that constructs a function type that has |R| as its +// return type and has TypeLists items as its arguments. +template +using MakeFunctionType = typename MakeFunctionTypeImpl::Type; + +// Used for ExtractArgs. +template +struct ExtractArgsImpl; + +template +struct ExtractArgsImpl { + using Type = TypeList; +}; + +// A type-level function that extracts function arguments into a TypeList. +// E.g. ExtractArgs is evaluated to TypeList. +template +using ExtractArgs = typename ExtractArgsImpl::Type; } // namespace internal @@ -508,17 +614,25 @@ static inline internal::OwnedWrapper Owned(T* o) { return internal::OwnedWrapper(o); } -// We offer 2 syntaxes for calling Passed(). The first takes a temporary and -// is best suited for use with the return value of a function. The second -// takes a pointer to the scoper and is just syntactic sugar to avoid having -// to write Passed(scoper.Pass()). -template -static inline internal::PassedWrapper Passed(T scoper) { - return internal::PassedWrapper(scoper.Pass()); +// We offer 2 syntaxes for calling Passed(). The first takes an rvalue and +// is best suited for use with the return value of a function or other temporary +// rvalues. The second takes a pointer to the scoper and is just syntactic sugar +// to avoid having to write Passed(std::move(scoper)). +// +// Both versions of Passed() prevent T from being an lvalue reference. The first +// via use of enable_if, and the second takes a T* which will not bind to T&. +template ::value && + !std::is_lvalue_reference::value>::type* = + nullptr> +static inline internal::PassedWrapper Passed(T&& scoper) { + return internal::PassedWrapper(std::move(scoper)); } -template +template ::value>::type* = + nullptr> static inline internal::PassedWrapper Passed(T* scoper) { - return internal::PassedWrapper(scoper->Pass()); + return internal::PassedWrapper(std::move(*scoper)); } template diff --git a/security/sandbox/chromium/base/bind_internal.h b/security/sandbox/chromium/base/bind_internal.h index ae17ebf86c1a..ac7cd0098787 100644 --- a/security/sandbox/chromium/base/bind_internal.h +++ b/security/sandbox/chromium/base/bind_internal.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py bind_internal.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -10,11 +5,16 @@ #ifndef BASE_BIND_INTERNAL_H_ #define BASE_BIND_INTERNAL_H_ +#include + +#include + #include "base/bind_helpers.h" #include "base/callback_internal.h" #include "base/memory/raw_scoped_refptr_mismatch_checker.h" #include "base/memory/weak_ptr.h" #include "base/template_util.h" +#include "base/tuple.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -41,43 +41,83 @@ namespace internal { // even if the invocation syntax differs. // RunType -- A function type (as opposed to function _pointer_ type) for // a Run() function. Usually just a convenience typedef. -// (Bound)ArgsType -- A function type that is being (ab)used to store the -// types of set of arguments. The "return" type is always -// void here. We use this hack so that we do not need -// a new type name for each arity of type. (eg., -// BindState1, BindState2). This makes forward -// declarations and friending much much easier. +// (Bound)Args -- A set of types that stores the arguments. // // Types: // RunnableAdapter<> -- Wraps the various "function" pointer types into an // object that adheres to the Runnable interface. -// There are |3*ARITY| RunnableAdapter types. -// FunctionTraits<> -- Type traits that unwrap a function signature into a -// a set of easier to use typedefs. Used mainly for -// compile time asserts. -// There are |ARITY| FunctionTraits types. // ForceVoidReturn<> -- Helper class for translating function signatures to // equivalent forms with a "void" return type. -// There are |ARITY| ForceVoidReturn types. // FunctorTraits<> -- Type traits used determine the correct RunType and // RunnableType for a Functor. This is where function // signature adapters are applied. -// There are |ARITY| ForceVoidReturn types. // MakeRunnable<> -- Takes a Functor and returns an object in the Runnable // type class that represents the underlying Functor. -// There are |O(1)| MakeRunnable types. // InvokeHelper<> -- Take a Runnable + arguments and actully invokes it. -// Handle the differing syntaxes needed for WeakPtr<> support, -// and for ignoring return values. This is separate from -// Invoker to avoid creating multiple version of Invoker<> -// which grows at O(n^2) with the arity. -// There are |k*ARITY| InvokeHelper types. +// Handle the differing syntaxes needed for WeakPtr<> +// support, and for ignoring return values. This is separate +// from Invoker to avoid creating multiple version of +// Invoker<>. // Invoker<> -- Unwraps the curried parameters and executes the Runnable. -// There are |(ARITY^2 + ARITY)/2| Invoketypes. // BindState<> -- Stores the curried parameters, and is the main entry point // into the Bind() system, doing most of the type resolution. // There are ARITY BindState types. +// HasNonConstReferenceParam selects true_type when any of the parameters in +// |Sig| is a non-const reference. +// Implementation note: This non-specialized case handles zero-arity case only. +// Non-zero-arity cases should be handled by the specialization below. +template +struct HasNonConstReferenceItem : false_type {}; + +// Implementation note: Select true_type if the first parameter is a non-const +// reference. Otherwise, skip the first parameter and check rest of parameters +// recursively. +template +struct HasNonConstReferenceItem> + : std::conditional::value, + true_type, + HasNonConstReferenceItem>>::type {}; + +// HasRefCountedTypeAsRawPtr selects true_type when any of the |Args| is a raw +// pointer to a RefCounted type. +// Implementation note: This non-specialized case handles zero-arity case only. +// Non-zero-arity cases should be handled by the specialization below. +template +struct HasRefCountedTypeAsRawPtr : false_type {}; + +// Implementation note: Select true_type if the first parameter is a raw pointer +// to a RefCounted type. Otherwise, skip the first parameter and check rest of +// parameters recursively. +template +struct HasRefCountedTypeAsRawPtr + : std::conditional::value, + true_type, + HasRefCountedTypeAsRawPtr>::type {}; + +// BindsArrayToFirstArg selects true_type when |is_method| is true and the first +// item of |Args| is an array type. +// Implementation note: This non-specialized case handles !is_method case and +// zero-arity case only. Other cases should be handled by the specialization +// below. +template +struct BindsArrayToFirstArg : false_type {}; + +template +struct BindsArrayToFirstArg : is_array {}; + +// HasRefCountedParamAsRawPtr is the same to HasRefCountedTypeAsRawPtr except +// when |is_method| is true HasRefCountedParamAsRawPtr skips the first argument. +// Implementation note: This non-specialized case handles !is_method case and +// zero-arity case only. Other cases should be handled by the specialization +// below. +template +struct HasRefCountedParamAsRawPtr : HasRefCountedTypeAsRawPtr {}; + +template +struct HasRefCountedParamAsRawPtr + : HasRefCountedTypeAsRawPtr {}; + // RunnableAdapter<> // // The RunnableAdapter<> templates provide a uniform interface for invoking @@ -101,625 +141,65 @@ namespace internal { template class RunnableAdapter; -// Function: Arity 0. -template -class RunnableAdapter { +// Function. +template +class RunnableAdapter { public: - typedef R (RunType)(); + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R RunType(Args...); - explicit RunnableAdapter(R(*function)()) + explicit RunnableAdapter(R(*function)(Args...)) : function_(function) { } - R Run() { - return function_(); + R Run(typename CallbackParamTraits::ForwardType... args) { + return function_(CallbackForward(args)...); } private: - R (*function_)(); + R (*function_)(Args...); }; -// Method: Arity 0. -template -class RunnableAdapter { +// Method. +template +class RunnableAdapter { public: - typedef R (RunType)(T*); - typedef true_type IsMethod; + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R RunType(T*, Args...); + using IsMethod = true_type; - explicit RunnableAdapter(R(T::*method)()) + explicit RunnableAdapter(R(T::*method)(Args...)) : method_(method) { } - R Run(T* object) { - return (object->*method_)(); + R Run(T* object, typename CallbackParamTraits::ForwardType... args) { + return (object->*method_)(CallbackForward(args)...); } private: - R (T::*method_)(); + R (T::*method_)(Args...); }; -// Const Method: Arity 0. -template -class RunnableAdapter { +// Const Method. +template +class RunnableAdapter { public: - typedef R (RunType)(const T*); - typedef true_type IsMethod; + using RunType = R(const T*, Args...); + using IsMethod = true_type; - explicit RunnableAdapter(R(T::*method)() const) + explicit RunnableAdapter(R(T::*method)(Args...) const) : method_(method) { } - R Run(const T* object) { - return (object->*method_)(); + R Run(const T* object, + typename CallbackParamTraits::ForwardType... args) { + return (object->*method_)(CallbackForward(args)...); } private: - R (T::*method_)() const; -}; - -// Function: Arity 1. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1); - - explicit RunnableAdapter(R(*function)(A1)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1) { - return function_(CallbackForward(a1)); - } - - private: - R (*function_)(A1); -}; - -// Method: Arity 1. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1) { - return (object->*method_)(CallbackForward(a1)); - } - - private: - R (T::*method_)(A1); -}; - -// Const Method: Arity 1. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1) { - return (object->*method_)(CallbackForward(a1)); - } - - private: - R (T::*method_)(A1) const; -}; - -// Function: Arity 2. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2); - - explicit RunnableAdapter(R(*function)(A1, A2)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2) { - return function_(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (*function_)(A1, A2); -}; - -// Method: Arity 2. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (T::*method_)(A1, A2); -}; - -// Const Method: Arity 2. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (T::*method_)(A1, A2) const; -}; - -// Function: Arity 3. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3); - - explicit RunnableAdapter(R(*function)(A1, A2, A3)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (*function_)(A1, A2, A3); -}; - -// Method: Arity 3. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (T::*method_)(A1, A2, A3); -}; - -// Const Method: Arity 3. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (T::*method_)(A1, A2, A3) const; -}; - -// Function: Arity 4. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (*function_)(A1, A2, A3, A4); -}; - -// Method: Arity 4. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (T::*method_)(A1, A2, A3, A4); -}; - -// Const Method: Arity 4. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (T::*method_)(A1, A2, A3, A4) const; -}; - -// Function: Arity 5. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4, A5); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5); -}; - -// Method: Arity 5. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5); -}; - -// Const Method: Arity 5. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5) const; -}; - -// Function: Arity 6. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4, A5, A6); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5, A6); -}; - -// Method: Arity 6. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6); -}; - -// Const Method: Arity 6. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6) const; -}; - -// Function: Arity 7. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6, A7)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6, - typename CallbackParamTraits::ForwardType a7) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5, A6, A7); -}; - -// Method: Arity 7. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6, A7); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6, - typename CallbackParamTraits::ForwardType a7) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6, A7); -}; - -// Const Method: Arity 7. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6, A7); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6, - typename CallbackParamTraits::ForwardType a7) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6, A7) const; -}; - - -// FunctionTraits<> -// -// Breaks a function signature apart into typedefs for easier introspection. -template -struct FunctionTraits; - -template -struct FunctionTraits { - typedef R ReturnType; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; - typedef A6 A6Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; - typedef A6 A6Type; - typedef A7 A7Type; + R (T::*method_)(Args...) const; }; @@ -729,47 +209,11 @@ struct FunctionTraits { template struct ForceVoidReturn; -template -struct ForceVoidReturn { - typedef void(RunType)(); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4, A5); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4, A5, A6); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4, A5, A6, A7); +template +struct ForceVoidReturn { + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef void RunType(Args...); }; @@ -778,21 +222,21 @@ struct ForceVoidReturn { // See description at top of file. template struct FunctorTraits { - typedef RunnableAdapter RunnableType; - typedef typename RunnableType::RunType RunType; + using RunnableType = RunnableAdapter; + using RunType = typename RunnableType::RunType; }; template -struct FunctorTraits > { - typedef typename FunctorTraits::RunnableType RunnableType; - typedef typename ForceVoidReturn< - typename RunnableType::RunType>::RunType RunType; +struct FunctorTraits> { + using RunnableType = typename FunctorTraits::RunnableType; + using RunType = + typename ForceVoidReturn::RunType; }; template -struct FunctorTraits > { - typedef Callback RunnableType; - typedef typename Callback::RunType RunType; +struct FunctorTraits> { + using RunnableType = Callback ; + using RunType = typename Callback::RunType; }; @@ -812,7 +256,7 @@ MakeRunnable(const IgnoreResultHelper& t) { } template -const typename FunctorTraits >::RunnableType& +const typename FunctorTraits>::RunnableType& MakeRunnable(const Callback& t) { DCHECK(!t.is_null()); return t; @@ -840,246 +284,27 @@ template struct InvokeHelper; -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable) { - return runnable.Run(); +template +struct InvokeHelper> { + static ReturnType MakeItSo(Runnable runnable, Args... args) { + return runnable.Run(CallbackForward(args)...); } }; -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable) { - runnable.Run(); +template +struct InvokeHelper> { + static void MakeItSo(Runnable runnable, Args... args) { + runnable.Run(CallbackForward(args)...); } }; -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1) { - return runnable.Run(CallbackForward(a1)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1) { - runnable.Run(CallbackForward(a1)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr) { +template +struct InvokeHelper> { + static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, Args... args) { if (!weak_ptr.get()) { return; } - runnable.Run(weak_ptr.get()); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2) { - runnable.Run(CallbackForward(a1), CallbackForward(a2)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5, A6 a6) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, - A6 a6) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5, A6 a6) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5, A6 a6, A7 a7) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, - A6 a6, A7 a7) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6), - CallbackForward(a7)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5, A6 a6, A7 a7) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6), - CallbackForward(a7)); + runnable.Run(weak_ptr.get(), CallbackForward(args)...); } }; @@ -1090,8 +315,8 @@ struct InvokeHelper { // WeakCalls are only supported for functions with a void return type. // Otherwise, the function result would be undefined if the the WeakPtr<> // is invalidated. - COMPILE_ASSERT(is_void::value, - weak_ptrs_can_only_bind_to_methods_without_return_values); + static_assert(is_void::value, + "weak_ptrs can only bind to methods without return values"); }; #endif @@ -1099,1419 +324,30 @@ struct InvokeHelper { // Invoker<> // // See description at the top of the file. -template +template struct Invoker; -// Arity 0 -> 0. -template -struct Invoker<0, StorageType, R()> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper - ::MakeItSo(storage->runnable_); - } -}; - -// Arity 1 -> 1. -template -struct Invoker<0, StorageType, R(X1)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1); - +template +struct Invoker, + StorageType, TypeList, + InvokeHelperType, R(UnboundForwardArgs...)> { static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1) { + UnboundForwardArgs... unbound_args) { StorageType* storage = static_cast(base); - // Local references to make debugger stepping easier. If in a debugger, // you really want to warp ahead and step through the // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1)> - ::MakeItSo(storage->runnable_, CallbackForward(x1)); - } -}; - -// Arity 1 -> 0. -template -struct Invoker<1, StorageType, R(X1)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1)); - } -}; - -// Arity 2 -> 2. -template -struct Invoker<0, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 2 -> 1. -template -struct Invoker<1, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 2 -> 0. -template -struct Invoker<2, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 3 -> 3. -template -struct Invoker<0, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 2. -template -struct Invoker<1, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 1. -template -struct Invoker<2, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 0. -template -struct Invoker<3, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 4 -> 4. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 3. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 2. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 1. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 0. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 5 -> 5. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 4. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 3. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 2. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 1. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 0. -template -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 6 -> 6. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 5. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 4. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 3. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 2. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 1. -template -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 0. -template -struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 7 -> 7. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 6. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 5. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 4. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 3. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 2. -template -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 1. -template -struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - return InvokeHelper::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 0. -template -struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - typedef typename StorageType::Bound7UnwrapTraits Bound7UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - typename Bound7UnwrapTraits::ForwardType x7 = - Bound7UnwrapTraits::Unwrap(storage->p7_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); + return InvokeHelperType::MakeItSo( + storage->runnable_, + Unwrappers::Unwrap(get(storage->bound_args_))..., + CallbackForward(unbound_args)...); } }; @@ -2526,261 +362,61 @@ struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { // Normally, this is the same as the RunType of the Runnable, but it can // be different if an adapter like IgnoreResult() has been used. // -// BoundArgsType contains the storage type for all the bound arguments by -// (ab)using a function type. -template +// BoundArgs contains the storage type for all the bound arguments. +template struct BindState; -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef false_type IsWeakCall; - typedef Invoker<0, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - explicit BindState(const Runnable& runnable) - : runnable_(runnable) { - } +template +struct BindState final + : public BindStateBase { + private: + using StorageType = BindState; + using RunnableType = Runnable; - virtual ~BindState() { } + // true_type if Runnable is a method invocation and the first bound argument + // is a WeakPtr. + using IsWeakCall = + IsWeakMethod::value, BoundArgs...>; + + using BoundIndices = MakeIndexSequence; + using Unwrappers = TypeList...>; + using UnboundForwardArgs = DropTypeListItem< + sizeof...(BoundArgs), + TypeList::ForwardType...>>; + using UnboundForwardRunType = MakeFunctionType; + + using InvokeHelperArgs = ConcatTypeLists< + TypeList::ForwardType...>, + UnboundForwardArgs>; + using InvokeHelperType = + InvokeHelper; + + using UnboundArgs = DropTypeListItem>; + + public: + using InvokerType = Invoker; + using UnboundRunType = MakeFunctionType; + + BindState(const Runnable& runnable, const BoundArgs&... bound_args) + : BindStateBase(&Destroy), + runnable_(runnable), + ref_(bound_args...), + bound_args_(bound_args...) {} RunnableType runnable_; -}; + MaybeScopedRefPtr::value, BoundArgs...> ref_; + Tuple bound_args_; -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<1, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; + private: + ~BindState() {} - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1) - : runnable_(runnable), - p1_(p1) { - MaybeRefcount::value, P1>::AddRef(p1_); + static void Destroy(BindStateBase* self) { + delete static_cast(self); } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<2, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2) - : runnable_(runnable), - p1_(p1), - p2_(p2) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<3, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<4, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<5, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - typedef UnwrapTraits Bound5UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<6, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - typedef UnwrapTraits Bound5UnwrapTraits; - typedef UnwrapTraits Bound6UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5, const P6& p6) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5), - p6_(p6) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; - P6 p6_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<7, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - typedef UnwrapTraits Bound5UnwrapTraits; - typedef UnwrapTraits Bound6UnwrapTraits; - typedef UnwrapTraits Bound7UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5, const P6& p6, const P7& p7) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5), - p6_(p6), - p7_(p7) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; - P6 p6_; - P7 p7_; }; } // namespace internal diff --git a/security/sandbox/chromium/base/bind_internal_win.h b/security/sandbox/chromium/base/bind_internal_win.h index c3f7477668e3..2ee12ef214d3 100644 --- a/security/sandbox/chromium/base/bind_internal_win.h +++ b/security/sandbox/chromium/base/bind_internal_win.h @@ -8,6 +8,8 @@ #ifndef BASE_BIND_INTERNAL_WIN_H_ #define BASE_BIND_INTERNAL_WIN_H_ +#include "build/build_config.h" + // In the x64 architecture in Windows, __fastcall, __stdcall, etc, are all // the same as __cdecl which would turn the following specializations into // multiple definitions. @@ -23,7 +25,9 @@ class RunnableAdapter; template class RunnableAdapter { public: - typedef R (RunType)(Args...); + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R RunType(Args...); explicit RunnableAdapter(R(__stdcall *function)(Args...)) : function_(function) { @@ -41,7 +45,9 @@ class RunnableAdapter { template class RunnableAdapter { public: - typedef R (RunType)(Args...); + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R RunType(Args...); explicit RunnableAdapter(R(__fastcall *function)(Args...)) : function_(function) { diff --git a/security/sandbox/chromium/base/bit_cast.h b/security/sandbox/chromium/base/bit_cast.h new file mode 100644 index 000000000000..b548467e7b86 --- /dev/null +++ b/security/sandbox/chromium/base/bit_cast.h @@ -0,0 +1,71 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BIT_CAST_H_ +#define BASE_BIT_CAST_H_ + +#include + +// bit_cast is a template function that implements the equivalent +// of "*reinterpret_cast(&source)". We need this in very low-level +// functions like the protobuf library and fast math support. +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method actually produces undefined behavior according to +// the ISO C++98 specification, section 3.10 ("basic.lval"), paragraph 15. +// (This did not substantially change in C++11.) Roughly, this section says: if +// an object in memory has one type, and a program accesses it with a different +// type, then the result is undefined behavior for most values of "different +// type". +// +// This is true for any cast syntax, either *(int*)&f or +// *reinterpret_cast(&f). And it is particularly true for conversions +// between integral lvalues and floating-point lvalues. +// +// The purpose of this paragraph is to allow optimizing compilers to assume that +// expressions with different types refer to different memory. Compilers are +// known to take advantage of this. So a non-conforming program quietly +// produces wildly incorrect output. +// +// The problem is not the use of reinterpret_cast. The problem is type punning: +// holding an object in memory of one type and reading its bits back using a +// different type. +// +// The C++ standard is more subtle and complex than this, but that is the basic +// idea. +// +// Anyways ... +// +// bit_cast<> calls memcpy() which is blessed by the standard, especially by the +// example in section 3.9 . Also, of course, bit_cast<> wraps up the nasty +// logic in one place. +// +// Fortunately memcpy() is very fast. In optimized mode, compilers replace +// calls to memcpy() with inline object code when the size argument is a +// compile-time constant. On a 32-bit system, memcpy(d,s,4) compiles to one +// load and one store, and memcpy(d,s,8) compiles to two loads and two stores. +// +// WARNING: if Dest or Source is a non-POD type, the result of the memcpy +// is likely to surprise you. + +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), + "bit_cast requires source and destination to be the same size"); + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +#endif // BASE_BIT_CAST_H_ diff --git a/security/sandbox/chromium/base/callback.h b/security/sandbox/chromium/base/callback.h index 364f506139f6..3bf0008b6d33 100644 --- a/security/sandbox/chromium/base/callback.h +++ b/security/sandbox/chromium/base/callback.h @@ -1,8 +1,3 @@ -// This file was GENERATED by command: -// pump.py callback.h.pump -// DO NOT EDIT BY HAND!!! - - // 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. @@ -31,7 +26,7 @@ // much like lexical closures are used in other languages. For example, it // is used in Chromium code to schedule tasks on different MessageLoops. // -// A callback with no unbound input parameters (base::Callback) +// A callback with no unbound input parameters (base::Callback) // is called a base::Closure. Note that this is NOT the same as what other // languages refer to as a closure -- it does not retain a reference to its // enclosing environment. @@ -53,7 +48,7 @@ // BINDING A BARE FUNCTION // // int Return5() { return 5; } -// base::Callback func_cb = base::Bind(&Return5); +// base::Callback func_cb = base::Bind(&Return5); // LOG(INFO) << func_cb.Run(); // Prints 5. // // BINDING A CLASS METHOD @@ -67,7 +62,7 @@ // void PrintBye() { LOG(INFO) << "bye."; } // }; // scoped_refptr ref = new Ref(); -// base::Callback ref_cb = base::Bind(&Ref::Foo, ref); +// base::Callback ref_cb = base::Bind(&Ref::Foo, ref); // LOG(INFO) << ref_cb.Run(); // Prints out 3. // // By default the object must support RefCounted or you will get a compiler @@ -109,10 +104,10 @@ // calling. // // void MyFunc(int i, const std::string& str) {} -// base::Callback cb = base::Bind(&MyFunc, 23, "hello world"); +// base::Callback cb = base::Bind(&MyFunc, 23, "hello world"); // cb.Run(); // -// A callback with no unbound input parameters (base::Callback) +// A callback with no unbound input parameters (base::Callback) // is called a base::Closure. So we could have also written: // // base::Closure cb = base::Bind(&MyFunc, 23, "hello world"); @@ -170,7 +165,7 @@ // that doesn't expect a return value. // // int DoSomething(int arg) { cout << arg << endl; } -// base::Callback) cb = +// base::Callback cb = // base::Bind(base::IgnoreResult(&DoSomething)); // // @@ -180,7 +175,7 @@ // // Bound parameters are specified as arguments to Bind() and are passed to the // function. A callback with no parameters or no unbound parameters is called a -// Closure (base::Callback and base::Closure are the same thing). +// Closure (base::Callback and base::Closure are the same thing). // // PASSING PARAMETERS OWNED BY THE CALLBACK // @@ -359,33 +354,30 @@ namespace base { // // If you are thinking of forward declaring Callback in your own header file, // please include "base/callback_forward.h" instead. -template -class Callback; namespace internal { -template +template struct BindState; } // namespace internal -template -class Callback : public internal::CallbackBase { +template +class Callback : public internal::CallbackBase { public: - typedef R(RunType)(); + // MSVC 2013 doesn't support Type Alias of function types. + // Revisit this after we update it to newer version. + typedef R RunType(Args...); - Callback() : CallbackBase(NULL) { } + Callback() : CallbackBase(nullptr) { } - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) + template + explicit Callback( + internal::BindState* bind_state) : CallbackBase(bind_state) { - // Force the assignment to a local variable of PolymorphicInvoke // so the compiler will typecheck that the passed in Run() method has // the correct type. PolymorphicInvoke invoke_func = - &internal::BindState + &internal::BindState ::InvokerType::Run; polymorphic_invoke_ = reinterpret_cast(invoke_func); } @@ -394,377 +386,20 @@ class Callback : public internal::CallbackBase { return CallbackBase::Equals(other); } - R Run() const { + R Run(typename internal::CallbackParamTraits::ForwardType... args) + const { PolymorphicInvoke f = reinterpret_cast(polymorphic_invoke_); - return f(bind_state_.get()); + return f(bind_state_.get(), internal::CallbackForward(args)...); } private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*); - + using PolymorphicInvoke = + R(*)(internal::BindStateBase*, + typename internal::CallbackParamTraits::ForwardType...); }; -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5, A6); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5, - typename internal::CallbackParamTraits::ForwardType a6) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5), - internal::CallbackForward(a6)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See base/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5, - typename internal::CallbackParamTraits::ForwardType a6, - typename internal::CallbackParamTraits::ForwardType a7) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5), - internal::CallbackForward(a6), - internal::CallbackForward(a7)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - - -// Syntactic sugar to make Callback easier to declare since it -// will be used in a lot of APIs with delayed execution. -typedef Callback Closure; - } // namespace base -#endif // BASE_CALLBACK_H +#endif // BASE_CALLBACK_H_ diff --git a/security/sandbox/chromium/base/callback_forward.h b/security/sandbox/chromium/base/callback_forward.h index 79832481af23..a9a263a50ea7 100644 --- a/security/sandbox/chromium/base/callback_forward.h +++ b/security/sandbox/chromium/base/callback_forward.h @@ -10,8 +10,10 @@ namespace base { template class Callback; -typedef Callback Closure; +// Syntactic sugar to make Callback easier to declare since it +// will be used in a lot of APIs with delayed execution. +using Closure = Callback; } // namespace base -#endif // BASE_CALLBACK_FORWARD_H +#endif // BASE_CALLBACK_FORWARD_H_ diff --git a/security/sandbox/chromium/base/callback_internal.cc b/security/sandbox/chromium/base/callback_internal.cc index ed0fb0dd0cce..2553fe7e1b64 100644 --- a/security/sandbox/chromium/base/callback_internal.cc +++ b/security/sandbox/chromium/base/callback_internal.cc @@ -9,6 +9,18 @@ namespace base { namespace internal { +void BindStateBase::AddRef() { + AtomicRefCountInc(&ref_count_); +} + +void BindStateBase::Release() { + if (!AtomicRefCountDec(&ref_count_)) + destructor_(this); +} + +CallbackBase::CallbackBase(const CallbackBase& c) = default; +CallbackBase& CallbackBase::operator=(const CallbackBase& c) = default; + void CallbackBase::Reset() { polymorphic_invoke_ = NULL; // NULL the bind_state_ last, since it may be holding the last ref to whatever @@ -24,7 +36,7 @@ bool CallbackBase::Equals(const CallbackBase& other) const { CallbackBase::CallbackBase(BindStateBase* bind_state) : bind_state_(bind_state), polymorphic_invoke_(NULL) { - DCHECK(!bind_state_.get() || bind_state_->HasOneRef()); + DCHECK(!bind_state_.get() || bind_state_->ref_count_ == 1); } CallbackBase::~CallbackBase() { diff --git a/security/sandbox/chromium/base/callback_internal.h b/security/sandbox/chromium/base/callback_internal.h index b85973d38154..d1d8ab8ec659 100644 --- a/security/sandbox/chromium/base/callback_internal.h +++ b/security/sandbox/chromium/base/callback_internal.h @@ -9,16 +9,19 @@ #define BASE_CALLBACK_INTERNAL_H_ #include +#include +#include +#include "base/atomic_ref_count.h" #include "base/base_export.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" - -template -class ScopedVector; +#include "base/template_util.h" namespace base { namespace internal { +class CallbackBase; // BindStateBase is used to provide an opaque handle that the Callback // class can use to represent a function object with bound arguments. It @@ -26,16 +29,39 @@ namespace internal { // DoInvoke function to perform the function execution. This allows // us to shield the Callback class from the types of the bound argument via // "type erasure." -class BindStateBase : public RefCountedThreadSafe { +// At the base level, the only task is to add reference counting data. Don't use +// RefCountedThreadSafe since it requires the destructor to be a virtual method. +// Creating a vtable for every BindState template instantiation results in a lot +// of bloat. Its only task is to call the destructor which can be done with a +// function pointer. +class BindStateBase { protected: - friend class RefCountedThreadSafe; - virtual ~BindStateBase() {} + explicit BindStateBase(void (*destructor)(BindStateBase*)) + : ref_count_(0), destructor_(destructor) {} + ~BindStateBase() = default; + + private: + friend class scoped_refptr; + friend class CallbackBase; + + void AddRef(); + void Release(); + + AtomicRefCount ref_count_; + + // Pointer to a function that will properly destroy |this|. + void (*destructor_)(BindStateBase*); + + DISALLOW_COPY_AND_ASSIGN(BindStateBase); }; // Holds the Callback methods that don't require specialization to reduce // template bloat. class BASE_EXPORT CallbackBase { public: + CallbackBase(const CallbackBase& c); + CallbackBase& operator=(const CallbackBase& c); + // Returns true if Callback is null (doesn't refer to anything). bool is_null() const { return bind_state_.get() == NULL; } @@ -47,7 +73,7 @@ class BASE_EXPORT CallbackBase { // another type. It is not okay to use void*. We create a InvokeFuncStorage // that that can store our function pointer, and then cast it back to // the original type on usage. - typedef void(*InvokeFuncStorage)(void); + using InvokeFuncStorage = void(*)(); // Returns true if this callback equals |other|. |other| may be null. bool Equals(const CallbackBase& other) const; @@ -68,8 +94,14 @@ class BASE_EXPORT CallbackBase { }; // A helper template to determine if given type is non-const move-only-type, -// i.e. if a value of the given type should be passed via .Pass() in a -// destructive way. +// i.e. if a value of the given type should be passed via std::move() in a +// destructive way. Types are considered to be move-only if they have a +// sentinel MoveOnlyTypeForCPP03 member: a class typically gets this from using +// the DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND macro. +// It would be easy to generalize this trait to all move-only types... but this +// confuses template deduction in VS2013 with certain types such as +// std::unique_ptr. +// TODO(dcheng): Revisit this when Windows switches to VS2015 by default. template struct IsMoveOnlyType { template static YesType Test(const typename U::MoveOnlyTypeForCPP03*); @@ -81,6 +113,21 @@ template struct IsMoveOnlyType { !is_const::value; }; +// Specialization of IsMoveOnlyType so that std::unique_ptr is still considered +// move-only, even without the sentinel member. +template +struct IsMoveOnlyType> : std::true_type {}; + +template +struct CallbackParamTraitsForMoveOnlyType; + +template +struct CallbackParamTraitsForNonMoveOnlyType; + +// TODO(tzik): Use a default parameter once MSVS supports variadic templates +// with default values. +// http://connect.microsoft.com/VisualStudio/feedbackdetail/view/957801/compilation-error-with-variadic-templates +// // This is a typetraits object that's used to take an argument type, and // extract a suitable type for storing and forwarding arguments. // @@ -92,10 +139,17 @@ template struct IsMoveOnlyType { // parameters by const reference. In this case, we end up passing an actual // array type in the initializer list which C++ does not allow. This will // break passing of C-string literals. -template ::value> -struct CallbackParamTraits { - typedef const T& ForwardType; - typedef T StorageType; +template +struct CallbackParamTraits + : std::conditional::value, + CallbackParamTraitsForMoveOnlyType, + CallbackParamTraitsForNonMoveOnlyType>::type { +}; + +template +struct CallbackParamTraitsForNonMoveOnlyType { + using ForwardType = const T&; + using StorageType = T; }; // The Storage should almost be impossible to trigger unless someone manually @@ -104,9 +158,9 @@ struct CallbackParamTraits { // // The ForwardType should only be used for unbound arguments. template -struct CallbackParamTraits { - typedef T& ForwardType; - typedef T StorageType; +struct CallbackParamTraitsForNonMoveOnlyType { + using ForwardType = T&; + using StorageType = T; }; // Note that for array types, we implicitly add a const in the conversion. This @@ -115,16 +169,16 @@ struct CallbackParamTraits { // T[n]" does not seem to match correctly, so we are stuck with this // restriction. template -struct CallbackParamTraits { - typedef const T* ForwardType; - typedef const T* StorageType; +struct CallbackParamTraitsForNonMoveOnlyType { + using ForwardType = const T*; + using StorageType = const T*; }; // See comment for CallbackParamTraits. template -struct CallbackParamTraits { - typedef const T* ForwardType; - typedef const T* StorageType; +struct CallbackParamTraitsForNonMoveOnlyType { + using ForwardType = const T*; + using StorageType = const T*; }; // Parameter traits for movable-but-not-copyable scopers. @@ -141,9 +195,9 @@ struct CallbackParamTraits { // reference cannot be used with temporaries which means the result of a // function or a cast would not be usable with Callback<> or Bind(). template -struct CallbackParamTraits { - typedef T ForwardType; - typedef T StorageType; +struct CallbackParamTraitsForMoveOnlyType { + using ForwardType = T; + using StorageType = T; }; // CallbackForward() is a very limited simulation of C++11's std::forward() @@ -155,7 +209,7 @@ struct CallbackParamTraits { // default template compiles out to be a no-op. // // In C++11, std::forward would replace all uses of this function. However, it -// is impossible to implement a general std::forward with C++11 due to a lack +// is impossible to implement a general std::forward without C++11 due to a lack // of rvalue references. // // In addition to Callback/Bind, this is used by PostTaskAndReplyWithResult to @@ -163,13 +217,15 @@ struct CallbackParamTraits { // parameter to another callback. This is to support Callbacks that return // the movable-but-not-copyable types whitelisted above. template -typename enable_if::value, T>::type& CallbackForward(T& t) { +typename std::enable_if::value, T>::type& CallbackForward( + T& t) { return t; } template -typename enable_if::value, T>::type CallbackForward(T& t) { - return t.Pass(); +typename std::enable_if::value, T>::type CallbackForward( + T& t) { + return std::move(t); } } // namespace internal diff --git a/security/sandbox/chromium/base/compiler_specific.h b/security/sandbox/chromium/base/compiler_specific.h index 47ca9778b670..339e9b74e9c2 100644 --- a/security/sandbox/chromium/base/compiler_specific.h +++ b/security/sandbox/chromium/base/compiler_specific.h @@ -9,6 +9,9 @@ #if defined(COMPILER_MSVC) +// For _Printf_format_string_. +#include + // Macros for suppressing and disabling warnings on MSVC. // // Warning numbers are enumerated at: @@ -57,6 +60,7 @@ #else // Not MSVC +#define _Printf_format_string_ #define MSVC_SUPPRESS_WARNING(n) #define MSVC_PUSH_DISABLE_WARNING(n) #define MSVC_PUSH_WARNING_LEVEL(n) @@ -68,28 +72,6 @@ #endif // COMPILER_MSVC -// The C++ standard requires that static const members have an out-of-class -// definition (in a single compilation unit), but MSVC chokes on this (when -// language extensions, which are required, are enabled). (You're only likely to -// notice the need for a definition if you take the address of the member or, -// more commonly, pass it to a function that takes it as a reference argument -- -// probably an STL function.) This macro makes MSVC do the right thing. See -// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more -// information. Use like: -// -// In .h file: -// struct Foo { -// static const int kBar = 5; -// }; -// -// In .cc file: -// STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar; -#if defined(COMPILER_MSVC) -#define STATIC_CONST_MEMBER_DEFINITION __declspec(selectany) -#else -#define STATIC_CONST_MEMBER_DEFINITION -#endif - // Annotate a variable indicating it's ok if the variable is not used. // (Typically used to silence a compiler warning when the assignment // is important for some other reason.) @@ -101,7 +83,7 @@ // Annotate a typedef or function indicating it's ok if it's not used. // Use like: // typedef Foo Bar ALLOW_UNUSED_TYPE; -#if defined(COMPILER_GCC) +#if defined(COMPILER_GCC) || defined(__clang__) #define ALLOW_UNUSED_TYPE __attribute__((unused)) #else #define ALLOW_UNUSED_TYPE @@ -128,13 +110,11 @@ #define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) #endif -// Return the byte alignment of the given type (available at compile time). Use -// sizeof(type) prior to checking __alignof to workaround Visual C++ bug: -// http://goo.gl/isH0C +// Return the byte alignment of the given type (available at compile time). // Use like: -// ALIGNOF(int32) // this would be 4 +// ALIGNOF(int32_t) // this would be 4 #if defined(COMPILER_MSVC) -#define ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type)) +#define ALIGNOF(type) __alignof(type) #elif defined(COMPILER_GCC) #define ALIGNOF(type) __alignof__(type) #endif @@ -142,8 +122,9 @@ // Annotate a function indicating the caller must examine the return value. // Use like: // int foo() WARN_UNUSED_RESULT; -// To explicitly ignore a result, see |ignore_result()| in . -#if defined(COMPILER_GCC) +// To explicitly ignore a result, see |ignore_result()| in base/macros.h. +#undef WARN_UNUSED_RESULT +#if defined(COMPILER_GCC) || defined(__clang__) #define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else #define WARN_UNUSED_RESULT @@ -175,9 +156,17 @@ // Mark a memory region fully initialized. // Use this to annotate code that deliberately reads uninitialized data, for // example a GC scavenging root set pointers from the stack. -#define MSAN_UNPOISON(p, s) __msan_unpoison(p, s) +#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size) + +// Check a memory region for initializedness, as if it was being used here. +// If any bits are uninitialized, crash with an MSan report. +// Use this to sanitize data which MSan won't be able to track, e.g. before +// passing data to another process via shared memory. +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) \ + __msan_check_mem_is_initialized(p, size) #else // MEMORY_SANITIZER -#define MSAN_UNPOISON(p, s) +#define MSAN_UNPOISON(p, size) +#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size) #endif // MEMORY_SANITIZER // Macro useful for writing cross-platform function pointers. diff --git a/security/sandbox/chromium/base/containers/hash_tables.h b/security/sandbox/chromium/base/containers/hash_tables.h index 738015366c1b..c421dddf3e7c 100644 --- a/security/sandbox/chromium/base/containers/hash_tables.h +++ b/security/sandbox/chromium/base/containers/hash_tables.h @@ -21,9 +21,11 @@ #ifndef BASE_CONTAINERS_HASH_TABLES_H_ #define BASE_CONTAINERS_HASH_TABLES_H_ +#include +#include + #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "build/build_config.h" @@ -45,15 +47,9 @@ #undef __DEPRECATED #endif -#if defined(OS_ANDROID) && !defined(MOZ_WIDGET_GONK) -#include -#include -#define BASE_HASH_IMPL_NAMESPACE std -#else #include #include #define BASE_HASH_IMPL_NAMESPACE __gnu_cxx -#endif #include @@ -84,7 +80,6 @@ struct hash { } }; -#if !defined(OS_ANDROID) // The GNU C++ library provides identity hash functions for many integral types, // but not for |long long|. This hash function will truncate if |size_t| is // narrower than |long long|. This is probably good enough for what we will @@ -102,7 +97,6 @@ DEFINE_TRIVIAL_HASH(long long); DEFINE_TRIVIAL_HASH(unsigned long long); #undef DEFINE_TRIVIAL_HASH -#endif // !defined(OS_ANDROID) // Implement string hash functions so that strings of various flavors can // be used as keys in STL maps and sets. The hash algorithm comes from the @@ -204,19 +198,19 @@ using hash_set = BASE_HASH_IMPL_NAMESPACE::hash_set; // h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32 // // Contact danakj@chromium.org for any questions. -inline std::size_t HashInts32(uint32 value1, uint32 value2) { - uint64 value1_64 = value1; - uint64 hash64 = (value1_64 << 32) | value2; +inline std::size_t HashInts32(uint32_t value1, uint32_t value2) { + uint64_t value1_64 = value1; + uint64_t hash64 = (value1_64 << 32) | value2; - if (sizeof(std::size_t) >= sizeof(uint64)) + if (sizeof(std::size_t) >= sizeof(uint64_t)) return static_cast(hash64); - uint64 odd_random = 481046412LL << 32 | 1025306955LL; - uint32 shift_random = 10121U << 16; + uint64_t odd_random = 481046412LL << 32 | 1025306955LL; + uint32_t shift_random = 10121U << 16; hash64 = hash64 * odd_random + shift_random; std::size_t high_bits = static_cast( - hash64 >> (8 * (sizeof(uint64) - sizeof(std::size_t)))); + hash64 >> (8 * (sizeof(uint64_t) - sizeof(std::size_t)))); return high_bits; } @@ -225,87 +219,46 @@ inline std::size_t HashInts32(uint32 value1, uint32 value2) { // breaking the two 64-bit inputs into 4 32-bit values: // http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000 // Then we reduce our result to 32 bits if required, similar to above. -inline std::size_t HashInts64(uint64 value1, uint64 value2) { - uint32 short_random1 = 842304669U; - uint32 short_random2 = 619063811U; - uint32 short_random3 = 937041849U; - uint32 short_random4 = 3309708029U; +inline std::size_t HashInts64(uint64_t value1, uint64_t value2) { + uint32_t short_random1 = 842304669U; + uint32_t short_random2 = 619063811U; + uint32_t short_random3 = 937041849U; + uint32_t short_random4 = 3309708029U; - uint32 value1a = static_cast(value1 & 0xffffffff); - uint32 value1b = static_cast((value1 >> 32) & 0xffffffff); - uint32 value2a = static_cast(value2 & 0xffffffff); - uint32 value2b = static_cast((value2 >> 32) & 0xffffffff); + uint32_t value1a = static_cast(value1 & 0xffffffff); + uint32_t value1b = static_cast((value1 >> 32) & 0xffffffff); + uint32_t value2a = static_cast(value2 & 0xffffffff); + uint32_t value2b = static_cast((value2 >> 32) & 0xffffffff); - uint64 product1 = static_cast(value1a) * short_random1; - uint64 product2 = static_cast(value1b) * short_random2; - uint64 product3 = static_cast(value2a) * short_random3; - uint64 product4 = static_cast(value2b) * short_random4; + uint64_t product1 = static_cast(value1a) * short_random1; + uint64_t product2 = static_cast(value1b) * short_random2; + uint64_t product3 = static_cast(value2a) * short_random3; + uint64_t product4 = static_cast(value2b) * short_random4; - uint64 hash64 = product1 + product2 + product3 + product4; + uint64_t hash64 = product1 + product2 + product3 + product4; - if (sizeof(std::size_t) >= sizeof(uint64)) + if (sizeof(std::size_t) >= sizeof(uint64_t)) return static_cast(hash64); - uint64 odd_random = 1578233944LL << 32 | 194370989LL; - uint32 shift_random = 20591U << 16; + uint64_t odd_random = 1578233944LL << 32 | 194370989LL; + uint32_t shift_random = 20591U << 16; hash64 = hash64 * odd_random + shift_random; std::size_t high_bits = static_cast( - hash64 >> (8 * (sizeof(uint64) - sizeof(std::size_t)))); + hash64 >> (8 * (sizeof(uint64_t) - sizeof(std::size_t)))); return high_bits; } -#define DEFINE_32BIT_PAIR_HASH(Type1, Type2) \ -inline std::size_t HashPair(Type1 value1, Type2 value2) { \ - return HashInts32(value1, value2); \ +template +inline std::size_t HashPair(T1 value1, T2 value2) { + // This condition is expected to be compile-time evaluated and optimised away + // in release builds. + if (sizeof(T1) > sizeof(uint32_t) || (sizeof(T2) > sizeof(uint32_t))) + return HashInts64(value1, value2); + + return HashInts32(value1, value2); } -DEFINE_32BIT_PAIR_HASH(int16, int16); -DEFINE_32BIT_PAIR_HASH(int16, uint16); -DEFINE_32BIT_PAIR_HASH(int16, int32); -DEFINE_32BIT_PAIR_HASH(int16, uint32); -DEFINE_32BIT_PAIR_HASH(uint16, int16); -DEFINE_32BIT_PAIR_HASH(uint16, uint16); -DEFINE_32BIT_PAIR_HASH(uint16, int32); -DEFINE_32BIT_PAIR_HASH(uint16, uint32); -DEFINE_32BIT_PAIR_HASH(int32, int16); -DEFINE_32BIT_PAIR_HASH(int32, uint16); -DEFINE_32BIT_PAIR_HASH(int32, int32); -DEFINE_32BIT_PAIR_HASH(int32, uint32); -DEFINE_32BIT_PAIR_HASH(uint32, int16); -DEFINE_32BIT_PAIR_HASH(uint32, uint16); -DEFINE_32BIT_PAIR_HASH(uint32, int32); -DEFINE_32BIT_PAIR_HASH(uint32, uint32); - -#undef DEFINE_32BIT_PAIR_HASH - -#define DEFINE_64BIT_PAIR_HASH(Type1, Type2) \ -inline std::size_t HashPair(Type1 value1, Type2 value2) { \ - return HashInts64(value1, value2); \ -} - -DEFINE_64BIT_PAIR_HASH(int16, int64); -DEFINE_64BIT_PAIR_HASH(int16, uint64); -DEFINE_64BIT_PAIR_HASH(uint16, int64); -DEFINE_64BIT_PAIR_HASH(uint16, uint64); -DEFINE_64BIT_PAIR_HASH(int32, int64); -DEFINE_64BIT_PAIR_HASH(int32, uint64); -DEFINE_64BIT_PAIR_HASH(uint32, int64); -DEFINE_64BIT_PAIR_HASH(uint32, uint64); -DEFINE_64BIT_PAIR_HASH(int64, int16); -DEFINE_64BIT_PAIR_HASH(int64, uint16); -DEFINE_64BIT_PAIR_HASH(int64, int32); -DEFINE_64BIT_PAIR_HASH(int64, uint32); -DEFINE_64BIT_PAIR_HASH(int64, int64); -DEFINE_64BIT_PAIR_HASH(int64, uint64); -DEFINE_64BIT_PAIR_HASH(uint64, int16); -DEFINE_64BIT_PAIR_HASH(uint64, uint16); -DEFINE_64BIT_PAIR_HASH(uint64, int32); -DEFINE_64BIT_PAIR_HASH(uint64, uint32); -DEFINE_64BIT_PAIR_HASH(uint64, int64); -DEFINE_64BIT_PAIR_HASH(uint64, uint64); - -#undef DEFINE_64BIT_PAIR_HASH } // namespace base namespace BASE_HASH_NAMESPACE { @@ -320,7 +273,7 @@ struct hash > { } }; -} +} // namespace BASE_HASH_NAMESPACE #undef DEFINE_PAIR_HASH_FUNCTION_START #undef DEFINE_PAIR_HASH_FUNCTION_END diff --git a/security/sandbox/chromium/base/cpu.cc b/security/sandbox/chromium/base/cpu.cc index ef3309dad180..713544566439 100644 --- a/security/sandbox/chromium/base/cpu.cc +++ b/security/sandbox/chromium/base/cpu.cc @@ -4,12 +4,15 @@ #include "base/cpu.h" +#include +#include +#include #include #include #include -#include "base/basictypes.h" +#include "base/macros.h" #include "base/strings/string_piece.h" #include "build/build_config.h" @@ -43,7 +46,7 @@ CPU::CPU() has_sse41_(false), has_sse42_(false), has_avx_(false), - has_avx_hardware_(false), + has_avx2_(false), has_aesni_(false), has_non_stop_time_stamp_counter_(false), has_broken_neon_(false), @@ -72,7 +75,7 @@ void __cpuid(int cpu_info[4], int info_type) { void __cpuid(int cpu_info[4], int info_type) { __asm__ volatile ( - "cpuid \n\t" + "cpuid\n" : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) : "a"(info_type) ); @@ -82,11 +85,12 @@ void __cpuid(int cpu_info[4], int info_type) { // _xgetbv returns the value of an Intel Extended Control Register (XCR). // Currently only XCR0 is defined by Intel so |xcr| should always be zero. -uint64 _xgetbv(uint32 xcr) { - uint32 eax, edx; +uint64_t _xgetbv(uint32_t xcr) { + uint32_t eax, edx; - __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr)); - return (static_cast(edx) << 32) | eax; + __asm__ volatile ( + "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr)); + return (static_cast(edx) << 32) | eax; } #endif // !_MSC_VER @@ -110,7 +114,7 @@ class LazyCpuInfoValue { revision = 0; const struct { const char key[17]; - unsigned *result; + unsigned int* result; } kUnsignedValues[] = { {"CPU implementer", &implementer}, {"CPU architecture", &architecture}, @@ -156,7 +160,7 @@ class LazyCpuInfoValue { // The string may have leading "0x" or not, so we use strtoul to // handle that. - char *endptr; + char* endptr; std::string value(value_sp.as_string()); unsigned long int result = strtoul(value.c_str(), &endptr, 0); if (*endptr == 0 && result <= UINT_MAX) { @@ -211,7 +215,11 @@ void CPU::Initialize() { // Interpret CPU feature information. if (num_ids > 0) { + int cpu_info7[4] = {0}; __cpuid(cpu_info, 1); + if (num_ids >= 7) { + __cpuid(cpu_info7, 7); + } signature_ = cpu_info[0]; stepping_ = cpu_info[0] & 0xf; model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0); @@ -226,8 +234,6 @@ void CPU::Initialize() { has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; has_sse41_ = (cpu_info[2] & 0x00080000) != 0; has_sse42_ = (cpu_info[2] & 0x00100000) != 0; - has_avx_hardware_ = - (cpu_info[2] & 0x10000000) != 0; // AVX instructions will generate an illegal instruction exception unless // a) they are supported by the CPU, // b) XSAVE is supported by the CPU and @@ -239,11 +245,12 @@ void CPU::Initialize() { // Because of that, we also test the XSAVE bit because its description in // the CPUID documentation suggests that it signals xgetbv support. has_avx_ = - has_avx_hardware_ && + (cpu_info[2] & 0x10000000) != 0 && (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ && (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ && (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */; has_aesni_ = (cpu_info[2] & 0x02000000) != 0; + has_avx2_ = has_avx_ && (cpu_info7[1] & 0x00000020) != 0; } // Get the brand string of the cpu. @@ -275,6 +282,7 @@ void CPU::Initialize() { } CPU::IntelMicroArchitecture CPU::GetIntelMicroArchitecture() const { + if (has_avx2()) return AVX2; if (has_avx()) return AVX; if (has_sse42()) return SSE42; if (has_sse41()) return SSE41; diff --git a/security/sandbox/chromium/base/cpu.h b/security/sandbox/chromium/base/cpu.h index 0c809f00c8b9..8c3c06c04444 100644 --- a/security/sandbox/chromium/base/cpu.h +++ b/security/sandbox/chromium/base/cpu.h @@ -26,6 +26,7 @@ class BASE_EXPORT CPU { SSE41, SSE42, AVX, + AVX2, MAX_INTEL_MICRO_ARCHITECTURE }; @@ -46,12 +47,7 @@ class BASE_EXPORT CPU { bool has_sse41() const { return has_sse41_; } bool has_sse42() const { return has_sse42_; } bool has_avx() const { return has_avx_; } - // has_avx_hardware returns true when AVX is present in the CPU. This might - // differ from the value of |has_avx()| because |has_avx()| also tests for - // operating system support needed to actually call AVX instuctions. - // Note: you should never need to call this function. It was added in order - // to workaround a bug in NSS but |has_avx()| is what you want. - bool has_avx_hardware() const { return has_avx_hardware_; } + bool has_avx2() const { return has_avx2_; } bool has_aesni() const { return has_aesni_; } bool has_non_stop_time_stamp_counter() const { return has_non_stop_time_stamp_counter_; @@ -83,7 +79,7 @@ class BASE_EXPORT CPU { bool has_sse41_; bool has_sse42_; bool has_avx_; - bool has_avx_hardware_; + bool has_avx2_; bool has_aesni_; bool has_non_stop_time_stamp_counter_; bool has_broken_neon_; diff --git a/security/sandbox/chromium/base/debug/debugger.h b/security/sandbox/chromium/base/debug/debugger.h index d62ea3f7e17b..8680e281ed1e 100644 --- a/security/sandbox/chromium/base/debug/debugger.h +++ b/security/sandbox/chromium/base/debug/debugger.h @@ -6,8 +6,8 @@ // debuggers. You should use this to test if you're running under a debugger, // and if you would like to yield (breakpoint) into the debugger. -#ifndef BASE_DEBUG_DEBUGGER_H -#define BASE_DEBUG_DEBUGGER_H +#ifndef BASE_DEBUG_DEBUGGER_H_ +#define BASE_DEBUG_DEBUGGER_H_ #include "base/base_export.h" @@ -41,4 +41,4 @@ BASE_EXPORT bool IsDebugUISuppressed(); } // namespace debug } // namespace base -#endif // BASE_DEBUG_DEBUGGER_H +#endif // BASE_DEBUG_DEBUGGER_H_ diff --git a/security/sandbox/chromium/base/debug/leak_annotations.h b/security/sandbox/chromium/base/debug/leak_annotations.h index 27fe663bddb2..dc502461d075 100644 --- a/security/sandbox/chromium/base/debug/leak_annotations.h +++ b/security/sandbox/chromium/base/debug/leak_annotations.h @@ -5,7 +5,7 @@ #ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_ #define BASE_DEBUG_LEAK_ANNOTATIONS_H_ -#include "base/basictypes.h" +#include "base/macros.h" #include "build/build_config.h" // This file defines macros which can be used to annotate intentional memory @@ -21,15 +21,7 @@ #if defined(LEAK_SANITIZER) && !defined(OS_NACL) -// Public LSan API from . -extern "C" { -void __lsan_disable(); -void __lsan_enable(); -void __lsan_ignore_object(const void *p); - -// Invoke leak detection immediately. If leaks are found, the process will exit. -void __lsan_do_leak_check(); -} // extern "C" +#include class ScopedLeakSanitizerDisabler { public: @@ -46,7 +38,6 @@ class ScopedLeakSanitizerDisabler { #else -// If neither HeapChecker nor LSan are used, the annotations should be no-ops. #define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0) #define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0) diff --git a/security/sandbox/chromium/base/debug/profiler.cc b/security/sandbox/chromium/base/debug/profiler.cc index ed553cdf2939..75e9aac0cba5 100644 --- a/security/sandbox/chromium/base/debug/profiler.cc +++ b/security/sandbox/chromium/base/debug/profiler.cc @@ -6,16 +6,18 @@ #include +#include "base/debug/debugging_flags.h" #include "base/process/process_handle.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" +#include "build/build_config.h" #if defined(OS_WIN) #include "base/win/pe_image.h" #endif // defined(OS_WIN) // TODO(peria): Enable profiling on Windows. -#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) +#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) #include "third_party/tcmalloc/chromium/src/gperftools/profiler.h" #endif @@ -23,15 +25,15 @@ namespace base { namespace debug { // TODO(peria): Enable profiling on Windows. -#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) +#if BUILDFLAG(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN) static int profile_count = 0; void StartProfiling(const std::string& name) { ++profile_count; std::string full_name(name); - std::string pid = StringPrintf("%d", GetCurrentProcId()); - std::string count = StringPrintf("%d", profile_count); + std::string pid = IntToString(GetCurrentProcId()); + std::string count = IntToString(profile_count); ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid); ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count); ProfilerStart(full_name.c_str()); diff --git a/security/sandbox/chromium/base/debug/profiler.h b/security/sandbox/chromium/base/debug/profiler.h index e1dda8974262..7cce7b08f90a 100644 --- a/security/sandbox/chromium/base/debug/profiler.h +++ b/security/sandbox/chromium/base/debug/profiler.h @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_DEBUG_PROFILER_H -#define BASE_DEBUG_PROFILER_H +#ifndef BASE_DEBUG_PROFILER_H_ +#define BASE_DEBUG_PROFILER_H_ + +#include #include #include "base/base_export.h" -#include "base/basictypes.h" // The Profiler functions allow usage of the underlying sampling based // profiler. If the application has not been built with the necessary @@ -87,4 +88,4 @@ BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc(); } // namespace debug } // namespace base -#endif // BASE_DEBUG_DEBUGGER_H +#endif // BASE_DEBUG_PROFILER_H_ diff --git a/security/sandbox/chromium/base/file_descriptor_posix.h b/security/sandbox/chromium/base/file_descriptor_posix.h index 376ad39e307a..2a366116a31d 100644 --- a/security/sandbox/chromium/base/file_descriptor_posix.h +++ b/security/sandbox/chromium/base/file_descriptor_posix.h @@ -14,9 +14,16 @@ namespace base { // We introduct a special structure for file descriptors in order that we are // able to use template specialisation to special-case their handling. // -// WARNING: (Chromium only) There are subtleties to consider if serialising -// these objects over IPC. See comments in ipc/ipc_message_utils.h -// above the template specialisation for this structure. +// IMPORTANT: This is primarily intended for use when sending file descriptors +// over IPC. Even if |auto_close| is true, base::FileDescriptor does NOT close() +// |fd| when going out of scope. Instead, a consumer of a base::FileDescriptor +// must invoke close() on |fd| if |auto_close| is true. +// +// In the case of IPC, the the IPC subsystem knows to close() |fd| after sending +// a message that contains a base::FileDescriptor if auto_close == true. On the +// other end, the receiver must make sure to close() |fd| after it has finished +// processing the IPC message. See the IPC::ParamTraits<> specialization in +// ipc/ipc_message_utils.h for all the details. // ----------------------------------------------------------------------------- struct FileDescriptor { FileDescriptor() : fd(-1), auto_close(false) {} diff --git a/security/sandbox/chromium/base/files/file_path.h b/security/sandbox/chromium/base/files/file_path.h index ad42b9549275..89e9cbfb1d5c 100644 --- a/security/sandbox/chromium/base/files/file_path.h +++ b/security/sandbox/chromium/base/files/file_path.h @@ -53,7 +53,7 @@ // between char[]-based pathnames on POSIX systems and wchar_t[]-based // pathnames on Windows. // -// Paths can't contain NULs as a precaution agaist premature truncation. +// As a precaution against premature truncation, paths can't contain NULs. // // Because a FilePath object should not be instantiated at the global scope, // instead, use a FilePath::CharType[] and initialize it with @@ -83,9 +83,9 @@ // in case it ever comes across such a system. FilePath needs this support // for Windows UNC paths, anyway. // References: -// The Open Group Base Specifications Issue 7, sections 3.266 ("Pathname") +// The Open Group Base Specifications Issue 7, sections 3.267 ("Pathname") // and 4.12 ("Pathname Resolution"), available at: -// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_266 +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_267 // http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 // // - Windows treats c:\\ the same way it treats \\. This was intended to @@ -103,13 +103,17 @@ #define BASE_FILES_FILE_PATH_H_ #include + +#include #include #include #include "base/base_export.h" +#include "base/compiler_specific.h" #include "base/containers/hash_tables.h" +#include "base/macros.h" #include "base/strings/string16.h" -#include "base/strings/string_piece.h" // For implicit conversions. +#include "base/strings/string_piece.h" #include "build/build_config.h" // Windows-style drive letter support and pathname separator characters can be @@ -121,11 +125,20 @@ #define FILE_PATH_USES_WIN_SEPARATORS #endif // OS_WIN -class Pickle; -class PickleIterator; +// To print path names portably use PRIsFP (based on PRIuS and friends from +// C99 and format_macros.h) like this: +// base::StringPrintf("Path is %" PRIsFP ".\n", path.value().c_str()); +#if defined(OS_POSIX) +#define PRIsFP "s" +#elif defined(OS_WIN) +#define PRIsFP "ls" +#endif // OS_WIN namespace base { +class Pickle; +class PickleIterator; + // An abstraction to isolate users from the differences between native // pathnames on different platforms. class BASE_EXPORT FilePath { @@ -141,6 +154,7 @@ class BASE_EXPORT FilePath { typedef std::wstring StringType; #endif // OS_WIN + typedef BasicStringPiece StringPieceType; typedef StringType::value_type CharType; // Null-terminated array of separators used to separate components in @@ -163,7 +177,7 @@ class BASE_EXPORT FilePath { FilePath(); FilePath(const FilePath& that); - explicit FilePath(const StringType& path); + explicit FilePath(StringPieceType path); ~FilePath(); FilePath& operator=(const FilePath& that); @@ -237,7 +251,7 @@ class BASE_EXPORT FilePath { // ASSERT(new_path == path.value()); // NOTE: this is different from the original file_util implementation which // returned the extension without a leading "." ("jpg" instead of ".jpg") - StringType Extension() const; + StringType Extension() const WARN_UNUSED_RESULT; // Returns the path's file extension, as in Extension(), but will // never return a double extension. @@ -246,7 +260,7 @@ class BASE_EXPORT FilePath { // we can rename this to Extension() and the other to something like // LongExtension(), defaulting to short extensions and leaving the // long "extensions" to logic like base::GetUniquePathNumber(). - StringType FinalExtension() const; + StringType FinalExtension() const WARN_UNUSED_RESULT; // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" // NOTE: this is slightly different from the similar file_util implementation @@ -265,25 +279,23 @@ class BASE_EXPORT FilePath { // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" FilePath InsertBeforeExtension( - const StringType& suffix) const WARN_UNUSED_RESULT; + StringPieceType suffix) const WARN_UNUSED_RESULT; FilePath InsertBeforeExtensionASCII( - const base::StringPiece& suffix) const WARN_UNUSED_RESULT; + StringPiece suffix) const WARN_UNUSED_RESULT; // Adds |extension| to |file_name|. Returns the current FilePath if // |extension| is empty. Returns "" if BaseName() == "." or "..". - FilePath AddExtension( - const StringType& extension) const WARN_UNUSED_RESULT; + FilePath AddExtension(StringPieceType extension) const WARN_UNUSED_RESULT; // Replaces the extension of |file_name| with |extension|. If |file_name| // does not have an extension, then |extension| is added. If |extension| is // empty, then the extension is removed from |file_name|. // Returns "" if BaseName() == "." or "..". - FilePath ReplaceExtension( - const StringType& extension) const WARN_UNUSED_RESULT; + FilePath ReplaceExtension(StringPieceType extension) const WARN_UNUSED_RESULT; // Returns true if the file path matches the specified extension. The test is // case insensitive. Don't forget the leading period if appropriate. - bool MatchesExtension(const StringType& extension) const; + bool MatchesExtension(StringPieceType extension) const; // Returns a FilePath by appending a separator and the supplied path // component to this object's path. Append takes care to avoid adding @@ -291,7 +303,7 @@ class BASE_EXPORT FilePath { // If this object's path is kCurrentDirectory, a new FilePath corresponding // only to |component| is returned. |component| must be a relative path; // it is an error to pass an absolute path. - FilePath Append(const StringType& component) const WARN_UNUSED_RESULT; + FilePath Append(StringPieceType component) const WARN_UNUSED_RESULT; FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; // Although Windows StringType is std::wstring, since the encoding it uses for @@ -300,8 +312,7 @@ class BASE_EXPORT FilePath { // On Linux, although it can use any 8-bit encoding for paths, we assume that // ASCII is a valid subset, regardless of the encoding, since many operating // system paths will always be ASCII. - FilePath AppendASCII(const base::StringPiece& component) - const WARN_UNUSED_RESULT; + FilePath AppendASCII(StringPiece component) const WARN_UNUSED_RESULT; // Returns true if this FilePath contains an absolute path. On Windows, an // absolute path begins with either a drive letter specification followed by @@ -385,14 +396,14 @@ class BASE_EXPORT FilePath { // on parts of a file path, e.g., just the extension. // CompareIgnoreCase() returns -1, 0 or 1 for less-than, equal-to and // greater-than respectively. - static int CompareIgnoreCase(const StringType& string1, - const StringType& string2); - static bool CompareEqualIgnoreCase(const StringType& string1, - const StringType& string2) { + static int CompareIgnoreCase(StringPieceType string1, + StringPieceType string2); + static bool CompareEqualIgnoreCase(StringPieceType string1, + StringPieceType string2) { return CompareIgnoreCase(string1, string2) == 0; } - static bool CompareLessIgnoreCase(const StringType& string1, - const StringType& string2) { + static bool CompareLessIgnoreCase(StringPieceType string1, + StringPieceType string2) { return CompareIgnoreCase(string1, string2) < 0; } @@ -402,14 +413,14 @@ class BASE_EXPORT FilePath { // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#UnicodeSubtleties // for further comments. // Returns the epmty string if the conversion failed. - static StringType GetHFSDecomposedForm(const FilePath::StringType& string); + static StringType GetHFSDecomposedForm(StringPieceType string); // Special UTF-8 version of FastUnicodeCompare. Cf: // http://developer.apple.com/mac/library/technotes/tn/tn1150.html#StringComparisonAlgorithm // IMPORTANT: The input strings must be in the special HFS decomposed form! // (cf. above GetHFSDecomposedForm method) - static int HFSFastUnicodeCompare(const StringType& string1, - const StringType& string2); + static int HFSFastUnicodeCompare(StringPieceType string1, + StringPieceType string2); #endif #if defined(OS_ANDROID) @@ -432,21 +443,22 @@ class BASE_EXPORT FilePath { StringType path_; }; -} // namespace base - // This is required by googletest to print a readable output on test failures. -BASE_EXPORT extern void PrintTo(const base::FilePath& path, std::ostream* out); +// This is declared here for use in gtest-based unit tests but is defined in +// the test_support_base target. Depend on that to use this in your unit test. +// This should not be used in production code - call ToString() instead. +void PrintTo(const FilePath& path, std::ostream* out); + +} // namespace base // Macros for string literal initialization of FilePath::CharType[], and for // using a FilePath::CharType[] in a printf-style format string. #if defined(OS_POSIX) #define FILE_PATH_LITERAL(x) x #define PRFilePath "s" -#define PRFilePathLiteral "%s" #elif defined(OS_WIN) #define FILE_PATH_LITERAL(x) L ## x #define PRFilePath "ls" -#define PRFilePathLiteral L"%ls" #endif // OS_WIN // Provide a hash function so that hash_sets and maps can contain FilePath diff --git a/security/sandbox/chromium/base/float_util.h b/security/sandbox/chromium/base/float_util.h deleted file mode 100644 index 90273106cd65..000000000000 --- a/security/sandbox/chromium/base/float_util.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_FLOAT_UTIL_H_ -#define BASE_FLOAT_UTIL_H_ - -#include "build/build_config.h" - -#include - -#include - -namespace base { - -template -inline bool IsFinite(const Float& number) { -#if defined(OS_POSIX) - return std::isfinite(number) != 0; -#elif defined(OS_WIN) - return _finite(number) != 0; -#endif -} - -template -inline bool IsNaN(const Float& number) { -#if defined(OS_POSIX) - return std::isnan(number) != 0; -#elif defined(OS_WIN) - return _isnan(number) != 0; -#endif -} - -} // namespace base - -#endif // BASE_FLOAT_UTIL_H_ diff --git a/security/sandbox/chromium/base/guid.h b/security/sandbox/chromium/base/guid.h index abcc589f72d9..c0a06f8858e3 100644 --- a/security/sandbox/chromium/base/guid.h +++ b/security/sandbox/chromium/base/guid.h @@ -5,10 +5,11 @@ #ifndef BASE_GUID_H_ #define BASE_GUID_H_ +#include + #include #include "base/base_export.h" -#include "base/basictypes.h" #include "build/build_config.h" namespace base { @@ -24,7 +25,7 @@ BASE_EXPORT bool IsValidGUID(const std::string& guid); #if defined(OS_POSIX) // For unit testing purposes only. Do not use outside of tests. -BASE_EXPORT std::string RandomDataToGUIDString(const uint64 bytes[2]); +BASE_EXPORT std::string RandomDataToGUIDString(const uint64_t bytes[2]); #endif } // namespace base diff --git a/security/sandbox/chromium/base/hash.cc b/security/sandbox/chromium/base/hash.cc index a7db64a919c8..d3206f6a6c9c 100644 --- a/security/sandbox/chromium/base/hash.cc +++ b/security/sandbox/chromium/base/hash.cc @@ -11,7 +11,7 @@ extern "C" uint32_t SuperFastHash(const char* data, int len); namespace base { -uint32 SuperFastHash(const char* data, int len) { +uint32_t SuperFastHash(const char* data, int len) { return ::SuperFastHash(data, len); } diff --git a/security/sandbox/chromium/base/hash.h b/security/sandbox/chromium/base/hash.h index e46f6ac24842..ed8d9fd4cc9b 100644 --- a/security/sandbox/chromium/base/hash.h +++ b/security/sandbox/chromium/base/hash.h @@ -5,21 +5,23 @@ #ifndef BASE_HASH_H_ #define BASE_HASH_H_ +#include +#include + #include #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/logging.h" namespace base { // WARNING: This hash function should not be used for any cryptographic purpose. -BASE_EXPORT uint32 SuperFastHash(const char* data, int len); +BASE_EXPORT uint32_t SuperFastHash(const char* data, int len); // Computes a hash of a memory buffer |data| of a given |length|. // WARNING: This hash function should not be used for any cryptographic purpose. -inline uint32 Hash(const char* data, size_t length) { +inline uint32_t Hash(const char* data, size_t length) { if (length > static_cast(std::numeric_limits::max())) { NOTREACHED(); return 0; @@ -29,7 +31,7 @@ inline uint32 Hash(const char* data, size_t length) { // Computes a hash of a string |str|. // WARNING: This hash function should not be used for any cryptographic purpose. -inline uint32 Hash(const std::string& str) { +inline uint32_t Hash(const std::string& str) { return Hash(str.data(), str.size()); } diff --git a/security/sandbox/chromium/base/lazy_instance.cc b/security/sandbox/chromium/base/lazy_instance.cc index 594c1fee4ffe..54680655a2a6 100644 --- a/security/sandbox/chromium/base/lazy_instance.cc +++ b/security/sandbox/chromium/base/lazy_instance.cc @@ -6,7 +6,6 @@ #include "base/at_exit.h" #include "base/atomicops.h" -#include "base/basictypes.h" #include "base/threading/platform_thread.h" namespace base { diff --git a/security/sandbox/chromium/base/lazy_instance.h b/security/sandbox/chromium/base/lazy_instance.h index d52b5431c29b..fd0321017df8 100644 --- a/security/sandbox/chromium/base/lazy_instance.h +++ b/security/sandbox/chromium/base/lazy_instance.h @@ -39,7 +39,6 @@ #include "base/atomicops.h" #include "base/base_export.h" -#include "base/basictypes.h" #include "base/debug/leak_annotations.h" #include "base/logging.h" #include "base/memory/aligned_memory.h" diff --git a/security/sandbox/chromium/base/location.cc b/security/sandbox/chromium/base/location.cc index 8b32b9785ba2..1333e6ec4535 100644 --- a/security/sandbox/chromium/base/location.cc +++ b/security/sandbox/chromium/base/location.cc @@ -31,6 +31,13 @@ Location::Location() program_counter_(NULL) { } +Location::Location(const Location& other) + : function_name_(other.function_name_), + file_name_(other.file_name_), + line_number_(other.line_number_), + program_counter_(other.program_counter_) { +} + std::string Location::ToString() const { return std::string(function_name_) + "@" + file_name_ + ":" + base::IntToString(line_number_); diff --git a/security/sandbox/chromium/base/location.h b/security/sandbox/chromium/base/location.h index 05a4f6610905..d3bb23c63ea4 100644 --- a/security/sandbox/chromium/base/location.h +++ b/security/sandbox/chromium/base/location.h @@ -5,10 +5,13 @@ #ifndef BASE_LOCATION_H_ #define BASE_LOCATION_H_ +#include + +#include #include #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/containers/hash_tables.h" namespace tracked_objects { @@ -27,18 +30,15 @@ class BASE_EXPORT Location { // Provide a default constructor for easy of debugging. Location(); - // Comparison operator for insertion into a std::map<> hash tables. - // All we need is *some* (any) hashing distinction. Strings should already - // be unique, so we don't bother with strcmp or such. - // Use line number as the primary key (because it is fast, and usually gets us - // a difference), and then pointers as secondary keys (just to get some - // distinctions). - bool operator < (const Location& other) const { - if (line_number_ != other.line_number_) - return line_number_ < other.line_number_; - if (file_name_ != other.file_name_) - return file_name_ < other.file_name_; - return function_name_ < other.function_name_; + // Copy constructor. + Location(const Location& other); + + // Comparator for hash map insertion. + // No need to use |function_name_| since the other two fields uniquely + // identify this location. + bool operator==(const Location& other) const { + return line_number_ == other.line_number_ && + file_name_ == other.file_name_; } const char* function_name() const { return function_name_; } @@ -48,6 +48,22 @@ class BASE_EXPORT Location { std::string ToString() const; + // Hash operator for hash maps. + struct Hash { + size_t operator()(const Location& location) const { + // Compute the hash value using file name pointer and line number. + // No need to use |function_name_| since the other two fields uniquely + // identify this location. + + // The file name will always be uniquely identified by its pointer since + // it comes from __FILE__, so no need to check the contents of the string. + // See the definition of FROM_HERE in location.h, and how it is used + // elsewhere. + return base::HashPair(reinterpret_cast(location.file_name()), + location.line_number()); + } + }; + // Translate the some of the state in this instance into a human readable // string with HTML characters in the function names escaped, and append that // string to |output|. Inclusion of the file_name_ and function_name_ are diff --git a/security/sandbox/chromium/base/logging.h b/security/sandbox/chromium/base/logging.h index 6a1df765895c..300c9b52694b 100644 --- a/security/sandbox/chromium/base/logging.h +++ b/security/sandbox/chromium/base/logging.h @@ -5,14 +5,16 @@ #ifndef BASE_LOGGING_H_ #define BASE_LOGGING_H_ +#include + #include -#include #include #include +#include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/debug/debugger.h" +#include "base/macros.h" #include "build/build_config.h" // @@ -238,6 +240,9 @@ BASE_EXPORT void SetMinLogLevel(int level); // Gets the current log level. BASE_EXPORT int GetMinLogLevel(); +// Used by LOG_IS_ON to lazy-evaluate stream arguments. +BASE_EXPORT bool ShouldCreateLogMessage(int severity); + // Gets the VLOG default verbosity level. BASE_EXPORT int GetVlogVerbosity(); @@ -340,7 +345,7 @@ const LogSeverity LOG_0 = LOG_ERROR; // LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will // always fire if they fail. #define LOG_IS_ON(severity) \ - ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) + (::logging::ShouldCreateLogMessage(::logging::LOG_##severity)) // We can't do any caching tricks with VLOG_IS_ON() like the // google-glog version since it requires GCC extensions. This means @@ -350,7 +355,7 @@ const LogSeverity LOG_0 = LOG_ERROR; ((verboselevel) <= ::logging::GetVlogLevel(__FILE__)) // Helper macro which avoids evaluating the arguments to a stream if -// the condition doesn't hold. +// the condition doesn't hold. Condition is evaluated once and only once. #define LAZY_STREAM(stream, condition) \ !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) @@ -426,6 +431,21 @@ const LogSeverity LOG_0 = LOG_ERROR; #define EAT_STREAM_PARAMETERS \ true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL) +// Captures the result of a CHECK_EQ (for example) and facilitates testing as a +// boolean. +class CheckOpResult { + public: + // |message| must be null if and only if the check failed. + CheckOpResult(std::string* message) : message_(message) {} + // Returns true if the check succeeded. + operator bool() const { return !message_; } + // Returns the message. + std::string* message() { return message_; } + + private: + std::string* message_; +}; + // CHECK dies with a fatal error if condition is not true. It is *not* // controlled by NDEBUG, so the check will be executed regardless of // compilation mode. @@ -436,7 +456,7 @@ const LogSeverity LOG_0 = LOG_ERROR; #if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID) // Make all CHECK functions discard their log strings to reduce code -// bloat for official release builds. +// bloat for official release builds (except Android). // TODO(akalin): This would be more valuable if there were some way to // remove BreakDebugger() from the backtrace, perhaps by turning it @@ -470,9 +490,10 @@ const LogSeverity LOG_0 = LOG_ERROR; #else // _PREFAST_ -#define CHECK(condition) \ - LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ - << "Check failed: " #condition ". " +// Do as much work as possible out of line to reduce inline code size. +#define CHECK(condition) \ + LAZY_STREAM(logging::LogMessage(__FILE__, __LINE__, #condition).stream(), \ + !(condition)) #define PCHECK(condition) \ LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \ @@ -482,14 +503,18 @@ const LogSeverity LOG_0 = LOG_ERROR; // Helper macro for binary operators. // Don't use this macro directly in your code, use CHECK_EQ et al below. -// -// TODO(akalin): Rewrite this so that constructs like if (...) -// CHECK_EQ(...) else { ... } work properly. -#define CHECK_OP(name, op, val1, val2) \ - if (std::string* _result = \ - logging::Check##name##Impl((val1), (val2), \ - #val1 " " #op " " #val2)) \ - logging::LogMessage(__FILE__, __LINE__, _result).stream() +// The 'switch' is used to prevent the 'else' from being ambiguous when the +// macro is used in an 'if' clause such as: +// if (a == 1) +// CHECK_EQ(2, a); +#define CHECK_OP(name, op, val1, val2) \ + switch (0) case 0: default: \ + if (logging::CheckOpResult true_if_passed = \ + logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + ; \ + else \ + logging::LogMessage(__FILE__, __LINE__, true_if_passed.message()).stream() #endif @@ -551,7 +576,6 @@ DEFINE_CHECK_OP_IMPL(GT, > ) #define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2) #define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) #define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) -#define CHECK_IMPLIES(val1, val2) CHECK(!(val1) || (val2)) #if defined(NDEBUG) #define ENABLE_DLOG 0 @@ -560,9 +584,9 @@ DEFINE_CHECK_OP_IMPL(GT, > ) #endif #if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) -#define DCHECK_IS_ON 0 +#define DCHECK_IS_ON() 0 #else -#define DCHECK_IS_ON 1 +#define DCHECK_IS_ON() 1 #endif // Definitions for DLOG et al. @@ -616,14 +640,14 @@ enum { DEBUG_MODE = ENABLE_DLOG }; // Definitions for DCHECK et al. -#if DCHECK_IS_ON +#if DCHECK_IS_ON() #define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL const LogSeverity LOG_DCHECK = LOG_FATAL; -#else // DCHECK_IS_ON +#else // DCHECK_IS_ON() // These are just dummy values. #define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ @@ -631,7 +655,7 @@ const LogSeverity LOG_DCHECK = LOG_FATAL; #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO const LogSeverity LOG_DCHECK = LOG_INFO; -#endif // DCHECK_IS_ON +#endif // DCHECK_IS_ON() // DCHECK et al. make sure to reference |condition| regardless of // whether DCHECKs are enabled; this is so that we don't get unused @@ -653,26 +677,32 @@ const LogSeverity LOG_DCHECK = LOG_INFO; #else // _PREFAST_ -#define DCHECK(condition) \ - LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON ? !(condition) : false) \ - << "Check failed: " #condition ". " +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \ + << "Check failed: " #condition ". " -#define DPCHECK(condition) \ - LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON ? !(condition) : false) \ - << "Check failed: " #condition ". " +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() ? !(condition) : false) \ + << "Check failed: " #condition ". " #endif // _PREFAST_ // Helper macro for binary operators. // Don't use this macro directly in your code, use DCHECK_EQ et al below. -#define DCHECK_OP(name, op, val1, val2) \ - if (DCHECK_IS_ON) \ - if (std::string* _result = \ - logging::Check##name##Impl((val1), (val2), \ - #val1 " " #op " " #val2)) \ - logging::LogMessage( \ - __FILE__, __LINE__, ::logging::LOG_DCHECK, \ - _result).stream() +// The 'switch' is used to prevent the 'else' from being ambiguous when the +// macro is used in an 'if' clause such as: +// if (a == 1) +// DCHECK_EQ(2, a); +#define DCHECK_OP(name, op, val1, val2) \ + switch (0) case 0: default: \ + if (logging::CheckOpResult true_if_passed = \ + DCHECK_IS_ON() ? \ + logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2) : nullptr) \ + ; \ + else \ + logging::LogMessage(__FILE__, __LINE__, ::logging::LOG_DCHECK, \ + true_if_passed.message()).stream() // Equality/Inequality checks - compare two values, and log a // LOG_DCHECK message including the two values when the result is not @@ -699,11 +729,14 @@ const LogSeverity LOG_DCHECK = LOG_INFO; #define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2) #define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) #define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) -#define DCHECK_IMPLIES(val1, val2) DCHECK(!(val1) || (val2)) -#if !DCHECK_IS_ON && defined(OS_CHROMEOS) -#define NOTREACHED() LOG(ERROR) << "NOTREACHED() hit in " << \ - __FUNCTION__ << ". " +#if !DCHECK_IS_ON() && defined(OS_CHROMEOS) +// Implement logging of NOTREACHED() as a dedicated function to get function +// call overhead down to a minimum. +void LogErrorNotReached(const char* file, int line); +#define NOTREACHED() \ + true ? ::logging::LogErrorNotReached(__FILE__, __LINE__) \ + : EAT_STREAM_PARAMETERS #else #define NOTREACHED() DCHECK(false) #endif @@ -725,6 +758,9 @@ class BASE_EXPORT LogMessage { // Used for LOG(severity). LogMessage(const char* file, int line, LogSeverity severity); + // Used for CHECK(). Implied severity = LOG_FATAL. + LogMessage(const char* file, int line, const char* condition); + // Used for CHECK_EQ(), etc. Takes ownership of the given string. // Implied severity = LOG_FATAL. LogMessage(const char* file, int line, std::string* result); @@ -773,7 +809,7 @@ class BASE_EXPORT LogMessage { // A non-macro interface to the log facility; (useful // when the logging level is not a compile-time constant). -inline void LogAtLevel(int const log_level, std::string const &msg) { +inline void LogAtLevel(int log_level, const std::string& msg) { LogMessage(__FILE__, __LINE__, log_level).stream() << msg; } @@ -859,6 +895,9 @@ BASE_EXPORT void RawLog(int level, const char* message); } while (0) #if defined(OS_WIN) +// Returns true if logging to file is enabled. +BASE_EXPORT bool IsLoggingToFileEnabled(); + // Returns the default log file path. BASE_EXPORT std::wstring GetLogFileFullPath(); #endif @@ -918,9 +957,9 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { #define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS #elif NOTIMPLEMENTED_POLICY == 1 // TODO, figure out how to generate a warning -#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED) +#define NOTIMPLEMENTED() static_assert(false, "NOT_IMPLEMENTED") #elif NOTIMPLEMENTED_POLICY == 2 -#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED) +#define NOTIMPLEMENTED() static_assert(false, "NOT_IMPLEMENTED") #elif NOTIMPLEMENTED_POLICY == 3 #define NOTIMPLEMENTED() NOTREACHED() #elif NOTIMPLEMENTED_POLICY == 4 diff --git a/security/sandbox/chromium/base/macros.h b/security/sandbox/chromium/base/macros.h index b6240da2beee..46ee1dadb474 100644 --- a/security/sandbox/chromium/base/macros.h +++ b/security/sandbox/chromium/base/macros.h @@ -11,17 +11,14 @@ #define BASE_MACROS_H_ #include // For size_t. -#include // For memcpy. -#include "base/compiler_specific.h" // For ALLOW_UNUSED. - -// Put this in the private: declarations for a class to be uncopyable. +// Put this in the declarations for a class to be uncopyable. #define DISALLOW_COPY(TypeName) \ - TypeName(const TypeName&) + TypeName(const TypeName&) = delete -// Put this in the private: declarations for a class to be unassignable. +// Put this in the declarations for a class to be unassignable. #define DISALLOW_ASSIGN(TypeName) \ - void operator=(const TypeName&) + void operator=(const TypeName&) = delete // A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for a class @@ -29,13 +26,6 @@ TypeName(const TypeName&); \ void operator=(const TypeName&) -// An older, deprecated, politically incorrect name for the above. -// NOTE: The usage of this macro was banned from our code base, but some -// third_party libraries are yet using it. -// TODO(tfarina): Figure out how to fix the usage of this macro in the -// third_party libraries and get rid of it. -#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName) - // A macro to disallow all the implicit constructors, namely the // default constructor, copy constructor and operator= functions. // @@ -43,134 +33,21 @@ // that wants to prevent anyone from instantiating it. This is // especially useful for classes containing only static methods. #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ + TypeName() = delete; \ DISALLOW_COPY_AND_ASSIGN(TypeName) -// The arraysize(arr) macro returns the # of elements in an array arr. -// The expression is a compile-time constant, and therefore can be -// used in defining new arrays, for example. If you use arraysize on -// a pointer by mistake, you will get a compile-time error. +// The arraysize(arr) macro returns the # of elements in an array arr. The +// expression is a compile-time constant, and therefore can be used in defining +// new arrays, for example. If you use arraysize on a pointer by mistake, you +// will get a compile-time error. For the technical details, refer to +// http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx. // This template function declaration is used in defining arraysize. // Note that the function doesn't need an implementation, as we only // use its type. -template -char (&ArraySizeHelper(T (&array)[N]))[N]; - -// That gcc wants both of these prototypes seems mysterious. VC, for -// its part, can't decide which to use (another mystery). Matching of -// template overloads: the final frontier. -#ifndef _MSC_VER -template -char (&ArraySizeHelper(const T (&array)[N]))[N]; -#endif - +template char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) - -// Use implicit_cast as a safe version of static_cast or const_cast -// for upcasting in the type hierarchy (i.e. casting a pointer to Foo -// to a pointer to SuperclassOfFoo or casting a pointer to Foo to -// a const pointer to Foo). -// When you use implicit_cast, the compiler checks that the cast is safe. -// Such explicit implicit_casts are necessary in surprisingly many -// situations where C++ demands an exact type match instead of an -// argument type convertible to a target type. -// -// The From type can be inferred, so the preferred syntax for using -// implicit_cast is the same as for static_cast etc.: -// -// implicit_cast(expr) -// -// implicit_cast would have been part of the C++ standard library, -// but the proposal was submitted too late. It will probably make -// its way into the language in the future. -template -inline To implicit_cast(From const &f) { - return f; -} - -// The COMPILE_ASSERT macro can be used to verify that a compile time -// expression is true. For example, you could use it to verify the -// size of a static array: -// -// COMPILE_ASSERT(arraysize(content_type_names) == CONTENT_NUM_TYPES, -// content_type_names_incorrect_size); -// -// or to make sure a struct is smaller than a certain size: -// -// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); -// -// The second argument to the macro is the name of the variable. If -// the expression is false, most compilers will issue a warning/error -// containing the name of the variable. - -#undef COMPILE_ASSERT -#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg) - -// bit_cast is a template function that implements the -// equivalent of "*reinterpret_cast(&source)". We need this in -// very low-level functions like the protobuf library and fast math -// support. -// -// float f = 3.14159265358979; -// int i = bit_cast(f); -// // i = 0x40490fdb -// -// The classical address-casting method is: -// -// // WRONG -// float f = 3.14159265358979; // WRONG -// int i = * reinterpret_cast(&f); // WRONG -// -// The address-casting method actually produces undefined behavior -// according to ISO C++ specification section 3.10 -15 -. Roughly, this -// section says: if an object in memory has one type, and a program -// accesses it with a different type, then the result is undefined -// behavior for most values of "different type". -// -// This is true for any cast syntax, either *(int*)&f or -// *reinterpret_cast(&f). And it is particularly true for -// conversions between integral lvalues and floating-point lvalues. -// -// The purpose of 3.10 -15- is to allow optimizing compilers to assume -// that expressions with different types refer to different memory. gcc -// 4.0.1 has an optimizer that takes advantage of this. So a -// non-conforming program quietly produces wildly incorrect output. -// -// The problem is not the use of reinterpret_cast. The problem is type -// punning: holding an object in memory of one type and reading its bits -// back using a different type. -// -// The C++ standard is more subtle and complex than this, but that -// is the basic idea. -// -// Anyways ... -// -// bit_cast<> calls memcpy() which is blessed by the standard, -// especially by the example in section 3.9 . Also, of course, -// bit_cast<> wraps up the nasty logic in one place. -// -// Fortunately memcpy() is very fast. In optimized mode, with a -// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline -// code with the minimal amount of data movement. On a 32-bit system, -// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) -// compiles to two loads and two stores. -// -// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. -// -// WARNING: if Dest or Source is a non-POD type, the result of the memcpy -// is likely to surprise you. - -template -inline Dest bit_cast(const Source& source) { - COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual); - - Dest dest; - memcpy(&dest, &source, sizeof(dest)); - return dest; -} - // Used to explicitly mark the return value of a function as unused. If you are // really sure you don't want to do anything with the return value of a function // that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: diff --git a/security/sandbox/chromium/base/memory/aligned_memory.h b/security/sandbox/chromium/base/memory/aligned_memory.h index 1a4cba9ad845..bb7bd872cfb0 100644 --- a/security/sandbox/chromium/base/memory/aligned_memory.h +++ b/security/sandbox/chromium/base/memory/aligned_memory.h @@ -34,8 +34,10 @@ #ifndef BASE_MEMORY_ALIGNED_MEMORY_H_ #define BASE_MEMORY_ALIGNED_MEMORY_H_ +#include +#include + #include "base/base_export.h" -#include "base/basictypes.h" #include "base/compiler_specific.h" #if defined(COMPILER_MSVC) @@ -51,25 +53,26 @@ namespace base { template struct AlignedMemory {}; -#define BASE_DECL_ALIGNED_MEMORY(byte_alignment) \ - template \ - class AlignedMemory { \ - public: \ - ALIGNAS(byte_alignment) uint8 data_[Size]; \ - void* void_data() { return static_cast(data_); } \ - const void* void_data() const { \ - return static_cast(data_); \ - } \ - template \ - Type* data_as() { return static_cast(void_data()); } \ - template \ - const Type* data_as() const { \ - return static_cast(void_data()); \ - } \ - private: \ - void* operator new(size_t); \ - void operator delete(void*); \ - } +#define BASE_DECL_ALIGNED_MEMORY(byte_alignment) \ + template \ + class AlignedMemory { \ + public: \ + ALIGNAS(byte_alignment) uint8_t data_[Size]; \ + void* void_data() { return static_cast(data_); } \ + const void* void_data() const { return static_cast(data_); } \ + template \ + Type* data_as() { \ + return static_cast(void_data()); \ + } \ + template \ + const Type* data_as() const { \ + return static_cast(void_data()); \ + } \ + \ + private: \ + void* operator new(size_t); \ + void operator delete(void*); \ + } // Specialization for all alignments is required because MSVC (as of VS 2008) // does not understand ALIGNAS(ALIGNOF(Type)) or ALIGNAS(template_param). diff --git a/security/sandbox/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h b/security/sandbox/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h index 7974f3047b2e..09f982b12945 100644 --- a/security/sandbox/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h +++ b/security/sandbox/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h @@ -47,79 +47,14 @@ struct ParamsUseScopedRefptrCorrectly { }; template <> -struct ParamsUseScopedRefptrCorrectly { +struct ParamsUseScopedRefptrCorrectly> { enum { value = 1 }; }; -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !NeedsScopedRefptrButGetsRawPtr::value }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; -}; - -template -struct ParamsUseScopedRefptrCorrectly > { - enum { value = !(NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value || - NeedsScopedRefptrButGetsRawPtr::value) }; +template +struct ParamsUseScopedRefptrCorrectly> { + enum { value = !NeedsScopedRefptrButGetsRawPtr::value && + ParamsUseScopedRefptrCorrectly>::value }; }; } // namespace internal diff --git a/security/sandbox/chromium/base/memory/ref_counted.h b/security/sandbox/chromium/base/memory/ref_counted.h index ab47c4745f80..a1c12696990d 100644 --- a/security/sandbox/chromium/base/memory/ref_counted.h +++ b/security/sandbox/chromium/base/memory/ref_counted.h @@ -11,16 +11,12 @@ #include "base/atomic_ref_count.h" #include "base/base_export.h" #include "base/compiler_specific.h" +#include "base/macros.h" #ifndef NDEBUG #include "base/logging.h" #endif #include "base/threading/thread_collision_warner.h" #include "build/build_config.h" -#include "mozilla/Attributes.h" - -#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_ANDROID) -#define DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR -#endif namespace base { @@ -122,7 +118,7 @@ class BASE_EXPORT RefCountedThreadSafeBase { // ~MyFoo(); // }; // -// You should always make your destructor private, to avoid any code deleting +// You should always make your destructor non-public, to avoid any code deleting // the object accidently while there are references to it. template class RefCounted : public subtle::RefCountedBase { @@ -280,17 +276,29 @@ class scoped_refptr { AddRef(ptr_); } + // Copy constructor. scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { if (ptr_) AddRef(ptr_); } + // Copy conversion constructor. template scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) { if (ptr_) AddRef(ptr_); } + // Move constructor. This is required in addition to the conversion + // constructor below in order for clang to warn about pessimizing moves. + scoped_refptr(scoped_refptr&& r) : ptr_(r.get()) { r.ptr_ = nullptr; } + + // Move conversion constructor. + template + scoped_refptr(scoped_refptr&& r) : ptr_(r.get()) { + r.ptr_ = nullptr; + } + ~scoped_refptr() { if (ptr_) Release(ptr_); @@ -298,12 +306,6 @@ class scoped_refptr { T* get() const { return ptr_; } -#if !defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) - // Allow scoped_refptr to be used in boolean expression - // and comparison operations. - operator T*() const { return ptr_; } -#endif - T& operator*() const { assert(ptr_ != NULL); return *ptr_; @@ -334,6 +336,17 @@ class scoped_refptr { return *this = r.get(); } + scoped_refptr& operator=(scoped_refptr&& r) { + scoped_refptr(std::move(r)).swap(*this); + return *this; + } + + template + scoped_refptr& operator=(scoped_refptr&& r) { + scoped_refptr(std::move(r)).swap(*this); + return *this; + } + void swap(T** pp) { T* p = ptr_; ptr_ = *pp; @@ -344,7 +357,21 @@ class scoped_refptr { swap(&r.ptr_); } -#if defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) + private: + template friend class scoped_refptr; + + // Allow scoped_refptr to be used in boolean expressions, but not + // implicitly convertible to a real bool (which is dangerous). + // + // Note that this trick is only safe when the == and != operators + // are declared explicitly, as otherwise "refptr1 == refptr2" + // will compile but do the wrong thing (i.e., convert to Testable + // and then do the comparison). + typedef T* scoped_refptr::*Testable; + + public: + operator Testable() const { return ptr_ ? &scoped_refptr::ptr_ : nullptr; } + template bool operator==(const scoped_refptr& rhs) const { return ptr_ == rhs.get(); @@ -359,7 +386,6 @@ class scoped_refptr { bool operator<(const scoped_refptr& rhs) const { return ptr_ < rhs.get(); } -#endif protected: T* ptr_; @@ -390,8 +416,8 @@ scoped_refptr make_scoped_refptr(T* t) { return scoped_refptr(t); } -#if defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) -// Temporary operator overloads to facilitate the transition... +// Temporary operator overloads to facilitate the transition. See +// https://crbug.com/110610. template bool operator==(const scoped_refptr& lhs, const U* rhs) { return lhs.get() == rhs; @@ -416,6 +442,5 @@ template std::ostream& operator<<(std::ostream& out, const scoped_refptr& p) { return out << p.get(); } -#endif // defined(DISABLE_SCOPED_REFPTR_CONVERSION_OPERATOR) #endif // BASE_MEMORY_REF_COUNTED_H_ diff --git a/security/sandbox/chromium/base/memory/scoped_ptr.h b/security/sandbox/chromium/base/memory/scoped_ptr.h index ae9eb0fee8bd..282a014869b6 100644 --- a/security/sandbox/chromium/base/memory/scoped_ptr.h +++ b/security/sandbox/chromium/base/memory/scoped_ptr.h @@ -37,42 +37,43 @@ // in that they are "movable but not copyable." You can use the scopers in // the parameter and return types of functions to signify ownership transfer // in to and out of a function. When calling a function that has a scoper -// as the argument type, it must be called with the result of an analogous -// scoper's Pass() function or another function that generates a temporary; -// passing by copy will NOT work. Here is an example using scoped_ptr: +// as the argument type, it must be called with an rvalue of a scoper, which +// can be created by using std::move(), or the result of another function that +// generates a temporary; passing by copy will NOT work. Here is an example +// using scoped_ptr: // // void TakesOwnership(scoped_ptr arg) { -// // Do something with arg +// // Do something with arg. // } // scoped_ptr CreateFoo() { -// // No need for calling Pass() because we are constructing a temporary -// // for the return value. +// // No need for calling std::move() for returning a move-only value, or +// // when you already have an rvalue as we do here. // return scoped_ptr(new Foo("new")); // } // scoped_ptr PassThru(scoped_ptr arg) { -// return arg.Pass(); +// return arg; // } // // { // scoped_ptr ptr(new Foo("yay")); // ptr manages Foo("yay"). -// TakesOwnership(ptr.Pass()); // ptr no longer owns Foo("yay"). +// TakesOwnership(std::move(ptr)); // ptr no longer owns Foo("yay"). // scoped_ptr ptr2 = CreateFoo(); // ptr2 owns the return Foo. // scoped_ptr ptr3 = // ptr3 now owns what was in ptr2. -// PassThru(ptr2.Pass()); // ptr2 is correspondingly nullptr. +// PassThru(std::move(ptr2)); // ptr2 is correspondingly nullptr. // } // -// Notice that if you do not call Pass() when returning from PassThru(), or +// Notice that if you do not call std::move() when returning from PassThru(), or // when invoking TakesOwnership(), the code will not compile because scopers // are not copyable; they only implement move semantics which require calling -// the Pass() function to signify a destructive transfer of state. CreateFoo() -// is different though because we are constructing a temporary on the return -// line and thus can avoid needing to call Pass(). +// the std::move() function to signify a destructive transfer of state. +// CreateFoo() is different though because we are constructing a temporary on +// the return line and thus can avoid needing to call std::move(). // -// Pass() properly handles upcast in initialization, i.e. you can use a -// scoped_ptr to initialize a scoped_ptr: +// The conversion move-constructor properly handles upcast in initialization, +// i.e. you can use a scoped_ptr to initialize a scoped_ptr: // // scoped_ptr foo(new Foo()); -// scoped_ptr parent(foo.Pass()); +// scoped_ptr parent(std::move(foo)); #ifndef BASE_MEMORY_SCOPED_PTR_H_ #define BASE_MEMORY_SCOPED_PTR_H_ @@ -84,10 +85,13 @@ #include #include -#include // For std::swap(). +#include +#include +#include +#include -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/move.h" #include "base/template_util.h" @@ -98,61 +102,6 @@ class RefCountedBase; class RefCountedThreadSafeBase; } // namespace subtle -// Function object which deletes its parameter, which must be a pointer. -// If C is an array type, invokes 'delete[]' on the parameter; otherwise, -// invokes 'delete'. The default deleter for scoped_ptr. -template -struct DefaultDeleter { - DefaultDeleter() {} - template DefaultDeleter(const DefaultDeleter& other) { - // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor - // if U* is implicitly convertible to T* and U is not an array type. - // - // Correct implementation should use SFINAE to disable this - // constructor. However, since there are no other 1-argument constructors, - // using a COMPILE_ASSERT() based on is_convertible<> and requiring - // complete types is simpler and will cause compile failures for equivalent - // misuses. - // - // Note, the is_convertible check also ensures that U is not an - // array. T is guaranteed to be a non-array, so any U* where U is an array - // cannot convert to T*. - enum { T_must_be_complete = sizeof(T) }; - enum { U_must_be_complete = sizeof(U) }; - COMPILE_ASSERT((base::is_convertible::value), - U_ptr_must_implicitly_convert_to_T_ptr); - } - inline void operator()(T* ptr) const { - enum { type_must_be_complete = sizeof(T) }; - delete ptr; - } -}; - -// Specialization of DefaultDeleter for array types. -template -struct DefaultDeleter { - inline void operator()(T* ptr) const { - enum { type_must_be_complete = sizeof(T) }; - delete[] ptr; - } - - private: - // Disable this operator for any U != T because it is undefined to execute - // an array delete when the static type of the array mismatches the dynamic - // type. - // - // References: - // C++98 [expr.delete]p3 - // http://cplusplus.github.com/LWG/lwg-defects.html#938 - template void operator()(U* array) const; -}; - -template -struct DefaultDeleter { - // Never allow someone to declare something like scoped_ptr. - COMPILE_ASSERT(sizeof(T) == -1, do_not_use_array_with_size_as_type); -}; - // Function object which invokes 'free' on its parameter, which must be // a pointer. Can be used to store malloc-allocated pointers in scoped_ptr: // @@ -174,17 +123,6 @@ template struct IsNotRefCounted { }; }; -template -struct ShouldAbortOnSelfReset { - template - static NoType Test(const typename U::AllowSelfReset*); - - template - static YesType Test(...); - - static const bool value = sizeof(Test(0)) == sizeof(YesType); -}; - // Minimal implementation of the core logic of scoped_ptr, suitable for // reuse in both scoped_ptr and its specializations. template @@ -215,37 +153,28 @@ class scoped_ptr_impl { } ~scoped_ptr_impl() { - if (data_.ptr != nullptr) { - // Not using get_deleter() saves one function call in non-optimized - // builds. - static_cast(data_)(data_.ptr); - } + // Match libc++, which calls reset() in its destructor. + // Use nullptr as the new value for three reasons: + // 1. libc++ does it. + // 2. Avoids infinitely recursing into destructors if two classes are owned + // in a reference cycle (see ScopedPtrTest.ReferenceCycle). + // 3. If |this| is accessed in the future, in a use-after-free bug, attempts + // to dereference |this|'s pointer should cause either a failure or a + // segfault closer to the problem. If |this| wasn't reset to nullptr, + // the access would cause the deleted memory to be read or written + // leading to other more subtle issues. + reset(nullptr); } void reset(T* p) { - // This is a self-reset, which is no longer allowed for default deleters: - // https://crbug.com/162971 - assert(!ShouldAbortOnSelfReset::value || p == nullptr || p != data_.ptr); - - // Note that running data_.ptr = p can lead to undefined behavior if - // get_deleter()(get()) deletes this. In order to prevent this, reset() - // should update the stored pointer before deleting its old value. - // - // However, changing reset() to use that behavior may cause current code to - // break in unexpected ways. If the destruction of the owned object - // dereferences the scoped_ptr when it is destroyed by a call to reset(), - // then it will incorrectly dispatch calls to |p| rather than the original - // value of |data_.ptr|. - // - // During the transition period, set the stored pointer to nullptr while - // deleting the object. Eventually, this safety check will be removed to - // prevent the scenario initially described from occuring and - // http://crbug.com/176091 can be closed. + // Match C++11's definition of unique_ptr::reset(), which requires changing + // the pointer before invoking the deleter on the old pointer. This prevents + // |this| from being accessed after the deleter is run, which may destroy + // |this|. T* old = data_.ptr; - data_.ptr = nullptr; + data_.ptr = p; if (old != nullptr) static_cast(data_)(old); - data_.ptr = p; } T* get() const { return data_.ptr; } @@ -299,25 +228,27 @@ class scoped_ptr_impl { // dereference it, you get the thread safety guarantees of T. // // The size of scoped_ptr is small. On most compilers, when using the -// DefaultDeleter, sizeof(scoped_ptr) == sizeof(T*). Custom deleters will -// increase the size proportional to whatever state they need to have. See +// std::default_delete, sizeof(scoped_ptr) == sizeof(T*). Custom deleters +// will increase the size proportional to whatever state they need to have. See // comments inside scoped_ptr_impl<> for details. // // Current implementation targets having a strict subset of C++11's // unique_ptr<> features. Known deficiencies include not supporting move-only // deleteres, function pointers as deleters, and deleters with reference // types. -template > +template > class scoped_ptr { - MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_ptr) + DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(scoped_ptr) - COMPILE_ASSERT(base::internal::IsNotRefCounted::value, - T_is_refcounted_type_and_needs_scoped_refptr); + static_assert(!std::is_array::value, + "scoped_ptr doesn't support array with size"); + static_assert(base::internal::IsNotRefCounted::value, + "T is a refcounted type and needs a scoped_refptr"); public: // The element and deleter types. - typedef T element_type; - typedef D deleter_type; + using element_type = T; + using deleter_type = D; // Constructor. Defaults to initializing with nullptr. scoped_ptr() : impl_(nullptr) {} @@ -329,44 +260,87 @@ class scoped_ptr { scoped_ptr(element_type* p, const D& d) : impl_(p, d) {} // Constructor. Allows construction from a nullptr. - scoped_ptr(decltype(nullptr)) : impl_(nullptr) {} + scoped_ptr(std::nullptr_t) : impl_(nullptr) {} - // Constructor. Allows construction from a scoped_ptr rvalue for a + // Move constructor. + // + // IMPLEMENTATION NOTE: Clang requires a move constructor to be defined (and + // not just the conversion constructor) in order to warn on pessimizing moves. + // The requirements for the move constructor are specified in C++11 + // 20.7.1.2.1.15-17, which has some subtleties around reference deleters. As + // we don't support reference (or move-only) deleters, the post conditions are + // trivially true: we always copy construct the deleter from other's deleter. + scoped_ptr(scoped_ptr&& other) : impl_(&other.impl_) {} + + // Conversion constructor. Allows construction from a scoped_ptr rvalue for a // convertible type and deleter. // - // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this constructor distinct - // from the normal move constructor. By C++11 20.7.1.2.1.21, this constructor - // has different post-conditions if D is a reference type. Since this - // implementation does not support deleters with reference type, - // we do not need a separate move constructor allowing us to avoid one - // use of SFINAE. You only need to care about this if you modify the - // implementation of scoped_ptr. - template - scoped_ptr(scoped_ptr&& other) - : impl_(&other.impl_) { - COMPILE_ASSERT(!base::is_array::value, U_cannot_be_an_array); + // IMPLEMENTATION NOTE: C++ 20.7.1.2.1.19 requires this constructor to only + // participate in overload resolution if all the following are true: + // - U is implicitly convertible to T: this is important for 2 reasons: + // 1. So type traits don't incorrectly return true, e.g. + // std::is_convertible, scoped_ptr>::value + // should be false. + // 2. To make sure code like this compiles: + // void F(scoped_ptr); + // void F(scoped_ptr); + // // Ambiguous since both conversion constructors match. + // F(scoped_ptr()); + // - U is not an array type: to prevent conversions from scoped_ptr to + // scoped_ptr. + // - D is a reference type and E is the same type, or D is not a reference + // type and E is implicitly convertible to D: again, we don't support + // reference deleters, so we only worry about the latter requirement. + template ::value && + std::is_convertible::value && + std::is_convertible::value>::type* = + nullptr> + scoped_ptr(scoped_ptr&& other) + : impl_(&other.impl_) {} + + // operator=. + // + // IMPLEMENTATION NOTE: Unlike the move constructor, Clang does not appear to + // require a move assignment operator to trigger the pessimizing move warning: + // in this case, the warning triggers when moving a temporary. For consistency + // with the move constructor, we define it anyway. C++11 20.7.1.2.3.1-3 + // defines several requirements around this: like the move constructor, the + // requirements are simplified by the fact that we don't support move-only or + // reference deleters. + scoped_ptr& operator=(scoped_ptr&& rhs) { + impl_.TakeState(&rhs.impl_); + return *this; } // operator=. Allows assignment from a scoped_ptr rvalue for a convertible // type and deleter. // // IMPLEMENTATION NOTE: C++11 unique_ptr<> keeps this operator= distinct from - // the normal move assignment operator. By C++11 20.7.1.2.3.4, this templated - // form has different requirements on for move-only Deleters. Since this - // implementation does not support move-only Deleters, we do not need a - // separate move assignment operator allowing us to avoid one use of SFINAE. - // You only need to care about this if you modify the implementation of - // scoped_ptr. - template - scoped_ptr& operator=(scoped_ptr&& rhs) { - COMPILE_ASSERT(!base::is_array::value, U_cannot_be_an_array); + // the normal move assignment operator. C++11 20.7.1.2.3.4-7 contains the + // requirement for this operator, but like the conversion constructor, the + // requirements are greatly simplified by not supporting move-only or + // reference deleters. + template ::value && + std::is_convertible::value && + // Note that this really should be + // std::is_assignable, but + // appears to be missing this on some + // platforms. This is close enough (though + // it's not the same). + std::is_convertible::value>::type* = + nullptr> + scoped_ptr& operator=(scoped_ptr&& rhs) { impl_.TakeState(&rhs.impl_); return *this; } // operator=. Allows assignment from a nullptr. Deletes the currently owned // object, if any. - scoped_ptr& operator=(decltype(nullptr)) { + scoped_ptr& operator=(std::nullptr_t) { reset(); return *this; } @@ -407,12 +381,6 @@ class scoped_ptr { return impl_.get() ? &scoped_ptr::impl_ : nullptr; } - // Comparison operators. - // These return whether two scoped_ptr refer to the same object, not just to - // two different but equal objects. - bool operator==(const element_type* p) const { return impl_.get() == p; } - bool operator!=(const element_type* p) const { return impl_.get() != p; } - // Swap two scoped pointers. void swap(scoped_ptr& p2) { impl_.swap(p2.impl_); @@ -433,23 +401,16 @@ class scoped_ptr { // Forbidden for API compatibility with std::unique_ptr. explicit scoped_ptr(int disallow_construction_from_null); - - // Forbid comparison of scoped_ptr types. If U != T, it totally - // doesn't make sense, and if U == T, it still doesn't make sense - // because you should never have the same object owned by two different - // scoped_ptrs. - template bool operator==(scoped_ptr const& p2) const; - template bool operator!=(scoped_ptr const& p2) const; }; template class scoped_ptr { - MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(scoped_ptr) + DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(scoped_ptr) public: // The element and deleter types. - typedef T element_type; - typedef D deleter_type; + using element_type = T; + using deleter_type = D; // Constructor. Defaults to initializing with nullptr. scoped_ptr() : impl_(nullptr) {} @@ -464,13 +425,11 @@ class scoped_ptr { // (C++98 [expr.delete]p3). If you're doing this, fix your code. // - it cannot be const-qualified differently from T per unique_ptr spec // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting - // to work around this may use implicit_cast(). - // However, because of the first bullet in this comment, users MUST - // NOT use implicit_cast() to upcast the static type of the array. + // to work around this may use const_cast(). explicit scoped_ptr(element_type* array) : impl_(array) {} // Constructor. Allows construction from a nullptr. - scoped_ptr(decltype(nullptr)) : impl_(nullptr) {} + scoped_ptr(std::nullptr_t) : impl_(nullptr) {} // Constructor. Allows construction from a scoped_ptr rvalue. scoped_ptr(scoped_ptr&& other) : impl_(&other.impl_) {} @@ -483,7 +442,7 @@ class scoped_ptr { // operator=. Allows assignment from a nullptr. Deletes the currently owned // array, if any. - scoped_ptr& operator=(decltype(nullptr)) { + scoped_ptr& operator=(std::nullptr_t) { reset(); return *this; } @@ -514,12 +473,6 @@ class scoped_ptr { return impl_.get() ? &scoped_ptr::impl_ : nullptr; } - // Comparison operators. - // These return whether two scoped_ptr refer to the same object, not just to - // two different but equal objects. - bool operator==(element_type* array) const { return impl_.get() == array; } - bool operator!=(element_type* array) const { return impl_.get() != array; } - // Swap two scoped pointers. void swap(scoped_ptr& p2) { impl_.swap(p2.impl_); @@ -552,13 +505,6 @@ class scoped_ptr { // reasons as the constructor above. template void reset(U* array); void reset(int disallow_reset_from_null); - - // Forbid comparison of scoped_ptr types. If U != T, it totally - // doesn't make sense, and if U == T, it still doesn't make sense - // because you should never have the same object owned by two different - // scoped_ptrs. - template bool operator==(scoped_ptr const& p2) const; - template bool operator!=(scoped_ptr const& p2) const; }; // Free functions @@ -567,14 +513,82 @@ void swap(scoped_ptr& p1, scoped_ptr& p2) { p1.swap(p2); } +template +bool operator==(const scoped_ptr& p1, const scoped_ptr& p2) { + return p1.get() == p2.get(); +} template -bool operator==(T* p1, const scoped_ptr& p2) { - return p1 == p2.get(); +bool operator==(const scoped_ptr& p, std::nullptr_t) { + return p.get() == nullptr; +} +template +bool operator==(std::nullptr_t, const scoped_ptr& p) { + return p.get() == nullptr; } +template +bool operator!=(const scoped_ptr& p1, const scoped_ptr& p2) { + return !(p1 == p2); +} template -bool operator!=(T* p1, const scoped_ptr& p2) { - return p1 != p2.get(); +bool operator!=(const scoped_ptr& p, std::nullptr_t) { + return !(p == nullptr); +} +template +bool operator!=(std::nullptr_t, const scoped_ptr& p) { + return !(p == nullptr); +} + +template +bool operator<(const scoped_ptr& p1, const scoped_ptr& p2) { + return p1.get() < p2.get(); +} +template +bool operator<(const scoped_ptr& p, std::nullptr_t) { + return p.get() < nullptr; +} +template +bool operator<(std::nullptr_t, const scoped_ptr& p) { + return nullptr < p.get(); +} + +template +bool operator>(const scoped_ptr& p1, const scoped_ptr& p2) { + return p2 < p1; +} +template +bool operator>(const scoped_ptr& p, std::nullptr_t) { + return nullptr < p; +} +template +bool operator>(std::nullptr_t, const scoped_ptr& p) { + return p < nullptr; +} + +template +bool operator<=(const scoped_ptr& p1, const scoped_ptr& p2) { + return !(p1 > p2); +} +template +bool operator<=(const scoped_ptr& p, std::nullptr_t) { + return !(p > nullptr); +} +template +bool operator<=(std::nullptr_t, const scoped_ptr& p) { + return !(nullptr > p); +} + +template +bool operator>=(const scoped_ptr& p1, const scoped_ptr& p2) { + return !(p1 < p2); +} +template +bool operator>=(const scoped_ptr& p, std::nullptr_t) { + return !(p < nullptr); +} +template +bool operator>=(std::nullptr_t, const scoped_ptr& p) { + return !(nullptr < p); } // A function to convert T* into scoped_ptr @@ -585,4 +599,9 @@ scoped_ptr make_scoped_ptr(T* ptr) { return scoped_ptr(ptr); } +template +std::ostream& operator<<(std::ostream& out, const scoped_ptr& p) { + return out << p.get(); +} + #endif // BASE_MEMORY_SCOPED_PTR_H_ diff --git a/security/sandbox/chromium/base/memory/singleton.h b/security/sandbox/chromium/base/memory/singleton.h index e5e2e3efed08..79e4441a8ed8 100644 --- a/security/sandbox/chromium/base/memory/singleton.h +++ b/security/sandbox/chromium/base/memory/singleton.h @@ -22,8 +22,8 @@ #include "base/at_exit.h" #include "base/atomicops.h" #include "base/base_export.h" +#include "base/macros.h" #include "base/memory/aligned_memory.h" -#include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_restrictions.h" namespace base { @@ -37,10 +37,10 @@ static const subtle::AtomicWord kBeingCreatedMarker = 1; // we can implement the more complicated pieces out of line in the .cc file. BASE_EXPORT subtle::AtomicWord WaitForInstance(subtle::AtomicWord* instance); -} // namespace internal -} // namespace base +class DeleteTraceLogForTesting; + +} // namespace internal -// TODO(joth): Move more of this file into namespace base // Default traits for Singleton. Calls operator new and operator delete on // the object. Registers automatic deletion at process exit. @@ -111,7 +111,7 @@ struct StaticMemorySingletonTraits { // this is traits for returning NULL. static Type* New() { // Only constructs once and returns pointer; otherwise returns NULL. - if (base::subtle::NoBarrier_AtomicExchange(&dead_, 1)) + if (subtle::NoBarrier_AtomicExchange(&dead_, 1)) return NULL; return new(buffer_.void_data()) Type(); @@ -126,20 +126,19 @@ struct StaticMemorySingletonTraits { static const bool kAllowedToAccessOnNonjoinableThread = true; // Exposed for unittesting. - static void Resurrect() { - base::subtle::NoBarrier_Store(&dead_, 0); - } + static void Resurrect() { subtle::NoBarrier_Store(&dead_, 0); } private: - static base::AlignedMemory buffer_; + static AlignedMemory buffer_; // Signal the object was already deleted, so it is not revived. - static base::subtle::Atomic32 dead_; + static subtle::Atomic32 dead_; }; -template base::AlignedMemory +template +AlignedMemory StaticMemorySingletonTraits::buffer_; -template base::subtle::Atomic32 - StaticMemorySingletonTraits::dead_ = 0; +template +subtle::Atomic32 StaticMemorySingletonTraits::dead_ = 0; // The Singleton class manages a single // instance of Type which will be created on first use and will be destroyed at @@ -191,7 +190,7 @@ template base::subtle::Atomic32 // RAE = kRegisterAtExit // // On every platform, if Traits::RAE is true, the singleton will be destroyed at -// process exit. More precisely it uses base::AtExitManager which requires an +// process exit. More precisely it uses AtExitManager which requires an // object of this type to be instantiated. AtExitManager mimics the semantics // of atexit() such as LIFO order but under Windows is safer to call. For more // information see at_exit.h. @@ -210,6 +209,7 @@ template base::subtle::Atomic32 // (b) Your factory function must never throw an exception. This class is not // exception-safe. // + template , typename DifferentiatingType = Type> @@ -220,7 +220,7 @@ class Singleton { friend Type* Type::GetInstance(); // Allow TraceLog tests to test tracing after OnExit. - friend class DeleteTraceLogForTesting; + friend class internal::DeleteTraceLogForTesting; // This class is safe to be constructed and copy-constructed since it has no // member. @@ -230,45 +230,37 @@ class Singleton { #ifndef NDEBUG // Avoid making TLS lookup on release builds. if (!Traits::kAllowedToAccessOnNonjoinableThread) - base::ThreadRestrictions::AssertSingletonAllowed(); + ThreadRestrictions::AssertSingletonAllowed(); #endif // The load has acquire memory ordering as the thread which reads the // instance_ pointer must acquire visibility over the singleton data. - base::subtle::AtomicWord value = base::subtle::Acquire_Load(&instance_); - if (value != 0 && value != base::internal::kBeingCreatedMarker) { - // See the corresponding HAPPENS_BEFORE below. - ANNOTATE_HAPPENS_AFTER(&instance_); + subtle::AtomicWord value = subtle::Acquire_Load(&instance_); + if (value != 0 && value != internal::kBeingCreatedMarker) { return reinterpret_cast(value); } // Object isn't created yet, maybe we will get to create it, let's try... - if (base::subtle::Acquire_CompareAndSwap( - &instance_, 0, base::internal::kBeingCreatedMarker) == 0) { + if (subtle::Acquire_CompareAndSwap(&instance_, 0, + internal::kBeingCreatedMarker) == 0) { // instance_ was NULL and is now kBeingCreatedMarker. Only one thread // will ever get here. Threads might be spinning on us, and they will // stop right after we do this store. Type* newval = Traits::New(); - // This annotation helps race detectors recognize correct lock-less - // synchronization between different threads calling get(). - // See the corresponding HAPPENS_AFTER below and above. - ANNOTATE_HAPPENS_BEFORE(&instance_); // Releases the visibility over instance_ to the readers. - base::subtle::Release_Store( - &instance_, reinterpret_cast(newval)); + subtle::Release_Store(&instance_, + reinterpret_cast(newval)); if (newval != NULL && Traits::kRegisterAtExit) - base::AtExitManager::RegisterCallback(OnExit, NULL); + AtExitManager::RegisterCallback(OnExit, NULL); return newval; } // We hit a race. Wait for the other thread to complete it. - value = base::internal::WaitForInstance(&instance_); + value = internal::WaitForInstance(&instance_); - // See the corresponding HAPPENS_BEFORE above. - ANNOTATE_HAPPENS_AFTER(&instance_); return reinterpret_cast(value); } @@ -278,15 +270,15 @@ class Singleton { static void OnExit(void* /*unused*/) { // AtExit should only ever be register after the singleton instance was // created. We should only ever get here with a valid instance_ pointer. - Traits::Delete( - reinterpret_cast(base::subtle::NoBarrier_Load(&instance_))); + Traits::Delete(reinterpret_cast(subtle::NoBarrier_Load(&instance_))); instance_ = 0; } - static base::subtle::AtomicWord instance_; + static subtle::AtomicWord instance_; }; template -base::subtle::AtomicWord Singleton:: - instance_ = 0; +subtle::AtomicWord Singleton::instance_ = 0; + +} // namespace base #endif // BASE_MEMORY_SINGLETON_H_ diff --git a/security/sandbox/chromium/base/memory/weak_ptr.h b/security/sandbox/chromium/base/memory/weak_ptr.h index 8a433922fdf0..33d1e4736245 100644 --- a/security/sandbox/chromium/base/memory/weak_ptr.h +++ b/security/sandbox/chromium/base/memory/weak_ptr.h @@ -16,6 +16,7 @@ // // class Controller { // public: +// Controller() : weak_factory_(this) {} // void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); } // void WorkComplete(const Result& result) { ... } // private: @@ -58,15 +59,20 @@ // off to other task runners, e.g. to use to post tasks back to object on the // bound sequence. // -// Invalidating the factory's WeakPtrs un-binds it from the sequence, allowing -// it to be passed for a different sequence to use or delete it. +// If all WeakPtr objects are destroyed or invalidated then the factory is +// unbound from the SequencedTaskRunner/Thread. The WeakPtrFactory may then be +// destroyed, or new WeakPtr objects may be used, from a different sequence. +// +// Thus, at least one WeakPtr object must exist and have been dereferenced on +// the correct thread to enforce that other WeakPtr objects will enforce they +// are used on the desired thread. #ifndef BASE_MEMORY_WEAK_PTR_H_ #define BASE_MEMORY_WEAK_PTR_H_ -#include "base/basictypes.h" #include "base/base_export.h" #include "base/logging.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/sequence_checker.h" #include "base/template_util.h" @@ -155,8 +161,8 @@ class SupportsWeakPtrBase { static WeakPtr StaticAsWeakPtr(Derived* t) { typedef is_convertible convertible; - COMPILE_ASSERT(convertible::value, - AsWeakPtr_argument_inherits_from_SupportsWeakPtr); + static_assert(convertible::value, + "AsWeakPtr argument must inherit from SupportsWeakPtr"); return AsWeakPtrImpl(t, *t); } diff --git a/security/sandbox/chromium/base/move.h b/security/sandbox/chromium/base/move.h index 06f3f323723f..0af416e00e17 100644 --- a/security/sandbox/chromium/base/move.h +++ b/security/sandbox/chromium/base/move.h @@ -2,228 +2,56 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/compiler_specific.h" - #ifndef BASE_MOVE_H_ #define BASE_MOVE_H_ -// Macro with the boilerplate that makes a type move-only in C++03. +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "build/build_config.h" + +// TODO(crbug.com/566182): DEPRECATED! +// Use DISALLOW_COPY_AND_ASSIGN instead, or if your type will be used in +// Callbacks, use DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND instead. +#define MOVE_ONLY_TYPE_FOR_CPP_03(type) \ + DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(type) + +// A macro to disallow the copy constructor and copy assignment functions. +// This should be used in the private: declarations for a class. // -// USAGE +// Use this macro instead of DISALLOW_COPY_AND_ASSIGN if you want to pass +// ownership of the type through a base::Callback without heap-allocating it +// into a scoped_ptr. The class must define a move constructor and move +// assignment operator to make this work. // -// This macro should be used instead of DISALLOW_COPY_AND_ASSIGN to create -// a "move-only" type. Unlike DISALLOW_COPY_AND_ASSIGN, this macro should be -// the first line in a class declaration. -// -// A class using this macro must call .Pass() (or somehow be an r-value already) -// before it can be: -// -// * Passed as a function argument -// * Used as the right-hand side of an assignment -// * Returned from a function -// -// Each class will still need to define their own "move constructor" and "move -// operator=" to make this useful. Here's an example of the macro, the move -// constructor, and the move operator= from the scoped_ptr class: -// -// template -// class scoped_ptr { -// MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue) -// public: -// scoped_ptr(RValue& other) : ptr_(other.release()) { } -// scoped_ptr& operator=(RValue& other) { -// swap(other); -// return *this; -// } -// }; -// -// Note that the constructor must NOT be marked explicit. -// -// For consistency, the second parameter to the macro should always be RValue -// unless you have a strong reason to do otherwise. It is only exposed as a -// macro parameter so that the move constructor and move operator= don't look -// like they're using a phantom type. -// -// -// HOW THIS WORKS -// -// For a thorough explanation of this technique, see: -// -// http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor -// -// The summary is that we take advantage of 2 properties: -// -// 1) non-const references will not bind to r-values. -// 2) C++ can apply one user-defined conversion when initializing a -// variable. -// -// The first lets us disable the copy constructor and assignment operator -// by declaring private version of them with a non-const reference parameter. -// -// For l-values, direct initialization still fails like in -// DISALLOW_COPY_AND_ASSIGN because the copy constructor and assignment -// operators are private. -// -// For r-values, the situation is different. The copy constructor and -// assignment operator are not viable due to (1), so we are trying to call -// a non-existent constructor and non-existing operator= rather than a private -// one. Since we have not committed an error quite yet, we can provide an -// alternate conversion sequence and a constructor. We add -// -// * a private struct named "RValue" -// * a user-defined conversion "operator RValue()" -// * a "move constructor" and "move operator=" that take the RValue& as -// their sole parameter. -// -// Only r-values will trigger this sequence and execute our "move constructor" -// or "move operator=." L-values will match the private copy constructor and -// operator= first giving a "private in this context" error. This combination -// gives us a move-only type. -// -// For signaling a destructive transfer of data from an l-value, we provide a -// method named Pass() which creates an r-value for the current instance -// triggering the move constructor or move operator=. -// -// Other ways to get r-values is to use the result of an expression like a -// function call. -// -// Here's an example with comments explaining what gets triggered where: -// -// class Foo { -// MOVE_ONLY_TYPE_FOR_CPP_03(Foo, RValue); -// -// public: -// ... API ... -// Foo(RValue other); // Move constructor. -// Foo& operator=(RValue rhs); // Move operator= -// }; -// -// Foo MakeFoo(); // Function that returns a Foo. -// -// Foo f; -// Foo f_copy(f); // ERROR: Foo(Foo&) is private in this context. -// Foo f_assign; -// f_assign = f; // ERROR: operator=(Foo&) is private in this context. -// -// -// Foo f(MakeFoo()); // R-value so alternate conversion executed. -// Foo f_copy(f.Pass()); // R-value so alternate conversion executed. -// f = f_copy.Pass(); // R-value so alternate conversion executed. -// -// -// IMPLEMENTATION SUBTLETIES WITH RValue -// -// The RValue struct is just a container for a pointer back to the original -// object. It should only ever be created as a temporary, and no external -// class should ever declare it or use it in a parameter. -// -// It is tempting to want to use the RValue type in function parameters, but -// excluding the limited usage here for the move constructor and move -// operator=, doing so would mean that the function could take both r-values -// and l-values equially which is unexpected. See COMPARED To Boost.Move for -// more details. -// -// An alternate, and incorrect, implementation of the RValue class used by -// Boost.Move makes RValue a fieldless child of the move-only type. RValue& -// is then used in place of RValue in the various operators. The RValue& is -// "created" by doing *reinterpret_cast(this). This has the appeal -// of never creating a temporary RValue struct even with optimizations -// disabled. Also, by virtue of inheritance you can treat the RValue -// reference as if it were the move-only type itself. Unfortunately, -// using the result of this reinterpret_cast<> is actually undefined behavior -// due to C++98 5.2.10.7. In certain compilers (e.g., NaCl) the optimizer -// will generate non-working code. -// -// In optimized builds, both implementations generate the same assembly so we -// choose the one that adheres to the standard. -// -// -// WHY HAVE typedef void MoveOnlyTypeForCPP03 -// -// Callback<>/Bind() needs to understand movable-but-not-copyable semantics -// to call .Pass() appropriately when it is expected to transfer the value. -// The cryptic typedef MoveOnlyTypeForCPP03 is added to make this check -// easy and automatic in helper templates for Callback<>/Bind(). +// This version of the macro adds a Pass() function and a cryptic +// MoveOnlyTypeForCPP03 typedef for the base::Callback implementation to use. // See IsMoveOnlyType template and its usage in base/callback_internal.h // for more details. -// -// -// COMPARED TO C++11 -// -// In C++11, you would implement this functionality using an r-value reference -// and our .Pass() method would be replaced with a call to std::move(). -// -// This emulation also has a deficiency where it uses up the single -// user-defined conversion allowed by C++ during initialization. This can -// cause problems in some API edge cases. For instance, in scoped_ptr, it is -// impossible to make a function "void Foo(scoped_ptr p)" accept a -// value of type scoped_ptr even if you add a constructor to -// scoped_ptr<> that would make it look like it should work. C++11 does not -// have this deficiency. -// -// -// COMPARED TO Boost.Move -// -// Our implementation similar to Boost.Move, but we keep the RValue struct -// private to the move-only type, and we don't use the reinterpret_cast<> hack. -// -// In Boost.Move, RValue is the boost::rv<> template. This type can be used -// when writing APIs like: -// -// void MyFunc(boost::rv& f) -// -// that can take advantage of rv<> to avoid extra copies of a type. However you -// would still be able to call this version of MyFunc with an l-value: -// -// Foo f; -// MyFunc(f); // Uh oh, we probably just destroyed |f| w/o calling Pass(). -// -// unless someone is very careful to also declare a parallel override like: -// -// void MyFunc(const Foo& f) -// -// that would catch the l-values first. This was declared unsafe in C++11 and -// a C++11 compiler will explicitly fail MyFunc(f). Unfortunately, we cannot -// ensure this in C++03. -// -// Since we have no need for writing such APIs yet, our implementation keeps -// RValue private and uses a .Pass() method to do the conversion instead of -// trying to write a version of "std::move()." Writing an API like std::move() -// would require the RValue struct to be public. -// -// -// CAVEATS -// -// If you include a move-only type as a field inside a class that does not -// explicitly declare a copy constructor, the containing class's implicit -// copy constructor will change from Containing(const Containing&) to -// Containing(Containing&). This can cause some unexpected errors. -// -// http://llvm.org/bugs/show_bug.cgi?id=11528 -// -// The workaround is to explicitly declare your copy constructor. -// -#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \ - private: \ - struct rvalue_type { \ - explicit rvalue_type(type* object) : object(object) {} \ - type* object; \ - }; \ - type(type&); \ - void operator=(type&); \ - public: \ - operator rvalue_type() { return rvalue_type(this); } \ - type Pass() WARN_UNUSED_RESULT { return type(rvalue_type(this)); } \ - typedef void MoveOnlyTypeForCPP03; \ +// TODO(crbug.com/566182): Remove this macro and use DISALLOW_COPY_AND_ASSIGN +// everywhere instead. +#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_MACOSX) +#define DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(type) \ + private: \ + type(const type&) = delete; \ + void operator=(const type&) = delete; \ + \ + public: \ + typedef void MoveOnlyTypeForCPP03; \ + \ private: - -#define MOVE_ONLY_TYPE_WITH_MOVE_CONSTRUCTOR_FOR_CPP_03(type) \ - private: \ - type(type&); \ - void operator=(type&); \ - public: \ - type&& Pass() WARN_UNUSED_RESULT { return static_cast(*this); } \ - typedef void MoveOnlyTypeForCPP03; \ +#else +#define DISALLOW_COPY_AND_ASSIGN_WITH_MOVE_FOR_BIND(type) \ + private: \ + type(const type&) = delete; \ + void operator=(const type&) = delete; \ + \ + public: \ + type&& Pass() WARN_UNUSED_RESULT { return std::move(*this); } \ + typedef void MoveOnlyTypeForCPP03; \ + \ private: +#endif #endif // BASE_MOVE_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_conversions.h b/security/sandbox/chromium/base/numerics/safe_conversions.h index fe85fc6f52d3..baac188fd2a7 100644 --- a/security/sandbox/chromium/base/numerics/safe_conversions.h +++ b/security/sandbox/chromium/base/numerics/safe_conversions.h @@ -2,10 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SAFE_CONVERSIONS_H_ -#define BASE_SAFE_CONVERSIONS_H_ +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_H_ + +#include #include +#include #include "base/logging.h" #include "base/numerics/safe_conversions_impl.h" @@ -20,6 +23,24 @@ inline bool IsValueInRangeForNumericType(Src value) { internal::RANGE_VALID; } +// Convenience function for determining if a numeric value is negative without +// throwing compiler warnings on: unsigned(value) < 0. +template +typename std::enable_if::is_signed, bool>::type +IsValueNegative(T value) { + static_assert(std::numeric_limits::is_specialized, + "Argument must be numeric."); + return value < 0; +} + +template +typename std::enable_if::is_signed, bool>::type + IsValueNegative(T) { + static_assert(std::numeric_limits::is_specialized, + "Argument must be numeric."); + return false; +} + // checked_cast<> is analogous to static_cast<> for numeric types, // except that it CHECKs that the specified numeric conversion will not // overflow or underflow. NaN source will always trigger a CHECK. @@ -29,10 +50,30 @@ inline Dst checked_cast(Src value) { return static_cast(value); } +// HandleNaN will cause this class to CHECK(false). +struct SaturatedCastNaNBehaviorCheck { + template + static T HandleNaN() { + CHECK(false); + return T(); + } +}; + +// HandleNaN will return 0 in this case. +struct SaturatedCastNaNBehaviorReturnZero { + template + static T HandleNaN() { + return T(); + } +}; + // saturated_cast<> is analogous to static_cast<> for numeric types, except // that the specified numeric conversion will saturate rather than overflow or -// underflow. NaN assignment to an integral will trigger a CHECK condition. -template +// underflow. NaN assignment to an integral will defer the behavior to a +// specified class. By default, it will return 0. +template inline Dst saturated_cast(Src value) { // Optimization for floating point values, which already saturate. if (std::numeric_limits::is_iec559) @@ -50,15 +91,75 @@ inline Dst saturated_cast(Src value) { // Should fail only on attempting to assign NaN to a saturated integer. case internal::RANGE_INVALID: - CHECK(false); - return std::numeric_limits::max(); + return NaNHandler::template HandleNaN(); } NOTREACHED(); return static_cast(value); } +// strict_cast<> is analogous to static_cast<> for numeric types, except that +// it will cause a compile failure if the destination type is not large enough +// to contain any value in the source type. It performs no runtime checking. +template +inline Dst strict_cast(Src value) { + static_assert(std::numeric_limits::is_specialized, + "Argument must be numeric."); + static_assert(std::numeric_limits::is_specialized, + "Result must be numeric."); + static_assert((internal::StaticDstRangeRelationToSrcRange::value == + internal::NUMERIC_RANGE_CONTAINED), + "The numeric conversion is out of range for this type. You " + "should probably use one of the following conversion " + "mechanisms on the value you want to pass:\n" + "- base::checked_cast\n" + "- base::saturated_cast\n" + "- base::CheckedNumeric"); + + return static_cast(value); +} + +// StrictNumeric implements compile time range checking between numeric types by +// wrapping assignment operations in a strict_cast. This class is intended to be +// used for function arguments and return types, to ensure the destination type +// can always contain the source type. This is essentially the same as enforcing +// -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied +// incrementally at API boundaries, making it easier to convert code so that it +// compiles cleanly with truncation warnings enabled. +// This template should introduce no runtime overhead, but it also provides no +// runtime checking of any of the associated mathematical operations. Use +// CheckedNumeric for runtime range checks of tha actual value being assigned. +template +class StrictNumeric { + public: + typedef T type; + + StrictNumeric() : value_(0) {} + + // Copy constructor. + template + StrictNumeric(const StrictNumeric& rhs) + : value_(strict_cast(rhs.value_)) {} + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to StrictNumerics to make them easier to use. + template + StrictNumeric(Src value) + : value_(strict_cast(value)) {} + + // The numeric cast operator basically handles all the magic. + template + operator Dst() const { + return strict_cast(value_); + } + + private: + T value_; +}; + +// Explicitly make a shorter size_t typedef for convenience. +typedef StrictNumeric SizeT; + } // namespace base -#endif // BASE_SAFE_CONVERSIONS_H_ - +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_conversions_impl.h b/security/sandbox/chromium/base/numerics/safe_conversions_impl.h index c26757a4b3c0..02e68e25de9d 100644 --- a/security/sandbox/chromium/base/numerics/safe_conversions_impl.h +++ b/security/sandbox/chromium/base/numerics/safe_conversions_impl.h @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SAFE_CONVERSIONS_IMPL_H_ -#define BASE_SAFE_CONVERSIONS_IMPL_H_ +#ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ +#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ + +#include +#include #include @@ -108,6 +111,55 @@ inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound, (is_in_lower_bound ? 0 : RANGE_UNDERFLOW)); } +// The following helper template addresses a corner case in range checks for +// conversion from a floating-point type to an integral type of smaller range +// but larger precision (e.g. float -> unsigned). The problem is as follows: +// 1. Integral maximum is always one less than a power of two, so it must be +// truncated to fit the mantissa of the floating point. The direction of +// rounding is implementation defined, but by default it's always IEEE +// floats, which round to nearest and thus result in a value of larger +// magnitude than the integral value. +// Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX +// // is 4294967295u. +// 2. If the floating point value is equal to the promoted integral maximum +// value, a range check will erroneously pass. +// Example: (4294967296f <= 4294967295u) // This is true due to a precision +// // loss in rounding up to float. +// 3. When the floating point value is then converted to an integral, the +// resulting value is out of range for the target integral type and +// thus is implementation defined. +// Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. +// To fix this bug we manually truncate the maximum value when the destination +// type is an integral of larger precision than the source floating-point type, +// such that the resulting maximum is represented exactly as a floating point. +template +struct NarrowingRange { + typedef typename std::numeric_limits SrcLimits; + typedef typename std::numeric_limits DstLimits; + + static Dst max() { + // The following logic avoids warnings where the max function is + // instantiated with invalid values for a bit shift (even though + // such a function can never be called). + static const int shift = + (MaxExponent::value > MaxExponent::value && + SrcLimits::digits < DstLimits::digits && SrcLimits::is_iec559 && + DstLimits::is_integer) + ? (DstLimits::digits - SrcLimits::digits) + : 0; + + // We use UINTMAX_C below to avoid compiler warnings about shifting floating + // points. Since it's a compile time calculation, it shouldn't have any + // performance impact. + return DstLimits::max() - static_cast((UINTMAX_C(1) << shift) - 1); + } + + static Dst min() { + return std::numeric_limits::is_iec559 ? -DstLimits::max() + : DstLimits::min(); + } +}; + template < typename Dst, typename Src, @@ -147,11 +199,8 @@ struct DstRangeRelationToSrcRangeImpl { static RangeConstraint Check(Src value) { - return std::numeric_limits::is_iec559 - ? GetRangeConstraint(value <= std::numeric_limits::max(), - value >= -std::numeric_limits::max()) - : GetRangeConstraint(value <= std::numeric_limits::max(), - value >= std::numeric_limits::min()); + return GetRangeConstraint((value <= NarrowingRange::max()), + (value >= NarrowingRange::min())); } }; @@ -163,7 +212,7 @@ struct DstRangeRelationToSrcRangeImpl { static RangeConstraint Check(Src value) { - return GetRangeConstraint(value <= std::numeric_limits::max(), true); + return GetRangeConstraint(value <= NarrowingRange::max(), true); } }; @@ -178,7 +227,7 @@ struct DstRangeRelationToSrcRangeImpl sizeof(Src) ? RANGE_VALID : GetRangeConstraint( - value <= static_cast(std::numeric_limits::max()), + value <= static_cast(NarrowingRange::max()), true); } }; @@ -195,7 +244,7 @@ struct DstRangeRelationToSrcRangeImpl::value >= MaxExponent::value) ? GetRangeConstraint(true, value >= static_cast(0)) : GetRangeConstraint( - value <= static_cast(std::numeric_limits::max()), + value <= static_cast(NarrowingRange::max()), value >= static_cast(0)); } }; @@ -212,5 +261,4 @@ inline RangeConstraint DstRangeRelationToSrcRange(Src value) { } // namespace internal } // namespace base -#endif // BASE_SAFE_CONVERSIONS_IMPL_H_ - +#endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_math.h b/security/sandbox/chromium/base/numerics/safe_math.h new file mode 100644 index 000000000000..d169690a8278 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_math.h @@ -0,0 +1,299 @@ +// 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 BASE_NUMERICS_SAFE_MATH_H_ +#define BASE_NUMERICS_SAFE_MATH_H_ + +#include + +#include "base/numerics/safe_math_impl.h" + +namespace base { + +namespace internal { + +// CheckedNumeric implements all the logic and operators for detecting integer +// boundary conditions such as overflow, underflow, and invalid conversions. +// The CheckedNumeric type implicitly converts from floating point and integer +// data types, and contains overloads for basic arithmetic operations (i.e.: +, +// -, *, /, %). +// +// The following methods convert from CheckedNumeric to standard numeric values: +// IsValid() - Returns true if the underlying numeric value is valid (i.e. has +// has not wrapped and is not the result of an invalid conversion). +// ValueOrDie() - Returns the underlying value. If the state is not valid this +// call will crash on a CHECK. +// ValueOrDefault() - Returns the current value, or the supplied default if the +// state is not valid. +// ValueFloating() - Returns the underlying floating point value (valid only +// only for floating point CheckedNumeric types). +// +// Bitwise operations are explicitly not supported, because correct +// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison +// operations are explicitly not supported because they could result in a crash +// on a CHECK condition. You should use patterns like the following for these +// operations: +// Bitwise operation: +// CheckedNumeric checked_int = untrusted_input_value; +// int x = checked_int.ValueOrDefault(0) | kFlagValues; +// Comparison: +// CheckedNumeric checked_size = untrusted_input_value; +// checked_size += HEADER LENGTH; +// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size) +// Do stuff... +template +class CheckedNumeric { + public: + typedef T type; + + CheckedNumeric() {} + + // Copy constructor. + template + CheckedNumeric(const CheckedNumeric& rhs) + : state_(rhs.ValueUnsafe(), rhs.validity()) {} + + template + CheckedNumeric(Src value, RangeConstraint validity) + : state_(value, validity) {} + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template + CheckedNumeric(Src value) + : state_(value) { + static_assert(std::numeric_limits::is_specialized, + "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template + CheckedNumeric(StrictNumeric value) + : state_(static_cast(value)) { + } + + // IsValid() is the public API to test if a CheckedNumeric is currently valid. + bool IsValid() const { return validity() == RANGE_VALID; } + + // ValueOrDie() The primary accessor for the underlying value. If the current + // state is not valid it will CHECK and crash. + T ValueOrDie() const { + CHECK(IsValid()); + return state_.value(); + } + + // ValueOrDefault(T default_value) A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + T ValueOrDefault(T default_value) const { + return IsValid() ? state_.value() : default_value; + } + + // ValueFloating() - Since floating point values include their validity state, + // we provide an easy method for extracting them directly, without a risk of + // crashing on a CHECK. + T ValueFloating() const { + static_assert(std::numeric_limits::is_iec559, "Argument must be float."); + return CheckedNumeric::cast(*this).ValueUnsafe(); + } + + // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for + // tests and to avoid a big matrix of friend operator overloads. But the + // values it returns are likely to change in the future. + // Returns: current validity state (i.e. valid, overflow, underflow, nan). + // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for + // saturation/wrapping so we can expose this state consistently and implement + // saturated arithmetic. + RangeConstraint validity() const { return state_.validity(); } + + // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now + // for tests and to avoid a big matrix of friend operator overloads. But the + // values it returns are likely to change in the future. + // Returns: the raw numeric value, regardless of the current state. + // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for + // saturation/wrapping so we can expose this state consistently and implement + // saturated arithmetic. + T ValueUnsafe() const { return state_.value(); } + + // Prototypes for the supported arithmetic operator overloads. + template CheckedNumeric& operator+=(Src rhs); + template CheckedNumeric& operator-=(Src rhs); + template CheckedNumeric& operator*=(Src rhs); + template CheckedNumeric& operator/=(Src rhs); + template CheckedNumeric& operator%=(Src rhs); + + CheckedNumeric operator-() const { + RangeConstraint validity; + T value = CheckedNeg(state_.value(), &validity); + // Negation is always valid for floating point. + if (std::numeric_limits::is_iec559) + return CheckedNumeric(value); + + validity = GetRangeConstraint(state_.validity() | validity); + return CheckedNumeric(value, validity); + } + + CheckedNumeric Abs() const { + RangeConstraint validity; + T value = CheckedAbs(state_.value(), &validity); + // Absolute value is always valid for floating point. + if (std::numeric_limits::is_iec559) + return CheckedNumeric(value); + + validity = GetRangeConstraint(state_.validity() | validity); + return CheckedNumeric(value, validity); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + CheckedNumeric::type> UnsignedAbs() const { + return CheckedNumeric::type>( + CheckedUnsignedAbs(state_.value()), state_.validity()); + } + + CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + CheckedNumeric operator--(int) { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These static methods behave like a convenience cast operator targeting + // the desired CheckedNumeric type. As an optimization, a reference is + // returned when Src is the same type as T. + template + static CheckedNumeric cast( + Src u, + typename std::enable_if::is_specialized, + int>::type = 0) { + return u; + } + + template + static CheckedNumeric cast( + const CheckedNumeric& u, + typename std::enable_if::value, int>::type = 0) { + return u; + } + + static const CheckedNumeric& cast(const CheckedNumeric& u) { return u; } + + private: + template + struct UnderlyingType { + using type = NumericType; + }; + + template + struct UnderlyingType> { + using type = NumericType; + }; + + CheckedNumericState state_; +}; + +// This is the boilerplate for the standard arithmetic operator overloads. A +// macro isn't the prettiest solution, but it beats rewriting these five times. +// Some details worth noting are: +// * We apply the standard arithmetic promotions. +// * We skip range checks for floating points. +// * We skip range checks for destination integers with sufficient range. +// TODO(jschuh): extract these out into templates. +#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \ + /* Binary arithmetic operator for CheckedNumerics of the same type. */ \ + template \ + CheckedNumeric::type> operator OP( \ + const CheckedNumeric& lhs, const CheckedNumeric& rhs) { \ + typedef typename ArithmeticPromotion::type Promotion; \ + /* Floating point always takes the fast path */ \ + if (std::numeric_limits::is_iec559) \ + return CheckedNumeric(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \ + if (IsIntegerArithmeticSafe::value) \ + return CheckedNumeric( \ + lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ + GetRangeConstraint(rhs.validity() | lhs.validity())); \ + RangeConstraint validity = RANGE_VALID; \ + T result = static_cast(Checked##NAME( \ + static_cast(lhs.ValueUnsafe()), \ + static_cast(rhs.ValueUnsafe()), \ + &validity)); \ + return CheckedNumeric( \ + result, \ + GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \ + } \ + /* Assignment arithmetic operator implementation from CheckedNumeric. */ \ + template \ + template \ + CheckedNumeric& CheckedNumeric::operator COMPOUND_OP(Src rhs) { \ + *this = CheckedNumeric::cast(*this) \ + OP CheckedNumeric::type>::cast(rhs); \ + return *this; \ + } \ + /* Binary arithmetic operator for CheckedNumeric of different type. */ \ + template \ + CheckedNumeric::type> operator OP( \ + const CheckedNumeric& lhs, const CheckedNumeric& rhs) { \ + typedef typename ArithmeticPromotion::type Promotion; \ + if (IsIntegerArithmeticSafe::value) \ + return CheckedNumeric( \ + lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \ + GetRangeConstraint(rhs.validity() | lhs.validity())); \ + return CheckedNumeric::cast(lhs) \ + OP CheckedNumeric::cast(rhs); \ + } \ + /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \ + template \ + CheckedNumeric::type> operator OP( \ + const CheckedNumeric& lhs, Src rhs) { \ + typedef typename ArithmeticPromotion::type Promotion; \ + if (IsIntegerArithmeticSafe::value) \ + return CheckedNumeric(lhs.ValueUnsafe() OP rhs, \ + lhs.validity()); \ + return CheckedNumeric::cast(lhs) \ + OP CheckedNumeric::cast(rhs); \ + } \ + /* Binary arithmetic operator for right numeric and left CheckedNumeric. */ \ + template \ + CheckedNumeric::type> operator OP( \ + Src lhs, const CheckedNumeric& rhs) { \ + typedef typename ArithmeticPromotion::type Promotion; \ + if (IsIntegerArithmeticSafe::value) \ + return CheckedNumeric(lhs OP rhs.ValueUnsafe(), \ + rhs.validity()); \ + return CheckedNumeric::cast(lhs) \ + OP CheckedNumeric::cast(rhs); \ + } + +BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= ) +BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= ) + +#undef BASE_NUMERIC_ARITHMETIC_OPERATORS + +} // namespace internal + +using internal::CheckedNumeric; + +} // namespace base + +#endif // BASE_NUMERICS_SAFE_MATH_H_ diff --git a/security/sandbox/chromium/base/numerics/safe_math_impl.h b/security/sandbox/chromium/base/numerics/safe_math_impl.h new file mode 100644 index 000000000000..4fbcc045b840 --- /dev/null +++ b/security/sandbox/chromium/base/numerics/safe_math_impl.h @@ -0,0 +1,545 @@ +// 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 BASE_NUMERICS_SAFE_MATH_IMPL_H_ +#define BASE_NUMERICS_SAFE_MATH_IMPL_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/numerics/safe_conversions.h" +#include "base/template_util.h" + +namespace base { +namespace internal { + +// Everything from here up to the floating point operations is portable C++, +// but it may not be fast. This code could be split based on +// platform/architecture and replaced with potentially faster implementations. + +// Integer promotion templates used by the portable checked integer arithmetic. +template +struct IntegerForSizeAndSign; +template <> +struct IntegerForSizeAndSign<1, true> { + typedef int8_t type; +}; +template <> +struct IntegerForSizeAndSign<1, false> { + typedef uint8_t type; +}; +template <> +struct IntegerForSizeAndSign<2, true> { + typedef int16_t type; +}; +template <> +struct IntegerForSizeAndSign<2, false> { + typedef uint16_t type; +}; +template <> +struct IntegerForSizeAndSign<4, true> { + typedef int32_t type; +}; +template <> +struct IntegerForSizeAndSign<4, false> { + typedef uint32_t type; +}; +template <> +struct IntegerForSizeAndSign<8, true> { + typedef int64_t type; +}; +template <> +struct IntegerForSizeAndSign<8, false> { + typedef uint64_t type; +}; + +// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to +// support 128-bit math, then the ArithmeticPromotion template below will need +// to be updated (or more likely replaced with a decltype expression). + +template +struct UnsignedIntegerForSize { + typedef typename std::enable_if< + std::numeric_limits::is_integer, + typename IntegerForSizeAndSign::type>::type type; +}; + +template +struct SignedIntegerForSize { + typedef typename std::enable_if< + std::numeric_limits::is_integer, + typename IntegerForSizeAndSign::type>::type type; +}; + +template +struct TwiceWiderInteger { + typedef typename std::enable_if< + std::numeric_limits::is_integer, + typename IntegerForSizeAndSign< + sizeof(Integer) * 2, + std::numeric_limits::is_signed>::type>::type type; +}; + +template +struct PositionOfSignBit { + static const typename std::enable_if::is_integer, + size_t>::type value = + 8 * sizeof(Integer) - 1; +}; + +// This is used for UnsignedAbs, where we need to support floating-point +// template instantiations even though we don't actually support the operations. +// However, there is no corresponding implementation of e.g. CheckedUnsignedAbs, +// so the float versions will not compile. +template ::is_integer, + bool IsFloat = std::numeric_limits::is_iec559> +struct UnsignedOrFloatForSize; + +template +struct UnsignedOrFloatForSize { + typedef typename UnsignedIntegerForSize::type type; +}; + +template +struct UnsignedOrFloatForSize { + typedef Numeric type; +}; + +// Helper templates for integer manipulations. + +template +bool HasSignBit(T x) { + // Cast to unsigned since right shift on signed is undefined. + return !!(static_cast::type>(x) >> + PositionOfSignBit::value); +} + +// This wrapper undoes the standard integer promotions. +template +T BinaryComplement(T x) { + return ~x; +} + +// Here are the actual portable checked integer math implementations. +// TODO(jschuh): Break this code out from the enable_if pattern and find a clean +// way to coalesce things into the CheckedNumericState specializations below. + +template +typename std::enable_if::is_integer, T>::type +CheckedAdd(T x, T y, RangeConstraint* validity) { + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + typedef typename UnsignedIntegerForSize::type UnsignedDst; + UnsignedDst ux = static_cast(x); + UnsignedDst uy = static_cast(y); + UnsignedDst uresult = ux + uy; + // Addition is valid if the sign of (x + y) is equal to either that of x or + // that of y. + if (std::numeric_limits::is_signed) { + if (HasSignBit(BinaryComplement((uresult ^ ux) & (uresult ^ uy)))) + *validity = RANGE_VALID; + else // Direction of wrap is inverse of result sign. + *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW; + + } else { // Unsigned is either valid or overflow. + *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW; + } + return static_cast(uresult); +} + +template +typename std::enable_if::is_integer, T>::type +CheckedSub(T x, T y, RangeConstraint* validity) { + // Since the value of x+y is undefined if we have a signed type, we compute + // it using the unsigned type of the same size. + typedef typename UnsignedIntegerForSize::type UnsignedDst; + UnsignedDst ux = static_cast(x); + UnsignedDst uy = static_cast(y); + UnsignedDst uresult = ux - uy; + // Subtraction is valid if either x and y have same sign, or (x-y) and x have + // the same sign. + if (std::numeric_limits::is_signed) { + if (HasSignBit(BinaryComplement((uresult ^ ux) & (ux ^ uy)))) + *validity = RANGE_VALID; + else // Direction of wrap is inverse of result sign. + *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW; + + } else { // Unsigned is either valid or underflow. + *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW; + } + return static_cast(uresult); +} + +// Integer multiplication is a bit complicated. In the fast case we just +// we just promote to a twice wider type, and range check the result. In the +// slow case we need to manually check that the result won't be truncated by +// checking with division against the appropriate bound. +template +typename std::enable_if::is_integer && + sizeof(T) * 2 <= sizeof(uintmax_t), + T>::type +CheckedMul(T x, T y, RangeConstraint* validity) { + typedef typename TwiceWiderInteger::type IntermediateType; + IntermediateType tmp = + static_cast(x) * static_cast(y); + *validity = DstRangeRelationToSrcRange(tmp); + return static_cast(tmp); +} + +template +typename std::enable_if::is_integer && + std::numeric_limits::is_signed && + (sizeof(T) * 2 > sizeof(uintmax_t)), + T>::type +CheckedMul(T x, T y, RangeConstraint* validity) { + // If either side is zero then the result will be zero. + if (!x || !y) { + return RANGE_VALID; + + } else if (x > 0) { + if (y > 0) + *validity = + x <= std::numeric_limits::max() / y ? RANGE_VALID : RANGE_OVERFLOW; + else + *validity = y >= std::numeric_limits::min() / x ? RANGE_VALID + : RANGE_UNDERFLOW; + + } else { + if (y > 0) + *validity = x >= std::numeric_limits::min() / y ? RANGE_VALID + : RANGE_UNDERFLOW; + else + *validity = + y >= std::numeric_limits::max() / x ? RANGE_VALID : RANGE_OVERFLOW; + } + + return x * y; +} + +template +typename std::enable_if::is_integer && + !std::numeric_limits::is_signed && + (sizeof(T) * 2 > sizeof(uintmax_t)), + T>::type +CheckedMul(T x, T y, RangeConstraint* validity) { + *validity = (y == 0 || x <= std::numeric_limits::max() / y) + ? RANGE_VALID + : RANGE_OVERFLOW; + return x * y; +} + +// Division just requires a check for an invalid negation on signed min/-1. +template +T CheckedDiv(T x, + T y, + RangeConstraint* validity, + typename std::enable_if::is_integer, + int>::type = 0) { + if (std::numeric_limits::is_signed && x == std::numeric_limits::min() && + y == static_cast(-1)) { + *validity = RANGE_OVERFLOW; + return std::numeric_limits::min(); + } + + *validity = RANGE_VALID; + return x / y; +} + +template +typename std::enable_if::is_integer && + std::numeric_limits::is_signed, + T>::type +CheckedMod(T x, T y, RangeConstraint* validity) { + *validity = y > 0 ? RANGE_VALID : RANGE_INVALID; + return x % y; +} + +template +typename std::enable_if::is_integer && + !std::numeric_limits::is_signed, + T>::type +CheckedMod(T x, T y, RangeConstraint* validity) { + *validity = RANGE_VALID; + return x % y; +} + +template +typename std::enable_if::is_integer && + std::numeric_limits::is_signed, + T>::type +CheckedNeg(T value, RangeConstraint* validity) { + *validity = + value != std::numeric_limits::min() ? RANGE_VALID : RANGE_OVERFLOW; + // The negation of signed min is min, so catch that one. + return -value; +} + +template +typename std::enable_if::is_integer && + !std::numeric_limits::is_signed, + T>::type +CheckedNeg(T value, RangeConstraint* validity) { + // The only legal unsigned negation is zero. + *validity = value ? RANGE_UNDERFLOW : RANGE_VALID; + return static_cast( + -static_cast::type>(value)); +} + +template +typename std::enable_if::is_integer && + std::numeric_limits::is_signed, + T>::type +CheckedAbs(T value, RangeConstraint* validity) { + *validity = + value != std::numeric_limits::min() ? RANGE_VALID : RANGE_OVERFLOW; + return static_cast(std::abs(value)); +} + +template +typename std::enable_if::is_integer && + !std::numeric_limits::is_signed, + T>::type +CheckedAbs(T value, RangeConstraint* validity) { + // T is unsigned, so |value| must already be positive. + *validity = RANGE_VALID; + return value; +} + +template +typename std::enable_if::is_integer && + std::numeric_limits::is_signed, + typename UnsignedIntegerForSize::type>::type +CheckedUnsignedAbs(T value) { + typedef typename UnsignedIntegerForSize::type UnsignedT; + return value == std::numeric_limits::min() + ? static_cast(std::numeric_limits::max()) + 1 + : static_cast(std::abs(value)); +} + +template +typename std::enable_if::is_integer && + !std::numeric_limits::is_signed, + T>::type +CheckedUnsignedAbs(T value) { + // T is unsigned, so |value| must already be positive. + return value; +} + +// These are the floating point stubs that the compiler needs to see. Only the +// negation operation is ever called. +#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \ + template \ + typename std::enable_if::is_iec559, T>::type \ + Checked##NAME(T, T, RangeConstraint*) { \ + NOTREACHED(); \ + return 0; \ + } + +BASE_FLOAT_ARITHMETIC_STUBS(Add) +BASE_FLOAT_ARITHMETIC_STUBS(Sub) +BASE_FLOAT_ARITHMETIC_STUBS(Mul) +BASE_FLOAT_ARITHMETIC_STUBS(Div) +BASE_FLOAT_ARITHMETIC_STUBS(Mod) + +#undef BASE_FLOAT_ARITHMETIC_STUBS + +template +typename std::enable_if::is_iec559, T>::type CheckedNeg( + T value, + RangeConstraint*) { + return -value; +} + +template +typename std::enable_if::is_iec559, T>::type CheckedAbs( + T value, + RangeConstraint*) { + return std::abs(value); +} + +// Floats carry around their validity state with them, but integers do not. So, +// we wrap the underlying value in a specialization in order to hide that detail +// and expose an interface via accessors. +enum NumericRepresentation { + NUMERIC_INTEGER, + NUMERIC_FLOATING, + NUMERIC_UNKNOWN +}; + +template +struct GetNumericRepresentation { + static const NumericRepresentation value = + std::numeric_limits::is_integer + ? NUMERIC_INTEGER + : (std::numeric_limits::is_iec559 ? NUMERIC_FLOATING + : NUMERIC_UNKNOWN); +}; + +template ::value> +class CheckedNumericState {}; + +// Integrals require quite a bit of additional housekeeping to manage state. +template +class CheckedNumericState { + private: + T value_; + RangeConstraint validity_; + + public: + template + friend class CheckedNumericState; + + CheckedNumericState() : value_(0), validity_(RANGE_VALID) {} + + template + CheckedNumericState(Src value, RangeConstraint validity) + : value_(static_cast(value)), + validity_(GetRangeConstraint(validity | + DstRangeRelationToSrcRange(value))) { + static_assert(std::numeric_limits::is_specialized, + "Argument must be numeric."); + } + + // Copy constructor. + template + CheckedNumericState(const CheckedNumericState& rhs) + : value_(static_cast(rhs.value())), + validity_(GetRangeConstraint( + rhs.validity() | DstRangeRelationToSrcRange(rhs.value()))) {} + + template + explicit CheckedNumericState( + Src value, + typename std::enable_if::is_specialized, + int>::type = 0) + : value_(static_cast(value)), + validity_(DstRangeRelationToSrcRange(value)) {} + + RangeConstraint validity() const { return validity_; } + T value() const { return value_; } +}; + +// Floating points maintain their own validity, but need translation wrappers. +template +class CheckedNumericState { + private: + T value_; + + public: + template + friend class CheckedNumericState; + + CheckedNumericState() : value_(0.0) {} + + template + CheckedNumericState( + Src value, + RangeConstraint validity, + typename std::enable_if::is_integer, int>::type = + 0) { + switch (DstRangeRelationToSrcRange(value)) { + case RANGE_VALID: + value_ = static_cast(value); + break; + + case RANGE_UNDERFLOW: + value_ = -std::numeric_limits::infinity(); + break; + + case RANGE_OVERFLOW: + value_ = std::numeric_limits::infinity(); + break; + + case RANGE_INVALID: + value_ = std::numeric_limits::quiet_NaN(); + break; + + default: + NOTREACHED(); + } + } + + template + explicit CheckedNumericState( + Src value, + typename std::enable_if::is_specialized, + int>::type = 0) + : value_(static_cast(value)) {} + + // Copy constructor. + template + CheckedNumericState(const CheckedNumericState& rhs) + : value_(static_cast(rhs.value())) {} + + RangeConstraint validity() const { + return GetRangeConstraint(value_ <= std::numeric_limits::max(), + value_ >= -std::numeric_limits::max()); + } + T value() const { return value_; } +}; + +// For integers less than 128-bit and floats 32-bit or larger, we can distil +// C/C++ arithmetic promotions down to two simple rules: +// 1. The type with the larger maximum exponent always takes precedence. +// 2. The resulting type must be promoted to at least an int. +// The following template specializations implement that promotion logic. +enum ArithmeticPromotionCategory { + LEFT_PROMOTION, + RIGHT_PROMOTION, + DEFAULT_PROMOTION +}; + +template ::value > MaxExponent::value) + ? (MaxExponent::value > MaxExponent::value + ? LEFT_PROMOTION + : DEFAULT_PROMOTION) + : (MaxExponent::value > MaxExponent::value + ? RIGHT_PROMOTION + : DEFAULT_PROMOTION) > +struct ArithmeticPromotion; + +template +struct ArithmeticPromotion { + typedef Lhs type; +}; + +template +struct ArithmeticPromotion { + typedef Rhs type; +}; + +template +struct ArithmeticPromotion { + typedef int type; +}; + +// We can statically check if operations on the provided types can wrap, so we +// can skip the checked operations if they're not needed. So, for an integer we +// care if the destination type preserves the sign and is twice the width of +// the source. +template +struct IsIntegerArithmeticSafe { + static const bool value = !std::numeric_limits::is_iec559 && + StaticDstRangeRelationToSrcRange::value == + NUMERIC_RANGE_CONTAINED && + sizeof(T) >= (2 * sizeof(Lhs)) && + StaticDstRangeRelationToSrcRange::value != + NUMERIC_RANGE_CONTAINED && + sizeof(T) >= (2 * sizeof(Rhs)); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ diff --git a/security/sandbox/chromium/base/path_service.h b/security/sandbox/chromium/base/path_service.h index 554eb9e34644..c7f1abe71498 100644 --- a/security/sandbox/chromium/base/path_service.h +++ b/security/sandbox/chromium/base/path_service.h @@ -13,9 +13,9 @@ #include "build/build_config.h" namespace base { + class FilePath; class ScopedPathOverride; -} // namespace // The path service is a global table mapping keys to file system paths. It is // OK to use this service from multiple threads. @@ -29,7 +29,7 @@ class BASE_EXPORT PathService { // // Returns true if the directory or file was successfully retrieved. On // failure, 'path' will not be changed. - static bool Get(int key, base::FilePath* path); + static bool Get(int key, FilePath* path); // Overrides the path to a special directory or file. This cannot be used to // change the value of DIR_CURRENT, but that should be obvious. Also, if the @@ -44,7 +44,7 @@ class BASE_EXPORT PathService { // // Unit tests generally should use ScopedPathOverride instead. Overrides from // one test should not carry over to another. - static bool Override(int key, const base::FilePath& path); + static bool Override(int key, const FilePath& path); // This function does the same as PathService::Override but it takes extra // parameters: @@ -56,7 +56,7 @@ class BASE_EXPORT PathService { // - |create| guides whether the directory to be overriden must // be created in case it doesn't exist already. static bool OverrideAndCreateIfNeeded(int key, - const base::FilePath& path, + const FilePath& path, bool is_absolute, bool create); @@ -68,7 +68,7 @@ class BASE_EXPORT PathService { // WARNING: This function could be called on any thread from which the // PathService is used, so a the ProviderFunc MUST BE THREADSAFE. // - typedef bool (*ProviderFunc)(int, base::FilePath*); + typedef bool (*ProviderFunc)(int, FilePath*); // Call to register a path provider. You must specify the range "[key_start, // key_end)" of supported path keys. @@ -80,7 +80,7 @@ class BASE_EXPORT PathService { static void DisableCache(); private: - friend class base::ScopedPathOverride; + friend class ScopedPathOverride; FRIEND_TEST_ALL_PREFIXES(PathServiceTest, RemoveOverride); // Removes an override for a special directory or file. Returns true if there @@ -89,4 +89,9 @@ class BASE_EXPORT PathService { static bool RemoveOverride(int key); }; +} // namespace base + +// TODO(brettw) Convert all callers to using the base namespace and remove this. +using base::PathService; + #endif // BASE_PATH_SERVICE_H_ diff --git a/security/sandbox/chromium/base/port.h b/security/sandbox/chromium/base/port.h deleted file mode 100644 index 0a04d55f0f62..000000000000 --- a/security/sandbox/chromium/base/port.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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 BASE_PORT_H_ -#define BASE_PORT_H_ - -#include -#include "build/build_config.h" - -// DEPRECATED: Use ...LL and ...ULL suffixes. -// TODO(viettrungluu): Delete these. These are only here until |GG_(U)INT64_C| -// are deleted (some other header files (re)define |GG_(U)INT64_C|, so our -// definitions of them must exactly match theirs). -#ifdef COMPILER_MSVC -#define GG_LONGLONG(x) x##I64 -#define GG_ULONGLONG(x) x##UI64 -#else -#define GG_LONGLONG(x) x##LL -#define GG_ULONGLONG(x) x##ULL -#endif - -// DEPRECATED: In Chromium, we force-define __STDC_CONSTANT_MACROS, so you can -// just use the regular (U)INTn_C macros from . -// TODO(viettrungluu): Remove the remaining GG_(U)INTn_C macros. -#define GG_INT64_C(x) GG_LONGLONG(x) -#define GG_UINT64_C(x) GG_ULONGLONG(x) - -// It's possible for functions that use a va_list, such as StringPrintf, to -// invalidate the data in it upon use. The fix is to make a copy of the -// structure before using it and use that copy instead. va_copy is provided -// for this purpose. MSVC does not provide va_copy, so define an -// implementation here. It is not guaranteed that assignment is a copy, so the -// StringUtil.VariableArgsFunc unit test tests this capability. -#if defined(COMPILER_GCC) -#define GG_VA_COPY(a, b) (va_copy(a, b)) -#elif defined(COMPILER_MSVC) -#define GG_VA_COPY(a, b) (a = b) -#endif - -// Define an OS-neutral wrapper for shared library entry points -#if defined(OS_WIN) -#define API_CALL __stdcall -#else -#define API_CALL -#endif - -#endif // BASE_PORT_H_ diff --git a/security/sandbox/chromium/base/process/process_handle.h b/security/sandbox/chromium/base/process/process_handle.h index 6f8094ee80bf..ef7a6025525e 100644 --- a/security/sandbox/chromium/base/process/process_handle.h +++ b/security/sandbox/chromium/base/process/process_handle.h @@ -5,12 +5,13 @@ #ifndef BASE_PROCESS_PROCESS_HANDLE_H_ #define BASE_PROCESS_PROCESS_HANDLE_H_ +#include +#include + #include "base/base_export.h" -#include "base/basictypes.h" #include "base/files/file_path.h" #include "build/build_config.h" -#include #if defined(OS_WIN) #include #endif @@ -35,58 +36,44 @@ const ProcessId kNullProcessId = 0; #endif // defined(OS_WIN) // Returns the id of the current process. +// Note that on some platforms, this is not guaranteed to be unique across +// processes (use GetUniqueIdForProcess if uniqueness is required). BASE_EXPORT ProcessId GetCurrentProcId(); +// Returns a unique ID for the current process. The ID will be unique across all +// currently running processes within the chrome session, but IDs of terminated +// processes may be reused. This returns an opaque value that is different from +// a process's PID. +BASE_EXPORT uint32_t GetUniqueIdForProcess(); + +#if defined(OS_LINUX) +// When a process is started in a different PID namespace from the browser +// process, this function must be called with the process's PID in the browser's +// PID namespace in order to initialize its unique ID. Not thread safe. +// WARNING: To avoid inconsistent results from GetUniqueIdForProcess, this +// should only be called very early after process startup - ideally as soon +// after process creation as possible. +BASE_EXPORT void InitUniqueIdForProcessInPidNamespace( + ProcessId pid_outside_of_namespace); +#endif + // Returns the ProcessHandle of the current process. BASE_EXPORT ProcessHandle GetCurrentProcessHandle(); -// Converts a PID to a process handle. This handle must be closed by -// CloseProcessHandle when you are done with it. Returns true on success. -BASE_EXPORT bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle); - -// Converts a PID to a process handle. On Windows the handle is opened -// with more access rights and must only be used by trusted code. -// You have to close returned handle using CloseProcessHandle. Returns true -// on success. -// TODO(sanjeevr): Replace all calls to OpenPrivilegedProcessHandle with the -// more specific OpenProcessHandleWithAccess method and delete this. -BASE_EXPORT bool OpenPrivilegedProcessHandle(ProcessId pid, - ProcessHandle* handle); - -// Converts a PID to a process handle using the desired access flags. Use a -// combination of the kProcessAccess* flags defined above for |access_flags|. -BASE_EXPORT bool OpenProcessHandleWithAccess(ProcessId pid, - uint32 access_flags, - ProcessHandle* handle); - -// Closes the process handle opened by OpenProcessHandle. -BASE_EXPORT void CloseProcessHandle(ProcessHandle process); - -// Returns the unique ID for the specified process. This is functionally the -// same as Windows' GetProcessId(), but works on versions of Windows before -// Win XP SP1 as well. +// Returns the process ID for the specified process. This is functionally the +// same as Windows' GetProcessId(), but works on versions of Windows before Win +// XP SP1 as well. +// DEPRECATED. New code should be using Process::Pid() instead. +// Note that on some platforms, this is not guaranteed to be unique across +// processes. BASE_EXPORT ProcessId GetProcId(ProcessHandle process); -#if defined(OS_WIN) -enum IntegrityLevel { - INTEGRITY_UNKNOWN, - LOW_INTEGRITY, - MEDIUM_INTEGRITY, - HIGH_INTEGRITY, -}; -// Determine the integrity level of the specified process. Returns false -// if the system does not support integrity levels (pre-Vista) or in the case -// of an underlying system failure. -BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process, - IntegrityLevel* level); -#endif +// Returns the ID for the parent of the given process. +BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process); #if defined(OS_POSIX) // Returns the path to the executable of the given process. BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process); - -// Returns the ID for the parent of the given process. -BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process); #endif } // namespace base diff --git a/security/sandbox/chromium/base/rand_util.h b/security/sandbox/chromium/base/rand_util.h index 6130c129b7fa..881dbd50bb18 100644 --- a/security/sandbox/chromium/base/rand_util.h +++ b/security/sandbox/chromium/base/rand_util.h @@ -5,15 +5,18 @@ #ifndef BASE_RAND_UTIL_H_ #define BASE_RAND_UTIL_H_ +#include +#include + #include #include "base/base_export.h" -#include "base/basictypes.h" +#include "build/build_config.h" namespace base { -// Returns a random number in range [0, kuint64max]. Thread-safe. -BASE_EXPORT uint64 RandUint64(); +// Returns a random number in range [0, UINT64_MAX]. Thread-safe. +BASE_EXPORT uint64_t RandUint64(); // Returns a random number between min and max (inclusive). Thread-safe. BASE_EXPORT int RandInt(int min, int max); @@ -23,14 +26,14 @@ BASE_EXPORT int RandInt(int min, int max); // Note that this can be used as an adapter for std::random_shuffle(): // Given a pre-populated |std::vector myvector|, shuffle it as // std::random_shuffle(myvector.begin(), myvector.end(), base::RandGenerator); -BASE_EXPORT uint64 RandGenerator(uint64 range); +BASE_EXPORT uint64_t RandGenerator(uint64_t range); // Returns a random double in range [0, 1). Thread-safe. BASE_EXPORT double RandDouble(); // Given input |bits|, convert with maximum precision to a double in // the range [0, 1). Thread-safe. -BASE_EXPORT double BitsToOpenEndedUnitInterval(uint64 bits); +BASE_EXPORT double BitsToOpenEndedUnitInterval(uint64_t bits); // Fills |output_length| bytes of |output| with random data. // diff --git a/security/sandbox/chromium/base/safe_strerror_posix.cc b/security/sandbox/chromium/base/safe_strerror_posix.cc deleted file mode 100644 index 9da7aeef1190..000000000000 --- a/security/sandbox/chromium/base/safe_strerror_posix.cc +++ /dev/null @@ -1,119 +0,0 @@ -// 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. - -#if defined(__ANDROID__) -// Post-L versions of bionic define the GNU-specific strerror_r if _GNU_SOURCE -// is defined, but the symbol is renamed to __gnu_strerror_r which only exists -// on those later versions. To preserve ABI compatibility with older versions, -// undefine _GNU_SOURCE and use the POSIX version. -#undef _GNU_SOURCE -#endif - -#include "build/build_config.h" -#include "base/safe_strerror_posix.h" - -#include -#include -#include - -#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(OS_NACL)) - -#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__) -// GCC will complain about the unused second wrap function unless we tell it -// that we meant for them to be potentially unused, which is exactly what this -// attribute is for. -#define POSSIBLY_UNUSED __attribute__((unused)) -#else -#define POSSIBLY_UNUSED -#endif - -#if USE_HISTORICAL_STRERRO_R -// glibc has two strerror_r functions: a historical GNU-specific one that -// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4 -// that returns int. This wraps the GNU-specific one. -static void POSSIBLY_UNUSED wrap_posix_strerror_r( - char *(*strerror_r_ptr)(int, char *, size_t), - int err, - char *buf, - size_t len) { - // GNU version. - char *rc = (*strerror_r_ptr)(err, buf, len); - if (rc != buf) { - // glibc did not use buf and returned a static string instead. Copy it - // into buf. - buf[0] = '\0'; - strncat(buf, rc, len - 1); - } - // The GNU version never fails. Unknown errors get an "unknown error" message. - // The result is always null terminated. -} -#endif // USE_HISTORICAL_STRERRO_R - -// Wrapper for strerror_r functions that implement the POSIX interface. POSIX -// does not define the behaviour for some of the edge cases, so we wrap it to -// guarantee that they are handled. This is compiled on all POSIX platforms, but -// it will only be used on Linux if the POSIX strerror_r implementation is -// being used (see below). -static void POSSIBLY_UNUSED wrap_posix_strerror_r( - int (*strerror_r_ptr)(int, char *, size_t), - int err, - char *buf, - size_t len) { - int old_errno = errno; - // Have to cast since otherwise we get an error if this is the GNU version - // (but in such a scenario this function is never called). Sadly we can't use - // C++-style casts because the appropriate one is reinterpret_cast but it's - // considered illegal to reinterpret_cast a type to itself, so we get an - // error in the opposite case. - int result = (*strerror_r_ptr)(err, buf, len); - if (result == 0) { - // POSIX is vague about whether the string will be terminated, although - // it indirectly implies that typically ERANGE will be returned, instead - // of truncating the string. We play it safe by always terminating the - // string explicitly. - buf[len - 1] = '\0'; - } else { - // Error. POSIX is vague about whether the return value is itself a system - // error code or something else. On Linux currently it is -1 and errno is - // set. On BSD-derived systems it is a system error and errno is unchanged. - // We try and detect which case it is so as to put as much useful info as - // we can into our message. - int strerror_error; // The error encountered in strerror - int new_errno = errno; - if (new_errno != old_errno) { - // errno was changed, so probably the return value is just -1 or something - // else that doesn't provide any info, and errno is the error. - strerror_error = new_errno; - } else { - // Either the error from strerror_r was the same as the previous value, or - // errno wasn't used. Assume the latter. - strerror_error = result; - } - // snprintf truncates and always null-terminates. - snprintf(buf, - len, - "Error %d while retrieving error %d", - strerror_error, - err); - } - errno = old_errno; -} - -void safe_strerror_r(int err, char *buf, size_t len) { - if (buf == NULL || len <= 0) { - return; - } - // If using glibc (i.e., Linux), the compiler will automatically select the - // appropriate overloaded function based on the function type of strerror_r. - // The other one will be elided from the translation unit since both are - // static. - wrap_posix_strerror_r(&strerror_r, err, buf, len); -} - -std::string safe_strerror(int err) { - const int buffer_size = 256; - char buf[buffer_size]; - safe_strerror_r(err, buf, sizeof(buf)); - return std::string(buf); -} diff --git a/security/sandbox/chromium/base/safe_strerror_posix.h b/security/sandbox/chromium/base/safe_strerror_posix.h deleted file mode 100644 index 2f77d84a1b96..000000000000 --- a/security/sandbox/chromium/base/safe_strerror_posix.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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 BASE_SAFE_STRERROR_POSIX_H_ -#define BASE_SAFE_STRERROR_POSIX_H_ - -#include - -#include "base/base_export.h" - -// BEFORE using anything from this file, first look at PLOG and friends in -// logging.h and use them instead if applicable. -// -// This file declares safe, portable alternatives to the POSIX strerror() -// function. strerror() is inherently unsafe in multi-threaded apps and should -// never be used. Doing so can cause crashes. Additionally, the thread-safe -// alternative strerror_r varies in semantics across platforms. Use these -// functions instead. - -// Thread-safe strerror function with dependable semantics that never fails. -// It will write the string form of error "err" to buffer buf of length len. -// If there is an error calling the OS's strerror_r() function then a message to -// that effect will be printed into buf, truncating if necessary. The final -// result is always null-terminated. The value of errno is never changed. -// -// Use this instead of strerror_r(). -BASE_EXPORT void safe_strerror_r(int err, char *buf, size_t len); - -// Calls safe_strerror_r with a buffer of suitable size and returns the result -// in a C++ string. -// -// Use this instead of strerror(). Note though that safe_strerror_r will be -// more robust in the case of heap corruption errors, since it doesn't need to -// allocate a string. -BASE_EXPORT std::string safe_strerror(int err); - -#endif // BASE_SAFE_STRERROR_POSIX_H_ diff --git a/security/sandbox/chromium/base/scoped_clear_errno.h b/security/sandbox/chromium/base/scoped_clear_errno.h index 7b972fc85ac6..585f6f768f1f 100644 --- a/security/sandbox/chromium/base/scoped_clear_errno.h +++ b/security/sandbox/chromium/base/scoped_clear_errno.h @@ -7,7 +7,7 @@ #include -#include "base/basictypes.h" +#include "base/macros.h" namespace base { diff --git a/security/sandbox/chromium/base/sequence_checker_impl.h b/security/sandbox/chromium/base/sequence_checker_impl.h index 741aafee6459..e3c5fed508c4 100644 --- a/security/sandbox/chromium/base/sequence_checker_impl.h +++ b/security/sandbox/chromium/base/sequence_checker_impl.h @@ -6,7 +6,7 @@ #define BASE_SEQUENCE_CHECKER_IMPL_H_ #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" #include "base/synchronization/lock.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread_checker_impl.h" diff --git a/security/sandbox/chromium/base/sequenced_task_runner.h b/security/sandbox/chromium/base/sequenced_task_runner.h index 71dcf05c7503..6bb3f2b8717b 100644 --- a/security/sandbox/chromium/base/sequenced_task_runner.h +++ b/security/sandbox/chromium/base/sequenced_task_runner.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef BASE_SEQUENCED_TASKRUNNER_H_ -#define BASE_SEQUENCED_TASKRUNNER_H_ +#ifndef BASE_SEQUENCED_TASK_RUNNER_H_ +#define BASE_SEQUENCED_TASK_RUNNER_H_ #include "base/base_export.h" #include "base/sequenced_task_runner_helpers.h" @@ -156,4 +156,4 @@ class BASE_EXPORT SequencedTaskRunner : public TaskRunner { } // namespace base -#endif // BASE_SEQUENCED_TASKRUNNER_H_ +#endif // BASE_SEQUENCED_TASK_RUNNER_H_ diff --git a/security/sandbox/chromium/base/sequenced_task_runner_helpers.h b/security/sandbox/chromium/base/sequenced_task_runner_helpers.h index da519bf91896..7980b46b6cea 100644 --- a/security/sandbox/chromium/base/sequenced_task_runner_helpers.h +++ b/security/sandbox/chromium/base/sequenced_task_runner_helpers.h @@ -5,8 +5,8 @@ #ifndef BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_ #define BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_ -#include "base/basictypes.h" #include "base/debug/alias.h" +#include "base/macros.h" // TODO(akalin): Investigate whether it's possible to just have // SequencedTaskRunner use these helpers (instead of MessageLoop). diff --git a/security/sandbox/chromium/base/single_thread_task_runner.h b/security/sandbox/chromium/base/single_thread_task_runner.h index b5311db16942..6e9319314894 100644 --- a/security/sandbox/chromium/base/single_thread_task_runner.h +++ b/security/sandbox/chromium/base/single_thread_task_runner.h @@ -16,7 +16,8 @@ namespace base { // there is a specific need to run tasks on only a single thread. // // SingleThreadTaskRunner implementations might: -// - Post tasks to an existing thread's MessageLoop (see MessageLoopProxy). +// - Post tasks to an existing thread's MessageLoop (see +// MessageLoop::task_runner()). // - Create their own worker thread and MessageLoop to post tasks to. // - Add tasks to a FIFO and signal to a non-MessageLoop thread for them to // be processed. This allows TaskRunner-oriented code run on threads diff --git a/security/sandbox/chromium/base/stl_util.h b/security/sandbox/chromium/base/stl_util.h index 3602df0377ad..12e226a9db9b 100644 --- a/security/sandbox/chromium/base/stl_util.h +++ b/security/sandbox/chromium/base/stl_util.h @@ -90,17 +90,12 @@ void STLDeleteContainerPairSecondPointers(ForwardIterator begin, } } -// To treat a possibly-empty vector as an array, use these functions. -// If you know the array will never be empty, you can use &*v.begin() -// directly, but that is undefined behaviour if |v| is empty. -template -inline T* vector_as_array(std::vector* v) { - return v->empty() ? NULL : &*v->begin(); -} - -template -inline const T* vector_as_array(const std::vector* v) { - return v->empty() ? NULL : &*v->begin(); +// Counts the number of instances of val in a container. +template +typename std::iterator_traits< + typename Container::const_iterator>::difference_type +STLCount(const Container& container, const T& val) { + return std::count(container.begin(), container.end(), val); } // Return a mutable char* pointing to a string's internal buffer, @@ -148,8 +143,7 @@ template void STLDeleteValues(T* container) { if (!container) return; - for (typename T::iterator i(container->begin()); i != container->end(); ++i) - delete i->second; + STLDeleteContainerPairSecondPointers(container->begin(), container->end()); container->clear(); } @@ -196,6 +190,14 @@ bool ContainsKey(const Collection& collection, const Key& key) { return collection.find(key) != collection.end(); } +// Test to see if a collection like a vector contains a particular value. +// Returns true if the value is in the collection. +template +bool ContainsValue(const Collection& collection, const Value& value) { + return std::find(collection.begin(), collection.end(), value) != + collection.end(); +} + namespace base { // Returns true if the container is sorted. diff --git a/security/sandbox/chromium/base/strings/nullable_string16.h b/security/sandbox/chromium/base/strings/nullable_string16.h index 5997d174419a..016c25c2501f 100644 --- a/security/sandbox/chromium/base/strings/nullable_string16.h +++ b/security/sandbox/chromium/base/strings/nullable_string16.h @@ -41,6 +41,6 @@ inline bool operator!=(const NullableString16& a, const NullableString16& b) { BASE_EXPORT std::ostream& operator<<(std::ostream& out, const NullableString16& value); -} // namespace +} // namespace base #endif // BASE_STRINGS_NULLABLE_STRING16_H_ diff --git a/security/sandbox/chromium/base/strings/safe_sprintf.cc b/security/sandbox/chromium/base/strings/safe_sprintf.cc index 5b5756355192..a51c778271e8 100644 --- a/security/sandbox/chromium/base/strings/safe_sprintf.cc +++ b/security/sandbox/chromium/base/strings/safe_sprintf.cc @@ -4,8 +4,14 @@ #include "base/strings/safe_sprintf.h" +#include +#include + #include +#include "base/macros.h" +#include "build/build_config.h" + #if !defined(NDEBUG) // In debug builds, we use RAW_CHECK() to print useful error messages, if // SafeSPrintf() is called with broken arguments. @@ -69,7 +75,7 @@ const char kDownCaseHexDigits[] = "0123456789abcdef"; #if defined(NDEBUG) // We would like to define kSSizeMax as std::numeric_limits::max(), // but C++ doesn't allow us to do that for constants. Instead, we have to -// use careful casting and shifting. We later use a COMPILE_ASSERT to +// use careful casting and shifting. We later use a static_assert to // verify that this worked correctly. namespace { const size_t kSSizeMax = kSSizeMaxConst; @@ -107,18 +113,13 @@ class Buffer { : buffer_(buffer), size_(size - 1), // Account for trailing NUL byte count_(0) { -// The following assertion does not build on Mac and Android. This is because -// static_assert only works with compile-time constants, but mac uses -// libstdc++4.2 and android uses stlport, which both don't mark -// numeric_limits::max() as constexp. Likewise, MSVS2013's standard library -// also doesn't mark max() as constexpr yet. cl.exe supports static_cast but -// doesn't really implement constexpr yet so it doesn't complain, but clang -// does. -#if __cplusplus >= 201103 && !defined(OS_ANDROID) && !defined(OS_MACOSX) && \ - !defined(OS_IOS) && !(defined(__clang__) && defined(OS_WIN)) - COMPILE_ASSERT(kSSizeMaxConst == \ - static_cast(std::numeric_limits::max()), - kSSizeMax_is_the_max_value_of_an_ssize_t); +// MSVS2013's standard library doesn't mark max() as constexpr yet. cl.exe +// supports static_cast but doesn't really implement constexpr yet so it doesn't +// complain, but clang does. +#if __cplusplus >= 201103 && !(defined(__clang__) && defined(OS_WIN)) + static_assert(kSSizeMaxConst == + static_cast(std::numeric_limits::max()), + "kSSizeMaxConst should be the max value of an ssize_t"); #endif DEBUG_CHECK(size > 0); DEBUG_CHECK(size <= kSSizeMax); @@ -510,11 +511,11 @@ ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, const Arg* args, buffer.Pad(' ', padding, 1); // Convert the argument to an ASCII character and output it. - char ch = static_cast(arg.integer.i); - if (!ch) { + char as_char = static_cast(arg.integer.i); + if (!as_char) { goto end_of_output_buffer; } - buffer.Out(ch); + buffer.Out(as_char); break; } case 'd': // Output a possibly signed decimal value. case 'o': // Output an unsigned octal value. diff --git a/security/sandbox/chromium/base/strings/safe_sprintf.h b/security/sandbox/chromium/base/strings/safe_sprintf.h index 2d173202d3e7..65524a50c3ae 100644 --- a/security/sandbox/chromium/base/strings/safe_sprintf.h +++ b/security/sandbox/chromium/base/strings/safe_sprintf.h @@ -17,7 +17,6 @@ #endif #include "base/base_export.h" -#include "base/basictypes.h" namespace base { namespace strings { diff --git a/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc b/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc index 02c75f990d16..931ace8b1245 100644 --- a/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc +++ b/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc @@ -4,13 +4,17 @@ #include "base/strings/safe_sprintf.h" +#include +#include #include #include #include #include "base/logging.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" // Death tests on Android are currently very flaky. No need to add more flaky @@ -61,7 +65,7 @@ TEST(SafeSPrintfTest, NoArguments) { // always add a trailing NUL; it always deduplicates '%' characters). static const char text[] = "hello world"; char ref[20], buf[20]; - memset(ref, 'X', sizeof(char) * arraysize(buf)); + memset(ref, 'X', sizeof(ref)); memcpy(buf, ref, sizeof(buf)); // A negative buffer size should always result in an error. diff --git a/security/sandbox/chromium/base/strings/string16.h b/security/sandbox/chromium/base/strings/string16.h index 804dca42e456..e47669c1b565 100644 --- a/security/sandbox/chromium/base/strings/string16.h +++ b/security/sandbox/chromium/base/strings/string16.h @@ -26,11 +26,13 @@ // libc functions with custom, 2-byte-char compatible routines. It is capable // of carrying UTF-16-encoded data. +#include +#include #include #include #include "base/base_export.h" -#include "base/basictypes.h" +#include "build/build_config.h" #if defined(WCHAR_T_IS_UTF16) @@ -46,7 +48,7 @@ typedef std::char_traits string16_char_traits; namespace base { -typedef uint16 char16; +typedef uint16_t char16; // char16 versions of the functions required by string16_char_traits; these // are based on the wide character functions of similar names ("w" or "wcs" @@ -64,7 +66,8 @@ struct string16_char_traits { // int_type needs to be able to hold each possible value of char_type, and in // addition, the distinct value of eof(). - COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width); + static_assert(sizeof(int_type) > sizeof(char_type), + "int must be larger than 16 bits wide"); typedef std::streamoff off_type; typedef mbstate_t state_type; @@ -94,7 +97,7 @@ struct string16_char_traits { return c16memchr(s, a, n); } - static char_type* move(char_type* s1, const char_type* s2, int_type n) { + static char_type* move(char_type* s1, const char_type* s2, size_t n) { return c16memmove(s1, s2, n); } diff --git a/security/sandbox/chromium/base/strings/string_number_conversions.cc b/security/sandbox/chromium/base/strings/string_number_conversions.cc index 642d24e9ada1..07248501e315 100644 --- a/security/sandbox/chromium/base/strings/string_number_conversions.cc +++ b/security/sandbox/chromium/base/strings/string_number_conversions.cc @@ -12,6 +12,8 @@ #include #include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/numerics/safe_math.h" #include "base/scoped_clear_errno.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/dmg_fp/dmg_fp.h" @@ -20,77 +22,37 @@ namespace base { namespace { -template +template struct IntToStringT { - // This is to avoid a compiler warning about unary minus on unsigned type. - // For example, say you had the following code: - // template - // INT abs(INT value) { return value < 0 ? -value : value; } - // Even though if INT is unsigned, it's impossible for value < 0, so the - // unary minus will never be taken, the compiler will still generate a - // warning. We do a little specialization dance... - template - struct ToUnsignedT {}; - - template - struct ToUnsignedT { - static UINT2 ToUnsigned(INT2 value) { - return static_cast(value); - } - }; - - template - struct ToUnsignedT { - static UINT2 ToUnsigned(INT2 value) { - return static_cast(value < 0 ? -value : value); - } - }; - - // This set of templates is very similar to the above templates, but - // for testing whether an integer is negative. - template - struct TestNegT {}; - template - struct TestNegT { - static bool TestNeg(INT2 value) { - // value is unsigned, and can never be negative. - return false; - } - }; - template - struct TestNegT { - static bool TestNeg(INT2 value) { - return value < 0; - } - }; - static STR IntToString(INT value) { // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. // So round up to allocate 3 output characters per byte, plus 1 for '-'. - const int kOutputBufSize = 3 * sizeof(INT) + 1; + const size_t kOutputBufSize = + 3 * sizeof(INT) + std::numeric_limits::is_signed; - // Allocate the whole string right away, we will right back to front, and + // Create the string in a temporary buffer, write it back to front, and // then return the substr of what we ended up using. - STR outbuf(kOutputBufSize, 0); + using CHR = typename STR::value_type; + CHR outbuf[kOutputBufSize]; - bool is_neg = TestNegT::TestNeg(value); - // Even though is_neg will never be true when INT is parameterized as - // unsigned, even the presence of the unary operation causes a warning. - UINT res = ToUnsignedT::ToUnsigned(value); + // The ValueOrDie call below can never fail, because UnsignedAbs is valid + // for all valid inputs. + auto res = CheckedNumeric(value).UnsignedAbs().ValueOrDie(); - typename STR::iterator it(outbuf.end()); + CHR* end = outbuf + kOutputBufSize; + CHR* i = end; do { - --it; - DCHECK(it != outbuf.begin()); - *it = static_cast((res % 10) + '0'); + --i; + DCHECK(i != outbuf); + *i = static_cast((res % 10) + '0'); res /= 10; } while (res != 0); - if (is_neg) { - --it; - DCHECK(it != outbuf.begin()); - *it = static_cast('-'); + if (IsValueNegative(value)) { + --i; + DCHECK(i != outbuf); + *i = static_cast('-'); } - return STR(it, outbuf.end()); + return STR(i, end); } }; @@ -101,9 +63,9 @@ template class BaseCharToDigit { // Faster specialization for bases <= 10 template class BaseCharToDigit { public: - static bool Convert(CHAR c, uint8* digit) { + static bool Convert(CHAR c, uint8_t* digit) { if (c >= '0' && c < '0' + BASE) { - *digit = static_cast(c - '0'); + *digit = static_cast(c - '0'); return true; } return false; @@ -113,7 +75,7 @@ template class BaseCharToDigit { // Specialization for bases where 10 < base <= 36 template class BaseCharToDigit { public: - static bool Convert(CHAR c, uint8* digit) { + static bool Convert(CHAR c, uint8_t* digit) { if (c >= '0' && c <= '9') { *digit = c - '0'; } else if (c >= 'a' && c < 'a' + BASE - 10) { @@ -127,14 +89,15 @@ template class BaseCharToDigit { } }; -template bool CharToDigit(CHAR c, uint8* digit) { +template +bool CharToDigit(CHAR c, uint8_t* digit) { return BaseCharToDigit::Convert(c, digit); } -// There is an IsWhitespace for wchars defined in string_util.h, but it is -// locale independent, whereas the functions we are replacing were -// locale-dependent. TBD what is desired, but for the moment let's not introduce -// a change in behaviour. +// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it +// is locale independent, whereas the functions we are replacing were +// locale-dependent. TBD what is desired, but for the moment let's not +// introduce a change in behaviour. template class WhitespaceHelper { }; @@ -224,7 +187,7 @@ class IteratorRangeToNumber { } for (const_iterator current = begin; current != end; ++current) { - uint8 new_digit = 0; + uint8_t new_digit = 0; if (!CharToDigit(*current, &new_digit)) { return false; @@ -245,7 +208,7 @@ class IteratorRangeToNumber { class Positive : public Base { public: - static bool CheckBounds(value_type* output, uint8 new_digit) { + static bool CheckBounds(value_type* output, uint8_t new_digit) { if (*output > static_cast(traits::max() / traits::kBase) || (*output == static_cast(traits::max() / traits::kBase) && new_digit > traits::max() % traits::kBase)) { @@ -254,14 +217,14 @@ class IteratorRangeToNumber { } return true; } - static void Increment(uint8 increment, value_type* output) { + static void Increment(uint8_t increment, value_type* output) { *output += increment; } }; class Negative : public Base { public: - static bool CheckBounds(value_type* output, uint8 new_digit) { + static bool CheckBounds(value_type* output, uint8_t new_digit) { if (*output < traits::min() / traits::kBase || (*output == traits::min() / traits::kBase && new_digit > 0 - traits::min() % traits::kBase)) { @@ -270,7 +233,7 @@ class IteratorRangeToNumber { } return true; } - static void Increment(uint8 increment, value_type* output) { + static void Increment(uint8_t increment, value_type* output) { *output -= increment; } }; @@ -295,20 +258,17 @@ class BaseHexIteratorRangeToIntTraits : public BaseIteratorRangeToNumberTraits { }; -template +template class BaseHexIteratorRangeToUIntTraits - : public BaseIteratorRangeToNumberTraits { -}; + : public BaseIteratorRangeToNumberTraits {}; -template +template class BaseHexIteratorRangeToInt64Traits - : public BaseIteratorRangeToNumberTraits { -}; + : public BaseIteratorRangeToNumberTraits {}; -template +template class BaseHexIteratorRangeToUInt64Traits - : public BaseIteratorRangeToNumberTraits { -}; + : public BaseIteratorRangeToNumberTraits {}; typedef BaseHexIteratorRangeToIntTraits HexIteratorRangeToIntTraits; @@ -322,15 +282,15 @@ typedef BaseHexIteratorRangeToInt64Traits typedef BaseHexIteratorRangeToUInt64Traits HexIteratorRangeToUInt64Traits; -template -bool HexStringToBytesT(const STR& input, std::vector* output) { +template +bool HexStringToBytesT(const STR& input, std::vector* output) { DCHECK_EQ(output->size(), 0u); size_t count = input.size(); if (count == 0 || (count % 2) != 0) return false; for (uintptr_t i = 0; i < count / 2; ++i) { - uint8 msb = 0; // most significant 4 bits - uint8 lsb = 0; // least significant 4 bits + uint8_t msb = 0; // most significant 4 bits + uint8_t lsb = 0; // least significant 4 bits if (!CharToDigit<16>(input[i * 2], &msb) || !CharToDigit<16>(input[i * 2 + 1], &lsb)) return false; @@ -368,47 +328,43 @@ bool String16ToIntImpl(const StringPiece16& input, VALUE* output) { } // namespace std::string IntToString(int value) { - return IntToStringT:: - IntToString(value); + return IntToStringT::IntToString(value); } string16 IntToString16(int value) { - return IntToStringT:: - IntToString(value); + return IntToStringT::IntToString(value); } std::string UintToString(unsigned int value) { - return IntToStringT:: - IntToString(value); + return IntToStringT::IntToString(value); } string16 UintToString16(unsigned int value) { - return IntToStringT:: - IntToString(value); + return IntToStringT::IntToString(value); } -std::string Int64ToString(int64 value) { - return IntToStringT::IntToString(value); +std::string Int64ToString(int64_t value) { + return IntToStringT::IntToString(value); } -string16 Int64ToString16(int64 value) { - return IntToStringT::IntToString(value); +string16 Int64ToString16(int64_t value) { + return IntToStringT::IntToString(value); } -std::string Uint64ToString(uint64 value) { - return IntToStringT::IntToString(value); +std::string Uint64ToString(uint64_t value) { + return IntToStringT::IntToString(value); } -string16 Uint64ToString16(uint64 value) { - return IntToStringT::IntToString(value); +string16 Uint64ToString16(uint64_t value) { + return IntToStringT::IntToString(value); } std::string SizeTToString(size_t value) { - return IntToStringT::IntToString(value); + return IntToStringT::IntToString(value); } string16 SizeTToString16(size_t value) { - return IntToStringT::IntToString(value); + return IntToStringT::IntToString(value); } std::string DoubleToString(double value) { @@ -434,19 +390,19 @@ bool StringToUint(const StringPiece16& input, unsigned* output) { return String16ToIntImpl(input, output); } -bool StringToInt64(const StringPiece& input, int64* output) { +bool StringToInt64(const StringPiece& input, int64_t* output) { return StringToIntImpl(input, output); } -bool StringToInt64(const StringPiece16& input, int64* output) { +bool StringToInt64(const StringPiece16& input, int64_t* output) { return String16ToIntImpl(input, output); } -bool StringToUint64(const StringPiece& input, uint64* output) { +bool StringToUint64(const StringPiece& input, uint64_t* output) { return StringToIntImpl(input, output); } -bool StringToUint64(const StringPiece16& input, uint64* output) { +bool StringToUint64(const StringPiece16& input, uint64_t* output) { return String16ToIntImpl(input, output); } @@ -507,22 +463,22 @@ bool HexStringToInt(const StringPiece& input, int* output) { input.begin(), input.end(), output); } -bool HexStringToUInt(const StringPiece& input, uint32* output) { +bool HexStringToUInt(const StringPiece& input, uint32_t* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } -bool HexStringToInt64(const StringPiece& input, int64* output) { +bool HexStringToInt64(const StringPiece& input, int64_t* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } -bool HexStringToUInt64(const StringPiece& input, uint64* output) { +bool HexStringToUInt64(const StringPiece& input, uint64_t* output) { return IteratorRangeToNumber::Invoke( input.begin(), input.end(), output); } -bool HexStringToBytes(const std::string& input, std::vector* output) { +bool HexStringToBytes(const std::string& input, std::vector* output) { return HexStringToBytesT(input, output); } diff --git a/security/sandbox/chromium/base/strings/string_number_conversions.h b/security/sandbox/chromium/base/strings/string_number_conversions.h index 050e627a1a13..1265f0dcba3a 100644 --- a/security/sandbox/chromium/base/strings/string_number_conversions.h +++ b/security/sandbox/chromium/base/strings/string_number_conversions.h @@ -5,11 +5,13 @@ #ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_ #define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_ +#include +#include + #include #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" @@ -35,11 +37,11 @@ BASE_EXPORT string16 IntToString16(int value); BASE_EXPORT std::string UintToString(unsigned value); BASE_EXPORT string16 UintToString16(unsigned value); -BASE_EXPORT std::string Int64ToString(int64 value); -BASE_EXPORT string16 Int64ToString16(int64 value); +BASE_EXPORT std::string Int64ToString(int64_t value); +BASE_EXPORT string16 Int64ToString16(int64_t value); -BASE_EXPORT std::string Uint64ToString(uint64 value); -BASE_EXPORT string16 Uint64ToString16(uint64 value); +BASE_EXPORT std::string Uint64ToString(uint64_t value); +BASE_EXPORT string16 Uint64ToString16(uint64_t value); BASE_EXPORT std::string SizeTToString(size_t value); BASE_EXPORT string16 SizeTToString16(size_t value); @@ -64,27 +66,31 @@ BASE_EXPORT std::string DoubleToString(double value); // - No characters parseable as a number at the beginning of the string. // |*output| will be set to 0. // - Empty string. |*output| will be set to 0. +// WARNING: Will write to |output| even when returning false. +// Read the comments above carefully. BASE_EXPORT bool StringToInt(const StringPiece& input, int* output); BASE_EXPORT bool StringToInt(const StringPiece16& input, int* output); BASE_EXPORT bool StringToUint(const StringPiece& input, unsigned* output); BASE_EXPORT bool StringToUint(const StringPiece16& input, unsigned* output); -BASE_EXPORT bool StringToInt64(const StringPiece& input, int64* output); -BASE_EXPORT bool StringToInt64(const StringPiece16& input, int64* output); +BASE_EXPORT bool StringToInt64(const StringPiece& input, int64_t* output); +BASE_EXPORT bool StringToInt64(const StringPiece16& input, int64_t* output); -BASE_EXPORT bool StringToUint64(const StringPiece& input, uint64* output); -BASE_EXPORT bool StringToUint64(const StringPiece16& input, uint64* output); +BASE_EXPORT bool StringToUint64(const StringPiece& input, uint64_t* output); +BASE_EXPORT bool StringToUint64(const StringPiece16& input, uint64_t* output); BASE_EXPORT bool StringToSizeT(const StringPiece& input, size_t* output); BASE_EXPORT bool StringToSizeT(const StringPiece16& input, size_t* output); // For floating-point conversions, only conversions of input strings in decimal // form are defined to work. Behavior with strings representing floating-point -// numbers in hexadecimal, and strings representing non-fininte values (such as +// numbers in hexadecimal, and strings representing non-finite values (such as // NaN and inf) is undefined. Otherwise, these behave the same as the integral // variants. This expects the input string to NOT be specific to the locale. // If your input is locale specific, use ICU to read the number. +// WARNING: Will write to |output| even when returning false. +// Read the comments here and above StringToInt() carefully. BASE_EXPORT bool StringToDouble(const std::string& input, double* output); // Hex encoding ---------------------------------------------------------------- @@ -106,25 +112,25 @@ BASE_EXPORT bool HexStringToInt(const StringPiece& input, int* output); // Will only successful parse hex values that will fit into |output|, i.e. // 0x00000000 < |input| < 0xFFFFFFFF. // The string is not required to start with 0x. -BASE_EXPORT bool HexStringToUInt(const StringPiece& input, uint32* output); +BASE_EXPORT bool HexStringToUInt(const StringPiece& input, uint32_t* output); // Best effort conversion, see StringToInt above for restrictions. // Will only successful parse hex values that will fit into |output|, i.e. // -0x8000000000000000 < |input| < 0x7FFFFFFFFFFFFFFF. -BASE_EXPORT bool HexStringToInt64(const StringPiece& input, int64* output); +BASE_EXPORT bool HexStringToInt64(const StringPiece& input, int64_t* output); // Best effort conversion, see StringToInt above for restrictions. // Will only successful parse hex values that will fit into |output|, i.e. // 0x0000000000000000 < |input| < 0xFFFFFFFFFFFFFFFF. // The string is not required to start with 0x. -BASE_EXPORT bool HexStringToUInt64(const StringPiece& input, uint64* output); +BASE_EXPORT bool HexStringToUInt64(const StringPiece& input, uint64_t* output); // Similar to the previous functions, except that output is a vector of bytes. // |*output| will contain as many bytes as were successfully parsed prior to the // error. There is no overflow, but input.size() must be evenly divisible by 2. // Leading 0x or +/- are not allowed. BASE_EXPORT bool HexStringToBytes(const std::string& input, - std::vector* output); + std::vector* output); } // namespace base diff --git a/security/sandbox/chromium/base/strings/string_piece.cc b/security/sandbox/chromium/base/strings/string_piece.cc index 4c7f1122f2d8..c26bb3652fa7 100644 --- a/security/sandbox/chromium/base/strings/string_piece.cc +++ b/security/sandbox/chromium/base/strings/string_piece.cc @@ -5,9 +5,13 @@ #include "base/strings/string_piece.h" +#include + #include #include +#include "base/logging.h" + namespace base { namespace { @@ -433,5 +437,16 @@ StringPiece16 substr(const StringPiece16& self, return substrT(self, pos, n); } +#if DCHECK_IS_ON() +void AssertIteratorsInOrder(std::string::const_iterator begin, + std::string::const_iterator end) { + DCHECK(begin <= end) << "StringPiece iterators swapped or invalid."; +} +void AssertIteratorsInOrder(string16::const_iterator begin, + string16::const_iterator end) { + DCHECK(begin <= end) << "StringPiece iterators swapped or invalid."; +} +#endif + } // namespace internal } // namespace base diff --git a/security/sandbox/chromium/base/strings/string_piece.h b/security/sandbox/chromium/base/strings/string_piece.h index ae4511f69a93..31e7596d119c 100644 --- a/security/sandbox/chromium/base/strings/string_piece.h +++ b/security/sandbox/chromium/base/strings/string_piece.h @@ -18,12 +18,6 @@ // Both of these have the same lifetime semantics. Passing by value // generates slightly smaller code. For more discussion, Googlers can see // the thread go/stringpiecebyvalue on c-users. -// -// StringPiece16 is similar to StringPiece but for base::string16 instead of -// std::string. We do not define as large of a subset of the STL functions -// from basic_string as in StringPiece, but this can be changed if these -// functions (find, find_first_of, etc.) are found to be useful in this context. -// #ifndef BASE_STRINGS_STRING_PIECE_H_ #define BASE_STRINGS_STRING_PIECE_H_ @@ -34,10 +28,9 @@ #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/containers/hash_tables.h" +#include "base/logging.h" #include "base/strings/string16.h" -#include "mozilla/Attributes.h" namespace base { @@ -150,6 +143,14 @@ BASE_EXPORT StringPiece16 substr(const StringPiece16& self, size_t pos, size_t n); +#if DCHECK_IS_ON() +// Asserts that begin <= end to catch some errors with iterator usage. +BASE_EXPORT void AssertIteratorsInOrder(std::string::const_iterator begin, + std::string::const_iterator end); +BASE_EXPORT void AssertIteratorsInOrder(string16::const_iterator begin, + string16::const_iterator end); +#endif + } // namespace internal // BasicStringPiece ------------------------------------------------------------ @@ -187,9 +188,18 @@ template class BasicStringPiece { BasicStringPiece(const value_type* offset, size_type len) : ptr_(offset), length_(len) {} BasicStringPiece(const typename STRING_TYPE::const_iterator& begin, - const typename STRING_TYPE::const_iterator& end) - : ptr_((end > begin) ? &(*begin) : NULL), - length_((end > begin) ? (size_type)(end - begin) : 0) {} + const typename STRING_TYPE::const_iterator& end) { +#if DCHECK_IS_ON() + // This assertion is done out-of-line to avoid bringing in logging.h and + // instantiating logging macros for every instantiation. + internal::AssertIteratorsInOrder(begin, end); +#endif + length_ = static_cast(std::distance(begin, end)); + + // The length test before assignment is to avoid dereferencing an iterator + // that may point to the end() of a string. + ptr_ = length_ > 0 ? &*begin : nullptr; + } // data() may return a pointer to a buffer with embedded NULs, and the // returned buffer may or may not be null terminated. Therefore it is diff --git a/security/sandbox/chromium/base/strings/string_split.cc b/security/sandbox/chromium/base/strings/string_split.cc index 7ef4760c58f1..6c949b989a9b 100644 --- a/security/sandbox/chromium/base/strings/string_split.cc +++ b/security/sandbox/chromium/base/strings/string_split.cc @@ -4,211 +4,261 @@ #include "base/strings/string_split.h" +#include + #include "base/logging.h" #include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" namespace base { namespace { -template -void SplitStringT(const STR& str, - const typename STR::value_type s, - bool trim_whitespace, - std::vector* r) { - r->clear(); - size_t last = 0; - size_t c = str.size(); - for (size_t i = 0; i <= c; ++i) { - if (i == c || str[i] == s) { - STR tmp(str, last, i - last); - if (trim_whitespace) - TrimWhitespace(tmp, TRIM_ALL, &tmp); - // Avoid converting an empty or all-whitespace source string into a vector - // of one empty string. - if (i != c || !r->empty() || !tmp.empty()) - r->push_back(tmp); - last = i + 1; - } - } +// PieceToOutputType converts a StringPiece as needed to a given output type, +// which is either the same type of StringPiece (a NOP) or the corresponding +// non-piece string type. +// +// The default converter is a NOP, it works when the OutputType is the +// correct StringPiece. +template +OutputType PieceToOutputType(BasicStringPiece piece) { + return piece; +} +template<> // Convert StringPiece to std::string +std::string PieceToOutputType(StringPiece piece) { + return piece.as_string(); +} +template<> // Convert StringPiece16 to string16. +string16 PieceToOutputType(StringPiece16 piece) { + return piece.as_string(); } -bool SplitStringIntoKeyValue(const std::string& line, - char key_value_delimiter, - std::string* key, - std::string* value) { - key->clear(); - value->clear(); +// Returns either the ASCII or UTF-16 whitespace. +template BasicStringPiece WhitespaceForType(); +template<> StringPiece16 WhitespaceForType() { + return kWhitespaceUTF16; +} +template<> StringPiece WhitespaceForType() { + return kWhitespaceASCII; +} + +// Optimize the single-character case to call find() on the string instead, +// since this is the common case and can be made faster. This could have been +// done with template specialization too, but would have been less clear. +// +// There is no corresponding FindFirstNotOf because StringPiece already +// implements these different versions that do the optimized searching. +size_t FindFirstOf(StringPiece piece, char c, size_t pos) { + return piece.find(c, pos); +} +size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) { + return piece.find(c, pos); +} +size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) { + return piece.find_first_of(one_of, pos); +} +size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) { + return piece.find_first_of(one_of, pos); +} + +// General string splitter template. Can take 8- or 16-bit input, can produce +// the corresponding string or StringPiece output, and can take single- or +// multiple-character delimiters. +// +// DelimiterType is either a character (Str::value_type) or a string piece of +// multiple characters (BasicStringPiece). StringPiece has a version of +// find for both of these cases, and the single-character version is the most +// common and can be implemented faster, which is why this is a template. +template +static std::vector SplitStringT( + BasicStringPiece str, + DelimiterType delimiter, + WhitespaceHandling whitespace, + SplitResult result_type) { + std::vector result; + if (str.empty()) + return result; + + size_t start = 0; + while (start != Str::npos) { + size_t end = FindFirstOf(str, delimiter, start); + + BasicStringPiece piece; + if (end == Str::npos) { + piece = str.substr(start); + start = Str::npos; + } else { + piece = str.substr(start, end - start); + start = end + 1; + } + + if (whitespace == TRIM_WHITESPACE) + piece = TrimString(piece, WhitespaceForType(), TRIM_ALL); + + if (result_type == SPLIT_WANT_ALL || !piece.empty()) + result.push_back(PieceToOutputType(piece)); + } + return result; +} + +bool AppendStringKeyValue(StringPiece input, + char delimiter, + StringPairs* result) { + // Always append a new item regardless of success (it might be empty). The + // below code will copy the strings directly into the result pair. + result->resize(result->size() + 1); + auto& result_pair = result->back(); // Find the delimiter. - size_t end_key_pos = line.find_first_of(key_value_delimiter); + size_t end_key_pos = input.find_first_of(delimiter); if (end_key_pos == std::string::npos) { - DVLOG(1) << "cannot find delimiter in: " << line; - return false; // no delimiter + DVLOG(1) << "cannot find delimiter in: " << input; + return false; // No delimiter. } - key->assign(line, 0, end_key_pos); + input.substr(0, end_key_pos).CopyToString(&result_pair.first); // Find the value string. - std::string remains(line, end_key_pos, line.size() - end_key_pos); - size_t begin_value_pos = remains.find_first_not_of(key_value_delimiter); - if (begin_value_pos == std::string::npos) { - DVLOG(1) << "cannot parse value from line: " << line; - return false; // no value + StringPiece remains = input.substr(end_key_pos, input.size() - end_key_pos); + size_t begin_value_pos = remains.find_first_not_of(delimiter); + if (begin_value_pos == StringPiece::npos) { + DVLOG(1) << "cannot parse value from input: " << input; + return false; // No value. } - value->assign(remains, begin_value_pos, remains.size() - begin_value_pos); + remains.substr(begin_value_pos, remains.size() - begin_value_pos) + .CopyToString(&result_pair.second); + return true; } -template -void SplitStringUsingSubstrT(const STR& str, - const STR& s, - std::vector* r) { - r->clear(); - typename STR::size_type begin_index = 0; - while (true) { - const typename STR::size_type end_index = str.find(s, begin_index); - if (end_index == STR::npos) { - const STR term = str.substr(begin_index); - STR tmp; - TrimWhitespace(term, TRIM_ALL, &tmp); - r->push_back(tmp); - return; - } - const STR term = str.substr(begin_index, end_index - begin_index); - STR tmp; - TrimWhitespace(term, TRIM_ALL, &tmp); - r->push_back(tmp); - begin_index = end_index + s.size(); - } -} +template +void SplitStringUsingSubstrT(BasicStringPiece input, + BasicStringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type, + std::vector* result) { + using Piece = BasicStringPiece; + using size_type = typename Piece::size_type; -template -void SplitStringAlongWhitespaceT(const STR& str, std::vector* result) { result->clear(); - const size_t length = str.length(); - if (!length) - return; + for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos; + begin_index = end_index + delimiter.size()) { + end_index = input.find(delimiter, begin_index); + Piece term = end_index == Piece::npos + ? input.substr(begin_index) + : input.substr(begin_index, end_index - begin_index); - bool last_was_ws = false; - size_t last_non_ws_start = 0; - for (size_t i = 0; i < length; ++i) { - switch (str[i]) { - // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR. - case L' ': - case L'\t': - case L'\xA': - case L'\xB': - case L'\xC': - case L'\xD': - if (!last_was_ws) { - if (i > 0) { - result->push_back( - str.substr(last_non_ws_start, i - last_non_ws_start)); - } - last_was_ws = true; - } - break; + if (whitespace == TRIM_WHITESPACE) + term = TrimString(term, WhitespaceForType(), TRIM_ALL); - default: // Not a space character. - if (last_was_ws) { - last_was_ws = false; - last_non_ws_start = i; - } - break; - } - } - if (!last_was_ws) { - result->push_back( - str.substr(last_non_ws_start, length - last_non_ws_start)); + if (result_type == SPLIT_WANT_ALL || !term.empty()) + result->push_back(PieceToOutputType(term)); } } } // namespace -void SplitString(const string16& str, - char16 c, - std::vector* r) { - DCHECK(CBU16_IS_SINGLE(c)); - SplitStringT(str, c, true, r); +std::vector SplitString(StringPiece input, + StringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type) { + if (separators.size() == 1) { + return SplitStringT( + input, separators[0], whitespace, result_type); + } + return SplitStringT( + input, separators, whitespace, result_type); } -void SplitString(const std::string& str, - char c, - std::vector* r) { -#if CHAR_MIN < 0 - DCHECK(c >= 0); -#endif - DCHECK(c < 0x7F); - SplitStringT(str, c, true, r); +std::vector SplitString(StringPiece16 input, + StringPiece16 separators, + WhitespaceHandling whitespace, + SplitResult result_type) { + if (separators.size() == 1) { + return SplitStringT( + input, separators[0], whitespace, result_type); + } + return SplitStringT( + input, separators, whitespace, result_type); } -bool SplitStringIntoKeyValuePairs(const std::string& line, +std::vector SplitStringPiece(StringPiece input, + StringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type) { + if (separators.size() == 1) { + return SplitStringT( + input, separators[0], whitespace, result_type); + } + return SplitStringT( + input, separators, whitespace, result_type); +} + +std::vector SplitStringPiece(StringPiece16 input, + StringPiece16 separators, + WhitespaceHandling whitespace, + SplitResult result_type) { + if (separators.size() == 1) { + return SplitStringT( + input, separators[0], whitespace, result_type); + } + return SplitStringT( + input, separators, whitespace, result_type); +} + +bool SplitStringIntoKeyValuePairs(StringPiece input, char key_value_delimiter, char key_value_pair_delimiter, StringPairs* key_value_pairs) { key_value_pairs->clear(); - std::vector pairs; - SplitString(line, key_value_pair_delimiter, &pairs); + std::vector pairs = SplitStringPiece( + input, std::string(1, key_value_pair_delimiter), + TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY); + key_value_pairs->reserve(pairs.size()); bool success = true; - for (size_t i = 0; i < pairs.size(); ++i) { - // Don't add empty pairs into the result. - if (pairs[i].empty()) - continue; - - std::string key; - std::string value; - if (!SplitStringIntoKeyValue(pairs[i], key_value_delimiter, &key, &value)) { + for (const StringPiece& pair : pairs) { + if (!AppendStringKeyValue(pair, key_value_delimiter, key_value_pairs)) { // Don't return here, to allow for pairs without associated // value or key; just record that the split failed. success = false; } - key_value_pairs->push_back(make_pair(key, value)); } return success; } -void SplitStringUsingSubstr(const string16& str, - const string16& s, - std::vector* r) { - SplitStringUsingSubstrT(str, s, r); +void SplitStringUsingSubstr(StringPiece16 input, + StringPiece16 delimiter, + std::vector* result) { + SplitStringUsingSubstrT(input, delimiter, TRIM_WHITESPACE, SPLIT_WANT_ALL, + result); } -void SplitStringUsingSubstr(const std::string& str, - const std::string& s, - std::vector* r) { - SplitStringUsingSubstrT(str, s, r); +void SplitStringUsingSubstr(StringPiece input, + StringPiece delimiter, + std::vector* result) { + SplitStringUsingSubstrT(input, delimiter, TRIM_WHITESPACE, SPLIT_WANT_ALL, + result); } -void SplitStringDontTrim(const string16& str, - char16 c, - std::vector* r) { - DCHECK(CBU16_IS_SINGLE(c)); - SplitStringT(str, c, false, r); +std::vector SplitStringPieceUsingSubstr( + StringPiece16 input, + StringPiece16 delimiter, + WhitespaceHandling whitespace, + SplitResult result_type) { + std::vector result; + SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); + return result; } -void SplitStringDontTrim(const std::string& str, - char c, - std::vector* r) { - DCHECK(IsStringUTF8(str)); -#if CHAR_MIN < 0 - DCHECK(c >= 0); -#endif - DCHECK(c < 0x7F); - SplitStringT(str, c, false, r); -} - -void SplitStringAlongWhitespace(const string16& str, - std::vector* result) { - SplitStringAlongWhitespaceT(str, result); -} - -void SplitStringAlongWhitespace(const std::string& str, - std::vector* result) { - SplitStringAlongWhitespaceT(str, result); +std::vector SplitStringPieceUsingSubstr( + StringPiece input, + StringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type) { + std::vector result; + SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result); + return result; } } // namespace base diff --git a/security/sandbox/chromium/base/strings/string_split.h b/security/sandbox/chromium/base/strings/string_split.h index 55d8cb377edf..ec9f24604a63 100644 --- a/security/sandbox/chromium/base/strings/string_split.h +++ b/security/sandbox/chromium/base/strings/string_split.h @@ -11,71 +11,118 @@ #include "base/base_export.h" #include "base/strings/string16.h" +#include "base/strings/string_piece.h" namespace base { -// Splits |str| into a vector of strings delimited by |c|, placing the results -// in |r|. If several instances of |c| are contiguous, or if |str| begins with -// or ends with |c|, then an empty string is inserted. +enum WhitespaceHandling { + KEEP_WHITESPACE, + TRIM_WHITESPACE, +}; + +enum SplitResult { + // Strictly return all results. + // + // If the input is ",," and the separator is ',' this will return a + // vector of three empty strings. + SPLIT_WANT_ALL, + + // Only nonempty results will be added to the results. Multiple separators + // will be coalesced. Separators at the beginning and end of the input will + // be ignored. With TRIM_WHITESPACE, whitespace-only results will be dropped. + // + // If the input is ",," and the separator is ',', this will return an empty + // vector. + SPLIT_WANT_NONEMPTY, +}; + +// Split the given string on ANY of the given separators, returning copies of +// the result. // -// Every substring is trimmed of any leading or trailing white space. -// NOTE: |c| must be in BMP (Basic Multilingual Plane) -BASE_EXPORT void SplitString(const string16& str, - char16 c, - std::vector* r); +// To split on either commas or semicolons, keeping all whitespace: +// +// std::vector tokens = base::SplitString( +// input, ",;", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); +BASE_EXPORT std::vector SplitString( + StringPiece input, + StringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type); +BASE_EXPORT std::vector SplitString( + StringPiece16 input, + StringPiece16 separators, + WhitespaceHandling whitespace, + SplitResult result_type); -// |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which -// the trailing byte of a multi-byte character can be in the ASCII range. -// UTF-8, and other single/multi-byte ASCII-compatible encodings are OK. -// Note: |c| must be in the ASCII range. -BASE_EXPORT void SplitString(const std::string& str, - char c, - std::vector* r); +// Like SplitString above except it returns a vector of StringPieces which +// reference the original buffer without copying. Although you have to be +// careful to keep the original string unmodified, this provides an efficient +// way to iterate through tokens in a string. +// +// To iterate through all whitespace-separated tokens in an input string: +// +// for (const auto& cur : +// base::SplitStringPiece(input, base::kWhitespaceASCII, +// base::KEEP_WHITESPACE, +// base::SPLIT_WANT_NONEMPTY)) { +// ... +BASE_EXPORT std::vector SplitStringPiece( + StringPiece input, + StringPiece separators, + WhitespaceHandling whitespace, + SplitResult result_type); +BASE_EXPORT std::vector SplitStringPiece( + StringPiece16 input, + StringPiece16 separators, + WhitespaceHandling whitespace, + SplitResult result_type); -typedef std::vector > StringPairs; +using StringPairs = std::vector>; // Splits |line| into key value pairs according to the given delimiters and // removes whitespace leading each key and trailing each value. Returns true // only if each pair has a non-empty key and value. |key_value_pairs| will // include ("","") pairs for entries without |key_value_delimiter|. -BASE_EXPORT bool SplitStringIntoKeyValuePairs(const std::string& line, +BASE_EXPORT bool SplitStringIntoKeyValuePairs(StringPiece input, char key_value_delimiter, char key_value_pair_delimiter, StringPairs* key_value_pairs); -// The same as SplitString, but use a substring delimiter instead of a char. -BASE_EXPORT void SplitStringUsingSubstr(const string16& str, - const string16& s, - std::vector* r); -BASE_EXPORT void SplitStringUsingSubstr(const std::string& str, - const std::string& s, - std::vector* r); - -// The same as SplitString, but don't trim white space. -// NOTE: |c| must be in BMP (Basic Multilingual Plane) -BASE_EXPORT void SplitStringDontTrim(const string16& str, - char16 c, - std::vector* r); -// |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which -// the trailing byte of a multi-byte character can be in the ASCII range. -// UTF-8, and other single/multi-byte ASCII-compatible encodings are OK. -// Note: |c| must be in the ASCII range. -BASE_EXPORT void SplitStringDontTrim(const std::string& str, - char c, - std::vector* r); - -// WARNING: this uses whitespace as defined by the HTML5 spec. If you need -// a function similar to this but want to trim all types of whitespace, then -// factor this out into a function that takes a string containing the characters -// that are treated as whitespace. +// Similar to SplitString, but use a substring delimiter instead of a list of +// characters that are all possible delimiters. // -// Splits the string along whitespace (where whitespace is the five space -// characters defined by HTML 5). Each contiguous block of non-whitespace -// characters is added to result. -BASE_EXPORT void SplitStringAlongWhitespace(const string16& str, - std::vector* result); -BASE_EXPORT void SplitStringAlongWhitespace(const std::string& str, - std::vector* result); +// TODO(brettw) this should probably be changed and expanded to provide a +// mirror of the SplitString[Piece] API above, just with the different +// delimiter handling. +BASE_EXPORT void SplitStringUsingSubstr(StringPiece16 input, + StringPiece16 delimiter, + std::vector* result); +BASE_EXPORT void SplitStringUsingSubstr(StringPiece input, + StringPiece delimiter, + std::vector* result); + +// Like SplitStringUsingSubstr above except it returns a vector of StringPieces +// which reference the original buffer without copying. Although you have to be +// careful to keep the original string unmodified, this provides an efficient +// way to iterate through tokens in a string. +// +// To iterate through all newline-separated tokens in an input string: +// +// for (const auto& cur : +// base::SplitStringUsingSubstr(input, "\r\n", +// base::KEEP_WHITESPACE, +// base::SPLIT_WANT_NONEMPTY)) { +// ... +BASE_EXPORT std::vector SplitStringPieceUsingSubstr( + StringPiece16 input, + StringPiece16 delimiter, + WhitespaceHandling whitespace, + SplitResult result_type); +BASE_EXPORT std::vector SplitStringPieceUsingSubstr( + StringPiece input, + StringPiece delimiter, + WhitespaceHandling whitespace, + SplitResult result_type); } // namespace base diff --git a/security/sandbox/chromium/base/strings/string_util.cc b/security/sandbox/chromium/base/strings/string_util.cc index 43f15a360d93..e8000abd40bc 100644 --- a/security/sandbox/chromium/base/strings/string_util.cc +++ b/security/sandbox/chromium/base/strings/string_util.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,19 +17,19 @@ #include #include +#include #include -#include "base/basictypes.h" #include "base/logging.h" +#include "base/macros.h" #include "base/memory/singleton.h" +#include "base/strings/string_split.h" #include "base/strings/utf_string_conversion_utils.h" #include "base/strings/utf_string_conversions.h" #include "base/third_party/icu/icu_utf.h" #include "build/build_config.h" -// Remove when this entire file is in the base namespace. -using base::char16; -using base::string16; +namespace base { namespace { @@ -79,13 +80,13 @@ template inline T* AlignToMachineWord(T* pointer) { } template struct NonASCIIMask; -template<> struct NonASCIIMask<4, base::char16> { +template<> struct NonASCIIMask<4, char16> { static inline uint32_t value() { return 0xFF80FF80U; } }; template<> struct NonASCIIMask<4, char> { static inline uint32_t value() { return 0x80808080U; } }; -template<> struct NonASCIIMask<8, base::char16> { +template<> struct NonASCIIMask<8, char16> { static inline uint64_t value() { return 0xFF80FF80FF80FF80ULL; } }; template<> struct NonASCIIMask<8, char> { @@ -102,8 +103,6 @@ template<> struct NonASCIIMask<8, wchar_t> { } // namespace -namespace base { - bool IsWprintfFormatPortable(const wchar_t* format) { for (const wchar_t* position = format; *position != '\0'; ++position) { if (*position == '%') { @@ -139,6 +138,91 @@ bool IsWprintfFormatPortable(const wchar_t* format) { return true; } +namespace { + +template +StringType ToLowerASCIIImpl(BasicStringPiece str) { + StringType ret; + ret.reserve(str.size()); + for (size_t i = 0; i < str.size(); i++) + ret.push_back(ToLowerASCII(str[i])); + return ret; +} + +template +StringType ToUpperASCIIImpl(BasicStringPiece str) { + StringType ret; + ret.reserve(str.size()); + for (size_t i = 0; i < str.size(); i++) + ret.push_back(ToUpperASCII(str[i])); + return ret; +} + +} // namespace + +std::string ToLowerASCII(StringPiece str) { + return ToLowerASCIIImpl(str); +} + +string16 ToLowerASCII(StringPiece16 str) { + return ToLowerASCIIImpl(str); +} + +std::string ToUpperASCII(StringPiece str) { + return ToUpperASCIIImpl(str); +} + +string16 ToUpperASCII(StringPiece16 str) { + return ToUpperASCIIImpl(str); +} + +template +int CompareCaseInsensitiveASCIIT(BasicStringPiece a, + BasicStringPiece b) { + // Find the first characters that aren't equal and compare them. If the end + // of one of the strings is found before a nonequal character, the lengths + // of the strings are compared. + size_t i = 0; + while (i < a.length() && i < b.length()) { + typename StringType::value_type lower_a = ToLowerASCII(a[i]); + typename StringType::value_type lower_b = ToLowerASCII(b[i]); + if (lower_a < lower_b) + return -1; + if (lower_a > lower_b) + return 1; + i++; + } + + // End of one string hit before finding a different character. Expect the + // common case to be "strings equal" at this point so check that first. + if (a.length() == b.length()) + return 0; + + if (a.length() < b.length()) + return -1; + return 1; +} + +int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b) { + return CompareCaseInsensitiveASCIIT(a, b); +} + +int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) { + return CompareCaseInsensitiveASCIIT(a, b); +} + +bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) { + if (a.length() != b.length()) + return false; + return CompareCaseInsensitiveASCIIT(a, b) == 0; +} + +bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) { + if (a.length() != b.length()) + return false; + return CompareCaseInsensitiveASCIIT(a, b) == 0; +} + const std::string& EmptyString() { return EmptyStrings::GetInstance()->s; } @@ -168,54 +252,58 @@ bool ReplaceCharsT(const STR& input, } bool ReplaceChars(const string16& input, - const base::StringPiece16& replace_chars, + const StringPiece16& replace_chars, const string16& replace_with, string16* output) { return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); } bool ReplaceChars(const std::string& input, - const base::StringPiece& replace_chars, + const StringPiece& replace_chars, const std::string& replace_with, std::string* output) { return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output); } bool RemoveChars(const string16& input, - const base::StringPiece16& remove_chars, + const StringPiece16& remove_chars, string16* output) { return ReplaceChars(input, remove_chars.as_string(), string16(), output); } bool RemoveChars(const std::string& input, - const base::StringPiece& remove_chars, + const StringPiece& remove_chars, std::string* output) { return ReplaceChars(input, remove_chars.as_string(), std::string(), output); } -template -TrimPositions TrimStringT(const STR& input, - const STR& trim_chars, +template +TrimPositions TrimStringT(const Str& input, + BasicStringPiece trim_chars, TrimPositions positions, - STR* output) { - // Find the edges of leading/trailing whitespace as desired. + Str* output) { + // Find the edges of leading/trailing whitespace as desired. Need to use + // a StringPiece version of input to be able to call find* on it with the + // StringPiece version of trim_chars (normally the trim_chars will be a + // constant so avoid making a copy). + BasicStringPiece input_piece(input); const size_t last_char = input.length() - 1; const size_t first_good_char = (positions & TRIM_LEADING) ? - input.find_first_not_of(trim_chars) : 0; + input_piece.find_first_not_of(trim_chars) : 0; const size_t last_good_char = (positions & TRIM_TRAILING) ? - input.find_last_not_of(trim_chars) : last_char; + input_piece.find_last_not_of(trim_chars) : last_char; - // When the string was all whitespace, report that we stripped off whitespace - // from whichever position the caller was interested in. For empty input, we - // stripped no whitespace, but we still need to clear |output|. + // When the string was all trimmed, report that we stripped off characters + // from whichever position the caller was interested in. For empty input, we + // stripped no characters, but we still need to clear |output|. if (input.empty() || - (first_good_char == STR::npos) || (last_good_char == STR::npos)) { + (first_good_char == Str::npos) || (last_good_char == Str::npos)) { bool input_was_empty = input.empty(); // in case output == &input output->clear(); return input_was_empty ? TRIM_NONE : positions; } - // Trim the whitespace. + // Trim. *output = input.substr(first_good_char, last_good_char - first_good_char + 1); @@ -226,17 +314,38 @@ TrimPositions TrimStringT(const STR& input, } bool TrimString(const string16& input, - const base::StringPiece16& trim_chars, + StringPiece16 trim_chars, string16* output) { - return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) != - TRIM_NONE; + return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; } bool TrimString(const std::string& input, - const base::StringPiece& trim_chars, + StringPiece trim_chars, std::string* output) { - return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) != - TRIM_NONE; + return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; +} + +template +BasicStringPiece TrimStringPieceT(BasicStringPiece input, + BasicStringPiece trim_chars, + TrimPositions positions) { + size_t begin = (positions & TRIM_LEADING) ? + input.find_first_not_of(trim_chars) : 0; + size_t end = (positions & TRIM_TRAILING) ? + input.find_last_not_of(trim_chars) + 1 : input.size(); + return input.substr(begin, end - begin); +} + +StringPiece16 TrimString(StringPiece16 input, + const StringPiece16& trim_chars, + TrimPositions positions) { + return TrimStringPieceT(input, trim_chars, positions); +} + +StringPiece TrimString(StringPiece input, + const StringPiece& trim_chars, + TrimPositions positions) { + return TrimStringPieceT(input, trim_chars, positions); } void TruncateUTF8ToByteSize(const std::string& input, @@ -247,10 +356,11 @@ void TruncateUTF8ToByteSize(const std::string& input, *output = input; return; } - DCHECK_LE(byte_size, static_cast(kint32max)); - // Note: This cast is necessary because CBU8_NEXT uses int32s. - int32 truncation_length = static_cast(byte_size); - int32 char_index = truncation_length - 1; + DCHECK_LE(byte_size, + static_cast(std::numeric_limits::max())); + // Note: This cast is necessary because CBU8_NEXT uses int32_ts. + int32_t truncation_length = static_cast(byte_size); + int32_t char_index = truncation_length - 1; const char* data = input.data(); // Using CBU8, we will move backwards from the truncation point @@ -258,7 +368,7 @@ void TruncateUTF8ToByteSize(const std::string& input, // character. Once a full UTF8 character is found, we will // truncate the string to the end of that character. while (char_index >= 0) { - int32 prev = char_index; + int32_t prev = char_index; base_icu::UChar32 code_point = 0; CBU8_NEXT(data, char_index, truncation_length, code_point); if (!IsValidCharacter(code_point) || @@ -278,22 +388,22 @@ void TruncateUTF8ToByteSize(const std::string& input, TrimPositions TrimWhitespace(const string16& input, TrimPositions positions, string16* output) { - return TrimStringT(input, base::string16(kWhitespaceUTF16), positions, - output); + return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output); +} + +StringPiece16 TrimWhitespace(StringPiece16 input, + TrimPositions positions) { + return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions); } TrimPositions TrimWhitespaceASCII(const std::string& input, TrimPositions positions, std::string* output) { - return TrimStringT(input, std::string(kWhitespaceASCII), positions, output); + return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output); } -// This function is only for backward-compatibility. -// To be removed when all callers are updated. -TrimPositions TrimWhitespace(const std::string& input, - TrimPositions positions, - std::string* output) { - return TrimWhitespaceASCII(input, positions, output); +StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) { + return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions); } template @@ -309,7 +419,7 @@ STR CollapseWhitespaceT(const STR& text, int chars_written = 0; for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) { - if (IsWhitespace(*i)) { + if (IsUnicodeWhitespace(*i)) { if (!in_whitespace) { // Reduce all whitespace sequences to a single space. in_whitespace = true; @@ -406,13 +516,13 @@ bool IsStringASCII(const std::wstring& str) { } #endif -bool IsStringUTF8(const std::string& str) { +bool IsStringUTF8(const StringPiece& str) { const char *src = str.data(); - int32 src_len = static_cast(str.length()); - int32 char_index = 0; + int32_t src_len = static_cast(str.length()); + int32_t char_index = 0; while (char_index < src_len) { - int32 code_point; + int32_t code_point; CBU8_NEXT(src, char_index, src_len, code_point); if (!IsValidCharacter(code_point)) return false; @@ -420,109 +530,140 @@ bool IsStringUTF8(const std::string& str) { return true; } -} // namespace base - -template -static inline bool DoLowerCaseEqualsASCII(Iter a_begin, - Iter a_end, - const char* b) { - for (Iter it = a_begin; it != a_end; ++it, ++b) { - if (!*b || base::ToLowerASCII(*it) != *b) +// Implementation note: Normally this function will be called with a hardcoded +// constant for the lowercase_ascii parameter. Constructing a StringPiece from +// a C constant requires running strlen, so the result will be two passes +// through the buffers, one to file the length of lowercase_ascii, and one to +// compare each letter. +// +// This function could have taken a const char* to avoid this and only do one +// pass through the string. But the strlen is faster than the case-insensitive +// compares and lets us early-exit in the case that the strings are different +// lengths (will often be the case for non-matches). So whether one approach or +// the other will be faster depends on the case. +// +// The hardcoded strings are typically very short so it doesn't matter, and the +// string piece gives additional flexibility for the caller (doesn't have to be +// null terminated) so we choose the StringPiece route. +template +static inline bool DoLowerCaseEqualsASCII(BasicStringPiece str, + StringPiece lowercase_ascii) { + if (str.size() != lowercase_ascii.size()) + return false; + for (size_t i = 0; i < str.size(); i++) { + if (ToLowerASCII(str[i]) != lowercase_ascii[i]) return false; } - return *b == 0; + return true; } -// Front-ends for LowerCaseEqualsASCII. -bool LowerCaseEqualsASCII(const std::string& a, const char* b) { - return DoLowerCaseEqualsASCII(a.begin(), a.end(), b); +bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) { + return DoLowerCaseEqualsASCII(str, lowercase_ascii); } -bool LowerCaseEqualsASCII(const string16& a, const char* b) { - return DoLowerCaseEqualsASCII(a.begin(), a.end(), b); +bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowercase_ascii) { + return DoLowerCaseEqualsASCII(str, lowercase_ascii); } -bool LowerCaseEqualsASCII(std::string::const_iterator a_begin, - std::string::const_iterator a_end, - const char* b) { - return DoLowerCaseEqualsASCII(a_begin, a_end, b); -} - -bool LowerCaseEqualsASCII(string16::const_iterator a_begin, - string16::const_iterator a_end, - const char* b) { - return DoLowerCaseEqualsASCII(a_begin, a_end, b); -} - -// TODO(port): Resolve wchar_t/iterator issues that require OS_ANDROID here. -#if !defined(OS_ANDROID) -bool LowerCaseEqualsASCII(const char* a_begin, - const char* a_end, - const char* b) { - return DoLowerCaseEqualsASCII(a_begin, a_end, b); -} - -bool LowerCaseEqualsASCII(const char16* a_begin, - const char16* a_end, - const char* b) { - return DoLowerCaseEqualsASCII(a_begin, a_end, b); -} - -#endif // !defined(OS_ANDROID) - -bool EqualsASCII(const string16& a, const base::StringPiece& b) { - if (a.length() != b.length()) +bool EqualsASCII(StringPiece16 str, StringPiece ascii) { + if (str.length() != ascii.length()) return false; - return std::equal(b.begin(), b.end(), a.begin()); + return std::equal(ascii.begin(), ascii.end(), str.begin()); } -bool StartsWithASCII(const std::string& str, - const std::string& search, - bool case_sensitive) { - if (case_sensitive) - return str.compare(0, search.length(), search) == 0; - else - return base::strncasecmp(str.c_str(), search.c_str(), search.length()) == 0; -} +template +bool StartsWithT(BasicStringPiece str, + BasicStringPiece search_for, + CompareCase case_sensitivity) { + if (search_for.size() > str.size()) + return false; -template -bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) { - if (case_sensitive) { - return str.compare(0, search.length(), search) == 0; - } else { - if (search.size() > str.size()) + BasicStringPiece source = str.substr(0, search_for.size()); + + switch (case_sensitivity) { + case CompareCase::SENSITIVE: + return source == search_for; + + case CompareCase::INSENSITIVE_ASCII: + return std::equal( + search_for.begin(), search_for.end(), + source.begin(), + CaseInsensitiveCompareASCII()); + + default: + NOTREACHED(); return false; - return std::equal(search.begin(), search.end(), str.begin(), - base::CaseInsensitiveCompare()); } } -bool StartsWith(const string16& str, const string16& search, - bool case_sensitive) { - return StartsWithT(str, search, case_sensitive); +bool StartsWith(StringPiece str, + StringPiece search_for, + CompareCase case_sensitivity) { + return StartsWithT(str, search_for, case_sensitivity); } -template -bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) { - size_t str_length = str.length(); - size_t search_length = search.length(); - if (search_length > str_length) +bool StartsWith(StringPiece16 str, + StringPiece16 search_for, + CompareCase case_sensitivity) { + return StartsWithT(str, search_for, case_sensitivity); +} + +template +bool EndsWithT(BasicStringPiece str, + BasicStringPiece search_for, + CompareCase case_sensitivity) { + if (search_for.size() > str.size()) return false; - if (case_sensitive) - return str.compare(str_length - search_length, search_length, search) == 0; - return std::equal(search.begin(), search.end(), - str.begin() + (str_length - search_length), - base::CaseInsensitiveCompare()); + + BasicStringPiece source = str.substr(str.size() - search_for.size(), + search_for.size()); + + switch (case_sensitivity) { + case CompareCase::SENSITIVE: + return source == search_for; + + case CompareCase::INSENSITIVE_ASCII: + return std::equal( + source.begin(), source.end(), + search_for.begin(), + CaseInsensitiveCompareASCII()); + + default: + NOTREACHED(); + return false; + } } -bool EndsWith(const std::string& str, const std::string& search, - bool case_sensitive) { - return EndsWithT(str, search, case_sensitive); +bool EndsWith(StringPiece str, + StringPiece search_for, + CompareCase case_sensitivity) { + return EndsWithT(str, search_for, case_sensitivity); } -bool EndsWith(const string16& str, const string16& search, - bool case_sensitive) { - return EndsWithT(str, search, case_sensitive); +bool EndsWith(StringPiece16 str, + StringPiece16 search_for, + CompareCase case_sensitivity) { + return EndsWithT(str, search_for, case_sensitivity); +} + +char HexDigitToInt(wchar_t c) { + DCHECK(IsHexDigit(c)); + if (c >= '0' && c <= '9') + return static_cast(c - '0'); + if (c >= 'A' && c <= 'F') + return static_cast(c - 'A' + 10); + if (c >= 'a' && c <= 'f') + return static_cast(c - 'a' + 10); + return 0; +} + +bool IsUnicodeWhitespace(wchar_t c) { + // kWhitespaceWide is a NULL-terminated string + for (const wchar_t* cur = kWhitespaceWide; *cur; ++cur) { + if (*cur == c) + return true; + } + return false; } static const char* const kByteStringsUnlocalized[] = { @@ -534,7 +675,7 @@ static const char* const kByteStringsUnlocalized[] = { " PB" }; -string16 FormatBytesUnlocalized(int64 bytes) { +string16 FormatBytesUnlocalized(int64_t bytes) { double unit_amount = static_cast(bytes); size_t dimension = 0; const int kKilo = 1024; @@ -553,153 +694,210 @@ string16 FormatBytesUnlocalized(int64 bytes) { kByteStringsUnlocalized[dimension]); } - return base::ASCIIToUTF16(buf); + return ASCIIToUTF16(buf); } +// Runs in O(n) time in the length of |str|. template void DoReplaceSubstringsAfterOffset(StringType* str, - size_t start_offset, - const StringType& find_this, - const StringType& replace_with, + size_t offset, + BasicStringPiece find_this, + BasicStringPiece replace_with, bool replace_all) { - if ((start_offset == StringType::npos) || (start_offset >= str->length())) + DCHECK(!find_this.empty()); + + // If the find string doesn't appear, there's nothing to do. + offset = str->find(find_this.data(), offset, find_this.size()); + if (offset == StringType::npos) return; - DCHECK(!find_this.empty()); - for (size_t offs(str->find(find_this, start_offset)); - offs != StringType::npos; offs = str->find(find_this, offs)) { - str->replace(offs, find_this.length(), replace_with); - offs += replace_with.length(); + // If we're only replacing one instance, there's no need to do anything + // complicated. + size_t find_length = find_this.length(); + if (!replace_all) { + str->replace(offset, find_length, replace_with.data(), replace_with.size()); + return; + } - if (!replace_all) - break; + // If the find and replace strings are the same length, we can simply use + // replace() on each instance, and finish the entire operation in O(n) time. + size_t replace_length = replace_with.length(); + if (find_length == replace_length) { + do { + str->replace(offset, find_length, + replace_with.data(), replace_with.size()); + offset = str->find(find_this.data(), offset + replace_length, + find_this.size()); + } while (offset != StringType::npos); + return; + } + + // Since the find and replace strings aren't the same length, a loop like the + // one above would be O(n^2) in the worst case, as replace() will shift the + // entire remaining string each time. We need to be more clever to keep + // things O(n). + // + // If we're shortening the string, we can alternate replacements with shifting + // forward the intervening characters using memmove(). + size_t str_length = str->length(); + if (find_length > replace_length) { + size_t write_offset = offset; + do { + if (replace_length) { + str->replace(write_offset, replace_length, + replace_with.data(), replace_with.size()); + write_offset += replace_length; + } + size_t read_offset = offset + find_length; + offset = std::min( + str->find(find_this.data(), read_offset, find_this.size()), + str_length); + size_t length = offset - read_offset; + if (length) { + memmove(&(*str)[write_offset], &(*str)[read_offset], + length * sizeof(typename StringType::value_type)); + write_offset += length; + } + } while (offset < str_length); + str->resize(write_offset); + return; + } + + // We're lengthening the string. We can use alternating replacements and + // memmove() calls like above, but we need to precalculate the final string + // length and then expand from back-to-front to avoid overwriting the string + // as we're reading it, needing to shift, or having to copy to a second string + // temporarily. + size_t first_match = offset; + + // First, calculate the final length and resize the string. + size_t final_length = str_length; + size_t expansion = replace_length - find_length; + size_t current_match; + do { + final_length += expansion; + // Minor optimization: save this offset into |current_match|, so that on + // exit from the loop, |current_match| will point at the last instance of + // the find string, and we won't need to find() it again immediately. + current_match = offset; + offset = str->find(find_this.data(), offset + find_length, + find_this.size()); + } while (offset != StringType::npos); + str->resize(final_length); + + // Now do the replacement loop, working backwards through the string. + for (size_t prev_match = str_length, write_offset = final_length; ; + current_match = str->rfind(find_this.data(), current_match - 1, + find_this.size())) { + size_t read_offset = current_match + find_length; + size_t length = prev_match - read_offset; + if (length) { + write_offset -= length; + memmove(&(*str)[write_offset], &(*str)[read_offset], + length * sizeof(typename StringType::value_type)); + } + write_offset -= replace_length; + str->replace(write_offset, replace_length, + replace_with.data(), replace_with.size()); + if (current_match == first_match) + return; + prev_match = current_match; } } void ReplaceFirstSubstringAfterOffset(string16* str, size_t start_offset, - const string16& find_this, - const string16& replace_with) { - DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, - false); // replace first instance + StringPiece16 find_this, + StringPiece16 replace_with) { + DoReplaceSubstringsAfterOffset( + str, start_offset, find_this, replace_with, false); // Replace first. } void ReplaceFirstSubstringAfterOffset(std::string* str, size_t start_offset, - const std::string& find_this, - const std::string& replace_with) { - DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, - false); // replace first instance + StringPiece find_this, + StringPiece replace_with) { + DoReplaceSubstringsAfterOffset( + str, start_offset, find_this, replace_with, false); // Replace first. } void ReplaceSubstringsAfterOffset(string16* str, size_t start_offset, - const string16& find_this, - const string16& replace_with) { - DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, - true); // replace all instances + StringPiece16 find_this, + StringPiece16 replace_with) { + DoReplaceSubstringsAfterOffset( + str, start_offset, find_this, replace_with, true); // Replace all. } void ReplaceSubstringsAfterOffset(std::string* str, size_t start_offset, - const std::string& find_this, - const std::string& replace_with) { - DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, - true); // replace all instances + StringPiece find_this, + StringPiece replace_with) { + DoReplaceSubstringsAfterOffset( + str, start_offset, find_this, replace_with, true); // Replace all. } - -template -static size_t TokenizeT(const STR& str, - const STR& delimiters, - std::vector* tokens) { - tokens->clear(); - - size_t start = str.find_first_not_of(delimiters); - while (start != STR::npos) { - size_t end = str.find_first_of(delimiters, start + 1); - if (end == STR::npos) { - tokens->push_back(str.substr(start)); - break; - } else { - tokens->push_back(str.substr(start, end - start)); - start = str.find_first_not_of(delimiters, end + 1); - } - } - - return tokens->size(); +template +inline typename string_type::value_type* WriteIntoT(string_type* str, + size_t length_with_null) { + DCHECK_GT(length_with_null, 1u); + str->reserve(length_with_null); + str->resize(length_with_null - 1); + return &((*str)[0]); } -size_t Tokenize(const string16& str, - const string16& delimiters, - std::vector* tokens) { - return TokenizeT(str, delimiters, tokens); +char* WriteInto(std::string* str, size_t length_with_null) { + return WriteIntoT(str, length_with_null); } -size_t Tokenize(const std::string& str, - const std::string& delimiters, - std::vector* tokens) { - return TokenizeT(str, delimiters, tokens); -} - -size_t Tokenize(const base::StringPiece& str, - const base::StringPiece& delimiters, - std::vector* tokens) { - return TokenizeT(str, delimiters, tokens); +char16* WriteInto(string16* str, size_t length_with_null) { + return WriteIntoT(str, length_with_null); } template -static STR JoinStringT(const std::vector& parts, const STR& sep) { +static STR JoinStringT(const std::vector& parts, + BasicStringPiece sep) { if (parts.empty()) return STR(); STR result(parts[0]); - typename std::vector::const_iterator iter = parts.begin(); + auto iter = parts.begin(); ++iter; for (; iter != parts.end(); ++iter) { - result += sep; + sep.AppendToString(&result); result += *iter; } return result; } -std::string JoinString(const std::vector& parts, char sep) { - return JoinStringT(parts, std::string(1, sep)); -} - -string16 JoinString(const std::vector& parts, char16 sep) { - return JoinStringT(parts, string16(1, sep)); -} - std::string JoinString(const std::vector& parts, - const std::string& separator) { + StringPiece separator) { return JoinStringT(parts, separator); } string16 JoinString(const std::vector& parts, - const string16& separator) { + StringPiece16 separator) { return JoinStringT(parts, separator); } template -OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string, - const std::vector& subst, std::vector* offsets) { +OutStringType DoReplaceStringPlaceholders( + const FormatStringType& format_string, + const std::vector& subst, + std::vector* offsets) { size_t substitutions = subst.size(); size_t sub_length = 0; - for (typename std::vector::const_iterator iter = subst.begin(); - iter != subst.end(); ++iter) { - sub_length += iter->length(); - } + for (const auto& cur : subst) + sub_length += cur.length(); OutStringType formatted; formatted.reserve(format_string.length() + sub_length); std::vector r_offsets; - for (typename FormatStringType::const_iterator i = format_string.begin(); - i != format_string.end(); ++i) { + for (auto i = format_string.begin(); i != format_string.end(); ++i) { if ('$' == *i) { if (i + 1 != format_string.end()) { ++i; @@ -737,10 +935,8 @@ OutStringType DoReplaceStringPlaceholders(const FormatStringType& format_string, } } if (offsets) { - for (std::vector::const_iterator i = r_offsets.begin(); - i != r_offsets.end(); ++i) { - offsets->push_back(i->offset); - } + for (const auto& cur : r_offsets) + offsets->push_back(cur.offset); } return formatted; } @@ -751,7 +947,7 @@ string16 ReplaceStringPlaceholders(const string16& format_string, return DoReplaceStringPlaceholders(format_string, subst, offsets); } -std::string ReplaceStringPlaceholders(const base::StringPiece& format_string, +std::string ReplaceStringPlaceholders(const StringPiece& format_string, const std::vector& subst, std::vector* offsets) { return DoReplaceStringPlaceholders(format_string, subst, offsets); @@ -771,161 +967,6 @@ string16 ReplaceStringPlaceholders(const string16& format_string, return result; } -static bool IsWildcard(base_icu::UChar32 character) { - return character == '*' || character == '?'; -} - -// Move the strings pointers to the point where they start to differ. -template -static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end, - const CHAR** string, const CHAR* string_end, - NEXT next) { - const CHAR* escape = NULL; - while (*pattern != pattern_end && *string != string_end) { - if (!escape && IsWildcard(**pattern)) { - // We don't want to match wildcard here, except if it's escaped. - return; - } - - // Check if the escapement char is found. If so, skip it and move to the - // next character. - if (!escape && **pattern == '\\') { - escape = *pattern; - next(pattern, pattern_end); - continue; - } - - // Check if the chars match, if so, increment the ptrs. - const CHAR* pattern_next = *pattern; - const CHAR* string_next = *string; - base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end); - if (pattern_char == next(&string_next, string_end) && - pattern_char != CBU_SENTINEL) { - *pattern = pattern_next; - *string = string_next; - } else { - // Uh oh, it did not match, we are done. If the last char was an - // escapement, that means that it was an error to advance the ptr here, - // let's put it back where it was. This also mean that the MatchPattern - // function will return false because if we can't match an escape char - // here, then no one will. - if (escape) { - *pattern = escape; - } - return; - } - - escape = NULL; - } -} - -template -static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) { - while (*pattern != end) { - if (!IsWildcard(**pattern)) - return; - next(pattern, end); - } -} - -template -static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end, - const CHAR* pattern, const CHAR* pattern_end, - int depth, - NEXT next) { - const int kMaxDepth = 16; - if (depth > kMaxDepth) - return false; - - // Eat all the matching chars. - EatSameChars(&pattern, pattern_end, &eval, eval_end, next); - - // If the string is empty, then the pattern must be empty too, or contains - // only wildcards. - if (eval == eval_end) { - EatWildcard(&pattern, pattern_end, next); - return pattern == pattern_end; - } - - // Pattern is empty but not string, this is not a match. - if (pattern == pattern_end) - return false; - - // If this is a question mark, then we need to compare the rest with - // the current string or the string with one character eaten. - const CHAR* next_pattern = pattern; - next(&next_pattern, pattern_end); - if (pattern[0] == '?') { - if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, - depth + 1, next)) - return true; - const CHAR* next_eval = eval; - next(&next_eval, eval_end); - if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end, - depth + 1, next)) - return true; - } - - // This is a *, try to match all the possible substrings with the remainder - // of the pattern. - if (pattern[0] == '*') { - // Collapse duplicate wild cards (********** into *) so that the - // method does not recurse unnecessarily. http://crbug.com/52839 - EatWildcard(&next_pattern, pattern_end, next); - - while (eval != eval_end) { - if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, - depth + 1, next)) - return true; - eval++; - } - - // We reached the end of the string, let see if the pattern contains only - // wildcards. - if (eval == eval_end) { - EatWildcard(&pattern, pattern_end, next); - if (pattern != pattern_end) - return false; - return true; - } - } - - return false; -} - -struct NextCharUTF8 { - base_icu::UChar32 operator()(const char** p, const char* end) { - base_icu::UChar32 c; - int offset = 0; - CBU8_NEXT(*p, offset, end - *p, c); - *p += offset; - return c; - } -}; - -struct NextCharUTF16 { - base_icu::UChar32 operator()(const char16** p, const char16* end) { - base_icu::UChar32 c; - int offset = 0; - CBU16_NEXT(*p, offset, end - *p, c); - *p += offset; - return c; - } -}; - -bool MatchPattern(const base::StringPiece& eval, - const base::StringPiece& pattern) { - return MatchPatternT(eval.data(), eval.data() + eval.size(), - pattern.data(), pattern.data() + pattern.size(), - 0, NextCharUTF8()); -} - -bool MatchPattern(const string16& eval, const string16& pattern) { - return MatchPatternT(eval.c_str(), eval.c_str() + eval.size(), - pattern.c_str(), pattern.c_str() + pattern.size(), - 0, NextCharUTF16()); -} - // The following code is compatible with the OpenBSD lcpy interface. See: // http://www.gratisoft.us/todd/papers/strlcpy.html // ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c @@ -950,9 +991,11 @@ size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { } // namespace -size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { +size_t strlcpy(char* dst, const char* src, size_t dst_size) { return lcpyT(dst, src, dst_size); } -size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { +size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { return lcpyT(dst, src, dst_size); } + +} // namespace base diff --git a/security/sandbox/chromium/base/strings/string_util.h b/security/sandbox/chromium/base/strings/string_util.h index 1e2ac700e100..e369f294d0ba 100644 --- a/security/sandbox/chromium/base/strings/string_util.h +++ b/security/sandbox/chromium/base/strings/string_util.h @@ -9,35 +9,24 @@ #include #include // va_list +#include +#include #include #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" // For implicit conversions. +#include "build/build_config.h" namespace base { -// C standard-library functions like "strncasecmp" and "snprintf" that aren't -// cross-platform are provided as "base::strncasecmp", and their prototypes -// are listed below. These functions are then implemented as inline calls -// to the platform-specific equivalents in the platform-specific headers. - -// Compares the two strings s1 and s2 without regard to case using -// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if -// s2 > s1 according to a lexicographic comparison. -int strcasecmp(const char* s1, const char* s2); - -// Compares up to count characters of s1 and s2 without regard to case using -// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if -// s2 > s1 according to a lexicographic comparison. -int strncasecmp(const char* s1, const char* s2, size_t count); - -// Same as strncmp but for char16 strings. -int strncmp16(const char16* s1, const char16* s2, size_t count); +// C standard-library functions that aren't cross-platform are provided as +// "base::...", and their prototypes are listed below. These functions are +// then implemented as inline calls to the platform-specific equivalents in the +// platform-specific headers. // Wrapper for vsnprintf that always null-terminates and always returns the // number of characters that would be in an untruncated formatted @@ -49,9 +38,14 @@ int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments) // We separate the declaration from the implementation of this inline // function just so the PRINTF_FORMAT works. -inline int snprintf(char* buffer, size_t size, const char* format, ...) - PRINTF_FORMAT(3, 4); -inline int snprintf(char* buffer, size_t size, const char* format, ...) { +inline int snprintf(char* buffer, + size_t size, + _Printf_format_string_ const char* format, + ...) PRINTF_FORMAT(3, 4); +inline int snprintf(char* buffer, + size_t size, + _Printf_format_string_ const char* format, + ...) { va_list arguments; va_start(arguments, format); int result = vsnprintf(buffer, size, format, arguments); @@ -93,27 +87,38 @@ BASE_EXPORT bool IsWprintfFormatPortable(const wchar_t* format); // ASCII-specific tolower. The standard library's tolower is locale sensitive, // so we don't want to use it here. -template inline Char ToLowerASCII(Char c) { +inline char ToLowerASCII(char c) { + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} +inline char16 ToLowerASCII(char16 c) { return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; } // ASCII-specific toupper. The standard library's toupper is locale sensitive, // so we don't want to use it here. -template inline Char ToUpperASCII(Char c) { +inline char ToUpperASCII(char c) { + return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c; +} +inline char16 ToUpperASCII(char16 c) { return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c; } -// Function objects to aid in comparing/searching strings. +// Converts the given string to it's ASCII-lowercase equivalent. +BASE_EXPORT std::string ToLowerASCII(StringPiece str); +BASE_EXPORT string16 ToLowerASCII(StringPiece16 str); -template struct CaseInsensitiveCompare { - public: - bool operator()(Char x, Char y) const { - // TODO(darin): Do we really want to do locale sensitive comparisons here? - // See http://crbug.com/24917 - return tolower(x) == tolower(y); - } -}; +// Converts the given string to it's ASCII-uppercase equivalent. +BASE_EXPORT std::string ToUpperASCII(StringPiece str); +BASE_EXPORT string16 ToUpperASCII(StringPiece16 str); +// Functor for case-insensitive ASCII comparisons for STL algorithms like +// std::search. +// +// Note that a full Unicode version of this functor is not possible to write +// because case mappings might change the number of characters, depend on +// context (combining accents), and require handling UTF-16. If you need +// proper Unicode support, use base::i18n::ToLower/FoldCase and then just +// use a normal operator== on the result. template struct CaseInsensitiveCompareASCII { public: bool operator()(Char x, Char y) const { @@ -121,6 +126,22 @@ template struct CaseInsensitiveCompareASCII { } }; +// Like strcasecmp for case-insensitive ASCII characters only. Returns: +// -1 (a < b) +// 0 (a == b) +// 1 (a > b) +// (unlike strcasecmp which can return values greater or less than 1/-1). For +// full Unicode support, use base::i18n::ToLower or base::i18h::FoldCase +// and then just call the normal string operators on the result. +BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece a, StringPiece b); +BASE_EXPORT int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b); + +// Equality for ASCII case-insensitive comparisons. For full Unicode support, +// use base::i18n::ToLower or base::i18h::FoldCase and then compare with either +// == or !=. +BASE_EXPORT bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b); +BASE_EXPORT bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b); + // These threadsafe functions return references to globally unique empty // strings. // @@ -138,10 +159,12 @@ BASE_EXPORT const std::string& EmptyString(); BASE_EXPORT const string16& EmptyString16(); // Contains the set of characters representing whitespace in the corresponding -// encoding. Null-terminated. -BASE_EXPORT extern const wchar_t kWhitespaceWide[]; -BASE_EXPORT extern const char16 kWhitespaceUTF16[]; +// encoding. Null-terminated. The ASCII versions are the whitespaces as defined +// by HTML5, and don't include control characters. +BASE_EXPORT extern const wchar_t kWhitespaceWide[]; // Includes Unicode. +BASE_EXPORT extern const char16 kWhitespaceUTF16[]; // Includes Unicode. BASE_EXPORT extern const char kWhitespaceASCII[]; +BASE_EXPORT extern const char16 kWhitespaceASCIIAs16[]; // No unicode. // Null-terminated string representing the UTF-8 byte order mark. BASE_EXPORT extern const char kUtf8ByteOrderMark[]; @@ -150,10 +173,10 @@ BASE_EXPORT extern const char kUtf8ByteOrderMark[]; // if any characters were removed. |remove_chars| must be null-terminated. // NOTE: Safe to use the same variable for both |input| and |output|. BASE_EXPORT bool RemoveChars(const string16& input, - const base::StringPiece16& remove_chars, + const StringPiece16& remove_chars, string16* output); BASE_EXPORT bool RemoveChars(const std::string& input, - const base::StringPiece& remove_chars, + const StringPiece& remove_chars, std::string* output); // Replaces characters in |replace_chars| from anywhere in |input| with @@ -162,55 +185,65 @@ BASE_EXPORT bool RemoveChars(const std::string& input, // |replace_chars| must be null-terminated. // NOTE: Safe to use the same variable for both |input| and |output|. BASE_EXPORT bool ReplaceChars(const string16& input, - const base::StringPiece16& replace_chars, + const StringPiece16& replace_chars, const string16& replace_with, string16* output); BASE_EXPORT bool ReplaceChars(const std::string& input, - const base::StringPiece& replace_chars, + const StringPiece& replace_chars, const std::string& replace_with, std::string* output); +enum TrimPositions { + TRIM_NONE = 0, + TRIM_LEADING = 1 << 0, + TRIM_TRAILING = 1 << 1, + TRIM_ALL = TRIM_LEADING | TRIM_TRAILING, +}; + // Removes characters in |trim_chars| from the beginning and end of |input|. -// |trim_chars| must be null-terminated. -// NOTE: Safe to use the same variable for both |input| and |output|. +// The 8-bit version only works on 8-bit characters, not UTF-8. +// +// It is safe to use the same variable for both |input| and |output| (this is +// the normal usage to trim in-place). BASE_EXPORT bool TrimString(const string16& input, - const base::StringPiece16& trim_chars, + StringPiece16 trim_chars, string16* output); BASE_EXPORT bool TrimString(const std::string& input, - const base::StringPiece& trim_chars, + StringPiece trim_chars, std::string* output); +// StringPiece versions of the above. The returned pieces refer to the original +// buffer. +BASE_EXPORT StringPiece16 TrimString(StringPiece16 input, + const StringPiece16& trim_chars, + TrimPositions positions); +BASE_EXPORT StringPiece TrimString(StringPiece input, + const StringPiece& trim_chars, + TrimPositions positions); + // Truncates a string to the nearest UTF-8 character that will leave // the string less than or equal to the specified byte size. BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input, const size_t byte_size, std::string* output); -// Trims any whitespace from either end of the input string. Returns where -// whitespace was found. -// The non-wide version has two functions: -// * TrimWhitespaceASCII() -// This function is for ASCII strings and only looks for ASCII whitespace; -// Please choose the best one according to your usage. +// Trims any whitespace from either end of the input string. +// +// The StringPiece versions return a substring referencing the input buffer. +// The ASCII versions look only for ASCII whitespace. +// +// The std::string versions return where whitespace was found. // NOTE: Safe to use the same variable for both input and output. -enum TrimPositions { - TRIM_NONE = 0, - TRIM_LEADING = 1 << 0, - TRIM_TRAILING = 1 << 1, - TRIM_ALL = TRIM_LEADING | TRIM_TRAILING, -}; BASE_EXPORT TrimPositions TrimWhitespace(const string16& input, TrimPositions positions, - base::string16* output); + string16* output); +BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input, + TrimPositions positions); BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input, TrimPositions positions, std::string* output); - -// Deprecated. This function is only for backward compatibility and calls -// TrimWhitespaceASCII(). -BASE_EXPORT TrimPositions TrimWhitespace(const std::string& input, - TrimPositions positions, - std::string* output); +BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input, + TrimPositions positions); // Searches for CR or LF characters. Removes all contiguous whitespace // strings that contain them. This is useful when trying to deal with text @@ -248,7 +281,7 @@ BASE_EXPORT bool ContainsOnlyChars(const StringPiece16& input, // // IsStringASCII assumes the input is likely all ASCII, and does not leave early // if it is not the case. -BASE_EXPORT bool IsStringUTF8(const std::string& str); +BASE_EXPORT bool IsStringUTF8(const StringPiece& str); BASE_EXPORT bool IsStringASCII(const StringPiece& str); BASE_EXPORT bool IsStringASCII(const StringPiece16& str); // A convenience adaptor for WebStrings, as they don't convert into @@ -258,85 +291,43 @@ BASE_EXPORT bool IsStringASCII(const string16& str); BASE_EXPORT bool IsStringASCII(const std::wstring& str); #endif -// Converts the elements of the given string. This version uses a pointer to -// clearly differentiate it from the non-pointer variant. -template inline void StringToLowerASCII(str* s) { - for (typename str::iterator i = s->begin(); i != s->end(); ++i) - *i = ToLowerASCII(*i); -} +// Compare the lower-case form of the given string against the given +// previously-lower-cased ASCII string (typically a constant). +BASE_EXPORT bool LowerCaseEqualsASCII(StringPiece str, + StringPiece lowecase_ascii); +BASE_EXPORT bool LowerCaseEqualsASCII(StringPiece16 str, + StringPiece lowecase_ascii); -template inline str StringToLowerASCII(const str& s) { - // for std::string and std::wstring - str output(s); - StringToLowerASCII(&output); - return output; -} +// Performs a case-sensitive string compare of the given 16-bit string against +// the given 8-bit ASCII string (typically a constant). The behavior is +// undefined if the |ascii| string is not ASCII. +BASE_EXPORT bool EqualsASCII(StringPiece16 str, StringPiece ascii); -} // namespace base - -#if defined(OS_WIN) -#include "base/strings/string_util_win.h" -#elif defined(OS_POSIX) -#include "base/strings/string_util_posix.h" -#else -#error Define string operations appropriately for your platform -#endif - -// Converts the elements of the given string. This version uses a pointer to -// clearly differentiate it from the non-pointer variant. -template inline void StringToUpperASCII(str* s) { - for (typename str::iterator i = s->begin(); i != s->end(); ++i) - *i = base::ToUpperASCII(*i); -} - -template inline str StringToUpperASCII(const str& s) { - // for std::string and std::wstring - str output(s); - StringToUpperASCII(&output); - return output; -} - -// Compare the lower-case form of the given string against the given ASCII -// string. This is useful for doing checking if an input string matches some -// token, and it is optimized to avoid intermediate string copies. This API is -// borrowed from the equivalent APIs in Mozilla. -BASE_EXPORT bool LowerCaseEqualsASCII(const std::string& a, const char* b); -BASE_EXPORT bool LowerCaseEqualsASCII(const base::string16& a, const char* b); - -// Same thing, but with string iterators instead. -BASE_EXPORT bool LowerCaseEqualsASCII(std::string::const_iterator a_begin, - std::string::const_iterator a_end, - const char* b); -BASE_EXPORT bool LowerCaseEqualsASCII(base::string16::const_iterator a_begin, - base::string16::const_iterator a_end, - const char* b); -BASE_EXPORT bool LowerCaseEqualsASCII(const char* a_begin, - const char* a_end, - const char* b); -BASE_EXPORT bool LowerCaseEqualsASCII(const base::char16* a_begin, - const base::char16* a_end, - const char* b); - -// Performs a case-sensitive string compare. The behavior is undefined if both -// strings are not ASCII. -BASE_EXPORT bool EqualsASCII(const base::string16& a, const base::StringPiece& b); - -// Returns true if str starts with search, or false otherwise. -BASE_EXPORT bool StartsWithASCII(const std::string& str, - const std::string& search, - bool case_sensitive); -BASE_EXPORT bool StartsWith(const base::string16& str, - const base::string16& search, - bool case_sensitive); - -// Returns true if str ends with search, or false otherwise. -BASE_EXPORT bool EndsWith(const std::string& str, - const std::string& search, - bool case_sensitive); -BASE_EXPORT bool EndsWith(const base::string16& str, - const base::string16& search, - bool case_sensitive); +// Indicates case sensitivity of comparisons. Only ASCII case insensitivity +// is supported. Full Unicode case-insensitive conversions would need to go in +// base/i18n so it can use ICU. +// +// If you need to do Unicode-aware case-insensitive StartsWith/EndsWith, it's +// best to call base::i18n::ToLower() or base::i18n::FoldCase() (see +// base/i18n/case_conversion.h for usage advice) on the arguments, and then use +// the results to a case-sensitive comparison. +enum class CompareCase { + SENSITIVE, + INSENSITIVE_ASCII, +}; +BASE_EXPORT bool StartsWith(StringPiece str, + StringPiece search_for, + CompareCase case_sensitivity); +BASE_EXPORT bool StartsWith(StringPiece16 str, + StringPiece16 search_for, + CompareCase case_sensitivity); +BASE_EXPORT bool EndsWith(StringPiece str, + StringPiece search_for, + CompareCase case_sensitivity); +BASE_EXPORT bool EndsWith(StringPiece16 str, + StringPiece16 search_for, + CompareCase case_sensitivity); // Determines the type of ASCII character, independent of locale (the C // library versions will change based on locale). @@ -360,41 +351,34 @@ inline bool IsHexDigit(Char c) { (c >= 'a' && c <= 'f'); } -template -inline char HexDigitToInt(Char c) { - DCHECK(IsHexDigit(c)); - if (c >= '0' && c <= '9') - return static_cast(c - '0'); - if (c >= 'A' && c <= 'F') - return static_cast(c - 'A' + 10); - if (c >= 'a' && c <= 'f') - return static_cast(c - 'a' + 10); - return 0; -} +// Returns the integer corresponding to the given hex character. For example: +// '4' -> 4 +// 'a' -> 10 +// 'B' -> 11 +// Assumes the input is a valid hex character. DCHECKs in debug builds if not. +BASE_EXPORT char HexDigitToInt(wchar_t c); -// Returns true if it's a whitespace character. -inline bool IsWhitespace(wchar_t c) { - return wcschr(base::kWhitespaceWide, c) != NULL; -} +// Returns true if it's a Unicode whitespace character. +BASE_EXPORT bool IsUnicodeWhitespace(wchar_t c); // Return a byte string in human-readable format with a unit suffix. Not // appropriate for use in any UI; use of FormatBytes and friends in ui/base is // highly recommended instead. TODO(avi): Figure out how to get callers to use // FormatBytes instead; remove this. -BASE_EXPORT base::string16 FormatBytesUnlocalized(int64 bytes); +BASE_EXPORT string16 FormatBytesUnlocalized(int64_t bytes); // Starting at |start_offset| (usually 0), replace the first instance of // |find_this| with |replace_with|. BASE_EXPORT void ReplaceFirstSubstringAfterOffset( base::string16* str, size_t start_offset, - const base::string16& find_this, - const base::string16& replace_with); + StringPiece16 find_this, + StringPiece16 replace_with); BASE_EXPORT void ReplaceFirstSubstringAfterOffset( std::string* str, size_t start_offset, - const std::string& find_this, - const std::string& replace_with); + StringPiece find_this, + StringPiece replace_with); // Starting at |start_offset| (usually 0), look through |str| and replace all // instances of |find_this| with |replace_with|. @@ -403,14 +387,15 @@ BASE_EXPORT void ReplaceFirstSubstringAfterOffset( // characters, for example: // std::replace(str.begin(), str.end(), 'a', 'b'); BASE_EXPORT void ReplaceSubstringsAfterOffset( - base::string16* str, + string16* str, size_t start_offset, - const base::string16& find_this, - const base::string16& replace_with); -BASE_EXPORT void ReplaceSubstringsAfterOffset(std::string* str, - size_t start_offset, - const std::string& find_this, - const std::string& replace_with); + StringPiece16 find_this, + StringPiece16 replace_with); +BASE_EXPORT void ReplaceSubstringsAfterOffset( + std::string* str, + size_t start_offset, + StringPiece find_this, + StringPiece replace_with); // Reserves enough memory in |str| to accommodate |length_with_null| characters, // sets the size of |str| to |length_with_null - 1| characters, and returns a @@ -432,101 +417,45 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset(std::string* str, // of the string, and not doing that will mean people who access |str| rather // than str.c_str() will get back a string of whatever size |str| had on entry // to this function (probably 0). -template -inline typename string_type::value_type* WriteInto(string_type* str, - size_t length_with_null) { - DCHECK_GT(length_with_null, 1u); - str->reserve(length_with_null); - str->resize(length_with_null - 1); - return &((*str)[0]); -} - -//----------------------------------------------------------------------------- - -// Splits a string into its fields delimited by any of the characters in -// |delimiters|. Each field is added to the |tokens| vector. Returns the -// number of tokens found. -BASE_EXPORT size_t Tokenize(const base::string16& str, - const base::string16& delimiters, - std::vector* tokens); -BASE_EXPORT size_t Tokenize(const std::string& str, - const std::string& delimiters, - std::vector* tokens); -BASE_EXPORT size_t Tokenize(const base::StringPiece& str, - const base::StringPiece& delimiters, - std::vector* tokens); +BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null); +BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null); +#ifndef OS_WIN +BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null); +#endif // Does the opposite of SplitString(). -BASE_EXPORT base::string16 JoinString(const std::vector& parts, - base::char16 s); -BASE_EXPORT std::string JoinString( - const std::vector& parts, char s); - -// Join |parts| using |separator|. -BASE_EXPORT std::string JoinString( - const std::vector& parts, - const std::string& separator); -BASE_EXPORT base::string16 JoinString( - const std::vector& parts, - const base::string16& separator); +BASE_EXPORT std::string JoinString(const std::vector& parts, + StringPiece separator); +BASE_EXPORT string16 JoinString(const std::vector& parts, + StringPiece16 separator); // Replace $1-$2-$3..$9 in the format string with |a|-|b|-|c|..|i| respectively. // Additionally, any number of consecutive '$' characters is replaced by that // number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be // NULL. This only allows you to use up to nine replacements. -BASE_EXPORT base::string16 ReplaceStringPlaceholders( - const base::string16& format_string, - const std::vector& subst, +BASE_EXPORT string16 ReplaceStringPlaceholders( + const string16& format_string, + const std::vector& subst, std::vector* offsets); BASE_EXPORT std::string ReplaceStringPlaceholders( - const base::StringPiece& format_string, + const StringPiece& format_string, const std::vector& subst, std::vector* offsets); // Single-string shortcut for ReplaceStringHolders. |offset| may be NULL. -BASE_EXPORT base::string16 ReplaceStringPlaceholders( - const base::string16& format_string, - const base::string16& a, - size_t* offset); +BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + size_t* offset); -// Returns true if the string passed in matches the pattern. The pattern -// string can contain wildcards like * and ? -// The backslash character (\) is an escape character for * and ? -// We limit the patterns to having a max of 16 * or ? characters. -// ? matches 0 or 1 character, while * matches 0 or more characters. -BASE_EXPORT bool MatchPattern(const base::StringPiece& string, - const base::StringPiece& pattern); -BASE_EXPORT bool MatchPattern(const base::string16& string, - const base::string16& pattern); +} // namespace base -// Hack to convert any char-like type to its unsigned counterpart. -// For example, it will convert char, signed char and unsigned char to unsigned -// char. -template -struct ToUnsigned { - typedef T Unsigned; -}; - -template<> -struct ToUnsigned { - typedef unsigned char Unsigned; -}; -template<> -struct ToUnsigned { - typedef unsigned char Unsigned; -}; -template<> -struct ToUnsigned { -#if defined(WCHAR_T_IS_UTF16) - typedef unsigned short Unsigned; -#elif defined(WCHAR_T_IS_UTF32) - typedef uint32 Unsigned; +#if defined(OS_WIN) +#include "base/strings/string_util_win.h" +#elif defined(OS_POSIX) +#include "base/strings/string_util_posix.h" +#else +#error Define string operations appropriately for your platform #endif -}; -template<> -struct ToUnsigned { - typedef unsigned short Unsigned; -}; #endif // BASE_STRINGS_STRING_UTIL_H_ diff --git a/security/sandbox/chromium/base/strings/string_util_constants.cc b/security/sandbox/chromium/base/strings/string_util_constants.cc index 146e5fd53556..aba1b12b88f0 100644 --- a/security/sandbox/chromium/base/strings/string_util_constants.cc +++ b/security/sandbox/chromium/base/strings/string_util_constants.cc @@ -52,6 +52,16 @@ const char kWhitespaceASCII[] = { 0 }; +const char16 kWhitespaceASCIIAs16[] = { + 0x09, // CHARACTER TABULATION + 0x0A, // LINE FEED (LF) + 0x0B, // LINE TABULATION + 0x0C, // FORM FEED (FF) + 0x0D, // CARRIAGE RETURN (CR) + 0x20, // SPACE + 0 +}; + const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF"; } // namespace base diff --git a/security/sandbox/chromium/base/strings/string_util_posix.h b/security/sandbox/chromium/base/strings/string_util_posix.h index f4009d4ae7f1..8299118e1065 100644 --- a/security/sandbox/chromium/base/strings/string_util_posix.h +++ b/security/sandbox/chromium/base/strings/string_util_posix.h @@ -6,6 +6,7 @@ #define BASE_STRINGS_STRING_UTIL_POSIX_H_ #include +#include #include #include #include @@ -20,27 +21,11 @@ inline char* strdup(const char* str) { return ::strdup(str); } -inline int strcasecmp(const char* string1, const char* string2) { - return ::strcasecmp(string1, string2); -} - -inline int strncasecmp(const char* string1, const char* string2, size_t count) { - return ::strncasecmp(string1, string2, count); -} - inline int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments) { return ::vsnprintf(buffer, size, format, arguments); } -inline int strncmp16(const char16* s1, const char16* s2, size_t count) { -#if defined(WCHAR_T_IS_UTF16) - return ::wcsncmp(s1, s2, count); -#elif defined(WCHAR_T_IS_UTF32) - return c16memcmp(s1, s2, count); -#endif -} - inline int vswprintf(wchar_t* buffer, size_t size, const wchar_t* format, va_list arguments) { DCHECK(IsWprintfFormatPortable(format)); diff --git a/security/sandbox/chromium/base/strings/string_util_win.h b/security/sandbox/chromium/base/strings/string_util_win.h index 602ba2737840..7f260bfc8b4e 100644 --- a/security/sandbox/chromium/base/strings/string_util_win.h +++ b/security/sandbox/chromium/base/strings/string_util_win.h @@ -6,6 +6,7 @@ #define BASE_STRINGS_STRING_UTIL_WIN_H_ #include +#include #include #include #include @@ -20,26 +21,11 @@ inline char* strdup(const char* str) { return _strdup(str); } -inline int strcasecmp(const char* s1, const char* s2) { - return _stricmp(s1, s2); -} - -inline int strncasecmp(const char* s1, const char* s2, size_t count) { - return _strnicmp(s1, s2, count); -} - -inline int strncmp16(const char16* s1, const char16* s2, size_t count) { - return ::wcsncmp(s1, s2, count); -} - inline int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments) { - int length = _vsprintf_p(buffer, size, format, arguments); - if (length < 0) { - if (size > 0) - buffer[0] = 0; - return _vscprintf_p(format, arguments); - } + int length = vsnprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscprintf(format, arguments); return length; } @@ -47,12 +33,9 @@ inline int vswprintf(wchar_t* buffer, size_t size, const wchar_t* format, va_list arguments) { DCHECK(IsWprintfFormatPortable(format)); - int length = _vswprintf_p(buffer, size, format, arguments); - if (length < 0) { - if (size > 0) - buffer[0] = 0; - return _vscwprintf_p(format, arguments); - } + int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscwprintf(format, arguments); return length; } diff --git a/security/sandbox/chromium/base/strings/stringprintf.cc b/security/sandbox/chromium/base/strings/stringprintf.cc index 3d024fa65aaa..415845d61654 100644 --- a/security/sandbox/chromium/base/strings/stringprintf.cc +++ b/security/sandbox/chromium/base/strings/stringprintf.cc @@ -5,10 +5,15 @@ #include "base/strings/stringprintf.h" #include +#include +#include + +#include "base/macros.h" #include "base/scoped_clear_errno.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" namespace base { @@ -27,7 +32,7 @@ inline int vsnprintfT(char* buffer, return base::vsnprintf(buffer, buf_size, format, argptr); } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) inline int vsnprintfT(wchar_t* buffer, size_t buf_size, const wchar_t* format, @@ -48,7 +53,7 @@ static void StringAppendVT(StringType* dst, typename StringType::value_type stack_buf[1024]; va_list ap_copy; - GG_VA_COPY(ap_copy, ap); + va_copy(ap_copy, ap); #if !defined(OS_WIN) ScopedClearErrno clear_errno; @@ -94,7 +99,7 @@ static void StringAppendVT(StringType* dst, // NOTE: You can only use a va_list once. Since we're in a while loop, we // need to make a new copy each time so we don't use up the original. - GG_VA_COPY(ap_copy, ap); + va_copy(ap_copy, ap); result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy); va_end(ap_copy); @@ -117,7 +122,7 @@ std::string StringPrintf(const char* format, ...) { return result; } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) std::wstring StringPrintf(const wchar_t* format, ...) { va_list ap; va_start(ap, format); @@ -143,7 +148,7 @@ const std::string& SStringPrintf(std::string* dst, const char* format, ...) { return *dst; } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, ...) { va_list ap; @@ -162,7 +167,7 @@ void StringAppendF(std::string* dst, const char* format, ...) { va_end(ap); } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { va_list ap; va_start(ap, format); @@ -175,7 +180,7 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) { StringAppendVT(dst, format, ap); } -#if !defined(OS_ANDROID) +#if defined(OS_WIN) void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { StringAppendVT(dst, format, ap); } diff --git a/security/sandbox/chromium/base/strings/stringprintf.h b/security/sandbox/chromium/base/strings/stringprintf.h index d924a0e59f21..7a75d89e10f7 100644 --- a/security/sandbox/chromium/base/strings/stringprintf.h +++ b/security/sandbox/chromium/base/strings/stringprintf.h @@ -11,16 +11,18 @@ #include "base/base_export.h" #include "base/compiler_specific.h" +#include "build/build_config.h" namespace base { // Return a C++ string given printf-like input. -BASE_EXPORT std::string StringPrintf(const char* format, ...) +BASE_EXPORT std::string StringPrintf(_Printf_format_string_ const char* format, + ...) PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; -// OS_ANDROID's libc does not support wchar_t, so several overloads are omitted. -#if !defined(OS_ANDROID) -BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...) - WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; +#if defined(OS_WIN) +BASE_EXPORT std::wstring StringPrintf( + _Printf_format_string_ const wchar_t* format, + ...) WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; #endif // Return a C++ string given vprintf-like input. @@ -28,30 +30,32 @@ BASE_EXPORT std::string StringPrintV(const char* format, va_list ap) PRINTF_FORMAT(1, 0) WARN_UNUSED_RESULT; // Store result into a supplied string and return it. -BASE_EXPORT const std::string& SStringPrintf(std::string* dst, - const char* format, ...) - PRINTF_FORMAT(2, 3); -#if !defined(OS_ANDROID) -BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst, - const wchar_t* format, ...) - WPRINTF_FORMAT(2, 3); +BASE_EXPORT const std::string& SStringPrintf( + std::string* dst, + _Printf_format_string_ const char* format, + ...) PRINTF_FORMAT(2, 3); +#if defined(OS_WIN) +BASE_EXPORT const std::wstring& SStringPrintf( + std::wstring* dst, + _Printf_format_string_ const wchar_t* format, + ...) WPRINTF_FORMAT(2, 3); #endif // Append result to a supplied string. -BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...) - PRINTF_FORMAT(2, 3); -#if !defined(OS_ANDROID) -// TODO(evanm): this is only used in a few places in the code; -// replace with string16 version. -BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...) - WPRINTF_FORMAT(2, 3); +BASE_EXPORT void StringAppendF(std::string* dst, + _Printf_format_string_ const char* format, + ...) PRINTF_FORMAT(2, 3); +#if defined(OS_WIN) +BASE_EXPORT void StringAppendF(std::wstring* dst, + _Printf_format_string_ const wchar_t* format, + ...) WPRINTF_FORMAT(2, 3); #endif // Lower-level routine that takes a va_list and appends to a specified // string. All other routines are just convenience wrappers around it. BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap) PRINTF_FORMAT(2, 0); -#if !defined(OS_ANDROID) +#if defined(OS_WIN) BASE_EXPORT void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) WPRINTF_FORMAT(2, 0); diff --git a/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc index 022c0dffd895..3101a6028883 100644 --- a/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc +++ b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc @@ -11,15 +11,15 @@ namespace base { // ReadUnicodeCharacter -------------------------------------------------------- bool ReadUnicodeCharacter(const char* src, - int32 src_len, - int32* char_index, - uint32* code_point_out) { + int32_t src_len, + int32_t* char_index, + uint32_t* code_point_out) { // U8_NEXT expects to be able to use -1 to signal an error, so we must // use a signed type for code_point. But this function returns false // on error anyway, so code_point_out is unsigned. - int32 code_point; + int32_t code_point; CBU8_NEXT(src, *char_index, src_len, code_point); - *code_point_out = static_cast(code_point); + *code_point_out = static_cast(code_point); // The ICU macro above moves to the next char, we want to point to the last // char consumed. @@ -30,9 +30,9 @@ bool ReadUnicodeCharacter(const char* src, } bool ReadUnicodeCharacter(const char16* src, - int32 src_len, - int32* char_index, - uint32* code_point) { + int32_t src_len, + int32_t* char_index, + uint32_t* code_point) { if (CBU16_IS_SURROGATE(src[*char_index])) { if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) || *char_index + 1 >= src_len || @@ -55,9 +55,9 @@ bool ReadUnicodeCharacter(const char16* src, #if defined(WCHAR_T_IS_UTF32) bool ReadUnicodeCharacter(const wchar_t* src, - int32 src_len, - int32* char_index, - uint32* code_point) { + int32_t src_len, + int32_t* char_index, + uint32_t* code_point) { // Conversion is easy since the source is 32-bit. *code_point = src[*char_index]; @@ -68,7 +68,7 @@ bool ReadUnicodeCharacter(const wchar_t* src, // WriteUnicodeCharacter ------------------------------------------------------- -size_t WriteUnicodeCharacter(uint32 code_point, std::string* output) { +size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output) { if (code_point <= 0x7f) { // Fast path the common case of one byte. output->push_back(static_cast(code_point)); @@ -89,7 +89,7 @@ size_t WriteUnicodeCharacter(uint32 code_point, std::string* output) { return char_offset - original_char_offset; } -size_t WriteUnicodeCharacter(uint32 code_point, string16* output) { +size_t WriteUnicodeCharacter(uint32_t code_point, string16* output) { if (CBU16_LENGTH(code_point) == 1) { // Thie code point is in the Basic Multilingual Plane (BMP). output->push_back(static_cast(code_point)); diff --git a/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h index 22abbbc9e798..c7164045390e 100644 --- a/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h +++ b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h @@ -7,12 +7,15 @@ // This should only be used by the various UTF string conversion files. +#include +#include + #include "base/base_export.h" #include "base/strings/string16.h" namespace base { -inline bool IsValidCodepoint(uint32 code_point) { +inline bool IsValidCodepoint(uint32_t code_point) { // Excludes the surrogate code points ([0xD800, 0xDFFF]) and // codepoints larger than 0x10FFFF (the highest codepoint allowed). // Non-characters and unassigned codepoints are allowed. @@ -20,7 +23,7 @@ inline bool IsValidCodepoint(uint32 code_point) { (code_point >= 0xE000u && code_point <= 0x10FFFFu); } -inline bool IsValidCharacter(uint32 code_point) { +inline bool IsValidCharacter(uint32_t code_point) { // Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in // 0xFFFE or 0xFFFF) from the set of valid code points. return code_point < 0xD800u || (code_point >= 0xE000u && @@ -38,40 +41,39 @@ inline bool IsValidCharacter(uint32 code_point) { // // Returns true on success. On false, |*code_point| will be invalid. BASE_EXPORT bool ReadUnicodeCharacter(const char* src, - int32 src_len, - int32* char_index, - uint32* code_point_out); + int32_t src_len, + int32_t* char_index, + uint32_t* code_point_out); // Reads a UTF-16 character. The usage is the same as the 8-bit version above. BASE_EXPORT bool ReadUnicodeCharacter(const char16* src, - int32 src_len, - int32* char_index, - uint32* code_point); + int32_t src_len, + int32_t* char_index, + uint32_t* code_point); #if defined(WCHAR_T_IS_UTF32) // Reads UTF-32 character. The usage is the same as the 8-bit version above. BASE_EXPORT bool ReadUnicodeCharacter(const wchar_t* src, - int32 src_len, - int32* char_index, - uint32* code_point); + int32_t src_len, + int32_t* char_index, + uint32_t* code_point); #endif // defined(WCHAR_T_IS_UTF32) // WriteUnicodeCharacter ------------------------------------------------------- // Appends a UTF-8 character to the given 8-bit string. Returns the number of // bytes written. -// TODO(brettw) Bug 79631: This function should not be exposed. -BASE_EXPORT size_t WriteUnicodeCharacter(uint32 code_point, +BASE_EXPORT size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output); // Appends the given code point as a UTF-16 character to the given 16-bit // string. Returns the number of 16-bit values written. -BASE_EXPORT size_t WriteUnicodeCharacter(uint32 code_point, string16* output); +BASE_EXPORT size_t WriteUnicodeCharacter(uint32_t code_point, string16* output); #if defined(WCHAR_T_IS_UTF32) // Appends the given UTF-32 character to the given 32-bit string. Returns the // number of 32-bit values written. -inline size_t WriteUnicodeCharacter(uint32 code_point, std::wstring* output) { +inline size_t WriteUnicodeCharacter(uint32_t code_point, std::wstring* output) { // This is the easy case, just append the character. output->push_back(code_point); return 1; diff --git a/security/sandbox/chromium/base/strings/utf_string_conversions.cc b/security/sandbox/chromium/base/strings/utf_string_conversions.cc index 9796eec5e15e..6b17eacd6ce9 100644 --- a/security/sandbox/chromium/base/strings/utf_string_conversions.cc +++ b/security/sandbox/chromium/base/strings/utf_string_conversions.cc @@ -4,9 +4,12 @@ #include "base/strings/utf_string_conversions.h" +#include + #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversion_utils.h" +#include "build/build_config.h" namespace base { @@ -24,9 +27,9 @@ bool ConvertUnicode(const SRC_CHAR* src, DEST_STRING* output) { // ICU requires 32-bit numbers. bool success = true; - int32 src_len32 = static_cast(src_len); - for (int32 i = 0; i < src_len32; i++) { - uint32 code_point; + int32_t src_len32 = static_cast(src_len); + for (int32_t i = 0; i < src_len32; i++) { + uint32_t code_point; if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) { WriteUnicodeCharacter(code_point, output); } else { @@ -73,7 +76,7 @@ bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output) { } } -std::wstring UTF8ToWide(const StringPiece& utf8) { +std::wstring UTF8ToWide(StringPiece utf8) { if (IsStringASCII(utf8)) { return std::wstring(utf8.begin(), utf8.end()); } @@ -153,7 +156,7 @@ bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { } } -string16 UTF8ToUTF16(const StringPiece& utf8) { +string16 UTF8ToUTF16(StringPiece utf8) { if (IsStringASCII(utf8)) { return string16(utf8.begin(), utf8.end()); } @@ -176,7 +179,7 @@ bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { } } -std::string UTF16ToUTF8(const string16& utf16) { +std::string UTF16ToUTF8(StringPiece16 utf16) { if (IsStringASCII(utf16)) { return std::string(utf16.begin(), utf16.end()); } @@ -195,7 +198,7 @@ bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { return UTF8ToWide(src, src_len, output); } -string16 UTF8ToUTF16(const StringPiece& utf8) { +string16 UTF8ToUTF16(StringPiece utf8) { return UTF8ToWide(utf8); } @@ -203,23 +206,24 @@ bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { return WideToUTF8(src, src_len, output); } -std::string UTF16ToUTF8(const string16& utf16) { - return WideToUTF8(utf16); +std::string UTF16ToUTF8(StringPiece16 utf16) { + if (IsStringASCII(utf16)) + return std::string(utf16.data(), utf16.data() + utf16.length()); + + std::string ret; + PrepareForUTF8Output(utf16.data(), utf16.length(), &ret); + ConvertUnicode(utf16.data(), utf16.length(), &ret); + return ret; } #endif -std::wstring ASCIIToWide(const StringPiece& ascii) { - DCHECK(IsStringASCII(ascii)) << ascii; - return std::wstring(ascii.begin(), ascii.end()); -} - -string16 ASCIIToUTF16(const StringPiece& ascii) { +string16 ASCIIToUTF16(StringPiece ascii) { DCHECK(IsStringASCII(ascii)) << ascii; return string16(ascii.begin(), ascii.end()); } -std::string UTF16ToASCII(const string16& utf16) { +std::string UTF16ToASCII(StringPiece16 utf16) { DCHECK(IsStringASCII(utf16)) << UTF16ToUTF8(utf16); return std::string(utf16.begin(), utf16.end()); } diff --git a/security/sandbox/chromium/base/strings/utf_string_conversions.h b/security/sandbox/chromium/base/strings/utf_string_conversions.h index 13e0b7193be3..2995f4cbcf60 100644 --- a/security/sandbox/chromium/base/strings/utf_string_conversions.h +++ b/security/sandbox/chromium/base/strings/utf_string_conversions.h @@ -5,6 +5,8 @@ #ifndef BASE_STRINGS_UTF_STRING_CONVERSIONS_H_ #define BASE_STRINGS_UTF_STRING_CONVERSIONS_H_ +#include + #include #include "base/base_export.h" @@ -24,7 +26,7 @@ BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len, BASE_EXPORT std::string WideToUTF8(const std::wstring& wide); BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output); -BASE_EXPORT std::wstring UTF8ToWide(const StringPiece& utf8); +BASE_EXPORT std::wstring UTF8ToWide(StringPiece utf8); BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output); @@ -34,19 +36,18 @@ BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len, BASE_EXPORT std::wstring UTF16ToWide(const string16& utf16); BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output); -BASE_EXPORT string16 UTF8ToUTF16(const StringPiece& utf8); +BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8); BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output); -BASE_EXPORT std::string UTF16ToUTF8(const string16& utf16); +BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16); -// These convert an ASCII string, typically a hardcoded constant, to a -// UTF16/Wide string. -BASE_EXPORT std::wstring ASCIIToWide(const StringPiece& ascii); -BASE_EXPORT string16 ASCIIToUTF16(const StringPiece& ascii); +// This converts an ASCII string, typically a hardcoded constant, to a UTF16 +// string. +BASE_EXPORT string16 ASCIIToUTF16(StringPiece ascii); // Converts to 7-bit ASCII by truncating. The result must be known to be ASCII // beforehand. -BASE_EXPORT std::string UTF16ToASCII(const string16& utf16); +BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16); } // namespace base diff --git a/security/sandbox/chromium/base/synchronization/condition_variable.h b/security/sandbox/chromium/base/synchronization/condition_variable.h index 5d8507d43730..a41b2ba5a781 100644 --- a/security/sandbox/chromium/base/synchronization/condition_variable.h +++ b/security/sandbox/chromium/base/synchronization/condition_variable.h @@ -65,16 +65,16 @@ #ifndef BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_ #define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_ +#include "base/base_export.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/synchronization/lock.h" #include "build/build_config.h" #if defined(OS_POSIX) #include #endif -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/synchronization/lock.h" - namespace base { class ConditionVarImpl; @@ -104,7 +104,7 @@ class BASE_EXPORT ConditionVariable { #elif defined(OS_POSIX) pthread_cond_t condition_; pthread_mutex_t* user_mutex_; -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() base::Lock* user_lock_; // Needed to adjust shadow lock state on wait. #endif diff --git a/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc b/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc index 013284c888f7..d86fd180ec7f 100644 --- a/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc +++ b/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc @@ -5,18 +5,19 @@ #include "base/synchronization/condition_variable.h" #include +#include #include -#include "base/logging.h" #include "base/synchronization/lock.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" +#include "build/build_config.h" namespace base { ConditionVariable::ConditionVariable(Lock* user_lock) : user_mutex_(user_lock->lock_.native_handle()) -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() , user_lock_(user_lock) #endif { @@ -42,31 +43,45 @@ ConditionVariable::ConditionVariable(Lock* user_lock) } ConditionVariable::~ConditionVariable() { +#if defined(OS_MACOSX) + // This hack is necessary to avoid a fatal pthreads subsystem bug in the + // Darwin kernel. http://crbug.com/517681. + { + base::Lock lock; + base::AutoLock l(lock); + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1; + pthread_cond_timedwait_relative_np(&condition_, lock.lock_.native_handle(), + &ts); + } +#endif + int rv = pthread_cond_destroy(&condition_); DCHECK_EQ(0, rv); } void ConditionVariable::Wait() { base::ThreadRestrictions::AssertWaitAllowed(); -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() user_lock_->CheckHeldAndUnmark(); #endif int rv = pthread_cond_wait(&condition_, user_mutex_); DCHECK_EQ(0, rv); -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() user_lock_->CheckUnheldAndMark(); #endif } void ConditionVariable::TimedWait(const TimeDelta& max_time) { base::ThreadRestrictions::AssertWaitAllowed(); - int64 usecs = max_time.InMicroseconds(); + int64_t usecs = max_time.InMicroseconds(); struct timespec relative_time; relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond; relative_time.tv_nsec = (usecs % Time::kMicrosecondsPerSecond) * Time::kNanosecondsPerMicrosecond; -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() user_lock_->CheckHeldAndUnmark(); #endif @@ -104,7 +119,7 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) { #endif // OS_MACOSX DCHECK(rv == 0 || rv == ETIMEDOUT); -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() user_lock_->CheckUnheldAndMark(); #endif } diff --git a/security/sandbox/chromium/base/synchronization/lock.cc b/security/sandbox/chromium/base/synchronization/lock.cc index b1576c50c713..03297ada52fb 100644 --- a/security/sandbox/chromium/base/synchronization/lock.cc +++ b/security/sandbox/chromium/base/synchronization/lock.cc @@ -6,10 +6,9 @@ // is functionally a wrapper around the LockImpl class, so the only // real intelligence in the class is in the debugging logic. -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) - #include "base/synchronization/lock.h" -#include "base/logging.h" + +#if DCHECK_IS_ON() namespace base { @@ -36,4 +35,4 @@ void Lock::CheckUnheldAndMark() { } // namespace base -#endif // !NDEBUG || DCHECK_ALWAYS_ON +#endif // DCHECK_IS_ON() diff --git a/security/sandbox/chromium/base/synchronization/lock.h b/security/sandbox/chromium/base/synchronization/lock.h index f384e4147281..f7dd35dccba3 100644 --- a/security/sandbox/chromium/base/synchronization/lock.h +++ b/security/sandbox/chromium/base/synchronization/lock.h @@ -6,8 +6,11 @@ #define BASE_SYNCHRONIZATION_LOCK_H_ #include "base/base_export.h" +#include "base/logging.h" +#include "base/macros.h" #include "base/synchronization/lock_impl.h" #include "base/threading/platform_thread.h" +#include "build/build_config.h" namespace base { @@ -16,7 +19,7 @@ namespace base { // AssertAcquired() method. class BASE_EXPORT Lock { public: -#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#if !DCHECK_IS_ON() // Optimized wrapper implementation Lock() : lock_() {} ~Lock() {} @@ -56,7 +59,7 @@ class BASE_EXPORT Lock { } void AssertAcquired() const; -#endif // NDEBUG && !DCHECK_ALWAYS_ON +#endif // DCHECK_IS_ON() #if defined(OS_POSIX) // The posix implementation of ConditionVariable needs to be able @@ -70,7 +73,7 @@ class BASE_EXPORT Lock { #endif private: -#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) +#if DCHECK_IS_ON() // Members and routines taking care of locks assertions. // Note that this checks for recursive locks and allows them // if the variable is set. This is allowed by the underlying implementation @@ -82,7 +85,7 @@ class BASE_EXPORT Lock { // All private data is implicitly protected by lock_. // Be VERY careful to only access members under that lock. base::PlatformThreadRef owning_thread_ref_; -#endif // !NDEBUG || DCHECK_ALWAYS_ON +#endif // DCHECK_IS_ON() // Platform specific underlying lock implementation. internal::LockImpl lock_; diff --git a/security/sandbox/chromium/base/synchronization/lock_impl.h b/security/sandbox/chromium/base/synchronization/lock_impl.h index 42e2f99068b9..ed85987b39e1 100644 --- a/security/sandbox/chromium/base/synchronization/lock_impl.h +++ b/security/sandbox/chromium/base/synchronization/lock_impl.h @@ -5,6 +5,8 @@ #ifndef BASE_SYNCHRONIZATION_LOCK_IMPL_H_ #define BASE_SYNCHRONIZATION_LOCK_IMPL_H_ +#include "base/base_export.h" +#include "base/macros.h" #include "build/build_config.h" #if defined(OS_WIN) @@ -13,9 +15,6 @@ #include #endif -#include "base/base_export.h" -#include "base/basictypes.h" - namespace base { namespace internal { diff --git a/security/sandbox/chromium/base/synchronization/waitable_event.h b/security/sandbox/chromium/base/synchronization/waitable_event.h index 39c0d11538cd..b5d91d00b51f 100644 --- a/security/sandbox/chromium/base/synchronization/waitable_event.h +++ b/security/sandbox/chromium/base/synchronization/waitable_event.h @@ -5,8 +5,11 @@ #ifndef BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ #define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ +#include + #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" +#include "build/build_config.h" #if defined(OS_WIN) #include "base/win/scoped_handle.h" @@ -21,9 +24,6 @@ namespace base { -// This replaces INFINITE from Win32 -static const int kNoTimeout = -1; - class TimeDelta; // A WaitableEvent can be a useful thread synchronization tool when you want to @@ -53,11 +53,7 @@ class BASE_EXPORT WaitableEvent { // Create a WaitableEvent from an Event HANDLE which has already been // created. This objects takes ownership of the HANDLE and will close it when // deleted. - // TODO(rvargas): Pass ScopedHandle instead (and on Release). - explicit WaitableEvent(HANDLE event_handle); - - // Releases ownership of the handle from this object. - HANDLE Release(); + explicit WaitableEvent(win::ScopedHandle event_handle); #endif ~WaitableEvent(); diff --git a/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc b/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc index 696ffc7a02ab..64d4376fe565 100644 --- a/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc +++ b/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc @@ -2,13 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include #include "base/logging.h" -#include "base/synchronization/waitable_event.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" #include "base/threading/thread_restrictions.h" // ----------------------------------------------------------------------------- diff --git a/security/sandbox/chromium/base/task_runner.h b/security/sandbox/chromium/base/task_runner.h index 7d07b8ceb028..6dd82ccaca99 100644 --- a/security/sandbox/chromium/base/task_runner.h +++ b/security/sandbox/chromium/base/task_runner.h @@ -5,8 +5,9 @@ #ifndef BASE_TASK_RUNNER_H_ #define BASE_TASK_RUNNER_H_ +#include + #include "base/base_export.h" -#include "base/basictypes.h" #include "base/callback_forward.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" @@ -104,7 +105,7 @@ class BASE_EXPORT TaskRunner // public: // void GetData() { // scoped_refptr buffer = new DataBuffer(); - // target_thread_.message_loop_proxy()->PostTaskAndReply( + // target_thread_.task_runner()->PostTaskAndReply( // FROM_HERE, // base::Bind(&DataBuffer::AddData, buffer), // base::Bind(&DataLoader::OnDataReceived, AsWeakPtr(), buffer)); diff --git a/security/sandbox/chromium/base/template_util.h b/security/sandbox/chromium/base/template_util.h index 83fa322a6722..d58807a77ce7 100644 --- a/security/sandbox/chromium/base/template_util.h +++ b/security/sandbox/chromium/base/template_util.h @@ -5,7 +5,7 @@ #ifndef BASE_TEMPLATE_UTIL_H_ #define BASE_TEMPLATE_UTIL_H_ -#include // For size_t. +#include #include "build/build_config.h" @@ -117,12 +117,6 @@ struct is_class sizeof(internal::YesType)> { }; -template -struct enable_if {}; - -template -struct enable_if { typedef T type; }; - } // namespace base #endif // BASE_TEMPLATE_UTIL_H_ diff --git a/security/sandbox/chromium/base/third_party/icu/icu_utf.cc b/security/sandbox/chromium/base/third_party/icu/icu_utf.cc index b47c8ac3c112..2b67c5d9c211 100644 --- a/security/sandbox/chromium/base/third_party/icu/icu_utf.cc +++ b/security/sandbox/chromium/base/third_party/icu/icu_utf.cc @@ -74,32 +74,28 @@ namespace base_icu { * lead bytes above 0xf4 are illegal. * We keep them in this table for skipping long ISO 10646-UTF-8 sequences. */ -const uint8 -utf8_countTrailBytes[256]={ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +const uint8_t utf8_countTrailBytes[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, - 3, 3, 3, /* illegal in Unicode */ - 4, 4, 4, 4, /* illegal in Unicode */ - 5, 5, /* illegal in Unicode */ - 0, 0 /* illegal bytes 0xfe and 0xff */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, /* illegal in Unicode */ + 4, 4, 4, 4, /* illegal in Unicode */ + 5, 5, /* illegal in Unicode */ + 0, 0 /* illegal bytes 0xfe and 0xff */ }; static const UChar32 @@ -133,12 +129,15 @@ utf8_errorValue[6]={ * * Note that a UBool is the same as an int8_t. */ -UChar32 -utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool strict) { - int32 i=*pi; - uint8 count=CBU8_COUNT_TRAIL_BYTES(c); +UChar32 utf8_nextCharSafeBody(const uint8_t* s, + int32_t* pi, + int32_t length, + UChar32 c, + UBool strict) { + int32_t i = *pi; + uint8_t count = CBU8_COUNT_TRAIL_BYTES(c); if((i)+count<=(length)) { - uint8 trail, illegal=0; + uint8_t trail, illegal = 0; CBU8_MASK_LEAD_BYTE((c), count); /* count==0 for illegally leading trail bytes and the illegal bytes 0xfe and 0xff */ @@ -192,7 +191,7 @@ utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool /* illegal is also set if count>=4 */ if(illegal || (c)0 && CBU8_IS_TRAIL(s[i])) { @@ -210,7 +209,7 @@ utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool } } else /* too few bytes left */ { /* error handling */ - int32 i0=i; + int32_t i0 = i; /* don't just set (i)=(length) in case there is an illegal sequence */ while((i)<(length) && CBU8_IS_TRAIL(s[i])) { ++(i); diff --git a/security/sandbox/chromium/base/third_party/icu/icu_utf.h b/security/sandbox/chromium/base/third_party/icu/icu_utf.h index 2b993b099f82..4370fdec15e0 100644 --- a/security/sandbox/chromium/base/third_party/icu/icu_utf.h +++ b/security/sandbox/chromium/base/third_party/icu/icu_utf.h @@ -17,13 +17,13 @@ #ifndef BASE_THIRD_PARTY_ICU_ICU_UTF_H_ #define BASE_THIRD_PARTY_ICU_ICU_UTF_H_ -#include "base/basictypes.h" +#include namespace base_icu { -typedef int32 UChar32; -typedef uint16 UChar; -typedef int8 UBool; +typedef int32_t UChar32; +typedef uint16_t UChar; +typedef int8_t UBool; // General --------------------------------------------------------------------- // from utf.h @@ -54,10 +54,9 @@ typedef int8 UBool; * @return TRUE or FALSE * @stable ICU 2.4 */ -#define CBU_IS_UNICODE_NONCHAR(c) \ - ((c)>=0xfdd0 && \ - ((uint32)(c)<=0xfdef || ((c)&0xfffe)==0xfffe) && \ - (uint32)(c)<=0x10ffff) +#define CBU_IS_UNICODE_NONCHAR(c) \ + ((c) >= 0xfdd0 && ((uint32_t)(c) <= 0xfdef || ((c)&0xfffe) == 0xfffe) && \ + (uint32_t)(c) <= 0x10ffff) /** * Is c a Unicode code point value (0..U+10ffff) @@ -76,11 +75,10 @@ typedef int8 UBool; * @return TRUE or FALSE * @stable ICU 2.4 */ -#define CBU_IS_UNICODE_CHAR(c) \ - ((uint32)(c)<0xd800 || \ - ((uint32)(c)>0xdfff && \ - (uint32)(c)<=0x10ffff && \ - !CBU_IS_UNICODE_NONCHAR(c))) +#define CBU_IS_UNICODE_CHAR(c) \ + ((uint32_t)(c) < 0xd800 || \ + ((uint32_t)(c) > 0xdfff && (uint32_t)(c) <= 0x10ffff && \ + !CBU_IS_UNICODE_NONCHAR(c))) /** * Is this code point a surrogate (U+d800..U+dfff)? @@ -103,13 +101,14 @@ typedef int8 UBool; // UTF-8 macros ---------------------------------------------------------------- // from utf8.h -extern const uint8 utf8_countTrailBytes[256]; +extern const uint8_t utf8_countTrailBytes[256]; /** * Count the trail bytes for a UTF-8 lead byte. * @internal */ -#define CBU8_COUNT_TRAIL_BYTES(leadByte) (base_icu::utf8_countTrailBytes[(uint8)leadByte]) +#define CBU8_COUNT_TRAIL_BYTES(leadByte) \ + (base_icu::utf8_countTrailBytes[(uint8_t)leadByte]) /** * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value. @@ -131,7 +130,7 @@ extern const uint8 utf8_countTrailBytes[256]; * @return TRUE or FALSE * @stable ICU 2.4 */ -#define CBU8_IS_LEAD(c) ((uint8)((c)-0xc0)<0x3e) +#define CBU8_IS_LEAD(c) ((uint8_t)((c)-0xc0) < 0x3e) /** * Is this code unit (byte) a UTF-8 trail byte? @@ -148,16 +147,16 @@ extern const uint8 utf8_countTrailBytes[256]; * @return 1..4, or 0 if c is a surrogate or not a Unicode code point * @stable ICU 2.4 */ -#define CBU8_LENGTH(c) \ - ((uint32)(c)<=0x7f ? 1 : \ - ((uint32)(c)<=0x7ff ? 2 : \ - ((uint32)(c)<=0xd7ff ? 3 : \ - ((uint32)(c)<=0xdfff || (uint32)(c)>0x10ffff ? 0 : \ - ((uint32)(c)<=0xffff ? 3 : 4)\ - ) \ - ) \ - ) \ - ) +#define CBU8_LENGTH(c) \ + ((uint32_t)(c) <= 0x7f \ + ? 1 \ + : ((uint32_t)(c) <= 0x7ff \ + ? 2 \ + : ((uint32_t)(c) <= 0xd7ff \ + ? 3 \ + : ((uint32_t)(c) <= 0xdfff || (uint32_t)(c) > 0x10ffff \ + ? 0 \ + : ((uint32_t)(c) <= 0xffff ? 3 : 4))))) /** * The maximum number of UTF-8 code units (bytes) per Unicode code point (U+0000..U+10ffff). @@ -170,7 +169,11 @@ extern const uint8 utf8_countTrailBytes[256]; * Function for handling "next code point" with error-checking. * @internal */ -UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool strict); +UChar32 utf8_nextCharSafeBody(const uint8_t* s, + int32_t* pi, + int32_t length, + UChar32 c, + UBool strict); /** * Get a code point from a string at a code point boundary offset, @@ -183,55 +186,59 @@ UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c * If the offset points to a trail byte or an illegal UTF-8 sequence, then * c is set to a negative value. * - * @param s const uint8 * string + * @param s const uint8_t * string * @param i string offset, i=0x80) { \ - if(CBU8_IS_LEAD(c)) { \ - (c)=base_icu::utf8_nextCharSafeBody((const uint8 *)s, &(i), (int32)(length), c, -1); \ - } else { \ - (c)=CBU_SENTINEL; \ - } \ - } \ -} +#define CBU8_NEXT(s, i, length, c) \ + { \ + (c) = (s)[(i)++]; \ + if (((uint8_t)(c)) >= 0x80) { \ + if (CBU8_IS_LEAD(c)) { \ + (c) = base_icu::utf8_nextCharSafeBody((const uint8_t*)s, &(i), \ + (int32_t)(length), c, -1); \ + } else { \ + (c) = CBU_SENTINEL; \ + } \ + } \ + } /** * Append a code point to a string, overwriting 1 to 4 bytes. * The offset points to the current end of the string contents * and is advanced (post-increment). - * "Unsafe" macro, assumes a valid code point and sufficient space in the string. + * "Unsafe" macro, assumes a valid code point and sufficient space in the + * string. * Otherwise, the result is undefined. * - * @param s const uint8 * string buffer + * @param s const uint8_t * string buffer * @param i string offset * @param c code point to append * @see CBU8_APPEND * @stable ICU 2.4 */ -#define CBU8_APPEND_UNSAFE(s, i, c) { \ - if((uint32)(c)<=0x7f) { \ - (s)[(i)++]=(uint8)(c); \ - } else { \ - if((uint32)(c)<=0x7ff) { \ - (s)[(i)++]=(uint8)(((c)>>6)|0xc0); \ - } else { \ - if((uint32)(c)<=0xffff) { \ - (s)[(i)++]=(uint8)(((c)>>12)|0xe0); \ - } else { \ - (s)[(i)++]=(uint8)(((c)>>18)|0xf0); \ - (s)[(i)++]=(uint8)((((c)>>12)&0x3f)|0x80); \ - } \ - (s)[(i)++]=(uint8)((((c)>>6)&0x3f)|0x80); \ - } \ - (s)[(i)++]=(uint8)(((c)&0x3f)|0x80); \ - } \ -} +#define CBU8_APPEND_UNSAFE(s, i, c) \ + { \ + if ((uint32_t)(c) <= 0x7f) { \ + (s)[(i)++] = (uint8_t)(c); \ + } else { \ + if ((uint32_t)(c) <= 0x7ff) { \ + (s)[(i)++] = (uint8_t)(((c) >> 6) | 0xc0); \ + } else { \ + if ((uint32_t)(c) <= 0xffff) { \ + (s)[(i)++] = (uint8_t)(((c) >> 12) | 0xe0); \ + } else { \ + (s)[(i)++] = (uint8_t)(((c) >> 18) | 0xf0); \ + (s)[(i)++] = (uint8_t)((((c) >> 12) & 0x3f) | 0x80); \ + } \ + (s)[(i)++] = (uint8_t)((((c) >> 6) & 0x3f) | 0x80); \ + } \ + (s)[(i)++] = (uint8_t)(((c)&0x3f) | 0x80); \ + } \ + } // UTF-16 macros --------------------------------------------------------------- // from utf16.h @@ -325,7 +332,7 @@ UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c * @return 1 or 2 * @stable ICU 2.4 */ -#define CBU16_LENGTH(c) ((uint32)(c)<=0xffff ? 1 : 2) +#define CBU16_LENGTH(c) ((uint32_t)(c) <= 0xffff ? 1 : 2) /** * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff). @@ -353,16 +360,17 @@ UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c * @param c output UChar32 variable * @stable ICU 2.4 */ -#define CBU16_NEXT(s, i, length, c) { \ - (c)=(s)[(i)++]; \ - if(CBU16_IS_LEAD(c)) { \ - uint16 __c2; \ - if((i)<(length) && CBU16_IS_TRAIL(__c2=(s)[(i)])) { \ - ++(i); \ - (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \ - } \ - } \ -} +#define CBU16_NEXT(s, i, length, c) \ + { \ + (c) = (s)[(i)++]; \ + if (CBU16_IS_LEAD(c)) { \ + uint16_t __c2; \ + if ((i) < (length) && CBU16_IS_TRAIL(__c2 = (s)[(i)])) { \ + ++(i); \ + (c) = CBU16_GET_SUPPLEMENTARY((c), __c2); \ + } \ + } \ + } /** * Append a code point to a string, overwriting 1 or 2 code units. @@ -377,14 +385,15 @@ UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c * @see CBU16_APPEND * @stable ICU 2.4 */ -#define CBU16_APPEND_UNSAFE(s, i, c) { \ - if((uint32)(c)<=0xffff) { \ - (s)[(i)++]=(uint16)(c); \ - } else { \ - (s)[(i)++]=(uint16)(((c)>>10)+0xd7c0); \ - (s)[(i)++]=(uint16)(((c)&0x3ff)|0xdc00); \ - } \ -} +#define CBU16_APPEND_UNSAFE(s, i, c) \ + { \ + if ((uint32_t)(c) <= 0xffff) { \ + (s)[(i)++] = (uint16_t)(c); \ + } else { \ + (s)[(i)++] = (uint16_t)(((c) >> 10) + 0xd7c0); \ + (s)[(i)++] = (uint16_t)(((c)&0x3ff) | 0xdc00); \ + } \ + } } // namesapce base_icu diff --git a/security/sandbox/chromium/base/third_party/valgrind/LICENSE b/security/sandbox/chromium/base/third_party/valgrind/LICENSE new file mode 100644 index 000000000000..41f677bd1797 --- /dev/null +++ b/security/sandbox/chromium/base/third_party/valgrind/LICENSE @@ -0,0 +1,39 @@ + Notice that the following BSD-style license applies to the Valgrind header + files used by Chromium (valgrind.h and memcheck.h). However, the rest of + Valgrind is licensed under the terms of the GNU General Public License, + version 2, unless otherwise indicated. + + ---------------------------------------------------------------- + + Copyright (C) 2000-2008 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/security/sandbox/chromium/base/third_party/valgrind/valgrind.h b/security/sandbox/chromium/base/third_party/valgrind/valgrind.h new file mode 100644 index 000000000000..0bae0aa130ee --- /dev/null +++ b/security/sandbox/chromium/base/third_party/valgrind/valgrind.h @@ -0,0 +1,4792 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2010 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 6 + + +#include + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_ppc64_aix5 +#undef PLAT_ppc32_aix5 +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_arm_linux + +#if defined(_AIX) && defined(__64BIT__) +# define PLAT_ppc64_aix5 1 +#elif defined(_AIX) && !defined(__64BIT__) +# define PLAT_ppc32_aix5 1 +#elif defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif defined(__MINGW32__) || defined(__CYGWIN32__) || defined(_WIN32) && defined(_M_IX86) +# define PLAT_x86_win32 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) +# define PLAT_ppc64_linux 1 +#elif defined(__linux__) && defined(__arm__) +# define PLAT_arm_linux 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { \ + (_zzq_rlval) = (_zzq_default); \ + } + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || (defined(PLAT_x86_win32) && defined(__GNUC__)) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ + +/* ------------------------- x86-Win32 ------------------------- */ + +#if defined(PLAT_x86_win32) && !defined(__GNUC__) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#if defined(_MSC_VER) + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm rol edi, 3 __asm rol edi, 13 \ + __asm rol edi, 29 __asm rol edi, 19 + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile uintptr_t _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (uintptr_t)(_zzq_request); \ + _zzq_args[1] = (uintptr_t)(_zzq_arg1); \ + _zzq_args[2] = (uintptr_t)(_zzq_arg2); \ + _zzq_args[3] = (uintptr_t)(_zzq_arg3); \ + _zzq_args[4] = (uintptr_t)(_zzq_arg4); \ + _zzq_args[5] = (uintptr_t)(_zzq_arg5); \ + __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + __asm xchg ebx,ebx \ + __asm mov _zzq_result, edx \ + } \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + __asm xchg ecx,ecx \ + __asm mov __addr, eax \ + } \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX ERROR + +#else +#error Unsupported compiler. +#endif + +#endif /* PLAT_x86_win32 */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + { volatile unsigned long long int _zzq_args[6]; \ + volatile unsigned long long int _zzq_result; \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[6]; \ + register unsigned long long int _zzq_result __asm__("r3"); \ + register unsigned long long int* _zzq_ptr __asm__("r4"); \ + _zzq_args[0] = (unsigned long long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1" \ + : "=r" (_zzq_result) \ + : "0" (_zzq_default), "r" (_zzq_ptr) \ + : "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr __asm__("r3"); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4" \ + : "=r" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#endif /* PLAT_arm_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + unsigned int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \ + "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned int _zzq_args[7]; \ + register unsigned int _zzq_result; \ + register unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "lwz 3, 24(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +typedef + struct { + unsigned long long int nraddr; /* where's the code? */ + unsigned long long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST( \ + _zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + { unsigned long long int _zzq_args[7]; \ + register unsigned long long int _zzq_result; \ + register unsigned long long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int long long)(_zzq_request); \ + _zzq_args[1] = (unsigned int long long)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int long long)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int long long)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int long long)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int long long)(_zzq_arg5); \ + _zzq_args[6] = (unsigned int long long)(_zzq_default); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 4,%1\n\t" \ + "ld 3, 48(4)\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" \ + : "=b" (_zzq_result) \ + : "b" (_zzq_ptr) \ + : "r3", "r4", "cc", "memory"); \ + _zzq_rlval = _zzq_result; \ + } + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + register unsigned long long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "r3", "cc", "memory" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#endif /* PLAT_ppc64_aix5 */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgwZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgwZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subl $12, %%esp\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subl $8, %%esp\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subl $4, %%esp\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $16, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subl $12, %%esp\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subl $8, %%esp\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subl $4, %%esp\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $32, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subl $12, %%esp\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subl $8, %%esp\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subl $4, %%esp\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + "addl $48, %%esp\n" \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* This is all pretty complex. It's so as to make stack unwinding + work reliably. See bug 243270. The basic problem is the sub and + add of 128 of %rsp in all of the following macros. If gcc believes + the CFA is in %rsp, then unwinding may fail, because what's at the + CFA is not what gcc "expected" when it constructs the CFIs for the + places where the macros are instantiated. + + But we can't just add a CFI annotation to increase the CFA offset + by 128, to match the sub of 128 from %rsp, because we don't know + whether gcc has chosen %rsp as the CFA at that point, or whether it + has chosen some other register (eg, %rbp). In the latter case, + adding a CFI annotation to change the CFA offset is simply wrong. + + So the solution is to get hold of the CFA using + __builtin_dwarf_cfa(), put it in a known register, and add a + CFI annotation to say what the register is. We choose %rbp for + this (perhaps perversely), because: + + (1) %rbp is already subject to unwinding. If a new register was + chosen then the unwinder would have to unwind it in all stack + traces, which is expensive, and + + (2) %rbp is already subject to precise exception updates in the + JIT. If a new register was chosen, we'd have to have precise + exceptions for it too, which reduces performance of the + generated code. + + However .. one extra complication. We can't just whack the result + of __builtin_dwarf_cfa() into %rbp and then add %rbp to the + list of trashed registers at the end of the inline assembly + fragments; gcc won't allow %rbp to appear in that list. Hence + instead we need to stash %rbp in %r15 for the duration of the asm, + and say that %r15 is trashed instead. gcc seems happy to go with + that. + + Oh .. and this all needs to be conditionalised so that it is + unchanged from before this commit, when compiled with older gccs + that don't support __builtin_dwarf_cfa. Furthermore, since + this header file is freestanding, it has to be independent of + config.h, and so the following conditionalisation cannot depend on + configure time checks. + + Although it's not clear from + 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', + this expression excludes Darwin. + .cfi directives in Darwin assembly appear to be completely + different and I haven't investigated how they work. + + For even more entertainment value, note we have to use the + completely undocumented __builtin_dwarf_cfa(), which appears to + really compute the CFA, whereas __builtin_frame_address(0) claims + to but actually doesn't. See + https://bugs.kde.org/show_bug.cgi?id=243270#c47 +*/ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"r"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + "movq %%rbp, %%r15\n\t" \ + "movq %2, %%rbp\n\t" \ + ".cfi_remember_state\n\t" \ + ".cfi_def_cfa rbp, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "movq %%r15, %%rbp\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE +# define VALGRIND_CFI_EPILOGUE +#endif + + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastyness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux, ppc32-aix5, ppc64-aix5); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $136,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $8, %%rsp\n" \ + "addq $136,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $16, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $136,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $24, %%rsp\n" \ + "addq $136,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $32, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $136,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $40, %%rsp\n" \ + "addq $136,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + "addq $48, %%rsp\n" \ + "addq $128,%%rsp\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,16\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "addi 1,1,32\n\t" \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)" /* restore tocptr */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,128" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + "addi 1,1,144" /* restore frame */ \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #4 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #8 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #12 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #16 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #20 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #24 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #28 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory",__CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + "add sp, sp, #32 \n\t" \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ ppc32-aix5 ------------------------- */ + +#if defined(PLAT_ppc32_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "lwz 3," #_n_fr "(1)\n\t" \ + "stw 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc32-aix5, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(64) \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(64) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "stw 2,-8(11)\n\t" /* save tocptr */ \ + "lwz 2,-4(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(72) \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,68(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,64(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,60(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,56(1)\n\t" \ + /* args1-8 */ \ + "lwz 3, 4(11)\n\t" /* arg1->r3 */ \ + "lwz 4, 8(11)\n\t" /* arg2->r4 */ \ + "lwz 5, 12(11)\n\t" /* arg3->r5 */ \ + "lwz 6, 16(11)\n\t" /* arg4->r6 */ \ + "lwz 7, 20(11)\n\t" /* arg5->r7 */ \ + "lwz 8, 24(11)\n\t" /* arg6->r8 */ \ + "lwz 9, 28(11)\n\t" /* arg7->r9 */ \ + "lwz 10, 32(11)\n\t" /* arg8->r10 */ \ + "lwz 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "lwz 2,-8(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(72) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_aix5 */ + +/* ------------------------ ppc64-aix5 ------------------------- */ + +#if defined(PLAT_ppc64_aix5) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Expand the stack frame, copying enough info that unwinding + still works. Trashes r3. */ + +#define VG_EXPAND_FRAME_BY_trashes_r3(_n_fr) \ + "addi 1,1,-" #_n_fr "\n\t" \ + "ld 3," #_n_fr "(1)\n\t" \ + "std 3,0(1)\n\t" + +#define VG_CONTRACT_FRAME_BY(_n_fr) \ + "addi 1,1," #_n_fr "\n\t" + +/* These CALL_FN_ macros assume that on ppc64-aix5, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(128) \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(128) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + "mr 11,%1\n\t" \ + VG_EXPAND_FRAME_BY_trashes_r3(512) \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + VG_EXPAND_FRAME_BY_trashes_r3(144) \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VG_CONTRACT_FRAME_BY(144) \ + VG_CONTRACT_FRAME_BY(512) \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64_aix5 */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, + + /* Querying of debug info. */ + VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + + +/* + * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind + * client request and whose value equals the client request result. + */ + +#if defined(NVALGRIND) + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (_zzq_default) + +#else /*defined(NVALGRIND)*/ + +#if defined(_MSC_VER) + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (vg_VALGRIND_DO_CLIENT_REQUEST_EXPR((uintptr_t)(_zzq_default), \ + (_zzq_request), (uintptr_t)(_zzq_arg1), (uintptr_t)(_zzq_arg2), \ + (uintptr_t)(_zzq_arg3), (uintptr_t)(_zzq_arg4), \ + (uintptr_t)(_zzq_arg5))) + +static __inline unsigned +vg_VALGRIND_DO_CLIENT_REQUEST_EXPR(uintptr_t _zzq_default, + unsigned _zzq_request, uintptr_t _zzq_arg1, + uintptr_t _zzq_arg2, uintptr_t _zzq_arg3, + uintptr_t _zzq_arg4, uintptr_t _zzq_arg5) +{ + unsigned _zzq_rlval; + VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, _zzq_request, + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5); + return _zzq_rlval; +} + +#else /*defined(_MSC_VER)*/ + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (__extension__({unsigned int _zzq_rlval; \ + VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + _zzq_rlval; \ + })) + +#endif /*defined(_MSC_VER)*/ + +#endif /*defined(NVALGRIND)*/ + + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0) \ + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0); \ + } + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**** " part at the + start and the backtrace (if present). */ + +#if defined(NVALGRIND) + +# define VALGRIND_PRINTF(...) +# define VALGRIND_PRINTF_BACKTRACE(...) + +#else /* NVALGRIND */ + +#if !defined(_MSC_VER) +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +} + +#if !defined(_MSC_VER) +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ + unsigned long _qzz_res; + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +} + +#endif /* NVALGRIND */ + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + __extension__ \ + ({unsigned long _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0); \ + _qyy_res; \ + }) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + __extension__ \ + ({unsigned int _qyy_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qyy_res, 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0); \ + _qyy_res; \ + }) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + In many cases, these two client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Note: there is currently no VALGRIND_REALLOCLIKE_BLOCK client request; it + has to be emulated with MALLOCLIKE/FREELIKE and memory copying. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0); \ + } + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0); \ + } + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0); \ + } + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0); \ + } + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0); \ + } + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0); \ + } + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0); \ + } + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0); \ + } + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Mark a piece of memory as being a stack. Returns a stack id. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0); \ + _qzz_res; \ + }) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0); \ + } + +/* Change the start and end address of the stack id. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0); \ + } + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0); \ + } + +/* Map a code address to a source file name and line number. buf64 + must point to a 64-byte buffer in the caller's address space. The + result will be dumped in there and is guaranteed to be zero + terminated. If no info is found, the first byte is set to zero. */ +#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ + {unsigned int _qzz_res; \ + VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0, \ + VG_USERREQ__MAP_IP_TO_SRCLOC, \ + addr, buf64, 0, 0, 0); \ + } + + +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64_linux +#undef PLAT_arm_linux +#undef PLAT_ppc32_aix5 +#undef PLAT_ppc64_aix5 + +#endif /* __VALGRIND_H */ diff --git a/security/sandbox/chromium/base/threading/platform_thread.h b/security/sandbox/chromium/base/threading/platform_thread.h index 28743145610c..e2b09bcb5bfd 100644 --- a/security/sandbox/chromium/base/threading/platform_thread.h +++ b/security/sandbox/chromium/base/threading/platform_thread.h @@ -9,8 +9,10 @@ #ifndef BASE_THREADING_PLATFORM_THREAD_H_ #define BASE_THREADING_PLATFORM_THREAD_H_ +#include + #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" #include "base/time/time.h" #include "build/build_config.h" @@ -73,21 +75,9 @@ class PlatformThreadHandle { typedef pthread_t Handle; #endif - PlatformThreadHandle() - : handle_(0), - id_(0) { - } + PlatformThreadHandle() : handle_(0) {} - explicit PlatformThreadHandle(Handle handle) - : handle_(handle), - id_(0) { - } - - PlatformThreadHandle(Handle handle, - PlatformThreadId id) - : handle_(handle), - id_(id) { - } + explicit PlatformThreadHandle(Handle handle) : handle_(handle) {} bool is_equal(const PlatformThreadHandle& other) const { return handle_ == other.handle_; @@ -102,23 +92,22 @@ class PlatformThreadHandle { } private: - friend class PlatformThread; - Handle handle_; - PlatformThreadId id_; }; const PlatformThreadId kInvalidThreadId(0); -// Valid values for SetThreadPriority() -enum ThreadPriority{ - kThreadPriority_Normal, - // Suitable for low-latency, glitch-resistant audio. - kThreadPriority_RealtimeAudio, - // Suitable for threads which generate data for the display (at ~60Hz). - kThreadPriority_Display, +// Valid values for priority of Thread::Options and SimpleThread::Options, and +// SetCurrentThreadPriority(), listed in increasing order of importance. +enum class ThreadPriority { // Suitable for threads that shouldn't disrupt high priority work. - kThreadPriority_Background + BACKGROUND, + // Default priority level. + NORMAL, + // Suitable for threads which generate data for the display (at ~60Hz). + DISPLAY, + // Suitable for low-latency, glitch-resistant audio. + REALTIME_AUDIO, }; // A namespace for low-level thread functions. @@ -141,7 +130,10 @@ class BASE_EXPORT PlatformThread { // we're on the right thread quickly. static PlatformThreadRef CurrentRef(); - // Get the current handle. + // Get the handle representing the current thread. On Windows, this is a + // pseudo handle constant which will always represent the thread using it and + // hence should not be shared with other threads nor be used to differentiate + // the current thread from another. static PlatformThreadHandle CurrentHandle(); // Yield the current thread so another thread can be scheduled. @@ -151,9 +143,8 @@ class BASE_EXPORT PlatformThread { static void Sleep(base::TimeDelta duration); // Sets the thread name visible to debuggers/tools. This has no effect - // otherwise. This name pointer is not copied internally. Thus, it must stay - // valid until the thread ends. - static void SetName(const char* name); + // otherwise. + static void SetName(const std::string& name); // Gets the thread name, if previously set by SetName. static const char* GetName(); @@ -166,14 +157,15 @@ class BASE_EXPORT PlatformThread { // NOTE: When you are done with the thread handle, you must call Join to // release system resources associated with the thread. You must ensure that // the Delegate object outlives the thread. - static bool Create(size_t stack_size, Delegate* delegate, - PlatformThreadHandle* thread_handle); + static bool Create(size_t stack_size, + Delegate* delegate, + PlatformThreadHandle* thread_handle) { + return CreateWithPriority(stack_size, delegate, thread_handle, + ThreadPriority::NORMAL); + } // CreateWithPriority() does the same thing as Create() except the priority of - // the thread is set based on |priority|. Can be used in place of Create() - // followed by SetThreadPriority(). SetThreadPriority() has not been - // implemented on the Linux platform yet, this is the only way to get a high - // priority thread on Linux. + // the thread is set based on |priority|. static bool CreateWithPriority(size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle, ThreadPriority priority); @@ -188,8 +180,15 @@ class BASE_EXPORT PlatformThread { // |thread_handle|. static void Join(PlatformThreadHandle thread_handle); - static void SetThreadPriority(PlatformThreadHandle handle, - ThreadPriority priority); + // Toggles the current thread's priority at runtime. A thread may not be able + // to raise its priority back up after lowering it if the process does not + // have a proper permission, e.g. CAP_SYS_NICE on Linux. + // Since changing other threads' priority is not permitted in favor of + // security, this interface is restricted to change only the current thread + // priority (https://crbug.com/399473). + static void SetCurrentThreadPriority(ThreadPriority priority); + + static ThreadPriority GetCurrentThreadPriority(); private: DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); diff --git a/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc new file mode 100644 index 000000000000..9af02044fcee --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc @@ -0,0 +1,35 @@ +// 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 "base/threading/platform_thread_internal_posix.h" + +#include "base/logging.h" + +namespace base { + +namespace internal { + +int ThreadPriorityToNiceValue(ThreadPriority priority) { + for (const ThreadPriorityToNiceValuePair& pair : + kThreadPriorityToNiceValueMap) { + if (pair.priority == priority) + return pair.nice_value; + } + NOTREACHED() << "Unknown ThreadPriority"; + return 0; +} + +ThreadPriority NiceValueToThreadPriority(int nice_value) { + for (const ThreadPriorityToNiceValuePair& pair : + kThreadPriorityToNiceValueMap) { + if (pair.nice_value == nice_value) + return pair.priority; + } + NOTREACHED() << "Unknown nice value"; + return ThreadPriority::NORMAL; +} + +} // namespace internal + +} // namespace base diff --git a/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h new file mode 100644 index 000000000000..05a8d1e26e78 --- /dev/null +++ b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h @@ -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. + +#ifndef BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ +#define BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ + +#include "base/threading/platform_thread.h" + +namespace base { + +namespace internal { + +struct ThreadPriorityToNiceValuePair { + ThreadPriority priority; + int nice_value; +}; +extern const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4]; + +// Returns the nice value matching |priority| based on the platform-specific +// implementation of kThreadPriorityToNiceValueMap. +int ThreadPriorityToNiceValue(ThreadPriority priority); + +// Returns the ThreadPrioirty matching |nice_value| based on the platform- +// specific implementation of kThreadPriorityToNiceValueMap. +ThreadPriority NiceValueToThreadPriority(int nice_value); + +// Allows platform specific tweaks to the generic POSIX solution for +// SetCurrentThreadPriority. Returns true if the platform-specific +// implementation handled this |priority| change, false if the generic +// implementation should instead proceed. +bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority); + +// Returns true if there is a platform-specific ThreadPriority set on the +// current thread (and returns the actual ThreadPriority via |priority|). +// Returns false otherwise, leaving |priority| untouched. +bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority); + +} // namespace internal + +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_ diff --git a/security/sandbox/chromium/base/threading/platform_thread_linux.cc b/security/sandbox/chromium/base/threading/platform_thread_linux.cc index d9e2bd9932f5..4057ede947e2 100644 --- a/security/sandbox/chromium/base/threading/platform_thread_linux.cc +++ b/security/sandbox/chromium/base/threading/platform_thread_linux.cc @@ -6,47 +6,78 @@ #include #include +#include #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/safe_strerror_posix.h" +#include "base/threading/platform_thread_internal_posix.h" #include "base/threading/thread_id_name_manager.h" -#include "base/threading/thread_restrictions.h" #include "base/tracked_objects.h" +#include "build/build_config.h" #if !defined(OS_NACL) +#include #include -#include -#include -#include +#include #include #endif namespace base { +namespace internal { + namespace { - -int ThreadNiceValue(ThreadPriority priority) { - switch (priority) { - case kThreadPriority_RealtimeAudio: - return -10; - case kThreadPriority_Background: - return 10; - case kThreadPriority_Normal: - return 0; - case kThreadPriority_Display: - return -6; - default: - NOTREACHED() << "Unknown priority."; - return 0; - } -} - +#if !defined(OS_NACL) +const struct sched_param kRealTimePrio = {8}; +const struct sched_param kResetPrio = {0}; +#endif } // namespace +const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = { + {ThreadPriority::BACKGROUND, 10}, + {ThreadPriority::NORMAL, 0}, + {ThreadPriority::DISPLAY, -6}, + {ThreadPriority::REALTIME_AUDIO, -10}, +}; + +bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) { +#if !defined(OS_NACL) + ThreadPriority current_priority; + if (priority != ThreadPriority::REALTIME_AUDIO && + GetCurrentThreadPriorityForPlatform(¤t_priority) && + current_priority == ThreadPriority::REALTIME_AUDIO) { + // If the pthread's round-robin scheduler is already enabled, and the new + // priority will use setpriority() instead, the pthread scheduler should be + // reset to use SCHED_OTHER so that setpriority() just works. + pthread_setschedparam(pthread_self(), SCHED_OTHER, &kResetPrio); + return false; + } + return priority == ThreadPriority::REALTIME_AUDIO && + pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0; +#else + return false; +#endif +} + +bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { +#if !defined(OS_NACL) + int maybe_sched_rr = 0; + struct sched_param maybe_realtime_prio = {0}; + if (pthread_getschedparam(pthread_self(), &maybe_sched_rr, + &maybe_realtime_prio) == 0 && + maybe_sched_rr == SCHED_RR && + maybe_realtime_prio.sched_priority == kRealTimePrio.sched_priority) { + *priority = ThreadPriority::REALTIME_AUDIO; + return true; + } +#endif + return false; +} + +} // namespace internal + // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); tracked_objects::ThreadData::InitializeThreadContext(name); @@ -63,43 +94,13 @@ void PlatformThread::SetName(const char* name) { // Note that glibc also has a 'pthread_setname_np' api, but it may not be // available everywhere and it's only benefit over using prctl directly is // that it can set the name of threads other than the current thread. - int err = prctl(PR_SET_NAME, name); + int err = prctl(PR_SET_NAME, name.c_str()); // We expect EPERM failures in sandboxed processes, just ignore those. if (err < 0 && errno != EPERM) DPLOG(ERROR) << "prctl(PR_SET_NAME)"; #endif // !defined(OS_NACL) } -// static -void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, - ThreadPriority priority) { -#if !defined(OS_NACL) - if (priority == kThreadPriority_RealtimeAudio) { - const struct sched_param kRealTimePrio = {8}; - if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) { - // Got real time priority, no need to set nice level. - return; - } - } - - // setpriority(2) should change the whole thread group's (i.e. process) - // priority. however, on linux it will only change the target thread's - // priority. see the bugs section in - // http://man7.org/linux/man-pages/man2/getpriority.2.html. - // we prefer using 0 rather than the current thread id since they are - // equivalent but it makes sandboxing easier (https://crbug.com/399473). - DCHECK_NE(handle.id_, kInvalidThreadId); - const int kNiceSetting = ThreadNiceValue(priority); - const PlatformThreadId current_id = PlatformThread::CurrentId(); - if (setpriority(PRIO_PROCESS, - handle.id_ == current_id ? 0 : handle.id_, - kNiceSetting)) { - DVPLOG(1) << "Failed to set nice value of thread (" << handle.id_ << ") to " - << kNiceSetting; - } -#endif // !defined(OS_NACL) -} - void InitThreading() {} void InitOnThread() {} diff --git a/security/sandbox/chromium/base/threading/platform_thread_posix.cc b/security/sandbox/chromium/base/threading/platform_thread_posix.cc index 42b416d4a38f..39a007316f78 100644 --- a/security/sandbox/chromium/base/threading/platform_thread_posix.cc +++ b/security/sandbox/chromium/base/threading/platform_thread_posix.cc @@ -5,28 +5,25 @@ #include "base/threading/platform_thread.h" #include +#include #include +#include +#include +#include +#include #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" -#include "base/safe_strerror_posix.h" -#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread_internal_posix.h" #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_restrictions.h" -#include "base/tracked_objects.h" - -#if defined(OS_MACOSX) -#include -#include -#endif +#include "build/build_config.h" #if defined(OS_LINUX) -#include -#include #include -#include -#include +#elif defined(OS_ANDROID) +#include #endif namespace base { @@ -40,39 +37,29 @@ namespace { struct ThreadParams { ThreadParams() - : delegate(NULL), - joinable(false), - priority(kThreadPriority_Normal), - handle(NULL), - handle_set(false, false) { - } + : delegate(NULL), joinable(false), priority(ThreadPriority::NORMAL) {} PlatformThread::Delegate* delegate; bool joinable; ThreadPriority priority; - PlatformThreadHandle* handle; - WaitableEvent handle_set; }; void* ThreadFunc(void* params) { base::InitOnThread(); - ThreadParams* thread_params = static_cast(params); - PlatformThread::Delegate* delegate = thread_params->delegate; - if (!thread_params->joinable) - base::ThreadRestrictions::SetSingletonAllowed(false); + PlatformThread::Delegate* delegate = nullptr; - if (thread_params->priority != kThreadPriority_Normal) { - PlatformThread::SetThreadPriority(PlatformThread::CurrentHandle(), - thread_params->priority); + { + scoped_ptr thread_params(static_cast(params)); + + delegate = thread_params->delegate; + if (!thread_params->joinable) + base::ThreadRestrictions::SetSingletonAllowed(false); + + if (thread_params->priority != ThreadPriority::NORMAL) + PlatformThread::SetCurrentThreadPriority(thread_params->priority); } - // Stash the id in the handle so the calling thread has a complete - // handle, and unblock the parent thread. - *(thread_params->handle) = PlatformThreadHandle(pthread_self(), - PlatformThread::CurrentId()); - thread_params->handle_set.Signal(); - ThreadIdNameManager::GetInstance()->RegisterThread( PlatformThread::CurrentHandle().platform_handle(), PlatformThread::CurrentId()); @@ -87,21 +74,21 @@ void* ThreadFunc(void* params) { return NULL; } -bool CreateThread(size_t stack_size, bool joinable, +bool CreateThread(size_t stack_size, + bool joinable, PlatformThread::Delegate* delegate, PlatformThreadHandle* thread_handle, ThreadPriority priority) { + DCHECK(thread_handle); base::InitThreading(); - bool success = false; pthread_attr_t attributes; pthread_attr_init(&attributes); // Pthreads are joinable by default, so only specify the detached // attribute if the thread should be non-joinable. - if (!joinable) { + if (!joinable) pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); - } // Get a better default if available. if (stack_size == 0) @@ -110,33 +97,27 @@ bool CreateThread(size_t stack_size, bool joinable, if (stack_size > 0) pthread_attr_setstacksize(&attributes, stack_size); - ThreadParams params; - params.delegate = delegate; - params.joinable = joinable; - params.priority = priority; - params.handle = thread_handle; + scoped_ptr params(new ThreadParams); + params->delegate = delegate; + params->joinable = joinable; + params->priority = priority; pthread_t handle; - int err = pthread_create(&handle, - &attributes, - ThreadFunc, - ¶ms); - success = !err; - if (!success) { + int err = pthread_create(&handle, &attributes, ThreadFunc, params.get()); + bool success = !err; + if (success) { + // ThreadParams should be deleted on the created thread after used. + ignore_result(params.release()); + } else { // Value of |handle| is undefined if pthread_create fails. handle = 0; errno = err; PLOG(ERROR) << "pthread_create"; } + *thread_handle = PlatformThreadHandle(handle); pthread_attr_destroy(&attributes); - // Don't let this call complete until the thread id - // is set in the handle. - if (success) - params.handle_set.Wait(); - CHECK_EQ(handle, thread_handle->platform_handle()); - return success; } @@ -158,9 +139,9 @@ PlatformThreadId PlatformThread::CurrentId() { return pthread_self(); #elif defined(OS_NACL) && !defined(__GLIBC__) // Pointers are 32-bits in NaCl. - return reinterpret_cast(pthread_self()); + return reinterpret_cast(pthread_self()); #elif defined(OS_POSIX) - return reinterpret_cast(pthread_self()); + return reinterpret_cast(pthread_self()); #endif } @@ -171,7 +152,7 @@ PlatformThreadRef PlatformThread::CurrentRef() { // static PlatformThreadHandle PlatformThread::CurrentHandle() { - return PlatformThreadHandle(pthread_self(), CurrentId()); + return PlatformThreadHandle(pthread_self()); } // static @@ -199,19 +180,10 @@ const char* PlatformThread::GetName() { return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); } -// static -bool PlatformThread::Create(size_t stack_size, Delegate* delegate, - PlatformThreadHandle* thread_handle) { - base::ThreadRestrictions::ScopedAllowWait allow_wait; - return CreateThread(stack_size, true /* joinable thread */, - delegate, thread_handle, kThreadPriority_Normal); -} - // static bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle, ThreadPriority priority) { - base::ThreadRestrictions::ScopedAllowWait allow_wait; return CreateThread(stack_size, true, // joinable thread delegate, thread_handle, priority); } @@ -220,9 +192,8 @@ bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { PlatformThreadHandle unused; - base::ThreadRestrictions::ScopedAllowWait allow_wait; bool result = CreateThread(stack_size, false /* non-joinable thread */, - delegate, &unused, kThreadPriority_Normal); + delegate, &unused, ThreadPriority::NORMAL); return result; } @@ -232,7 +203,61 @@ void PlatformThread::Join(PlatformThreadHandle thread_handle) { // the thread referred to by |thread_handle| may still be running long-lived / // blocking tasks. base::ThreadRestrictions::AssertIOAllowed(); - CHECK_EQ(0, pthread_join(thread_handle.handle_, NULL)); + CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), NULL)); } +// Mac has its own Set/GetCurrentThreadPriority() implementations. +#if !defined(OS_MACOSX) + +// static +void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { +#if defined(OS_NACL) + NOTIMPLEMENTED(); +#else + if (internal::SetCurrentThreadPriorityForPlatform(priority)) + return; + + // setpriority(2) should change the whole thread group's (i.e. process) + // priority. However, as stated in the bugs section of + // http://man7.org/linux/man-pages/man2/getpriority.2.html: "under the current + // Linux/NPTL implementation of POSIX threads, the nice value is a per-thread + // attribute". Also, 0 is prefered to the current thread id since it is + // equivalent but makes sandboxing easier (https://crbug.com/399473). + const int nice_setting = internal::ThreadPriorityToNiceValue(priority); + if (setpriority(PRIO_PROCESS, 0, nice_setting)) { + DVPLOG(1) << "Failed to set nice value of thread (" + << PlatformThread::CurrentId() << ") to " << nice_setting; + } +#endif // defined(OS_NACL) +} + +// static +ThreadPriority PlatformThread::GetCurrentThreadPriority() { +#if defined(OS_NACL) + NOTIMPLEMENTED(); + return ThreadPriority::NORMAL; +#else + // Mirrors SetCurrentThreadPriority()'s implementation. + ThreadPriority platform_specific_priority; + if (internal::GetCurrentThreadPriorityForPlatform( + &platform_specific_priority)) { + return platform_specific_priority; + } + + // Need to clear errno before calling getpriority(): + // http://man7.org/linux/man-pages/man2/getpriority.2.html + errno = 0; + int nice_value = getpriority(PRIO_PROCESS, 0); + if (errno != 0) { + DVPLOG(1) << "Failed to get nice value of thread (" + << PlatformThread::CurrentId() << ")"; + return ThreadPriority::NORMAL; + } + + return internal::NiceValueToThreadPriority(nice_value); +#endif // !defined(OS_NACL) +} + +#endif // !defined(OS_MACOSX) + } // namespace base diff --git a/security/sandbox/chromium/base/threading/platform_thread_win.cc b/security/sandbox/chromium/base/threading/platform_thread_win.cc index 3df371943f5e..d5bd9bed08e7 100644 --- a/security/sandbox/chromium/base/threading/platform_thread_win.cc +++ b/security/sandbox/chromium/base/threading/platform_thread_win.cc @@ -4,6 +4,8 @@ #include "base/threading/platform_thread.h" +#include + #include "base/debug/alias.h" #include "base/debug/profiler.h" #include "base/logging.h" @@ -46,6 +48,7 @@ void SetNameInternal(PlatformThreadId thread_id, const char* name) { struct ThreadParams { PlatformThread::Delegate* delegate; bool joinable; + ThreadPriority priority; }; DWORD __stdcall ThreadFunc(void* params) { @@ -54,6 +57,9 @@ DWORD __stdcall ThreadFunc(void* params) { if (!thread_params->joinable) base::ThreadRestrictions::SetSingletonAllowed(false); + if (thread_params->priority != ThreadPriority::NORMAL) + PlatformThread::SetCurrentThreadPriority(thread_params->priority); + // Retrieve a copy of the thread handle to use as the key in the // thread name mapping. PlatformThreadHandle::Handle platform_handle; @@ -83,15 +89,16 @@ DWORD __stdcall ThreadFunc(void* params) { PlatformThread::CurrentId()); } - return NULL; + return 0; } -// CreateThreadInternal() matches PlatformThread::Create(), except that -// |out_thread_handle| may be NULL, in which case a non-joinable thread is -// created. +// CreateThreadInternal() matches PlatformThread::CreateWithPriority(), except +// that |out_thread_handle| may be nullptr, in which case a non-joinable thread +// is created. bool CreateThreadInternal(size_t stack_size, PlatformThread::Delegate* delegate, - PlatformThreadHandle* out_thread_handle) { + PlatformThreadHandle* out_thread_handle, + ThreadPriority priority) { unsigned int flags = 0; if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) { flags = STACK_SIZE_PARAM_IS_A_RESERVATION; @@ -101,15 +108,16 @@ bool CreateThreadInternal(size_t stack_size, ThreadParams* params = new ThreadParams; params->delegate = delegate; - params->joinable = out_thread_handle != NULL; + params->joinable = out_thread_handle != nullptr; + params->priority = priority; // Using CreateThread here vs _beginthreadex makes thread creation a bit // faster and doesn't require the loader lock to be available. Our code will // have to work running on CreateThread() threads anyway, since we run code // on the Windows thread pool, etc. For some background on the difference: // http://www.microsoft.com/msj/1099/win32/win321099.aspx - void* thread_handle = CreateThread( - NULL, stack_size, ThreadFunc, params, flags, NULL); + void* thread_handle = + ::CreateThread(nullptr, stack_size, ThreadFunc, params, flags, nullptr); if (!thread_handle) { delete params; return false; @@ -126,18 +134,17 @@ bool CreateThreadInternal(size_t stack_size, // static PlatformThreadId PlatformThread::CurrentId() { - return GetCurrentThreadId(); + return ::GetCurrentThreadId(); } // static PlatformThreadRef PlatformThread::CurrentRef() { - return PlatformThreadRef(GetCurrentThreadId()); + return PlatformThreadRef(::GetCurrentThreadId()); } // static PlatformThreadHandle PlatformThread::CurrentHandle() { - NOTIMPLEMENTED(); // See OpenThread() - return PlatformThreadHandle(); + return PlatformThreadHandle(::GetCurrentThread()); } // static @@ -155,7 +162,7 @@ void PlatformThread::Sleep(TimeDelta duration) { } // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const std::string& name) { ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); // On Windows only, we don't need to tell the profiler about the "BrokerEvent" @@ -164,7 +171,7 @@ void PlatformThread::SetName(const char* name) { // which would also (as a side effect) initialize the profiler in this unused // context, including setting up thread local storage, etc. The performance // impact is not terrible, but there is no reason to do initialize it. - if (0 != strcmp(name, "BrokerEvent")) + if (name != "BrokerEvent") tracked_objects::ThreadData::InitializeThreadContext(name); // The debugger needs to be around to catch the name in the exception. If @@ -174,7 +181,7 @@ void PlatformThread::SetName(const char* name) { if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) return; - SetNameInternal(CurrentId(), name); + SetNameInternal(CurrentId(), name.c_str()); } // static @@ -182,31 +189,23 @@ const char* PlatformThread::GetName() { return ThreadIdNameManager::GetInstance()->GetName(CurrentId()); } -// static -bool PlatformThread::Create(size_t stack_size, Delegate* delegate, - PlatformThreadHandle* thread_handle) { - DCHECK(thread_handle); - return CreateThreadInternal(stack_size, delegate, thread_handle); -} - // static bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate, PlatformThreadHandle* thread_handle, ThreadPriority priority) { - bool result = Create(stack_size, delegate, thread_handle); - if (result) - SetThreadPriority(*thread_handle, priority); - return result; + DCHECK(thread_handle); + return CreateThreadInternal(stack_size, delegate, thread_handle, priority); } // static bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { - return CreateThreadInternal(stack_size, delegate, NULL); + return CreateThreadInternal(stack_size, delegate, nullptr, + ThreadPriority::NORMAL); } // static void PlatformThread::Join(PlatformThreadHandle thread_handle) { - DCHECK(thread_handle.handle_); + DCHECK(thread_handle.platform_handle()); // TODO(willchan): Enable this check once I can get it to work for Windows // shutdown. // Joining another thread may block the current thread for a long time, since @@ -218,33 +217,68 @@ void PlatformThread::Join(PlatformThreadHandle thread_handle) { // Wait for the thread to exit. It should already have terminated but make // sure this assumption is valid. - DWORD result = WaitForSingleObject(thread_handle.handle_, INFINITE); + DWORD result = WaitForSingleObject(thread_handle.platform_handle(), INFINITE); if (result != WAIT_OBJECT_0) { // Debug info for bug 127931. DWORD error = GetLastError(); debug::Alias(&error); debug::Alias(&result); - debug::Alias(&thread_handle.handle_); CHECK(false); } - CloseHandle(thread_handle.handle_); + CloseHandle(thread_handle.platform_handle()); } // static -void PlatformThread::SetThreadPriority(PlatformThreadHandle handle, - ThreadPriority priority) { +void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) { + int desired_priority = THREAD_PRIORITY_ERROR_RETURN; switch (priority) { - case kThreadPriority_Normal: - ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_NORMAL); + case ThreadPriority::BACKGROUND: + desired_priority = THREAD_PRIORITY_LOWEST; break; - case kThreadPriority_RealtimeAudio: - ::SetThreadPriority(handle.handle_, THREAD_PRIORITY_TIME_CRITICAL); + case ThreadPriority::NORMAL: + desired_priority = THREAD_PRIORITY_NORMAL; + break; + case ThreadPriority::DISPLAY: + desired_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case ThreadPriority::REALTIME_AUDIO: + desired_priority = THREAD_PRIORITY_TIME_CRITICAL; break; default: NOTREACHED() << "Unknown priority."; break; } + DCHECK_NE(desired_priority, THREAD_PRIORITY_ERROR_RETURN); + +#ifndef NDEBUG + const BOOL success = +#endif + ::SetThreadPriority(PlatformThread::CurrentHandle().platform_handle(), + desired_priority); + DPLOG_IF(ERROR, !success) << "Failed to set thread priority to " + << desired_priority; +} + +// static +ThreadPriority PlatformThread::GetCurrentThreadPriority() { + int priority = + ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle()); + switch (priority) { + case THREAD_PRIORITY_LOWEST: + return ThreadPriority::BACKGROUND; + case THREAD_PRIORITY_NORMAL: + return ThreadPriority::NORMAL; + case THREAD_PRIORITY_ABOVE_NORMAL: + return ThreadPriority::DISPLAY; + case THREAD_PRIORITY_TIME_CRITICAL: + return ThreadPriority::REALTIME_AUDIO; + case THREAD_PRIORITY_ERROR_RETURN: + DPCHECK(false) << "GetThreadPriority error"; // Falls through. + default: + NOTREACHED() << "Unexpected priority: " << priority; + return ThreadPriority::NORMAL; + } } } // namespace base diff --git a/security/sandbox/chromium/base/threading/sequenced_worker_pool.h b/security/sandbox/chromium/base/threading/sequenced_worker_pool.h index 63c6204ebbb7..ba0e444210fe 100644 --- a/security/sandbox/chromium/base/threading/sequenced_worker_pool.h +++ b/security/sandbox/chromium/base/threading/sequenced_worker_pool.h @@ -5,14 +5,17 @@ #ifndef BASE_THREADING_SEQUENCED_WORKER_POOL_H_ #define BASE_THREADING_SEQUENCED_WORKER_POOL_H_ +#include + #include #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/callback_forward.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/single_thread_task_runner.h" #include "base/task_runner.h" namespace tracked_objects { @@ -21,7 +24,7 @@ class Location; namespace base { -class MessageLoopProxy; +class SingleThreadTaskRunner; template class DeleteHelper; @@ -44,7 +47,8 @@ class SequencedTaskRunner; // destruction will be visible to T2. // // Example: -// SequencedWorkerPool::SequenceToken token = pool.GetSequenceToken(); +// SequencedWorkerPool::SequenceToken token = +// SequencedWorkerPool::GetSequenceToken(); // pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN, // FROM_HERE, base::Bind(...)); // pool.PostSequencedWorkerTask(token, SequencedWorkerPool::SKIP_ON_SHUTDOWN, @@ -120,7 +124,7 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { // Opaque identifier that defines sequencing of tasks posted to the worker // pool. - class SequenceToken { + class BASE_EXPORT SequenceToken { public: SequenceToken() : id_(0) {} ~SequenceToken() {} @@ -134,6 +138,10 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { return id_ != 0; } + // Returns a string representation of this token. This method should only be + // used for debugging. + std::string ToString() const; + private: friend class SequencedWorkerPool; @@ -156,25 +164,38 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { // an unsequenced task, returns an invalid SequenceToken. static SequenceToken GetSequenceTokenForCurrentThread(); + // Gets a SequencedTaskRunner for the current thread. If the current thread is + // running an unsequenced task, a new SequenceToken will be generated and set, + // so that the returned SequencedTaskRunner is guaranteed to run tasks after + // the current task has finished running. + static scoped_refptr + GetSequencedTaskRunnerForCurrentThread(); + + // Returns a unique token that can be used to sequence tasks posted to + // PostSequencedWorkerTask(). Valid tokens are always nonzero. + // TODO(bauerb): Rename this to better differentiate from + // GetSequenceTokenForCurrentThread(). + static SequenceToken GetSequenceToken(); + + // Returns the SequencedWorkerPool that owns this thread, or null if the + // current thread is not a SequencedWorkerPool worker thread. + static scoped_refptr GetWorkerPoolForCurrentThread(); + // When constructing a SequencedWorkerPool, there must be a - // MessageLoop on the current thread unless you plan to deliberately - // leak it. + // ThreadTaskRunnerHandle on the current thread unless you plan to + // deliberately leak it. // Pass the maximum number of threads (they will be lazily created as needed) // and a prefix for the thread name to aid in debugging. SequencedWorkerPool(size_t max_threads, const std::string& thread_name_prefix); - // Like above, but with |observer| for testing. Does not take - // ownership of |observer|. + // Like above, but with |observer| for testing. Does not take ownership of + // |observer|. SequencedWorkerPool(size_t max_threads, const std::string& thread_name_prefix, TestingObserver* observer); - // Returns a unique token that can be used to sequence tasks posted to - // PostSequencedWorkerTask(). Valid tokens are always nonzero. - SequenceToken GetSequenceToken(); - // Returns the sequence token associated with the given name. Calling this // function multiple times with the same string will always produce the // same sequence token. If the name has not been used before, a new token @@ -299,6 +320,10 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { // sequence_token. bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const; + // Returns true if any thread is currently processing a task with the given + // sequence token. Should only be called with a valid sequence token. + bool IsRunningSequence(SequenceToken sequence_token) const; + // Blocks until all pending tasks are complete. This should only be called in // unit tests when you want to validate something that should have happened. // This will not flush delayed tasks; delayed tasks get deleted. @@ -320,12 +345,10 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { // Must be called from the same thread this object was constructed on. void Shutdown() { Shutdown(0); } - // A variant that allows an arbitrary number of new blocking tasks to - // be posted during shutdown from within tasks that execute during shutdown. - // Only tasks designated as BLOCKING_SHUTDOWN will be allowed, and only if - // posted by tasks that are not designated as CONTINUE_ON_SHUTDOWN. Once + // A variant that allows an arbitrary number of new blocking tasks to be + // posted during shutdown. The tasks cannot be posted within the execution + // context of tasks whose shutdown behavior is not BLOCKING_SHUTDOWN. Once // the limit is reached, subsequent calls to post task fail in all cases. - // // Must be called from the same thread this object was constructed on. void Shutdown(int max_new_blocking_tasks_after_shutdown); @@ -347,7 +370,7 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { class Inner; class Worker; - const scoped_refptr constructor_message_loop_; + const scoped_refptr constructor_task_runner_; // Avoid pulling in too many headers by putting (almost) everything // into |inner_|. diff --git a/security/sandbox/chromium/base/threading/thread_checker_impl.h b/security/sandbox/chromium/base/threading/thread_checker_impl.h index 879ac3ab3546..c92e143db0c3 100644 --- a/security/sandbox/chromium/base/threading/thread_checker_impl.h +++ b/security/sandbox/chromium/base/threading/thread_checker_impl.h @@ -6,6 +6,7 @@ #define BASE_THREADING_THREAD_CHECKER_IMPL_H_ #include "base/base_export.h" +#include "base/compiler_specific.h" #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" @@ -22,7 +23,7 @@ class BASE_EXPORT ThreadCheckerImpl { ThreadCheckerImpl(); ~ThreadCheckerImpl(); - bool CalledOnValidThread() const; + bool CalledOnValidThread() const WARN_UNUSED_RESULT; // Changes the thread that is checked for in CalledOnValidThread. This may // be useful when an object may be created on one thread and then used diff --git a/security/sandbox/chromium/base/threading/thread_collision_warner.h b/security/sandbox/chromium/base/threading/thread_collision_warner.h index de4e9c3eb58f..4699a910dd2f 100644 --- a/security/sandbox/chromium/base/threading/thread_collision_warner.h +++ b/security/sandbox/chromium/base/threading/thread_collision_warner.h @@ -9,8 +9,8 @@ #include "base/atomicops.h" #include "base/base_export.h" -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" // A helper class alongside macros to be used to verify assumptions about thread // safety of a class. diff --git a/security/sandbox/chromium/base/threading/thread_id_name_manager.cc b/security/sandbox/chromium/base/threading/thread_id_name_manager.cc index 7c85c1b5e996..56cfa273a87d 100644 --- a/security/sandbox/chromium/base/threading/thread_id_name_manager.cc +++ b/security/sandbox/chromium/base/threading/thread_id_name_manager.cc @@ -48,17 +48,16 @@ void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle, name_to_interned_name_[kDefaultName]; } -void ThreadIdNameManager::SetName(PlatformThreadId id, const char* name) { - std::string str_name(name); - +void ThreadIdNameManager::SetName(PlatformThreadId id, + const std::string& name) { AutoLock locked(lock_); - NameToInternedNameMap::iterator iter = name_to_interned_name_.find(str_name); + NameToInternedNameMap::iterator iter = name_to_interned_name_.find(name); std::string* leaked_str = NULL; if (iter != name_to_interned_name_.end()) { leaked_str = iter->second; } else { - leaked_str = new std::string(str_name); - name_to_interned_name_[str_name] = leaked_str; + leaked_str = new std::string(name); + name_to_interned_name_[name] = leaked_str; } ThreadIdToHandleMap::iterator id_to_handle_iter = diff --git a/security/sandbox/chromium/base/threading/thread_id_name_manager.h b/security/sandbox/chromium/base/threading/thread_id_name_manager.h index 0ea59df6572c..f469b605e40d 100644 --- a/security/sandbox/chromium/base/threading/thread_id_name_manager.h +++ b/security/sandbox/chromium/base/threading/thread_id_name_manager.h @@ -9,14 +9,15 @@ #include #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" #include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" -template struct DefaultSingletonTraits; - namespace base { +template +struct DefaultSingletonTraits; + class BASE_EXPORT ThreadIdNameManager { public: static ThreadIdNameManager* GetInstance(); @@ -27,7 +28,7 @@ class BASE_EXPORT ThreadIdNameManager { void RegisterThread(PlatformThreadHandle::Handle handle, PlatformThreadId id); // Set the name for the given id. - void SetName(PlatformThreadId id, const char* name); + void SetName(PlatformThreadId id, const std::string& name); // Get the name for the given id. const char* GetName(PlatformThreadId id); diff --git a/security/sandbox/chromium/base/threading/thread_local.h b/security/sandbox/chromium/base/threading/thread_local.h index df9c4b72573b..f40420cd2f8b 100644 --- a/security/sandbox/chromium/base/threading/thread_local.h +++ b/security/sandbox/chromium/base/threading/thread_local.h @@ -52,8 +52,9 @@ #define BASE_THREADING_THREAD_LOCAL_H_ #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" #include "base/threading/thread_local_storage.h" +#include "build/build_config.h" #if defined(OS_POSIX) #include diff --git a/security/sandbox/chromium/base/threading/thread_local_posix.cc b/security/sandbox/chromium/base/threading/thread_local_posix.cc index 75ea4795d453..8bc46ad1902d 100644 --- a/security/sandbox/chromium/base/threading/thread_local_posix.cc +++ b/security/sandbox/chromium/base/threading/thread_local_posix.cc @@ -7,6 +7,7 @@ #include #include "base/logging.h" +#include "build/build_config.h" #if !defined(OS_ANDROID) diff --git a/security/sandbox/chromium/base/threading/thread_local_storage.h b/security/sandbox/chromium/base/threading/thread_local_storage.h index 53ebe55e51ce..013b0aeffbbe 100644 --- a/security/sandbox/chromium/base/threading/thread_local_storage.h +++ b/security/sandbox/chromium/base/threading/thread_local_storage.h @@ -5,8 +5,10 @@ #ifndef BASE_THREADING_THREAD_LOCAL_STORAGE_H_ #define BASE_THREADING_THREAD_LOCAL_STORAGE_H_ +#include "base/atomicops.h" #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" +#include "build/build_config.h" #if defined(OS_WIN) #include @@ -26,7 +28,7 @@ class BASE_EXPORT PlatformThreadLocalStorage { #if defined(OS_WIN) typedef unsigned long TLSKey; - enum { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES }; + enum : unsigned { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES }; #elif defined(OS_POSIX) typedef pthread_key_t TLSKey; // The following is a "reserved key" which is used in our generic Chromium @@ -98,8 +100,7 @@ class BASE_EXPORT ThreadLocalStorage { // Set up the TLS slot. Called by the constructor. // 'destructor' is a pointer to a function to perform per-thread cleanup of // this object. If set to NULL, no cleanup is done for this TLS slot. - // Returns false on error. - bool Initialize(TLSDestructorFunc destructor); + void Initialize(TLSDestructorFunc destructor); // Free a previously allocated TLS 'slot'. // If a destructor was set for this slot, removes @@ -115,10 +116,12 @@ class BASE_EXPORT ThreadLocalStorage { // value 'value'. void Set(void* value); - bool initialized() const { return initialized_; } + bool initialized() const { + return base::subtle::Acquire_Load(&initialized_) != 0; + } // The internals of this struct should be considered private. - bool initialized_; + base::subtle::Atomic32 initialized_; int slot_; }; @@ -136,6 +139,7 @@ class BASE_EXPORT ThreadLocalStorage { DISALLOW_COPY_AND_ASSIGN(Slot); }; + private: DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); }; diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.cc b/security/sandbox/chromium/base/threading/thread_restrictions.cc index 871f2dc874c3..00306c5ae7d6 100644 --- a/security/sandbox/chromium/base/threading/thread_restrictions.cc +++ b/security/sandbox/chromium/base/threading/thread_restrictions.cc @@ -23,7 +23,7 @@ LazyInstance::Leaky LazyInstance::Leaky g_wait_disallowed = LAZY_INSTANCE_INITIALIZER; -} // anonymous namespace +} // namespace // static bool ThreadRestrictions::SetIOAllowed(bool allowed) { @@ -69,7 +69,7 @@ void ThreadRestrictions::DisallowWaiting() { // static void ThreadRestrictions::AssertWaitAllowed() { if (g_wait_disallowed.Get().Get()) { - LOG(FATAL) << "Waiting is not allowed to be used on this thread to prevent" + LOG(FATAL) << "Waiting is not allowed to be used on this thread to prevent " << "jank and deadlock."; } } diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.h b/security/sandbox/chromium/base/threading/thread_restrictions.h index 7c46fd2bdb4c..eec00fbb7969 100644 --- a/security/sandbox/chromium/base/threading/thread_restrictions.h +++ b/security/sandbox/chromium/base/threading/thread_restrictions.h @@ -6,7 +6,7 @@ #define BASE_THREADING_THREAD_RESTRICTIONS_H_ #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" // See comment at top of thread_checker.h #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) @@ -22,6 +22,7 @@ class ScopedAllowWaitForLegacyWebViewApi; namespace cc { class CompletionEvent; +class SingleThreadTaskGraphRunner; } namespace chromeos { class BlockingMethodCaller; @@ -39,10 +40,12 @@ class BrowserShutdownProfileDumper; class BrowserTestBase; class GpuChannelHost; class NestedMessagePumpAndroid; -class RenderWidgetResizeHelper; class ScopedAllowWaitForAndroidLayoutTests; +class ScopedAllowWaitForDebugURL; +class SoftwareOutputDeviceMus; class TextInputClientMac; -} +class RasterWorkerPool; +} // namespace content namespace dbus { class Bus; } @@ -50,12 +53,20 @@ namespace disk_cache { class BackendImpl; class InFlightIO; } +namespace gles2 { +class CommandBufferClientImpl; +} namespace mojo { namespace common { -class WatcherThreadManager; +class MessagePumpMojo; } } +namespace mus { +class CommandBufferLocal; +class GpuState; +} namespace net { +class NetworkChangeNotifierMac; namespace internal { class AddressTrackerLinux; } @@ -65,6 +76,14 @@ namespace remoting { class AutoThread; } +namespace ui { +class WindowResizeHelperMac; +} + +namespace views { +class WindowManagerConnection; +} + namespace base { namespace android { @@ -172,13 +191,15 @@ class BASE_EXPORT ThreadRestrictions { friend class content::BrowserShutdownProfileDumper; friend class content::BrowserTestBase; friend class content::NestedMessagePumpAndroid; - friend class content::RenderWidgetResizeHelper; friend class content::ScopedAllowWaitForAndroidLayoutTests; + friend class content::ScopedAllowWaitForDebugURL; friend class ::HistogramSynchronizer; friend class ::ScopedAllowWaitForLegacyWebViewApi; friend class cc::CompletionEvent; - friend class mojo::common::WatcherThreadManager; + friend class cc::SingleThreadTaskGraphRunner; + friend class content::RasterWorkerPool; friend class remoting::AutoThread; + friend class ui::WindowResizeHelperMac; friend class MessagePumpDefault; friend class SequencedWorkerPool; friend class SimpleThread; @@ -186,6 +207,10 @@ class BASE_EXPORT ThreadRestrictions { friend class ThreadTestHelper; friend class PlatformThread; friend class android::JavaHandlerThread; + friend class gles2::CommandBufferClientImpl; + friend class mojo::common::MessagePumpMojo; + friend class mus::CommandBufferLocal; + friend class mus::GpuState; // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. @@ -202,9 +227,14 @@ class BASE_EXPORT ThreadRestrictions { friend class disk_cache::BackendImpl; // http://crbug.com/74623 friend class disk_cache::InFlightIO; // http://crbug.com/74623 friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097 + friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097 friend class ::BrowserProcessImpl; // http://crbug.com/125207 friend class ::NativeBackendKWallet; // http://crbug.com/125331 - // END USAGE THAT NEEDS TO BE FIXED. +#if !defined(OFFICIAL_BUILD) + friend class content::SoftwareOutputDeviceMus; // Interim non-production code +#endif + friend class views::WindowManagerConnection; +// END USAGE THAT NEEDS TO BE FIXED. #if ENABLE_THREAD_RESTRICTIONS static bool SetWaitAllowed(bool allowed); diff --git a/security/sandbox/chromium/base/time/time.cc b/security/sandbox/chromium/base/time/time.cc index c46a69cbc434..9188887e27bc 100644 --- a/security/sandbox/chromium/base/time/time.cc +++ b/security/sandbox/chromium/base/time/time.cc @@ -4,16 +4,18 @@ #include "base/time/time.h" +#include #include #include #include #include -#include "base/float_util.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/macros.h" #include "base/strings/stringprintf.h" #include "base/third_party/nspr/prtime.h" +#include "build/build_config.h" namespace base { @@ -21,7 +23,7 @@ namespace base { // static TimeDelta TimeDelta::Max() { - return TimeDelta(std::numeric_limits::max()); + return TimeDelta(std::numeric_limits::max()); } int TimeDelta::InDays() const { @@ -56,10 +58,10 @@ double TimeDelta::InSecondsF() const { return static_cast(delta_) / Time::kMicrosecondsPerSecond; } -int64 TimeDelta::InSeconds() const { +int64_t TimeDelta::InSeconds() const { if (is_max()) { // Preserve max to prevent overflow. - return std::numeric_limits::max(); + return std::numeric_limits::max(); } return delta_ / Time::kMicrosecondsPerSecond; } @@ -72,31 +74,61 @@ double TimeDelta::InMillisecondsF() const { return static_cast(delta_) / Time::kMicrosecondsPerMillisecond; } -int64 TimeDelta::InMilliseconds() const { +int64_t TimeDelta::InMilliseconds() const { if (is_max()) { // Preserve max to prevent overflow. - return std::numeric_limits::max(); + return std::numeric_limits::max(); } return delta_ / Time::kMicrosecondsPerMillisecond; } -int64 TimeDelta::InMillisecondsRoundedUp() const { +int64_t TimeDelta::InMillisecondsRoundedUp() const { if (is_max()) { // Preserve max to prevent overflow. - return std::numeric_limits::max(); + return std::numeric_limits::max(); } return (delta_ + Time::kMicrosecondsPerMillisecond - 1) / Time::kMicrosecondsPerMillisecond; } -int64 TimeDelta::InMicroseconds() const { +int64_t TimeDelta::InMicroseconds() const { if (is_max()) { // Preserve max to prevent overflow. - return std::numeric_limits::max(); + return std::numeric_limits::max(); } return delta_; } +namespace time_internal { + +int64_t SaturatedAdd(TimeDelta delta, int64_t value) { + CheckedNumeric rv(delta.delta_); + rv += value; + return FromCheckedNumeric(rv); +} + +int64_t SaturatedSub(TimeDelta delta, int64_t value) { + CheckedNumeric rv(delta.delta_); + rv -= value; + return FromCheckedNumeric(rv); +} + +int64_t FromCheckedNumeric(const CheckedNumeric value) { + if (value.IsValid()) + return value.ValueUnsafe(); + + // We could return max/min but we don't really expose what the maximum delta + // is. Instead, return max/(-max), which is something that clients can reason + // about. + // TODO(rvargas) crbug.com/332611: don't use internal values. + int64_t limit = std::numeric_limits::max(); + if (value.validity() == internal::RANGE_UNDERFLOW) + limit = -limit; + return value.ValueOrDefault(limit); +} + +} // namespace time_internal + std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) { return os << time_delta.InSecondsF() << "s"; } @@ -105,7 +137,7 @@ std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) { // static Time Time::Max() { - return Time(std::numeric_limits::max()); + return Time(std::numeric_limits::max()); } // static @@ -114,7 +146,7 @@ Time Time::FromTimeT(time_t tt) { return Time(); // Preserve 0 so we can tell it doesn't exist. if (tt == std::numeric_limits::max()) return Max(); - return Time((tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset); + return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSeconds(tt); } time_t Time::ToTimeT() const { @@ -124,7 +156,7 @@ time_t Time::ToTimeT() const { // Preserve max without offset to prevent overflow. return std::numeric_limits::max(); } - if (std::numeric_limits::max() - kTimeTToMicrosecondsOffset <= us_) { + if (std::numeric_limits::max() - kTimeTToMicrosecondsOffset <= us_) { DLOG(WARNING) << "Overflow when converting base::Time with internal " << "value " << us_ << " to time_t."; return std::numeric_limits::max(); @@ -134,13 +166,9 @@ time_t Time::ToTimeT() const { // static Time Time::FromDoubleT(double dt) { - if (dt == 0 || IsNaN(dt)) + if (dt == 0 || std::isnan(dt)) return Time(); // Preserve 0 so we can tell it doesn't exist. - if (dt == std::numeric_limits::infinity()) - return Max(); - return Time(static_cast((dt * - static_cast(kMicrosecondsPerSecond)) + - kTimeTToMicrosecondsOffset)); + return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSecondsD(dt); } double Time::ToDoubleT() const { @@ -167,10 +195,8 @@ Time Time::FromTimeSpec(const timespec& ts) { Time Time::FromJsTime(double ms_since_epoch) { // The epoch is a valid time, so this constructor doesn't interpret // 0 as the null time. - if (ms_since_epoch == std::numeric_limits::infinity()) - return Max(); - return Time(static_cast(ms_since_epoch * kMicrosecondsPerMillisecond) + - kTimeTToMicrosecondsOffset); + return Time(kTimeTToMicrosecondsOffset) + + TimeDelta::FromMillisecondsD(ms_since_epoch); } double Time::ToJsTime() const { @@ -186,14 +212,14 @@ double Time::ToJsTime() const { kMicrosecondsPerMillisecond); } -int64 Time::ToJavaTime() const { +int64_t Time::ToJavaTime() const { if (is_null()) { // Preserve 0 so the invalid result doesn't depend on the platform. return 0; } if (is_max()) { // Preserve max without offset to prevent overflow. - return std::numeric_limits::max(); + return std::numeric_limits::max(); } return ((us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerMillisecond); @@ -276,6 +302,19 @@ TimeTicks TimeTicks::UnixEpoch() { return leaky_unix_epoch_singleton_instance.Get().unix_epoch(); } +TimeTicks TimeTicks::SnappedToNextTick(TimeTicks tick_phase, + TimeDelta tick_interval) const { + // |interval_offset| is the offset from |this| to the next multiple of + // |tick_interval| after |tick_phase|, possibly negative if in the past. + TimeDelta interval_offset = (tick_phase - *this) % tick_interval; + // If |this| is exactly on the interval (i.e. offset==0), don't adjust. + // Otherwise, if |tick_phase| was in the past, adjust forward to the next + // tick after |this|. + if (!interval_offset.is_zero() && tick_phase < *this) + interval_offset += tick_interval; + return *this + interval_offset; +} + std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks) { // This function formats a TimeTicks object as "bogo-microseconds". // The origin and granularity of the count are platform-specific, and may very @@ -286,6 +325,11 @@ std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks) { return os << as_time_delta.InMicroseconds() << " bogo-microseconds"; } +std::ostream& operator<<(std::ostream& os, ThreadTicks thread_ticks) { + const TimeDelta as_time_delta = thread_ticks - ThreadTicks(); + return os << as_time_delta.InMicroseconds() << " bogo-thread-microseconds"; +} + // Time::Exploded ------------------------------------------------------------- inline bool is_in_range(int value, int lo, int hi) { diff --git a/security/sandbox/chromium/base/time/time.h b/security/sandbox/chromium/base/time/time.h index 3a94a98dd89b..ea19d7ed9d4d 100644 --- a/security/sandbox/chromium/base/time/time.h +++ b/security/sandbox/chromium/base/time/time.h @@ -4,35 +4,59 @@ // Time represents an absolute point in coordinated universal time (UTC), // internally represented as microseconds (s/1,000,000) since the Windows epoch -// (1601-01-01 00:00:00 UTC) (See http://crbug.com/14734). System-dependent -// clock interface routines are defined in time_PLATFORM.cc. +// (1601-01-01 00:00:00 UTC). System-dependent clock interface routines are +// defined in time_PLATFORM.cc. Note that values for Time may skew and jump +// around as the operating system makes adjustments to synchronize (e.g., with +// NTP servers). Thus, client code that uses the Time class must account for +// this. // // TimeDelta represents a duration of time, internally represented in // microseconds. // -// TimeTicks represents an abstract time that is most of the time incrementing -// for use in measuring time durations. It is internally represented in -// microseconds. It can not be converted to a human-readable time, but is -// guaranteed not to decrease (if the user changes the computer clock, -// Time::Now() may actually decrease or jump). But note that TimeTicks may -// "stand still", for example if the computer suspended. +// TimeTicks and ThreadTicks represent an abstract time that is most of the time +// incrementing, for use in measuring time durations. Internally, they are +// represented in microseconds. They can not be converted to a human-readable +// time, but are guaranteed not to decrease (unlike the Time class). Note that +// TimeTicks may "stand still" (e.g., if the computer is suspended), and +// ThreadTicks will "stand still" whenever the thread has been de-scheduled by +// the operating system. // -// These classes are represented as only a 64-bit value, so they can be -// efficiently passed by value. +// All time classes are copyable, assignable, and occupy 64-bits per +// instance. Thus, they can be efficiently passed by-value (as opposed to +// by-reference). // // Definitions of operator<< are provided to make these types work with // DCHECK_EQ() and other log macros. For human-readable formatting, see // "base/i18n/time_formatting.h". +// +// So many choices! Which time class should you use? Examples: +// +// Time: Interpreting the wall-clock time provided by a remote +// system. Detecting whether cached resources have +// expired. Providing the user with a display of the current date +// and time. Determining the amount of time between events across +// re-boots of the machine. +// +// TimeTicks: Tracking the amount of time a task runs. Executing delayed +// tasks at the right time. Computing presentation timestamps. +// Synchronizing audio and video using TimeTicks as a common +// reference clock (lip-sync). Measuring network round-trip +// latency. +// +// ThreadTicks: Benchmarking how long the current thread has been doing actual +// work. #ifndef BASE_TIME_TIME_H_ #define BASE_TIME_TIME_H_ +#include #include #include +#include #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/numerics/safe_math.h" #include "build/build_config.h" #if defined(OS_MACOSX) @@ -50,14 +74,29 @@ // For FILETIME in FromFileTime, until it moves to a new converter class. // See TODO(iyengar) below. #include -#endif -#include +#include "base/gtest_prod_util.h" +#endif namespace base { -class Time; -class TimeTicks; +class TimeDelta; + +// The functions in the time_internal namespace are meant to be used only by the +// time classes and functions. Please use the math operators defined in the +// time classes instead. +namespace time_internal { + +// Add or subtract |value| from a TimeDelta. The int64_t argument and return +// value are in terms of a microsecond timebase. +BASE_EXPORT int64_t SaturatedAdd(TimeDelta delta, int64_t value); +BASE_EXPORT int64_t SaturatedSub(TimeDelta delta, int64_t value); + +// Clamp |value| on overflow and underflow conditions. The int64_t argument and +// return value are in terms of a microsecond timebase. +BASE_EXPORT int64_t FromCheckedNumeric(const CheckedNumeric value); + +} // namespace time_internal // TimeDelta ------------------------------------------------------------------ @@ -70,11 +109,11 @@ class BASE_EXPORT TimeDelta { static TimeDelta FromDays(int days); static TimeDelta FromHours(int hours); static TimeDelta FromMinutes(int minutes); - static TimeDelta FromSeconds(int64 secs); - static TimeDelta FromMilliseconds(int64 ms); + static TimeDelta FromSeconds(int64_t secs); + static TimeDelta FromMilliseconds(int64_t ms); static TimeDelta FromSecondsD(double secs); static TimeDelta FromMillisecondsD(double ms); - static TimeDelta FromMicroseconds(int64 us); + static TimeDelta FromMicroseconds(int64_t us); #if defined(OS_WIN) static TimeDelta FromQPCValue(LONGLONG qpc_value); #endif @@ -83,9 +122,7 @@ class BASE_EXPORT TimeDelta { // when deserializing a |TimeDelta| structure, using a value known to be // compatible. It is not provided as a constructor because the integer type // may be unclear from the perspective of a caller. - static TimeDelta FromInternalValue(int64 delta) { - return TimeDelta(delta); - } + static TimeDelta FromInternalValue(int64_t delta) { return TimeDelta(delta); } // Returns the maximum time delta, which should be greater than any reasonable // time delta we might compare it to. Adding or subtracting the maximum time @@ -96,14 +133,24 @@ class BASE_EXPORT TimeDelta { // use this and do arithmetic on it, as it is more error prone than using the // provided operators. // For serializing, use FromInternalValue to reconstitute. - int64 ToInternalValue() const { - return delta_; + int64_t ToInternalValue() const { return delta_; } + + // Returns the magnitude (absolute value) of this TimeDelta. + TimeDelta magnitude() const { + // Some toolchains provide an incomplete C++11 implementation and lack an + // int64_t overload for std::abs(). The following is a simple branchless + // implementation: + const int64_t mask = delta_ >> (sizeof(delta_) * 8 - 1); + return TimeDelta((delta_ + mask) ^ mask); + } + + // Returns true if the time delta is zero. + bool is_zero() const { + return delta_ == 0; } // Returns true if the time delta is the maximum time delta. - bool is_max() const { - return delta_ == std::numeric_limits::max(); - } + bool is_max() const { return delta_ == std::numeric_limits::max(); } #if defined(OS_POSIX) struct timespec ToTimeSpec() const; @@ -118,11 +165,11 @@ class BASE_EXPORT TimeDelta { int InHours() const; int InMinutes() const; double InSecondsF() const; - int64 InSeconds() const; + int64_t InSeconds() const; double InMillisecondsF() const; - int64 InMilliseconds() const; - int64 InMillisecondsRoundedUp() const; - int64 InMicroseconds() const; + int64_t InMilliseconds() const; + int64_t InMillisecondsRoundedUp() const; + int64_t InMicroseconds() const; TimeDelta& operator=(TimeDelta other) { delta_ = other.delta_; @@ -131,47 +178,48 @@ class BASE_EXPORT TimeDelta { // Computations with other deltas. TimeDelta operator+(TimeDelta other) const { - return TimeDelta(delta_ + other.delta_); + return TimeDelta(time_internal::SaturatedAdd(*this, other.delta_)); } TimeDelta operator-(TimeDelta other) const { - return TimeDelta(delta_ - other.delta_); + return TimeDelta(time_internal::SaturatedSub(*this, other.delta_)); } TimeDelta& operator+=(TimeDelta other) { - delta_ += other.delta_; - return *this; + return *this = (*this + other); } TimeDelta& operator-=(TimeDelta other) { - delta_ -= other.delta_; - return *this; + return *this = (*this - other); } TimeDelta operator-() const { return TimeDelta(-delta_); } - // Computations with ints, note that we only allow multiplicative operations - // with ints, and additive operations with other deltas. - TimeDelta operator*(int64 a) const { - return TimeDelta(delta_ * a); + // Computations with numeric types. + template + TimeDelta operator*(T a) const { + CheckedNumeric rv(delta_); + rv *= a; + return TimeDelta(time_internal::FromCheckedNumeric(rv)); } - TimeDelta operator/(int64 a) const { - return TimeDelta(delta_ / a); + template + TimeDelta operator/(T a) const { + CheckedNumeric rv(delta_); + rv /= a; + return TimeDelta(time_internal::FromCheckedNumeric(rv)); } - TimeDelta& operator*=(int64 a) { - delta_ *= a; - return *this; + template + TimeDelta& operator*=(T a) { + return *this = (*this * a); } - TimeDelta& operator/=(int64 a) { - delta_ /= a; - return *this; - } - int64 operator/(TimeDelta a) const { - return delta_ / a.delta_; + template + TimeDelta& operator/=(T a) { + return *this = (*this / a); } - // Defined below because it depends on the definition of the other classes. - Time operator+(Time t) const; - TimeTicks operator+(TimeTicks t) const; + int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; } + TimeDelta operator%(TimeDelta a) const { + return TimeDelta(delta_ % a.delta_); + } // Comparison operators. bool operator==(TimeDelta other) const { @@ -194,47 +242,151 @@ class BASE_EXPORT TimeDelta { } private: - friend class Time; - friend class TimeTicks; - friend TimeDelta operator*(int64 a, TimeDelta td); + friend int64_t time_internal::SaturatedAdd(TimeDelta delta, int64_t value); + friend int64_t time_internal::SaturatedSub(TimeDelta delta, int64_t value); // Constructs a delta given the duration in microseconds. This is private // to avoid confusion by callers with an integer constructor. Use // FromSeconds, FromMilliseconds, etc. instead. - explicit TimeDelta(int64 delta_us) : delta_(delta_us) { - } + explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {} + + // Private method to build a delta from a double. + static TimeDelta FromDouble(double value); // Delta in microseconds. - int64 delta_; + int64_t delta_; }; -inline TimeDelta operator*(int64 a, TimeDelta td) { - return TimeDelta(a * td.delta_); +template +inline TimeDelta operator*(T a, TimeDelta td) { + return td * a; } // For logging use only. BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta); +// Do not reference the time_internal::TimeBase template class directly. Please +// use one of the time subclasses instead, and only reference the public +// TimeBase members via those classes. +namespace time_internal { + +// TimeBase-------------------------------------------------------------------- + +// Provides value storage and comparison/math operations common to all time +// classes. Each subclass provides for strong type-checking to ensure +// semantically meaningful comparison/math of time values from the same clock +// source or timeline. +template +class TimeBase { + public: + static const int64_t kHoursPerDay = 24; + static const int64_t kMillisecondsPerSecond = 1000; + static const int64_t kMillisecondsPerDay = + kMillisecondsPerSecond * 60 * 60 * kHoursPerDay; + static const int64_t kMicrosecondsPerMillisecond = 1000; + static const int64_t kMicrosecondsPerSecond = + kMicrosecondsPerMillisecond * kMillisecondsPerSecond; + static const int64_t kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60; + static const int64_t kMicrosecondsPerHour = kMicrosecondsPerMinute * 60; + static const int64_t kMicrosecondsPerDay = + kMicrosecondsPerHour * kHoursPerDay; + static const int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; + static const int64_t kNanosecondsPerMicrosecond = 1000; + static const int64_t kNanosecondsPerSecond = + kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; + + // Returns true if this object has not been initialized. + // + // Warning: Be careful when writing code that performs math on time values, + // since it's possible to produce a valid "zero" result that should not be + // interpreted as a "null" value. + bool is_null() const { + return us_ == 0; + } + + // Returns true if this object represents the maximum time. + bool is_max() const { return us_ == std::numeric_limits::max(); } + + // For serializing only. Use FromInternalValue() to reconstitute. Please don't + // use this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64_t ToInternalValue() const { return us_; } + + TimeClass& operator=(TimeClass other) { + us_ = other.us_; + return *(static_cast(this)); + } + + // Compute the difference between two times. + TimeDelta operator-(TimeClass other) const { + return TimeDelta::FromMicroseconds(us_ - other.us_); + } + + // Return a new time modified by some delta. + TimeClass operator+(TimeDelta delta) const { + return TimeClass(time_internal::SaturatedAdd(delta, us_)); + } + TimeClass operator-(TimeDelta delta) const { + return TimeClass(-time_internal::SaturatedSub(delta, us_)); + } + + // Modify by some time delta. + TimeClass& operator+=(TimeDelta delta) { + return static_cast(*this = (*this + delta)); + } + TimeClass& operator-=(TimeDelta delta) { + return static_cast(*this = (*this - delta)); + } + + // Comparison operators + bool operator==(TimeClass other) const { + return us_ == other.us_; + } + bool operator!=(TimeClass other) const { + return us_ != other.us_; + } + bool operator<(TimeClass other) const { + return us_ < other.us_; + } + bool operator<=(TimeClass other) const { + return us_ <= other.us_; + } + bool operator>(TimeClass other) const { + return us_ > other.us_; + } + bool operator>=(TimeClass other) const { + return us_ >= other.us_; + } + + // Converts an integer value representing TimeClass to a class. This is used + // when deserializing a |TimeClass| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static TimeClass FromInternalValue(int64_t us) { return TimeClass(us); } + + protected: + explicit TimeBase(int64_t us) : us_(us) {} + + // Time value in a microsecond timebase. + int64_t us_; +}; + +} // namespace time_internal + +template +inline TimeClass operator+(TimeDelta delta, TimeClass t) { + return t + delta; +} + // Time ----------------------------------------------------------------------- -// Represents a wall clock time in UTC. -class BASE_EXPORT Time { +// Represents a wall clock time in UTC. Values are not guaranteed to be +// monotonically non-decreasing and are subject to large amounts of skew. +class BASE_EXPORT Time : public time_internal::TimeBase is rarely useful. One such use is when A is non-const ref that you +// Tuple is rarely useful. One such use is when A is non-const ref that you // want filled by the dispatchee, and the tuple is merely a container for that // output (a "tier"). See MakeRefTuple and its usages. -struct Tuple0 { - typedef Tuple0 ValueTuple; - typedef Tuple0 RefTuple; - typedef Tuple0 ParamTuple; +template +struct TupleBaseImpl; +template +using TupleBase = TupleBaseImpl, Ts...>; +template +struct TupleLeaf; + +template +struct Tuple final : TupleBase { + Tuple() : TupleBase() {} + explicit Tuple(typename TupleTraits::ParamType... args) + : TupleBase(args...) {} }; -template -struct Tuple1 { - public: - typedef A TypeA; +// Avoids ambiguity between Tuple's two constructors. +template <> +struct Tuple<> final {}; - Tuple1() {} - explicit Tuple1(typename TupleTraits::ParamType a) : a(a) {} - - A a; +template +struct TupleBaseImpl, Ts...> : TupleLeaf... { + TupleBaseImpl() : TupleLeaf()... {} + explicit TupleBaseImpl(typename TupleTraits::ParamType... args) + : TupleLeaf(args)... {} }; -template -struct Tuple2 { - public: - typedef A TypeA; - typedef B TypeB; +template +struct TupleLeaf { + TupleLeaf() {} + explicit TupleLeaf(typename TupleTraits::ParamType x) : x(x) {} - Tuple2() {} - Tuple2(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b) - : a(a), b(b) { - } + T& get() { return x; } + const T& get() const { return x; } - A a; - B b; + T x; }; -template -struct Tuple3 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; +// Tuple getters -------------------------------------------------------------- +// +// Allows accessing an arbitrary tuple element by index. +// +// Example usage: +// base::Tuple t2; +// base::get<0>(t2) = 42; +// base::get<1>(t2) = 3.14; - Tuple3() {} - Tuple3(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c) - : a(a), b(b), c(c){ - } +template +T& get(TupleLeaf& leaf) { + return leaf.get(); +} - A a; - B b; - C c; -}; - -template -struct Tuple4 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - - Tuple4() {} - Tuple4(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d) - : a(a), b(b), c(c), d(d) { - } - - A a; - B b; - C c; - D d; -}; - -template -struct Tuple5 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - - Tuple5() {} - Tuple5(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e) - : a(a), b(b), c(c), d(d), e(e) { - } - - A a; - B b; - C c; - D d; - E e; -}; - -template -struct Tuple6 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - - Tuple6() {} - Tuple6(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e, - typename TupleTraits::ParamType f) - : a(a), b(b), c(c), d(d), e(e), f(f) { - } - - A a; - B b; - C c; - D d; - E e; - F f; -}; - -template -struct Tuple7 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - typedef G TypeG; - - Tuple7() {} - Tuple7(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e, - typename TupleTraits::ParamType f, - typename TupleTraits::ParamType g) - : a(a), b(b), c(c), d(d), e(e), f(f), g(g) { - } - - A a; - B b; - C c; - D d; - E e; - F f; - G g; -}; - -template -struct Tuple8 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - typedef G TypeG; - typedef H TypeH; - - Tuple8() {} - Tuple8(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e, - typename TupleTraits::ParamType f, - typename TupleTraits::ParamType g, - typename TupleTraits::ParamType h) - : a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) { - } - - A a; - B b; - C c; - D d; - E e; - F f; - G g; - H h; -}; +template +const T& get(const TupleLeaf& leaf) { + return leaf.get(); +} // Tuple types ---------------------------------------------------------------- // // Allows for selection of ValueTuple/RefTuple/ParamTuple without needing the // definitions of class types the tuple takes as parameters. -template <> -struct TupleTypes< Tuple0 > { - typedef Tuple0 ValueTuple; - typedef Tuple0 RefTuple; - typedef Tuple0 ParamTuple; -}; +template +struct TupleTypes; -template -struct TupleTypes< Tuple1 > { - typedef Tuple1::ValueType> ValueTuple; - typedef Tuple1::RefType> RefTuple; - typedef Tuple1::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple2 > { - typedef Tuple2::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple2::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple2::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple3 > { - typedef Tuple3::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple3::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple3::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple4 > { - typedef Tuple4::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple4::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple4::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple5 > { - typedef Tuple5::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple5::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple5::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple6 > { - typedef Tuple6::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple6::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple6::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple7 > { - typedef Tuple7::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple7::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple7::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple8 > { - typedef Tuple8::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple8::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple8::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; +template +struct TupleTypes> { + using ValueTuple = Tuple::ValueType...>; + using RefTuple = Tuple::RefType...>; + using ParamTuple = Tuple::ParamType...>; }; // Tuple creators ------------------------------------------------------------- @@ -421,105 +220,17 @@ typedef Tuple8::RefType, // Helper functions for constructing tuples while inferring the template // argument types. -inline Tuple0 MakeTuple() { - return Tuple0(); -} - -template -inline Tuple1 MakeTuple(const A& a) { - return Tuple1(a); -} - -template -inline Tuple2 MakeTuple(const A& a, const B& b) { - return Tuple2(a, b); -} - -template -inline Tuple3 MakeTuple(const A& a, const B& b, const C& c) { - return Tuple3(a, b, c); -} - -template -inline Tuple4 MakeTuple(const A& a, const B& b, const C& c, - const D& d) { - return Tuple4(a, b, c, d); -} - -template -inline Tuple5 MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e) { - return Tuple5(a, b, c, d, e); -} - -template -inline Tuple6 MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f) { - return Tuple6(a, b, c, d, e, f); -} - -template -inline Tuple7 MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f, - const G& g) { - return Tuple7(a, b, c, d, e, f, g); -} - -template -inline Tuple8 MakeTuple(const A& a, const B& b, - const C& c, const D& d, - const E& e, const F& f, - const G& g, const H& h) { - return Tuple8(a, b, c, d, e, f, g, h); +template +inline Tuple MakeTuple(const Ts&... arg) { + return Tuple(arg...); } // The following set of helpers make what Boost refers to as "Tiers" - a tuple // of references. -template -inline Tuple1 MakeRefTuple(A& a) { - return Tuple1(a); -} - -template -inline Tuple2 MakeRefTuple(A& a, B& b) { - return Tuple2(a, b); -} - -template -inline Tuple3 MakeRefTuple(A& a, B& b, C& c) { - return Tuple3(a, b, c); -} - -template -inline Tuple4 MakeRefTuple(A& a, B& b, C& c, D& d) { - return Tuple4(a, b, c, d); -} - -template -inline Tuple5 MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { - return Tuple5(a, b, c, d, e); -} - -template -inline Tuple6 MakeRefTuple(A& a, B& b, C& c, D& d, E& e, - F& f) { - return Tuple6(a, b, c, d, e, f); -} - -template -inline Tuple7 MakeRefTuple(A& a, B& b, C& c, D& d, - E& e, F& f, G& g) { - return Tuple7(a, b, c, d, e, f, g); -} - -template -inline Tuple8 MakeRefTuple(A& a, B& b, C& c, - D& d, E& e, F& f, - G& g, H& h) { - return Tuple8(a, b, c, d, e, f, g, h); +template +inline Tuple MakeRefTuple(Ts&... arg) { + return Tuple(arg...); } // Dispatchers ---------------------------------------------------------------- @@ -533,759 +244,63 @@ inline Tuple8 MakeRefTuple(A& a, B& b, C& c, // Non-Static Dispatchers with no out params. -template -inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { - (obj->*method)(); +template +inline void DispatchToMethodImpl(ObjT* obj, + Method method, + const Tuple& arg, + IndexSequence) { + (obj->*method)(base::internal::UnwrapTraits::Unwrap(get(arg))...); } -template -inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a)); -} - -template +template inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple7& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f), - base::internal::UnwrapTraits::Unwrap(arg.g)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple8& arg) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f), - base::internal::UnwrapTraits::Unwrap(arg.g), - base::internal::UnwrapTraits::Unwrap(arg.h)); + const Tuple& arg) { + DispatchToMethodImpl(obj, method, arg, MakeIndexSequence()); } // Static Dispatchers with no out params. -template -inline void DispatchToFunction(Function function, const Tuple0& arg) { - (*function)(); +template +inline void DispatchToFunctionImpl(Function function, + const Tuple& arg, + IndexSequence) { + (*function)(base::internal::UnwrapTraits::Unwrap(get(arg))...); } -template -inline void DispatchToFunction(Function function, const A& arg) { - (*function)(arg); +template +inline void DispatchToFunction(Function function, const Tuple& arg) { + DispatchToFunctionImpl(function, arg, MakeIndexSequence()); } -template -inline void DispatchToFunction(Function function, const Tuple1& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a)); +// Dispatchers with out parameters. + +template +inline void DispatchToMethodImpl(ObjT* obj, + Method method, + const Tuple& in, + Tuple* out, + IndexSequence, + IndexSequence) { + (obj->*method)(base::internal::UnwrapTraits::Unwrap(get(in))..., + &get(*out)...); } -template -inline void DispatchToFunction(Function function, const Tuple2& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b)); -} - -template -inline void DispatchToFunction(Function function, const Tuple3& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple4& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple5& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple6& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple7& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f), - base::internal::UnwrapTraits::Unwrap(arg.g)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple8& arg) { - (*function)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f), - base::internal::UnwrapTraits::Unwrap(arg.g), - base::internal::UnwrapTraits::Unwrap(arg.h)); -} - -// Dispatchers with 0 out param (as a Tuple0). - -template +template inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& arg, Tuple0*) { - (obj->*method)(); + const Tuple& in, + Tuple* out) { + DispatchToMethodImpl(obj, method, in, out, + MakeIndexSequence(), + MakeIndexSequence()); } -template -inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg)); -} +} // namespace base -template -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple1& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a)); -} - -template -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple2& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& arg, Tuple0*) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(arg.a), - base::internal::UnwrapTraits::Unwrap(arg.b), - base::internal::UnwrapTraits::Unwrap(arg.c), - base::internal::UnwrapTraits::Unwrap(arg.d), - base::internal::UnwrapTraits::Unwrap(arg.e), - base::internal::UnwrapTraits::Unwrap(arg.f)); -} - -// Dispatchers with 1 out param. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple1* out) { - (obj->*method)(&out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple1* out) { - (obj->*method)(in, &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple1* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple1* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple1* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple1* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple1* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple1* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - base::internal::UnwrapTraits::Unwrap(in.f), - &out->a); -} - -// Dispatchers with 2 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple2* out) { - (obj->*method)(&out->a, &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple2* out) { - (obj->*method)(in, &out->a, &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple2* out) { - (obj->*method)( - base::internal::UnwrapTraits::Unwrap(in.a), &out->a, &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple2* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple2* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple2* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple2* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple2* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - base::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b); -} - -// Dispatchers with 3 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple3* out) { - (obj->*method)(&out->a, &out->b, &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple3* out) { - (obj->*method)(in, &out->a, &out->b, &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple3* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple3* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple3* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple3* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple3* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple3* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - base::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b, - &out->c); -} - -// Dispatchers with 4 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple4* out) { - (obj->*method)(&out->a, &out->b, &out->c, &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple4* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - base::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b, - &out->c, - &out->d); -} - -// Dispatchers with 5 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple5* out) { - (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple5* out) { - (obj->*method)(base::internal::UnwrapTraits::Unwrap(in.a), - base::internal::UnwrapTraits::Unwrap(in.b), - base::internal::UnwrapTraits::Unwrap(in.c), - base::internal::UnwrapTraits::Unwrap(in.d), - base::internal::UnwrapTraits::Unwrap(in.e), - base::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -#endif // BASE_TUPLE_H__ +#endif // BASE_TUPLE_H_ diff --git a/security/sandbox/chromium/base/values.h b/security/sandbox/chromium/base/values.h index 04b2d26eac60..07e5b6c83823 100644 --- a/security/sandbox/chromium/base/values.h +++ b/security/sandbox/chromium/base/values.h @@ -9,7 +9,7 @@ // JavaScript. As such, it is NOT a generalized variant type, since only the // types supported by JavaScript/JSON are supported. // -// IN PARTICULAR this means that there is no support for int64 or unsigned +// IN PARTICULAR this means that there is no support for int64_t or unsigned // numbers. Writing JSON with such types would violate the spec. If you need // something like this, either use a double or make a string value containing // the number you want. @@ -18,6 +18,7 @@ #define BASE_VALUES_H_ #include +#include #include #include @@ -26,13 +27,15 @@ #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" +#include "base/strings/string_piece.h" namespace base { +class BinaryValue; class DictionaryValue; class FundamentalValue; class ListValue; @@ -63,7 +66,7 @@ class BASE_EXPORT Value { virtual ~Value(); - static Value* CreateNullValue(); + static scoped_ptr CreateNullValue(); // Returns the type of the value stored by the current Value object. // Each type will be implemented by only one subclass of Value, so it's @@ -85,6 +88,7 @@ class BASE_EXPORT Value { virtual bool GetAsString(std::string* out_value) const; virtual bool GetAsString(string16* out_value) const; virtual bool GetAsString(const StringValue** out_value) const; + virtual bool GetAsBinary(const BinaryValue** out_value) const; virtual bool GetAsList(ListValue** out_value); virtual bool GetAsList(const ListValue** out_value) const; virtual bool GetAsDictionary(DictionaryValue** out_value); @@ -97,6 +101,8 @@ class BASE_EXPORT Value { // Subclasses return their own type directly in their overrides; // this works because C++ supports covariant return types. virtual Value* DeepCopy() const; + // Preferred version of DeepCopy. TODO(estade): remove the above. + scoped_ptr CreateDeepCopy() const; // Compares if two Value objects have equal contents. virtual bool Equals(const Value* other) const; @@ -188,6 +194,7 @@ class BASE_EXPORT BinaryValue: public Value { const char* GetBuffer() const { return buffer_.get(); } // Overridden from Value: + bool GetAsBinary(const BinaryValue** out_value) const override; BinaryValue* DeepCopy() const override; bool Equals(const Value* other) const override; @@ -203,6 +210,9 @@ class BASE_EXPORT BinaryValue: public Value { // are |std::string|s and should be UTF-8 encoded. class BASE_EXPORT DictionaryValue : public Value { public: + // Returns |value| if it is a dictionary, nullptr otherwise. + static scoped_ptr From(scoped_ptr value); + DictionaryValue(); ~DictionaryValue() override; @@ -228,9 +238,9 @@ class BASE_EXPORT DictionaryValue : public Value { // within a key, but there are no other restrictions on keys. // If the key at any step of the way doesn't exist, or exists but isn't // a DictionaryValue, a new DictionaryValue will be created and attached - // to the path in that location. - // Note that the dictionary takes ownership of the value referenced by - // |in_value|, and therefore |in_value| must be non-NULL. + // to the path in that location. |in_value| must be non-null. + void Set(const std::string& path, scoped_ptr in_value); + // Deprecated version of the above. TODO(estade): remove. void Set(const std::string& path, Value* in_value); // Convenience forms of Set(). These methods will replace any existing @@ -243,6 +253,9 @@ class BASE_EXPORT DictionaryValue : public Value { // Like Set(), but without special treatment of '.'. This allows e.g. URLs to // be used as paths. + void SetWithoutPathExpansion(const std::string& key, + scoped_ptr in_value); + // Deprecated version of the above. TODO(estade): remove. void SetWithoutPathExpansion(const std::string& key, Value* in_value); // Convenience forms of SetWithoutPathExpansion(). @@ -262,8 +275,8 @@ class BASE_EXPORT DictionaryValue : public Value { // Otherwise, it will return false and |out_value| will be untouched. // Note that the dictionary always owns the value that's returned. // |out_value| is optional and will only be set if non-NULL. - bool Get(const std::string& path, const Value** out_value) const; - bool Get(const std::string& path, Value** out_value); + bool Get(StringPiece path, const Value** out_value) const; + bool Get(StringPiece path, Value** out_value); // These are convenience forms of Get(). The value will be retrieved // and the return value will be true if the path is valid and the value at @@ -279,9 +292,9 @@ class BASE_EXPORT DictionaryValue : public Value { bool GetStringASCII(const std::string& path, std::string* out_value) const; bool GetBinary(const std::string& path, const BinaryValue** out_value) const; bool GetBinary(const std::string& path, BinaryValue** out_value); - bool GetDictionary(const std::string& path, + bool GetDictionary(StringPiece path, const DictionaryValue** out_value) const; - bool GetDictionary(const std::string& path, DictionaryValue** out_value); + bool GetDictionary(StringPiece path, DictionaryValue** out_value); bool GetList(const std::string& path, const ListValue** out_value) const; bool GetList(const std::string& path, ListValue** out_value); @@ -330,7 +343,7 @@ class BASE_EXPORT DictionaryValue : public Value { // Makes a copy of |this| but doesn't include empty dictionaries and lists in // the copy. This never returns NULL, even if |this| itself is empty. - DictionaryValue* DeepCopyWithoutEmptyChildren() const; + scoped_ptr DeepCopyWithoutEmptyChildren() const; // Merge |dictionary| into this dictionary. This is done recursively, i.e. any // sub-dictionaries will be merged as well. In case of key collisions, the @@ -362,6 +375,8 @@ class BASE_EXPORT DictionaryValue : public Value { // Overridden from Value: DictionaryValue* DeepCopy() const override; + // Preferred version of DeepCopy. TODO(estade): remove the above. + scoped_ptr CreateDeepCopy() const; bool Equals(const Value* other) const override; private: @@ -376,6 +391,9 @@ class BASE_EXPORT ListValue : public Value { typedef ValueVector::iterator iterator; typedef ValueVector::const_iterator const_iterator; + // Returns |value| if it is a list, nullptr otherwise. + static scoped_ptr From(scoped_ptr value); + ListValue(); ~ListValue() override; @@ -394,6 +412,8 @@ class BASE_EXPORT ListValue : public Value { // Returns true if successful, or false if the index was negative or // the value is a null pointer. bool Set(size_t index, Value* in_value); + // Preferred version of the above. TODO(estade): remove the above. + bool Set(size_t index, scoped_ptr in_value); // Gets the Value at the given index. Modifies |out_value| (and returns true) // only if the index falls within the current list range. @@ -439,6 +459,8 @@ class BASE_EXPORT ListValue : public Value { iterator Erase(iterator iter, scoped_ptr* out_value); // Appends a Value to the end of the list. + void Append(scoped_ptr in_value); + // Deprecated version of the above. TODO(estade): remove. void Append(Value* in_value); // Convenience forms of Append. @@ -480,19 +502,29 @@ class BASE_EXPORT ListValue : public Value { ListValue* DeepCopy() const override; bool Equals(const Value* other) const override; + // Preferred version of DeepCopy. TODO(estade): remove DeepCopy. + scoped_ptr CreateDeepCopy() const; + private: ValueVector list_; DISALLOW_COPY_AND_ASSIGN(ListValue); }; -// This interface is implemented by classes that know how to serialize and -// deserialize Value objects. +// This interface is implemented by classes that know how to serialize +// Value objects. class BASE_EXPORT ValueSerializer { public: virtual ~ValueSerializer(); virtual bool Serialize(const Value& root) = 0; +}; + +// This interface is implemented by classes that know how to deserialize Value +// objects. +class BASE_EXPORT ValueDeserializer { + public: + virtual ~ValueDeserializer(); // This method deserializes the subclass-specific format into a Value object. // If the return value is non-NULL, the caller takes ownership of returned @@ -500,7 +532,8 @@ class BASE_EXPORT ValueSerializer { // error_code will be set with the underlying error. // If |error_message| is non-null, it will be filled in with a formatted // error message including the location of the error if appropriate. - virtual Value* Deserialize(int* error_code, std::string* error_str) = 0; + virtual scoped_ptr Deserialize(int* error_code, + std::string* error_str) = 0; }; // Stream operator so Values can be used in assertion statements. In order that diff --git a/security/sandbox/chromium/base/version.h b/security/sandbox/chromium/base/version.h index b3012eb921b1..85c99a355bda 100644 --- a/security/sandbox/chromium/base/version.h +++ b/security/sandbox/chromium/base/version.h @@ -5,11 +5,11 @@ #ifndef BASE_VERSION_H_ #define BASE_VERSION_H_ +#include #include #include #include "base/base_export.h" -#include "base/basictypes.h" namespace base { @@ -24,7 +24,7 @@ class BASE_EXPORT Version { ~Version(); // Initializes from a decimal dotted version number, like "0.1.1". - // Each component is limited to a uint16. Call IsValid() to learn + // Each component is limited to a uint16_t. Call IsValid() to learn // the outcome. explicit Version(const std::string& version_str); @@ -57,10 +57,10 @@ class BASE_EXPORT Version { // Return the string representation of this version. const std::string GetString() const; - const std::vector& components() const { return components_; } + const std::vector& components() const { return components_; } private: - std::vector components_; + std::vector components_; }; } // namespace base diff --git a/security/sandbox/chromium/base/win/pe_image.cc b/security/sandbox/chromium/base/win/pe_image.cc index db28699b7349..4b5d620999a9 100644 --- a/security/sandbox/chromium/base/win/pe_image.cc +++ b/security/sandbox/chromium/base/win/pe_image.cc @@ -5,20 +5,13 @@ // This file implements PEImage, a generic class to manipulate PE files. // This file was adapted from GreenBorder's Code. +#include + #include "base/win/pe_image.h" namespace base { namespace win { -#if defined(_WIN64) && !defined(NACL_WIN64) -// TODO(jschuh): crbug.com/167707 Make sure this is ok. -#pragma message ("Warning: \ - This code is not tested on x64. Please make sure all the base unit tests\ - pass before doing any real work. The current unit tests don't test the\ - differences between 32- and 64-bits implementations. Bugs may slip through.\ - You need to improve the coverage before continuing.") -#endif - // Structure to perform imports enumerations. struct EnumAllImportsStorage { PEImage::EnumImportsFunction callback; @@ -27,46 +20,56 @@ struct EnumAllImportsStorage { namespace { - // Compare two strings byte by byte on an unsigned basis. - // if s1 == s2, return 0 - // if s1 < s2, return negative - // if s1 > s2, return positive - // Exception if inputs are invalid. - int StrCmpByByte(LPCSTR s1, LPCSTR s2) { - while (*s1 != '\0' && *s1 == *s2) { - ++s1; - ++s2; - } +// PdbInfo Signature +const DWORD kPdbInfoSignature = 'SDSR'; - return (*reinterpret_cast(s1) - - *reinterpret_cast(s2)); +// Compare two strings byte by byte on an unsigned basis. +// if s1 == s2, return 0 +// if s1 < s2, return negative +// if s1 > s2, return positive +// Exception if inputs are invalid. +int StrCmpByByte(LPCSTR s1, LPCSTR s2) { + while (*s1 != '\0' && *s1 == *s2) { + ++s1; + ++s2; } + return (*reinterpret_cast(s1) - + *reinterpret_cast(s2)); +} + +struct PdbInfo { + DWORD Signature; + GUID Guid; + DWORD Age; + char PdbFileName[1]; +}; + } // namespace // Callback used to enumerate imports. See EnumImportChunksFunction. bool ProcessImportChunk(const PEImage &image, LPCSTR module, PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, PVOID cookie) { - EnumAllImportsStorage &storage = *reinterpret_cast( - cookie); + EnumAllImportsStorage& storage = + *reinterpret_cast(cookie); return image.EnumOneImportChunk(storage.callback, module, name_table, iat, storage.cookie); } // Callback used to enumerate delay imports. See EnumDelayImportChunksFunction. -bool ProcessDelayImportChunk(const PEImage &image, +bool ProcessDelayImportChunk(const PEImage& image, PImgDelayDescr delay_descriptor, - LPCSTR module, PIMAGE_THUNK_DATA name_table, - PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat, - PIMAGE_THUNK_DATA unload_iat, PVOID cookie) { - EnumAllImportsStorage &storage = *reinterpret_cast( - cookie); + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) { + EnumAllImportsStorage& storage = + *reinterpret_cast(cookie); return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor, - module, name_table, iat, bound_iat, - unload_iat, storage.cookie); + module, name_table, iat, storage.cookie); } void PEImage::set_module(HMODULE module) { @@ -149,6 +152,36 @@ PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName( return ret; } +bool PEImage::GetDebugId(LPGUID guid, LPDWORD age) const { + if (NULL == guid || NULL == age) { + return false; + } + + DWORD debug_directory_size = + GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG); + PIMAGE_DEBUG_DIRECTORY debug_directory = + reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG)); + + size_t directory_count = + debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY); + + for (size_t index = 0; index < directory_count; ++index) { + if (debug_directory[index].Type == IMAGE_DEBUG_TYPE_CODEVIEW) { + PdbInfo* pdb_info = reinterpret_cast( + RVAToAddr(debug_directory[index].AddressOfRawData)); + if (pdb_info->Signature != kPdbInfoSignature) { + // Unsupported PdbInfo signature + return false; + } + *guid = pdb_info->Guid; + *age = pdb_info->Age; + return true; + } + } + return false; +} + PDWORD PEImage::GetExportEntry(LPCSTR name) const { PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory(); @@ -178,11 +211,7 @@ FARPROC PEImage::GetProcAddress(LPCSTR function_name) const { // Check for forwarded exports as a special case. if (exports <= function && exports + size > function) -#pragma warning(push) -#pragma warning(disable: 4312) - // This cast generates a warning because it is 32 bit specific. - return reinterpret_cast(0xFFFFFFFF); -#pragma warning(pop) + return reinterpret_cast(-1); return reinterpret_cast(function); } @@ -407,8 +436,6 @@ bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback, for (; delay_descriptor->rvaHmod; delay_descriptor++) { PIMAGE_THUNK_DATA name_table; PIMAGE_THUNK_DATA iat; - PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT - PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT LPCSTR module_name; // check if VC7-style imports, using RVAs instead of @@ -416,33 +443,25 @@ bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback, bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0; if (rvas) { - module_name = reinterpret_cast( - RVAToAddr(delay_descriptor->rvaDLLName)); + module_name = + reinterpret_cast(RVAToAddr(delay_descriptor->rvaDLLName)); name_table = reinterpret_cast( - RVAToAddr(delay_descriptor->rvaINT)); + RVAToAddr(delay_descriptor->rvaINT)); iat = reinterpret_cast( - RVAToAddr(delay_descriptor->rvaIAT)); - bound_iat = reinterpret_cast( - RVAToAddr(delay_descriptor->rvaBoundIAT)); - unload_iat = reinterpret_cast( - RVAToAddr(delay_descriptor->rvaUnloadIAT)); + RVAToAddr(delay_descriptor->rvaIAT)); } else { -#pragma warning(push) -#pragma warning(disable: 4312) - // These casts generate warnings because they are 32 bit specific. - module_name = reinterpret_cast(delay_descriptor->rvaDLLName); + // Values in IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT are 32-bit, even on 64-bit + // platforms. See section 4.8 of PECOFF image spec rev 8.3. + module_name = reinterpret_cast( + static_cast(delay_descriptor->rvaDLLName)); name_table = reinterpret_cast( - delay_descriptor->rvaINT); - iat = reinterpret_cast(delay_descriptor->rvaIAT); - bound_iat = reinterpret_cast( - delay_descriptor->rvaBoundIAT); - unload_iat = reinterpret_cast( - delay_descriptor->rvaUnloadIAT); -#pragma warning(pop) + static_cast(delay_descriptor->rvaINT)); + iat = reinterpret_cast( + static_cast(delay_descriptor->rvaIAT)); } if (!callback(*this, delay_descriptor, module_name, name_table, iat, - bound_iat, unload_iat, cookie)) + cookie)) return false; } @@ -454,12 +473,7 @@ bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback, LPCSTR module_name, PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, - PIMAGE_THUNK_DATA bound_iat, - PIMAGE_THUNK_DATA unload_iat, PVOID cookie) const { - UNREFERENCED_PARAMETER(bound_iat); - UNREFERENCED_PARAMETER(unload_iat); - for (; name_table->u1.Ordinal; name_table++, iat++) { LPCSTR name = NULL; WORD ordinal = 0; @@ -475,12 +489,8 @@ bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback, import = reinterpret_cast( RVAToAddr(name_table->u1.ForwarderString)); } else { -#pragma warning(push) -#pragma warning(disable: 4312) - // This cast generates a warning because it is 32 bit specific. import = reinterpret_cast( name_table->u1.ForwarderString); -#pragma warning(pop) } hint = import->Hint; @@ -521,13 +531,13 @@ bool PEImage::VerifyMagic() const { return true; } -bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const { +bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD* on_disk_offset) const { LPVOID address = RVAToAddr(rva); return ImageAddrToOnDiskOffset(address, on_disk_offset); } bool PEImage::ImageAddrToOnDiskOffset(LPVOID address, - DWORD *on_disk_offset) const { + DWORD* on_disk_offset) const { if (NULL == address) return false; @@ -536,14 +546,11 @@ bool PEImage::ImageAddrToOnDiskOffset(LPVOID address, if (NULL == section_header) return false; -#pragma warning(push) -#pragma warning(disable: 4311) - // These casts generate warnings because they are 32 bit specific. // Don't follow the virtual RVAToAddr, use the one on the base. - DWORD offset_within_section = reinterpret_cast(address) - - reinterpret_cast(PEImage::RVAToAddr( - section_header->VirtualAddress)); -#pragma warning(pop) + DWORD offset_within_section = + static_cast(reinterpret_cast(address)) - + static_cast(reinterpret_cast( + PEImage::RVAToAddr(section_header->VirtualAddress))); *on_disk_offset = section_header->PointerToRawData + offset_within_section; return true; diff --git a/security/sandbox/chromium/base/win/pe_image.h b/security/sandbox/chromium/base/win/pe_image.h index d93e99dc1439..4c36bcf850e6 100644 --- a/security/sandbox/chromium/base/win/pe_image.h +++ b/security/sandbox/chromium/base/win/pe_image.h @@ -69,8 +69,6 @@ class PEImage { LPCSTR module, PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, - PIMAGE_THUNK_DATA bound_iat, - PIMAGE_THUNK_DATA unload_iat, PVOID cookie); // Callback to enumerate relocations. @@ -132,6 +130,9 @@ class PEImage { // Returns the exports directory. PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const; + // Returns the debug id (guid+age). + bool GetDebugId(LPGUID guid, LPDWORD age) const; + // Returns a given export entry. // Use: e = image.GetExportEntry(f); // Pre: 'f' is either a zero terminated string or ordinal @@ -145,7 +146,7 @@ class PEImage { // Pre: 'f' is either a zero terminated string or ordinal. // Post: if 'f' is a non-forwarded export from image, 'p' is // the exported function. If 'f' is a forwarded export - // then p is the special value 0xFFFFFFFF. In this case + // then p is the special value -1. In this case // RVAToAddr(*GetExportEntry) can be used to resolve // the string that describes the forward. FARPROC GetProcAddress(LPCSTR function_name) const; @@ -201,8 +202,6 @@ class PEImage { LPCSTR module_name, PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, - PIMAGE_THUNK_DATA bound_iat, - PIMAGE_THUNK_DATA unload_iat, PVOID cookie) const; // Enumerates PE relocation entries. @@ -235,19 +234,15 @@ class PEImageAsData : public PEImage { public: explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {} - virtual PVOID RVAToAddr(DWORD rva) const; + PVOID RVAToAddr(DWORD rva) const override; }; inline bool PEImage::IsOrdinal(LPCSTR name) { -#pragma warning(push) -#pragma warning(disable: 4311) - // This cast generates a warning because it is 32 bit specific. - return reinterpret_cast(name) <= 0xFFFF; -#pragma warning(pop) + return reinterpret_cast(name) <= 0xFFFF; } inline WORD PEImage::ToOrdinal(LPCSTR name) { - return reinterpret_cast(name); + return static_cast(reinterpret_cast(name)); } inline HMODULE PEImage::module() const { diff --git a/security/sandbox/chromium/base/win/scoped_handle.cc b/security/sandbox/chromium/base/win/scoped_handle.cc index 280302169209..9c21603a0e87 100644 --- a/security/sandbox/chromium/base/win/scoped_handle.cc +++ b/security/sandbox/chromium/base/win/scoped_handle.cc @@ -4,14 +4,22 @@ #include "base/win/scoped_handle.h" +#include + #include #include "base/debug/alias.h" #include "base/hash.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/macros.h" #include "base/synchronization/lock_impl.h" +extern "C" { +__declspec(dllexport) void* GetHandleVerifier(); +typedef void* (*GetHandleVerifierFn)(); +} + namespace { struct HandleHash { @@ -30,20 +38,9 @@ struct Info { }; typedef std::unordered_map HandleMap; -// g_lock protects g_handle_map and g_closing. +// g_lock protects the handle map and setting g_active_verifier. typedef base::internal::LockImpl NativeLock; base::LazyInstance::Leaky g_lock = LAZY_INSTANCE_INITIALIZER; -base::LazyInstance::Leaky g_handle_map = LAZY_INSTANCE_INITIALIZER; -bool g_closing = false; - -// g_verifier_enabled is not protected by g_lock because that would require -// using the lock (hence, synchornizing multiple threads) even when the -// verifier is not in use. Note that this variable is initialized to track all -// handles, and it should only move to the disabled state, and never back to -// enabled, because that would crash when seeing handles created while the -// verifier was disabled. This also implies that it is OK if the value change is -// not propagated immediately to all CPUs (as would happen with a lock). -bool g_verifier_enabled = true; bool CloseHandleWrapper(HANDLE handle) { if (!::CloseHandle(handle)) @@ -68,81 +65,183 @@ class AutoNativeLock { DISALLOW_COPY_AND_ASSIGN(AutoNativeLock); }; +// Implements the actual object that is verifying handles for this process. +// The active instance is shared across the module boundary but there is no +// way to delete this object from the wrong side of it (or any side, actually). +class ActiveVerifier { + public: + explicit ActiveVerifier(bool enabled) + : enabled_(enabled), closing_(false), lock_(g_lock.Pointer()) { + } + + // Retrieves the current verifier. + static ActiveVerifier* Get(); + + // The methods required by HandleTraits. They are virtual because we need to + // forward the call execution to another module, instead of letting the + // compiler call the version that is linked in the current module. + virtual bool CloseHandle(HANDLE handle); + virtual void StartTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2); + virtual void StopTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2); + virtual void Disable(); + virtual void OnHandleBeingClosed(HANDLE handle); + + private: + ~ActiveVerifier(); // Not implemented. + + static void InstallVerifier(); + + bool enabled_; + bool closing_; + NativeLock* lock_; + HandleMap map_; + DISALLOW_COPY_AND_ASSIGN(ActiveVerifier); +}; +ActiveVerifier* g_active_verifier = NULL; + +// static +ActiveVerifier* ActiveVerifier::Get() { + if (!g_active_verifier) + ActiveVerifier::InstallVerifier(); + + return g_active_verifier; +} + +// static +void ActiveVerifier::InstallVerifier() { +#if defined(COMPONENT_BUILD) + AutoNativeLock lock(g_lock.Get()); + g_active_verifier = new ActiveVerifier(true); +#else + // If you are reading this, wondering why your process seems deadlocked, take + // a look at your DllMain code and remove things that should not be done + // there, like doing whatever gave you that nice windows handle you are trying + // to store in a ScopedHandle. + HMODULE main_module = ::GetModuleHandle(NULL); + GetHandleVerifierFn get_handle_verifier = + reinterpret_cast(::GetProcAddress( + main_module, "GetHandleVerifier")); + + if (!get_handle_verifier) { + g_active_verifier = new ActiveVerifier(false); + return; + } + + ActiveVerifier* verifier = + reinterpret_cast(get_handle_verifier()); + + // This lock only protects against races in this module, which is fine. + AutoNativeLock lock(g_lock.Get()); + g_active_verifier = verifier ? verifier : new ActiveVerifier(true); +#endif +} + +bool ActiveVerifier::CloseHandle(HANDLE handle) { + if (!enabled_) + return CloseHandleWrapper(handle); + + AutoNativeLock lock(*lock_); + closing_ = true; + CloseHandleWrapper(handle); + closing_ = false; + + return true; +} + +void ActiveVerifier::StartTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2) { + if (!enabled_) + return; + + // Grab the thread id before the lock. + DWORD thread_id = GetCurrentThreadId(); + + AutoNativeLock lock(*lock_); + + Info handle_info = { owner, pc1, pc2, thread_id }; + std::pair item(handle, handle_info); + std::pair result = map_.insert(item); + if (!result.second) { + Info other = result.first->second; + base::debug::Alias(&other); + CHECK(false); + } +} + +void ActiveVerifier::StopTracking(HANDLE handle, const void* owner, + const void* pc1, const void* pc2) { + if (!enabled_) + return; + + AutoNativeLock lock(*lock_); + HandleMap::iterator i = map_.find(handle); + if (i == map_.end()) + CHECK(false); + + Info other = i->second; + if (other.owner != owner) { + base::debug::Alias(&other); + CHECK(false); + } + + map_.erase(i); +} + +void ActiveVerifier::Disable() { + enabled_ = false; +} + +void ActiveVerifier::OnHandleBeingClosed(HANDLE handle) { + if (!enabled_) + return; + + AutoNativeLock lock(*lock_); + if (closing_) + return; + + HandleMap::iterator i = map_.find(handle); + if (i == map_.end()) + return; + + Info other = i->second; + base::debug::Alias(&other); + CHECK(false); +} + } // namespace +void* GetHandleVerifier() { + return g_active_verifier; +} + namespace base { namespace win { // Static. bool HandleTraits::CloseHandle(HANDLE handle) { - if (!g_verifier_enabled) - return CloseHandleWrapper(handle); - - AutoNativeLock lock(g_lock.Get()); - g_closing = true; - CloseHandleWrapper(handle); - g_closing = false; - - return true; + return ActiveVerifier::Get()->CloseHandle(handle); } // Static. void VerifierTraits::StartTracking(HANDLE handle, const void* owner, const void* pc1, const void* pc2) { - if (!g_verifier_enabled) - return; - - // Grab the thread id before the lock. - DWORD thread_id = GetCurrentThreadId(); - - AutoNativeLock lock(g_lock.Get()); - - Info handle_info = { owner, pc1, pc2, thread_id }; - std::pair item(handle, handle_info); - std::pair result = g_handle_map.Get().insert(item); - if (!result.second) { - Info other = result.first->second; - debug::Alias(&other); - CHECK(false); - } + return ActiveVerifier::Get()->StartTracking(handle, owner, pc1, pc2); } // Static. void VerifierTraits::StopTracking(HANDLE handle, const void* owner, const void* pc1, const void* pc2) { - if (!g_verifier_enabled) - return; - - AutoNativeLock lock(g_lock.Get()); - HandleMap::iterator i = g_handle_map.Get().find(handle); - if (i == g_handle_map.Get().end()) - CHECK(false); - - Info other = i->second; - if (other.owner != owner) { - debug::Alias(&other); - CHECK(false); - } - - g_handle_map.Get().erase(i); + return ActiveVerifier::Get()->StopTracking(handle, owner, pc1, pc2); } void DisableHandleVerifier() { - g_verifier_enabled = false; + return ActiveVerifier::Get()->Disable(); } void OnHandleBeingClosed(HANDLE handle) { - AutoNativeLock lock(g_lock.Get()); - if (g_closing) - return; - - HandleMap::iterator i = g_handle_map.Get().find(handle); - if (i == g_handle_map.Get().end()) - return; - - Info other = i->second; - debug::Alias(&other); - CHECK(false); + return ActiveVerifier::Get()->OnHandleBeingClosed(handle); } } // namespace win diff --git a/security/sandbox/chromium/base/win/scoped_handle.h b/security/sandbox/chromium/base/win/scoped_handle.h index db24f4bb362a..404ab669385a 100644 --- a/security/sandbox/chromium/base/win/scoped_handle.h +++ b/security/sandbox/chromium/base/win/scoped_handle.h @@ -8,9 +8,9 @@ #include #include "base/base_export.h" -#include "base/basictypes.h" #include "base/location.h" #include "base/logging.h" +#include "base/macros.h" #include "base/move.h" // TODO(rvargas): remove this with the rest of the verifier. @@ -27,12 +27,16 @@ namespace win { // Generic wrapper for raw handles that takes care of closing handles // automatically. The class interface follows the style of -// the ScopedFILE class with one addition: +// the ScopedFILE class with two additions: // - IsValid() method can tolerate multiple invalid handle values such as NULL // and INVALID_HANDLE_VALUE (-1) for Win32 handles. +// - Set() (and the constructors and assignment operators that call it) +// preserve the Windows LastError code. This ensures that GetLastError() can +// be called after stashing a handle in a GenericScopedHandle object. Doing +// this explicitly is necessary because of bug 528394 and VC++ 2015. template class GenericScopedHandle { - MOVE_ONLY_TYPE_FOR_CPP_03(GenericScopedHandle, RValue) + MOVE_ONLY_TYPE_FOR_CPP_03(GenericScopedHandle) public: typedef typename Traits::Handle Handle; @@ -43,9 +47,9 @@ class GenericScopedHandle { Set(handle); } - // Move constructor for C++03 move emulation of this type. - GenericScopedHandle(RValue other) : handle_(Traits::NullHandle()) { - Set(other.object->Take()); + GenericScopedHandle(GenericScopedHandle&& other) + : handle_(Traits::NullHandle()) { + Set(other.Take()); } ~GenericScopedHandle() { @@ -56,16 +60,16 @@ class GenericScopedHandle { return Traits::IsHandleValid(handle_); } - // Move operator= for C++03 move emulation of this type. - GenericScopedHandle& operator=(RValue other) { - if (this != other.object) { - Set(other.object->Take()); - } + GenericScopedHandle& operator=(GenericScopedHandle&& other) { + DCHECK_NE(this, &other); + Set(other.Take()); return *this; } void Set(Handle handle) { if (handle_ != handle) { + // Preserve old LastError to avoid bug 528394. + auto last_error = ::GetLastError(); Close(); if (Traits::IsHandleValid(handle)) { @@ -73,6 +77,7 @@ class GenericScopedHandle { Verifier::StartTracking(handle, this, BASE_WIN_GET_CALLER, tracked_objects::GetProgramCounter()); } + ::SetLastError(last_error); } } @@ -174,4 +179,4 @@ void BASE_EXPORT OnHandleBeingClosed(HANDLE handle); } // namespace win } // namespace base -#endif // BASE_SCOPED_HANDLE_WIN_H_ +#endif // BASE_WIN_SCOPED_HANDLE_H_ diff --git a/security/sandbox/chromium/base/win/scoped_process_information.cc b/security/sandbox/chromium/base/win/scoped_process_information.cc index bb2463774a8a..634a538eece5 100644 --- a/security/sandbox/chromium/base/win/scoped_process_information.cc +++ b/security/sandbox/chromium/base/win/scoped_process_information.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/win/scoped_handle.h" +#include "base/win/windows_version.h" namespace base { namespace win { @@ -20,11 +21,49 @@ bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) { return true; HANDLE temp = NULL; - if (!::DuplicateHandle(::GetCurrentProcess(), source, - ::GetCurrentProcess(), &temp, 0, FALSE, - DUPLICATE_SAME_ACCESS)) { - DPLOG(ERROR) << "Failed to duplicate a handle."; - return false; + + // TODO(shrikant): Remove following code as soon as we gather some + // information regarding AppContainer related DuplicateHandle failures that + // only seem to happen on certain machine and only random launches (normally + // renderer launches seem to succeed even on those machines.) + if (base::win::GetVersion() == base::win::VERSION_WIN8 || + base::win::GetVersion() == base::win::VERSION_WIN8_1) { + typedef LONG (WINAPI *NtDuplicateObject)( + IN HANDLE SourceProcess, + IN HANDLE SourceHandle, + IN HANDLE TargetProcess, + OUT PHANDLE TargetHandle, + IN ACCESS_MASK DesiredAccess, + IN ULONG Attributes, + IN ULONG Options); + + typedef ULONG (WINAPI *RtlNtStatusToDosError)(IN LONG Status); + + NtDuplicateObject nt_duplicate_object = + reinterpret_cast(::GetProcAddress( + GetModuleHandle(L"ntdll.dll"), "NtDuplicateObject")); + if (nt_duplicate_object != NULL) { + LONG status = nt_duplicate_object(::GetCurrentProcess(), source, + ::GetCurrentProcess(), &temp, + 0, FALSE, DUPLICATE_SAME_ACCESS); + if (status < 0) { + DPLOG(ERROR) << "Failed to duplicate a handle."; + RtlNtStatusToDosError ntstatus_to_doserror = + reinterpret_cast(::GetProcAddress( + GetModuleHandle(L"ntdll.dll"), "RtlNtStatusToDosError")); + if (ntstatus_to_doserror != NULL) { + ::SetLastError(ntstatus_to_doserror(status)); + } + return false; + } + } + } else { + if (!::DuplicateHandle(::GetCurrentProcess(), source, + ::GetCurrentProcess(), &temp, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + DPLOG(ERROR) << "Failed to duplicate a handle."; + return false; + } } target->Set(temp); return true; diff --git a/security/sandbox/chromium/base/win/scoped_process_information.h b/security/sandbox/chromium/base/win/scoped_process_information.h index 2e240544122f..01df861f0e74 100644 --- a/security/sandbox/chromium/base/win/scoped_process_information.h +++ b/security/sandbox/chromium/base/win/scoped_process_information.h @@ -7,8 +7,8 @@ #include -#include "base/basictypes.h" #include "base/base_export.h" +#include "base/macros.h" #include "base/win/scoped_handle.h" namespace base { diff --git a/security/sandbox/chromium/base/win/startup_information.h b/security/sandbox/chromium/base/win/startup_information.h index 7cef81f2c8ff..5b777baefe13 100644 --- a/security/sandbox/chromium/base/win/startup_information.h +++ b/security/sandbox/chromium/base/win/startup_information.h @@ -6,9 +6,10 @@ #define BASE_WIN_STARTUP_INFORMATION_H_ #include +#include #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" namespace base { namespace win { @@ -24,12 +25,14 @@ class BASE_EXPORT StartupInformation { bool InitializeProcThreadAttributeList(DWORD attribute_count); // Sets one entry in the initialized attribute list. + // |value| needs to live at least as long as the StartupInformation object + // this is called on. bool UpdateProcThreadAttribute(DWORD_PTR attribute, void* value, size_t size); LPSTARTUPINFOW startup_info() { return &startup_info_.StartupInfo; } - const LPSTARTUPINFOW startup_info() const { + LPSTARTUPINFOW startup_info() const { return const_cast(&startup_info_.StartupInfo); } @@ -45,5 +48,4 @@ class BASE_EXPORT StartupInformation { } // namespace win } // namespace base -#endif // BASE_WIN_SCOPED_STARTUP_INFO_EX_H_ - +#endif // BASE_WIN_STARTUP_INFORMATION_H_ diff --git a/security/sandbox/chromium/base/win/windows_version.cc b/security/sandbox/chromium/base/win/windows_version.cc index fc2def39194e..eb3f4993d2cd 100644 --- a/security/sandbox/chromium/base/win/windows_version.cc +++ b/security/sandbox/chromium/base/win/windows_version.cc @@ -6,17 +6,80 @@ #include +#include "base/file_version_info_win.h" +#include "base/files/file_path.h" #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "base/win/registry.h" namespace { typedef BOOL (WINAPI *GetProductInfoPtr)(DWORD, DWORD, DWORD, DWORD, PDWORD); -} +} // namespace namespace base { namespace win { +namespace { + +// Helper to map a major.minor.x.build version (e.g. 6.1) to a Windows release. +Version MajorMinorBuildToVersion(int major, int minor, int build) { + if ((major == 5) && (minor > 0)) { + // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003. + return (minor == 1) ? VERSION_XP : VERSION_SERVER_2003; + } else if (major == 6) { + switch (minor) { + case 0: + // Treat Windows Server 2008 the same as Windows Vista. + return VERSION_VISTA; + case 1: + // Treat Windows Server 2008 R2 the same as Windows 7. + return VERSION_WIN7; + case 2: + // Treat Windows Server 2012 the same as Windows 8. + return VERSION_WIN8; + default: + DCHECK_EQ(minor, 3); + return VERSION_WIN8_1; + } + } else if (major == 10) { + if (build < 10586) { + return VERSION_WIN10; + } else { + return VERSION_WIN10_TH2; + } + } else if (major > 6) { + NOTREACHED(); + return VERSION_WIN_LAST; + } + + return VERSION_PRE_XP; +} + +// Retrieve a version from kernel32. This is useful because when running in +// compatibility mode for a down-level version of the OS, the file version of +// kernel32 will still be the "real" version. +Version GetVersionFromKernel32() { + scoped_ptr file_version_info( + static_cast( + FileVersionInfoWin::CreateFileVersionInfo( + base::FilePath(FILE_PATH_LITERAL("kernel32.dll"))))); + if (file_version_info) { + const int major = + HIWORD(file_version_info->fixed_file_info()->dwFileVersionMS); + const int minor = + LOWORD(file_version_info->fixed_file_info()->dwFileVersionMS); + const int build = + HIWORD(file_version_info->fixed_file_info()->dwFileVersionLS); + return MajorMinorBuildToVersion(major, minor, build); + } + + NOTREACHED(); + return VERSION_WIN_LAST; +} + +} // namespace + // static OSInfo* OSInfo::GetInstance() { // Note: we don't use the Singleton class because it depends on AtExitManager, @@ -35,6 +98,8 @@ OSInfo* OSInfo::GetInstance() { OSInfo::OSInfo() : version_(VERSION_PRE_XP), + kernel32_version_(VERSION_PRE_XP), + got_kernel32_version_(false), architecture_(OTHER_ARCHITECTURE), wow64_status_(GetWOW64StatusForProcess(GetCurrentProcess())) { OSVERSIONINFOEX version_info = { sizeof version_info }; @@ -42,38 +107,12 @@ OSInfo::OSInfo() version_number_.major = version_info.dwMajorVersion; version_number_.minor = version_info.dwMinorVersion; version_number_.build = version_info.dwBuildNumber; - if ((version_number_.major == 5) && (version_number_.minor > 0)) { - // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003. - version_ = (version_number_.minor == 1) ? VERSION_XP : VERSION_SERVER_2003; - } else if (version_number_.major == 6) { - switch (version_number_.minor) { - case 0: - // Treat Windows Server 2008 the same as Windows Vista. - version_ = VERSION_VISTA; - break; - case 1: - // Treat Windows Server 2008 R2 the same as Windows 7. - version_ = VERSION_WIN7; - break; - case 2: - // Treat Windows Server 2012 the same as Windows 8. - version_ = VERSION_WIN8; - break; - default: - DCHECK_EQ(version_number_.minor, 3); - version_ = VERSION_WIN8_1; - break; - } - } else if (version_number_.major == 10) { - version_ = VERSION_WIN10; - } else if (version_number_.major > 6) { - NOTREACHED(); - version_ = VERSION_WIN_LAST; - } + version_ = MajorMinorBuildToVersion( + version_number_.major, version_number_.minor, version_number_.build); service_pack_.major = version_info.wServicePackMajor; service_pack_.minor = version_info.wServicePackMinor; - SYSTEM_INFO system_info = { 0 }; + SYSTEM_INFO system_info = {}; ::GetNativeSystemInfo(&system_info); switch (system_info.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_INTEL: architecture_ = X86_ARCHITECTURE; break; @@ -145,6 +184,14 @@ OSInfo::OSInfo() OSInfo::~OSInfo() { } +Version OSInfo::Kernel32Version() const { + if (!got_kernel32_version_) { + kernel32_version_ = GetVersionFromKernel32(); + got_kernel32_version_ = true; + } + return kernel32_version_; +} + std::string OSInfo::processor_model_name() { if (processor_model_name_.empty()) { const wchar_t kProcessorNameString[] = diff --git a/security/sandbox/chromium/base/win/windows_version.h b/security/sandbox/chromium/base/win/windows_version.h index a52e64e07425..7bc8b6fe560f 100644 --- a/security/sandbox/chromium/base/win/windows_version.h +++ b/security/sandbox/chromium/base/win/windows_version.h @@ -5,10 +5,12 @@ #ifndef BASE_WIN_WINDOWS_VERSION_H_ #define BASE_WIN_WINDOWS_VERSION_H_ +#include + #include #include "base/base_export.h" -#include "base/basictypes.h" +#include "base/macros.h" typedef void* HANDLE; @@ -19,16 +21,20 @@ namespace win { // syntactic sugar reasons; see the declaration of GetVersion() below. // NOTE: Keep these in order so callers can do things like // "if (base::win::GetVersion() >= base::win::VERSION_VISTA) ...". +// +// This enum is used in metrics histograms, so they shouldn't be reordered or +// removed. New values can be added before VERSION_WIN_LAST. enum Version { VERSION_PRE_XP = 0, // Not supported. - VERSION_XP, - VERSION_SERVER_2003, // Also includes XP Pro x64 and Server 2003 R2. - VERSION_VISTA, // Also includes Windows Server 2008. - VERSION_WIN7, // Also includes Windows Server 2008 R2. - VERSION_WIN8, // Also includes Windows Server 2012. - VERSION_WIN8_1, // Also includes Windows Server 2012 R2. - VERSION_WIN10, // Also includes Windows 10 Server. - VERSION_WIN_LAST, // Indicates error condition. + VERSION_XP = 1, + VERSION_SERVER_2003 = 2, // Also includes XP Pro x64 and Server 2003 R2. + VERSION_VISTA = 3, // Also includes Windows Server 2008. + VERSION_WIN7 = 4, // Also includes Windows Server 2008 R2. + VERSION_WIN8 = 5, // Also includes Windows Server 2012. + VERSION_WIN8_1 = 6, // Also includes Windows Server 2012 R2. + VERSION_WIN10 = 7, // Also includes Windows 10 Server. + VERSION_WIN10_TH2 = 8, // Threshold 2: Version 1511, Build 10586. + VERSION_WIN_LAST, // Indicates error condition. }; // A rough bucketing of the available types of versions of Windows. This is used @@ -83,6 +89,7 @@ class BASE_EXPORT OSInfo { static OSInfo* GetInstance(); Version version() const { return version_; } + Version Kernel32Version() const; // The next two functions return arrays of values, [major, minor(, build)]. VersionNumber version_number() const { return version_number_; } VersionType version_type() const { return version_type_; } @@ -102,6 +109,8 @@ class BASE_EXPORT OSInfo { ~OSInfo(); Version version_; + mutable Version kernel32_version_; + mutable bool got_kernel32_version_; VersionNumber version_number_; VersionType version_type_; ServicePack service_pack_; diff --git a/security/sandbox/chromium/build/build_config.h b/security/sandbox/chromium/build/build_config.h index b07660db412f..d8c3db6ed781 100644 --- a/security/sandbox/chromium/build/build_config.h +++ b/security/sandbox/chromium/build/build_config.h @@ -61,8 +61,8 @@ #error Please add support for your platform in build/build_config.h #endif -#if defined(USE_OPENSSL) && defined(USE_NSS) -#error Cannot use both OpenSSL and NSS +#if defined(USE_OPENSSL_CERTS) && defined(USE_NSS_CERTS) +#error Cannot use both OpenSSL and NSS for certificates #endif // For access to standard BSD features, use OS_BSD instead of a diff --git a/security/sandbox/chromium/build/buildflag.h b/security/sandbox/chromium/build/buildflag.h new file mode 100644 index 000000000000..283f5bce41c2 --- /dev/null +++ b/security/sandbox/chromium/build/buildflag.h @@ -0,0 +1,47 @@ +// 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 BUILD_BUILDFLAG_H_ +#define BUILD_BUILDFLAG_H_ + +// These macros un-mangle the names of the build flags in a way that looks +// natural, and gives errors if the flag is not defined. Normally in the +// preprocessor it's easy to make mistakes that interpret "you haven't done +// the setup to know what the flag is" as "flag is off". Normally you would +// include the generated header rather than include this file directly. +// +// This is for use with generated headers. See build/build_header.gni. + +// This dance of two macros does a concatenation of two preprocessor args using +// ## doubly indirectly because using ## directly prevents macros in that +// parameter from being expanded. +#define BUILDFLAG_CAT_INDIRECT(a, b) a ## b +#define BUILDFLAG_CAT(a, b) BUILDFLAG_CAT_INDIRECT(a, b) + +// Accessor for build flags. +// +// To test for a value, if the build file specifies: +// +// ENABLE_FOO=true +// +// Then you would check at build-time in source code with: +// +// #include "foo_flags.h" // The header the build file specified. +// +// #if BUILDFLAG(ENABLE_FOO) +// ... +// #endif +// +// There will no #define called ENABLE_FOO so if you accidentally test for +// whether that is defined, it will always be negative. You can also use +// the value in expressions: +// +// const char kSpamServerName[] = BUILDFLAG(SPAM_SERVER_NAME); +// +// Because the flag is accessed as a preprocessor macro with (), an error +// will be thrown if the proper header defining the internal flag value has +// not been included. +#define BUILDFLAG(flag) (BUILDFLAG_CAT(BUILDFLAG_INTERNAL_, flag)()) + +#endif // BUILD_BUILDFLAG_H_ diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc index b80f0b30540d..3330c4774052 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc @@ -4,125 +4,74 @@ #include "sandbox/linux/bpf_dsl/bpf_dsl.h" +#include +#include + #include #include "base/logging.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "sandbox/linux/bpf_dsl/bpf_dsl_impl.h" +#include "sandbox/linux/bpf_dsl/errorcode.h" #include "sandbox/linux/bpf_dsl/policy_compiler.h" -#include "sandbox/linux/seccomp-bpf/errorcode.h" -#include "mozilla/Attributes.h" +#include "sandbox/linux/system_headers/linux_seccomp.h" namespace sandbox { namespace bpf_dsl { namespace { -class AllowResultExprImpl : public internal::ResultExprImpl { +class ReturnResultExprImpl : public internal::ResultExprImpl { public: - AllowResultExprImpl() {} + explicit ReturnResultExprImpl(uint32_t ret) : ret_(ret) {} - ErrorCode Compile(PolicyCompiler* pc) const override { - return ErrorCode(ErrorCode::ERR_ALLOWED); + CodeGen::Node Compile(PolicyCompiler* pc) const override { + return pc->Return(ret_); + } + + bool IsAllow() const override { return IsAction(SECCOMP_RET_ALLOW); } + + bool IsDeny() const override { + return IsAction(SECCOMP_RET_ERRNO) || IsAction(SECCOMP_RET_KILL); } private: - ~AllowResultExprImpl() override {} + ~ReturnResultExprImpl() 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); + bool IsAction(uint32_t action) const { + return (ret_ & SECCOMP_RET_ACTION) == action; } - ErrorCode Compile(PolicyCompiler* pc) const override { - return pc->Error(err_); - } + uint32_t ret_; - private: - ~ErrorResultExprImpl() override {} - - int err_; - - DISALLOW_COPY_AND_ASSIGN(ErrorResultExprImpl); -}; - -class KillResultExprImpl : public internal::ResultExprImpl { - public: - explicit KillResultExprImpl(const char* msg) : msg_(msg) { DCHECK(msg_); } - - ErrorCode Compile(PolicyCompiler* pc) const override { - return pc->Kill(msg_); - } - - private: - ~KillResultExprImpl() override {} - - const char* msg_; - - DISALLOW_COPY_AND_ASSIGN(KillResultExprImpl); -}; - -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); + DISALLOW_COPY_AND_ASSIGN(ReturnResultExprImpl); }; class TrapResultExprImpl : public internal::ResultExprImpl { public: - TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg) - : func_(func), arg_(arg) { + 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_); + CodeGen::Node 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 UnsafeTrapResultExprImpl : public internal::ResultExprImpl { - public: - UnsafeTrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg) - : func_(func), arg_(arg) { - DCHECK(func_); - } - - ErrorCode Compile(PolicyCompiler* pc) const override { - return pc->UnsafeTrap(func_, arg_); - } - - bool HasUnsafeTraps() const override { return true; } - - private: - ~UnsafeTrapResultExprImpl() override {} - - TrapRegistry::TrapFnc func_; - const void* arg_; - - DISALLOW_COPY_AND_ASSIGN(UnsafeTrapResultExprImpl); -}; - class IfThenResultExprImpl : public internal::ResultExprImpl { public: IfThenResultExprImpl(const BoolExpr& cond, @@ -130,9 +79,12 @@ class IfThenResultExprImpl : public internal::ResultExprImpl { 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)); + CodeGen::Node Compile(PolicyCompiler* pc) const override { + // We compile the "then" and "else" expressions in separate statements so + // they have a defined sequencing. See https://crbug.com/529480. + CodeGen::Node then_node = then_result_->Compile(pc); + CodeGen::Node else_node = else_result_->Compile(pc); + return cond_->Compile(pc, then_node, else_node); } bool HasUnsafeTraps() const override { @@ -153,10 +105,10 @@ 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; + CodeGen::Node Compile(PolicyCompiler* pc, + CodeGen::Node then_node, + CodeGen::Node else_node) const override { + return value_ ? then_node : else_node; } private: @@ -167,40 +119,39 @@ class ConstBoolExprImpl : public internal::BoolExprImpl { DISALLOW_COPY_AND_ASSIGN(ConstBoolExprImpl); }; -class PrimitiveBoolExprImpl : public internal::BoolExprImpl { +class MaskedEqualBoolExprImpl : 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) {} + MaskedEqualBoolExprImpl(int argno, + size_t width, + uint64_t mask, + uint64_t value) + : argno_(argno), width_(width), 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); + CodeGen::Node Compile(PolicyCompiler* pc, + CodeGen::Node then_node, + CodeGen::Node else_node) const override { + return pc->MaskedEqual(argno_, width_, mask_, value_, then_node, else_node); } private: - ~PrimitiveBoolExprImpl() override {} + ~MaskedEqualBoolExprImpl() override {} int argno_; - ErrorCode::ArgType is_32bit_; + size_t width_; uint64_t mask_; uint64_t value_; - DISALLOW_COPY_AND_ASSIGN(PrimitiveBoolExprImpl); + DISALLOW_COPY_AND_ASSIGN(MaskedEqualBoolExprImpl); }; 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); + CodeGen::Node Compile(PolicyCompiler* pc, + CodeGen::Node then_node, + CodeGen::Node else_node) const override { + return cond_->Compile(pc, else_node, then_node); } private: @@ -216,10 +167,11 @@ class AndBoolExprImpl : public internal::BoolExprImpl { 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); + CodeGen::Node Compile(PolicyCompiler* pc, + CodeGen::Node then_node, + CodeGen::Node else_node) const override { + return lhs_->Compile(pc, rhs_->Compile(pc, then_node, else_node), + else_node); } private: @@ -236,10 +188,11 @@ class OrBoolExprImpl : public internal::BoolExprImpl { 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)); + CodeGen::Node Compile(PolicyCompiler* pc, + CodeGen::Node then_node, + CodeGen::Node else_node) const override { + return lhs_->Compile(pc, then_node, + rhs_->Compile(pc, then_node, else_node)); } private: @@ -259,6 +212,14 @@ 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: @@ -272,54 +233,63 @@ uint64_t DefaultMask(size_t size) { } BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) { + // If this is changed, update Arg::EqualTo's static_cast rules + // accordingly. 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)); + return BoolExpr(new const MaskedEqualBoolExprImpl(num, size, mask, val)); } } // namespace internal ResultExpr Allow() { - return ResultExpr(new const AllowResultExprImpl()); + return ResultExpr(new const ReturnResultExprImpl(SECCOMP_RET_ALLOW)); } ResultExpr Error(int err) { - return ResultExpr(new const ErrorResultExprImpl(err)); + CHECK(err >= ErrorCode::ERR_MIN_ERRNO && err <= ErrorCode::ERR_MAX_ERRNO); + return ResultExpr(new const ReturnResultExprImpl(SECCOMP_RET_ERRNO + err)); } -ResultExpr Kill(const char* msg) { - return ResultExpr(new const KillResultExprImpl(msg)); +ResultExpr Kill() { + return ResultExpr(new const ReturnResultExprImpl(SECCOMP_RET_KILL)); } ResultExpr Trace(uint16_t aux) { - return ResultExpr(new const TraceResultExprImpl(aux)); + return ResultExpr(new const ReturnResultExprImpl(SECCOMP_RET_TRACE + aux)); } ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) { - return ResultExpr(new const TrapResultExprImpl(trap_func, aux)); + return ResultExpr( + new const TrapResultExprImpl(trap_func, aux, true /* safe */)); } ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) { - return ResultExpr(new const UnsafeTrapResultExprImpl(trap_func, 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) { +BoolExpr Not(const BoolExpr& cond) { return BoolExpr(new const NegateBoolExprImpl(cond)); } -BoolExpr operator&&(const BoolExpr& lhs, const BoolExpr& rhs) { +BoolExpr AllOf() { + return BoolConst(true); +} + +BoolExpr AllOf(const BoolExpr& lhs, const BoolExpr& rhs) { return BoolExpr(new const AndBoolExprImpl(lhs, rhs)); } -BoolExpr operator||(const BoolExpr& lhs, const BoolExpr& rhs) { +BoolExpr AnyOf() { + return BoolConst(false); +} + +BoolExpr AnyOf(const BoolExpr& lhs, const BoolExpr& rhs) { return BoolExpr(new const OrBoolExprImpl(lhs, rhs)); } diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h index 63b095deb674..ffd20ff42d43 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h @@ -5,6 +5,7 @@ #ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_ #define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_ +#include #include #include @@ -33,15 +34,17 @@ // class SillyPolicy : public Policy { // public: // SillyPolicy() {} -// virtual ~SillyPolicy() {} -// virtual ResultExpr EvaluateSyscall(int sysno) const override { +// ~SillyPolicy() override {} +// ResultExpr EvaluateSyscall(int sysno) const override { // if (sysno == __NR_fcntl) { // Arg fd(0), cmd(1); // Arg flags(2); // const uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK; -// return If(fd == 0 && cmd == F_SETFL && (flags & ~kGoodFlags) == 0, +// return If(AllOf(fd == 0, +// cmd == F_SETFL, +// (flags & ~kGoodFlags) == 0), // Allow()) -// .ElseIf(cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC, +// .ElseIf(AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC), // Error(EMFILE)) // .Else(Trap(SetFlagHandler, NULL)); // } else { @@ -55,11 +58,11 @@ // // More generally, the DSL currently supports the following grammar: // -// result = Allow() | Error(errno) | Kill(msg) | Trace(aux) +// result = Allow() | Error(errno) | Kill() | 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 +// bool = BoolConst(boolean) | Not(bool) | AllOf(bool...) | AnyOf(bool...) // | arg == val | arg != val // arg = Arg(num) | arg & mask // @@ -89,8 +92,8 @@ SANDBOX_EXPORT ResultExpr Allow(); // 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); +// Kill specifies a result to kill the process (task) immediately. +SANDBOX_EXPORT ResultExpr Kill(); // 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. @@ -117,11 +120,22 @@ SANDBOX_EXPORT ResultExpr // 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); +// Not returns a BoolExpr representing the logical negation of |cond|. +SANDBOX_EXPORT BoolExpr Not(const BoolExpr& cond); + +// AllOf returns a BoolExpr representing the logical conjunction ("and") +// of zero or more BoolExprs. +SANDBOX_EXPORT BoolExpr AllOf(); +SANDBOX_EXPORT BoolExpr AllOf(const BoolExpr& lhs, const BoolExpr& rhs); +template +SANDBOX_EXPORT BoolExpr AllOf(const BoolExpr& first, const Rest&... rest); + +// AnyOf returns a BoolExpr representing the logical disjunction ("or") +// of zero or more BoolExprs. +SANDBOX_EXPORT BoolExpr AnyOf(); +SANDBOX_EXPORT BoolExpr AnyOf(const BoolExpr& lhs, const BoolExpr& rhs); +template +SANDBOX_EXPORT BoolExpr AnyOf(const BoolExpr& first, const Rest&... rest); template class SANDBOX_EXPORT Arg { @@ -144,7 +158,7 @@ class SANDBOX_EXPORT Arg { // 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); } + friend BoolExpr operator!=(const Arg& lhs, T rhs) { return Not(lhs == rhs); } private: Arg(int num, uint64_t mask) : num_(num), mask_(mask) {} @@ -199,15 +213,16 @@ class SANDBOX_EXPORT Caser { ~Caser() {} // Case adds a single-value "case" clause to the switch. - Caser Case(T value, ResultExpr result) const; + Caser Case(T value, const 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 Cases(const std::vector& values, ResultExpr result) const; + template + Caser CasesImpl(const ResultExpr& result, const Values&... values) const; // Terminate the switch with a "default" clause. - ResultExpr Default(ResultExpr result) const; + ResultExpr Default(const ResultExpr& result) const; private: Caser(const Arg& arg, Elser elser) : arg_(arg), elser_(elser) {} @@ -226,17 +241,10 @@ class SANDBOX_EXPORT Caser { // use like: // Switch(arg).CASES((3, 5, 7), result)...; #define SANDBOX_BPF_DSL_CASES(values, result) \ - Cases(SANDBOX_BPF_DSL_CASES_HELPER values, result) + CasesImpl(result, SANDBOX_BPF_DSL_CASES_HELPER values) -// 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)); \ - }) +// Helper macro to strip parentheses. +#define SANDBOX_BPF_DSL_CASES_HELPER(...) __VA_ARGS__ // ===================================================================== // Official API ends here. @@ -248,9 +256,9 @@ namespace internal { // BoolExpr is defined in bpf_dsl, since it's merely a typedef for // scoped_refptr, argument-dependent lookup only // searches the "internal" nested namespace. -using bpf_dsl::operator!; -using bpf_dsl::operator||; -using bpf_dsl::operator&&; +using bpf_dsl::Not; +using bpf_dsl::AllOf; +using bpf_dsl::AnyOf; // Returns a boolean expression that represents whether system call // argument |num| of size |size| is equal to |val|, when masked @@ -278,6 +286,10 @@ Arg::Arg(int num) // see http://www.parashift.com/c++-faq-lite/template-friends.html. template BoolExpr Arg::EqualTo(T val) const { + if (sizeof(T) == 4) { + // Prevent sign-extension of negative int32_t values. + return internal::ArgEq(num_, sizeof(T), mask_, static_cast(val)); + } return internal::ArgEq(num_, sizeof(T), mask_, static_cast(val)); } @@ -287,30 +299,36 @@ SANDBOX_EXPORT Caser Switch(const Arg& arg) { } template -Caser Caser::Case(T value, ResultExpr result) const { +Caser Caser::Case(T value, const ResultExpr& result) const { return SANDBOX_BPF_DSL_CASES((value), result); } template -Caser Caser::Cases(const std::vector& values, - ResultExpr result) const { +template +Caser Caser::CasesImpl(const ResultExpr& result, + const Values&... values) 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::const_iterator Iter; - BoolExpr test = BoolConst(false); - for (Iter i = values.begin(), end = values.end(); i != end; ++i) { - test = test || (arg_ == *i); - } - return Caser(arg_, elser_.ElseIf(test, result)); + return Caser(arg_, elser_.ElseIf(AnyOf((arg_ == values)...), result)); } template -ResultExpr Caser::Default(ResultExpr result) const { +ResultExpr Caser::Default(const ResultExpr& result) const { return elser_.Else(result); } +template +BoolExpr AllOf(const BoolExpr& first, const Rest&... rest) { + return AllOf(first, AllOf(rest...)); +} + +template +BoolExpr AnyOf(const BoolExpr& first, const Rest&... rest) { + return AnyOf(first, AnyOf(rest...)); +} + } // namespace bpf_dsl } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_impl.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_impl.h index 48b91692b662..0064f8a7a217 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_impl.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_impl.h @@ -7,12 +7,12 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "sandbox/linux/bpf_dsl/codegen.h" #include "sandbox/sandbox_export.h" namespace sandbox { -class ErrorCode; - namespace bpf_dsl { +class ErrorCode; class PolicyCompiler; namespace internal { @@ -20,12 +20,12 @@ namespace internal { // Internal interface implemented by BoolExpr implementations. class BoolExprImpl : public base::RefCounted { public: - // Compile uses |pc| to construct an ErrorCode that conditionally continues - // to either |true_ec| or |false_ec|, depending on whether the represented + // Compile uses |pc| to emit a CodeGen::Node that conditionally continues + // to either |then_node| or |false_node|, depending on whether the represented // boolean expression is true or false. - virtual ErrorCode Compile(PolicyCompiler* pc, - ErrorCode true_ec, - ErrorCode false_ec) const = 0; + virtual CodeGen::Node Compile(PolicyCompiler* pc, + CodeGen::Node then_node, + CodeGen::Node else_node) const = 0; protected: BoolExprImpl() {} @@ -39,14 +39,20 @@ class BoolExprImpl : public base::RefCounted { // Internal interface implemented by ResultExpr implementations. class ResultExprImpl : public base::RefCounted { public: - // Compile uses |pc| to construct an ErrorCode analogous to the represented - // result expression. - virtual ErrorCode Compile(PolicyCompiler* pc) const = 0; + // Compile uses |pc| to emit a CodeGen::Node that executes the + // represented result expression. + virtual CodeGen::Node 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() {} diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.cc new file mode 100644 index 000000000000..647f55aa8ab0 --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.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 "sandbox/linux/bpf_dsl/codegen.h" + +#include +#include + +#include +#include + +#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::max(); + +const CodeGen::Node CodeGen::kNullNode; + +CodeGen::CodeGen() : program_(), equivalent_(), memos_() { +} + +CodeGen::~CodeGen() { +} + +CodeGen::Program CodeGen::Compile(CodeGen::Node head) { + return Program(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(BPF_MAXINSNS)); + CHECK_EQ(program_.size(), equivalent_.size()); + + Node res = program_.size(); + program_.push_back(sock_filter{ + code, static_cast(jt), static_cast(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/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.h new file mode 100644 index 000000000000..03c3b236ef64 --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.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_BPF_DSL_CODEGEN_H__ +#define SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ + +#include +#include + +#include +#include + +#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); +// const struct sock_fprog prog = { +// static_cast(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 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. + Program Compile(Node head); + + private: + using MemoKey = base::Tuple; + 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 equivalent_; + + std::map memos_; + + DISALLOW_COPY_AND_ASSIGN(CodeGen); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_BPF_DSL_CODEGEN_H__ diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/cons.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/cons.h index fa47c140ff50..be050f77817e 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/cons.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/cons.h @@ -5,6 +5,7 @@ #ifndef SANDBOX_LINUX_BPF_DSL_CONS_H_ #define SANDBOX_LINUX_BPF_DSL_CONS_H_ +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "sandbox/sandbox_export.h" diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc index ddbcd0ea9b00..2edf592f68fa 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc @@ -4,101 +4,155 @@ #include "sandbox/linux/bpf_dsl/dump_bpf.h" +#include +#include +#include #include +#include + +#include "base/strings/stringprintf.h" +#include "sandbox/linux/bpf_dsl/codegen.h" +#include "sandbox/linux/bpf_dsl/seccomp_macros.h" #include "sandbox/linux/bpf_dsl/trap_registry.h" -#include "sandbox/linux/seccomp-bpf/codegen.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.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: - fprintf(stderr, BPF_OP(iter->code) == BPF_NEG - ? "A := -A\n" : "A := A %s 0x%x\n", - BPF_OP(iter->code) == BPF_ADD ? "+" : - BPF_OP(iter->code) == BPF_SUB ? "-" : - BPF_OP(iter->code) == BPF_MUL ? "*" : - BPF_OP(iter->code) == BPF_DIV ? "/" : - BPF_OP(iter->code) == BPF_MOD ? "%" : - BPF_OP(iter->code) == BPF_OR ? "|" : - BPF_OP(iter->code) == BPF_XOR ? "^" : - BPF_OP(iter->code) == BPF_AND ? "&" : - BPF_OP(iter->code) == BPF_LSH ? "<<" : - BPF_OP(iter->code) == BPF_RSH ? ">>" : "???", - (int)iter->k); - break; - default: - fprintf(stderr, "???\n"); - break; - } +namespace { + +const char* AluOpToken(uint32_t code) { + switch (BPF_OP(code)) { + case BPF_ADD: + return "+"; + case BPF_SUB: + return "-"; + case BPF_MUL: + return "*"; + case BPF_DIV: + return "/"; + case BPF_MOD: + return "%"; + case BPF_OR: + return "|"; + case BPF_XOR: + return "^"; + case BPF_AND: + return "&"; + case BPF_LSH: + return "<<"; + case BPF_RSH: + return ">>"; + default: + return "???"; } - return; +} + +const char* JmpOpToken(uint32_t code) { + switch (BPF_OP(code)) { + case BPF_JSET: + return "&"; + case BPF_JEQ: + return "=="; + case BPF_JGE: + return ">="; + default: + return "???"; + } +} + +const char* DataOffsetName(size_t off) { + switch (off) { + case SECCOMP_NR_IDX: + return "System call number"; + case SECCOMP_ARCH_IDX: + return "Architecture"; + case SECCOMP_IP_LSB_IDX: + return "Instruction pointer (LSB)"; + case SECCOMP_IP_MSB_IDX: + return "Instruction pointer (MSB)"; + default: + return "???"; + } +} + +void AppendInstruction(std::string* dst, size_t pc, const sock_filter& insn) { + base::StringAppendF(dst, "%3zu) ", pc); + switch (BPF_CLASS(insn.code)) { + case BPF_LD: + if (insn.code == BPF_LD + BPF_W + BPF_ABS) { + base::StringAppendF(dst, "LOAD %" PRIu32 " // ", insn.k); + size_t maybe_argno = + (insn.k - offsetof(struct arch_seccomp_data, args)) / + sizeof(uint64_t); + if (maybe_argno < 6 && insn.k == SECCOMP_ARG_LSB_IDX(maybe_argno)) { + base::StringAppendF(dst, "Argument %zu (LSB)\n", maybe_argno); + } else if (maybe_argno < 6 && + insn.k == SECCOMP_ARG_MSB_IDX(maybe_argno)) { + base::StringAppendF(dst, "Argument %zu (MSB)\n", maybe_argno); + } else { + base::StringAppendF(dst, "%s\n", DataOffsetName(insn.k)); + } + } else { + base::StringAppendF(dst, "Load ???\n"); + } + break; + case BPF_JMP: + if (BPF_OP(insn.code) == BPF_JA) { + base::StringAppendF(dst, "JMP %zu\n", pc + insn.k + 1); + } else { + base::StringAppendF( + dst, "if A %s 0x%" PRIx32 "; then JMP %zu else JMP %zu\n", + JmpOpToken(insn.code), insn.k, pc + insn.jt + 1, pc + insn.jf + 1); + } + break; + case BPF_RET: + base::StringAppendF(dst, "RET 0x%" PRIx32 " // ", insn.k); + if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP) { + base::StringAppendF(dst, "Trap #%" PRIu32 "\n", + insn.k & SECCOMP_RET_DATA); + } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { + base::StringAppendF(dst, "errno = %" PRIu32 "\n", + insn.k & SECCOMP_RET_DATA); + } else if ((insn.k & SECCOMP_RET_ACTION) == SECCOMP_RET_TRACE) { + base::StringAppendF(dst, "Trace #%" PRIu32 "\n", + insn.k & SECCOMP_RET_DATA); + } else if (insn.k == SECCOMP_RET_ALLOW) { + base::StringAppendF(dst, "Allowed\n"); + } else if (insn.k == SECCOMP_RET_KILL) { + base::StringAppendF(dst, "Kill\n"); + } else { + base::StringAppendF(dst, "???\n"); + } + break; + case BPF_ALU: + if (BPF_OP(insn.code) == BPF_NEG) { + base::StringAppendF(dst, "A := -A\n"); + } else { + base::StringAppendF(dst, "A := A %s 0x%" PRIx32 "\n", + AluOpToken(insn.code), insn.k); + } + break; + default: + base::StringAppendF(dst, "???\n"); + break; + } +} + +} // namespace + +void DumpBPF::PrintProgram(const CodeGen::Program& program) { + fputs(StringPrintProgram(program).c_str(), stderr); +} + +std::string DumpBPF::StringPrintProgram(const CodeGen::Program& program) { + std::string res; + for (size_t i = 0; i < program.size(); i++) { + AppendInstruction(&res, i + 1, program[i]); + } + return res; } } // namespace bpf_dsl diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h index ce260f87f974..a7db58981bf9 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h @@ -2,7 +2,9 @@ // 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/codegen.h" +#include + +#include "sandbox/linux/bpf_dsl/codegen.h" #include "sandbox/sandbox_export.h" namespace sandbox { @@ -12,6 +14,10 @@ class SANDBOX_EXPORT DumpBPF { public: // PrintProgram writes |program| in a human-readable format to stderr. static void PrintProgram(const CodeGen::Program& program); + + // StringPrintProgram writes |program| in a human-readable format to + // a std::string. + static std::string StringPrintProgram(const CodeGen::Program& program); }; } // namespace bpf_dsl diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/errorcode.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/errorcode.h new file mode 100644 index 000000000000..611c27dd8071 --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/errorcode.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SANDBOX_LINUX_BPF_DSL_ERRORCODE_H__ +#define SANDBOX_LINUX_BPF_DSL_ERRORCODE_H__ + +#include "base/macros.h" +#include "sandbox/sandbox_export.h" + +namespace sandbox { +namespace bpf_dsl { + +// TODO(mdempsky): Find a proper home for ERR_{MIN,MAX}_ERRNO and +// remove this header. +class SANDBOX_EXPORT ErrorCode { + public: + enum { + 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 + }; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorCode); +}; + +} // namespace bpf_dsl +} // namespace sandbox + +#endif // SANDBOX_LINUX_BPF_DSL_ERRORCODE_H__ diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h new file mode 100644 index 000000000000..a747770c78de --- /dev/null +++ b/security/sandbox/chromium/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 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 // 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/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc index 5be32f4f65b2..7ce517a5d5c6 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc @@ -5,7 +5,8 @@ #include "sandbox/linux/bpf_dsl/policy_compiler.h" #include -#include +#include +#include #include #include @@ -14,14 +15,13 @@ #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/policy.h" -#include "sandbox/linux/seccomp-bpf/codegen.h" -#include "sandbox/linux/seccomp-bpf/die.h" -#include "sandbox/linux/seccomp-bpf/errorcode.h" -#include "sandbox/linux/seccomp-bpf/instruction.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/seccomp-bpf/syscall.h" -#include "sandbox/linux/seccomp-bpf/syscall_iterator.h" +#include "sandbox/linux/bpf_dsl/seccomp_macros.h" +#include "sandbox/linux/bpf_dsl/syscall_set.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 { @@ -55,10 +55,8 @@ bool HasExactlyOneBit(uint64_t x) { return x != 0 && (x & (x - 1)) == 0; } -bool IsDenied(const ErrorCode& code) { - return (code.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_TRAP || - (code.err() >= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MIN_ERRNO) && - code.err() <= (SECCOMP_RET_ERRNO + ErrorCode::ERR_MAX_ERRNO)); +ResultExpr DefaultPanic(const char* error) { + return Kill(); } // A Trap() handler that returns an "errno" value. The value is encoded @@ -72,11 +70,8 @@ intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) { return -err; } -intptr_t BPFFailure(const struct arch_seccomp_data&, void* aux) { - SANDBOX_DIE(static_cast(aux)); -} - bool HasUnsafeTraps(const Policy* policy) { + DCHECK(policy); for (uint32_t sysnum : SyscallSet::ValidOnly()) { if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) { return true; @@ -88,65 +83,54 @@ bool HasUnsafeTraps(const Policy* policy) { } // namespace struct PolicyCompiler::Range { - Range(uint32_t f, const ErrorCode& e) : from(f), err(e) {} uint32_t from; - ErrorCode err; + CodeGen::Node node; }; PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry) : policy_(policy), registry_(registry), - conds_(), + escapepc_(0), + panic_func_(DefaultPanic), gen_(), has_unsafe_traps_(HasUnsafeTraps(policy_)) { + DCHECK(policy); } PolicyCompiler::~PolicyCompiler() { } -scoped_ptr PolicyCompiler::Compile() { - if (!IsDenied(policy_->InvalidSyscall()->Compile(this))) { - SANDBOX_DIE("Policies should deny invalid system calls."); - } +CodeGen::Program PolicyCompiler::Compile() { + 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_) { - // As support for unsafe jumps essentially defeats all the security - // measures that the sandbox provides, we print a big warning message -- - // and of course, we make sure to only ever enable this feature if it - // is actually requested by the sandbox policy. - if (Syscall::Call(-1) == -1 && errno == ENOSYS) { - SANDBOX_DIE( - "Support for UnsafeTrap() has not yet been ported to this " - "architecture"); - } + CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC"; for (int sysnum : kSyscallsRequiredForUnsafeTraps) { - if (!policy_->EvaluateSyscall(sysnum)->Compile(this) - .Equals(ErrorCode(ErrorCode::ERR_ALLOWED))) { - SANDBOX_DIE( - "Policies that use UnsafeTrap() must unconditionally allow all " - "required system calls"); - } + CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow()) + << "Policies that use UnsafeTrap() must unconditionally allow all " + "required system calls"; } - if (!registry_->EnableUnsafeTraps()) { - // We should never be able to get here, as UnsafeTrap() should never - // actually return a valid ErrorCode object unless the user set the - // CHROME_SANDBOX_DEBUGGING environment variable; and therefore, - // "has_unsafe_traps" would always be false. But better double-check - // than enabling dangerous code. - SANDBOX_DIE("We'd rather die than enable unsafe traps"); - } + CHECK(registry_->EnableUnsafeTraps()) + << "We'd rather die than enable unsafe traps"; } // Assemble the BPF filter program. - scoped_ptr program(new CodeGen::Program()); - gen_.Compile(AssemblePolicy(), program.get()); - return program.Pass(); + return gen_.Compile(AssemblePolicy()); } -Instruction* PolicyCompiler::AssemblePolicy() { +void PolicyCompiler::DangerousSetEscapePC(uint64_t escapepc) { + escapepc_ = escapepc; +} + +void PolicyCompiler::SetPanicFunc(PanicFunc panic_func) { + panic_func_ = panic_func; +} + +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 @@ -156,31 +140,29 @@ Instruction* PolicyCompiler::AssemblePolicy() { return CheckArch(MaybeAddEscapeHatch(DispatchSyscall())); } -Instruction* PolicyCompiler::CheckArch(Instruction* passed) { +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, - RetExpression(Kill("Invalid audit architecture in BPF filter")))); + BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX, + gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed, + CompileResult(panic_func_( + "Invalid audit architecture in BPF filter")))); } -Instruction* PolicyCompiler::MaybeAddEscapeHatch(Instruction* rest) { +CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) { // If no unsafe traps, then simply return |rest|. if (!has_unsafe_traps_) { return rest; } - // Allow system calls, if they originate from our magic return address - // (which we can query by calling Syscall::Call(-1)). - uint64_t syscall_entry_point = - static_cast(static_cast(Syscall::Call(-1))); - uint32_t low = static_cast(syscall_entry_point); - uint32_t hi = static_cast(syscall_entry_point >> 32); + // 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(escapepc_); + const uint32_t hipc = static_cast(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 @@ -190,30 +172,24 @@ Instruction* PolicyCompiler::MaybeAddEscapeHatch(Instruction* rest) { // 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, + BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX, gen_.MakeInstruction( - BPF_JMP + BPF_JEQ + BPF_K, - low, + 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, - hi, - RetExpression(ErrorCode(ErrorCode::ERR_ALLOWED)), - rest)), + BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX, + gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc, + CompileResult(Allow()), rest)), rest)); } -Instruction* PolicyCompiler::DispatchSyscall() { - // Evaluate all possible system calls and group their ErrorCodes into +CodeGen::Node PolicyCompiler::DispatchSyscall() { + // Evaluate all possible system calls and group their Nodes into // ranges of identical codes. Ranges ranges; FindRanges(&ranges); // Compile the system call ranges to an optimized BPF jumptable - Instruction* jumptable = AssembleJumpTable(ranges.begin(), ranges.end()); + 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. @@ -221,12 +197,12 @@ Instruction* PolicyCompiler::DispatchSyscall() { BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable)); } -Instruction* PolicyCompiler::CheckSyscallNumber(Instruction* passed) { +CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) { if (kIsIntel) { // On Intel architectures, verify that system call numbers are in the // expected number range. - Instruction* invalidX32 = - RetExpression(Kill("Illegal mixing of system call ABIs")); + CodeGen::Node invalidX32 = + CompileResult(panic_func_("Illegal mixing of system call ABIs")); if (kIsX32) { // The newer x32 API always sets bit 30. return gen_.MakeInstruction( @@ -247,113 +223,106 @@ void PolicyCompiler::FindRanges(Ranges* ranges) { // 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 ErrorCode invalid_err = policy_->InvalidSyscall()->Compile(this); + // negative) all return the same Node. + const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall()); uint32_t old_sysnum = 0; - ErrorCode old_err = SyscallSet::IsValid(old_sysnum) - ? policy_->EvaluateSyscall(old_sysnum)->Compile(this) - : invalid_err; + CodeGen::Node old_node = + SyscallSet::IsValid(old_sysnum) + ? CompileResult(policy_->EvaluateSyscall(old_sysnum)) + : invalid_node; for (uint32_t sysnum : SyscallSet::All()) { - ErrorCode err = + CodeGen::Node node = SyscallSet::IsValid(sysnum) - ? policy_->EvaluateSyscall(static_cast(sysnum))->Compile(this) - : invalid_err; - if (!err.Equals(old_err)) { - ranges->push_back(Range(old_sysnum, old_err)); + ? CompileResult(policy_->EvaluateSyscall(static_cast(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_err = err; + old_node = node; } } - ranges->push_back(Range(old_sysnum, old_err)); + ranges->push_back(Range{old_sysnum, old_node}); } -Instruction* PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start, - Ranges::const_iterator stop) { +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. - if (stop - start <= 0) { - SANDBOX_DIE("Invalid set of system call ranges"); - } else if (stop - start == 1) { + 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 RetExpression(start->err); + 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 + (stop - start) / 2; + Ranges::const_iterator mid = start + n / 2; // Sub-divide the list of ranges and continue recursively. - Instruction* jf = AssembleJumpTable(start, mid); - Instruction* jt = AssembleJumpTable(mid, stop); + 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); } -Instruction* 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: - SANDBOX_DIE("ErrorCode is not suitable for returning from a BPF program"); - } +CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) { + return res->Compile(this); } -Instruction* PolicyCompiler::CondExpression(const ErrorCode& cond) { - // Sanity check that |cond| makes sense. - if (cond.argno_ < 0 || cond.argno_ >= 6) { - SANDBOX_DIE("sandbox_bpf: invalid argument number"); +CodeGen::Node PolicyCompiler::MaskedEqual(int argno, + size_t width, + uint64_t mask, + uint64_t value, + CodeGen::Node passed, + CodeGen::Node failed) { + // Sanity check that arguments make sense. + CHECK(argno >= 0 && argno < 6) << "Invalid argument number " << argno; + CHECK(width == 4 || width == 8) << "Invalid argument width " << width; + CHECK_NE(0U, mask) << "Zero mask is invalid"; + CHECK_EQ(value, value & mask) << "Value contains masked out bits"; + if (sizeof(void*) == 4) { + CHECK_EQ(4U, width) << "Invalid width on 32-bit platform"; } - if (cond.width_ != ErrorCode::TP_32BIT && - cond.width_ != ErrorCode::TP_64BIT) { - SANDBOX_DIE("sandbox_bpf: invalid argument width"); + if (width == 4) { + CHECK_EQ(0U, mask >> 32) << "Mask exceeds argument size"; + CHECK_EQ(0U, value >> 32) << "Value exceeds argument size"; } - if (cond.mask_ == 0) { - SANDBOX_DIE("sandbox_bpf: zero mask is invalid"); - } - if ((cond.value_ & cond.mask_) != cond.value_) { - SANDBOX_DIE("sandbox_bpf: value contains masked out bits"); - } - if (cond.width_ == ErrorCode::TP_32BIT && - ((cond.mask_ >> 32) != 0 || (cond.value_ >> 32) != 0)) { - SANDBOX_DIE("sandbox_bpf: test exceeds argument size"); - } - // TODO(mdempsky): Reject TP_64BIT on 32-bit platforms. For now we allow it - // because some SandboxBPF unit tests exercise it. - - Instruction* passed = RetExpression(*cond.passed_); - Instruction* 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); + return MaskedEqualHalf(argno, width, mask, value, ArgHalf::UPPER, + MaskedEqualHalf(argno, width, mask, value, + ArgHalf::LOWER, passed, failed), + failed); } -Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond, - ArgHalf half, - Instruction* passed, - Instruction* failed) { - if (cond.width_ == ErrorCode::TP_32BIT && half == UpperHalf) { +CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno, + size_t width, + uint64_t full_mask, + uint64_t full_value, + ArgHalf half, + CodeGen::Node passed, + CodeGen::Node failed) { + if (width == 4 && half == ArgHalf::UPPER) { // Special logic for sanity checking the upper 32-bits of 32-bit system // call arguments. // TODO(mdempsky): Compile Unexpected64bitArgument() just per program. - Instruction* invalid_64bit = RetExpression(Unexpected64bitArgument()); + CodeGen::Node invalid_64bit = Unexpected64bitArgument(); - const uint32_t upper = SECCOMP_ARG_MSB_IDX(cond.argno_); - const uint32_t lower = SECCOMP_ARG_LSB_IDX(cond.argno_); + const uint32_t upper = SECCOMP_ARG_MSB_IDX(argno); + const uint32_t lower = SECCOMP_ARG_LSB_IDX(argno); if (sizeof(void*) == 4) { // On 32-bit platforms, the upper 32-bits should always be 0: @@ -396,10 +365,11 @@ Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond, 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_; + const uint32_t idx = (half == ArgHalf::UPPER) ? SECCOMP_ARG_MSB_IDX(argno) + : SECCOMP_ARG_LSB_IDX(argno); + const uint32_t mask = (half == ArgHalf::UPPER) ? full_mask >> 32 : full_mask; + const uint32_t value = + (half == ArgHalf::UPPER) ? full_value >> 32 : full_value; // Emit a suitable instruction sequence for (arg & mask) == value. @@ -454,12 +424,12 @@ Instruction* PolicyCompiler::CondExpressionHalf(const ErrorCode& cond, BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed))); } -ErrorCode PolicyCompiler::Unexpected64bitArgument() { - return Kill("Unexpected 64bit argument detected"); +CodeGen::Node PolicyCompiler::Unexpected64bitArgument() { + return CompileResult(panic_func_("Unexpected 64bit argument detected")); } -ErrorCode PolicyCompiler::Error(int err) { - if (has_unsafe_traps_) { +CodeGen::Node PolicyCompiler::Return(uint32_t ret) { + if (has_unsafe_traps_ && (ret & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { // 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 @@ -469,26 +439,18 @@ ErrorCode PolicyCompiler::Error(int err) { // 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(err)); + return Trap(ReturnErrno, reinterpret_cast(ret & SECCOMP_RET_DATA), + true); } - return ErrorCode(err); + return gen_.MakeInstruction(BPF_RET + BPF_K, ret); } -ErrorCode PolicyCompiler::MakeTrap(TrapRegistry::TrapFnc fnc, +CodeGen::Node 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); -} - -ErrorCode PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc, const void* aux) { - return MakeTrap(fnc, aux, true /* Safe Trap */); -} - -ErrorCode PolicyCompiler::UnsafeTrap(TrapRegistry::TrapFnc fnc, - const void* aux) { - return MakeTrap(fnc, aux, false /* Unsafe Trap */); + return gen_.MakeInstruction(BPF_RET + BPF_K, SECCOMP_RET_TRAP + trap_id); } bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { @@ -500,23 +462,5 @@ bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) { 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); -} - -ErrorCode PolicyCompiler::Kill(const char* msg) { - return Trap(BPFFailure, const_cast(msg)); -} - } // namespace bpf_dsl } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h index 4aec5fd1f493..48b1d780d956 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h @@ -5,21 +5,18 @@ #ifndef SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_ #define SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_ +#include #include -#include -#include #include #include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "sandbox/linux/seccomp-bpf/codegen.h" -#include "sandbox/linux/seccomp-bpf/errorcode.h" +#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h" +#include "sandbox/linux/bpf_dsl/codegen.h" +#include "sandbox/linux/bpf_dsl/trap_registry.h" #include "sandbox/sandbox_export.h" namespace sandbox { -struct Instruction; - namespace bpf_dsl { class Policy; @@ -28,140 +25,122 @@ class Policy; // Linux kernel. class SANDBOX_EXPORT PolicyCompiler { public: + using PanicFunc = bpf_dsl::ResultExpr (*)(const char* error); + 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 Compile(); + CodeGen::Program Compile(); - // Error returns an ErrorCode to indicate the system call should fail with - // the specified error number. - ErrorCode Error(int err); + // DangerousSetEscapePC sets the "escape PC" that is allowed to issue any + // system calls, regardless of policy. + void DangerousSetEscapePC(uint64_t escapepc); - // We can use ErrorCode to request calling of a trap handler. This method - // performs the required wrapping of the callback function into an - // ErrorCode object. - // The "aux" field can carry a pointer to arbitrary data. See EvaluateSyscall - // for a description of how to pass data from SetSandboxPolicy() to a Trap() - // handler. - ErrorCode Trap(TrapRegistry::TrapFnc fnc, const void* aux); - - // Calls a user-space trap handler and disables all sandboxing for system - // calls made from this trap handler. - // This feature is available only if explicitly enabled by the user having - // set the CHROME_SANDBOX_DEBUGGING environment variable. - // Returns an ET_INVALID ErrorCode, if called when not enabled. - // NOTE: This feature, by definition, disables all security features of - // the sandbox. It should never be used in production, but it can be - // very useful to diagnose code that is incompatible with the sandbox. - // If even a single system call returns "UnsafeTrap", the security of - // entire sandbox should be considered compromised. - ErrorCode UnsafeTrap(TrapRegistry::TrapFnc fnc, const void* aux); + // SetPanicFunc sets the callback function used for handling faulty + // system call conditions. The default behavior is to immediately kill + // the process. + // TODO(mdempsky): Move this into Policy? + void SetPanicFunc(PanicFunc panic_func); // 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. + // Functions below are meant for use within bpf_dsl itself. + + // Return returns a CodeGen::Node that returns the specified seccomp + // return value. + CodeGen::Node Return(uint32_t ret); + + // Trap returns a CodeGen::Node to indicate the system call should + // instead invoke a trap handler. + CodeGen::Node Trap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe); + + // MaskedEqual returns a CodeGen::Node that represents a conditional branch. // 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) + // to "value"; if equal, then "passed" will be executed, otherwise "failed". + // If "width" is 4, 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, + // the same as any other ABI violation (i.e., it panics). + CodeGen::Node MaskedEqual(int argno, + size_t width, uint64_t mask, uint64_t value, - const ErrorCode& passed, - const ErrorCode& failed); - - // Kill the program and print an error message. - ErrorCode Kill(const char* msg); - - // 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(); + CodeGen::Node passed, + CodeGen::Node failed); private: struct Range; typedef std::vector Ranges; - typedef std::map ErrMap; - typedef std::set Conds; - // Used by CondExpressionHalf to track which half of the argument it's + // Used by MaskedEqualHalf to track which half of the argument it's // emitting instructions for. - enum ArgHalf { - LowerHalf, - UpperHalf, + enum class ArgHalf { + LOWER, + UPPER, }; // Compile the configured policy into a complete instruction sequence. - Instruction* AssemblePolicy(); + 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. - Instruction* CheckArch(Instruction* passed); + CodeGen::Node CheckArch(CodeGen::Node passed); // If |has_unsafe_traps_| is true, returns an instruction sequence - // that allows all system calls from Syscall::Call(), and otherwise + // that allows all system calls from |escapepc_|, and otherwise // passes control to |rest|. Otherwise, simply returns |rest|. - Instruction* MaybeAddEscapeHatch(Instruction* 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. - Instruction* DispatchSyscall(); + 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). - Instruction* CheckSyscallNumber(Instruction* passed); + 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 + // ranges. System calls with identical CodeGen::Nodes are coalesced into a + // single // range. void FindRanges(Ranges* ranges); // Returns a BPF program snippet that implements a jump table for the // given range of system call numbers. This function runs recursively. - Instruction* AssembleJumpTable(Ranges::const_iterator start, - Ranges::const_iterator stop); + CodeGen::Node AssembleJumpTable(Ranges::const_iterator start, + Ranges::const_iterator stop); - // Returns a BPF program snippet that makes the BPF filter program exit - // with the given ErrorCode "err". N.B. the ErrorCode may very well be a - // conditional expression; if so, this function will recursively call - // CondExpression() and possibly RetExpression() to build a complex set of - // instructions. - Instruction* RetExpression(const ErrorCode& err); - - // Returns a BPF program that evaluates the conditional expression in - // "cond" and returns the appropriate value from the BPF filter program. - // This function recursively calls RetExpression(); it should only ever be - // called from RetExpression(). - Instruction* CondExpression(const ErrorCode& cond); + // CompileResult compiles an individual result expression into a + // CodeGen node. + CodeGen::Node CompileResult(const ResultExpr& res); // Returns a BPF program that evaluates half of a conditional expression; // it should only ever be called from CondExpression(). - Instruction* CondExpressionHalf(const ErrorCode& cond, - ArgHalf half, - Instruction* passed, - Instruction* failed); + CodeGen::Node MaskedEqualHalf(int argno, + size_t width, + uint64_t full_mask, + uint64_t full_value, + ArgHalf half, + CodeGen::Node passed, + CodeGen::Node failed); - // MakeTrap is the common implementation for Trap and UnsafeTrap. - ErrorCode MakeTrap(TrapRegistry::TrapFnc fnc, const void* aux, bool safe); + // Returns the fatal CodeGen::Node that is used to indicate that somebody + // attempted to pass a 64bit value in a 32bit system call argument. + CodeGen::Node Unexpected64bitArgument(); const Policy* policy_; TrapRegistry* registry_; + uint64_t escapepc_; + PanicFunc panic_func_; - Conds conds_; CodeGen gen_; bool has_unsafe_traps_; diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h similarity index 70% rename from security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h rename to security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h index 006e598940d1..af70f21cd77c 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/linux_seccomp.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h @@ -2,130 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__ +#ifndef SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_ +#define SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_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 -// #include - -#include -#include - -#include +#include // For __BIONIC__. // 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 // Fix for gcc 4.7, make sure __uint16_t is defined. +#if !defined(__native_client_nonsfi__) #include +#endif #if defined(__mips__) // sys/user.h in eglibc misses size_t definition #include #endif #endif -// 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 - -#ifndef BPF_MOD -#define BPF_MOD 0x90 -#endif -#ifndef BPF_XOR -#define BPF_XOR 0xA0 -#endif - -// In order to build will older tool chains, we currently have to avoid -// including . 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 +#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 @@ -133,9 +27,6 @@ #define SECCOMP_MAX_PROGRAM_SIZE (1<<30) #if defined(__i386__) -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 1024u -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL #define SECCOMP_ARCH AUDIT_ARCH_I386 #define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)]) @@ -160,10 +51,10 @@ 8*(nr) + 0) -#if defined(__BIONIC__) -// Old Bionic versions don't have sys/user.h, so we just define regs_struct -// directly. This can be removed once we no longer need to support these old -// Bionic versions. +#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; @@ -198,9 +89,6 @@ typedef user_regs_struct regs_struct; #define SECCOMP_PT_PARM6(_regs) (_regs).ebp #elif defined(__x86_64__) -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 1024u -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL #define SECCOMP_ARCH AUDIT_ARCH_X86_64 #define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)]) @@ -236,17 +124,6 @@ typedef user_regs_struct regs_struct; #define SECCOMP_PT_PARM6(_regs) (_regs).r9 #elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__)) -// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|, -// and a "ghost syscall private to the kernel", cmpxchg, -// at |__ARM_NR_BASE+0x00fff0|. -// See in the Linux kernel. -#define MIN_SYSCALL ((unsigned int)__NR_SYSCALL_BASE) -#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u) -#define MIN_PRIVATE_SYSCALL ((unsigned int)__ARM_NR_BASE) -#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u) -#define MIN_GHOST_SYSCALL ((unsigned int)__ARM_NR_BASE + 0xfff0u) -#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u) - #define SECCOMP_ARCH AUDIT_ARCH_ARM // ARM sigcontext_t is different from i386/x86_64. @@ -273,10 +150,10 @@ typedef user_regs_struct regs_struct; #define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \ 8*(nr) + 0) -#if defined(__BIONIC__) -// Old Bionic versions don't have sys/user.h, so we just define regs_struct -// directly. This can be removed once we no longer need to support these old -// Bionic versions. +#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]; }; @@ -314,9 +191,6 @@ typedef user_regs regs_struct; #define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5 #elif defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32) -#define MIN_SYSCALL __NR_O32_Linux -#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_Linux_syscalls) -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL #define SECCOMP_ARCH AUDIT_ARCH_MIPSEL #define SYSCALL_EIGHT_ARGS // MIPS sigcontext_t is different from i386/x86_64 and ARM. @@ -378,9 +252,6 @@ struct regs_struct { unsigned long long pstate; }; -#define MIN_SYSCALL 0u -#define MAX_PUBLIC_SYSCALL 279u -#define MAX_SYSCALL MAX_PUBLIC_SYSCALL #define SECCOMP_ARCH AUDIT_ARCH_AARCH64 #define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.regs[_reg]) @@ -420,4 +291,4 @@ struct regs_struct { #endif -#endif // SANDBOX_LINUX_SECCOMP_BPF_LINUX_SECCOMP_H__ +#endif // SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_ diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc similarity index 93% rename from security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc rename to security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc index 35905309f47d..3d61fa31fd38 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc @@ -2,11 +2,13 @@ // 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_iterator.h" +#include "sandbox/linux/bpf_dsl/syscall_set.h" + +#include #include "base/logging.h" #include "base/macros.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" +#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h" namespace sandbox { @@ -14,10 +16,11 @@ namespace { #if defined(__mips__) && (_MIPS_SIM == _MIPS_SIM_ABI32) // This is true for Mips O32 ABI. -COMPILE_ASSERT(MIN_SYSCALL == __NR_Linux, min_syscall_should_be_4000); +static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 4000"); #else // This true for supported architectures (Intel and ARM EABI). -COMPILE_ASSERT(MIN_SYSCALL == 0u, min_syscall_should_always_be_zero); +static_assert(MIN_SYSCALL == 0u, + "min syscall should always be zero"); #endif // SyscallRange represents an inclusive range of system call numbers. diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h similarity index 93% rename from security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h rename to security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h index 5080fcc09a21..b9f076d9321a 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__ +#ifndef SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__ +#define SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__ #include @@ -14,8 +14,6 @@ namespace sandbox { -// TODO(mdempsky): Rename this header to syscall_set.h. - // 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 @@ -102,4 +100,4 @@ SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs, } // namespace sandbox -#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_ITERATOR_H__ +#endif // SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__ diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h index 94d4722c8cc3..0a5d2f14cccf 100644 --- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h +++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h @@ -49,10 +49,19 @@ class SANDBOX_EXPORT TrapRegistry { // 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); diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.cc deleted file mode 100644 index eb857f00b6cd..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.cc +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/basicblock.h" - -namespace sandbox { - -BasicBlock::BasicBlock() {} - -BasicBlock::~BasicBlock() {} - -} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.h deleted file mode 100644 index d15a372cfea0..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/basicblock.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__ - -#include - -#include "sandbox/linux/seccomp-bpf/instruction.h" - -namespace sandbox { - -struct BasicBlock { - BasicBlock(); - ~BasicBlock(); - - // Our implementation of the code generator uses a "Less" operator to - // identify common sequences of basic blocks. This would normally be - // really easy to do, but STL requires us to wrap the comparator into - // a class. We begrudgingly add some code here that provides this wrapping. - template - class Less { - public: - Less(const T& data, - int (*cmp)(const BasicBlock*, const BasicBlock*, const T& data)) - : data_(data), cmp_(cmp) {} - - bool operator()(const BasicBlock* a, const BasicBlock* b) const { - return cmp_(a, b, data_) < 0; - } - - private: - const T& data_; - int (*cmp_)(const BasicBlock*, const BasicBlock*, const T&); - }; - - // Basic blocks are essentially nothing more than a set of instructions. - std::vector instructions; - - // In order to compute relative branch offsets we need to keep track of - // how far our block is away from the very last basic block. The "offset_" - // is measured in number of BPF instructions. - int offset; -}; - -} // namespace sandbox - -#endif // SANDBOX_LINUX_SECCOMP_BPF_BASICBLOCK_H__ diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h index 18699138d551..00d415c3fc5c 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h @@ -5,6 +5,7 @@ #ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_ #define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_ +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h" @@ -25,9 +26,9 @@ class BPFTesterCompatibilityDelegate : public BPFTesterDelegate { explicit BPFTesterCompatibilityDelegate(TestFunction test_function) : aux_(), test_function_(test_function) {} - virtual ~BPFTesterCompatibilityDelegate() {} + ~BPFTesterCompatibilityDelegate() override {} - virtual scoped_ptr GetSandboxBPFPolicy() override { + scoped_ptr 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 @@ -35,7 +36,7 @@ class BPFTesterCompatibilityDelegate : public BPFTesterDelegate { return scoped_ptr(new Policy(&aux_)); } - virtual void RunTestFunction() override { + void RunTestFunction() override { // Run the actual test. // The current object is guaranteed to live forever in the child process // where this will run. diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h index 57de1dd81715..cc4debd4c30b 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -102,12 +102,12 @@ class BPFTesterSimpleDelegate : public BPFTesterDelegate { public: explicit BPFTesterSimpleDelegate(void (*test_function)(void)) : test_function_(test_function) {} - virtual ~BPFTesterSimpleDelegate() {} + ~BPFTesterSimpleDelegate() override {} - virtual scoped_ptr GetSandboxBPFPolicy() override { + scoped_ptr GetSandboxBPFPolicy() override { return scoped_ptr(new PolicyClass()); } - virtual void RunTestFunction() override { + void RunTestFunction() override { DCHECK(test_function_); test_function_(); } diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc index 9d2bb7993c64..e300baf9ba29 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc @@ -11,12 +11,14 @@ #include #include "base/logging.h" +#include "base/macros.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/linux_syscalls.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" @@ -45,9 +47,9 @@ class EmptyClassTakingPolicy : public bpf_dsl::Policy { BPF_ASSERT(fourty_two); BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value()); } - virtual ~EmptyClassTakingPolicy() {} + ~EmptyClassTakingPolicy() override {} - virtual ResultExpr EvaluateSyscall(int sysno) const override { + ResultExpr EvaluateSyscall(int sysno) const override { DCHECK(SandboxBPF::IsValidSyscallNumber(sysno)); return Allow(); } @@ -82,21 +84,19 @@ TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) { class EnosysPtracePolicy : public bpf_dsl::Policy { public: - EnosysPtracePolicy() { - my_pid_ = syscall(__NR_getpid); - } - virtual ~EnosysPtracePolicy() { + 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_, syscall(__NR_getpid)); + BPF_ASSERT_EQ(my_pid_, sys_getpid()); } - virtual ResultExpr EvaluateSyscall(int system_call_number) const override { + 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_, syscall(__NR_getpid)); + BPF_ASSERT_EQ(my_pid_, sys_getpid()); return Error(ENOSYS); } else { return Allow(); @@ -111,12 +111,12 @@ class EnosysPtracePolicy : public bpf_dsl::Policy { class BasicBPFTesterDelegate : public BPFTesterDelegate { public: BasicBPFTesterDelegate() {} - virtual ~BasicBPFTesterDelegate() {} + ~BasicBPFTesterDelegate() override {} - virtual scoped_ptr GetSandboxBPFPolicy() override { + scoped_ptr GetSandboxBPFPolicy() override { return scoped_ptr(new EnosysPtracePolicy()); } - virtual void RunTestFunction() override { + void RunTestFunction() override { errno = 0; int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL); BPF_ASSERT(-1 == ret); diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.cc deleted file mode 100644 index 1ee79b6ed0e9..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.cc +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/codegen.h" - -#include - -#include - -#include "base/logging.h" -#include "sandbox/linux/seccomp-bpf/basicblock.h" -#include "sandbox/linux/seccomp-bpf/die.h" -#include "sandbox/linux/seccomp-bpf/instruction.h" - -namespace sandbox { - -CodeGen::CodeGen() : compiled_(false) {} - -CodeGen::~CodeGen() { - for (Instructions::iterator iter = instructions_.begin(); - iter != instructions_.end(); - ++iter) { - delete *iter; - } - for (BasicBlocks::iterator iter = basic_blocks_.begin(); - iter != basic_blocks_.end(); - ++iter) { - delete *iter; - } -} - -Instruction* CodeGen::MakeInstruction(uint16_t code, - uint32_t k, - Instruction* next) { - // We can handle non-jumping instructions and "always" jumps. Both of - // them are followed by exactly one "next" instruction. - // We allow callers to defer specifying "next", but then they must call - // "joinInstructions" later. - if (BPF_CLASS(code) == BPF_JMP && BPF_OP(code) != BPF_JA) { - SANDBOX_DIE( - "Must provide both \"true\" and \"false\" branch " - "for a BPF_JMP"); - } - if (next && BPF_CLASS(code) == BPF_RET) { - SANDBOX_DIE("Cannot append instructions after a return statement"); - } - if (BPF_CLASS(code) == BPF_JMP) { - // "Always" jumps use the "true" branch target, only. - Instruction* insn = new Instruction(code, 0, next, NULL); - instructions_.push_back(insn); - return insn; - } else { - // Non-jumping instructions do not use any of the branch targets. - Instruction* insn = new Instruction(code, k, next); - instructions_.push_back(insn); - return insn; - } -} - -Instruction* CodeGen::MakeInstruction(uint16_t code, - uint32_t k, - Instruction* jt, - Instruction* jf) { - // We can handle all conditional jumps. They are followed by both a - // "true" and a "false" branch. - if (BPF_CLASS(code) != BPF_JMP || BPF_OP(code) == BPF_JA) { - SANDBOX_DIE("Expected a BPF_JMP instruction"); - } - if (!jt || !jf) { - SANDBOX_DIE("Branches must jump to a valid instruction"); - } - Instruction* insn = new Instruction(code, k, jt, jf); - instructions_.push_back(insn); - return insn; -} - -void CodeGen::FindBranchTargets(const Instruction& instructions, - BranchTargets* branch_targets) { - // Follow all possible paths through the "instructions" graph and compute - // a list of branch targets. This will later be needed to compute the - // boundaries of basic blocks. - // We maintain a set of all instructions that we have previously seen. This - // set ultimately converges on all instructions in the program. - std::set seen_instructions; - Instructions stack; - for (const Instruction* insn = &instructions; insn;) { - seen_instructions.insert(insn); - if (BPF_CLASS(insn->code) == BPF_JMP) { - // Found a jump. Increase count of incoming edges for each of the jump - // targets. - ++(*branch_targets)[insn->jt_ptr]; - if (BPF_OP(insn->code) != BPF_JA) { - ++(*branch_targets)[insn->jf_ptr]; - stack.push_back(const_cast(insn)); - } - // Start a recursive decent for depth-first traversal. - if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) { - // We haven't seen the "true" branch yet. Traverse it now. We have - // already remembered the "false" branch on the stack and will - // traverse it later. - insn = insn->jt_ptr; - continue; - } else { - // Now try traversing the "false" branch. - insn = NULL; - } - } else { - // This is a non-jump instruction, just continue to the next instruction - // (if any). It's OK if "insn" becomes NULL when reaching a return - // instruction. - if (!insn->next != (BPF_CLASS(insn->code) == BPF_RET)) { - SANDBOX_DIE( - "Internal compiler error; return instruction must be at " - "the end of the BPF program"); - } - if (seen_instructions.find(insn->next) == seen_instructions.end()) { - insn = insn->next; - } else { - // We have seen this instruction before. That could happen if it is - // a branch target. No need to continue processing. - insn = NULL; - } - } - while (!insn && !stack.empty()) { - // We are done processing all the way to a leaf node, backtrack up the - // stack to any branches that we haven't processed yet. By definition, - // this has to be a "false" branch, as we always process the "true" - // branches right away. - insn = stack.back(); - stack.pop_back(); - if (seen_instructions.find(insn->jf_ptr) == seen_instructions.end()) { - // We haven't seen the "false" branch yet. So, that's where we'll - // go now. - insn = insn->jf_ptr; - } else { - // We have seen both the "true" and the "false" branch, continue - // up the stack. - if (seen_instructions.find(insn->jt_ptr) == seen_instructions.end()) { - SANDBOX_DIE( - "Internal compiler error; cannot find all " - "branch targets"); - } - insn = NULL; - } - } - } - return; -} - -BasicBlock* CodeGen::MakeBasicBlock(Instruction* head, Instruction* tail) { - // Iterate over all the instructions between "head" and "tail" and - // insert them into a new basic block. - BasicBlock* bb = new BasicBlock; - for (;; head = head->next) { - bb->instructions.push_back(head); - if (head == tail) { - break; - } - if (BPF_CLASS(head->code) == BPF_JMP) { - SANDBOX_DIE("Found a jump inside of a basic block"); - } - } - basic_blocks_.push_back(bb); - return bb; -} - -void CodeGen::AddBasicBlock(Instruction* head, - Instruction* tail, - const BranchTargets& branch_targets, - TargetsToBlocks* basic_blocks, - BasicBlock** firstBlock) { - // Add a new basic block to "basic_blocks". Also set "firstBlock", if it - // has not been set before. - BranchTargets::const_iterator iter = branch_targets.find(head); - if ((iter == branch_targets.end()) != !*firstBlock || - !*firstBlock != basic_blocks->empty()) { - SANDBOX_DIE( - "Only the very first basic block should have no " - "incoming jumps"); - } - BasicBlock* bb = MakeBasicBlock(head, tail); - if (!*firstBlock) { - *firstBlock = bb; - } - (*basic_blocks)[head] = bb; - return; -} - -BasicBlock* CodeGen::CutGraphIntoBasicBlocks( - Instruction* instructions, - const BranchTargets& branch_targets, - TargetsToBlocks* basic_blocks) { - // Textbook implementation of a basic block generator. All basic blocks - // start with a branch target and end with either a return statement or - // a jump (or are followed by an instruction that forms the beginning of a - // new block). Both conditional and "always" jumps are supported. - BasicBlock* first_block = NULL; - std::set seen_instructions; - Instructions stack; - Instruction* tail = NULL; - Instruction* head = instructions; - for (Instruction* insn = head; insn;) { - if (seen_instructions.find(insn) != seen_instructions.end()) { - // We somehow went in a circle. This should never be possible. Not even - // cyclic graphs are supposed to confuse us this much. - SANDBOX_DIE("Internal compiler error; cannot compute basic blocks"); - } - seen_instructions.insert(insn); - if (tail && branch_targets.find(insn) != branch_targets.end()) { - // We reached a branch target. Start a new basic block (this means, - // flushing the previous basic block first). - AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block); - head = insn; - } - if (BPF_CLASS(insn->code) == BPF_JMP) { - // We reached a jump instruction, this completes our current basic - // block. Flush it and continue by traversing both the true and the - // false branch of the jump. We need to maintain a stack to do so. - AddBasicBlock(head, insn, branch_targets, basic_blocks, &first_block); - if (BPF_OP(insn->code) != BPF_JA) { - stack.push_back(insn->jf_ptr); - } - insn = insn->jt_ptr; - - // If we are jumping to an instruction that we have previously - // processed, we are done with this branch. Continue by backtracking - // up the stack. - while (seen_instructions.find(insn) != seen_instructions.end()) { - backtracking: - if (stack.empty()) { - // We successfully traversed all reachable instructions. - return first_block; - } else { - // Going up the stack. - insn = stack.back(); - stack.pop_back(); - } - } - // Starting a new basic block. - tail = NULL; - head = insn; - } else { - // We found a non-jumping instruction, append it to current basic - // block. - tail = insn; - insn = insn->next; - if (!insn) { - // We reached a return statement, flush the current basic block and - // backtrack up the stack. - AddBasicBlock(head, tail, branch_targets, basic_blocks, &first_block); - goto backtracking; - } - } - } - return first_block; -} - -// We define a comparator that inspects the sequence of instructions in our -// basic block and any blocks referenced by this block. This function can be -// used in a "less" comparator for the purpose of storing pointers to basic -// blocks in STL containers; this gives an easy option to use STL to find -// shared tail sequences of basic blocks. -static int PointerCompare(const BasicBlock* block1, - const BasicBlock* block2, - const TargetsToBlocks& blocks) { - // Return <0, 0, or >0 depending on the ordering of "block1" and "block2". - // If we are looking at the exact same block, this is trivial and we don't - // need to do a full comparison. - if (block1 == block2) { - return 0; - } - - // We compare the sequence of instructions in both basic blocks. - const Instructions& insns1 = block1->instructions; - const Instructions& insns2 = block2->instructions; - // Basic blocks should never be empty. - CHECK(!insns1.empty()); - CHECK(!insns2.empty()); - - Instructions::const_iterator iter1 = insns1.begin(); - Instructions::const_iterator iter2 = insns2.begin(); - for (;; ++iter1, ++iter2) { - // If we have reached the end of the sequence of instructions in one or - // both basic blocks, we know the relative ordering between the two blocks - // and can return. - if (iter1 == insns1.end() || iter2 == insns2.end()) { - if (iter1 != insns1.end()) { - return 1; - } - if (iter2 != insns2.end()) { - return -1; - } - - // If the two blocks are the same length (and have elementwise-equal code - // and k fields) and their last instructions are neither a JMP nor a RET - // (which is the only way we can reach this point), then we must compare - // their successors. - Instruction* const insns1_last = insns1.back(); - Instruction* const insns2_last = insns2.back(); - CHECK(BPF_CLASS(insns1_last->code) != BPF_JMP && - BPF_CLASS(insns1_last->code) != BPF_RET); - - // Non jumping instructions will always have a valid next instruction. - CHECK(insns1_last->next); - CHECK(insns2_last->next); - return PointerCompare(blocks.find(insns1_last->next)->second, - blocks.find(insns2_last->next)->second, - blocks); - } - - // Compare the individual fields for both instructions. - const Instruction& insn1 = **iter1; - const Instruction& insn2 = **iter2; - if (insn1.code != insn2.code) { - return insn1.code - insn2.code; - } - if (insn1.k != insn2.k) { - return insn1.k - insn2.k; - } - - // Sanity check: If we're looking at a JMP or RET instruction, by definition - // it should be the last instruction of the basic block. - if (BPF_CLASS(insn1.code) == BPF_JMP || BPF_CLASS(insn1.code) == BPF_RET) { - CHECK_EQ(insns1.back(), &insn1); - CHECK_EQ(insns2.back(), &insn2); - } - - // RET instructions terminate execution, and only JMP instructions use the - // jt_ptr and jf_ptr fields. Anything else can continue to the next - // instruction in the basic block. - if (BPF_CLASS(insn1.code) == BPF_RET) { - return 0; - } else if (BPF_CLASS(insn1.code) != BPF_JMP) { - continue; - } - - // Recursively compare the "true" and "false" branches. - // A well-formed BPF program can't have any cycles, so we know - // that our recursive algorithm will ultimately terminate. - // In the unlikely event that the programmer made a mistake and - // went out of the way to give us a cyclic program, we will crash - // with a stack overflow. We are OK with that. - if (BPF_OP(insn1.code) != BPF_JA) { - int c = PointerCompare(blocks.find(insn1.jf_ptr)->second, - blocks.find(insn2.jf_ptr)->second, - blocks); - if (c != 0) { - return c; - } - } - return PointerCompare(blocks.find(insn1.jt_ptr)->second, - blocks.find(insn2.jt_ptr)->second, - blocks); - } -} - -void CodeGen::MergeTails(TargetsToBlocks* blocks) { - // We enter all of our basic blocks into a set using the BasicBlock::Less() - // comparator. This naturally results in blocks with identical tails of - // instructions to map to the same entry in the set. Whenever we discover - // that a particular chain of instructions is already in the set, we merge - // the basic blocks and update the pointer in the "blocks" map. - // Returns the number of unique basic blocks. - // N.B. We don't merge instructions on a granularity that is finer than - // a basic block. In practice, this is sufficiently rare that we don't - // incur a big cost. - // Similarly, we currently don't merge anything other than tails. In - // the future, we might decide to revisit this decision and attempt to - // merge arbitrary sub-sequences of instructions. - BasicBlock::Less less(*blocks, PointerCompare); - typedef std::set > Set; - Set seen_basic_blocks(less); - for (TargetsToBlocks::iterator iter = blocks->begin(); iter != blocks->end(); - ++iter) { - BasicBlock* bb = iter->second; - Set::const_iterator entry = seen_basic_blocks.find(bb); - if (entry == seen_basic_blocks.end()) { - // This is the first time we see this particular sequence of - // instructions. Enter the basic block into the set of known - // basic blocks. - seen_basic_blocks.insert(bb); - } else { - // We have previously seen another basic block that defines the same - // sequence of instructions. Merge the two blocks and update the - // pointer in the "blocks" map. - iter->second = *entry; - } - } -} - -void CodeGen::ComputeIncomingBranches(BasicBlock* block, - const TargetsToBlocks& targets_to_blocks, - IncomingBranches* incoming_branches) { - // We increment the number of incoming branches each time we encounter a - // basic block. But we only traverse recursively the very first time we - // encounter a new block. This is necessary to make topological sorting - // work correctly. - if (++(*incoming_branches)[block] == 1) { - Instruction* last_insn = block->instructions.back(); - if (BPF_CLASS(last_insn->code) == BPF_JMP) { - ComputeIncomingBranches(targets_to_blocks.find(last_insn->jt_ptr)->second, - targets_to_blocks, - incoming_branches); - if (BPF_OP(last_insn->code) != BPF_JA) { - ComputeIncomingBranches( - targets_to_blocks.find(last_insn->jf_ptr)->second, - targets_to_blocks, - incoming_branches); - } - } else if (BPF_CLASS(last_insn->code) != BPF_RET) { - ComputeIncomingBranches(targets_to_blocks.find(last_insn->next)->second, - targets_to_blocks, - incoming_branches); - } - } -} - -void CodeGen::TopoSortBasicBlocks(BasicBlock* first_block, - const TargetsToBlocks& blocks, - BasicBlocks* basic_blocks) { - // Textbook implementation of a toposort. We keep looking for basic blocks - // that don't have any incoming branches (initially, this is just the - // "first_block") and add them to the topologically sorted list of - // "basic_blocks". As we do so, we remove outgoing branches. This potentially - // ends up making our descendants eligible for the sorted list. The - // sorting algorithm terminates when there are no more basic blocks that have - // no incoming branches. If we didn't move all blocks from the set of - // "unordered_blocks" to the sorted list of "basic_blocks", there must have - // been a cyclic dependency. This should never happen in a BPF program, as - // well-formed BPF programs only ever have forward branches. - IncomingBranches unordered_blocks; - ComputeIncomingBranches(first_block, blocks, &unordered_blocks); - - std::set heads; - for (;;) { - // Move block from "unordered_blocks" to "basic_blocks". - basic_blocks->push_back(first_block); - - // Inspect last instruction in the basic block. This is typically either a - // jump or a return statement. But it could also be a "normal" instruction - // that is followed by a jump target. - Instruction* last_insn = first_block->instructions.back(); - if (BPF_CLASS(last_insn->code) == BPF_JMP) { - // Remove outgoing branches. This might end up moving our descendants - // into set of "head" nodes that no longer have any incoming branches. - TargetsToBlocks::const_iterator iter; - if (BPF_OP(last_insn->code) != BPF_JA) { - iter = blocks.find(last_insn->jf_ptr); - if (!--unordered_blocks[iter->second]) { - heads.insert(iter->second); - } - } - iter = blocks.find(last_insn->jt_ptr); - if (!--unordered_blocks[iter->second]) { - first_block = iter->second; - continue; - } - } else if (BPF_CLASS(last_insn->code) != BPF_RET) { - // We encountered an instruction that doesn't change code flow. Try to - // pick the next "first_block" from "last_insn->next", if possible. - TargetsToBlocks::const_iterator iter; - iter = blocks.find(last_insn->next); - if (!--unordered_blocks[iter->second]) { - first_block = iter->second; - continue; - } else { - // Our basic block is supposed to be followed by "last_insn->next", - // but dependencies prevent this from happening. Insert a BPF_JA - // instruction to correct the code flow. - Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, last_insn->next); - first_block->instructions.push_back(ja); - last_insn->next = ja; - } - } - if (heads.empty()) { - if (unordered_blocks.size() != basic_blocks->size()) { - SANDBOX_DIE("Internal compiler error; cyclic graph detected"); - } - return; - } - // Proceed by picking an arbitrary node from the set of basic blocks that - // do not have any incoming branches. - first_block = *heads.begin(); - heads.erase(heads.begin()); - } -} - -void CodeGen::ComputeRelativeJumps(BasicBlocks* basic_blocks, - const TargetsToBlocks& targets_to_blocks) { - // While we previously used pointers in jt_ptr and jf_ptr to link jump - // instructions to their targets, we now convert these jumps to relative - // jumps that are suitable for loading the BPF program into the kernel. - int offset = 0; - - // Since we just completed a toposort, all jump targets are guaranteed to - // go forward. This means, iterating over the basic blocks in reverse makes - // it trivial to compute the correct offsets. - BasicBlock* bb = NULL; - BasicBlock* last_bb = NULL; - for (BasicBlocks::reverse_iterator iter = basic_blocks->rbegin(); - iter != basic_blocks->rend(); - ++iter) { - last_bb = bb; - bb = *iter; - Instruction* insn = bb->instructions.back(); - if (BPF_CLASS(insn->code) == BPF_JMP) { - // Basic block ended in a jump instruction. We can now compute the - // appropriate offsets. - if (BPF_OP(insn->code) == BPF_JA) { - // "Always" jumps use the 32bit "k" field for the offset, instead - // of the 8bit "jt" and "jf" fields. - int jmp = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset; - insn->k = jmp; - insn->jt = insn->jf = 0; - } else { - // The offset computations for conditional jumps are just the same - // as for "always" jumps. - int jt = offset - targets_to_blocks.find(insn->jt_ptr)->second->offset; - int jf = offset - targets_to_blocks.find(insn->jf_ptr)->second->offset; - - // There is an added complication, because conditional relative jumps - // can only jump at most 255 instructions forward. If we have to jump - // further, insert an extra "always" jump. - Instructions::size_type jmp = bb->instructions.size(); - if (jt > 255 || (jt == 255 && jf > 255)) { - Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jt_ptr); - bb->instructions.push_back(ja); - ja->k = jt; - ja->jt = ja->jf = 0; - - // The newly inserted "always" jump, of course, requires us to adjust - // the jump targets in the original conditional jump. - jt = 0; - ++jf; - } - if (jf > 255) { - Instruction* ja = MakeInstruction(BPF_JMP + BPF_JA, 0, insn->jf_ptr); - bb->instructions.insert(bb->instructions.begin() + jmp, ja); - ja->k = jf; - ja->jt = ja->jf = 0; - - // Again, we have to adjust the jump targets in the original - // conditional jump. - ++jt; - jf = 0; - } - - // Now we can finally set the relative jump targets in the conditional - // jump instruction. Afterwards, we must no longer access the jt_ptr - // and jf_ptr fields. - insn->jt = jt; - insn->jf = jf; - } - } else if (BPF_CLASS(insn->code) != BPF_RET && - targets_to_blocks.find(insn->next)->second != last_bb) { - SANDBOX_DIE("Internal compiler error; invalid basic block encountered"); - } - - // Proceed to next basic block. - offset += bb->instructions.size(); - bb->offset = offset; - } - return; -} - -void CodeGen::ConcatenateBasicBlocks(const BasicBlocks& basic_blocks, - Program* program) { - // Our basic blocks have been sorted and relative jump offsets have been - // computed. The last remaining step is for all the instructions in our - // basic blocks to be concatenated into a BPF program. - program->clear(); - for (BasicBlocks::const_iterator bb_iter = basic_blocks.begin(); - bb_iter != basic_blocks.end(); - ++bb_iter) { - const BasicBlock& bb = **bb_iter; - for (Instructions::const_iterator insn_iter = bb.instructions.begin(); - insn_iter != bb.instructions.end(); - ++insn_iter) { - const Instruction& insn = **insn_iter; - program->push_back( - (struct sock_filter) {insn.code, insn.jt, insn.jf, insn.k}); - } - } - return; -} - -void CodeGen::Compile(Instruction* instructions, Program* program) { - if (compiled_) { - SANDBOX_DIE( - "Cannot call Compile() multiple times. Create a new code " - "generator instead"); - } - compiled_ = true; - - BranchTargets branch_targets; - FindBranchTargets(*instructions, &branch_targets); - TargetsToBlocks all_blocks; - BasicBlock* first_block = - CutGraphIntoBasicBlocks(instructions, branch_targets, &all_blocks); - MergeTails(&all_blocks); - BasicBlocks basic_blocks; - TopoSortBasicBlocks(first_block, all_blocks, &basic_blocks); - ComputeRelativeJumps(&basic_blocks, all_blocks); - ConcatenateBasicBlocks(basic_blocks, program); - return; -} - -} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.h deleted file mode 100644 index 671b09e4264d..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__ - -#include - -#include -#include - -#include "sandbox/sandbox_export.h" - -struct sock_filter; - -namespace sandbox { -struct BasicBlock; -struct Instruction; - -typedef std::vector Instructions; -typedef std::vector BasicBlocks; -typedef std::map BranchTargets; -typedef std::map TargetsToBlocks; -typedef std::map IncomingBranches; - -// The code generator instantiates a basic compiler that can convert a -// graph of BPF instructions into a well-formed stream of BPF instructions. -// Most notably, it ensures that jumps are always forward and don't exceed -// the limit of 255 instructions imposed by the instruction set. -// -// Callers would typically create a new CodeGen object and then use it to -// build a DAG of Instructions. They'll eventually call Compile() to convert -// this DAG to a Program. -// -// CodeGen gen; -// Instruction *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(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 Program; - - CodeGen(); - ~CodeGen(); - - // Create a new instruction. Instructions form a DAG. The instruction objects - // are owned by the CodeGen object. They do not need to be explicitly - // deleted. - // For details on the possible parameters refer to - Instruction* MakeInstruction(uint16_t code, - uint32_t k, - Instruction* next = nullptr); - Instruction* MakeInstruction(uint16_t code, - uint32_t k, - Instruction* jt, - Instruction* jf); - - // Compiles the graph of instructions into a BPF program that can be passed - // to the kernel. Please note that this function modifies the graph in place - // and must therefore only be called once per graph. - void Compile(Instruction* instructions, Program* program); - - private: - friend class CodeGenUnittestHelper; - - // Find all the instructions that are the target of BPF_JMPs. - void FindBranchTargets(const Instruction& instructions, - BranchTargets* branch_targets); - - // Combine instructions between "head" and "tail" into a new basic block. - // Basic blocks are defined as sequences of instructions whose only branch - // target is the very first instruction; furthermore, any BPF_JMP or BPF_RET - // instruction must be at the very end of the basic block. - BasicBlock* MakeBasicBlock(Instruction* head, Instruction* tail); - - // Creates a basic block and adds it to "basic_blocks"; sets "first_block" - // if it is still NULL. - void AddBasicBlock(Instruction* head, - Instruction* tail, - const BranchTargets& branch_targets, - TargetsToBlocks* basic_blocks, - BasicBlock** first_block); - - // Cuts the DAG of instructions into basic blocks. - BasicBlock* CutGraphIntoBasicBlocks(Instruction* instructions, - const BranchTargets& branch_targets, - TargetsToBlocks* blocks); - - // Find common tail sequences of basic blocks and coalesce them. - void MergeTails(TargetsToBlocks* blocks); - - // For each basic block, compute the number of incoming branches. - void ComputeIncomingBranches(BasicBlock* block, - const TargetsToBlocks& targets_to_blocks, - IncomingBranches* incoming_branches); - - // Topologically sort the basic blocks so that all jumps are forward jumps. - // This is a requirement for any well-formed BPF program. - void TopoSortBasicBlocks(BasicBlock* first_block, - const TargetsToBlocks& blocks, - BasicBlocks* basic_blocks); - - // Convert jt_ptr_ and jf_ptr_ fields in BPF_JMP instructions to valid - // jt_ and jf_ jump offsets. This can result in BPF_JA instructions being - // inserted, if we need to jump over more than 256 instructions. - void ComputeRelativeJumps(BasicBlocks* basic_blocks, - const TargetsToBlocks& targets_to_blocks); - - // Concatenate instructions from all basic blocks into a BPF program that - // can be passed to the kernel. - void ConcatenateBasicBlocks(const BasicBlocks&, Program* program); - - // We stick all instructions and basic blocks into pools that get destroyed - // when the CodeGen object is destroyed. This way, we neither need to worry - // about explicitly managing ownership, nor do we need to worry about using - // smart pointers in the presence of circular references. - Instructions instructions_; - BasicBlocks basic_blocks_; - - // Compile() must only ever be called once as it makes destructive changes - // to the DAG. - bool compiled_; -}; - -} // namespace sandbox - -#endif // SANDBOX_LINUX_SECCOMP_BPF_CODEGEN_H__ diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc deleted file mode 100644 index 77e22381395c..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/codegen_unittest.cc +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/codegen.h" - -#include - -#include -#include -#include - -#include "sandbox/linux/seccomp-bpf/basicblock.h" -#include "sandbox/linux/seccomp-bpf/errorcode.h" -#include "sandbox/linux/seccomp-bpf/instruction.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -// We want to access some of the private methods in the code generator. We -// do so by defining a "friend" that makes these methods public for us. -class CodeGenUnittestHelper : public CodeGen { - public: - using CodeGen::CutGraphIntoBasicBlocks; - using CodeGen::FindBranchTargets; - using CodeGen::MergeTails; -}; - -namespace { - -enum { - NO_FLAGS = 0x0000, - HAS_MERGEABLE_TAILS = 0x0001, -}; - -using ProgramTestFunc = void (*)(CodeGenUnittestHelper* gen, - Instruction* head, - int flags); - -class ProgramTest : public ::testing::TestWithParam { - protected: - ProgramTest() : gen_() {} - - // RunTest runs the test function argument. It should be called at - // the end of each program test case. - void RunTest(Instruction* head, int flags) { GetParam()(&gen_, head, flags); } - - Instruction* MakeInstruction(uint16_t code, - uint32_t k, - Instruction* next = nullptr) { - Instruction* ret = gen_.MakeInstruction(code, k, next); - EXPECT_NE(nullptr, ret); - EXPECT_EQ(code, ret->code); - EXPECT_EQ(k, ret->k); - if (code == BPF_JMP + BPF_JA) { - // Annoying inconsistency. - EXPECT_EQ(nullptr, ret->next); - EXPECT_EQ(next, ret->jt_ptr); - } else { - EXPECT_EQ(next, ret->next); - EXPECT_EQ(nullptr, ret->jt_ptr); - } - EXPECT_EQ(nullptr, ret->jf_ptr); - return ret; - } - - Instruction* MakeInstruction(uint16_t code, - uint32_t k, - Instruction* jt, - Instruction* jf) { - Instruction* ret = gen_.MakeInstruction(code, k, jt, jf); - EXPECT_NE(nullptr, ret); - EXPECT_EQ(code, ret->code); - EXPECT_EQ(k, ret->k); - EXPECT_EQ(nullptr, ret->next); - EXPECT_EQ(jt, ret->jt_ptr); - EXPECT_EQ(jf, ret->jf_ptr); - return ret; - } - - private: - CodeGenUnittestHelper gen_; -}; - -TEST_P(ProgramTest, OneInstruction) { - // Create the most basic valid BPF program: - // RET 0 - Instruction* head = MakeInstruction(BPF_RET + BPF_K, 0); - RunTest(head, NO_FLAGS); -} - -TEST_P(ProgramTest, SimpleBranch) { - // Create a program with a single branch: - // JUMP if eq 42 then $0 else $1 - // 0: RET 1 - // 1: RET 0 - Instruction* head = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, - 42, - MakeInstruction(BPF_RET + BPF_K, 1), - MakeInstruction(BPF_RET + BPF_K, 0)); - RunTest(head, NO_FLAGS); -} - -TEST_P(ProgramTest, AtypicalBranch) { - // Create a program with a single branch: - // JUMP if eq 42 then $0 else $0 - // 0: RET 0 - - Instruction* ret = MakeInstruction(BPF_RET + BPF_K, 0); - Instruction* 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, NO_FLAGS); -} - -TEST_P(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+) - Instruction* insn0 = MakeInstruction(BPF_RET + BPF_K, 42); - Instruction* insn1 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 42, insn0); - Instruction* insn2 = MakeInstruction(BPF_JMP + BPF_JA, 0, insn1); - - // We explicitly duplicate instructions so that MergeTails() can coalesce - // them later. - Instruction* insn3 = MakeInstruction( - BPF_LD + BPF_W + BPF_ABS, 42, MakeInstruction(BPF_RET + BPF_K, 42)); - - Instruction* insn4 = - MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn2, insn3); - Instruction* 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). - Instruction* insn6 = - MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 42, insn5, insn4); - - RunTest(insn6, HAS_MERGEABLE_TAILS); -} - -TEST_P(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 - - Instruction* i7 = MakeInstruction(BPF_RET + BPF_K, 1); - Instruction* i6 = MakeInstruction(BPF_RET + BPF_K, 0); - Instruction* i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); - Instruction* i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5); - Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); - Instruction* i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3); - Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); - Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); - - RunTest(i0, NO_FLAGS); -} - -TEST_P(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 - - Instruction* i5 = MakeInstruction(BPF_RET + BPF_K, 1); - Instruction* i4 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i5); - Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); - Instruction* i2 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 0, i3); - Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); - Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); - - RunTest(i0, NO_FLAGS); -} - -TEST_P(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 - - Instruction* i7 = MakeInstruction(BPF_RET + BPF_K, 1); - Instruction* i6 = MakeInstruction(BPF_RET + BPF_K, 0); - Instruction* i5 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i6, i7); - Instruction* i4 = MakeInstruction(BPF_RET + BPF_K, 42); - Instruction* i3 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 2, i4, i5); - Instruction* i2 = MakeInstruction(BPF_RET + BPF_K, 42); - Instruction* i1 = MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, 1, i2, i3); - Instruction* i0 = MakeInstruction(BPF_LD + BPF_W + BPF_ABS, 1, i1); - - RunTest(i0, HAS_MERGEABLE_TAILS); -} - -void MakeInstruction(CodeGenUnittestHelper* codegen, - Instruction* program, int) { - // Nothing to do here -} - -void FindBranchTargets(CodeGenUnittestHelper* codegen, Instruction* prg, int) { - BranchTargets branch_targets; - codegen->FindBranchTargets(*prg, &branch_targets); - - // Verifying the general properties that should be true for every - // well-formed BPF program. - // Perform a depth-first traversal of the BPF program an verify that all - // targets of BPF_JMP instructions are represented in the "branch_targets". - // At the same time, compute a set of both the branch targets and all the - // instructions in the program. - std::vector stack; - std::set all_instructions; - std::set target_instructions; - BranchTargets::const_iterator end = branch_targets.end(); - for (Instruction* insn = prg;;) { - all_instructions.insert(insn); - if (BPF_CLASS(insn->code) == BPF_JMP) { - target_instructions.insert(insn->jt_ptr); - ASSERT_TRUE(insn->jt_ptr != NULL); - ASSERT_TRUE(branch_targets.find(insn->jt_ptr) != end); - if (BPF_OP(insn->code) != BPF_JA) { - target_instructions.insert(insn->jf_ptr); - ASSERT_TRUE(insn->jf_ptr != NULL); - ASSERT_TRUE(branch_targets.find(insn->jf_ptr) != end); - stack.push_back(insn->jf_ptr); - } - insn = insn->jt_ptr; - } else if (BPF_CLASS(insn->code) == BPF_RET) { - ASSERT_TRUE(insn->next == NULL); - if (stack.empty()) { - break; - } - insn = stack.back(); - stack.pop_back(); - } else { - ASSERT_TRUE(insn->next != NULL); - insn = insn->next; - } - } - ASSERT_TRUE(target_instructions.size() == branch_targets.size()); - - // We can now subtract the set of the branch targets from the set of all - // instructions. This gives us a set with the instructions that nobody - // ever jumps to. Verify that they are no included in the - // "branch_targets" that FindBranchTargets() computed for us. - Instructions non_target_instructions(all_instructions.size() - - target_instructions.size()); - set_difference(all_instructions.begin(), - all_instructions.end(), - target_instructions.begin(), - target_instructions.end(), - non_target_instructions.begin()); - for (Instructions::const_iterator iter = non_target_instructions.begin(); - iter != non_target_instructions.end(); - ++iter) { - ASSERT_TRUE(branch_targets.find(*iter) == end); - } -} - -void CutGraphIntoBasicBlocks(CodeGenUnittestHelper* codegen, - Instruction* prg, - int) { - BranchTargets branch_targets; - codegen->FindBranchTargets(*prg, &branch_targets); - TargetsToBlocks all_blocks; - BasicBlock* first_block = - codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks); - ASSERT_TRUE(first_block != NULL); - ASSERT_TRUE(first_block->instructions.size() > 0); - Instruction* first_insn = first_block->instructions[0]; - - // Basic blocks are supposed to start with a branch target and end with - // either a jump or a return instruction. It can also end, if the next - // instruction forms the beginning of a new basic block. There should be - // no other jumps or return instructions in the middle of a basic block. - for (TargetsToBlocks::const_iterator bb_iter = all_blocks.begin(); - bb_iter != all_blocks.end(); - ++bb_iter) { - BasicBlock* bb = bb_iter->second; - ASSERT_TRUE(bb != NULL); - ASSERT_TRUE(bb->instructions.size() > 0); - Instruction* insn = bb->instructions[0]; - ASSERT_TRUE(insn == first_insn || - branch_targets.find(insn) != branch_targets.end()); - for (Instructions::const_iterator insn_iter = bb->instructions.begin();;) { - insn = *insn_iter; - if (++insn_iter != bb->instructions.end()) { - ASSERT_TRUE(BPF_CLASS(insn->code) != BPF_JMP); - ASSERT_TRUE(BPF_CLASS(insn->code) != BPF_RET); - } else { - ASSERT_TRUE(BPF_CLASS(insn->code) == BPF_JMP || - BPF_CLASS(insn->code) == BPF_RET || - branch_targets.find(insn->next) != branch_targets.end()); - break; - } - ASSERT_TRUE(branch_targets.find(*insn_iter) == branch_targets.end()); - } - } -} - -void MergeTails(CodeGenUnittestHelper* codegen, Instruction* prg, int flags) { - BranchTargets branch_targets; - codegen->FindBranchTargets(*prg, &branch_targets); - TargetsToBlocks all_blocks; - BasicBlock* first_block = - codegen->CutGraphIntoBasicBlocks(prg, branch_targets, &all_blocks); - - // The shape of our graph and thus the function of our program should - // still be unchanged after we run MergeTails(). We verify this by - // serializing the graph and verifying that it is still the same. - // We also verify that at least some of the edges changed because of - // tail merging. - std::string graph[2]; - std::string edges[2]; - - // The loop executes twice. After the first run, we call MergeTails() on - // our graph. - for (int i = 0;;) { - // Traverse the entire program in depth-first order. - std::vector stack; - for (BasicBlock* bb = first_block;;) { - // Serialize the instructions in this basic block. In general, we only - // need to serialize "code" and "k"; except for a BPF_JA instruction - // where "k" isn't set. - // The stream of instructions should be unchanged after MergeTails(). - for (Instructions::const_iterator iter = bb->instructions.begin(); - iter != bb->instructions.end(); - ++iter) { - graph[i].append(reinterpret_cast(&(*iter)->code), - sizeof((*iter)->code)); - if (BPF_CLASS((*iter)->code) != BPF_JMP || - BPF_OP((*iter)->code) != BPF_JA) { - graph[i].append(reinterpret_cast(&(*iter)->k), - sizeof((*iter)->k)); - } - } - - // Also serialize the addresses the basic blocks as we encounter them. - // This will change as basic blocks are coalesed by MergeTails(). - edges[i].append(reinterpret_cast(&bb), sizeof(bb)); - - // Depth-first traversal of the graph. We only ever need to look at the - // very last instruction in the basic block, as that is the only one that - // can change code flow. - Instruction* insn = bb->instructions.back(); - if (BPF_CLASS(insn->code) == BPF_JMP) { - // For jump instructions, we need to remember the "false" branch while - // traversing the "true" branch. This is not necessary for BPF_JA which - // only has a single branch. - if (BPF_OP(insn->code) != BPF_JA) { - stack.push_back(all_blocks[insn->jf_ptr]); - } - bb = all_blocks[insn->jt_ptr]; - } else if (BPF_CLASS(insn->code) == BPF_RET) { - // After a BPF_RET, see if we need to back track. - if (stack.empty()) { - break; - } - bb = stack.back(); - stack.pop_back(); - } else { - // For "normal" instructions, just follow to the next basic block. - bb = all_blocks[insn->next]; - } - } - - // Our loop runs exactly two times. - if (++i > 1) { - break; - } - codegen->MergeTails(&all_blocks); - } - ASSERT_TRUE(graph[0] == graph[1]); - if (flags & HAS_MERGEABLE_TAILS) { - ASSERT_TRUE(edges[0] != edges[1]); - } else { - ASSERT_TRUE(edges[0] == edges[1]); - } -} - -void CompileAndCompare(CodeGenUnittestHelper* codegen, Instruction* prg, int) { - // TopoSortBasicBlocks() has internal checks that cause it to fail, if it - // detects a problem. Typically, if anything goes wrong, this looks to the - // TopoSort algorithm as if there had been cycles in the input data. - // This provides a pretty good unittest. - // We hand-crafted the program returned by SampleProgram() to exercise - // several of the more interesting code-paths. See comments in - // SampleProgram() for details. - // In addition to relying on the internal consistency checks in the compiler, - // we also serialize the graph and the resulting BPF program and compare - // them. With the exception of BPF_JA instructions that might have been - // inserted, both instruction streams should be equivalent. - // As Compile() modifies the instructions, we have to serialize the graph - // before calling Compile(). - std::string source; - Instructions source_stack; - for (const Instruction* insn = prg, *next; insn; insn = next) { - if (BPF_CLASS(insn->code) == BPF_JMP) { - if (BPF_OP(insn->code) == BPF_JA) { - // Do not serialize BPF_JA instructions (see above). - next = insn->jt_ptr; - continue; - } else { - source_stack.push_back(insn->jf_ptr); - next = insn->jt_ptr; - } - } else if (BPF_CLASS(insn->code) == BPF_RET) { - if (source_stack.empty()) { - next = NULL; - } else { - next = source_stack.back(); - source_stack.pop_back(); - } - } else { - next = insn->next; - } - // Only serialize "code" and "k". That's all the information we need to - // compare. The rest of the information is encoded in the order of - // instructions. - source.append(reinterpret_cast(&insn->code), - sizeof(insn->code)); - source.append(reinterpret_cast(&insn->k), sizeof(insn->k)); - } - - // Compile the program - CodeGen::Program bpf; - codegen->Compile(prg, &bpf); - - // Serialize the resulting BPF instructions. - std::string assembly; - std::vector assembly_stack; - for (int idx = 0; idx >= 0;) { - ASSERT_TRUE(idx < (int)bpf.size()); - struct sock_filter& insn = bpf[idx]; - if (BPF_CLASS(insn.code) == BPF_JMP) { - if (BPF_OP(insn.code) == BPF_JA) { - // Do not serialize BPF_JA instructions (see above). - idx += insn.k + 1; - continue; - } else { - assembly_stack.push_back(idx + insn.jf + 1); - idx += insn.jt + 1; - } - } else if (BPF_CLASS(insn.code) == BPF_RET) { - if (assembly_stack.empty()) { - idx = -1; - } else { - idx = assembly_stack.back(); - assembly_stack.pop_back(); - } - } else { - ++idx; - } - // Serialize the same information that we serialized before compilation. - assembly.append(reinterpret_cast(&insn.code), sizeof(insn.code)); - assembly.append(reinterpret_cast(&insn.k), sizeof(insn.k)); - } - ASSERT_TRUE(source == assembly); -} - -const ProgramTestFunc kProgramTestFuncs[] = { - MakeInstruction, - FindBranchTargets, - CutGraphIntoBasicBlocks, - MergeTails, - CompileAndCompare, -}; - -INSTANTIATE_TEST_CASE_P(CodeGen, - ProgramTest, - ::testing::ValuesIn(kProgramTestFuncs)); - -} // namespace - -} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc index 777c9d113929..3baf1f13d9e5 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc @@ -16,6 +16,8 @@ #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 { @@ -32,7 +34,10 @@ void Die::ExitGroup() { // to a defined state; but we have not way to verify whether we actually // succeeded in doing so. Nonetheless, triggering a fatal signal could help // us terminate. - signal(SIGSEGV, SIG_DFL); + 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) { } diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.cc deleted file mode 100644 index ebae130b6a64..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/errorcode.h" - -#include "sandbox/linux/seccomp-bpf/die.h" -#include "sandbox/linux/seccomp-bpf/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(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/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.h deleted file mode 100644 index fb86fe802881..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode.h +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#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. -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/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc deleted file mode 100644 index ae68b0a57d03..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/errorcode_unittest.cc +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/errorcode.h" - -#include - -#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/linux_seccomp.h" -#include "sandbox/linux/seccomp-bpf/trap.h" -#include "sandbox/linux/tests/unit_tests.h" - -namespace sandbox { - -namespace { - -class DummyPolicy : public bpf_dsl::Policy { - public: - DummyPolicy() {} - virtual ~DummyPolicy() {} - - virtual 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); - 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"); - ErrorCode e1 = compiler.Trap(NULL, "b"); - SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) + 1 == - (e1.err() & SECCOMP_RET_DATA)); - - ErrorCode e2 = compiler.Trap(NULL, "a"); - SANDBOX_ASSERT((e0.err() & SECCOMP_RET_DATA) == - (e2.err() & SECCOMP_RET_DATA)); -} - -SANDBOX_TEST(ErrorCode, Equals) { - ErrorCode e1(ErrorCode::ERR_ALLOWED); - ErrorCode e2(ErrorCode::ERR_ALLOWED); - SANDBOX_ASSERT(e1.Equals(e1)); - SANDBOX_ASSERT(e1.Equals(e2)); - SANDBOX_ASSERT(e2.Equals(e1)); - - ErrorCode e3(EPERM); - SANDBOX_ASSERT(!e1.Equals(e3)); - - DummyPolicy dummy_policy; - bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry()); - ErrorCode e4 = compiler.Trap(NULL, "a"); - ErrorCode e5 = compiler.Trap(NULL, "b"); - ErrorCode e6 = compiler.Trap(NULL, "a"); - SANDBOX_ASSERT(!e1.Equals(e4)); - SANDBOX_ASSERT(!e3.Equals(e4)); - SANDBOX_ASSERT(!e5.Equals(e4)); - SANDBOX_ASSERT( e6.Equals(e4)); -} - -SANDBOX_TEST(ErrorCode, LessThan) { - ErrorCode e1(ErrorCode::ERR_ALLOWED); - ErrorCode e2(ErrorCode::ERR_ALLOWED); - SANDBOX_ASSERT(!e1.LessThan(e1)); - SANDBOX_ASSERT(!e1.LessThan(e2)); - SANDBOX_ASSERT(!e2.LessThan(e1)); - - ErrorCode e3(EPERM); - SANDBOX_ASSERT(!e1.LessThan(e3)); - SANDBOX_ASSERT( e3.LessThan(e1)); - - DummyPolicy dummy_policy; - bpf_dsl::PolicyCompiler compiler(&dummy_policy, Trap::Registry()); - ErrorCode e4 = compiler.Trap(NULL, "a"); - ErrorCode e5 = compiler.Trap(NULL, "b"); - ErrorCode e6 = compiler.Trap(NULL, "a"); - SANDBOX_ASSERT(e1.LessThan(e4)); - SANDBOX_ASSERT(e3.LessThan(e4)); - SANDBOX_ASSERT(e4.LessThan(e5)); - SANDBOX_ASSERT(!e4.LessThan(e6)); - SANDBOX_ASSERT(!e6.LessThan(e4)); -} - -} // namespace - -} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/instruction.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/instruction.h deleted file mode 100644 index 70b7791f9122..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/instruction.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__ - -#include - -#include - -namespace sandbox { - -// The fields in this structure have the same meaning as the corresponding -// fields in "struct sock_filter". See for a lot more -// detail. -// code -- Opcode of the instruction. This is typically a bitwise -// combination BPF_XXX values. -// k -- Operand; BPF instructions take zero or one operands. Operands -// are 32bit-wide constants, if present. They can be immediate -// values (if BPF_K is present in "code_"), addresses (if BPF_ABS -// is present in "code_"), or relative jump offsets (if BPF_JMP -// and BPF_JA are present in "code_"). -// jt, jf -- all conditional jumps have a 8bit-wide jump offset that allows -// jumps of up to 256 instructions forward. Conditional jumps are -// identified by BPF_JMP in "code_", but the lack of BPF_JA. -// Conditional jumps have a "t"rue and "f"alse branch. -struct Instruction { - // Constructor for an non-jumping instruction or for an unconditional - // "always" jump. - Instruction(uint16_t c, uint32_t parm, Instruction* n) - : code(c), jt(0), jf(0), jt_ptr(NULL), jf_ptr(NULL), next(n), k(parm) {} - - // Constructor for a conditional jump instruction. - Instruction(uint16_t c, uint32_t parm, Instruction* jt, Instruction* jf) - : code(c), jt(0), jf(0), jt_ptr(jt), jf_ptr(jf), next(NULL), k(parm) {} - - uint16_t code; - - // When code generation is complete, we will have computed relative - // branch targets that are in the range 0..255. - uint8_t jt, jf; - - // While assembling the BPF program, we use pointers for branch targets. - // Once we have computed basic blocks, these pointers will be entered as - // keys in a TargetsToBlocks map and should no longer be dereferenced - // directly. - Instruction* jt_ptr, *jf_ptr; - - // While assembling the BPF program, non-jumping instructions are linked - // by the "next_" pointer. This field is no longer needed when we have - // computed basic blocks. - Instruction* next; - - uint32_t k; -}; - -} // namespace sandbox - -#endif // SANDBOX_LINUX_SECCOMP_BPF_INSTRUCTION_H__ diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index d5a5d4d983e7..b48682ba947f 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -4,391 +4,172 @@ #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 -#endif - #include -#include -#include -#include -#include +#include #include -#include -#include #include -#include -#include #include #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/bpf_dsl.h" -#include "sandbox/linux/bpf_dsl/dump_bpf.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/seccomp-bpf/codegen.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/errorcode.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" #include "sandbox/linux/seccomp-bpf/syscall.h" -#include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/seccomp-bpf/trap.h" -#include "sandbox/linux/seccomp-bpf/verifier.h" -#include "sandbox/linux/services/linux_syscalls.h" - -using sandbox::bpf_dsl::Allow; -using sandbox::bpf_dsl::Error; -using sandbox::bpf_dsl::ResultExpr; +#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 { -const int kExpectedExitCode = 100; - -#if !defined(NDEBUG) -void WriteFailedStderrSetupMessage(int out_fd) { - const char* error_string = strerror(errno); - static const char msg[] = - "You have reproduced a puzzling issue.\n" - "Please, report to crbug.com/152530!\n" - "Failed to set up stderr: "; - if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg) - 1)) > 0 && error_string && - HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 && - HANDLE_EINTR(write(out_fd, "\n", 1))) { - } -} -#endif // !defined(NDEBUG) - -// We define a really simple sandbox policy. It is just good enough for us -// to tell that the sandbox has actually been activated. -class ProbePolicy : public bpf_dsl::Policy { - public: - ProbePolicy() {} - virtual ~ProbePolicy() {} - - virtual ResultExpr EvaluateSyscall(int sysnum) const override { - switch (sysnum) { - case __NR_getpid: - // Return EPERM so that we can check that the filter actually ran. - return Error(EPERM); - case __NR_exit_group: - // Allow exit() with a non-default return code. - return Allow(); - default: - // Make everything else fail in an easily recognizable way. - return Error(EINVAL); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(ProbePolicy); -}; - -void ProbeProcess(void) { - if (syscall(__NR_getpid) < 0 && errno == EPERM) { - syscall(__NR_exit_group, static_cast(kExpectedExitCode)); - } -} - -class AllowAllPolicy : public bpf_dsl::Policy { - public: - AllowAllPolicy() {} - virtual ~AllowAllPolicy() {} - - virtual ResultExpr EvaluateSyscall(int sysnum) const override { - DCHECK(SandboxBPF::IsValidSyscallNumber(sysnum)); - return Allow(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy); -}; - -void TryVsyscallProcess(void) { - time_t current_time; - // time() is implemented as a vsyscall. With an older glibc, with - // vsyscall=emulate and some versions of the seccomp BPF patch - // we may get SIGKILL-ed. Detect this! - if (time(¤t_time) != static_cast(-1)) { - syscall(__NR_exit_group, static_cast(kExpectedExitCode)); - } -} +bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } bool IsSingleThreaded(int proc_fd) { - if (proc_fd < 0) { - // Cannot determine whether program is single-threaded. Hope for - // the best... + 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; +} - struct stat sb; - int task = -1; - if ((task = openat(proc_fd, "self/task", O_RDONLY | O_DIRECTORY)) < 0 || - fstat(task, &sb) != 0 || sb.st_nlink != 3 || IGNORE_EINTR(close(task))) { - if (task >= 0) { - if (IGNORE_EINTR(close(task))) { - } - } +// 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; } - return true; + + 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(static_cast(rv)); +} + +intptr_t SandboxPanicTrap(const struct arch_seccomp_data&, void* aux) { + SANDBOX_DIE(static_cast(aux)); +} + +bpf_dsl::ResultExpr SandboxPanic(const char* error) { + return bpf_dsl::Trap(SandboxPanicTrap, error); } } // namespace -SandboxBPF::SandboxBPF() - : quiet_(false), proc_fd_(-1), sandbox_has_started_(false), policy_() { +SandboxBPF::SandboxBPF(bpf_dsl::Policy* policy) + : proc_fd_(), sandbox_has_started_(false), policy_(policy) { } SandboxBPF::~SandboxBPF() { } -bool SandboxBPF::IsValidSyscallNumber(int sysnum) { - return SyscallSet::IsValid(sysnum); -} - -bool SandboxBPF::RunFunctionInPolicy(void (*code_in_sandbox)(), - scoped_ptr policy) { - // Block all signals before forking a child process. This prevents an - // attacker from manipulating our test by sending us an unexpected signal. - sigset_t old_mask, new_mask; - if (sigfillset(&new_mask) || sigprocmask(SIG_BLOCK, &new_mask, &old_mask)) { - SANDBOX_DIE("sigprocmask() failed"); - } - int fds[2]; - if (pipe2(fds, O_NONBLOCK | O_CLOEXEC)) { - SANDBOX_DIE("pipe() failed"); - } - - if (fds[0] <= 2 || fds[1] <= 2) { - SANDBOX_DIE("Process started without standard file descriptors"); - } - - // This code is using fork() and should only ever run single-threaded. - // Most of the code below is "async-signal-safe" and only minor changes - // would be needed to support threads. - DCHECK(IsSingleThreaded(proc_fd_)); - pid_t pid = fork(); - if (pid < 0) { - // Die if we cannot fork(). We would probably fail a little later - // anyway, as the machine is likely very close to running out of - // memory. - // But what we don't want to do is return "false", as a crafty - // attacker might cause fork() to fail at will and could trick us - // into running without a sandbox. - sigprocmask(SIG_SETMASK, &old_mask, NULL); // OK, if it fails - SANDBOX_DIE("fork() failed unexpectedly"); - } - - // In the child process - if (!pid) { - // Test a very simple sandbox policy to verify that we can - // successfully turn on sandboxing. - Die::EnableSimpleExit(); - - errno = 0; - if (IGNORE_EINTR(close(fds[0]))) { - // This call to close() has been failing in strange ways. See - // crbug.com/152530. So we only fail in debug mode now. -#if !defined(NDEBUG) - WriteFailedStderrSetupMessage(fds[1]); - SANDBOX_DIE(NULL); -#endif - } - if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) { - // Stderr could very well be a file descriptor to .xsession-errors, or - // another file, which could be backed by a file system that could cause - // dup2 to fail while trying to close stderr. It's important that we do - // not fail on trying to close stderr. - // If dup2 fails here, we will continue normally, this means that our - // parent won't cause a fatal failure if something writes to stderr in - // this child. -#if !defined(NDEBUG) - // In DEBUG builds, we still want to get a report. - WriteFailedStderrSetupMessage(fds[1]); - SANDBOX_DIE(NULL); -#endif - } - if (IGNORE_EINTR(close(fds[1]))) { - // This call to close() has been failing in strange ways. See - // crbug.com/152530. So we only fail in debug mode now. -#if !defined(NDEBUG) - WriteFailedStderrSetupMessage(fds[1]); - SANDBOX_DIE(NULL); -#endif - } - - SetSandboxPolicy(policy.release()); - if (!StartSandbox(PROCESS_SINGLE_THREADED)) { - SANDBOX_DIE(NULL); - } - - // Run our code in the sandbox. - code_in_sandbox(); - - // code_in_sandbox() is not supposed to return here. - SANDBOX_DIE(NULL); - } - - // In the parent process. - if (IGNORE_EINTR(close(fds[1]))) { - SANDBOX_DIE("close() failed"); - } - if (sigprocmask(SIG_SETMASK, &old_mask, NULL)) { - SANDBOX_DIE("sigprocmask() failed"); - } - int status; - if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) { - SANDBOX_DIE("waitpid() failed unexpectedly"); - } - bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode; - - // If we fail to support sandboxing, there might be an additional - // error message. If so, this was an entirely unexpected and fatal - // failure. We should report the failure and somebody must fix - // things. This is probably a security-critical bug in the sandboxing - // code. - if (!rc) { - char buf[4096]; - ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); - if (len > 0) { - while (len > 1 && buf[len - 1] == '\n') { - --len; - } - buf[len] = '\000'; - SANDBOX_DIE(buf); - } - } - if (IGNORE_EINTR(close(fds[0]))) { - SANDBOX_DIE("close() failed"); - } - - return rc; -} - -bool SandboxBPF::KernelSupportSeccompBPF() { - return RunFunctionInPolicy(ProbeProcess, - scoped_ptr(new ProbePolicy())) && - RunFunctionInPolicy(TryVsyscallProcess, - scoped_ptr(new AllowAllPolicy())); -} - // static -SandboxBPF::SandboxStatus SandboxBPF::SupportsSeccompSandbox(int proc_fd) { - // It the sandbox is currently active, we clearly must have support for - // sandboxing. - if (status_ == STATUS_ENABLED) { - return status_; - } - - // Even if the sandbox was previously available, something might have - // changed in our run-time environment. Check one more time. - if (status_ == STATUS_AVAILABLE) { - if (!IsSingleThreaded(proc_fd)) { - status_ = STATUS_UNAVAILABLE; - } - return status_; - } - - if (status_ == STATUS_UNAVAILABLE && IsSingleThreaded(proc_fd)) { - // All state transitions resulting in STATUS_UNAVAILABLE are immediately - // preceded by STATUS_AVAILABLE. Furthermore, these transitions all - // happen, if and only if they are triggered by the process being multi- - // threaded. - // In other words, if a single-threaded process is currently in the - // STATUS_UNAVAILABLE state, it is safe to assume that sandboxing is - // actually available. - status_ = STATUS_AVAILABLE; - return status_; - } - - // If we have not previously checked for availability of the sandbox or if - // we otherwise don't believe to have a good cached value, we have to - // perform a thorough check now. - if (status_ == STATUS_UNKNOWN) { - // We create our own private copy of a "Sandbox" object. This ensures that - // the object does not have any policies configured, that might interfere - // with the tests done by "KernelSupportSeccompBPF()". - SandboxBPF sandbox; - - // By setting "quiet_ = true" we suppress messages for expected and benign - // failures (e.g. if the current kernel lacks support for BPF filters). - sandbox.quiet_ = true; - sandbox.set_proc_fd(proc_fd); - status_ = sandbox.KernelSupportSeccompBPF() ? STATUS_AVAILABLE - : STATUS_UNSUPPORTED; - - // As we are performing our tests from a child process, the run-time - // environment that is visible to the sandbox is always guaranteed to be - // single-threaded. Let's check here whether the caller is single- - // threaded. Otherwise, we mark the sandbox as temporarily unavailable. - if (status_ == STATUS_AVAILABLE && !IsSingleThreaded(proc_fd)) { - status_ = STATUS_UNAVAILABLE; - } - } - return status_; -} - -// static -SandboxBPF::SandboxStatus -SandboxBPF::SupportsSeccompThreadFilterSynchronization() { - // Applying NO_NEW_PRIVS, a BPF filter, and synchronizing the filter across - // the thread group are all handled atomically by this syscall. - const int rv = syscall( - __NR_seccomp, SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, NULL); - - if (rv == -1 && errno == EFAULT) { - return STATUS_AVAILABLE; - } else { - // TODO(jln): turn these into DCHECK after 417888 is considered fixed. - CHECK_EQ(-1, rv); - CHECK(ENOSYS == errno || EINVAL == errno); - return STATUS_UNSUPPORTED; - } -} - -void SandboxBPF::set_proc_fd(int proc_fd) { proc_fd_ = proc_fd; } - -bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { - CHECK(thread_state == PROCESS_SINGLE_THREADED || - thread_state == PROCESS_MULTI_THREADED); - - if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { - SANDBOX_DIE( - "Trying to start sandbox, even though it is known to be " - "unavailable"); +bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) { + // Never pretend to support seccomp with Valgrind, as it + // throws the tool off. + if (IsRunningOnValgrind()) { return false; - } else if (sandbox_has_started_) { + } + + 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_ < 0) { - proc_fd_ = open("/proc", O_RDONLY | O_DIRECTORY); - } - if (proc_fd_ < 0) { - // For now, continue in degraded mode, if we can't access /proc. - // In the future, we might want to tighten this requirement. + + if (!proc_fd_.is_valid()) { + SetProcFd(ProcUtil::OpenProc()); } - bool supports_tsync = - SupportsSeccompThreadFilterSynchronization() == STATUS_AVAILABLE; + const bool supports_tsync = KernelSupportsSeccompTsync(); - if (thread_state == PROCESS_SINGLE_THREADED) { - if (!IsSingleThreaded(proc_fd_)) { - SANDBOX_DIE("Cannot start sandbox; process is already multi-threaded"); - return false; - } - } else if (thread_state == PROCESS_MULTI_THREADED) { - if (IsSingleThreaded(proc_fd_)) { + 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; @@ -403,30 +184,49 @@ bool SandboxBPF::StartSandbox(SandboxThreadState thread_state) { // We no longer need access to any files in /proc. We want to do this // before installing the filters, just in case that our policy denies // close(). - if (proc_fd_ >= 0) { - if (IGNORE_EINTR(close(proc_fd_))) { - SANDBOX_DIE("Failed to close file descriptor for /proc"); - return false; - } - proc_fd_ = -1; + if (proc_fd_.is_valid()) { + proc_fd_.reset(); } // Install the filters. - InstallFilter(supports_tsync || thread_state == PROCESS_MULTI_THREADED); - - // We are now inside the sandbox. - status_ = STATUS_ENABLED; + InstallFilter(supports_tsync || + seccomp_level == SeccompLevel::MULTI_THREADED); return true; } -// Don't take a scoped_ptr here, polymorphism make their use awkward. -void SandboxBPF::SetSandboxPolicy(bpf_dsl::Policy* policy) { - DCHECK(!policy_); - if (sandbox_has_started_) { - SANDBOX_DIE("Cannot change policy after sandbox has started"); +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(args.args[0]), + static_cast(args.args[1]), static_cast(args.args[2]), + static_cast(args.args[3]), static_cast(args.args[4]), + static_cast(args.args[5])); +} + +CodeGen::Program SandboxBPF::AssembleFilter() { + DCHECK(policy_); + + bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry()); + if (Trap::SandboxDebuggingAllowedByUser()) { + compiler.DangerousSetEscapePC(EscapePC()); } - policy_.reset(policy); + compiler.SetPanicFunc(SandboxPanic); + return compiler.Compile(); } void SandboxBPF::InstallFilter(bool must_sync_threads) { @@ -441,13 +241,13 @@ void SandboxBPF::InstallFilter(bool must_sync_threads) { // 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(); + CodeGen::Program program = AssembleFilter(); - struct sock_filter bpf[program->size()]; - const struct sock_fprog prog = {static_cast(program->size()), + struct sock_filter bpf[program.size()]; + const struct sock_fprog prog = {static_cast(program.size()), bpf}; - memcpy(bpf, &(*program)[0], sizeof(bpf)); - delete program; + memcpy(bpf, &program[0], sizeof(bpf)); + CodeGen::Program().swap(program); // vector swap trick // 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 @@ -455,69 +255,26 @@ void SandboxBPF::InstallFilter(bool must_sync_threads) { policy_.reset(); if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { - SANDBOX_DIE(quiet_ ? NULL : "Kernel refuses to enable no-new-privs"); + 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 = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, - SECCOMP_FILTER_FLAG_TSYNC, reinterpret_cast(&prog)); + int rv = + sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog); if (rv) { - SANDBOX_DIE(quiet_ ? NULL : + 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(quiet_ ? NULL : "Kernel refuses to turn on BPF filters"); + SANDBOX_DIE("Kernel refuses to turn on BPF filters"); } } sandbox_has_started_ = true; } -scoped_ptr SandboxBPF::AssembleFilter( - bool force_verification) { -#if !defined(NDEBUG) - force_verification = true; -#endif - - bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry()); - scoped_ptr program = compiler.Compile(); - - // Make sure compilation resulted in BPF program that executes - // correctly. Otherwise, there is an internal error in our BPF compiler. - // There is really nothing the caller can do until the bug is fixed. - if (force_verification) { - // Verification is expensive. We only perform this step, if we are - // compiled in debug mode, or if the caller explicitly requested - // verification. - - const char* err = NULL; - if (!Verifier::VerifyBPF(&compiler, *program, *policy_, &err)) { - bpf_dsl::DumpBPF::PrintProgram(*program); - SANDBOX_DIE(err); - } - } - - return program.Pass(); -} - -bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) { - return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno); -} - -intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) { - return Syscall::Call(args.nr, - static_cast(args.args[0]), - static_cast(args.args[1]), - static_cast(args.args[2]), - static_cast(args.args[3]), - static_cast(args.args[4]), - static_cast(args.args[5])); -} - -SandboxBPF::SandboxStatus SandboxBPF::status_ = STATUS_UNKNOWN; - } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h index a7f2d5ff5c52..e758e0312140 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -2,15 +2,15 @@ // 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__ +#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_ #include -#include "base/compiler_specific.h" +#include "base/files/scoped_file.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "sandbox/linux/seccomp-bpf/codegen.h" +#include "sandbox/linux/bpf_dsl/codegen.h" #include "sandbox/sandbox_export.h" namespace sandbox { @@ -19,67 +19,63 @@ 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 SandboxStatus { - STATUS_UNKNOWN, // Status prior to calling supportsSeccompSandbox() - STATUS_UNSUPPORTED, // The kernel does not appear to support sandboxing - STATUS_UNAVAILABLE, // Currently unavailable but might work again later - STATUS_AVAILABLE, // Sandboxing is available but not currently active - STATUS_ENABLED // The sandbox is now active + enum class SeccompLevel { + SINGLE_THREADED, + MULTI_THREADED, }; - // Depending on the level of kernel support, seccomp-bpf may require the - // process to be single-threaded in order to enable it. When calling - // StartSandbox(), the program should indicate whether or not the sandbox - // should try and engage with multi-thread support. - enum SandboxThreadState { - PROCESS_INVALID, - PROCESS_SINGLE_THREADED, // The program is currently single-threaded. - // Note: PROCESS_MULTI_THREADED requires experimental kernel support that - // has not been contributed to upstream Linux. - PROCESS_MULTI_THREADED, // The program may be multi-threaded. - }; - - // Constructors and destructors. + // 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. Strictly speaking, that means we should disallow calling - // the destructor, if StartSandbox() has ever been called. In practice, - // this makes it needlessly complicated to operate on "Sandbox" - // objects. So, we instead opted to allow object destruction. But it - // should be noted that during its lifetime, the object probably made - // irreversible state changes to the runtime environment. These changes - // stay in effect even after the destructor has been run. - SandboxBPF(); + // The kernel does not provide any option for unloading a loaded sandbox. The + // sandbox remains engaged even when the object is destructed. ~SandboxBPF(); - // Checks whether a particular system call number is valid on the current - // architecture. E.g. on ARM there's a non-contiguous range of private - // system calls. - static bool IsValidSyscallNumber(int sysnum); + // Detect if the kernel supports the specified seccomp level. + // See StartSandbox() for a description of these. + static bool SupportsSeccompSandbox(SeccompLevel level); - // There are a lot of reasons why the Seccomp sandbox might not be available. - // This could be because the kernel does not support Seccomp mode, or it - // could be because another sandbox is already active. - // "proc_fd" should be a file descriptor for "/proc", or -1 if not - // provided by the caller. - static SandboxStatus SupportsSeccompSandbox(int proc_fd); + // 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; - // Determines if the kernel has support for the seccomp() system call to - // synchronize BPF filters across a thread group. - static SandboxStatus SupportsSeccompThreadFilterSynchronization(); - - // The sandbox needs to be able to access files in "/proc/self". If this - // directory is not accessible when "startSandbox()" gets called, the caller - // can provide an already opened file descriptor by calling "set_proc_fd()". + // The sandbox 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 - // eventually close it when "StartSandbox()" executes. - void set_proc_fd(int proc_fd); + // close it when "StartSandbox()" executes or when the sandbox object + // disappears. + void SetProcFd(base::ScopedFD proc_fd); - // Set the BPF policy as |policy|. Ownership of |policy| is transfered here - // to the sandbox object. - void SetSandboxPolicy(bpf_dsl::Policy* policy); + // 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. @@ -95,60 +91,18 @@ class SANDBOX_EXPORT SandboxBPF { // directly suitable as a return value for a trap handler. static intptr_t ForwardSyscall(const struct arch_seccomp_data& args); - // This is the main public entry point. It finds all system calls that - // need rewriting, sets up the resources needed by the sandbox, and - // enters Seccomp mode. - // The calling process must specify its current SandboxThreadState, as a way - // to tell the sandbox which type of kernel support it should engage. - // It is possible to stack multiple sandboxes by creating separate "Sandbox" - // objects and calling "StartSandbox()" on each of them. Please note, that - // this requires special care, though, as newly stacked sandboxes can never - // 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(SandboxThreadState thread_state) WARN_UNUSED_RESULT; + private: + friend class SandboxBPFTestRunner; // 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 AssembleFilter(bool force_verification); - - private: - // Get a file descriptor pointing to "/proc", if currently available. - int proc_fd() { return proc_fd_; } - - // Creates a subprocess and runs "code_in_sandbox" inside of the specified - // policy. The caller has to make sure that "this" has not yet been - // initialized with any other policies. - bool RunFunctionInPolicy(void (*code_in_sandbox)(), - scoped_ptr policy); - - // Performs a couple of sanity checks to verify that the kernel supports the - // features that we need for successful sandboxing. - // The caller has to make sure that "this" has not yet been initialized with - // any other policies. - bool KernelSupportSeccompBPF(); + CodeGen::Program AssembleFilter(); // Assembles and installs a filter based on the policy that has previously // been configured with SetSandboxPolicy(). void InstallFilter(bool must_sync_threads); - // Verify the correctness of a compiled program by comparing it against the - // current policy. This function should only ever be called by unit tests and - // by the sandbox internals. It should not be used by production code. - void VerifyProgram(const CodeGen::Program& program); - - static SandboxStatus status_; - - bool quiet_; - int proc_fd_; + base::ScopedFD proc_fd_; bool sandbox_has_started_; scoped_ptr policy_; @@ -157,4 +111,4 @@ class SANDBOX_EXPORT SandboxBPF { } // namespace sandbox -#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H__ +#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_ diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc index 59ee26f6ed41..c0193f9568fe 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc @@ -5,13 +5,13 @@ #include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h" #include -#include #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 { @@ -31,20 +31,12 @@ void SandboxBPFTestRunner::Run() { scoped_ptr policy = bpf_tester_delegate_->GetSandboxBPFPolicy(); - if (sandbox::SandboxBPF::SupportsSeccompSandbox(-1) == - sandbox::SandboxBPF::STATUS_AVAILABLE) { - // Ensure the the sandbox is actually available at this time - int proc_fd; - SANDBOX_ASSERT((proc_fd = open("/proc", O_RDONLY | O_DIRECTORY)) >= 0); - SANDBOX_ASSERT(sandbox::SandboxBPF::SupportsSeccompSandbox(proc_fd) == - sandbox::SandboxBPF::STATUS_AVAILABLE); - + if (sandbox::SandboxBPF::SupportsSeccompSandbox( + SandboxBPF::SeccompLevel::SINGLE_THREADED)) { // Initialize and then start the sandbox with our custom policy - sandbox::SandboxBPF sandbox; - sandbox.set_proc_fd(proc_fd); - sandbox.SetSandboxPolicy(policy.release()); - SANDBOX_ASSERT( - sandbox.StartSandbox(sandbox::SandboxBPF::PROCESS_SINGLE_THREADED)); + sandbox::SandboxBPF sandbox(policy.release()); + SANDBOX_ASSERT(sandbox.StartSandbox( + sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED)); // Run the actual test. bpf_tester_delegate_->RunTestFunction(); @@ -58,9 +50,8 @@ void SandboxBPFTestRunner::Run() { } // Call the compiler and verify the policy. That's the least we can do, // if we don't have kernel support. - sandbox::SandboxBPF sandbox; - sandbox.SetSandboxPolicy(policy.release()); - sandbox.AssembleFilter(true /* force_verification */); + sandbox::SandboxBPF sandbox(policy.release()); + sandbox.AssembleFilter(); sandbox::UnitTests::IgnoreThisTest(); } } diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h index 5d6b37460071..fef6240d74ee 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h @@ -45,11 +45,11 @@ class SandboxBPFTestRunner : public SandboxTestRunner { // This constructor takes ownership of the |bpf_tester_delegate| object. // (It doesn't take a scoped_ptr since they make polymorphism verbose). explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate); - virtual ~SandboxBPFTestRunner(); + ~SandboxBPFTestRunner() override; - virtual void Run() override; + void Run() override; - virtual bool ShouldCheckForLeaks() const override; + bool ShouldCheckForLeaks() const override; private: scoped_ptr bpf_tester_delegate_; diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc index 57f6bafb9d89..bc6461f11773 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc @@ -4,12 +4,11 @@ #include "sandbox/linux/seccomp-bpf/syscall.h" -#include #include +#include -#include "base/basictypes.h" #include "base/logging.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" +#include "sandbox/linux/bpf_dsl/seccomp_macros.h" namespace sandbox { @@ -134,16 +133,21 @@ asm(// We need to be able to tell the kernel exactly where we made a #else ".arm\n" #endif - "SyscallAsm:.fnstart\n" + "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" - "mov r7, sp\n" - ".cfi_def_cfa_register 7\n" ".cfi_def_cfa_offset 8\n" #else "stmfd sp!, {fp, lr}\n" @@ -178,7 +182,11 @@ asm(// We need to be able to tell the kernel exactly where we made a #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" @@ -279,8 +287,8 @@ intptr_t Syscall::Call(int nr, // that this would only be an issue for IA64, which we are currently not // planning on supporting. And it is even possible that this would work // on IA64, but for lack of actual hardware, I cannot test. - COMPILE_ASSERT(sizeof(void*) == sizeof(intptr_t), - pointer_types_and_intptr_must_be_exactly_the_same_size); + 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. diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h index 3686df5cb640..ccfc88dcb30e 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h @@ -9,13 +9,9 @@ #include #include "base/macros.h" +#include "sandbox/linux/system_headers/linux_signal.h" #include "sandbox/sandbox_export.h" -// Android's signal.h doesn't define ucontext etc. -#if defined(OS_ANDROID) -#include "sandbox/linux/services/android_ucontext.h" -#endif - namespace sandbox { // This purely static class can be used to perform system calls with some diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc deleted file mode 100644 index 3bc1eaa5b75a..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_iterator_unittest.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/syscall_iterator.h" - -#include - -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/tests/unit_tests.h" - -namespace sandbox { - -namespace { - -const SyscallSet kSyscallSets[] = { - SyscallSet::All(), - SyscallSet::InvalidOnly(), -}; - -SANDBOX_TEST(SyscallIterator, 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(SyscallIterator, 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(SyscallIterator, 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(SyscallIterator, ValidOnlyIsOnlyValid) { - for (uint32_t sysnum : SyscallSet::ValidOnly()) { - SANDBOX_ASSERT(SyscallSet::IsValid(sysnum)); - } -} - -SANDBOX_TEST(SyscallIterator, InvalidOnlyIsOnlyInvalid) { - for (uint32_t sysnum : SyscallSet::InvalidOnly()) { - SANDBOX_ASSERT(!SyscallSet::IsValid(sysnum)); - } -} - -SANDBOX_TEST(SyscallIterator, AllIsValidOnlyPlusInvalidOnly) { - std::vector 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(all.begin(), all.end())); -} - -} // namespace - -} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc index 0b685c3b4f07..01336f9b62c1 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc @@ -5,7 +5,10 @@ #include "sandbox/linux/seccomp-bpf/syscall.h" #include +#include #include +#include +#include #include #include #include @@ -13,7 +16,7 @@ #include -#include "base/basictypes.h" +#include "base/macros.h" #include "base/posix/eintr_wrapper.h" #include "build/build_config.h" #include "sandbox/linux/bpf_dsl/bpf_dsl.h" @@ -108,9 +111,9 @@ intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) { class CopyAllArgsOnUnamePolicy : public bpf_dsl::Policy { public: explicit CopyAllArgsOnUnamePolicy(std::vector* aux) : aux_(aux) {} - virtual ~CopyAllArgsOnUnamePolicy() {} + ~CopyAllArgsOnUnamePolicy() override {} - virtual ResultExpr EvaluateSyscall(int sysno) const override { + ResultExpr EvaluateSyscall(int sysno) const override { DCHECK(SandboxBPF::IsValidSyscallNumber(sysno)); if (sysno == __NR_uname) { return Trap(CopySyscallArgsToAux, aux_); diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc index dce6b7b85fd8..003708d2c898 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc @@ -6,22 +6,24 @@ #include #include +#include +#include #include #include #include #include +#include +#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/linux_seccomp.h" #include "sandbox/linux/seccomp-bpf/syscall.h" - -// Android's signal.h doesn't define ucontext etc. -#if defined(OS_ANDROID) -#include "sandbox/linux/services/android_ucontext.h" -#endif +#include "sandbox/linux/services/syscall_wrappers.h" +#include "sandbox/linux/system_headers/linux_seccomp.h" +#include "sandbox/linux/system_headers/linux_signal.h" namespace { @@ -52,13 +54,13 @@ const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING"; // possibly even worse. bool GetIsInSigHandler(const ucontext_t* ctx) { // Note: on Android, sigismember does not take a pointer to const. - return sigismember(const_cast(&ctx->uc_sigmask), SIGBUS); + return sigismember(const_cast(&ctx->uc_sigmask), LINUX_SIGBUS); } void SetIsInSigHandler() { sigset_t mask; - if (sigemptyset(&mask) || sigaddset(&mask, SIGBUS) || - sigprocmask(SIG_BLOCK, &mask, NULL)) { + if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGBUS) || + sandbox::sys_sigprocmask(LINUX_SIG_BLOCK, &mask, NULL)) { SANDBOX_DIE("Failed to block SIGBUS"); } } @@ -81,10 +83,13 @@ Trap::Trap() has_unsafe_traps_(false) { // Set new SIGSYS handler struct sigaction sa = {}; - sa.sa_sigaction = SigSysAction; - sa.sa_flags = SA_SIGINFO | SA_NODEFER; - struct sigaction old_sa; - if (sigaction(SIGSYS, &sa, &old_sa) < 0) { + // 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(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"); } @@ -98,8 +103,8 @@ Trap::Trap() // Unmask SIGSYS sigset_t mask; - if (sigemptyset(&mask) || sigaddset(&mask, SIGSYS) || - sigprocmask(SIG_UNBLOCK, &mask, NULL)) { + if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGSYS) || + sys_sigprocmask(LINUX_SIG_UNBLOCK, &mask, NULL)) { SANDBOX_DIE("Failed to configure SIGSYS handler"); } } @@ -119,16 +124,27 @@ bpf_dsl::TrapRegistry* Trap::Registry() { return global_trap_; } -void Trap::SigSysAction(int nr, siginfo_t* info, void* void_context) { +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(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, void_context); + global_trap_->SigSys(nr, info, ctx); } -void Trap::SigSys(int nr, siginfo_t* info, void* void_context) { +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; @@ -136,7 +152,7 @@ void Trap::SigSys(int nr, siginfo_t* info, void* void_context) { // Various sanity checks to make sure we actually received a signal // triggered by a BPF filter. If something else triggered SIGSYS // (e.g. kill()), there is really nothing we can do with this signal. - if (nr != SIGSYS || info->si_code != SYS_SECCOMP || !void_context || + if (nr != LINUX_SIGSYS || info->si_code != SYS_SECCOMP || !ctx || info->si_errno <= 0 || static_cast(info->si_errno) > trap_array_size_) { // ATI drivers seem to send SIGSYS, so this cannot be FATAL. @@ -147,9 +163,6 @@ void Trap::SigSys(int nr, siginfo_t* info, void* void_context) { return; } - // Obtain the signal context. This, most notably, gives us access to - // all CPU registers at the time of the signal. - ucontext_t* ctx = reinterpret_cast(void_context); // Obtain the siginfo information that is specific to SIGSYS. Unfortunately, // most versions of glibc don't include this information in siginfo_t. So, @@ -241,17 +254,7 @@ void Trap::SigSys(int nr, siginfo_t* info, void* void_context) { } 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::MakeTrap(TrapFnc fnc, const void* aux, bool safe) { - return Registry()->Add(fnc, aux, safe); + return std::tie(fnc, aux, safe) < std::tie(o.fnc, o.aux, o.safe); } uint16_t Trap::Add(TrapFnc fnc, const void* aux, bool safe) { @@ -352,15 +355,11 @@ uint16_t Trap::Add(TrapFnc fnc, const void* aux, bool safe) { return id; } -bool Trap::SandboxDebuggingAllowedByUser() const { +bool Trap::SandboxDebuggingAllowedByUser() { const char* debug_flag = getenv(kSandboxDebuggingEnv); return debug_flag && *debug_flag; } -bool Trap::EnableUnsafeTrapsInSigSysHandler() { - return Registry()->EnableUnsafeTraps(); -} - bool Trap::EnableUnsafeTraps() { if (!has_unsafe_traps_) { // Unsafe traps are a one-way fuse. Once enabled, they can never be turned diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h index 0a184e6b6ac6..a73d2064b475 100644 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h +++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h @@ -5,13 +5,14 @@ #ifndef SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__ #define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__ -#include +#include #include #include #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 { @@ -26,31 +27,17 @@ namespace sandbox { // true. Threads are incompatible with the seccomp sandbox anyway. class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry { public: - virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override; + uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override; - virtual bool EnableUnsafeTraps() override; + bool EnableUnsafeTraps() override; // Registry returns the trap registry used by Trap's SIGSYS handler, // creating it if necessary. static bpf_dsl::TrapRegistry* Registry(); - // Registers a new trap handler and sets up the appropriate SIGSYS handler - // as needed. - // N.B.: This makes a permanent state change. Traps cannot be unregistered, - // as that would break existing BPF filters that are still active. - // TODO(mdempsky): Deprecated; remove. - static uint16_t MakeTrap(TrapFnc fnc, const void* aux, bool safe); - - // Enables support for unsafe traps in the SIGSYS signal handler. This is a - // one-way fuse. It works in conjunction with the BPF compiler emitting code - // that unconditionally allows system calls, if they have a magic return - // address (i.e. SandboxSyscall(-1)). - // Once unsafe traps are enabled, the sandbox is essentially compromised. - // But this is still a very useful feature for debugging purposes. Use with - // care. This feature is availably only if enabled by the user (see above). - // Returns "true", if unsafe traps were turned on. - // TODO(mdempsky): Deprecated; remove. - static bool EnableUnsafeTrapsInSigSysHandler(); + // SandboxDebuggingAllowedByUser returns whether the + // "CHROME_SANDBOX_DEBUGGING" environment variable is set. + static bool SandboxDebuggingAllowedByUser(); private: struct TrapKey { @@ -67,18 +54,16 @@ class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry { // automatically as needed. Trap(); - // The destructor is unimplemented. Don't ever attempt to destruct this - // object. It'll break subsequent system calls that trigger a SIGSYS. - ~Trap(); + // The destructor is unimplemented as destroying this object would + // break subsequent system calls that trigger a SIGSYS. + ~Trap() = delete; - static void SigSysAction(int nr, siginfo_t* info, void* void_context); + 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, siginfo_t* info, void* void_context) + void SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx) __attribute__((noinline)); - bool SandboxDebuggingAllowedByUser() const; - // We have a global singleton that handles all of our SIGSYS traps. This // variable must never be deallocated after it has been set up initially, as // there is no way to reset in-kernel BPF filters that generate SIGSYS diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.cc deleted file mode 100644 index 548df25a8c2f..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.cc +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/seccomp-bpf/verifier.h" - -#include - -#include - -#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/seccomp-bpf/errorcode.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" -#include "sandbox/linux/seccomp-bpf/syscall_iterator.h" - -namespace sandbox { - -namespace { - -const uint64_t kLower32Bits = std::numeric_limits::max(); -const uint64_t kUpper32Bits = static_cast(kLower32Bits) << 32; -const uint64_t kFull64Bits = std::numeric_limits::max(); - -struct State { - State(const std::vector& p, - const struct arch_seccomp_data& d) - : program(p), data(d), ip(0), accumulator(0), acc_is_valid(false) {} - const std::vector& 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& program, - struct arch_seccomp_data* data, - const ErrorCode& root_code, - const ErrorCode& code, - const char** err) { - if (code.error_type() == ErrorCode::ET_SIMPLE || - code.error_type() == ErrorCode::ET_TRAP) { - uint32_t computed_ret = Verifier::EvaluateBPF(program, *data, err); - if (*err) { - return false; - } else if (computed_ret != EvaluateErrorCode(compiler, root_code, *data)) { - // For efficiency's sake, we'd much rather compare "computed_ret" - // against "code.err()". This works most of the time, but it doesn't - // always work for nested conditional expressions. The test values - // that we generate on the fly to probe expressions can trigger - // code flow decisions in multiple nodes of the decision tree, and the - // only way to compute the correct error code in that situation is by - // calling EvaluateErrorCode(). - *err = "Exit code from BPF program doesn't match"; - return false; - } - } else if (code.error_type() == ErrorCode::ET_COND) { - if (code.argno() < 0 || code.argno() >= 6) { - *err = "Invalid argument number in error code"; - return false; - } - - // 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(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(&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& 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(sysnum), - static_cast(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& 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 sandbox diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.h deleted file mode 100644 index 6bd747bd7534..000000000000 --- a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/verifier.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__ -#define SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__ - -#include - -#include - -#include "base/macros.h" - -struct sock_filter; - -namespace sandbox { -struct arch_seccomp_data; -namespace bpf_dsl { -class Policy; -class PolicyCompiler; -} - -class Verifier { - public: - // Evaluate the BPF program for all possible inputs and verify that it - // computes the correct result. We use the "evaluators" to determine - // the full set of possible inputs that we have to iterate over. - // Returns success, if the BPF filter accurately reflects the rules - // set by the "evaluators". - // Upon success, "err" is set to NULL. Upon failure, it contains a static - // error message that does not need to be free()'d. - static bool VerifyBPF(bpf_dsl::PolicyCompiler* compiler, - const std::vector& 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& program, - const struct arch_seccomp_data& data, - const char** err); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(Verifier); -}; - -} // namespace sandbox - -#endif // SANDBOX_LINUX_SECCOMP_BPF_VERIFIER_H__ diff --git a/security/sandbox/chromium/sandbox/linux/services/android_arm_ucontext.h b/security/sandbox/chromium/sandbox/linux/services/android_arm_ucontext.h deleted file mode 100644 index d1446c67bd75..000000000000 --- a/security/sandbox/chromium/sandbox/linux/services/android_arm_ucontext.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_ -#define SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_ - -#if !defined(__BIONIC_HAVE_UCONTEXT_T) -#include - -// 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 -#endif // __BIONIC_HAVE_UCONTEXT_T - -#endif // SANDBOX_LINUX_SERVICES_ANDROID_ARM_UCONTEXT_H_ diff --git a/security/sandbox/chromium/sandbox/linux/services/android_ucontext.h b/security/sandbox/chromium/sandbox/linux/services/android_ucontext.h deleted file mode 100644 index 2814710096bb..000000000000 --- a/security/sandbox/chromium/sandbox/linux/services/android_ucontext.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_ -#define SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_ - -#if defined(__ANDROID__) - -#if defined(__arm__) -#include "sandbox/linux/services/android_arm_ucontext.h" -#elif defined(__i386__) -#include "sandbox/linux/services/android_i386_ucontext.h" -#elif defined(__x86_64__) -#include "sandbox/linux/services/android_x86_64_ucontext.h" -#elif defined(__mips__) -#include "sandbox/linux/services/android_mips_ucontext.h" -#elif defined(__aarch64__) -#include "sandbox/linux/services/android_arm64_ucontext.h" -#else -#error "No support for your architecture in Android header" -#endif - -#else // __ANDROID__ -#error "Android header file included on non Android." -#endif // __ANDROID__ - -#endif // SANDBOX_LINUX_SERVICES_ANDROID_UCONTEXT_H_ diff --git a/security/sandbox/chromium/sandbox/linux/services/linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/services/linux_syscalls.h deleted file mode 100644 index b6a40c6b249c..000000000000 --- a/security/sandbox/chromium/sandbox/linux/services/linux_syscalls.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This header will be kept up to date so that we can compile system-call -// policies even when system headers are old. -// System call numbers are accessible through __NR_syscall_name. - -#ifndef SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_ -#define SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_ - -#if defined(__x86_64__) -#include "sandbox/linux/services/x86_64_linux_syscalls.h" -#endif - -#if defined(__i386__) -#include "sandbox/linux/services/x86_32_linux_syscalls.h" -#endif - -#if defined(__arm__) && defined(__ARM_EABI__) -#include "sandbox/linux/services/arm_linux_syscalls.h" -#endif - -#if defined(__mips__) && defined(_ABIO32) -#include "sandbox/linux/services/mips_linux_syscalls.h" -#endif - -#if defined(__aarch64__) -#include "sandbox/linux/services/arm64_linux_syscalls.h" -#endif - -#endif // SANDBOX_LINUX_SERVICES_LINUX_SYSCALLS_H_ - diff --git a/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc new file mode 100644 index 000000000000..9d5a6ada092c --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc @@ -0,0 +1,261 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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, + std::nullptr_t child_stack, + pid_t* ptid, + pid_t* ctid, + std::nullptr_t 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, std::nullptr_t oldset) { + // In some toolchain (in particular Android and PNaCl toolchain), + // sigset_t is 32 bits, but the Linux ABI uses more. + LinuxSigSet linux_value; + std::memset(&linux_value, 0, sizeof(LinuxSigSet)); + std::memcpy(&linux_value, set, std::min(sizeof(sigset_t), + sizeof(LinuxSigSet))); + + return syscall(__NR_rt_sigprocmask, how, &linux_value, nullptr, + sizeof(linux_value)); +} + +// 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. +#if !defined(OS_NACL_NONSFI) +int sys_sigaction(int signum, + const struct sigaction* act, + struct sigaction* oldact) { + return sigaction(signum, act, oldact); +} +#else +#if defined(ARCH_CPU_X86_FAMILY) + +// On x86_64, sa_restorer is required. We specify it on x86 as well in order to +// support kernels with VDSO disabled. +#if !defined(SA_RESTORER) +#define SA_RESTORER 0x04000000 +#endif + +// XSTR(__NR_foo) expands to a string literal containing the value value of +// __NR_foo. +#define STR(x) #x +#define XSTR(x) STR(x) + +// 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 does not return. +// TODO(rickyz): These assembly functions may still break stack unwinding on +// nonsfi NaCl builds. +#if defined(ARCH_CPU_X86_64) + +extern "C" { + void sys_rt_sigreturn(); +} + +asm( + ".text\n" + "sys_rt_sigreturn:\n" + "mov $" XSTR(__NR_rt_sigreturn) ", %eax\n" + "syscall\n"); + +#elif defined(ARCH_CPU_X86) +extern "C" { + void sys_sigreturn(); + void sys_rt_sigreturn(); +} + +asm( + ".text\n" + "sys_rt_sigreturn:\n" + "mov $" XSTR(__NR_rt_sigreturn) ", %eax\n" + "int $0x80\n" + + "sys_sigreturn:\n" + "pop %eax\n" + "mov $" XSTR(__NR_sigreturn) ", %eax\n" + "int $0x80\n"); +#else +#error "Unsupported architecture." +#endif + +#undef STR +#undef XSTR + +#endif + +int sys_sigaction(int signum, + const struct sigaction* act, + struct sigaction* oldact) { + LinuxSigAction linux_act = {}; + if (act) { + linux_act.kernel_handler = act->sa_handler; + std::memcpy(&linux_act.sa_mask, &act->sa_mask, + std::min(sizeof(linux_act.sa_mask), sizeof(act->sa_mask))); + linux_act.sa_flags = act->sa_flags; + +#if defined(ARCH_CPU_X86_FAMILY) + if (!(linux_act.sa_flags & SA_RESTORER)) { + linux_act.sa_flags |= SA_RESTORER; +#if defined(ARCH_CPU_X86_64) + linux_act.sa_restorer = sys_rt_sigreturn; +#elif defined(ARCH_CPU_X86) + linux_act.sa_restorer = + linux_act.sa_flags & SA_SIGINFO ? sys_rt_sigreturn : sys_sigreturn; +#else +#error "Unsupported architecture." +#endif + } +#endif + } + + LinuxSigAction linux_oldact = {}; + int result = syscall(__NR_rt_sigaction, signum, act ? &linux_act : nullptr, + oldact ? &linux_oldact : nullptr, + sizeof(LinuxSigSet)); + + if (result == 0 && oldact) { + oldact->sa_handler = linux_oldact.kernel_handler; + sigemptyset(&oldact->sa_mask); + std::memcpy(&oldact->sa_mask, &linux_oldact.sa_mask, + std::min(sizeof(linux_act.sa_mask), sizeof(act->sa_mask))); + oldact->sa_flags = linux_oldact.sa_flags; + } + return result; +} + +#endif // defined(MEMORY_SANITIZER) + +} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.h b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.h new file mode 100644 index 000000000000..057e4c87f473 --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.h @@ -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. + +#ifndef SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_ +#define SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_ + +#include +#include +#include + +#include + +#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, + std::nullptr_t child_stack, + pid_t* ptid, + pid_t* ctid, + std::nullptr_t 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, + std::nullptr_t 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/security/sandbox/chromium/sandbox/linux/services/arm_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h similarity index 98% rename from security/sandbox/chromium/sandbox/linux/services/arm_linux_syscalls.h rename to security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h index 5fa140df60b9..1addd53843c9 100644 --- a/security/sandbox/chromium/sandbox/linux/services/arm_linux_syscalls.h +++ b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h @@ -3,15 +3,17 @@ // found in the LICENSE file. // Generated from the Linux kernel's calls.S. -#ifndef SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_ -#define SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_ +#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 -// __NR_SYSCALL_BASE, __ARM_NR_BASE are defined in . -#include +#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. @@ -1203,6 +1205,10 @@ #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 @@ -1380,6 +1386,10 @@ #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 @@ -1405,5 +1415,4 @@ #define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0) #endif -#endif // SANDBOX_LINUX_SERVICES_ARM_LINUX_SYSCALLS_H_ - +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_ diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_ucontext.h b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_ucontext.h new file mode 100644 index 000000000000..35208fa2a533 --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_ucontext.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_LINUX_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_ +#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_ + +#include + +#if !defined(__BIONIC_HAVE_UCONTEXT_T) +#if !defined(__native_client_nonsfi__) +#include +#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 +#endif // __BIONIC_HAVE_UCONTEXT_T + +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_ diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/capability.h b/security/sandbox/chromium/sandbox/linux/system_headers/capability.h new file mode 100644 index 000000000000..f91fcf78acf0 --- /dev/null +++ b/security/sandbox/chromium/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 + +// 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/security/sandbox/chromium/sandbox/linux/services/android_i386_ucontext.h b/security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h similarity index 71% rename from security/sandbox/chromium/sandbox/linux/services/android_i386_ucontext.h rename to security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h index 580ac70a06aa..f4380339d902 100644 --- a/security/sandbox/chromium/sandbox/linux/services/android_i386_ucontext.h +++ b/security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h @@ -2,8 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_ -#define SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_ +#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_ +#define SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_ + +#include +#include // We do something compatible with glibc. Hopefully, at some point Android will // provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined. @@ -11,7 +14,16 @@ // 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 +#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 { @@ -68,7 +80,12 @@ typedef struct ucontext { struct ucontext* uc_link; stack_t uc_stack; mcontext_t uc_mcontext; - sigset_t uc_sigmask; + // 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; @@ -76,4 +93,4 @@ typedef struct ucontext { #include #endif // __BIONIC_HAVE_UCONTEXT_T -#endif // SANDBOX_LINUX_SERVICES_ANDROID_I386_UCONTEXT_H_ +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_ diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/linux_filter.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_filter.h new file mode 100644 index 000000000000..b23b6eb0c1e2 --- /dev/null +++ b/security/sandbox/chromium/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 + +// 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/security/sandbox/chromium/sandbox/linux/services/android_futex.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h similarity index 84% rename from security/sandbox/chromium/sandbox/linux/services/android_futex.h rename to security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h index 571f5d219906..4e2840333688 100644 --- a/security/sandbox/chromium/sandbox/linux/services/android_futex.h +++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h @@ -2,8 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_ -#define SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_ +#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_ +#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_ + +#if !defined(__native_client_nonsfi__) +#include +#endif // !defined(__native_client_nonsfi__) #if !defined(FUTEX_WAIT) #define FUTEX_WAIT 0 @@ -77,4 +81,4 @@ #define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG) #endif -#endif // SANDBOX_LINUX_SERVICES_ANDROID_FUTEX_H_ +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_ diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/linux_seccomp.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_seccomp.h new file mode 100644 index 000000000000..3deb3d2253c0 --- /dev/null +++ b/security/sandbox/chromium/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 +// #include + +// 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 . 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/security/sandbox/chromium/sandbox/linux/system_headers/linux_signal.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_signal.h new file mode 100644 index 000000000000..fb9a47b8d18f --- /dev/null +++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_signal.h @@ -0,0 +1,146 @@ +// 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_ + +#include + +// 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(__i386__) || defined(__x86_64__) || defined(__arm__) || \ + defined(__aarch64__) + +#define LINUX_SIGHUP 1 +#define LINUX_SIGINT 2 +#define LINUX_SIGQUIT 3 +#define LINUX_SIGABRT 6 +#define LINUX_SIGBUS 7 +#define LINUX_SIGUSR1 10 +#define LINUX_SIGSEGV 11 +#define LINUX_SIGUSR2 12 +#define LINUX_SIGPIPE 13 +#define LINUX_SIGTERM 15 +#define LINUX_SIGCHLD 17 +#define LINUX_SIGSYS 31 + +#define LINUX_SIG_BLOCK 0 +#define LINUX_SIG_UNBLOCK 1 + +#define LINUX_SA_SIGINFO 4 +#define LINUX_SA_NODEFER 0x40000000 +#define LINUX_SA_RESTART 0x10000000 + +#define LINUX_SIG_DFL 0 + +#elif defined(__mips__) + +#define LINUX_SIGHUP 1 +#define LINUX_SIGINT 2 +#define LINUX_SIGQUIT 3 +#define LINUX_SIGABRT 6 +#define LINUX_SIGBUS 10 +#define LINUX_SIGSEGV 11 +#define LINUX_SIGSYS 12 +#define LINUX_SIGPIPE 13 +#define LINUX_SIGTERM 15 +#define LINUX_SIGUSR1 16 +#define LINUX_SIGUSR2 17 +#define LINUX_SIGCHLD 18 + +#define LINUX_SIG_BLOCK 1 +#define LINUX_SIG_UNBLOCK 2 + +#define LINUX_SA_SIGINFO 0x00000008 +#define LINUX_SA_NODEFER 0x40000000 +#define LINUX_SA_RESTART 0x10000000 + +#define LINUX_SIG_DFL 0 + +#else +#error "Unsupported platform" +#endif + +#if defined(__native_client_nonsfi__) +#if !defined(__i386__) && !defined(__arm__) +#error "Unsupported platform" +#endif + +#include + +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__) + +#include + +static_assert(LINUX_SIGHUP == SIGHUP, "LINUX_SIGHUP == SIGHUP"); +static_assert(LINUX_SIGINT == SIGINT, "LINUX_SIGINT == SIGINT"); +static_assert(LINUX_SIGQUIT == SIGQUIT, "LINUX_SIGQUIT == SIGQUIT"); +static_assert(LINUX_SIGABRT == SIGABRT, "LINUX_SIGABRT == SIGABRT"); +static_assert(LINUX_SIGBUS == SIGBUS, "LINUX_SIGBUS == SIGBUS"); +static_assert(LINUX_SIGUSR1 == SIGUSR1, "LINUX_SIGUSR1 == SIGUSR1"); +static_assert(LINUX_SIGSEGV == SIGSEGV, "LINUX_SIGSEGV == SIGSEGV"); +static_assert(LINUX_SIGUSR2 == SIGUSR2, "LINUX_SIGUSR2 == SIGUSR2"); +static_assert(LINUX_SIGPIPE == SIGPIPE, "LINUX_SIGPIPE == SIGPIPE"); +static_assert(LINUX_SIGTERM == SIGTERM, "LINUX_SIGTERM == SIGTERM"); +static_assert(LINUX_SIGCHLD == SIGCHLD, "LINUX_SIGCHLD == SIGCHLD"); +static_assert(LINUX_SIGSYS == SIGSYS, "LINUX_SIGSYS == SIGSYS"); +static_assert(LINUX_SIG_BLOCK == SIG_BLOCK, "LINUX_SIG_BLOCK == SIG_BLOCK"); +static_assert(LINUX_SIG_UNBLOCK == SIG_UNBLOCK, + "LINUX_SIG_UNBLOCK == SIG_UNBLOCK"); +static_assert(LINUX_SA_SIGINFO == SA_SIGINFO, "LINUX_SA_SIGINFO == SA_SIGINFO"); +static_assert(LINUX_SA_NODEFER == SA_NODEFER, "LINUX_SA_NODEFER == SA_NODEFER"); +static_assert(LINUX_SA_RESTART == SA_RESTART, "LINUX_SA_RESTART == SA_RESTART"); +static_assert(LINUX_SIG_DFL == SIG_DFL, "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__) + +// struct sigset_t is different size in PNaCl from the Linux's. +#if defined(__mips__) +#if !defined(_NSIG_WORDS) +#define _NSIG_WORDS 4 +#endif +struct LinuxSigSet { + unsigned long sig[_NSIG_WORDS]; +}; +#else +typedef uint64_t LinuxSigSet; +#endif + +// struct sigaction is different in PNaCl from the Linux's. +#if defined(__mips__) +struct LinuxSigAction { + unsigned int sa_flags; + void (*kernel_handler)(int); + LinuxSigSet sa_mask; +}; +#else +struct LinuxSigAction { + void (*kernel_handler)(int); + uint32_t sa_flags; + void (*sa_restorer)(void); + LinuxSigSet sa_mask; +}; +#endif + +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SIGNAL_H_ diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_syscalls.h new file mode 100644 index 000000000000..2b441e47eaff --- /dev/null +++ b/security/sandbox/chromium/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/security/sandbox/chromium/sandbox/linux/system_headers/linux_ucontext.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_ucontext.h new file mode 100644 index 000000000000..ea4d8a6c1fd5 --- /dev/null +++ b/security/sandbox/chromium/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/security/sandbox/chromium/sandbox/linux/services/x86_32_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h similarity index 94% rename from security/sandbox/chromium/sandbox/linux/services/x86_32_linux_syscalls.h rename to security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h index 6b0acdaa1512..a6afc62d9908 100644 --- a/security/sandbox/chromium/sandbox/linux/services/x86_32_linux_syscalls.h +++ b/security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h @@ -3,8 +3,8 @@ // found in the LICENSE file. // Generated from the Linux kernel's syscall_32.tbl. -#ifndef SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_ -#define SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_ +#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" @@ -1422,73 +1422,5 @@ #define __NR_memfd_create 356 #endif -#if !defined(__NR_bpf) -#define __NR_bpf 357 -#endif - -#if !defined(__NR_execveat) -#define __NR_execveat 358 -#endif - -#if !defined(__NR_socket) -#define __NR_socket 359 -#endif - -#if !defined(__NR_socketpair) -#define __NR_socketpair 360 -#endif - -#if !defined(__NR_bind) -#define __NR_bind 361 -#endif - -#if !defined(__NR_connect) -#define __NR_connect 362 -#endif - -#if !defined(__NR_listen) -#define __NR_listen 363 -#endif - -#if !defined(__NR_accept4) -#define __NR_accept4 364 -#endif - -#if !defined(__NR_getsockopt) -#define __NR_getsockopt 365 -#endif - -#if !defined(__NR_setsockopt) -#define __NR_setsockopt 366 -#endif - -#if !defined(__NR_getsockname) -#define __NR_getsockname 367 -#endif - -#if !defined(__NR_getpeername) -#define __NR_getpeername 368 -#endif - -#if !defined(__NR_sendto) -#define __NR_sendto 369 -#endif - -#if !defined(__NR_sendmsg) -#define __NR_sendmsg 370 -#endif - -#if !defined(__NR_recvfrom) -#define __NR_recvfrom 371 -#endif - -#if !defined(__NR_recvmsg) -#define __NR_recvmsg 372 -#endif - -#if !defined(__NR_shutdown) -#define __NR_shutdown 373 -#endif - -#endif // SANDBOX_LINUX_SERVICES_X86_32_LINUX_SYSCALLS_H_ +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_ diff --git a/security/sandbox/chromium/sandbox/linux/services/x86_64_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h similarity index 99% rename from security/sandbox/chromium/sandbox/linux/services/x86_64_linux_syscalls.h rename to security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h index ea6c55512fd6..349504aee437 100644 --- a/security/sandbox/chromium/sandbox/linux/services/x86_64_linux_syscalls.h +++ b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h @@ -3,8 +3,8 @@ // found in the LICENSE file. // Generated from the Linux kernel's syscall_64.tbl. -#ifndef SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_ -#define SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_ +#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" @@ -1290,5 +1290,5 @@ #define __NR_memfd_create 319 #endif -#endif // SANDBOX_LINUX_SERVICES_X86_64_LINUX_SYSCALLS_H_ +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_ diff --git a/security/sandbox/chromium/sandbox/linux/services/android_x86_64_ucontext.h b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_ucontext.h similarity index 88% rename from security/sandbox/chromium/sandbox/linux/services/android_x86_64_ucontext.h rename to security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_ucontext.h index ef328e55d6f4..1f1abe642f6a 100644 --- a/security/sandbox/chromium/sandbox/linux/services/android_x86_64_ucontext.h +++ b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_ucontext.h @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_ -#define SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_ +#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_UCONTEXT_H_ +#define SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_UCONTEXT_H_ + +#include // We do something compatible with glibc. Hopefully, at some point Android will // provide that for us, and __BIONIC_HAVE_UCONTEXT_T should be defined. @@ -85,4 +87,4 @@ typedef struct ucontext { #include #endif // __BIONIC_HAVE_UCONTEXT_T -#endif // SANDBOX_LINUX_SERVICES_ANDROID_X86_64_UCONTEXT_H_ +#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_UCONTEXT_H_ diff --git a/security/sandbox/chromium/sandbox/sandbox_export.h b/security/sandbox/chromium/sandbox/sandbox_export.h index 40a403664062..35d6a1ba26ea 100644 --- a/security/sandbox/chromium/sandbox/sandbox_export.h +++ b/security/sandbox/chromium/sandbox/sandbox_export.h @@ -13,16 +13,13 @@ #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) diff --git a/security/sandbox/chromium/sandbox/win/src/Wow64.cc b/security/sandbox/chromium/sandbox/win/src/Wow64.cc index 59df1d6f6741..c5984d629ba1 100644 --- a/security/sandbox/chromium/sandbox/win/src/Wow64.cc +++ b/security/sandbox/chromium/sandbox/win/src/Wow64.cc @@ -2,10 +2,13 @@ // 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 "sandbox/win/src/Wow64.h" + +#include #include +#include "base/bit_cast.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/win/scoped_process_information.h" @@ -73,12 +76,11 @@ typedef BOOL (WINAPI* IsWow64ProcessFunction)(HANDLE process, BOOL* wow64); namespace sandbox { -Wow64::~Wow64() { - if (dll_load_) - ::CloseHandle(dll_load_); +Wow64::Wow64(TargetProcess* child, HMODULE ntdll) + : child_(child), ntdll_(ntdll), dll_load_(NULL), continue_load_(NULL) { +} - if (continue_load_) - ::CloseHandle(continue_load_); +Wow64::~Wow64() { } // The basic idea is to allocate one page of memory on the child, and initialize @@ -96,17 +98,20 @@ bool Wow64::WaitForNtdll() { 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); + dll_load_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); + continue_load_.Set(::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)) + if (!::DuplicateHandle(current_process, dll_load_.Get(), child_->Process(), + &remote_load, access, FALSE, 0)) { return false; - if (!::DuplicateHandle(current_process, continue_load_, child_->Process(), - &remote_continue, access, FALSE, 0)) + } + if (!::DuplicateHandle(current_process, continue_load_.Get(), + child_->Process(), &remote_continue, access, FALSE, + 0)) { return false; + } void* buffer = ::VirtualAllocEx(child_->Process(), NULL, page_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); @@ -137,7 +142,7 @@ bool Wow64::WaitForNtdll() { } bool Wow64::RunWowHelper(void* buffer) { - COMPILE_ASSERT(sizeof(buffer) <= sizeof(DWORD), unsupported_64_bits); + static_assert(sizeof(buffer) <= sizeof(DWORD), "unsupported 64 bits"); // Get the path to the helper (beside the exe). wchar_t prog_name[MAX_PATH]; @@ -185,11 +190,11 @@ bool Wow64::DllMapped() { } for (;;) { - DWORD reason = ::WaitForSingleObject(dll_load_, INFINITE); + DWORD reason = ::WaitForSingleObject(dll_load_.Get(), INFINITE); if (WAIT_TIMEOUT == reason || WAIT_ABANDONED == reason) return false; - if (!::ResetEvent(dll_load_)) + if (!::ResetEvent(dll_load_.Get())) return false; bool found = NtdllPresent(); @@ -198,7 +203,7 @@ bool Wow64::DllMapped() { return false; } - if (!::SetEvent(continue_load_)) + if (!::SetEvent(continue_load_.Get())) return false; if (found) diff --git a/security/sandbox/chromium/sandbox/win/src/Wow64.h b/security/sandbox/chromium/sandbox/win/src/Wow64.h index e9bbd53d9d02..acabc3565b32 100644 --- a/security/sandbox/chromium/sandbox/win/src/Wow64.h +++ b/security/sandbox/chromium/sandbox/win/src/Wow64.h @@ -7,7 +7,8 @@ #include -#include "base/basictypes.h" +#include "base/macros.h" +#include "base/win/scoped_handle.h" #include "sandbox/win/src/sandbox_types.h" namespace sandbox { @@ -18,8 +19,7 @@ class TargetProcess; // 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(TargetProcess* child, HMODULE ntdll); ~Wow64(); // Waits for the 32 bit DLL to get loaded on the child process. This function @@ -40,8 +40,10 @@ class Wow64 { 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. + // Event that is signaled on dll load. + base::win::ScopedHandle dll_load_; + // Event to signal to continue execution on the child. + base::win::ScopedHandle continue_load_; DISALLOW_IMPLICIT_CONSTRUCTORS(Wow64); }; diff --git a/security/sandbox/chromium/sandbox/win/src/Wow64_64.cc b/security/sandbox/chromium/sandbox/win/src/Wow64_64.cc index f03831bd1154..357deb8559f4 100644 --- a/security/sandbox/chromium/sandbox/win/src/Wow64_64.cc +++ b/security/sandbox/chromium/sandbox/win/src/Wow64_64.cc @@ -8,6 +8,10 @@ namespace sandbox { +Wow64::Wow64(TargetProcess* child, HMODULE ntdll) + : child_(child), ntdll_(ntdll), dll_load_(NULL), continue_load_(NULL) { +} + Wow64::~Wow64() { } diff --git a/security/sandbox/chromium/sandbox/win/src/app_container.cc b/security/sandbox/chromium/sandbox/win/src/app_container.cc index f8d754191592..a51f09208239 100644 --- a/security/sandbox/chromium/sandbox/win/src/app_container.cc +++ b/security/sandbox/chromium/sandbox/win/src/app_container.cc @@ -5,6 +5,7 @@ #include "sandbox/win/src/app_container.h" #include +#include #include #include "base/logging.h" diff --git a/security/sandbox/chromium/sandbox/win/src/app_container.h b/security/sandbox/chromium/sandbox/win/src/app_container.h index 8125d706fb4a..fe2b18933408 100644 --- a/security/sandbox/chromium/sandbox/win/src/app_container.h +++ b/security/sandbox/chromium/sandbox/win/src/app_container.h @@ -9,6 +9,7 @@ #include +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "sandbox/win/src/sandbox_types.h" @@ -56,7 +57,7 @@ class AppContainerAttributes { ResultCode CreateAppContainer(const base::string16& sid, const base::string16& name); -// Deletes an AppContainer previously created with a successfull call to +// Deletes an AppContainer previously created with a successful call to // CreateAppContainer. ResultCode DeleteAppContainer(const base::string16& sid); diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_test.cc b/security/sandbox/chromium/sandbox/win/src/app_container_test.cc index 1bfab2c3145b..ced5cbde7c1e 100644 --- a/security/sandbox/chromium/sandbox/win/src/app_container_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/app_container_test.cc @@ -141,4 +141,21 @@ TEST(AppContainerTest, RequiresImpersonation) { 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/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc index c5f21ab5e6f0..c3df2efc96c4 100644 --- a/security/sandbox/chromium/sandbox/win/src/broker_services.cc +++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc @@ -5,9 +5,12 @@ #include "sandbox/win/src/broker_services.h" #include +#include #include "base/logging.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" #include "base/threading/platform_thread.h" #include "base/win/scoped_handle.h" #include "base/win/scoped_process_information.h" @@ -15,8 +18,8 @@ #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/sandbox_policy_base.h" #include "sandbox/win/src/target_process.h" #include "sandbox/win/src/win2k_threadpool.h" #include "sandbox/win/src/win_utils.h" @@ -55,22 +58,47 @@ enum { // 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) { + JobTracker(base::win::ScopedHandle job, sandbox::PolicyBase* policy) + : job(job.Pass()), policy(policy) { } + ~JobTracker() { + FreeResources(); + } + + // Releases the Job and notifies the associated Policy object to release its + // resources as well. + void FreeResources(); + + base::win::ScopedHandle job; + sandbox::PolicyBase* policy; }; +void JobTracker::FreeResources() { + if (policy) { + BOOL res = ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK); + DCHECK(res); + // Closing the job causes the target process to be destroyed so this needs + // to happen before calling OnJobEmpty(). + HANDLE stale_job_handle = job.Get(); + job.Close(); + + // In OnJobEmpty() we don't actually use the job handle directly. + policy->OnJobEmpty(stale_job_handle); + policy->Release(); + policy = NULL; + } +} + // Helper structure that allows the broker to track peer processes struct PeerTracker { + PeerTracker(DWORD process_id, HANDLE broker_job_port) + : wait_object(NULL), id(process_id), job_port(broker_job_port) { + } + 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) { @@ -82,70 +110,30 @@ void DeregisterPeerTracker(PeerTracker* peer) { } } -// 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. - if (app_container) - 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) { +BrokerServicesBase::BrokerServicesBase() : thread_pool_(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_)) + if (job_port_.IsValid() || (NULL != thread_pool_)) return SBOX_ERROR_UNEXPECTED_CALL; ::InitializeCriticalSection(&lock_); - job_port_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); - if (NULL == job_port_) + job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)); + if (!job_port_.IsValid()) return SBOX_ERROR_GENERIC; - no_targets_ = ::CreateEventW(NULL, TRUE, FALSE, NULL); + no_targets_.Set(::CreateEventW(NULL, TRUE, FALSE, NULL)); - job_thread_ = ::CreateThread(NULL, 0, // Default security and stack. - TargetEventsThread, this, NULL, NULL); - if (NULL == job_thread_) + job_thread_.Set(::CreateThread(NULL, 0, // Default security and stack. + TargetEventsThread, this, NULL, NULL)); + if (!job_thread_.IsValid()) return SBOX_ERROR_GENERIC; return SBOX_ALL_OK; @@ -158,31 +146,24 @@ ResultCode BrokerServicesBase::Init() { // wait for threads here. BrokerServicesBase::~BrokerServicesBase() { // If there is no port Init() was never called successfully. - if (!job_port_) + if (!job_port_.IsValid()) 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_); + ::PostQueuedCompletionStatus(job_port_.Get(), 0, THREAD_CTRL_QUIT, FALSE); - if (WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_, 1000)) { + if (job_thread_.IsValid() && + WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_.Get(), 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_); + STLDeleteElements(&tracker_list_); delete thread_pool_; - ::CloseHandle(no_targets_); // Cancel the wait events and delete remaining peer trackers. for (PeerTrackerMap::iterator it = peer_map_.begin(); @@ -190,16 +171,7 @@ BrokerServicesBase::~BrokerServicesBase() { 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); - } + ::DeleteCriticalSection(&lock_); } TargetPolicy* BrokerServicesBase::CreatePolicy() { @@ -208,21 +180,6 @@ TargetPolicy* BrokerServicesBase::CreatePolicy() { 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 @@ -234,8 +191,8 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { base::PlatformThread::SetName("BrokerEvent"); BrokerServicesBase* broker = reinterpret_cast(param); - HANDLE port = broker->job_port_; - HANDLE no_targets = broker->no_targets_; + HANDLE port = broker->job_port_.Get(); + HANDLE no_targets = broker->no_targets_.Get(); int target_counter = 0; ::ResetEvent(no_targets); @@ -245,11 +202,12 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { ULONG_PTR key = 0; LPOVERLAPPED ovl = NULL; - if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE)) + 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 @@ -263,7 +221,7 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { // 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); + tracker->FreeResources(); break; } @@ -279,7 +237,8 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: { { AutoLock lock(&broker->lock_); - broker->child_process_ids_.erase(reinterpret_cast(ovl)); + broker->child_process_ids_.erase( + static_cast(reinterpret_cast(ovl))); } --target_counter; if (0 == target_counter) @@ -294,7 +253,7 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { } case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: { - BOOL res = ::TerminateJobObject(tracker->job, + BOOL res = ::TerminateJobObject(tracker->job.Get(), SBOX_FATAL_MEMORY_EXCEEDED); DCHECK(res); break; @@ -308,8 +267,8 @@ DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) { } else if (THREAD_CTRL_REMOVE_PEER == key) { // Remove a process from our list of peers. AutoLock lock(&broker->lock_); - PeerTrackerMap::iterator it = - broker->peer_map_.find(reinterpret_cast(ovl)); + PeerTrackerMap::iterator it = broker->peer_map_.find( + static_cast(reinterpret_cast(ovl))); DeregisterPeerTracker(it->second); broker->peer_map_.erase(it); } else if (THREAD_CTRL_QUIT == key) { @@ -349,58 +308,36 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // This downcast is safe as long as we control CreatePolicy() PolicyBase* policy_base = static_cast(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; + base::win::ScopedHandle initial_token; + base::win::ScopedHandle lockdown_token; + base::win::ScopedHandle lowbox_token; 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(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); + result = + policy_base->MakeTokens(&initial_token, &lockdown_token, &lowbox_token); if (SBOX_ALL_OK != result) return result; - base::win::ScopedHandle job(job_temp); + base::win::ScopedHandle job; + result = policy_base->MakeJobObject(&job); + if (SBOX_ALL_OK != result) + return result; // 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 inherited_handle_list; + base::string16 desktop = policy_base->GetAlternateDesktop(); if (!desktop.empty()) { startup_info.startup_info()->lpDesktop = @@ -408,6 +345,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, } bool inherit_handles = false; + if (base::win::GetVersion() >= base::win::VERSION_VISTA) { int attribute_count = 0; const AppContainerAttributes* app_container = @@ -415,7 +353,6 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, if (app_container) ++attribute_count; - DWORD64 mitigations; size_t mitigations_size; ConvertProcessMitigationsToPolicy(policy->GetProcessMitigations(), &mitigations, &mitigations_size); @@ -424,14 +361,20 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, HANDLE stdout_handle = policy_base->GetStdoutHandle(); HANDLE stderr_handle = policy_base->GetStderrHandle(); - HANDLE inherit_handle_list[2]; - int inherit_handle_count = 0; + if (stdout_handle != INVALID_HANDLE_VALUE) - inherit_handle_list[inherit_handle_count++] = stdout_handle; + inherited_handle_list.push_back(stdout_handle); + // Handles in the list must be unique. if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE) - inherit_handle_list[inherit_handle_count++] = stderr_handle; - if (inherit_handle_count) + inherited_handle_list.push_back(stderr_handle); + + const HandleList& policy_handle_list = policy_base->GetHandlesBeingShared(); + + for (auto handle : policy_handle_list) + inherited_handle_list.push_back(handle->Get()); + + if (inherited_handle_list.size()) ++attribute_count; if (!startup_info.InitializeProcThreadAttributeList(attribute_count)) @@ -451,11 +394,11 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, } } - if (inherit_handle_count) { + if (inherited_handle_list.size()) { if (!startup_info.UpdateProcThreadAttribute( PROC_THREAD_ATTRIBUTE_HANDLE_LIST, - inherit_handle_list, - sizeof(inherit_handle_list[0]) * inherit_handle_count)) { + &inherited_handle_list[0], + sizeof(HANDLE) * inherited_handle_list.size())) { return SBOX_ERROR_PROC_THREAD_ATTRIBUTES; } startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES; @@ -466,14 +409,6 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // have limited which handles will be inherited. inherit_handles = true; } - } else if (getenv("MOZ_WIN_INHERIT_STD_HANDLES_PRE_VISTA")) { - // On pre-Vista versions even if we can't limit what gets inherited, we - // sometimes want to inherit stdout/err for testing purposes. - startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES; - startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE; - startup_info.startup_info()->hStdOutput = policy_base->GetStdoutHandle(); - startup_info.startup_info()->hStdError = policy_base->GetStderrHandle(); - inherit_handles = true; } // Construct the thread pool here in case it is expensive. @@ -484,15 +419,19 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // 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_); + TargetProcess* target = + new TargetProcess(initial_token.Pass(), lockdown_token.Pass(), + lowbox_token.Pass(), job.Get(), thread_pool_); DWORD win_result = target->Create(exe_path, command_line, inherit_handles, startup_info, &process_info); - if (ERROR_SUCCESS != win_result) - return SpawnCleanup(target, win_result); + + 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)) { @@ -503,9 +442,13 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // the job object generates notifications using the completion port. policy_base->AddRef(); if (job.IsValid()) { - scoped_ptr tracker(new JobTracker(job.Take(), policy_base)); - if (!AssociateCompletionPort(tracker->job, job_port_, tracker.get())) - return SpawnCleanup(target, 0); + scoped_ptr tracker(new JobTracker(job.Pass(), 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.Get(), job_port_.Get(), + tracker.get())); + // Save the tracker because in cleanup we might need to force closing // the Jobs. tracker_list_.push_back(tracker.release()); @@ -515,10 +458,13 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // 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_); + ::SetEvent(no_targets_.Get()); // 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. + // Sandbox policy engine needs to know that these processes are valid + // targets for e.g. BrokerDuplicateHandle so track them as peer processes. + AddTargetPeer(process_info.process_handle()); } *target_info = process_info.Take(); @@ -527,7 +473,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, ResultCode BrokerServicesBase::WaitForAllTargets() { - ::WaitForSingleObject(no_targets_, INFINITE); + ::WaitForSingleObject(no_targets_.Get(), INFINITE); return SBOX_ALL_OK; } @@ -540,13 +486,14 @@ bool BrokerServicesBase::IsActiveTarget(DWORD process_id) { VOID CALLBACK BrokerServicesBase::RemovePeer(PVOID parameter, BOOLEAN timeout) { PeerTracker* peer = reinterpret_cast(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(peer->id)); + ::PostQueuedCompletionStatus( + peer->job_port, 0, THREAD_CTRL_REMOVE_PEER, + reinterpret_cast(static_cast(peer->id))); } ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) { scoped_ptr peer(new PeerTracker(::GetProcessId(peer_process), - job_port_)); + job_port_.Get())); if (!peer->id) return SBOX_ERROR_GENERIC; @@ -570,7 +517,7 @@ ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) { } // Release the pointer since it will be cleaned up by the callback. - peer.release(); + ignore_result(peer.release()); return SBOX_ALL_OK; } diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.h b/security/sandbox/chromium/sandbox/win/src/broker_services.h index 76011e51a88e..6c8f5231cabb 100644 --- a/security/sandbox/chromium/sandbox/win/src/broker_services.h +++ b/security/sandbox/chromium/sandbox/win/src/broker_services.h @@ -9,8 +9,8 @@ #include #include #include -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/win/scoped_handle.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/job.h" @@ -45,17 +45,17 @@ class BrokerServicesBase final : public BrokerServices, ~BrokerServicesBase(); // BrokerServices interface. - virtual ResultCode Init() override; - virtual TargetPolicy* CreatePolicy() override; - virtual ResultCode SpawnTarget(const wchar_t* exe_path, - const wchar_t* command_line, - TargetPolicy* policy, - PROCESS_INFORMATION* target) override; - virtual ResultCode WaitForAllTargets() override; - virtual ResultCode AddTargetPeer(HANDLE peer_process) override; - virtual ResultCode InstallAppContainer(const wchar_t* sid, - const wchar_t* name) override; - virtual ResultCode UninstallAppContainer(const wchar_t* sid) override; + 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 @@ -64,9 +64,8 @@ class BrokerServicesBase final : public BrokerServices, 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); + typedef std::list JobTrackerList; + typedef std::map PeerTrackerMap; // The routine that the worker thread executes. It is in charge of // notifications and cleanup-related tasks. @@ -77,14 +76,14 @@ class BrokerServicesBase final : public BrokerServices, // The completion port used by the job objects to communicate events to // the worker thread. - HANDLE job_port_; + base::win::ScopedHandle job_port_; // Handle to a manual-reset event that is signaled when the total target // process count reaches zero. - HANDLE no_targets_; + base::win::ScopedHandle no_targets_; // Handle to the worker thread that reacts to job notifications. - HANDLE job_thread_; + base::win::ScopedHandle job_thread_; // Lock used to protect the list of targets from being modified by 2 // threads at the same time. @@ -94,21 +93,16 @@ class BrokerServicesBase final : public BrokerServices, ThreadProvider* thread_pool_; // List of the trackers for closing and cleanup purposes. - typedef std::list 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 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 child_process_ids_; - typedef std::map> TokenCacheMap; - TokenCacheMap token_cache_; - DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase); }; diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_client.h b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h index 2a482b4182e4..60ff2437a070 100644 --- a/security/sandbox/chromium/sandbox/win/src/crosscall_client.h +++ b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h @@ -5,6 +5,9 @@ #ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_ #define SANDBOX_SRC_CROSSCALL_CLIENT_H_ +#include +#include + #include "sandbox/win/src/crosscall_params.h" #include "sandbox/win/src/sandbox.h" @@ -20,7 +23,7 @@ // // The general interface of CrossCall is: // ResultCode CrossCall(IPCProvider& ipc_provider, -// uint32 tag, +// uint32_t tag, // const Par1& p1, const Par2& p2,...pn // CrossCallReturn* answer) // @@ -40,7 +43,7 @@ namespace sandbox { // this is the assumed channel size. This can be overridden in a given // IPC implementation. -const uint32 kIPCChannelSize = 1024; +const uint32_t 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 @@ -67,9 +70,7 @@ class CopyHelper { } // Returns the size of the input in bytes. - uint32 GetSize() const { - return sizeof(T); - } + uint32_t GetSize() const { return sizeof(T); } // Returns true if the current type is used as an In or InOut parameter. bool IsInOut() { @@ -78,7 +79,7 @@ class CopyHelper { // Returns this object's type. ArgType GetType() { - COMPILE_ASSERT(sizeof(T) == sizeof(uint32), need_specialization); + static_assert(sizeof(T) == sizeof(uint32_t), "specialization needed"); return UINT32_TYPE; } @@ -106,9 +107,7 @@ class CopyHelper { } // Returns the size of the input in bytes. - uint32 GetSize() const { - return sizeof(t_); - } + uint32_t GetSize() const { return sizeof(t_); } // Returns true if the current type is used as an In or InOut parameter. bool IsInOut() { @@ -147,12 +146,13 @@ class CopyHelper { // Returns the size of the string in bytes. We define a NULL string to // be of zero length. - uint32 GetSize() const { + uint32_t GetSize() const { __try { - return (!t_) ? 0 : static_cast(StringLength(t_) * sizeof(t_[0])); + return (!t_) ? 0 + : static_cast(StringLength(t_) * sizeof(t_[0])); } __except(EXCEPTION_EXECUTE_HANDLER) { - return kuint32max; + return UINT32_MAX; } } @@ -194,9 +194,7 @@ class CopyHelper : public CopyHelper { return Base::Update(buffer); } - uint32 GetSize() const { - return Base::GetSize(); - } + uint32_t GetSize() const { return Base::GetSize(); } bool IsInOut() { return Base::IsInOut(); @@ -224,9 +222,7 @@ class CopyHelper : public CopyHelper { return Base::Update(buffer); } - uint32 GetSize() const { - return Base::GetSize(); - } + uint32_t GetSize() const { return Base::GetSize(); } bool IsInOut() { return Base::IsInOut(); @@ -242,7 +238,8 @@ class CopyHelper : public CopyHelper { // parameters. class InOutCountedBuffer : public CountedBuffer { public: - InOutCountedBuffer(void* buffer, uint32 size) : CountedBuffer(buffer, size) {} + InOutCountedBuffer(void* buffer, uint32_t size) + : CountedBuffer(buffer, size) {} }; // This copy helper template specialization catches the cases where the @@ -272,9 +269,7 @@ class CopyHelper { // Returns the size of the string in bytes. We define a NULL string to // be of zero length. - uint32 GetSize() const { - return t_.Size(); - } + uint32_t GetSize() const { return t_.Size(); } // Returns true if the current type is used as an In or InOut parameter. bool IsInOut() { @@ -300,7 +295,7 @@ class CopyHelper { ActualParams* params = new(raw_mem) ActualParams(tag); #define XCALL_GEN_COPY_PARAM(num, params) \ - COMPILE_ASSERT(kMaxIpcParams >= num, too_many_parameters); \ + static_assert(kMaxIpcParams >= num, "too many parameters"); \ CopyHelper ch##num(p##num); \ if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \ ch##num.IsInOut(), ch##num.GetType())) \ @@ -317,7 +312,9 @@ class CopyHelper { // CrossCall template with one input parameter template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t tag, + const Par1& p1, CrossCallReturn* answer) { XCALL_GEN_PARAMS_OBJ(1, call_params); XCALL_GEN_COPY_PARAM(1, call_params); @@ -334,8 +331,11 @@ ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, // CrossCall template with two input parameters. template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, CrossCallReturn* answer) { +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t 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); @@ -352,8 +352,12 @@ ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, // CrossCall template with three input parameters. template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, CrossCallReturn* answer) { +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t 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); @@ -371,10 +375,17 @@ ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, } // CrossCall template with four input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, const Par4& p4, +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t 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); @@ -395,11 +406,20 @@ ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, } // CrossCall template with five input parameters. -template -ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, - const Par2& p2, const Par3& p3, const Par4& p4, - const Par5& p5, CrossCallReturn* answer) { +template +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t 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); @@ -421,11 +441,22 @@ ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, } // CrossCall template with six input parameters. -template -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) { +template +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t 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); @@ -449,11 +480,23 @@ ResultCode CrossCall(IPCProvider& ipc_provider, uint32 tag, const Par1& p1, } // CrossCall template with seven input parameters. -template -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, +template +ResultCode CrossCall(IPCProvider& ipc_provider, + uint32_t 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); diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h index 4a71f6959599..eb59c44239e2 100644 --- a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h +++ b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h @@ -7,30 +7,29 @@ #include #include +#include +#include #include -#include "base/basictypes.h" +#include "base/macros.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 +// Increases |value| until there is no need for padding given an int64_t // alignment. Returns the increased value. -uint32 Align(uint32 value) { - uint32 alignment = sizeof(int64); +inline uint32_t Align(uint32_t value) { + uint32_t alignment = sizeof(int64_t); 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 +// An IPC call is defined by its 'tag' which is a (uint32_t) 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. // @@ -39,7 +38,7 @@ uint32 Align(uint32 value) { // 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 +// parameters can only be uint32_t integers. Returning complex structures or // strings is not supported. namespace sandbox { @@ -50,7 +49,7 @@ const size_t kExtendedReturnCount = 8; // Union of multiple types to be used as extended results // in the CrossCallReturn. union MultiType { - uint32 unsigned_int; + uint32_t unsigned_int; void* pointer; HANDLE handle; ULONG_PTR ulong_ptr; @@ -66,8 +65,8 @@ const int kMaxIpcParams = 9; // Contains the information about a parameter in the ipc buffer. struct ParamInfo { ArgType type_; - uint32 offset_; - uint32 size_; + uint32_t offset_; + uint32_t size_; }; // Models the return value and the return parameters of an IPC call @@ -76,7 +75,7 @@ struct ParamInfo { // might have to use other integer types. struct CrossCallReturn { // the IPC tag. It should match the original IPC tag. - uint32 tag; + uint32_t tag; // The result of the IPC operation itself. ResultCode call_outcome; // the result of the IPC call as executed in the server. The interpretation @@ -86,7 +85,7 @@ struct CrossCallReturn { DWORD win32_result; }; // Number of extended return values. - uint32 extended_count; + uint32_t extended_count; // for calls that should return a windows handle. It is found here. HANDLE handle; // The array of extended values. @@ -107,9 +106,7 @@ struct CrossCallReturn { class CrossCallParams { public: // Returns the tag (ipc unique id) associated with this IPC. - uint32 GetTag() const { - return tag_; - } + uint32_t GetTag() const { return tag_; } // Returns the beggining of the buffer where the IPC params can be stored. // prior to an IPC call @@ -118,9 +115,7 @@ class CrossCallParams { } // Returns how many parameter this IPC call should have. - const uint32 GetParamsCount() const { - return params_count_; - } + uint32_t GetParamsCount() const { return params_count_; } // Returns a pointer to the CrossCallReturn structure. CrossCallReturn* GetCallReturn() { @@ -128,9 +123,7 @@ class CrossCallParams { } // Returns TRUE if this call contains InOut parameters. - const bool IsInOut() const { - return (1 == is_in_out_); - } + bool IsInOut() const { return (1 == is_in_out_); } // Tells the CrossCall object if it contains InOut parameters. void SetIsInOut(bool value) { @@ -142,17 +135,14 @@ class CrossCallParams { 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) { - } + CrossCallParams(uint32_t tag, uint32_t params_count) + : tag_(tag), is_in_out_(0), params_count_(params_count) {} private: - uint32 tag_; - uint32 is_in_out_; + uint32_t tag_; + uint32_t is_in_out_; CrossCallReturn call_return; - const uint32 params_count_; + const uint32_t params_count_; DISALLOW_COPY_AND_ASSIGN(CrossCallParams); }; @@ -198,37 +188,40 @@ template class ActualCallParams : public CrossCallParams { public: // constructor. Pass the ipc unique tag as input - explicit ActualCallParams(uint32 tag) + explicit ActualCallParams(uint32_t tag) : CrossCallParams(tag, NUMBER_PARAMS) { param_info_[0].offset_ = - static_cast(parameters_ - reinterpret_cast(this)); + static_cast(parameters_ - reinterpret_cast(this)); } // Testing-only constructor. Allows setting the |number_params| to a // wrong value. - ActualCallParams(uint32 tag, uint32 number_params) + ActualCallParams(uint32_t tag, uint32_t number_params) : CrossCallParams(tag, number_params) { param_info_[0].offset_ = - static_cast(parameters_ - reinterpret_cast(this)); + static_cast(parameters_ - reinterpret_cast(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_; + uint32_t OverrideSize(uint32_t new_size) { + uint32_t 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) { + bool CopyParamIn(uint32_t index, + const void* parameter_address, + uint32_t size, + bool is_in_out, + ArgType type) { if (index >= NUMBER_PARAMS) { return false; } - if (kuint32max == size) { + if (UINT32_MAX == size) { // Memory error while getting the size. return false; } @@ -273,9 +266,7 @@ class ActualCallParams : public CrossCallParams { // 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_; - } + uint32_t GetSize() const { return param_info_[NUMBER_PARAMS].offset_; } protected: ActualCallParams() : CrossCallParams(0, NUMBER_PARAMS) { } @@ -287,9 +278,9 @@ class ActualCallParams : public CrossCallParams { DISALLOW_COPY_AND_ASSIGN(ActualCallParams); }; -COMPILE_ASSERT(sizeof(ActualCallParams<1, 1024>) == 1024, bad_size_buffer); -COMPILE_ASSERT(sizeof(ActualCallParams<2, 1024>) == 1024, bad_size_buffer); -COMPILE_ASSERT(sizeof(ActualCallParams<3, 1024>) == 1024, bad_size_buffer); +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 diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc index a01af6644239..9f71f333f02d 100644 --- a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc +++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc @@ -2,13 +2,17 @@ // 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 +#include + #include #include -#include "sandbox/win/src/crosscall_server.h" -#include "sandbox/win/src/crosscall_params.h" -#include "sandbox/win/src/crosscall_client.h" #include "base/logging.h" +#include "sandbox/win/src/crosscall_client.h" +#include "sandbox/win/src/crosscall_params.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 @@ -25,7 +29,7 @@ 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) { +uint32_t GetActualBufferSize(uint32_t 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; @@ -65,8 +69,9 @@ uint32 GetActualBufferSize(uint32 param_count, void* buffer_base) { } // Verifies that the declared sizes of an IPC buffer are within range. -bool IsSizeWithinRange(uint32 buffer_size, uint32 min_declared_size, - uint32 declared_size) { +bool IsSizeWithinRange(uint32_t buffer_size, + uint32_t min_declared_size, + uint32_t declared_size) { if ((buffer_size < min_declared_size) || (sizeof(CrossCallParamsEx) > min_declared_size)) { // Minimal computed size bigger than existing buffer or param_count @@ -103,8 +108,8 @@ void CrossCallParamsEx::operator delete(void* raw_memory) throw() { // 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) { + uint32_t buffer_size, + uint32_t* output_size) { // IMPORTANT: Everything inside buffer_base and derived from it such // as param_count and declared_size is untrusted. if (NULL == buffer_base) { @@ -118,9 +123,9 @@ CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, } char* backing_mem = NULL; - uint32 param_count = 0; - uint32 declared_size; - uint32 min_declared_size; + uint32_t param_count = 0; + uint32_t declared_size; + uint32_t min_declared_size; CrossCallParamsEx* copied_params = NULL; // Touching the untrusted buffer is done under a SEH try block. This @@ -175,8 +180,8 @@ CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, // 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; + for (uint32_t ix = 0; ix != param_count; ++ix) { + uint32_t size = 0; ArgType type; char* address = reinterpret_cast( copied_params->GetRawParameter(ix, &size, &type)); @@ -197,7 +202,8 @@ CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base, } // Accessors to the parameters in the raw buffer. -void* CrossCallParamsEx::GetRawParameter(uint32 index, uint32* size, +void* CrossCallParamsEx::GetRawParameter(uint32_t index, + uint32_t* size, ArgType* type) { if (index >= GetParamsCount()) { return NULL; @@ -211,20 +217,20 @@ void* CrossCallParamsEx::GetRawParameter(uint32 index, uint32* size, } // Covers common case for 32 bit integers. -bool CrossCallParamsEx::GetParameter32(uint32 index, uint32* param) { - uint32 size = 0; +bool CrossCallParamsEx::GetParameter32(uint32_t index, uint32_t* param) { + uint32_t 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(param)) = *(reinterpret_cast(start)); + *(reinterpret_cast(param)) = *(reinterpret_cast(start)); return true; } -bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) { - uint32 size = 0; +bool CrossCallParamsEx::GetParameterVoidPtr(uint32_t index, void** param) { + uint32_t size = 0; ArgType type; void* start = GetRawParameter(index, &size, &type); if ((NULL == start) || (sizeof(void*) != size) || (VOIDPTR_TYPE != type)) { @@ -236,8 +242,9 @@ bool CrossCallParamsEx::GetParameterVoidPtr(uint32 index, void** param) { // Covers the common case of reading a string. Note that the string is not // scanned for invalid characters. -bool CrossCallParamsEx::GetParameterStr(uint32 index, base::string16* string) { - uint32 size = 0; +bool CrossCallParamsEx::GetParameterStr(uint32_t index, + base::string16* string) { + uint32_t size = 0; ArgType type; void* start = GetRawParameter(index, &size, &type); if (WCHAR_TYPE != type) { @@ -257,9 +264,10 @@ bool CrossCallParamsEx::GetParameterStr(uint32 index, base::string16* string) { return true; } -bool CrossCallParamsEx::GetParameterPtr(uint32 index, uint32 expected_size, +bool CrossCallParamsEx::GetParameterPtr(uint32_t index, + uint32_t expected_size, void** pointer) { - uint32 size = 0; + uint32_t size = 0; ArgType type; void* start = GetRawParameter(index, &size, &type); @@ -297,4 +305,10 @@ Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc, return NULL; } +Dispatcher::Dispatcher() { +} + +Dispatcher::~Dispatcher() { +} + } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_server.h b/security/sandbox/chromium/sandbox/win/src/crosscall_server.h index e75400654120..0820ac440a62 100644 --- a/security/sandbox/chromium/sandbox/win/src/crosscall_server.h +++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.h @@ -5,10 +5,13 @@ #ifndef SANDBOX_SRC_CROSSCALL_SERVER_H_ #define SANDBOX_SRC_CROSSCALL_SERVER_H_ +#include + #include #include -#include "base/basictypes.h" + #include "base/callback.h" +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_params.h" @@ -94,31 +97,31 @@ class CrossCallParamsEx : public CrossCallParams { // 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); + uint32_t buffer_size, + uint32_t* 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); + void* GetRawParameter(uint32_t index, uint32_t* 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); + bool GetParameter32(uint32_t index, uint32_t* 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); + bool GetParameterVoidPtr(uint32_t 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); + bool GetParameterStr(uint32_t 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); + bool GetParameterPtr(uint32_t index, uint32_t expected_size, void** pointer); // Frees the memory associated with the IPC parameters. static void operator delete(void* raw_memory) throw(); @@ -143,7 +146,6 @@ void SetCallSuccess(CrossCallReturn* call_return); // process handle and the job object handle that contains the client process. struct ClientInfo { HANDLE process; - HANDLE job_object; DWORD process_id; }; @@ -207,7 +209,8 @@ class Dispatcher { // with the given service (IPC). virtual bool SetupService(InterceptionManager* manager, int service) = 0; - virtual ~Dispatcher() {} + Dispatcher(); + virtual ~Dispatcher(); protected: // Structure that defines an IPC Call with all the parameters and the handler. diff --git a/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc b/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc index 328ee00f2f17..48681e83bc99 100644 --- a/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc +++ b/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/eat_resolver.h" +#include + #include "base/win/pe_image.h" #include "sandbox/win/src/sandbox_nt_util.h" @@ -45,12 +47,8 @@ NTSTATUS EatResolverThunk::Setup(const void* target_module, return ret; // Perform the patch. -#pragma warning(push) -#pragma warning(disable: 4311) - // These casts generate warnings because they are 32 bit specific. - *eat_entry_ = reinterpret_cast(thunk_storage) - - reinterpret_cast(target_module); -#pragma warning(pop) + *eat_entry_ = static_cast(reinterpret_cast(thunk_storage)) - + static_cast(reinterpret_cast(target_module)); if (NULL != storage_used) *storage_used = GetThunkSize(); diff --git a/security/sandbox/chromium/sandbox/win/src/eat_resolver.h b/security/sandbox/chromium/sandbox/win/src/eat_resolver.h index 5e4b6e83cc58..a45d0063b5f1 100644 --- a/security/sandbox/chromium/sandbox/win/src/eat_resolver.h +++ b/security/sandbox/chromium/sandbox/win/src/eat_resolver.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_EAT_RESOLVER_H__ #define SANDBOX_SRC_EAT_RESOLVER_H__ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/resolver.h" @@ -15,25 +17,25 @@ namespace sandbox { class EatResolverThunk : public ResolverThunk { public: EatResolverThunk() : eat_entry_(NULL) {} - virtual ~EatResolverThunk() {} + ~EatResolverThunk() override {} // 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); + 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. - virtual NTSTATUS ResolveTarget(const void* module, - const char* function_name, - void** address); + NTSTATUS ResolveTarget(const void* module, + const char* function_name, + void** address) override; // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; + size_t GetThunkSize() const override; private: // The entry to patch. diff --git a/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc index 8b5236251f5e..f7509bd36efc 100644 --- a/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc @@ -74,7 +74,7 @@ SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t **argv) { SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t **argv) { if (argc != 1) { - SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; + return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } base::string16 full_path = MakePathToSys(argv[0], false); @@ -116,12 +116,12 @@ SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t **argv) { UNICODE_STRING object_name; RtlInitUnicodeString(&object_name, file.c_str()); - OBJECT_ATTRIBUTES obj_attributes = {0}; + OBJECT_ATTRIBUTES obj_attributes = {}; InitializeObjectAttributes(&obj_attributes, &object_name, OBJ_CASE_INSENSITIVE, NULL, NULL); HANDLE handle; - IO_STATUS_BLOCK io_block = {0}; + IO_STATUS_BLOCK io_block = {}; NTSTATUS status = NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, &io_block, NULL, 0, kSharing, FILE_OPEN, 0, NULL, 0); @@ -151,12 +151,12 @@ SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t **argv) { UNICODE_STRING object_name; RtlInitUnicodeString(&object_name, file.c_str()); - OBJECT_ATTRIBUTES obj_attributes = {0}; + OBJECT_ATTRIBUTES obj_attributes = {}; InitializeObjectAttributes(&obj_attributes, &object_name, OBJ_CASE_INSENSITIVE, NULL, NULL); HANDLE handle; - IO_STATUS_BLOCK io_block = {0}; + IO_STATUS_BLOCK io_block = {}; NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes, &io_block, kSharing, 0); if (NT_SUCCESS(status)) { @@ -175,9 +175,9 @@ SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t **argv) { 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}; + ULARGE_INTEGER free_user = {}; + ULARGE_INTEGER total = {}; + ULARGE_INTEGER free_total = {}; if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total, &free_total)) { if ((total.QuadPart != 0) && (free_total.QuadPart !=0)) { @@ -230,12 +230,12 @@ SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t **argv) { base::string16 file = MakePathToSys(argv[0], true); RtlInitUnicodeString(&object_name, file.c_str()); - OBJECT_ATTRIBUTES obj_attributes = {0}; + OBJECT_ATTRIBUTES obj_attributes = {}; InitializeObjectAttributes(&obj_attributes, &object_name, OBJ_CASE_INSENSITIVE, NULL, NULL); - FILE_BASIC_INFORMATION info = {0}; - FILE_NETWORK_OPEN_INFORMATION full_info = {0}; + FILE_BASIC_INFORMATION info = {}; + FILE_NETWORK_OPEN_INFORMATION full_info = {}; NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info); NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info); @@ -310,12 +310,12 @@ TEST(FilePolicyTest, AllowReadOnly) { EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name)); - wchar_t command_read[MAX_PATH + 20] = {0}; + wchar_t command_read[MAX_PATH + 20] = {}; wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name); - wchar_t command_read_create[MAX_PATH + 20] = {0}; + wchar_t command_read_create[MAX_PATH + 20] = {}; wsprintf(command_read_create, L"File_Create ReadCreate \"%ls\"", temp_file_name); - wchar_t command_write[MAX_PATH + 20] = {0}; + wchar_t command_write[MAX_PATH + 20] = {}; wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); // Verify that we cannot create the file after revert. @@ -346,12 +346,12 @@ TEST(FilePolicyTest, AllowImplicitDeviceName) { 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)); + std::wstring path(temp_file_name); + EXPECT_TRUE(ConvertToLongPath(&path)); EXPECT_TRUE(GetNtPathFromWin32Path(path, &path)); path = path.substr(sandbox::kNTDevicePrefixLen); - wchar_t command[MAX_PATH + 20] = {0}; + wchar_t command[MAX_PATH + 20] = {}; wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str()); path = std::wstring(kNTPrefix) + path; @@ -374,7 +374,7 @@ TEST(FilePolicyTest, AllowWildcard) { 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}; + wchar_t command_write[MAX_PATH + 20] = {}; wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name); // Verify that we have write access after revert. @@ -494,8 +494,7 @@ TEST(FilePolicyTest, TestRename) { ::DeleteFile(temp_file_name6); ::DeleteFile(temp_file_name8); - - wchar_t command[MAX_PATH*2 + 20] = {0}; + wchar_t command[MAX_PATH * 2 + 20] = {}; wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1, temp_file_name2); EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command)); diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc index 354fe4f1dc6a..6f96be2402c3 100644 --- a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc +++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/filesystem_dispatcher.h" +#include + #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/filesystem_interception.h" #include "sandbox/win/src/filesystem_policy.h" @@ -20,35 +22,37 @@ 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(&FilesystemDispatcher::NtCreateFile) - }; + {IPC_NTCREATEFILE_TAG, + {WCHAR_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE}}, + reinterpret_cast(&FilesystemDispatcher::NtCreateFile)}; static const IPCCall open_file = { - {IPC_NTOPENFILE_TAG, WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, - UINT32_TYPE}, - reinterpret_cast(&FilesystemDispatcher::NtOpenFile) - }; + {IPC_NTOPENFILE_TAG, + {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast(&FilesystemDispatcher::NtOpenFile)}; static const IPCCall attribs = { - {IPC_NTQUERYATTRIBUTESFILE_TAG, WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}, - reinterpret_cast( - &FilesystemDispatcher::NtQueryAttributesFile) - }; + {IPC_NTQUERYATTRIBUTESFILE_TAG, {WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}}, + reinterpret_cast( + &FilesystemDispatcher::NtQueryAttributesFile)}; static const IPCCall full_attribs = { - {IPC_NTQUERYFULLATTRIBUTESFILE_TAG, WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}, - reinterpret_cast( - &FilesystemDispatcher::NtQueryFullAttributesFile) - }; + {IPC_NTQUERYFULLATTRIBUTESFILE_TAG, + {WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}}, + reinterpret_cast( + &FilesystemDispatcher::NtQueryFullAttributesFile)}; static const IPCCall set_info = { - {IPC_NTSETINFO_RENAME_TAG, VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE, - UINT32_TYPE, UINT32_TYPE}, - reinterpret_cast( - &FilesystemDispatcher::NtSetInformationFile) - }; + {IPC_NTSETINFO_RENAME_TAG, + {VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE, UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &FilesystemDispatcher::NtSetInformationFile)}; ipc_calls_.push_back(create_params); ipc_calls_.push_back(open_file); @@ -84,13 +88,13 @@ bool FilesystemDispatcher::SetupService(InterceptionManager* manager, 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)) { + uint32_t attributes, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options) { + if (!PreProcessName(name)) { // The path requested might contain a reparse point. ipc->return_info.nt_status = STATUS_ACCESS_DENIED; return true; @@ -98,10 +102,11 @@ bool FilesystemDispatcher::NtCreateFile(IPCInfo* ipc, const wchar_t* filename = name->c_str(); - uint32 broker = TRUE; + uint32_t broker = TRUE; CountedParameterSet 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); @@ -131,11 +136,11 @@ bool FilesystemDispatcher::NtCreateFile(IPCInfo* ipc, bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc, base::string16* name, - uint32 attributes, - uint32 desired_access, - uint32 share_access, - uint32 open_options) { - if (!PreProcessName(*name, name)) { + uint32_t attributes, + uint32_t desired_access, + uint32_t share_access, + uint32_t open_options) { + if (!PreProcessName(name)) { // The path requested might contain a reparse point. ipc->return_info.nt_status = STATUS_ACCESS_DENIED; return true; @@ -143,10 +148,12 @@ bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc, const wchar_t* filename = name->c_str(); - uint32 broker = TRUE; + uint32_t broker = TRUE; + uint32_t create_disposition = FILE_OPEN; CountedParameterSet 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); @@ -174,18 +181,18 @@ bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc, bool FilesystemDispatcher::NtQueryAttributesFile(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, CountedBuffer* info) { if (sizeof(FILE_BASIC_INFORMATION) != info->Size()) return false; - if (!PreProcessName(*name, name)) { + if (!PreProcessName(name)) { // The path requested might contain a reparse point. ipc->return_info.nt_status = STATUS_ACCESS_DENIED; return true; } - uint32 broker = TRUE; + uint32_t broker = TRUE; const wchar_t* filename = name->c_str(); CountedParameterSet params; params[FileName::NAME] = ParamPickerMake(filename); @@ -214,18 +221,18 @@ bool FilesystemDispatcher::NtQueryAttributesFile(IPCInfo* ipc, bool FilesystemDispatcher::NtQueryFullAttributesFile(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, CountedBuffer* info) { if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size()) return false; - if (!PreProcessName(*name, name)) { + if (!PreProcessName(name)) { // The path requested might contain a reparse point. ipc->return_info.nt_status = STATUS_ACCESS_DENIED; return true; } - uint32 broker = TRUE; + uint32_t broker = TRUE; const wchar_t* filename = name->c_str(); CountedParameterSet params; params[FileName::NAME] = ParamPickerMake(filename); @@ -258,8 +265,8 @@ bool FilesystemDispatcher::NtSetInformationFile(IPCInfo* ipc, HANDLE handle, CountedBuffer* status, CountedBuffer* info, - uint32 length, - uint32 info_class) { + uint32_t length, + uint32_t info_class) { if (sizeof(IO_STATUS_BLOCK) != status->Size()) return false; if (length != info->Size()) @@ -274,13 +281,13 @@ bool FilesystemDispatcher::NtSetInformationFile(IPCInfo* ipc, base::string16 name; name.assign(rename_info->FileName, rename_info->FileNameLength / sizeof(rename_info->FileName[0])); - if (!PreProcessName(name, &name)) { + if (!PreProcessName(&name)) { // The path requested might contain a reparse point. ipc->return_info.nt_status = STATUS_ACCESS_DENIED; return true; } - uint32 broker = TRUE; + uint32_t broker = TRUE; const wchar_t* filename = name.c_str(); CountedParameterSet params; params[FileName::NAME] = ParamPickerMake(filename); diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h index 935a084bc8dd..61d918ed0586 100644 --- a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ #define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -16,42 +18,42 @@ namespace sandbox { class FilesystemDispatcher : public Dispatcher { public: explicit FilesystemDispatcher(PolicyBase* policy_base); - ~FilesystemDispatcher() {} + ~FilesystemDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + 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); + uint32_t attributes, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t 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); + uint32_t attributes, + uint32_t desired_access, + uint32_t share_access, + uint32_t create_options); // Processes IPC requests coming from calls to NtQueryAttributesFile in the // target. bool NtQueryAttributesFile(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, CountedBuffer* info); // Processes IPC requests coming from calls to NtQueryFullAttributesFile in // the target. bool NtQueryFullAttributesFile(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, CountedBuffer* info); // Processes IPC requests coming from calls to NtSetInformationFile with the @@ -60,8 +62,8 @@ class FilesystemDispatcher : public Dispatcher { HANDLE handle, CountedBuffer* status, CountedBuffer* info, - uint32 length, - uint32 info_class); + uint32_t length, + uint32_t info_class); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher); diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc index 950c450a9e10..26d790c5c259 100644 --- a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/filesystem_interception.h" +#include + #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/policy_params.h" @@ -12,7 +14,6 @@ #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sharedmem_ipc_client.h" #include "sandbox/win/src/target_services.h" -#include "mozilla/sandboxing/sandboxLogging.h" namespace sandbox { @@ -32,10 +33,6 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, if (STATUS_ACCESS_DENIED != status) return status; - mozilla::sandboxing::LogBlocked("NtCreateFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -51,18 +48,20 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, if (NULL == memory) break; - uint32 attributes = 0; + uint32_t 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 broker = FALSE; + uint32_t desired_access_uint32 = desired_access; + uint32_t options_uint32 = options; + uint32_t disposition_uint32 = disposition; + uint32_t broker = FALSE; CountedParameterSet 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); @@ -79,20 +78,18 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, if (SBOX_ALL_OK != code) break; + status = answer.nt_status; + if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; + break; __try { *file = answer.handle; io_status->Status = answer.nt_status; io_status->Information = answer.extended[0].ulong_ptr; - status = io_status->Status; } __except(EXCEPTION_EXECUTE_HANDLER) { break; } - mozilla::sandboxing::LogAllowed("NtCreateFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); } while (false); if (name) @@ -112,10 +109,6 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file, if (STATUS_ACCESS_DENIED != status) return status; - mozilla::sandboxing::LogBlocked("NtOpenFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -131,18 +124,20 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file, if (NULL == memory) break; - uint32 attributes; + uint32_t 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 broker = FALSE; + uint32_t desired_access_uint32 = desired_access; + uint32_t options_uint32 = options; + uint32_t disposition_uint32 = FILE_OPEN; + uint32_t broker = FALSE; CountedParameterSet 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); @@ -157,20 +152,18 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, PHANDLE file, if (SBOX_ALL_OK != code) break; + status = answer.nt_status; + if (!NT_SUCCESS(answer.nt_status)) - return answer.nt_status; + break; __try { *file = answer.handle; io_status->Status = answer.nt_status; io_status->Information = answer.extended[0].ulong_ptr; - status = io_status->Status; } __except(EXCEPTION_EXECUTE_HANDLER) { break; } - mozilla::sandboxing::LogAllowed("NtOpenFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); } while (false); if (name) @@ -188,10 +181,6 @@ NTSTATUS WINAPI TargetNtQueryAttributesFile( if (STATUS_ACCESS_DENIED != status) return status; - mozilla::sandboxing::LogBlocked("NtQueryAttributesFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -205,7 +194,7 @@ NTSTATUS WINAPI TargetNtQueryAttributesFile( if (NULL == memory) break; - uint32 attributes = 0; + uint32_t attributes = 0; NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, NULL); if (!NT_SUCCESS(ret) || NULL == name) @@ -214,7 +203,7 @@ NTSTATUS WINAPI TargetNtQueryAttributesFile( InOutCountedBuffer file_info(file_attributes, sizeof(FILE_BASIC_INFORMATION)); - uint32 broker = FALSE; + uint32_t broker = FALSE; CountedParameterSet params; params[FileName::NAME] = ParamPickerMake(name); params[FileName::BROKER] = ParamPickerMake(broker); @@ -227,15 +216,10 @@ NTSTATUS WINAPI TargetNtQueryAttributesFile( ResultCode code = CrossCall(ipc, IPC_NTQUERYATTRIBUTESFILE_TAG, name, attributes, file_info, &answer); - operator delete(name, NT_ALLOC); - if (SBOX_ALL_OK != code) break; - mozilla::sandboxing::LogAllowed("NtQueryAttributesFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - return answer.nt_status; + status = answer.nt_status; } while (false); @@ -255,10 +239,6 @@ NTSTATUS WINAPI TargetNtQueryFullAttributesFile( if (STATUS_ACCESS_DENIED != status) return status; - mozilla::sandboxing::LogBlocked("NtQueryFullAttributesFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -273,7 +253,7 @@ NTSTATUS WINAPI TargetNtQueryFullAttributesFile( if (NULL == memory) break; - uint32 attributes = 0; + uint32_t attributes = 0; NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, NULL); if (!NT_SUCCESS(ret) || NULL == name) @@ -282,7 +262,7 @@ NTSTATUS WINAPI TargetNtQueryFullAttributesFile( InOutCountedBuffer file_info(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION)); - uint32 broker = FALSE; + uint32_t broker = FALSE; CountedParameterSet params; params[FileName::NAME] = ParamPickerMake(name); params[FileName::BROKER] = ParamPickerMake(broker); @@ -295,15 +275,10 @@ NTSTATUS WINAPI TargetNtQueryFullAttributesFile( ResultCode code = CrossCall(ipc, IPC_NTQUERYFULLATTRIBUTESFILE_TAG, name, attributes, file_info, &answer); - operator delete(name, NT_ALLOC); - if (SBOX_ALL_OK != code) break; - mozilla::sandboxing::LogAllowed("NtQueryFullAttributesFile", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - return answer.nt_status; + status = answer.nt_status; } while (false); if (name) @@ -322,8 +297,6 @@ NTSTATUS WINAPI TargetNtSetInformationFile( if (STATUS_ACCESS_DENIED != status) return status; - mozilla::sandboxing::LogBlocked("NtSetInformationFile"); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -362,7 +335,7 @@ NTSTATUS WINAPI TargetNtSetInformationFile( if (!NT_SUCCESS(ret) || !name) break; - uint32 broker = FALSE; + uint32_t broker = FALSE; CountedParameterSet params; params[FileName::NAME] = ParamPickerMake(name); params[FileName::BROKER] = ParamPickerMake(broker); @@ -385,7 +358,6 @@ NTSTATUS WINAPI TargetNtSetInformationFile( break; status = answer.nt_status; - mozilla::sandboxing::LogAllowed("NtSetInformationFile"); } while (false); if (name) diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc index 85d8542ebb18..fe7f62fc245e 100644 --- a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc +++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc @@ -2,18 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include "sandbox/win/src/filesystem_policy.h" #include "base/logging.h" +#include "base/macros.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/sandbox_utils.h" #include "sandbox/win/src/win_utils.h" namespace { @@ -55,6 +58,18 @@ NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, 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 { @@ -67,7 +82,7 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, return false; } - if (!PreProcessName(mod_name, &mod_name)) { + if (!PreProcessName(&mod_name)) { // The path to be added might contain a reparse point. NOTREACHED(); return false; @@ -114,7 +129,9 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, 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; @@ -225,26 +242,29 @@ bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) { 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, + const base::string16& file, + uint32_t attributes, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + HANDLE* handle, NTSTATUS* nt_status, - ULONG_PTR *io_information) { + ULONG_PTR* io_information) { // The only action supported is ASK_BROKER which means create the requested // file as specified. if (ASK_BROKER != eval_result) { *nt_status = STATUS_ACCESS_DENIED; return false; } - IO_STATUS_BLOCK io_block = {0}; - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + IO_STATUS_BLOCK io_block = {}; + UNICODE_STRING uni_name = {}; + OBJECT_ATTRIBUTES obj_attributes = {}; + 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, @@ -256,14 +276,14 @@ bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, 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, + const base::string16& file, + uint32_t attributes, + uint32_t desired_access, + uint32_t share_access, + uint32_t open_options, + HANDLE* handle, NTSTATUS* nt_status, - ULONG_PTR *io_information) { + ULONG_PTR* io_information) { // The only action supported is ASK_BROKER which means open the requested // file as specified. if (ASK_BROKER != eval_result) { @@ -272,10 +292,13 @@ bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, } // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and // CreateDisposition = FILE_OPEN. - IO_STATUS_BLOCK io_block = {0}; - UNICODE_STRING uni_name = {0}; - OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + IO_STATUS_BLOCK io_block = {}; + UNICODE_STRING uni_name = {}; + OBJECT_ATTRIBUTES obj_attributes = {}; + 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, @@ -288,8 +311,8 @@ bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, bool FileSystemPolicy::QueryAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &file, - uint32 attributes, + const base::string16& file, + uint32_t attributes, FILE_BASIC_INFORMATION* file_info, NTSTATUS* nt_status) { // The only action supported is ASK_BROKER which means query the requested @@ -304,7 +327,10 @@ bool FileSystemPolicy::QueryAttributesFileAction( UNICODE_STRING uni_name = {0}; OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + 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; @@ -313,8 +339,8 @@ bool FileSystemPolicy::QueryAttributesFileAction( bool FileSystemPolicy::QueryFullAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &file, - uint32 attributes, + const base::string16& file, + uint32_t attributes, FILE_NETWORK_OPEN_INFORMATION* file_info, NTSTATUS* nt_status) { // The only action supported is ASK_BROKER which means query the requested @@ -329,17 +355,23 @@ bool FileSystemPolicy::QueryFullAttributesFileAction( UNICODE_STRING uni_name = {0}; OBJECT_ATTRIBUTES obj_attributes = {0}; - InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); + 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) { +bool FileSystemPolicy::SetInformationFileAction(EvalResult eval_result, + const ClientInfo& client_info, + HANDLE target_file_handle, + void* file_info, + uint32_t length, + uint32_t 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) { @@ -368,15 +400,14 @@ bool FileSystemPolicy::SetInformationFileAction( return true; } -bool PreProcessName(const base::string16& path, base::string16* new_path) { - ConvertToLongPath(path, new_path); +bool PreProcessName(base::string16* path) { + ConvertToLongPath(path); - bool reparsed = false; - if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed)) - return false; + if (ERROR_NOT_A_REPARSE_POINT == IsReparsePoint(*path)) + return true; - // We can't process reparsed file. - return !reparsed; + // We can't process a reparsed file. + return false; } base::string16 FixNTPrefixForMatch(const base::string16& name) { diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h index ce28344c535e..c2ee160d5045 100644 --- a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h @@ -5,9 +5,10 @@ #ifndef SANDBOX_SRC_FILESYSTEM_POLICY_H__ #define SANDBOX_SRC_FILESYSTEM_POLICY_H__ +#include + #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/nt_internals.h" @@ -40,13 +41,13 @@ class FileSystemPolicy { // '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, + const base::string16& file, + uint32_t attributes, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, HANDLE* handle, NTSTATUS* nt_status, ULONG_PTR* io_information); @@ -58,11 +59,11 @@ class FileSystemPolicy { // '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, + const base::string16& file, + uint32_t attributes, + uint32_t desired_access, + uint32_t share_access, + uint32_t open_options, HANDLE* handle, NTSTATUS* nt_status, ULONG_PTR* io_information); @@ -71,8 +72,8 @@ class FileSystemPolicy { // 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, + const base::string16& file, + uint32_t attributes, FILE_BASIC_INFORMATION* file_info, NTSTATUS* nt_status); @@ -81,8 +82,8 @@ class FileSystemPolicy { static bool QueryFullAttributesFileAction( EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &file, - uint32 attributes, + const base::string16& file, + uint32_t attributes, FILE_NETWORK_OPEN_INFORMATION* file_info, NTSTATUS* nt_status); @@ -92,16 +93,15 @@ class FileSystemPolicy { const ClientInfo& client_info, HANDLE target_file_handle, void* file_info, - uint32 length, - uint32 info_class, + uint32_t length, + uint32_t 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); +// Expands the path and check if it's a reparse point. Returns false if the path +// cannot be trusted. +bool PreProcessName(base::string16* 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) diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer.cc index 4111623038f8..f2012b7e071f 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_closer.cc +++ b/security/sandbox/chromium/sandbox/win/src/handle_closer.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/handle_closer.h" +#include + #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/win/windows_version.h" @@ -32,7 +34,11 @@ namespace sandbox { // Memory buffer mapped from the parent, with the list of handles. SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close; -HandleCloser::HandleCloser() {} +HandleCloser::HandleCloser() { +} + +HandleCloser::~HandleCloser() { +} ResultCode HandleCloser::AddHandle(const base::char16* handle_type, const base::char16* handle_name) { diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer.h b/security/sandbox/chromium/sandbox/win/src/handle_closer.h index 5c2740c01c54..9592230a06c1 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_closer.h +++ b/security/sandbox/chromium/sandbox/win/src/handle_closer.h @@ -5,10 +5,12 @@ #ifndef SANDBOX_SRC_HANDLE_CLOSER_H_ #define SANDBOX_SRC_HANDLE_CLOSER_H_ +#include + #include #include -#include "base/basictypes.h" +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/interception.h" #include "sandbox/win/src/sandbox_types.h" @@ -19,8 +21,7 @@ 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 > - HandleMap; +typedef std::map > HandleMap; // Type and set of corresponding handle names to close. struct HandleListEntry { @@ -43,6 +44,7 @@ SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info; 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. diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc index 07c6a09854d2..6b17f6547a86 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc +++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc @@ -4,6 +4,9 @@ #include "sandbox/win/src/handle_closer_agent.h" +#include +#include + #include "base/logging.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/win_utils.h" @@ -41,15 +44,68 @@ 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 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(dup_dummy) < + reinterpret_cast(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() { +void HandleCloserAgent::InitializeHandlesToClose(bool* is_csrss_connected) { CHECK(g_handles_to_close != NULL); + // Default to connected state + *is_csrss_connected = true; + // 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; + if (!wcscmp(input, L"ALPC Port")) { + *is_csrss_connected = false; + } HandleMap::mapped_type& handle_names = handles_to_close_[input]; input = reinterpret_cast(reinterpret_cast(entry) + entry->offset_to_names); @@ -67,7 +123,7 @@ void HandleCloserAgent::InitializeHandlesToClose() { DCHECK(reinterpret_cast(entry) >= input); DCHECK(reinterpret_cast(entry) - input < - sizeof(size_t) / sizeof(base::char16)); + static_cast(sizeof(size_t) / sizeof(base::char16))); } // Clean up the memory we copied over. @@ -136,6 +192,8 @@ bool HandleCloserAgent::CloseHandles() { return false; if (!::CloseHandle(handle)) return false; + // Attempt to stuff this handle with a new dummy Event. + AttemptToStuffHandleSlot(handle, result->first); } } diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h index 50ebf8959d34..042c98eafd1e 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h +++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h @@ -5,29 +5,39 @@ #ifndef SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ #define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_ -#include "base/basictypes.h" +#include "base/macros.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" +#include "sandbox/win/src/target_services.h" + namespace sandbox { // Target process code to close the handle list copied over from the broker. class HandleCloserAgent { public: - HandleCloserAgent() {} + HandleCloserAgent(); + ~HandleCloserAgent(); // Reads the serialized list from the broker and creates the lookup map. - void InitializeHandlesToClose(); + // Updates is_csrss_connected based on type of handles closed. + void InitializeHandlesToClose(bool* is_csrss_connected); // Closes any handles matching those in the lookup map. bool CloseHandles(); - // True if we have handles waiting to be closed + // 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); }; diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc index 5b7be474f58d..ceba818a7f4b 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc @@ -2,9 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #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" @@ -42,6 +46,26 @@ HANDLE GetMarkerFile(const wchar_t *extension) { 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; @@ -57,7 +81,7 @@ 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') + 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; @@ -65,8 +89,8 @@ SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) { case BEFORE_INIT: // Create a unique marker file that is open while the test is running. // The handles leak, but it will be closed by the test or on exit. - for (int i = 0; i < arraysize(kFileExtensions); ++i) - EXPECT_NE(GetMarkerFile(kFileExtensions[i]), INVALID_HANDLE_VALUE); + for (const wchar_t* kExtension : kFileExtensions) + CHECK_NE(GetMarkerFile(kExtension), INVALID_HANDLE_VALUE); return SBOX_TEST_SUCCEEDED; case AFTER_REVERT: { @@ -104,15 +128,79 @@ SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t **argv) { 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 to_check; + + switch (state++) { + case BEFORE_INIT: + // Create a unique marker file that is open while the test is running. + for (const wchar_t* kExtension : kFileExtensions) { + HANDLE handle = GetMarkerFile(kExtension); + 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 type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + + 32 * sizeof(wchar_t)); + OBJECT_TYPE_INFORMATION* type_info = + reinterpret_cast(&(type_info_buffer[0])); + NTSTATUS rc; + + // Get the type name, reusing the buffer. + ULONG size = static_cast(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( + &(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) { + for (const wchar_t* kExtension : kFileExtensions) { base::string16 handle_name; - base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i])); + base::win::ScopedHandle marker(GetMarkerFile(kExtension)); CHECK(marker.IsValid()); CHECK(sandbox::GetHandleName(marker.Get(), &handle_name)); command += (L" "); @@ -130,9 +218,9 @@ TEST(HandleCloserTest, CloseMarkerFiles) { sandbox::TargetPolicy* policy = runner.GetPolicy(); base::string16 command = base::string16(L"CheckForFileHandles N"); - for (int i = 0; i < arraysize(kFileExtensions); ++i) { + for (const wchar_t* kExtension : kFileExtensions) { base::string16 handle_name; - base::win::ScopedHandle marker(GetMarkerFile(kFileExtensions[i])); + base::win::ScopedHandle marker(GetMarkerFile(kExtension)); CHECK(marker.IsValid()); CHECK(sandbox::GetHandleName(marker.Get(), &handle_name)); CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()), @@ -145,6 +233,24 @@ TEST(HandleCloserTest, CloseMarkerFiles) { "Failed: " << command; } +TEST(HandleCloserTest, CheckStuffedHandle) { + TestRunner runner; + runner.SetTimeout(2000); + runner.SetTestState(EVERY_STATE); + sandbox::TargetPolicy* policy = runner.GetPolicy(); + + for (const wchar_t* kExtension : kFileExtensions) { + base::string16 handle_name; + base::win::ScopedHandle marker(GetMarkerFile(kExtension)); + 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); diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc index 66308f4b4ab3..4b98acd4c3d4 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc +++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/handle_dispatcher.h" +#include + #include "base/win/scoped_handle.h" #include "sandbox/win/src/handle_interception.h" #include "sandbox/win/src/handle_policy.h" @@ -20,10 +22,10 @@ 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(&HandleDispatcher::DuplicateHandleProxy) - }; + {IPC_DUPLICATEHANDLEPROXY_TAG, + {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &HandleDispatcher::DuplicateHandleProxy)}; ipc_calls_.push_back(duplicate_handle_proxy); } @@ -41,9 +43,9 @@ bool HandleDispatcher::SetupService(InterceptionManager* manager, bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc, HANDLE source_handle, - uint32 target_process_id, - uint32 desired_access, - uint32 options) { + uint32_t target_process_id, + uint32_t desired_access, + uint32_t options) { static NtQueryObject QueryObject = NULL; if (!QueryObject) ResolveNTFunctionPtr("NtQueryObject", &QueryObject); diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h index 739045d0a024..24dcdac2a383 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_ #define SANDBOX_SRC_HANDLE_DISPATCHER_H_ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -15,19 +17,19 @@ namespace sandbox { class HandleDispatcher : public Dispatcher { public: explicit HandleDispatcher(PolicyBase* policy_base); - ~HandleDispatcher() {} + ~HandleDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + 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); + uint32_t target_process_id, + uint32_t desired_access, + uint32_t options); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(HandleDispatcher); diff --git a/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc index 8074c416ac3b..d8c2808da3d8 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc @@ -5,6 +5,8 @@ #include #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" @@ -18,37 +20,33 @@ SBOX_TESTS_COMMAND int HandleInheritanceTests_PrintToStdout(int argc, } TEST(HandleInheritanceTests, TestStdoutInheritance) { - wchar_t temp_directory[MAX_PATH]; - wchar_t temp_file_name[MAX_PATH]; - ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u); - ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u); + 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.lpSecurityDescriptor = NULL; attrs.bInheritHandle = TRUE; - HANDLE file_handle = CreateFile( - temp_file_name, GENERIC_WRITE, - FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, - &attrs, OPEN_EXISTING, 0, NULL); - EXPECT_NE(file_handle, INVALID_HANDLE_VALUE); + 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; - EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStdoutHandle(file_handle)); + ASSERT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStdoutHandle(tmp_handle.Get())); int result = runner.RunTest(L"HandleInheritanceTests_PrintToStdout"); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, result); - EXPECT_TRUE(::CloseHandle(file_handle)); + ASSERT_EQ(SBOX_TEST_SUCCEEDED, result); std::string data; - EXPECT_TRUE(base::ReadFileToString(base::FilePath(temp_file_name), &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) { - EXPECT_EQ("Example output to stdout\r\n", data); + ASSERT_EQ("Example output to stdout\r\n", data); } else { - EXPECT_EQ("", data); + ASSERT_EQ("", data); } - - EXPECT_TRUE(::DeleteFile(temp_file_name)); } -} +} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc index 6fe2f117dfa9..a0df8d653d40 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc @@ -10,7 +10,6 @@ #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sharedmem_ipc_client.h" #include "sandbox/win/src/target_services.h" -#include "mozilla/sandboxing/sandboxLogging.h" namespace sandbox { @@ -35,12 +34,10 @@ ResultCode DuplicateHandleProxy(HANDLE source_handle, if (answer.win32_result) { ::SetLastError(answer.win32_result); - mozilla::sandboxing::LogBlocked("DuplicateHandle"); return SBOX_ERROR_GENERIC; } *target_handle = answer.handle; - mozilla::sandboxing::LogAllowed("DuplicateHandle"); return SBOX_ALL_OK; } diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.h b/security/sandbox/chromium/sandbox/win/src/handle_policy.h index ffe54b8ae2e9..29ce5ab6660f 100644 --- a/security/sandbox/chromium/sandbox/win/src/handle_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.h @@ -7,7 +7,6 @@ #include -#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" diff --git a/security/sandbox/chromium/sandbox/win/src/handle_table.cc b/security/sandbox/chromium/sandbox/win/src/handle_table.cc deleted file mode 100644 index 5575dc0fea6f..000000000000 --- a/security/sandbox/chromium/sandbox/win/src/handle_table.cc +++ /dev/null @@ -1,183 +0,0 @@ -// 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 -#include - -#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::Iterator HandleTable::HandlesForProcess(ULONG process_id) const { - SYSTEM_HANDLE_INFORMATION key; - key.ProcessId = static_cast(process_id); - - const SYSTEM_HANDLE_INFORMATION* start = handle_info()->Information; - const SYSTEM_HANDLE_INFORMATION* finish = - &handle_info()->Information[handle_info()->NumberOfHandles]; - - start = std::lower_bound(start, finish, key, CompareHandleEntries); - if (start->ProcessId != process_id) - return Iterator(*this, finish, finish); - finish = std::upper_bound(start, finish, key, CompareHandleEntries); - return Iterator(*this, start, finish); -} - -HandleTable::HandleEntry::HandleEntry( - const SYSTEM_HANDLE_INFORMATION* handle_info_entry) - : handle_entry_(handle_info_entry), last_entry_(0) { -} - -void HandleTable::HandleEntry::UpdateInfo(UpdateType flag) { - static NtQueryObject QueryObject = NULL; - if (!QueryObject) - ResolveNTFunctionPtr("NtQueryObject", &QueryObject); - - NTSTATUS result; - - // Always update the basic type info, but grab the names as needed. - if (needs_info_update()) { - handle_name_.clear(); - type_name_.clear(); - last_entry_ = handle_entry_; - - // Most handle names are very short, so start small and reuse this buffer. - if (type_info_buffer_.empty()) - type_info_buffer_.resize(sizeof(OBJECT_TYPE_INFORMATION) - + (32 * sizeof(wchar_t))); - ULONG size = static_cast(type_info_buffer_.size()); - result = QueryObject(reinterpret_cast(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_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 name; - do { - name.reset(static_cast(malloc(size))); - DCHECK(name.get()); - result = QueryObject(reinterpret_cast( - 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/security/sandbox/chromium/sandbox/win/src/handle_table.h b/security/sandbox/chromium/sandbox/win/src/handle_table.h deleted file mode 100644 index 1b553fae3aa8..000000000000 --- a/security/sandbox/chromium/sandbox/win/src/handle_table.h +++ /dev/null @@ -1,159 +0,0 @@ -// 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 -#include - -#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: - 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( - &(type_info_buffer_[0])); - } - - const SYSTEM_HANDLE_INFORMATION* handle_entry_; - const SYSTEM_HANDLE_INFORMATION* last_entry_; - std::vector 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 ¤t_; } - - private: - const HandleTable& table_; - HandleEntry current_; - const SYSTEM_HANDLE_INFORMATION* end_; - }; - - HandleTable(); - - Iterator begin() const { - return Iterator(*this, handle_info()->Information, - &handle_info()->Information[handle_info()->NumberOfHandles]); - } - - const SYSTEM_HANDLE_INFORMATION_EX* handle_info() const { - return reinterpret_cast( - &(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( - &(handle_info_buffer_[0])); - } - - std::vector handle_info_buffer_; - - DISALLOW_COPY_AND_ASSIGN(HandleTable); -}; - -} // namespace sandbox - -#endif // SANDBOX_SRC_HANDLE_TABLE_H_ diff --git a/security/sandbox/chromium/sandbox/win/src/interception.cc b/security/sandbox/chromium/sandbox/win/src/interception.cc index 5439db65b53b..f0a2a61fb379 100644 --- a/security/sandbox/chromium/sandbox/win/src/interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/interception.cc @@ -5,6 +5,8 @@ // For information about interceptions as a whole see // http://dev.chromium.org/developers/design-documents/sandbox . +#include + #include #include "sandbox/win/src/interception.h" @@ -17,27 +19,31 @@ #include "sandbox/win/src/interception_internal.h" #include "sandbox/win/src/interceptors.h" #include "sandbox/win/src/sandbox.h" +#include "sandbox/win/src/sandbox_rand.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 { +namespace sandbox { -const char kMapViewOfSectionName[] = "NtMapViewOfSection"; -const char kUnmapViewOfSectionName[] = "NtUnmapViewOfSection"; +namespace { // Standard allocation granularity and page size for Windows. const size_t kAllocGranularity = 65536; const size_t kPageSize = 4096; +} // namespace + +namespace internal { + // 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); + GetRandom(&offset); offset &= (kAllocGranularity - 1); } while (offset > (kAllocGranularity - size)); @@ -49,9 +55,7 @@ size_t GetGranularAlignedRandomOffset(size_t size) { return offset & ~(align_size - 1); } -} // namespace - -namespace sandbox { +} // namespace internal SANDBOX_INTERCEPT SharedMemory* g_interceptions; @@ -61,6 +65,12 @@ 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) { @@ -391,7 +401,7 @@ bool InterceptionManager::PatchNtdll(bool hot_patch_needed) { // 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); + size_t thunk_offset = internal::GetGranularAlignedRandomOffset(thunk_bytes); // Split the base and offset along page boundaries. thunk_base += thunk_offset & ~(kPageSize - 1); @@ -478,7 +488,9 @@ bool InterceptionManager::PatchClientFunctions(DllInterceptionData* thunks, #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) + if (os_info->version() >= base::win::VERSION_WIN10) + thunk = new Wow64W10ResolverThunk(child_->Process(), relaxed_); + else if (os_info->version() >= base::win::VERSION_WIN8) thunk = new Wow64W8ResolverThunk(child_->Process(), relaxed_); else thunk = new Wow64ResolverThunk(child_->Process(), relaxed_); diff --git a/security/sandbox/chromium/sandbox/win/src/interception.h b/security/sandbox/chromium/sandbox/win/src/interception.h index 739c81645dfa..4d1ee82ba399 100644 --- a/security/sandbox/chromium/sandbox/win/src/interception.h +++ b/security/sandbox/chromium/sandbox/win/src/interception.h @@ -9,11 +9,13 @@ #ifndef SANDBOX_SRC_INTERCEPTION_H_ #define SANDBOX_SRC_INTERCEPTION_H_ +#include + #include #include -#include "base/basictypes.h" #include "base/gtest_prod_util.h" +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/sandbox_types.h" @@ -134,6 +136,9 @@ class InterceptionManager { 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. diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.cc b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc index b2a66c471f02..1ef688638fd3 100644 --- a/security/sandbox/chromium/sandbox/win/src/interception_agent.cc +++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc @@ -7,11 +7,13 @@ #include "sandbox/win/src/interception_agent.h" +#include + +#include "sandbox/win/src/eat_resolver.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" +#include "sandbox/win/src/sidestep_resolver.h" namespace { diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.h b/security/sandbox/chromium/sandbox/win/src/interception_agent.h index 2762c611fd94..b2bce08b09a0 100644 --- a/security/sandbox/chromium/sandbox/win/src/interception_agent.h +++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.h @@ -9,7 +9,7 @@ #ifndef SANDBOX_SRC_INTERCEPTION_AGENT_H__ #define SANDBOX_SRC_INTERCEPTION_AGENT_H__ -#include "base/basictypes.h" +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/sandbox_types.h" diff --git a/security/sandbox/chromium/sandbox/win/src/interception_internal.h b/security/sandbox/chromium/sandbox/win/src/interception_internal.h index 810478addb6a..45a0557e5efe 100644 --- a/security/sandbox/chromium/sandbox/win/src/interception_internal.h +++ b/security/sandbox/chromium/sandbox/win/src/interception_internal.h @@ -9,6 +9,8 @@ #ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ #define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_ +#include + #include "sandbox/win/src/sandbox_types.h" namespace sandbox { diff --git a/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc b/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc index 0fc9b7cbe2eb..7b7932af67cc 100644 --- a/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc @@ -7,16 +7,25 @@ // included from this file. #include +#include +#include +#include + +#include "base/bits.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/interceptors.h" #include "sandbox/win/src/target_process.h" #include "testing/gtest/include/gtest/gtest.h" namespace sandbox { +namespace internal { +size_t GetGranularAlignedRandomOffset(size_t size); +} + // Walks the settings buffer, verifying that the values make sense and counting // objects. // Arguments: @@ -75,6 +84,45 @@ void WalkBuffer(void* buffer, size_t size, int* num_dlls, int* num_functions, } } +TEST(InterceptionManagerTest, GetGranularAlignedRandomOffset) { + std::set sizes; + + // 544 is current value of interceptions_.size() * sizeof(ThunkData) + + // sizeof(DllInterceptionData). + const size_t kThunkBytes = 544; + + // ciel(log2(544)) = 10. + // Alignment must be 2^10 = 1024. + const size_t kAlignmentBits = base::bits::Log2Ceiling(kThunkBytes); + const size_t kAlignment = static_cast(1) << kAlignmentBits; + + const size_t kAllocGranularity = 65536; + + // Generate enough sample data to ensure there is at least one value in each + // potential bucket. + for (size_t i = 0; i < 1000000; i++) + sizes.insert(internal::GetGranularAlignedRandomOffset(kThunkBytes)); + + size_t prev_val = 0; + size_t min_val = kAllocGranularity; + size_t min_nonzero_val = kAllocGranularity; + size_t max_val = 0; + + for (size_t val : sizes) { + ASSERT_LT(val, kAllocGranularity); + if (prev_val) + ASSERT_EQ(val - prev_val, kAlignment); + if (val) + min_nonzero_val = std::min(val, min_nonzero_val); + min_val = std::min(val, min_val); + prev_val = val; + max_val = std::max(val, max_val); + } + ASSERT_EQ(max_val, kAllocGranularity - kAlignment); + ASSERT_EQ(0u, min_val); + ASSERT_EQ(min_nonzero_val, kAlignment); +} + TEST(InterceptionManagerTest, BufferLayout1) { wchar_t exe_name[MAX_PATH]; ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); @@ -132,7 +180,7 @@ TEST(InterceptionManagerTest, BufferLayout1) { INTERCEPTION_EAT, function, OPEN_KEY_ID); // Verify that all interceptions were added - ASSERT_EQ(18, interceptions.interceptions_.size()); + ASSERT_EQ(18u, interceptions.interceptions_.size()); size_t buffer_size = interceptions.GetBufferSize(); scoped_ptr local_buffer(new BYTE[buffer_size]); @@ -148,7 +196,7 @@ TEST(InterceptionManagerTest, BufferLayout1) { // 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()); + EXPECT_EQ(3u, interceptions.interceptions_.size()); int num_dlls, num_functions, num_names; WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, @@ -185,7 +233,7 @@ TEST(InterceptionManagerTest, BufferLayout2) { INTERCEPTION_SMART_SIDESTEP, function, OPEN_FILE_ID); // Verify that all interceptions were added - ASSERT_EQ(5, interceptions.interceptions_.size()); + ASSERT_EQ(5u, interceptions.interceptions_.size()); size_t buffer_size = interceptions.GetBufferSize(); scoped_ptr local_buffer(new BYTE[buffer_size]); @@ -198,7 +246,7 @@ TEST(InterceptionManagerTest, BufferLayout2) { // 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()); + EXPECT_EQ(1u, interceptions.interceptions_.size()); int num_dlls, num_functions, num_names; WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, diff --git a/security/sandbox/chromium/sandbox/win/src/internal_types.h b/security/sandbox/chromium/sandbox/win/src/internal_types.h index 026bedb06422..e1028189d805 100644 --- a/security/sandbox/chromium/sandbox/win/src/internal_types.h +++ b/security/sandbox/chromium/sandbox/win/src/internal_types.h @@ -5,6 +5,8 @@ #ifndef SANDBOX_WIN_SRC_INTERNAL_TYPES_H_ #define SANDBOX_WIN_SRC_INTERNAL_TYPES_H_ +#include + namespace sandbox { const wchar_t kNtdllName[] = L"ntdll.dll"; @@ -28,18 +30,16 @@ enum ArgType { // Encapsulates a pointer to a buffer and the size of the buffer. class CountedBuffer { public: - CountedBuffer(void* buffer, uint32 size) : size_(size), buffer_(buffer) {} + CountedBuffer(void* buffer, uint32_t size) : size_(size), buffer_(buffer) {} - uint32 Size() const { - return size_; - } + uint32_t Size() const { return size_; } void* Buffer() const { return buffer_; } private: - uint32 size_; + uint32_t size_; void* buffer_; }; diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc b/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc index fc1f2a221f09..c41720da36a3 100644 --- a/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc @@ -2,7 +2,9 @@ // 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 +#include + #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sharedmem_ipc_client.h" @@ -67,11 +69,11 @@ TEST(IPCTest, ChannelMaker) { 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); + EXPECT_EQ(5u, client_control->channels_count); #if defined(_WIN64) - EXPECT_EQ(216, channel_start); + EXPECT_EQ(216u, channel_start); #else - EXPECT_EQ(108, channel_start); + EXPECT_EQ(108u, channel_start); #endif delete[] reinterpret_cast(client_control); } @@ -158,46 +160,46 @@ TEST(IPCTest, CrossCallStrPacking) { SharedMemIPCClient client(mem); CrossCallReturn answer; - uint32 tag1 = 666; + uint32_t 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(client.GetBuffer()); - EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(1u, 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; + uint32_t tag2 = 777; const wchar_t* null_text = NULL; CrossCall(client, tag2, null_text, &answer); actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(1u, actual_params->GetParamsCount()); EXPECT_EQ(tag2, actual_params->GetTag()); - uint32 param_size = 1; + uint32_t param_size = 1; ArgType type = INVALID_TYPE; void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); EXPECT_TRUE(NULL != param_addr); - EXPECT_EQ(0, param_size); + EXPECT_EQ(0u, param_size); EXPECT_EQ(WCHAR_TYPE, type); EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); - uint32 tag3 = 888; + uint32_t 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(client.GetBuffer()); - EXPECT_EQ(2, actual_params->GetParamsCount()); + EXPECT_EQ(2u, actual_params->GetParamsCount()); EXPECT_EQ(tag3, actual_params->GetTag()); type = INVALID_TYPE; param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); EXPECT_TRUE(NULL != param_addr); - EXPECT_EQ(0, param_size); + EXPECT_EQ(0u, param_size); EXPECT_EQ(WCHAR_TYPE, type); EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text)); EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text)); @@ -209,7 +211,7 @@ TEST(IPCTest, CrossCallStrPacking) { const wchar_t *text2 = L"AeFG"; CrossCall(client, tag1, text2, null_text, text, &answer); actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(3, actual_params->GetParamsCount()); + EXPECT_EQ(3u, 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()); @@ -218,7 +220,7 @@ TEST(IPCTest, CrossCallStrPacking) { type = INVALID_TYPE; param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); EXPECT_TRUE(NULL != param_addr); - EXPECT_EQ(0, param_size); + EXPECT_EQ(0u, param_size); EXPECT_EQ(WCHAR_TYPE, type); CloseChannelEvents(client_control); @@ -233,8 +235,8 @@ TEST(IPCTest, CrossCallIntPacking) { client_control->server_alive = HANDLE(1); FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY); - uint32 tag1 = 999; - uint32 tag2 = 111; + uint32_t tag1 = 999; + uint32_t tag2 = 111; const wchar_t *text = L"godzilla"; CrossCallParamsEx* actual_params; @@ -245,10 +247,10 @@ TEST(IPCTest, CrossCallIntPacking) { DWORD dw = 0xE6578; CrossCall(client, tag2, dw, &answer); actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(1, actual_params->GetParamsCount()); + EXPECT_EQ(1u, actual_params->GetParamsCount()); EXPECT_EQ(tag2, actual_params->GetTag()); ArgType type = INVALID_TYPE; - uint32 param_size = 1; + uint32_t param_size = 1; void* param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); ASSERT_EQ(sizeof(dw), param_size); EXPECT_EQ(UINT32_TYPE, type); @@ -259,7 +261,7 @@ TEST(IPCTest, CrossCallIntPacking) { HANDLE h = HANDLE(0x70000500); CrossCall(client, tag1, text, h, &answer); actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(2, actual_params->GetParamsCount()); + EXPECT_EQ(2u, actual_params->GetParamsCount()); EXPECT_EQ(tag1, actual_params->GetTag()); type = INVALID_TYPE; param_addr = actual_params->GetRawParameter(1, ¶m_size, &type); @@ -271,7 +273,7 @@ TEST(IPCTest, CrossCallIntPacking) { // Check combination of 32 and 64 bits. CrossCall(client, tag2, h, dw, h, &answer); actual_params = reinterpret_cast(client.GetBuffer()); - EXPECT_EQ(3, actual_params->GetParamsCount()); + EXPECT_EQ(3u, actual_params->GetParamsCount()); EXPECT_EQ(tag2, actual_params->GetTag()); type = INVALID_TYPE; param_addr = actual_params->GetRawParameter(0, ¶m_size, &type); @@ -299,28 +301,28 @@ TEST(IPCTest, CrossCallIntPacking) { 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; + const uint32_t kTag = 33; + const uint32_t kBufferSize = 256; ActualCallParams<1, kBufferSize> params_1(kTag); params_1.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE); void* buffer = const_cast(params_1.GetBuffer()); - uint32 out_size = 0; + uint32_t 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()); + EXPECT_EQ(1u, ccp->GetParamsCount()); delete[] (reinterpret_cast(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; + const int32_t kPtrDiffSz = sizeof(ptrdiff_t); + for (int32_t ix = -1; ix != 3; ++ix) { + uint32_t fake_num_params = (UINT32_MAX / kPtrDiffSz) + ix; ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params); params_2.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE); buffer = const_cast(params_2.GetBuffer()); @@ -337,7 +339,7 @@ TEST(IPCTest, CrossCallValidation) { buffer = const_cast(params_3.GetBuffer()); EXPECT_TRUE(NULL != buffer); - uint32 correct_size = params_3.OverrideSize(1); + uint32_t correct_size = params_3.OverrideSize(1); ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size); EXPECT_TRUE(NULL == ccp); @@ -389,11 +391,8 @@ DWORD WINAPI QuickResponseServer(PVOID param) { class CrossCallParamsMock : public CrossCallParams { public: - CrossCallParamsMock(uint32 tag, uint32 params_count) - : CrossCallParams(tag, params_count) { - } - private: - void* params[4]; + CrossCallParamsMock(uint32_t tag, uint32_t params_count) + : CrossCallParams(tag, params_count) {} }; void FakeOkAnswerInChannel(void* channel) { @@ -436,9 +435,9 @@ TEST(IPCTest, ClientFastServer) { 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); + EXPECT_EQ(0u, client_control->channels[1].ipc_tag); - uint32 tag = 7654; + uint32_t tag = 7654; CrossCallReturn answer; CrossCallParamsMock* params1 = new(buff1) CrossCallParamsMock(tag, 1); FakeOkAnswerInChannel(buff1); @@ -537,7 +536,7 @@ TEST(IPCTest, ClientSlowServer) { ::Sleep(1); void* buff0 = client.GetBuffer(); - uint32 tag = 4321; + uint32_t tag = 4321; CrossCallReturn answer; CrossCallParamsMock* params1 = new(buff0) CrossCallParamsMock(tag, 1); FakeOkAnswerInChannel(buff0); @@ -565,35 +564,29 @@ class UnitTestIPCDispatcher : public Dispatcher { }; UnitTestIPCDispatcher(); - ~UnitTestIPCDispatcher() {}; + ~UnitTestIPCDispatcher() override{}; - virtual bool SetupService(InterceptionManager* manager, int service) { + bool SetupService(InterceptionManager* manager, int service) override { return true; } private: - bool CallOneHandler(IPCInfo* ipc, HANDLE p1, uint32 p2) { + bool CallOneHandler(IPCInfo* ipc, HANDLE p1, uint32_t 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; - } + bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, uint32_t p2) { return true; } }; UnitTestIPCDispatcher::UnitTestIPCDispatcher() { - static const IPCCall call_one = { - {CALL_ONE_TAG, VOIDPTR_TYPE, UINT32_TYPE}, - reinterpret_cast( - &UnitTestIPCDispatcher::CallOneHandler) - }; - static const IPCCall call_two = { - {CALL_TWO_TAG, VOIDPTR_TYPE, UINT32_TYPE}, - reinterpret_cast( - &UnitTestIPCDispatcher::CallTwoHandler) - }; + static const IPCCall call_one = {{CALL_ONE_TAG, {VOIDPTR_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &UnitTestIPCDispatcher::CallOneHandler)}; + static const IPCCall call_two = {{CALL_TWO_TAG, {VOIDPTR_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &UnitTestIPCDispatcher::CallTwoHandler)}; ipc_calls_.push_back(call_one); ipc_calls_.push_back(call_two); } @@ -620,10 +613,10 @@ TEST(IPCTest, SharedMemServerTests) { 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(client_control), - NULL, &dispatcher, {0} }; + sandbox::SharedMemIPCServer::ServerControl srv_control = {}; + srv_control.channel_size = kIPCChannelSize; + srv_control.shared_base = reinterpret_cast(client_control); + srv_control.dispatcher = &dispatcher; sandbox::CrossCallReturn call_return = {0}; EXPECT_TRUE(SharedMemIPCServer::InvokeCallback(&srv_control, buff, diff --git a/security/sandbox/chromium/sandbox/win/src/job.cc b/security/sandbox/chromium/sandbox/win/src/job.cc index 8852ab0c7205..adf392ddd437 100644 --- a/security/sandbox/chromium/sandbox/win/src/job.cc +++ b/security/sandbox/chromium/sandbox/win/src/job.cc @@ -4,30 +4,33 @@ #include "sandbox/win/src/job.h" +#include + #include "base/win/windows_version.h" #include "sandbox/win/src/restricted_token.h" namespace sandbox { +Job::Job() : job_handle_(NULL) { +}; + 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_) + if (job_handle_.IsValid()) return ERROR_ALREADY_INITIALIZED; - job_handle_ = ::CreateJobObject(NULL, // No security attribute - job_name); - if (!job_handle_) + job_handle_.Set(::CreateJobObject(NULL, // No security attribute + job_name)); + if (!job_handle_.IsValid()) return ::GetLastError(); - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0}; - JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {}; + JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {}; // Set the settings for the different security levels. Note: The higher levels // inherit from the lower levels. @@ -68,7 +71,7 @@ DWORD Job::Init(JobLevel security_level, } } - if (FALSE == ::SetInformationJobObject(job_handle_, + if (FALSE == ::SetInformationJobObject(job_handle_.Get(), JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) { @@ -76,7 +79,7 @@ DWORD Job::Init(JobLevel security_level, } jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions); - if (FALSE == ::SetInformationJobObject(job_handle_, + if (FALSE == ::SetInformationJobObject(job_handle_.Get(), JobObjectBasicUIRestrictions, &jbur, sizeof(jbur))) { @@ -87,11 +90,11 @@ DWORD Job::Init(JobLevel security_level, } DWORD Job::UserHandleGrantAccess(HANDLE handle) { - if (!job_handle_) + if (!job_handle_.IsValid()) return ERROR_NO_DATA; if (!::UserHandleGrantAccess(handle, - job_handle_, + job_handle_.Get(), TRUE)) { // Access allowed. return ::GetLastError(); } @@ -99,17 +102,15 @@ DWORD Job::UserHandleGrantAccess(HANDLE handle) { return ERROR_SUCCESS; } -HANDLE Job::Detach() { - HANDLE handle_temp = job_handle_; - job_handle_ = NULL; - return handle_temp; +base::win::ScopedHandle Job::Take() { + return job_handle_.Pass(); } DWORD Job::AssignProcessToJob(HANDLE process_handle) { - if (!job_handle_) + if (!job_handle_.IsValid()) return ERROR_NO_DATA; - if (FALSE == ::AssignProcessToJobObject(job_handle_, process_handle)) + if (FALSE == ::AssignProcessToJobObject(job_handle_.Get(), process_handle)) return ::GetLastError(); return ERROR_SUCCESS; diff --git a/security/sandbox/chromium/sandbox/win/src/job.h b/security/sandbox/chromium/sandbox/win/src/job.h index 60dc3146b709..ad3dd41f2fe3 100644 --- a/security/sandbox/chromium/sandbox/win/src/job.h +++ b/security/sandbox/chromium/sandbox/win/src/job.h @@ -5,7 +5,10 @@ #ifndef SANDBOX_SRC_JOB_H_ #define SANDBOX_SRC_JOB_H_ -#include "base/basictypes.h" +#include + +#include "base/macros.h" +#include "base/win/scoped_handle.h" #include "sandbox/win/src/restricted_token_utils.h" namespace sandbox { @@ -17,7 +20,7 @@ namespace sandbox { // job.AssignProcessToJob(process_handle); class Job { public: - Job() : job_handle_(NULL) { } + Job(); ~Job(); @@ -47,14 +50,13 @@ class Job { // 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(); + // Revokes ownership to the job handle and returns it. + // If the object is not yet initialized, it returns an invalid handle. + base::win::ScopedHandle Take(); private: // Handle to the job referenced by the object. - HANDLE job_handle_; + base::win::ScopedHandle job_handle_; DISALLOW_COPY_AND_ASSIGN(Job); }; diff --git a/security/sandbox/chromium/sandbox/win/src/job_unittest.cc b/security/sandbox/chromium/sandbox/win/src/job_unittest.cc index a1b7acafe3e3..7ed9cf0d7e7f 100644 --- a/security/sandbox/chromium/sandbox/win/src/job_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/job_unittest.cc @@ -16,7 +16,8 @@ TEST(JobTest, TestCreation) { { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); + ASSERT_EQ(static_cast(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, @@ -30,20 +31,21 @@ TEST(JobTest, TestCreation) { // 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()); + ASSERT_EQ(static_cast(ERROR_FILE_NOT_FOUND), ::GetLastError()); } -// Tests the method "Detach". -TEST(JobTest, TestDetach) { - HANDLE job_handle; +// Tests the method "Take". +TEST(JobTest, Take) { + base::win::ScopedHandle 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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); - job_handle = job.Detach(); - ASSERT_TRUE(job_handle != NULL); + job_handle = job.Take(); + ASSERT_TRUE(job_handle.IsValid()); } // Check to be sure that the job is still alive even after the object is gone @@ -56,58 +58,58 @@ TEST(JobTest, TestDetach) { if (job_handle_dup) ::CloseHandle(job_handle_dup); - if (job_handle) - ::CloseHandle(job_handle); + job_handle.Close(); // 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()); + job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, FALSE, L"my_test_job_name"); + ASSERT_TRUE(job_handle_dup == NULL); + ASSERT_EQ(static_cast(ERROR_FILE_NOT_FOUND), ::GetLastError()); } // Tests the ui exceptions TEST(JobTest, TestExceptions) { - HANDLE job_handle; + base::win::ScopedHandle 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)); + ASSERT_EQ(static_cast(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); + job_handle = job.Take(); + ASSERT_TRUE(job_handle.IsValid()); JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; DWORD size = sizeof(jbur); - BOOL result = ::QueryInformationJobObject(job_handle, + BOOL result = ::QueryInformationJobObject(job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size); ASSERT_TRUE(result); - ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, 0); - ::CloseHandle(job_handle); + ASSERT_EQ(0u, jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD); + job_handle.Close(); } // 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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); - job_handle = job.Detach(); - ASSERT_TRUE(job_handle != NULL); + job_handle = job.Take(); + ASSERT_TRUE(job_handle.IsValid()); JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0}; DWORD size = sizeof(jbur); - BOOL result = ::QueryInformationJobObject(job_handle, + BOOL result = ::QueryInformationJobObject(job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size); ASSERT_TRUE(result); - ASSERT_EQ(jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD, - JOB_OBJECT_UILIMIT_READCLIPBOARD); - ::CloseHandle(job_handle); + ASSERT_EQ(static_cast(JOB_OBJECT_UILIMIT_READCLIPBOARD), + jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD); } } @@ -115,51 +117,59 @@ TEST(JobTest, TestExceptions) { TEST(JobTest, DoubleInit) { // Create the job. Job job; - ASSERT_EQ(ERROR_SUCCESS, job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); - ASSERT_EQ(ERROR_ALREADY_INITIALIZED, job.Init(JOB_LOCKDOWN, L"test", 0, 0)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0)); + ASSERT_EQ(static_cast(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); + ASSERT_EQ(static_cast(ERROR_NO_DATA), job.UserHandleGrantAccess(NULL)); + ASSERT_EQ(static_cast(ERROR_NO_DATA), job.AssignProcessToJob(NULL)); + ASSERT_FALSE(job.Take().IsValid()); } // 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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job1.Init(JOB_LOCKDOWN, L"job1", 0, 0)); Job job2; - ASSERT_EQ(ERROR_SUCCESS, job2.Init(JOB_RESTRICTED, L"job2", 0, 0)); + ASSERT_EQ(static_cast(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)); + ASSERT_EQ(static_cast(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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job4.Init(JOB_INTERACTIVE, L"job4", 0, 0)); Job job5; - ASSERT_EQ(ERROR_SUCCESS, job5.Init(JOB_UNPROTECTED, L"job5", 0, 0)); + ASSERT_EQ(static_cast(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)); + ASSERT_EQ(static_cast(ERROR_BAD_ARGUMENTS), + job6.Init(JOB_NONE, L"job6", 0, 0)); Job job7; - ASSERT_EQ(ERROR_BAD_ARGUMENTS, job7.Init( - static_cast(JOB_NONE+1), L"job7", 0, 0)); + ASSERT_EQ(static_cast(ERROR_BAD_ARGUMENTS), + job7.Init(static_cast(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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job.Init(JOB_UNPROTECTED, L"job_test_process", 0, 0)); BOOL result = FALSE; @@ -170,26 +180,25 @@ TEST(JobTest, ProcessInJob) { &temp_process_info); ASSERT_TRUE(result); base::win::ScopedProcessInformation pi(temp_process_info); - ASSERT_EQ(ERROR_SUCCESS, job.AssignProcessToJob(pi.process_handle())); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + job.AssignProcessToJob(pi.process_handle())); // Get the job handle. - HANDLE job_handle = job.Detach(); + base::win::ScopedHandle job_handle = job.Take(); // Check if the process is in the job. JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0}; DWORD size = sizeof(jbpidl); - result = ::QueryInformationJobObject(job_handle, + result = ::QueryInformationJobObject(job_handle.Get(), JobObjectBasicProcessIdList, &jbpidl, size, &size); EXPECT_TRUE(result); - EXPECT_EQ(1, jbpidl.NumberOfAssignedProcesses); - EXPECT_EQ(1, jbpidl.NumberOfProcessIdsInList); + EXPECT_EQ(1u, jbpidl.NumberOfAssignedProcesses); + EXPECT_EQ(1u, 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/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc index 5527319833cb..ea8d38035fe2 100644 --- a/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc +++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc @@ -4,7 +4,8 @@ #include "sandbox/win/src/named_pipe_dispatcher.h" -#include "base/basictypes.h" +#include + #include "base/strings/string_split.h" #include "sandbox/win/src/crosscall_client.h" @@ -23,10 +24,15 @@ 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(&NamedPipeDispatcher::CreateNamedPipe) - }; + {IPC_CREATENAMEDPIPEW_TAG, + {WCHAR_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE}}, + reinterpret_cast(&NamedPipeDispatcher::CreateNamedPipe)}; ipc_calls_.push_back(create_params); } @@ -42,25 +48,24 @@ bool NamedPipeDispatcher::SetupService(InterceptionManager* manager, 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) { + uint32_t open_mode, + uint32_t pipe_mode, + uint32_t max_instances, + uint32_t out_buffer_size, + uint32_t in_buffer_size, + uint32_t default_timeout) { ipc->return_info.win32_result = ERROR_ACCESS_DENIED; ipc->return_info.handle = INVALID_HANDLE_VALUE; - std::vector paths; - std::vector innerpaths; - base::SplitString(*name, '/', &paths); + base::StringPiece16 dotdot(L".."); - for (std::vector::const_iterator iter = paths.begin(); - iter != paths.end(); ++iter) { - base::SplitString(*iter, '\\', &innerpaths); - for (std::vector::const_iterator iter2 = innerpaths.begin(); - iter2 != innerpaths.end(); ++iter2) { - if (*iter2 == L"..") + for (const base::StringPiece16& path : base::SplitStringPiece( + *name, base::string16(1, '/'), + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { + for (const base::StringPiece16& inner : base::SplitStringPiece( + path, base::string16(1, '\\'), + base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { + if (inner == dotdot) return true; } } diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h index 1b87828dcfce..83f45e093f4d 100644 --- a/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ #define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -16,22 +18,22 @@ namespace sandbox { class NamedPipeDispatcher : public Dispatcher { public: explicit NamedPipeDispatcher(PolicyBase* policy_base); - ~NamedPipeDispatcher() {} + ~NamedPipeDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + 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); + uint32_t open_mode, + uint32_t pipe_mode, + uint32_t max_instances, + uint32_t out_buffer_size, + uint32_t in_buffer_size, + uint32_t default_timeout); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(NamedPipeDispatcher); diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc index a18327308775..c62d0931d735 100644 --- a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc @@ -12,7 +12,6 @@ #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sharedmem_ipc_client.h" #include "sandbox/win/src/target_services.h" -#include "mozilla/sandboxing/sandboxLogging.h" namespace sandbox { @@ -28,8 +27,6 @@ HANDLE WINAPI TargetCreateNamedPipeW( if (INVALID_HANDLE_VALUE != pipe) return pipe; - mozilla::sandboxing::LogBlocked("CreateNamedPipeW", pipe_name); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return INVALID_HANDLE_VALUE; @@ -65,7 +62,6 @@ HANDLE WINAPI TargetCreateNamedPipeW( if (ERROR_SUCCESS != answer.win32_result) return INVALID_HANDLE_VALUE; - mozilla::sandboxing::LogAllowed("CreateNamedPipeW", pipe_name); return answer.handle; } while (false); diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h index c904aa3618b8..02aa26c6184b 100644 --- a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h @@ -7,7 +7,6 @@ #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/policy_low_level.h" diff --git a/security/sandbox/chromium/sandbox/win/src/nt_internals.h b/security/sandbox/chromium/sandbox/win/src/nt_internals.h index 8b22e0e9a3d0..2a39d5b94ec9 100644 --- a/security/sandbox/chromium/sandbox/win/src/nt_internals.h +++ b/security/sandbox/chromium/sandbox/win/src/nt_internals.h @@ -8,6 +8,7 @@ #define SANDBOX_WIN_SRC_NT_INTERNALS_H__ #include +#include typedef LONG NTSTATUS; #define NT_SUCCESS(st) (st >= 0) @@ -58,6 +59,7 @@ typedef PSTRING POEM_STRING; typedef CONST STRING* PCOEM_STRING; #define OBJ_CASE_INSENSITIVE 0x00000040L +#define OBJ_OPENIF 0x00000080L typedef struct _OBJECT_ATTRIBUTES { ULONG Length; @@ -307,15 +309,27 @@ typedef enum _PROCESSINFOCLASS { } PROCESSINFOCLASS; typedef PVOID PPEB; -typedef PVOID KPRIORITY; +typedef LONG KPRIORITY; typedef struct _PROCESS_BASIC_INFORMATION { - NTSTATUS ExitStatus; + union { + NTSTATUS ExitStatus; + PVOID padding_for_x64_0; + }; PPEB PebBaseAddress; KAFFINITY AffinityMask; - KPRIORITY BasePriority; - ULONG UniqueProcessId; - ULONG InheritedFromUniqueProcessId; + union { + KPRIORITY BasePriority; + PVOID padding_for_x64_1; + }; + union { + DWORD UniqueProcessId; + PVOID padding_for_x64_2; + }; + union { + DWORD InheritedFromUniqueProcessId; + PVOID padding_for_x64_3; + }; } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( @@ -450,8 +464,8 @@ typedef NTSTATUS (WINAPI *NtQueryVirtualMemoryFunction)( IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, - IN ULONG MemoryInformationLength, - OUT PULONG ReturnLength OPTIONAL); + IN SIZE_T MemoryInformationLength, + OUT PSIZE_T ReturnLength OPTIONAL); typedef NTSTATUS (WINAPI *NtProtectVirtualMemoryFunction)( IN HANDLE ProcessHandle, @@ -635,6 +649,11 @@ typedef enum _EVENT_TYPE { 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, @@ -656,5 +675,29 @@ typedef NTSTATUS (WINAPI* NtOpenSymbolicLinkObjectFunction) ( #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/security/sandbox/chromium/sandbox/win/src/policy_broker.cc b/security/sandbox/chromium/sandbox/win/src/policy_broker.cc index dc5e18c28b9e..a946ebc76f28 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_broker.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_broker.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include "sandbox/win/src/policy_broker.h" @@ -38,7 +40,7 @@ SANDBOX_INTERCEPT NtExports g_nt; if (NULL == g_nt.member) \ return false -bool SetupNtdllImports(TargetProcess *child) { +bool InitGlobalNt() { HMODULE ntdll = ::GetModuleHandle(kNtdllName); base::win::PEImage ntdll_image(ntdll); @@ -75,6 +77,14 @@ bool SetupNtdllImports(TargetProcess *child) { INIT_GLOBAL_RTL(wcslen); INIT_GLOBAL_RTL(memcpy); + return true; +} + +bool SetupNtdllImports(TargetProcess *child) { + if (!InitGlobalNt()) { + return false; + } + #ifndef NDEBUG // Verify that the structure is fully initialized. for (size_t i = 0; i < sizeof(g_nt)/sizeof(void*); i++) diff --git a/security/sandbox/chromium/sandbox/win/src/policy_broker.h b/security/sandbox/chromium/sandbox/win/src/policy_broker.h index 1c5cc26c2360..15d3b21ef6f7 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_broker.h +++ b/security/sandbox/chromium/sandbox/win/src/policy_broker.h @@ -11,6 +11,9 @@ namespace sandbox { class TargetProcess; +// Initializes global imported symbols from ntdll. +bool InitGlobalNt(); + // Sets up interceptions not controlled by explicit policies. bool SetupBasicInterceptions(InterceptionManager* manager); diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc index 24ba119195a5..8dca0d9d6210 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc @@ -4,20 +4,22 @@ #include "sandbox/win/src/policy_engine_opcodes.h" -#include "base/basictypes.h" +#include +#include + #include "sandbox/win/src/sandbox_nt_types.h" #include "sandbox/win/src/sandbox_types.h" namespace { -const unsigned short kMaxUniStrSize = 0xfffc; +const unsigned short kMaxUniStrSize = 0xfffc / sizeof(wchar_t); bool InitStringUnicode(const wchar_t* source, size_t length, UNICODE_STRING* ustring) { + if (length > kMaxUniStrSize) { + return false; + } ustring->Buffer = const_cast(source); ustring->Length = static_cast(length) * sizeof(wchar_t); - if (length > kMaxUniStrSize) { - return false; - } ustring->MaximumLength = (NULL != source) ? ustring->Length + sizeof(wchar_t) : 0; return true; @@ -48,7 +50,7 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* pp, // Opcode OpAlwaysFalse: // Does not require input parameter. -PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32 options) { +PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32_t options) { return MakeBase(OP_ALWAYS_FALSE, options, -1); } @@ -56,9 +58,6 @@ template <> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* param, MatchContext* context) { - UNREFERENCED_PARAMETER(opcode); - UNREFERENCED_PARAMETER(param); - UNREFERENCED_PARAMETER(context); return EVAL_FALSE; } @@ -66,7 +65,7 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, // Opcode OpAlwaysTrue: // Does not require input parameter. -PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32 options) { +PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32_t options) { return MakeBase(OP_ALWAYS_TRUE, options, -1); } @@ -74,9 +73,6 @@ template <> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* param, MatchContext* context) { - UNREFERENCED_PARAMETER(opcode); - UNREFERENCED_PARAMETER(param); - UNREFERENCED_PARAMETER(context); return EVAL_TRUE; } @@ -85,8 +81,7 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, // Does not require input parameter. // Argument 0 contains the actual action to return. -PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, - uint32 options) { +PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, uint32_t options) { PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0); if (NULL == opcode) return NULL; opcode->SetArgument(0, action); @@ -97,8 +92,6 @@ template <> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* param, MatchContext* context) { - UNREFERENCED_PARAMETER(param); - UNREFERENCED_PARAMETER(context); int action = 0; opcode->GetArgument(0, &action); return static_cast(action); @@ -106,13 +99,13 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, ////////////////////////////////////////////////////////////////////////////// // Opcode OpNumberMatch: -// Requires a uint32 or void* in selected_param +// Requires a uint32_t 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* OpcodeFactory::MakeOpNumberMatch(int16_t selected_param, + uint32_t match, + uint32_t options) { PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); if (NULL == opcode) return NULL; opcode->SetArgument(0, match); @@ -120,9 +113,9 @@ PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16 selected_param, return opcode; } -PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16 selected_param, +PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16_t selected_param, const void* match, - uint32 options) { + uint32_t options) { PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param); if (NULL == opcode) return NULL; opcode->SetArgument(0, match); @@ -134,10 +127,9 @@ template <> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* param, MatchContext* context) { - UNREFERENCED_PARAMETER(context); - uint32 value_uint32 = 0; + uint32_t value_uint32 = 0; if (param->Get(&value_uint32)) { - uint32 match_uint32 = 0; + uint32_t match_uint32 = 0; opcode->GetArgument(0, &match_uint32); return (match_uint32 != value_uint32)? EVAL_FALSE : EVAL_TRUE; } else { @@ -153,14 +145,14 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, ////////////////////////////////////////////////////////////////////////////// // Opcode OpNumberMatchRange -// Requires a uint32 in selected_param. +// Requires a uint32_t 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) { +PolicyOpcode* OpcodeFactory::MakeOpNumberMatchRange(int16_t selected_param, + uint32_t lower_bound, + uint32_t upper_bound, + uint32_t options) { if (lower_bound > upper_bound) { return NULL; } @@ -176,12 +168,11 @@ template <> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* param, MatchContext* context) { - UNREFERENCED_PARAMETER(context); - uint32 value = 0; + uint32_t value = 0; if (!param->Get(&value)) return EVAL_ERROR; - uint32 lower_bound = 0; - uint32 upper_bound = 0; + uint32_t lower_bound = 0; + uint32_t upper_bound = 0; opcode->GetArgument(0, &lower_bound); opcode->GetArgument(1, &upper_bound); return((lower_bound <= value) && (upper_bound >= value))? @@ -190,12 +181,12 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, ////////////////////////////////////////////////////////////////////////////// // Opcode OpNumberAndMatch: -// Requires a uint32 in selected_param. +// Requires a uint32_t in selected_param. // Argument 0 is the stored number to match. -PolicyOpcode* OpcodeFactory::MakeOpNumberAndMatch(int16 selected_param, - uint32 match, - uint32 options) { +PolicyOpcode* OpcodeFactory::MakeOpNumberAndMatch(int16_t selected_param, + uint32_t match, + uint32_t options) { PolicyOpcode* opcode = MakeBase(OP_NUMBER_AND_MATCH, options, selected_param); if (NULL == opcode) return NULL; opcode->SetArgument(0, match); @@ -206,11 +197,10 @@ template <> EvalResult OpcodeEval(PolicyOpcode* opcode, const ParameterSet* param, MatchContext* context) { - UNREFERENCED_PARAMETER(context); - uint32 value = 0; + uint32_t value = 0; if (!param->Get(&value)) return EVAL_ERROR; - uint32 number = 0; + uint32_t number = 0; opcode->GetArgument(0, &number); return (number & value)? EVAL_TRUE : EVAL_FALSE; } @@ -224,11 +214,11 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, // as noted in the header file. // Argument 3 is the string matching options. -PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16 selected_param, +PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16_t selected_param, const wchar_t* match_str, int start_position, StringMatchOptions match_opts, - uint32 options) { + uint32_t options) { if (NULL == match_str) { return NULL; } @@ -314,9 +304,10 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, } UNICODE_STRING match_ustr; - InitStringUnicode(match_str, match_len, &match_ustr); UNICODE_STRING source_ustr; - InitStringUnicode(source_str, match_len, &source_ustr); + if (!InitStringUnicode(match_str, match_len, &match_ustr) || + !InitStringUnicode(source_str, match_len, &source_ustr)) + return EVAL_ERROR; if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, case_sensitive)) { @@ -328,9 +319,10 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, } } 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); + if (!InitStringUnicode(match_str, match_len, &match_ustr) || + !InitStringUnicode(source_str, match_len, &source_ustr)) + return EVAL_ERROR; do { if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, @@ -350,8 +342,8 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, // OpcodeMaker (other member functions). PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id, - uint32 options, - int16 selected_param) { + uint32_t options, + int16_t selected_param) { if (memory_size() < sizeof(PolicyOpcode)) { return NULL; } diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h index 741ebe39084d..11a9c813ed9d 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h +++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h @@ -5,7 +5,10 @@ #ifndef SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_ #define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_ -#include "base/basictypes.h" +#include +#include + +#include "base/macros.h" #include "base/numerics/safe_conversions.h" #include "sandbox/win/src/policy_engine_params.h" @@ -84,22 +87,22 @@ enum OpcodeID { // 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; +const uint32_t 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; +const uint32_t kPolNegateEval = 1; // Zero the MatchContext context structure. This happens after the opcode // is evaluated. -const uint32 kPolClearContext = 2; +const uint32_t 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; +const uint32_t kPolUseOREval = 4; // Keeps the evaluation state between opcode evaluations. This is used // for string matching where the next opcode needs to continue matching @@ -108,7 +111,7 @@ const uint32 kPolUseOREval = 4; // as an option kPolClearContext. struct MatchContext { size_t position; - uint32 options; + uint32_t options; MatchContext() { Clear(); @@ -154,7 +157,7 @@ class PolicyOpcode { // from 0 to < kArgumentCount. template void GetArgument(size_t index, T* argument) const { - COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size); + static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size"); *argument = *reinterpret_cast(&arguments_[index].mem); } @@ -162,7 +165,7 @@ class PolicyOpcode { // from 0 to < kArgumentCount. template void SetArgument(size_t index, const T& argument) { - COMPILE_ASSERT(sizeof(T) <= sizeof(arguments_[0]), invalid_size); + static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size"); *reinterpret_cast(&arguments_[index].mem) = argument; } @@ -190,13 +193,11 @@ class PolicyOpcode { } // Returns the stored options such as kPolNegateEval and others. - uint32 GetOptions() const { - return options_; - } + uint32_t GetOptions() const { return options_; } // Sets the stored options such as kPolNegateEval. - void SetOptions(uint32 options) { - options_ = base::checked_cast(options); + void SetOptions(uint32_t options) { + options_ = base::checked_cast(options); } private: @@ -218,11 +219,11 @@ class PolicyOpcode { 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. + int16_t parameter_; + // TODO(cpu): Making |options_| a uint32_t would avoid casting, but causes + // test failures. Somewhere code is relying on the size of this struct. // http://crbug.com/420296 - uint16 options_; + uint16_t options_; OpcodeArgument arguments_[PolicyOpcode::kArgumentCount]; }; @@ -297,39 +298,39 @@ class OpcodeFactory { } // Creates an OpAlwaysFalse opcode. - PolicyOpcode* MakeOpAlwaysFalse(uint32 options); + PolicyOpcode* MakeOpAlwaysFalse(uint32_t options); // Creates an OpAlwaysFalse opcode. - PolicyOpcode* MakeOpAlwaysTrue(uint32 options); + PolicyOpcode* MakeOpAlwaysTrue(uint32_t options); // Creates an OpAction opcode. // action: The action to return when Evaluate() is called. - PolicyOpcode* MakeOpAction(EvalResult action, uint32 options); + PolicyOpcode* MakeOpAction(EvalResult action, uint32_t options); // Creates an OpNumberMatch opcode. - // selected_param: index of the input argument. It must be a uint32 or the + // selected_param: index of the input argument. It must be a uint32_t 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); + PolicyOpcode* MakeOpNumberMatch(int16_t selected_param, + uint32_t match, + uint32_t 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, + PolicyOpcode* MakeOpVoidPtrMatch(int16_t selected_param, const void* match, - uint32 options); + uint32_t 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 + // selected_param: index of the input argument. It must be a uint32_t 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); + PolicyOpcode* MakeOpNumberMatchRange(int16_t selected_param, + uint32_t lower_bound, + uint32_t upper_bound, + uint32_t 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 @@ -344,26 +345,27 @@ class OpcodeFactory { // current implementation. // match_opts: Indicates additional matching flags. Currently CaseInsensitive // is supported. - PolicyOpcode* MakeOpWStringMatch(int16 selected_param, + PolicyOpcode* MakeOpWStringMatch(int16_t selected_param, const wchar_t* match_str, int start_position, StringMatchOptions match_opts, - uint32 options); + uint32_t 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 + // selected_param: index of the input argument. It must be uint32_t 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); + PolicyOpcode* MakeOpNumberAndMatch(int16_t selected_param, + uint32_t match, + uint32_t 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); + PolicyOpcode* MakeBase(OpcodeID opcode_id, + uint32_t options, + int16_t selected_param); // Allocates (and copies) a string (of size length) inside the buffer and // returns the displacement with respect to start. diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h index 5b3c5ef11ce5..fb4c00e3da10 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h +++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h @@ -5,7 +5,8 @@ #ifndef SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ #define SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__ -#include "base/basictypes.h" +#include + #include "sandbox/win/src/internal_types.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/sandbox_nt_util.h" @@ -61,11 +62,11 @@ class ParameterSet { ParameterSet() : real_type_(INVALID_TYPE), address_(NULL) {} // Retrieve the stored parameter. If the type does not match ulong fail. - bool Get(uint32* destination) const { + bool Get(uint32_t* destination) const { if (real_type_ != UINT32_TYPE) { return false; } - *destination = Void2TypePointerCopy(); + *destination = Void2TypePointerCopy(); return true; } @@ -152,9 +153,8 @@ class ParameterSetEx : public ParameterSet { : ParameterSet(WCHAR_TYPE, address) {} }; - -template<> -class ParameterSetEx : public ParameterSet { +template <> +class ParameterSetEx : public ParameterSet { public: ParameterSetEx(const void* address) : ParameterSet(UINT32_TYPE, address) {} diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc index 7ca25b2ed2ec..960db085c37c 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #include "sandbox/win/src/policy_engine_processor.h" namespace sandbox { @@ -23,7 +26,7 @@ EvalResult PolicyProcessor::GetAction() const { bool SkipOpcode(const PolicyOpcode& opcode, MatchContext* context, bool* keep_skipping) { if (opcode.IsAction()) { - uint32 options = context->options; + uint32_t options = context->options; context->Clear(); *keep_skipping = false; return (kPolUseOREval != options); @@ -32,7 +35,7 @@ bool SkipOpcode(const PolicyOpcode& opcode, MatchContext* context, return true; } -PolicyResult PolicyProcessor::Evaluate(uint32 options, +PolicyResult PolicyProcessor::Evaluate(uint32_t options, ParameterSet* parameters, size_t param_count) { if (NULL == policy_) { diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h index 85c999e27e99..0d5689604248 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h +++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h @@ -5,9 +5,12 @@ #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 +#include + +#include "base/macros.h" #include "sandbox/win/src/policy_engine_opcodes.h" +#include "sandbox/win/src/policy_engine_params.h" namespace sandbox { @@ -63,18 +66,18 @@ enum PolicyResult { }; // Policy evaluation flags -// TODO(cpu): implement the options 0 & 4. +// TODO(cpu): implement the options kStopOnErrors & kRankedEval. // // Stop evaluating as soon as an error is encountered. -const uint32 kStopOnErrors = 0; +const uint32_t kStopOnErrors = 1; // Ignore all non fatal opcode evaluation errors. -const uint32 kIgnoreErrors = 1; +const uint32_t kIgnoreErrors = 2; // Short-circuit evaluation: Only evaluate until opcode group that // evaluated to true has been found. -const uint32 kShortEval = 2; +const uint32_t 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 = 4; +const uint32_t kRankedEval = 8; // This class evaluates a policy-opcode stream given the memory where the // opcodes are and an input 'parameter set'. @@ -119,7 +122,7 @@ class PolicyProcessor { // 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, + PolicyResult Evaluate(uint32_t options, ParameterSet* parameters, size_t parameter_count); diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc index 325a101dac56..5572bdcd300a 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #include "sandbox/win/src/policy_engine_params.h" #include "sandbox/win/src/policy_engine_processor.h" #include "testing/gtest/include/gtest/gtest.h" @@ -61,8 +64,8 @@ TEST(PolicyEngineTest, Rules1) { 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; + uint32_t creation_mode = OPEN_EXISTING; + uint32_t flags = FILE_ATTRIBUTE_NORMAL; void* security_descriptor = NULL; POLPARAMS_BEGIN(eval_params) diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc index ef9b4e1fade6..526b23d6b013 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc @@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include - #include "sandbox/win/src/policy_low_level.h" -#include "base/basictypes.h" + +#include +#include + +#include +#include namespace { @@ -22,15 +24,19 @@ namespace { // 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; + const uint32_t kLastCharIsNone = 0; + const uint32_t kLastCharIsAlpha = 1; + const uint32_t kLastCharIsWild = 2; + const uint32_t kLastCharIsAsterisk = kLastCharIsWild + 4; + const uint32_t 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. @@ -63,7 +69,7 @@ LowLevelPolicy::~LowLevelPolicy() { bool LowLevelPolicy::Done() { typedef std::list RuleNodes; typedef std::list RuleList; - typedef std::map Mmap; + typedef std::map Mmap; Mmap mmap; for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) { @@ -76,7 +82,7 @@ bool LowLevelPolicy::Done() { size_t avail_size = policy_store_->data_size; for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) { - uint32 service = (*it).first; + uint32_t service = (*it).first; if (service >= kMaxServiceCount) { return false; } @@ -150,14 +156,16 @@ PolicyRule::PolicyRule(const PolicyRule& other) { // to zero. bool PolicyRule::GenStringOpcode(RuleType rule_type, StringMatchOptions match_opts, - uint16 parameter, int state, bool last_call, - int* skip_count, base::string16* fragment) { - + uint16_t 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; + uint32_t options = kPolNone; if (last_call) { if (IF_NOT == rule_type) { options = kPolClearContext | kPolNegateEval; @@ -214,7 +222,8 @@ bool PolicyRule::GenStringOpcode(RuleType rule_type, return true; } -bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, +bool PolicyRule::AddStringMatch(RuleType rule_type, + int16_t parameter, const wchar_t* string, StringMatchOptions match_opts) { if (done_) { @@ -223,7 +232,7 @@ bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, } const wchar_t* current_char = string; - uint32 last_char = kLastCharIsNone; + uint32_t 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. @@ -275,14 +284,14 @@ bool PolicyRule::AddStringMatch(RuleType rule_type, int16 parameter, } bool PolicyRule::AddNumberMatch(RuleType rule_type, - int16 parameter, - uint32 number, + int16_t parameter, + uint32_t 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; + uint32_t opts = (rule_type == IF_NOT) ? kPolNegateEval : kPolNone; if (EQUAL == comparison_op) { if (NULL == opcode_factory_->MakeOpNumberMatch(parameter, number, opts)) { diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.h b/security/sandbox/chromium/sandbox/win/src/policy_low_level.h index 6b733acac73f..f77787c7723c 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_low_level.h +++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.h @@ -5,13 +5,16 @@ #ifndef SANDBOX_SRC_POLICY_LOW_LEVEL_H__ #define SANDBOX_SRC_POLICY_LOW_LEVEL_H__ +#include +#include + #include -#include "base/basictypes.h" +#include "base/macros.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" +#include "sandbox/win/src/policy_engine_params.h" // Low level policy classes. // Built on top of the PolicyOpcode and OpcodeFatory, the low level policy @@ -41,7 +44,8 @@ namespace sandbox { // TODO(cpu): Move this constant to crosscall_client.h. const size_t kMaxServiceCount = 32; -COMPILE_ASSERT(IPC_LAST_TAG <= kMaxServiceCount, kMaxServiceCount_is_too_low); +static_assert(IPC_LAST_TAG <= kMaxServiceCount, + "kMaxServiceCount is too low"); // Defines the memory layout of the policy. This memory is filled by // LowLevelPolicy object. @@ -81,9 +85,7 @@ class LowLevelPolicy { public: // policy_store: must contain allocated memory and the internal // size fields set to correct values. - explicit LowLevelPolicy(PolicyGlobal* policy_store) - : policy_store_(policy_store) { - } + explicit LowLevelPolicy(PolicyGlobal* policy_store); // Destroys all the policy rules. ~LowLevelPolicy(); @@ -137,8 +139,10 @@ class PolicyRule { // 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); + bool AddStringMatch(RuleType rule_type, + int16_t 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. @@ -146,8 +150,8 @@ class PolicyRule { // 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, + int16_t parameter, + uint32_t number, RuleOp comparison_op); // Returns the number of opcodes generated so far. @@ -164,9 +168,13 @@ class 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); + bool GenStringOpcode(RuleType rule_type, + StringMatchOptions match_opts, + uint16_t 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 diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc index 4081a5885d3d..fd67469f7918 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #include "sandbox/win/src/policy_engine_params.h" #include "sandbox/win/src/policy_engine_processor.h" #include "sandbox/win/src/policy_low_level.h" @@ -56,7 +59,7 @@ TEST(PolicyEngineTest, SimpleStrMatch) { CASE_INSENSITIVE)); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 2; + const uint32_t kFakeService = 2; LowLevelPolicy policyGen(policy); EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); @@ -89,7 +92,7 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatch) { CASE_SENSITIVE)); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 2; + const uint32_t kFakeService = 2; LowLevelPolicy policyGen(policy); EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); @@ -127,7 +130,7 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) { CASE_SENSITIVE)); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; + const uint32_t kFakeService = 3; LowLevelPolicy policyGen(policy); EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); @@ -160,7 +163,7 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) { CASE_SENSITIVE)); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; + const uint32_t kFakeService = 3; LowLevelPolicy policyGen(policy); EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); @@ -199,14 +202,14 @@ TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) { EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL)); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; + const uint32_t kFakeService = 3; LowLevelPolicy policyGen(policy); EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr)); EXPECT_TRUE(policyGen.Done()); const wchar_t* filename = NULL; - uint32 access = 0; + uint32_t access = 0; POLPARAMS_BEGIN(eval_params) POLPARAM(filename) // Argument 0 POLPARAM(access) // Argument 1 @@ -248,15 +251,15 @@ TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) { EXPECT_TRUE(pr.AddNumberMatch(IF, 2, 66, EQUAL)); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kFakeService = 3; + const uint32_t 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; + uint32_t access = 0; + uint32_t sharing = 66; POLPARAMS_BEGIN(eval_params) POLPARAM(filename) // Argument 0 @@ -322,15 +325,15 @@ TEST(PolicyEngineTest, OneRuleTest) { PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kNtFakeCreateFile = 7; + const uint32_t 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; + uint32_t creation_mode = OPEN_EXISTING; + uint32_t flags = FILE_ATTRIBUTE_NORMAL; void* security_descriptor = NULL; POLPARAMS_BEGIN(eval_params) @@ -392,7 +395,7 @@ TEST(PolicyEngineTest, ThreeRulesTest) { EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); size_t opc1 = pr_pipe.GetOpcodeCount(); - EXPECT_EQ(3, opc1); + EXPECT_EQ(3u, opc1); PolicyRule pr_dump(ASK_BROKER); EXPECT_TRUE(pr_dump.AddStringMatch(IF, 0, L"\\\\/?/?\\*\\Crash Reports\\*", @@ -401,7 +404,7 @@ TEST(PolicyEngineTest, ThreeRulesTest) { EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); size_t opc2 = pr_dump.GetOpcodeCount(); - EXPECT_EQ(4, opc2); + EXPECT_EQ(4u, opc2); PolicyRule pr_winexe(SIGNAL_ALARM); EXPECT_TRUE(pr_winexe.AddStringMatch(IF, 0, L"\\\\/?/?\\C:\\Windows\\*.exe", @@ -409,7 +412,7 @@ TEST(PolicyEngineTest, ThreeRulesTest) { EXPECT_TRUE(pr_winexe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); size_t opc3 = pr_winexe.GetOpcodeCount(); - EXPECT_EQ(3, opc3); + EXPECT_EQ(3u, opc3); PolicyRule pr_adobe(GIVE_CACHED); EXPECT_TRUE(pr_adobe.AddStringMatch(IF, 0, L"c:\\adobe\\ver?.?\\", @@ -417,20 +420,20 @@ TEST(PolicyEngineTest, ThreeRulesTest) { EXPECT_TRUE(pr_adobe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL)); size_t opc4 = pr_adobe.GetOpcodeCount(); - EXPECT_EQ(4, opc4); + EXPECT_EQ(4u, 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); + EXPECT_EQ(2u, opc5); PolicyGlobal* policy = MakePolicyMemory(); - const uint32 kNtFakeNone = 4; - const uint32 kNtFakeCreateFile = 5; - const uint32 kNtFakeOpenFile = 6; + const uint32_t kNtFakeNone = 4; + const uint32_t kNtFakeCreateFile = 5; + const uint32_t kNtFakeOpenFile = 6; LowLevelPolicy policyGen(policy); EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_pipe)); @@ -486,8 +489,8 @@ TEST(PolicyEngineTest, ThreeRulesTest) { // Test the policy evaluation. const wchar_t* filename = L""; - uint32 creation_mode = OPEN_EXISTING; - uint32 flags = FILE_ATTRIBUTE_NORMAL; + uint32_t creation_mode = OPEN_EXISTING; + uint32_t flags = FILE_ATTRIBUTE_NORMAL; void* security_descriptor = NULL; POLPARAMS_BEGIN(params) diff --git a/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc index c999348fa8a7..f8c210e8762d 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc @@ -2,10 +2,13 @@ // 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 +#include + #include "sandbox/win/src/policy_engine_opcodes.h" +#include "sandbox/win/src/policy_engine_params.h" +#include "sandbox/win/src/sandbox_nt_types.h" +#include "sandbox/win/src/sandbox_types.h" #include "testing/gtest/include/gtest/gtest.h" @@ -45,7 +48,7 @@ TEST(PolicyEngineTest, ParameterSetTest) { // Test that we can store and retrieve a void pointer: const void* result1 =0; - uint32 result2 = 0; + uint32_t result2 = 0; EXPECT_TRUE(pset1.Get(&result1)); EXPECT_TRUE(pv1 == result1); EXPECT_FALSE(pset1.Get(&result2)); @@ -53,8 +56,8 @@ TEST(PolicyEngineTest, ParameterSetTest) { EXPECT_TRUE(pv2 == result1); EXPECT_FALSE(pset2.Get(&result2)); - // Test that we can store and retrieve a uint32: - uint32 number = 12747; + // Test that we can store and retrieve a uint32_t: + uint32_t number = 12747; ParameterSet pset3 = ParamPickerMake(number); EXPECT_FALSE(pset3.Get(&result1)); EXPECT_TRUE(pset3.Get(&result2)); @@ -122,7 +125,7 @@ TEST(PolicyEngineTest, TrueFalseOpcodes) { context.position = 1; context.options = kPolUseOREval; EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context)); - EXPECT_EQ(0, context.position); + EXPECT_EQ(0u, context.position); MatchContext context2; EXPECT_EQ(context2.options, context.options); } @@ -181,8 +184,8 @@ TEST(PolicyEngineTest, OpcodeMakerCase2) { TEST(PolicyEngineTest, IntegerOpcodes) { const wchar_t* txt = L"abcdef"; - uint32 num1 = 42; - uint32 num2 = 113377; + uint32_t num1 = 42; + uint32_t num2 = 113377; ParameterSet pp_wrong1 = ParamPickerMake(txt); ParameterSet pp_num1 = ParamPickerMake(num1); @@ -221,7 +224,7 @@ TEST(PolicyEngineTest, LogicalOpcodes) { char memory[kOpcodeMemory]; OpcodeFactory opcode_maker(memory, sizeof(memory)); - uint32 num1 = 0x10100702; + uint32_t num1 = 0x10100702; ParameterSet pp_num1 = ParamPickerMake(num1); PolicyOpcode* op_and1 = @@ -280,7 +283,7 @@ TEST(PolicyEngineTest, WCharOpcodes1) { kPolClearContext); ASSERT_NE(nullptr, op4); EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(0, mc1.position); + EXPECT_EQ(0u, mc1.position); // Test that we can properly match the last part of the string PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd, @@ -288,7 +291,7 @@ TEST(PolicyEngineTest, WCharOpcodes1) { kPolClearContext); ASSERT_NE(nullptr, op4b); EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(0, mc1.position); + EXPECT_EQ(0u, mc1.position); // Test matching 'jumps over' over the entire string. This is the // primitive we build '*' from. @@ -296,7 +299,7 @@ TEST(PolicyEngineTest, WCharOpcodes1) { CASE_SENSITIVE, kPolNone); ASSERT_NE(nullptr, op5); EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(24, mc1.position); + EXPECT_EQ(24u, 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, @@ -304,7 +307,7 @@ TEST(PolicyEngineTest, WCharOpcodes1) { kPolNone); ASSERT_NE(nullptr, op5b); EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1)); - EXPECT_EQ(24, mc1.position); + EXPECT_EQ(24u, 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'. @@ -319,12 +322,12 @@ TEST(PolicyEngineTest, WCharOpcodes1) { CASE_SENSITIVE, kPolNone); ASSERT_NE(nullptr, op7); EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2)); - EXPECT_EQ(37, mc2.position); + EXPECT_EQ(37u, 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); + EXPECT_EQ(37u, mc2.position); } TEST(PolicyEngineTest, WCharOpcodes2) { @@ -350,7 +353,7 @@ TEST(PolicyEngineTest, WCharOpcodes2) { 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); + EXPECT_EQ(35u, mc1.position); } TEST(PolicyEngineTest, ActionOpcodes) { diff --git a/security/sandbox/chromium/sandbox/win/src/policy_params.h b/security/sandbox/chromium/sandbox/win/src/policy_params.h index 7ded1fcfc470..e051d2b59d77 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_params.h +++ b/security/sandbox/chromium/sandbox/win/src/policy_params.h @@ -23,6 +23,7 @@ POLPARAMS_BEGIN(OpenFile) POLPARAM(NAME) POLPARAM(BROKER) // TRUE if called from the broker. POLPARAM(ACCESS) + POLPARAM(DISPOSITION) POLPARAM(OPTIONS) POLPARAMS_END(OpenFile) @@ -32,10 +33,10 @@ POLPARAMS_BEGIN(FileName) POLPARAM(BROKER) // TRUE if called from the broker. POLPARAMS_END(FileName) -COMPILE_ASSERT(OpenFile::NAME == static_cast(FileName::NAME), - to_simplify_fs_policies); -COMPILE_ASSERT(OpenFile::BROKER == static_cast(FileName::BROKER), - to_simplify_fs_policies); +static_assert(OpenFile::NAME == static_cast(FileName::NAME), + "to simplify fs policies"); +static_assert(OpenFile::BROKER == static_cast(FileName::BROKER), + "to simplify fs policies"); // Policy parameter for name-based policies. POLPARAMS_BEGIN(NameBased) diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target.cc b/security/sandbox/chromium/sandbox/win/src/policy_target.cc index 84b72037db68..568fe2f3a2bd 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_target.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_target.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/policy_target.h" +#include + #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/policy_engine_processor.h" @@ -79,16 +81,6 @@ NTSTATUS WINAPI TargetNtSetInformationThread( break; if (ThreadImpersonationToken != thread_info_class) break; - if (!thread_information) - break; - HANDLE token; - if (sizeof(token) > thread_information_bytes) - break; - - NTSTATUS ret = CopyData(&token, thread_information, sizeof(token)); - if (!NT_SUCCESS(ret) || NULL != token) - break; - // This is a revert to self. return STATUS_SUCCESS; } while (false); diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc index d1162e80b2f0..d1a82e54d59d 100644 --- a/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc @@ -2,6 +2,9 @@ // 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" @@ -11,6 +14,10 @@ #include "sandbox/win/tests/common/controller.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_WIN) +#include "base/win/win_util.h" +#endif + namespace sandbox { #define BINDNTDLL(name) \ @@ -253,9 +260,10 @@ TEST(PolicyTargetTest, DesktopPolicy) { if (result == SBOX_ALL_OK) target.Set(temp_process_info); - EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); + EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); - EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); + EXPECT_EQ(static_cast(WAIT_TIMEOUT), + ::WaitForSingleObject(target.process_handle(), 2000)); EXPECT_NE(::GetThreadDesktop(target.thread_id()), ::GetThreadDesktop(::GetCurrentThreadId())); @@ -316,9 +324,10 @@ TEST(PolicyTargetTest, WinstaPolicy) { if (result == SBOX_ALL_OK) target.Set(temp_process_info); - EXPECT_EQ(1, ::ResumeThread(target.thread_handle())); + EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); - EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(target.process_handle(), 2000)); + EXPECT_EQ(static_cast(WAIT_TIMEOUT), + ::WaitForSingleObject(target.process_handle(), 2000)); EXPECT_NE(::GetThreadDesktop(target.thread_id()), ::GetThreadDesktop(::GetCurrentThreadId())); @@ -344,4 +353,65 @@ TEST(PolicyTargetTest, WinstaPolicy) { 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().GetHandle()); + + base::string16 arguments(L"\""); + arguments += prog_name; + arguments += L"\" -child 0 shared_memory_handle "; + arguments += base::UintToString16(base::win::HandleToUint32(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(1u, ::ResumeThread(target.thread_handle())); + + EXPECT_EQ(static_cast(WAIT_TIMEOUT), + ::WaitForSingleObject(target.process_handle(), 2000)); + + EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); + + ::WaitForSingleObject(target.process_handle(), INFINITE); +} + } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc index 80e428483052..6ca8a8d7e6ee 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc +++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc @@ -4,10 +4,14 @@ #include "sandbox/win/src/process_mitigations.h" +#include + #include #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/sandbox_rand.h" #include "sandbox/win/src/win_utils.h" namespace { @@ -59,6 +63,13 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { } } + 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; @@ -79,7 +90,6 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { } 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; @@ -114,7 +124,7 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { // Enable ASLR policies. if (flags & MITIGATION_RELOCATE_IMAGE) { - PROCESS_MITIGATION_ASLR_POLICY policy = { 0 }; + PROCESS_MITIGATION_ASLR_POLICY policy = {}; policy.EnableForceRelocateImages = true; policy.DisallowStrippedImages = (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) == @@ -129,7 +139,7 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { // Enable strict handle policies. if (flags & MITIGATION_STRICT_HANDLE_CHECKS) { - PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = { 0 }; + PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = {}; policy.HandleExceptionsPermanentlyEnabled = policy.RaiseExceptionOnInvalidHandleReference = true; @@ -142,7 +152,7 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { // Enable system call policies. if (flags & MITIGATION_WIN32K_DISABLE) { - PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = { 0 }; + PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {}; policy.DisallowWin32kSystemCalls = true; if (!set_process_mitigation_policy(ProcessSystemCallDisablePolicy, &policy, @@ -154,7 +164,7 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) { // Enable system call policies. if (flags & MITIGATION_EXTENSION_DLL_DISABLE) { - PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = { 0 }; + PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = {}; policy.DisableExtensionPoints = true; if (!set_process_mitigation_policy(ProcessExtensionPointDisablePolicy, @@ -278,7 +288,7 @@ bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process, #if !defined(_WIN64) if (flags & MITIGATION_BOTTOM_UP_ASLR) { unsigned int limit; - rand_s(&limit); + GetRandom(&limit); char* ptr = 0; const size_t kMask64k = 0xFFFF; // Random range (512k-16.5mb) in 64k steps. @@ -309,7 +319,8 @@ bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) { MITIGATION_BOTTOM_UP_ASLR | MITIGATION_STRICT_HANDLE_CHECKS | MITIGATION_EXTENSION_DLL_DISABLE | - MITIGATION_DLL_SEARCH_ORDER)); + MITIGATION_DLL_SEARCH_ORDER | + MITIGATION_HARDEN_TOKEN_IL_POLICY)); } bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) { diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations.h index 9039ad677b21..8bee0245a6f0 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_mitigations.h +++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.h @@ -6,8 +6,8 @@ #define SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_ #include +#include -#include "base/basictypes.h" #include "sandbox/win/src/security_level.h" namespace sandbox { diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_test.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_test.cc index 4d2e9c6e9562..080d8eca3fcb 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_mitigations_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_test.cc @@ -30,26 +30,30 @@ typedef BOOL (WINAPI *GetProcessMitigationPolicyFunction)( GetProcessMitigationPolicyFunction get_process_mitigation_policy; +#if !defined(_WIN64) bool CheckWin8DepPolicy() { - PROCESS_MITIGATION_DEP_POLICY policy; + PROCESS_MITIGATION_DEP_POLICY policy = {}; if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessDEPPolicy, &policy, sizeof(policy))) { return false; } return policy.Enable && policy.Permanent; } +#endif // !defined(_WIN64) +#if defined(NDEBUG) bool CheckWin8AslrPolicy() { - PROCESS_MITIGATION_ASLR_POLICY policy; + PROCESS_MITIGATION_ASLR_POLICY policy = {}; if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessASLRPolicy, &policy, sizeof(policy))) { return false; } return policy.EnableForceRelocateImages && policy.DisallowStrippedImages; } +#endif // defined(NDEBUG) bool CheckWin8StrictHandlePolicy() { - PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy; + PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = {}; if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessStrictHandleCheckPolicy, &policy, sizeof(policy))) { @@ -60,7 +64,7 @@ bool CheckWin8StrictHandlePolicy() { } bool CheckWin8Win32CallPolicy() { - PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy; + PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {}; if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessSystemCallDisablePolicy, &policy, sizeof(policy))) { @@ -70,7 +74,7 @@ bool CheckWin8Win32CallPolicy() { } bool CheckWin8DllExtensionPolicy() { - PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy; + PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = {}; if (!get_process_mitigation_policy(::GetCurrentProcess(), ProcessExtensionPointDisablePolicy, &policy, sizeof(policy))) { @@ -91,8 +95,10 @@ SBOX_TESTS_COMMAND int CheckWin8(int argc, wchar_t **argv) { if (!get_process_mitigation_policy) return SBOX_TEST_NOT_FOUND; +#if !defined(_WIN64) // DEP is always enabled on 64-bit. if (!CheckWin8DepPolicy()) return SBOX_TEST_FIRST_ERROR; +#endif #if defined(NDEBUG) // ASLR cannot be forced in debug builds. if (!CheckWin8AslrPolicy()) @@ -165,9 +171,7 @@ SBOX_TESTS_COMMAND int CheckDep(int argc, wchar_t **argv) { 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; diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h index 65c9f77e5596..55a84a76e78b 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h @@ -5,7 +5,7 @@ #ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_ #define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_ -#include "base/basictypes.h" +#include "base/macros.h" #include "sandbox/win/src/sandbox_policy_base.h" namespace sandbox { @@ -15,10 +15,10 @@ namespace sandbox { class ProcessMitigationsWin32KDispatcher : public Dispatcher { public: explicit ProcessMitigationsWin32KDispatcher(PolicyBase* policy_base); - ~ProcessMitigationsWin32KDispatcher() {} + ~ProcessMitigationsWin32KDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + bool SetupService(InterceptionManager* manager, int service) override; private: PolicyBase* policy_base_; diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h index bf7b551227a5..0b295eacde20 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h +++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h @@ -6,7 +6,6 @@ #define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_ #include -#include "base/basictypes.h" #include "sandbox/win/src/sandbox_types.h" namespace sandbox { diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h index 078ed2bee151..32af16864505 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h @@ -5,7 +5,6 @@ #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" diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc index ca17d4920a46..8debd1e0fbe0 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc +++ b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc @@ -4,7 +4,9 @@ #include "sandbox/win/src/process_thread_dispatcher.h" -#include "base/basictypes.h" +#include +#include + #include "base/logging.h" #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/interception.h" @@ -97,34 +99,30 @@ 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( - &ThreadProcessDispatcher::NtOpenThread) - }; + {IPC_NTOPENTHREAD_TAG, {UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenThread)}; static const IPCCall open_process = { - {IPC_NTOPENPROCESS_TAG, UINT32_TYPE, UINT32_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenProcess) - }; + {IPC_NTOPENPROCESS_TAG, {UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenProcess)}; static const IPCCall process_token = { - {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, UINT32_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenProcessToken) - }; + {IPC_NTOPENPROCESSTOKEN_TAG, {VOIDPTR_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenProcessToken)}; static const IPCCall process_tokenex = { - {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::NtOpenProcessTokenEx) - }; + {IPC_NTOPENPROCESSTOKENEX_TAG, {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast( + &ThreadProcessDispatcher::NtOpenProcessTokenEx)}; static const IPCCall create_params = { - {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}, - reinterpret_cast( - &ThreadProcessDispatcher::CreateProcessW) - }; + {IPC_CREATEPROCESSW_TAG, + {WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}}, + reinterpret_cast( + &ThreadProcessDispatcher::CreateProcessW)}; ipc_calls_.push_back(open_thread); ipc_calls_.push_back(open_process); @@ -156,8 +154,8 @@ bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager, } bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, - uint32 desired_access, - uint32 thread_id) { + uint32_t desired_access, + uint32_t thread_id) { HANDLE handle; NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info, desired_access, thread_id, @@ -168,8 +166,8 @@ bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, } bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, - uint32 desired_access, - uint32 process_id) { + uint32_t desired_access, + uint32_t process_id) { HANDLE handle; NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info, desired_access, process_id, @@ -181,7 +179,7 @@ bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process, - uint32 desired_access) { + uint32_t desired_access) { HANDLE handle; NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info, process, desired_access, @@ -193,8 +191,8 @@ bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, - uint32 desired_access, - uint32 attributes) { + uint32_t desired_access, + uint32_t attributes) { HANDLE handle; NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info, process, diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h index c0b12d172161..008385f425d8 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ #define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -16,26 +18,30 @@ namespace sandbox { class ThreadProcessDispatcher : public Dispatcher { public: explicit ThreadProcessDispatcher(PolicyBase* policy_base); - ~ThreadProcessDispatcher() {} + ~ThreadProcessDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + 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); + bool NtOpenThread(IPCInfo* ipc, uint32_t desired_access, uint32_t thread_id); // Processes IPC requests coming from calls to NtOpenProcess() in the target. - bool NtOpenProcess(IPCInfo* ipc, uint32 desired_access, uint32 process_id); + bool NtOpenProcess(IPCInfo* ipc, + uint32_t desired_access, + uint32_t process_id); // Processes IPC requests from calls to NtOpenProcessToken() in the target. - bool NtOpenProcessToken(IPCInfo* ipc, HANDLE process, uint32 desired_access); + bool NtOpenProcessToken(IPCInfo* ipc, + HANDLE process, + uint32_t desired_access); // Processes IPC requests from calls to NtOpenProcessTokenEx() in the target. bool NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process, - uint32 desired_access, - uint32 attributes); + uint32_t desired_access, + uint32_t attributes); // Processes IPC requests coming from calls to CreateProcessW() in the target. bool CreateProcessW(IPCInfo* ipc, diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc index 19cc282098cc..bbf753ea536d 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/process_thread_interception.h" +#include + #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/policy_params.h" @@ -12,7 +14,6 @@ #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sharedmem_ipc_client.h" #include "sandbox/win/src/target_services.h" -#include "mozilla/sandboxing/sandboxLogging.h" namespace sandbox { @@ -29,14 +30,13 @@ NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, if (NT_SUCCESS(status)) return status; - mozilla::sandboxing::LogBlocked("NtOpenThread"); do { if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) break; if (!client_id) break; - uint32 thread_id = 0; + uint32_t thread_id = 0; bool should_break = false; __try { // We support only the calls for the current process @@ -54,8 +54,8 @@ NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, } } - thread_id = static_cast( - reinterpret_cast(client_id->UniqueThread)); + thread_id = static_cast( + reinterpret_cast(client_id->UniqueThread)); } __except(EXCEPTION_EXECUTE_HANDLER) { break; } @@ -95,7 +95,6 @@ NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread, break; } - mozilla::sandboxing::LogAllowed("NtOpenThread"); return answer.nt_status; } while (false); @@ -119,7 +118,7 @@ NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, if (!client_id) break; - uint32 process_id = 0; + uint32_t process_id = 0; bool should_break = false; __try { // Object attributes should be NULL or empty. @@ -133,8 +132,8 @@ NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess, } } - process_id = static_cast( - reinterpret_cast(client_id->UniqueProcess)); + process_id = static_cast( + reinterpret_cast(client_id->UniqueProcess)); } __except(EXCEPTION_EXECUTE_HANDLER) { break; } @@ -180,7 +179,6 @@ NTSTATUS WINAPI TargetNtOpenProcessToken( if (NT_SUCCESS(status)) return status; - mozilla::sandboxing::LogBlocked("NtOpenProcessToken"); do { if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) break; @@ -212,7 +210,6 @@ NTSTATUS WINAPI TargetNtOpenProcessToken( break; } - mozilla::sandboxing::LogAllowed("NtOpenProcessToken"); return answer.nt_status; } while (false); @@ -227,7 +224,6 @@ NTSTATUS WINAPI TargetNtOpenProcessTokenEx( if (NT_SUCCESS(status)) return status; - mozilla::sandboxing::LogBlocked("NtOpenProcessTokenEx"); do { if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) break; @@ -259,7 +255,6 @@ NTSTATUS WINAPI TargetNtOpenProcessTokenEx( break; } - mozilla::sandboxing::LogAllowed("NtOpenProcessTokenEx"); return answer.nt_status; } while (false); @@ -274,19 +269,20 @@ BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, LPVOID environment, LPCWSTR current_directory, LPSTARTUPINFOW startup_info, LPPROCESS_INFORMATION process_information) { - if (orig_CreateProcessW(application_name, command_line, process_attributes, + if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() && + orig_CreateProcessW(application_name, command_line, process_attributes, thread_attributes, inherit_handles, flags, environment, current_directory, startup_info, process_information)) { return TRUE; } - mozilla::sandboxing::LogBlocked("CreateProcessW", application_name); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return FALSE; + // Don't call GetLastError before InitCalled() succeeds because kernel32 may + // not be mapped yet. DWORD original_error = ::GetLastError(); do { @@ -320,7 +316,6 @@ BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW, if (ERROR_SUCCESS != answer.win32_result) return FALSE; - mozilla::sandboxing::LogAllowed("CreateProcessW", application_name); return TRUE; } while (false); @@ -343,12 +338,12 @@ BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, return TRUE; } - mozilla::sandboxing::LogBlocked("CreateProcessA", application_name); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return FALSE; + // Don't call GetLastError before InitCalled() succeeds because kernel32 may + // not be mapped yet. DWORD original_error = ::GetLastError(); do { @@ -405,7 +400,6 @@ BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA, if (ERROR_SUCCESS != answer.win32_result) return FALSE; - mozilla::sandboxing::LogAllowed("CreateProcessA", application_name); return TRUE; } while (false); diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc index 065359161f4f..b4976c0bbebe 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc +++ b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/process_thread_policy.h" +#include + #include #include "base/memory/scoped_ptr.h" @@ -101,8 +103,8 @@ bool ProcessPolicy::GenerateRules(const wchar_t* name, } NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, - uint32 desired_access, - uint32 thread_id, + uint32_t desired_access, + uint32_t thread_id, HANDLE* handle) { *handle = NULL; @@ -117,7 +119,7 @@ NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, client_id.UniqueThread = reinterpret_cast(static_cast(thread_id)); - HANDLE local_handle; + HANDLE local_handle = NULL; NTSTATUS status = NtOpenThread(&local_handle, desired_access, &attributes, &client_id); if (NT_SUCCESS(status)) { @@ -132,8 +134,8 @@ NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, } NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, - uint32 desired_access, - uint32 process_id, + uint32_t desired_access, + uint32_t process_id, HANDLE* handle) { *handle = NULL; @@ -148,7 +150,7 @@ NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, CLIENT_ID client_id = {0}; client_id.UniqueProcess = reinterpret_cast( static_cast(client_info.process_id)); - HANDLE local_handle; + HANDLE local_handle = NULL; NTSTATUS status = NtOpenProcess(&local_handle, desired_access, &attributes, &client_id); if (NT_SUCCESS(status)) { @@ -164,7 +166,7 @@ NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, HANDLE process, - uint32 desired_access, + uint32_t desired_access, HANDLE* handle) { *handle = NULL; NtOpenProcessTokenFunction NtOpenProcessToken = NULL; @@ -173,7 +175,7 @@ NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, if (CURRENT_PROCESS != process) return STATUS_ACCESS_DENIED; - HANDLE local_handle; + HANDLE local_handle = NULL; NTSTATUS status = NtOpenProcessToken(client_info.process, desired_access, &local_handle); if (NT_SUCCESS(status)) { @@ -188,8 +190,8 @@ NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, HANDLE process, - uint32 desired_access, - uint32 attributes, + uint32_t desired_access, + uint32_t attributes, HANDLE* handle) { *handle = NULL; NtOpenProcessTokenExFunction NtOpenProcessTokenEx = NULL; @@ -198,7 +200,7 @@ NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, if (CURRENT_PROCESS != process) return STATUS_ACCESS_DENIED; - HANDLE local_handle; + HANDLE local_handle = NULL; NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access, attributes, &local_handle); if (NT_SUCCESS(status)) { diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h index 2871dcaa276b..a66b52eb2f03 100644 --- a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h @@ -5,11 +5,12 @@ #ifndef SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ #define SANDBOX_SRC_PROCESS_THREAD_POLICY_H_ +#include + #include #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" @@ -36,16 +37,16 @@ class ProcessPolicy { // 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, + uint32_t desired_access, + uint32_t 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, + uint32_t desired_access, + uint32_t process_id, HANDLE* handle); // Opens the token associated with the process and returns the duplicated @@ -53,7 +54,7 @@ class ProcessPolicy { // token (using ::GetCurrentProcess()). static NTSTATUS OpenProcessTokenAction(const ClientInfo& client_info, HANDLE process, - uint32 desired_access, + uint32_t desired_access, HANDLE* handle); // Opens the token associated with the process and returns the duplicated @@ -61,8 +62,8 @@ class ProcessPolicy { // token (using ::GetCurrentProcess()). static NTSTATUS OpenProcessTokenExAction(const ClientInfo& client_info, HANDLE process, - uint32 desired_access, - uint32 attributes, + uint32_t desired_access, + uint32_t attributes, HANDLE* handle); // Processes a 'CreateProcessW()' request from the target. diff --git a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc index 967fe652adb5..fef727dac7d2 100644 --- a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc +++ b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc @@ -4,18 +4,20 @@ #include "sandbox/win/src/registry_dispatcher.h" +#include + #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" +#include "sandbox/win/src/sandbox.h" +#include "sandbox/win/src/sandbox_nt_util.h" namespace { @@ -42,15 +44,18 @@ 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(&RegistryDispatcher::NtCreateKey) - }; + {IPC_NTCREATEKEY_TAG, + {WCHAR_TYPE, + UINT32_TYPE, + VOIDPTR_TYPE, + UINT32_TYPE, + UINT32_TYPE, + UINT32_TYPE}}, + reinterpret_cast(&RegistryDispatcher::NtCreateKey)}; static const IPCCall open_params = { - {IPC_NTOPENKEY_TAG, WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE}, - reinterpret_cast(&RegistryDispatcher::NtOpenKey) - }; + {IPC_NTOPENKEY_TAG, {WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE}}, + reinterpret_cast(&RegistryDispatcher::NtOpenKey)}; ipc_calls_.push_back(create_params); ipc_calls_.push_back(open_params); @@ -76,11 +81,11 @@ bool RegistryDispatcher::SetupService(InterceptionManager* manager, bool RegistryDispatcher::NtCreateKey(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, HANDLE root, - uint32 desired_access, - uint32 title_index, - uint32 create_options) { + uint32_t desired_access, + uint32_t title_index, + uint32_t create_options) { base::win::ScopedHandle root_handle; base::string16 real_path = *name; @@ -126,9 +131,9 @@ bool RegistryDispatcher::NtCreateKey(IPCInfo* ipc, bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, HANDLE root, - uint32 desired_access) { + uint32_t desired_access) { base::win::ScopedHandle root_handle; base::string16 real_path = *name; diff --git a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h index 14411fa6ffeb..cb5af0c3d7c7 100644 --- a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_REGISTRY_DISPATCHER_H_ #define SANDBOX_SRC_REGISTRY_DISPATCHER_H_ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -16,27 +18,27 @@ namespace sandbox { class RegistryDispatcher : public Dispatcher { public: explicit RegistryDispatcher(PolicyBase* policy_base); - ~RegistryDispatcher() {} + ~RegistryDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + 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, + uint32_t attributes, HANDLE root, - uint32 desired_access, - uint32 title_index, - uint32 create_options); + uint32_t desired_access, + uint32_t title_index, + uint32_t create_options); // Processes IPC requests coming from calls to NtOpenKey in the target. bool NtOpenKey(IPCInfo* ipc, base::string16* name, - uint32 attributes, + uint32_t attributes, HANDLE root, - uint32 desired_access); + uint32_t desired_access); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher); diff --git a/security/sandbox/chromium/sandbox/win/src/registry_interception.cc b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc index 5e8a1add060b..e44099ccff6f 100644 --- a/security/sandbox/chromium/sandbox/win/src/registry_interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc @@ -4,13 +4,16 @@ #include "sandbox/win/src/registry_interception.h" +#include + #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" -#include "mozilla/sandboxing/sandboxLogging.h" namespace sandbox { @@ -26,12 +29,6 @@ NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey, if (NT_SUCCESS(status)) return status; - if (STATUS_OBJECT_NAME_NOT_FOUND != status) { - mozilla::sandboxing::LogBlocked("NtCreateKey", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - } - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -56,13 +53,36 @@ NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey, break; wchar_t* name; - uint32 attributes = 0; + uint32_t attributes = 0; HANDLE root_directory = 0; NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, &root_directory); if (!NT_SUCCESS(ret) || NULL == name) break; + uint32_t desired_access_uint32 = desired_access; + CountedParameterSet 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}; @@ -94,9 +114,6 @@ NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey, } __except(EXCEPTION_EXECUTE_HANDLER) { break; } - mozilla::sandboxing::LogAllowed("NtCreateKey", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); } while (false); return status; @@ -118,13 +135,36 @@ NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status, PHANDLE key, break; wchar_t* name; - uint32 attributes; + uint32_t attributes; HANDLE root_directory; NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes, &root_directory); if (!NT_SUCCESS(ret) || NULL == name) break; + uint32_t desired_access_uint32 = desired_access; + CountedParameterSet 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, @@ -150,9 +190,6 @@ NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status, PHANDLE key, } __except(EXCEPTION_EXECUTE_HANDLER) { break; } - mozilla::sandboxing::LogAllowed("NtOpenKey[Ex]", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); } while (false); return status; @@ -166,12 +203,6 @@ NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey, PHANDLE key, if (NT_SUCCESS(status)) return status; - if (STATUS_OBJECT_NAME_NOT_FOUND != status) { - mozilla::sandboxing::LogBlocked("NtOpenKey", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - } - return CommonNtOpenKey(status, key, desired_access, object_attributes); } @@ -189,12 +220,6 @@ NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx, if (NT_SUCCESS(status) || open_options != 0) return status; - if (STATUS_OBJECT_NAME_NOT_FOUND != status) { - mozilla::sandboxing::LogBlocked("NtOpenKeyEx", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - } - return CommonNtOpenKey(status, key, desired_access, object_attributes); } diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.cc b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc index 4c75d23716af..ec6638081425 100644 --- a/security/sandbox/chromium/sandbox/win/src/registry_policy.cc +++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include "sandbox/win/src/registry_policy.h" @@ -10,13 +12,13 @@ #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/sandbox_utils.h" #include "sandbox/win/src/win_utils.h" namespace { -static const uint32 kAllowedRegFlags = +static const uint32_t kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_READ | GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL; @@ -43,7 +45,7 @@ NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes, OBJECT_BASIC_INFORMATION info = {0}; status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), NULL); - NtClose(handle); + CHECK(NT_SUCCESS(NtClose(handle))); if (!NT_SUCCESS(status)) return status; @@ -137,7 +139,7 @@ bool RegistryPolicy::GenerateRules(const wchar_t* name, // 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); + uint32_t restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED); open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); break; @@ -166,12 +168,12 @@ bool RegistryPolicy::GenerateRules(const wchar_t* name, bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &key, - uint32 attributes, + const base::string16& key, + uint32_t attributes, HANDLE root_directory, - uint32 desired_access, - uint32 title_index, - uint32 create_options, + uint32_t desired_access, + uint32_t title_index, + uint32_t create_options, HANDLE* handle, NTSTATUS* nt_status, ULONG* disposition) { @@ -191,7 +193,7 @@ bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, UNICODE_STRING uni_name = {0}; OBJECT_ATTRIBUTES obj_attributes = {0}; InitObjectAttribs(key, attributes, root_directory, &obj_attributes, - &uni_name); + &uni_name, NULL); *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes, title_index, NULL, create_options, disposition, client_info.process); @@ -200,10 +202,10 @@ bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &key, - uint32 attributes, + const base::string16& key, + uint32_t attributes, HANDLE root_directory, - uint32 desired_access, + uint32_t desired_access, HANDLE* handle, NTSTATUS* nt_status) { // The only action supported is ASK_BROKER which means open the requested @@ -216,7 +218,7 @@ bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, UNICODE_STRING uni_name = {0}; OBJECT_ATTRIBUTES obj_attributes = {0}; InitObjectAttribs(key, attributes, root_directory, &obj_attributes, - &uni_name); + &uni_name, NULL); *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes, client_info.process); return true; diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.h b/security/sandbox/chromium/sandbox/win/src/registry_policy.h index 69af8415d249..ddea1bf5a14e 100644 --- a/security/sandbox/chromium/sandbox/win/src/registry_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.h @@ -5,9 +5,10 @@ #ifndef SANDBOX_SRC_REGISTRY_POLICY_H__ #define SANDBOX_SRC_REGISTRY_POLICY_H__ +#include + #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/nt_internals.h" @@ -31,12 +32,12 @@ class RegistryPolicy { // 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, + const base::string16& key, + uint32_t attributes, HANDLE root_directory, - uint32 desired_access, - uint32 title_index, - uint32 create_options, + uint32_t desired_access, + uint32_t title_index, + uint32_t create_options, HANDLE* handle, NTSTATUS* nt_status, ULONG* disposition); @@ -44,13 +45,13 @@ class RegistryPolicy { // 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); + const ClientInfo& client_info, + const base::string16& key, + uint32_t attributes, + HANDLE root_directory, + uint32_t desired_access, + HANDLE* handle, + NTSTATUS* nt_status); }; } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/resolver.cc b/security/sandbox/chromium/sandbox/win/src/resolver.cc index 6616fa52c363..d1719da51a57 100644 --- a/security/sandbox/chromium/sandbox/win/src/resolver.cc +++ b/security/sandbox/chromium/sandbox/win/src/resolver.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/resolver.h" +#include + #include "base/win/pe_image.h" #include "sandbox/win/src/sandbox_nt_util.h" diff --git a/security/sandbox/chromium/sandbox/win/src/resolver.h b/security/sandbox/chromium/sandbox/win/src/resolver.h index 85f1e9199085..9424060a3fee 100644 --- a/security/sandbox/chromium/sandbox/win/src/resolver.h +++ b/security/sandbox/chromium/sandbox/win/src/resolver.h @@ -6,7 +6,9 @@ // For more details see // http://dev.chromium.org/developers/design-documents/sandbox . -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #ifndef SANDBOX_SRC_RESOLVER_H__ diff --git a/security/sandbox/chromium/sandbox/win/src/resolver_32.cc b/security/sandbox/chromium/sandbox/win/src/resolver_32.cc index a591a8b1dd94..25df11cc43c5 100644 --- a/security/sandbox/chromium/sandbox/win/src/resolver_32.cc +++ b/security/sandbox/chromium/sandbox/win/src/resolver_32.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/resolver.h" +#include + // For placement new. This file must not depend on the CRT at runtime, but // placement operator new is inline. #include diff --git a/security/sandbox/chromium/sandbox/win/src/resolver_64.cc b/security/sandbox/chromium/sandbox/win/src/resolver_64.cc index 8b2cc53c97cc..6a9973544410 100644 --- a/security/sandbox/chromium/sandbox/win/src/resolver_64.cc +++ b/security/sandbox/chromium/sandbox/win/src/resolver_64.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/resolver.h" +#include + // For placement new. This file must not depend on the CRT at runtime, but // placement operator new is inline. #include @@ -12,34 +14,25 @@ namespace { -const BYTE kPushRax = 0x50; const USHORT kMovRax = 0xB848; -const ULONG kMovRspRax = 0x24048948; -const BYTE kRetNp = 0xC3; +const USHORT kJmpRax = 0xe0ff; #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 + // ff e0 jmp rax // - // The code modifies rax, but that should not be an issue for the common - // calling conventions. + // The code modifies rax, but that's fine for x64 ABI. InternalThunk() { - push_rax = kPushRax; mov_rax = kMovRax; + jmp_rax = kJmpRax; 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 + USHORT jmp_rax; // = ff e0 }; #pragma pack(pop) diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc index 3960926f4d79..1940ae3948a3 100644 --- a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc @@ -4,46 +4,80 @@ #include "sandbox/win/src/restricted_token.h" +#include + #include #include "base/logging.h" +#include "base/memory/scoped_ptr.h" #include "sandbox/win/src/acl.h" #include "sandbox/win/src/win_utils.h" +namespace { + +// Calls GetTokenInformation with the desired |info_class| and returns a buffer +// with the result. +scoped_ptr GetTokenInfo(const base::win::ScopedHandle& token, + TOKEN_INFORMATION_CLASS info_class, + DWORD* error) { + // Get the required buffer size. + DWORD size = 0; + ::GetTokenInformation(token.Get(), info_class, NULL, 0, &size); + if (!size) { + *error = ::GetLastError(); + return nullptr; + } + + scoped_ptr buffer(new BYTE[size]); + if (!::GetTokenInformation(token.Get(), info_class, buffer.get(), size, + &size)) { + *error = ::GetLastError(); + return nullptr; + } + + *error = ERROR_SUCCESS; + return buffer.Pass(); +} + +} // namespace namespace sandbox { -unsigned RestrictedToken::Init(const HANDLE effective_token) { +RestrictedToken::RestrictedToken() + : integrity_level_(INTEGRITY_LEVEL_LAST), + init_(false) { +} + +RestrictedToken::~RestrictedToken() { +} + +DWORD RestrictedToken::Init(const HANDLE effective_token) { if (init_) return ERROR_ALREADY_INITIALIZED; + HANDLE temp_token; 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 { + if (!::DuplicateHandle(::GetCurrentProcess(), effective_token, + ::GetCurrentProcess(), &temp_token, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { return ::GetLastError(); } } else { - if (!::OpenProcessToken(::GetCurrentProcess(), - TOKEN_ALL_ACCESS, - &effective_token_)) + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, + &temp_token)) { return ::GetLastError(); + } } + effective_token_.Set(temp_token); init_ = true; return ERROR_SUCCESS; } -unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const { +DWORD RestrictedToken::GetRestrictedToken( + base::win::ScopedHandle* token) const { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; @@ -85,13 +119,13 @@ unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const { } BOOL result = TRUE; - HANDLE new_token = NULL; + HANDLE new_token_handle = 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_, + result = ::CreateRestrictedToken(effective_token_.Get(), SANDBOX_INERT, static_cast(deny_size), deny_only_array, @@ -99,15 +133,16 @@ unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const { privileges_to_disable_array, static_cast(restrict_size), sids_to_restrict_array, - &new_token); + &new_token_handle); } 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, + result = ::DuplicateTokenEx(effective_token_.Get(), TOKEN_ALL_ACCESS, NULL, SecurityIdentification, TokenPrimary, - &new_token); + &new_token_handle); } + auto last_error = ::GetLastError(); if (deny_only_array) delete[] deny_only_array; @@ -119,99 +154,77 @@ unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const { delete[] privileges_to_disable_array; if (!result) - return ::GetLastError(); + return last_error; + + base::win::ScopedHandle new_token(new_token_handle); // Modify the default dacl on the token to contain Restricted and the user. - if (!AddSidToDefaultDacl(new_token, WinRestrictedCodeSid, GENERIC_ALL)) + if (!AddSidToDefaultDacl(new_token.Get(), WinRestrictedCodeSid, GENERIC_ALL)) return ::GetLastError(); - if (!AddUserSidToDefaultDacl(new_token, GENERIC_ALL)) + if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL)) return ::GetLastError(); - DWORD error = SetTokenIntegrityLevel(new_token, integrity_level_); + DWORD error = SetTokenIntegrityLevel(new_token.Get(), 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) + HANDLE token_handle; + if (!::DuplicateHandle(::GetCurrentProcess(), new_token.Get(), + ::GetCurrentProcess(), &token_handle, + TOKEN_ALL_ACCESS, FALSE, // Don't inherit. + 0)) { return ::GetLastError(); + } + token->Set(token_handle); return ERROR_SUCCESS; } -unsigned RestrictedToken::GetRestrictedTokenHandleForImpersonation( - HANDLE *token_handle) const { +DWORD RestrictedToken::GetRestrictedTokenForImpersonation( + base::win::ScopedHandle* token) const { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; - HANDLE restricted_token_handle; - unsigned err_code = GetRestrictedTokenHandle(&restricted_token_handle); + base::win::ScopedHandle restricted_token; + DWORD err_code = GetRestrictedToken(&restricted_token); if (ERROR_SUCCESS != err_code) return err_code; - HANDLE impersonation_token; - if (!::DuplicateToken(restricted_token_handle, + HANDLE impersonation_token_handle; + if (!::DuplicateToken(restricted_token.Get(), SecurityImpersonation, - &impersonation_token)) { - ::CloseHandle(restricted_token_handle); + &impersonation_token_handle)) { + return ::GetLastError(); + } + base::win::ScopedHandle impersonation_token(impersonation_token_handle); + + HANDLE token_handle; + if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.Get(), + ::GetCurrentProcess(), &token_handle, + TOKEN_ALL_ACCESS, FALSE, // Don't inherit. + 0)) { 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(); - + token->Set(token_handle); return ERROR_SUCCESS; } -unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector *exceptions) { +DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector *exceptions) { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; - TOKEN_GROUPS *token_groups = NULL; - DWORD size = 0; + DWORD error; + scoped_ptr buffer = + GetTokenInfo(effective_token_, TokenGroups, &error); - BOOL result = ::GetTokenInformation(effective_token_, - TokenGroups, - NULL, // No buffer. - 0, // Size is 0. - &size); - if (!size) - return ::GetLastError(); + if (!buffer) + return error; - token_groups = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenGroups, - token_groups, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_groups); - return ::GetLastError(); - } + TOKEN_GROUPS* token_groups = reinterpret_cast(buffer.get()); // Build the list of the deny only group SIDs for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { @@ -234,12 +247,10 @@ unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector *exceptions) { } } - delete[] reinterpret_cast(token_groups); - return ERROR_SUCCESS; } -unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) { +DWORD RestrictedToken::AddSidForDenyOnly(const Sid &sid) { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; @@ -248,62 +259,42 @@ unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) { return ERROR_SUCCESS; } -unsigned RestrictedToken::AddUserSidForDenyOnly() { +DWORD 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(new BYTE[size]); + scoped_ptr buffer(new BYTE[size]); + TOKEN_USER* token_user = reinterpret_cast(buffer.get()); - BOOL result = ::GetTokenInformation(effective_token_, - TokenUser, - token_user, - size, - &size); + BOOL result = ::GetTokenInformation(effective_token_.Get(), TokenUser, + token_user, size, &size); - if (!result) { - delete[] reinterpret_cast(token_user); + if (!result) return ::GetLastError(); - } Sid user = reinterpret_cast(token_user->User.Sid); sids_for_deny_only_.push_back(user); - delete[] reinterpret_cast(token_user); - return ERROR_SUCCESS; } -unsigned RestrictedToken::DeleteAllPrivileges( +DWORD RestrictedToken::DeleteAllPrivileges( const std::vector *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; + DWORD error; + scoped_ptr buffer = + GetTokenInfo(effective_token_, TokenPrivileges, &error); - BOOL result = ::GetTokenInformation(effective_token_, - TokenPrivileges, - NULL, // No buffer. - 0, // Size is 0. - &size); - if (!size) - return ::GetLastError(); - - token_privileges = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenPrivileges, - token_privileges, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_privileges); - return ::GetLastError(); - } + if (!buffer) + return error; + TOKEN_PRIVILEGES* token_privileges = + reinterpret_cast(buffer.get()); // Build the list of privileges to disable for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) { @@ -324,12 +315,10 @@ unsigned RestrictedToken::DeleteAllPrivileges( } } - delete[] reinterpret_cast(token_privileges); - return ERROR_SUCCESS; } -unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) { +DWORD RestrictedToken::DeletePrivilege(const wchar_t *privilege) { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; @@ -343,7 +332,7 @@ unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) { return ERROR_SUCCESS; } -unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) { +DWORD RestrictedToken::AddRestrictingSid(const Sid &sid) { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; @@ -352,32 +341,19 @@ unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) { return ERROR_SUCCESS; } -unsigned RestrictedToken::AddRestrictingSidLogonSession() { +DWORD RestrictedToken::AddRestrictingSidLogonSession() { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; - TOKEN_GROUPS *token_groups = NULL; - DWORD size = 0; + DWORD error; + scoped_ptr buffer = + GetTokenInfo(effective_token_, TokenGroups, &error); - BOOL result = ::GetTokenInformation(effective_token_, - TokenGroups, - NULL, // No buffer. - 0, // Size is 0. - &size); - if (!size) - return ::GetLastError(); + if (!buffer) + return error; - token_groups = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenGroups, - token_groups, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_groups); - return ::GetLastError(); - } + TOKEN_GROUPS* token_groups = reinterpret_cast(buffer.get()); SID *logon_sid = NULL; for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { @@ -390,67 +366,47 @@ unsigned RestrictedToken::AddRestrictingSidLogonSession() { if (logon_sid) sids_to_restrict_.push_back(logon_sid); - delete[] reinterpret_cast(token_groups); - return ERROR_SUCCESS; } -unsigned RestrictedToken::AddRestrictingSidCurrentUser() { +DWORD 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(new BYTE[size]); + scoped_ptr buffer(new BYTE[size]); + TOKEN_USER* token_user = reinterpret_cast(buffer.get()); - BOOL result = ::GetTokenInformation(effective_token_, - TokenUser, - token_user, - size, - &size); + BOOL result = ::GetTokenInformation(effective_token_.Get(), TokenUser, + token_user, size, &size); - if (!result) { - delete[] reinterpret_cast(token_user); + if (!result) return ::GetLastError(); - } Sid user = reinterpret_cast(token_user->User.Sid); sids_to_restrict_.push_back(user); - delete[] reinterpret_cast(token_user); - return ERROR_SUCCESS; } -unsigned RestrictedToken::AddRestrictingSidAllSids() { +DWORD RestrictedToken::AddRestrictingSidAllSids() { DCHECK(init_); if (!init_) return ERROR_NO_TOKEN; // Add the current user to the list. - unsigned error = AddRestrictingSidCurrentUser(); + DWORD error = AddRestrictingSidCurrentUser(); if (ERROR_SUCCESS != error) return error; - TOKEN_GROUPS *token_groups = NULL; - DWORD size = 0; + scoped_ptr buffer = + GetTokenInfo(effective_token_, TokenGroups, &error); - // Get the buffer size required. - BOOL result = ::GetTokenInformation(effective_token_, TokenGroups, NULL, 0, - &size); - if (!size) - return ::GetLastError(); + if (!buffer) + return error; - token_groups = reinterpret_cast(new BYTE[size]); - result = ::GetTokenInformation(effective_token_, - TokenGroups, - token_groups, - size, - &size); - if (!result) { - delete[] reinterpret_cast(token_groups); - return ::GetLastError(); - } + TOKEN_GROUPS* token_groups = reinterpret_cast(buffer.get()); // Build the list of restricting sids from all groups. for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { @@ -458,12 +414,10 @@ unsigned RestrictedToken::AddRestrictingSidAllSids() { AddRestrictingSid(reinterpret_cast(token_groups->Groups[i].Sid)); } - delete[] reinterpret_cast(token_groups); - return ERROR_SUCCESS; } -unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) { +DWORD RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) { integrity_level_ = integrity_level; return ERROR_SUCCESS; } diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.h b/security/sandbox/chromium/sandbox/win/src/restricted_token.h index 6d8e5506918d..d302f86da6b1 100644 --- a/security/sandbox/chromium/sandbox/win/src/restricted_token.h +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.h @@ -8,8 +8,9 @@ #include #include -#include "base/basictypes.h" +#include "base/macros.h" #include "base/strings/string16.h" +#include "base/win/scoped_handle.h" #include "sandbox/win/src/restricted_token_utils.h" #include "sandbox/win/src/security_level.h" #include "sandbox/win/src/sid.h" @@ -28,55 +29,46 @@ namespace sandbox { // any token handle. // Sample usage: // RestrictedToken restricted_token; -// unsigned err_code = restricted_token.Init(NULL); // Use the current -// // effective token +// DWORD 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); +// base::win::ScopedHandle token_handle; +// err_code = restricted_token.GetRestrictedToken(&token_handle); // if (ERROR_SUCCESS != err_code) { // // handle error. // } // [...] -// CloseHandle(token_handle); class RestrictedToken { public: // Init() has to be called before calling any other method in the class. - RestrictedToken() - : init_(false), effective_token_(NULL), - integrity_level_(INTEGRITY_LEVEL_LAST) { } - - ~RestrictedToken() { - if (effective_token_) - CloseHandle(effective_token_); - } + 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); + DWORD 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. + // Creates a 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 GetRestrictedTokenHandle(HANDLE *token_handle) const; + DWORD GetRestrictedToken(base::win::ScopedHandle* token) 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. + // for impersonation. Returns this impersonation 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. // - // The sample usage is the same as the GetRestrictedTokenHandle function. - unsigned GetRestrictedTokenHandleForImpersonation(HANDLE *token_handle) const; + // The sample usage is the same as the GetRestrictedToken function. + DWORD GetRestrictedTokenForImpersonation( + base::win::ScopedHandle* token) 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, @@ -94,7 +86,7 @@ class RestrictedToken { // 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 *exceptions); + DWORD AddAllSidsForDenyOnly(std::vector *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. @@ -102,13 +94,13 @@ class RestrictedToken { // // Sample Usage: // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID()); - unsigned AddSidForDenyOnly(const Sid &sid); + DWORD 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(); + DWORD 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 @@ -123,8 +115,7 @@ class RestrictedToken { // std::vector privilege_exceptions; // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); // restricted_token.DeleteAllPrivileges(&privilege_exceptions); - unsigned DeleteAllPrivileges( - const std::vector *exceptions); + DWORD DeleteAllPrivileges(const std::vector *exceptions); // Adds a privilege to the list of privileges to remove in the restricted // token. @@ -136,7 +127,7 @@ class RestrictedToken { // // Sample usage: // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME); - unsigned DeletePrivilege(const wchar_t *privilege); + DWORD 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. @@ -148,7 +139,7 @@ class RestrictedToken { // 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); + DWORD AddRestrictingSid(const Sid &sid); // Adds the logon sid of the token in the list of restricting sids for the // restricted token. @@ -156,7 +147,7 @@ class RestrictedToken { // 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(); + DWORD AddRestrictingSidLogonSession(); // Adds the owner sid of the token in the list of restricting sids for the // restricted token. @@ -164,18 +155,18 @@ class RestrictedToken { // 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(); + DWORD 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(); + DWORD 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); + DWORD SetIntegrityLevel(IntegrityLevel integrity_level); private: // The list of restricting sids in the restricted token. @@ -185,7 +176,7 @@ class RestrictedToken { // The list of sids to mark as Deny Only in the restricted token. std::vector sids_for_deny_only_; // The token to restrict. Can only be set in a constructor. - HANDLE effective_token_; + base::win::ScopedHandle 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) diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc index 8186f9c77c43..b11948e9ffd4 100644 --- a/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc @@ -8,6 +8,8 @@ #include #include #include + +#include "base/win/scoped_handle.h" #include "sandbox/win/src/restricted_token.h" #include "sandbox/win/src/sid.h" #include "testing/gtest/include/gtest/gtest.h" @@ -17,7 +19,8 @@ namespace sandbox { // Tests the initializatioin with an invalid token handle. TEST(RestrictedTokenTest, InvalidHandle) { RestrictedToken token; - ASSERT_EQ(ERROR_INVALID_HANDLE, token.Init(reinterpret_cast(0x5555))); + ASSERT_EQ(static_cast(ERROR_INVALID_HANDLE), + token.Init(reinterpret_cast(0x5555))); } // Tests the initialization with NULL as parameter. @@ -34,16 +37,16 @@ TEST(RestrictedTokenTest, DefaultInit) { // Create the token using the current token. RestrictedToken token_default; - ASSERT_EQ(ERROR_SUCCESS, token_default.Init(NULL)); + ASSERT_EQ(static_cast(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)); + base::win::ScopedHandle restricted_token_handle; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token_default.GetRestrictedToken(&restricted_token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(restricted_token_handle); + restricted_token.Attach(restricted_token_handle.Take()); ATL::CSid sid_user_restricted; ATL::CSid sid_user_default; @@ -76,16 +79,17 @@ TEST(RestrictedTokenTest, CustomInit) { // Create the token using the current token. RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(access_token.GetHandle())); + ASSERT_EQ(static_cast(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)); + base::win::ScopedHandle restricted_token_handle; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&restricted_token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(restricted_token_handle); + restricted_token.Attach(restricted_token_handle.Take()); ATL::CSid sid_restricted; ATL::CSid sid_default; @@ -99,19 +103,20 @@ TEST(RestrictedTokenTest, CustomInit) { // Verifies that the token created by the object are valid. TEST(RestrictedTokenTest, ResultToken) { RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - HANDLE restricted_token; - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&restricted_token)); + base::win::ScopedHandle restricted_token; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&restricted_token)); - ASSERT_TRUE(::IsTokenRestricted(restricted_token)); + ASSERT_TRUE(::IsTokenRestricted(restricted_token.Get())); DWORD length = 0; TOKEN_TYPE type; - ASSERT_TRUE(::GetTokenInformation(restricted_token, + ASSERT_TRUE(::GetTokenInformation(restricted_token.Get(), ::TokenType, &type, sizeof(type), @@ -119,37 +124,35 @@ TEST(RestrictedTokenTest, ResultToken) { ASSERT_EQ(type, TokenPrimary); - HANDLE impersonation_token; - ASSERT_EQ(ERROR_SUCCESS, - token.GetRestrictedTokenHandleForImpersonation(&impersonation_token)); + base::win::ScopedHandle impersonation_token; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedTokenForImpersonation(&impersonation_token)); - ASSERT_TRUE(::IsTokenRestricted(impersonation_token)); + ASSERT_TRUE(::IsTokenRestricted(impersonation_token.Get())); - ASSERT_TRUE(::GetTokenInformation(impersonation_token, + ASSERT_TRUE(::GetTokenInformation(impersonation_token.Get(), ::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(static_cast(ERROR_SUCCESS), token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - HANDLE handle; - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&handle)); + base::win::ScopedHandle handle; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(handle); + restricted_token.Attach(handle.Take()); ATL::CDacl dacl; ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl)); @@ -173,14 +176,16 @@ TEST(RestrictedTokenTest, DefaultDacl) { // Tests the method "AddSidForDenyOnly". TEST(RestrictedTokenTest, DenySid) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddSidForDenyOnly(Sid(WinWorldSid))); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddSidForDenyOnly(Sid(WinWorldSid))); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenGroups groups; ASSERT_TRUE(restricted_token.GetGroups(&groups)); @@ -191,7 +196,7 @@ TEST(RestrictedTokenTest, DenySid) { for (unsigned int i = 0; i < sids.GetCount(); i++) { if (ATL::Sids::World() == sids[i]) { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + ASSERT_EQ(static_cast(SE_GROUP_USE_FOR_DENY_ONLY), attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); } } @@ -200,14 +205,16 @@ TEST(RestrictedTokenTest, DenySid) { // Tests the method "AddAllSidsForDenyOnly". TEST(RestrictedTokenTest, DenySids) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddAllSidsForDenyOnly(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddAllSidsForDenyOnly(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenGroups groups; ASSERT_TRUE(restricted_token.GetGroups(&groups)); @@ -220,7 +227,7 @@ TEST(RestrictedTokenTest, DenySids) { 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, + ASSERT_EQ(static_cast(SE_GROUP_USE_FOR_DENY_ONLY), attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); } } @@ -229,17 +236,19 @@ TEST(RestrictedTokenTest, DenySids) { // Tests the method "AddAllSidsForDenyOnly" using an exception list. TEST(RestrictedTokenTest, DenySidsException) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; std::vector 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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddAllSidsForDenyOnly(&sids_exception)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenGroups groups; ASSERT_TRUE(restricted_token.GetGroups(&groups)); @@ -253,9 +262,9 @@ TEST(RestrictedTokenTest, DenySidsException) { 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); + ASSERT_EQ(0u, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); } else { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + ASSERT_EQ(static_cast(SE_GROUP_USE_FOR_DENY_ONLY), attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); } } @@ -265,14 +274,15 @@ TEST(RestrictedTokenTest, DenySidsException) { // Tests test method AddOwnerSidForDenyOnly. TEST(RestrictedTokenTest, DenyOwnerSid) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddUserSidForDenyOnly()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.AddUserSidForDenyOnly()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenGroups groups; ASSERT_TRUE(restricted_token.GetGroups(&groups)); @@ -286,7 +296,7 @@ TEST(RestrictedTokenTest, DenyOwnerSid) { for (unsigned int i = 0; i < sids.GetCount(); ++i) { if (user_sid == sids[i]) { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + ASSERT_EQ(static_cast(SE_GROUP_USE_FOR_DENY_ONLY), attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); } } @@ -295,22 +305,25 @@ TEST(RestrictedTokenTest, DenyOwnerSid) { // Tests test method AddOwnerSidForDenyOnly with a custom effective token. TEST(RestrictedTokenTest, DenyOwnerSidCustom) { // Get the current process token. - HANDLE token_handle = INVALID_HANDLE_VALUE; + HANDLE access_handle = INVALID_HANDLE_VALUE; ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, - &token_handle)); + &access_handle)); - ASSERT_NE(INVALID_HANDLE_VALUE, token_handle); + ASSERT_NE(INVALID_HANDLE_VALUE, access_handle); ATL::CAccessToken access_token; - access_token.Attach(token_handle); + access_token.Attach(access_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)); + base::win::ScopedHandle token_handle; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.Init(access_token.GetHandle())); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.AddUserSidForDenyOnly()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenGroups groups; ASSERT_TRUE(restricted_token.GetGroups(&groups)); @@ -324,7 +337,7 @@ TEST(RestrictedTokenTest, DenyOwnerSidCustom) { for (unsigned int i = 0; i < sids.GetCount(); ++i) { if (user_sid == sids[i]) { - ASSERT_EQ(SE_GROUP_USE_FOR_DENY_ONLY, + ASSERT_EQ(static_cast(SE_GROUP_USE_FOR_DENY_ONLY), attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY); } } @@ -333,35 +346,38 @@ TEST(RestrictedTokenTest, DenyOwnerSidCustom) { // Tests the method DeleteAllPrivileges. TEST(RestrictedTokenTest, DeleteAllPrivileges) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.DeleteAllPrivileges(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.DeleteAllPrivileges(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenPrivileges privileges; ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); - ASSERT_EQ(0, privileges.GetCount()); + ASSERT_EQ(0u, privileges.GetCount()); } // Tests the method DeleteAllPrivileges with an exception list. TEST(RestrictedTokenTest, DeleteAllPrivilegesException) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; std::vector 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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.DeleteAllPrivileges(&exceptions)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenPrivileges privileges; ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); @@ -371,7 +387,7 @@ TEST(RestrictedTokenTest, DeleteAllPrivilegesException) { privileges.GetNamesAndAttributes(&privilege_names, &privilege_name_attributes); - ASSERT_EQ(1, privileges.GetCount()); + ASSERT_EQ(1u, privileges.GetCount()); for (unsigned int i = 0; i < privileges.GetCount(); ++i) { ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME); @@ -381,14 +397,16 @@ TEST(RestrictedTokenTest, DeleteAllPrivilegesException) { // Tests the method DeletePrivilege. TEST(RestrictedTokenTest, DeletePrivilege) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - 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)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenPrivileges privileges; ASSERT_TRUE(restricted_token.GetPrivileges(&privileges)); @@ -421,7 +439,7 @@ void CheckRestrictingSid(const ATL::CAccessToken &restricted_token, delete[] memory; if (count >= 0) - ASSERT_EQ(count, atl_groups.GetCount()); + ASSERT_EQ(static_cast(count), atl_groups.GetCount()); ATL::CSid::CSidArray sids; ATL::CAtlArray attributes; @@ -441,15 +459,16 @@ void CheckRestrictingSid(const ATL::CAccessToken &restricted_token, // Tests the method AddRestrictingSid. TEST(RestrictedTokenTest, AddRestrictingSid) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1); } @@ -457,14 +476,16 @@ TEST(RestrictedTokenTest, AddRestrictingSid) { // Tests the method AddRestrictingSidCurrentUser. TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddRestrictingSidCurrentUser()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CSid user; restricted_token.GetUser(&user); @@ -474,22 +495,26 @@ TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) { // Tests the method AddRestrictingSidCurrentUser with a custom effective token. TEST(RestrictedTokenTest, AddRestrictingSidCurrentUserCustom) { // Get the current process token. - HANDLE token_handle = INVALID_HANDLE_VALUE; + HANDLE access_handle = INVALID_HANDLE_VALUE; ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, - &token_handle)); + &access_handle)); - ASSERT_NE(INVALID_HANDLE_VALUE, token_handle); + ASSERT_NE(INVALID_HANDLE_VALUE, access_handle); ATL::CAccessToken access_token; - access_token.Attach(token_handle); + access_token.Attach(access_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)); + base::win::ScopedHandle token_handle; + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.Init(access_token.GetHandle())); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddRestrictingSidCurrentUser()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CSid user; restricted_token.GetUser(&user); @@ -499,14 +524,16 @@ TEST(RestrictedTokenTest, AddRestrictingSidCurrentUserCustom) { // Tests the method AddRestrictingSidLogonSession. TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddRestrictingSidLogonSession()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CSid session; restricted_token.GetLogonSid(&session); @@ -516,17 +543,20 @@ TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) { // Tests adding a lot of restricting sids. TEST(RestrictedTokenTest, AddMultipleRestrictingSids) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidCurrentUser()); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidLogonSession()); - ASSERT_EQ(ERROR_SUCCESS, + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddRestrictingSidCurrentUser()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddRestrictingSidLogonSession()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.AddRestrictingSid(ATL::Sids::World().GetPSID())); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CSid session; restricted_token.GetLogonSid(&session); @@ -542,20 +572,22 @@ TEST(RestrictedTokenTest, AddMultipleRestrictingSids) { ATL::CTokenGroups atl_groups(*groups); delete[] memory; - ASSERT_EQ(3, atl_groups.GetCount()); + ASSERT_EQ(3u, atl_groups.GetCount()); } // Tests the method "AddRestrictingSidAllSids". TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) { RestrictedToken token; - HANDLE token_handle = NULL; + base::win::ScopedHandle token_handle; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); - ASSERT_EQ(ERROR_SUCCESS, token.AddRestrictingSidAllSids()); - ASSERT_EQ(ERROR_SUCCESS, token.GetRestrictedTokenHandle(&token_handle)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.AddRestrictingSidAllSids()); + ASSERT_EQ(static_cast(ERROR_SUCCESS), + token.GetRestrictedToken(&token_handle)); ATL::CAccessToken restricted_token; - restricted_token.Attach(token_handle); + restricted_token.Attach(token_handle.Take()); ATL::CTokenGroups groups; ASSERT_TRUE(restricted_token.GetGroups(&groups)); @@ -580,9 +612,9 @@ TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) { // Checks the error code when the object is initialized twice. TEST(RestrictedTokenTest, DoubleInit) { RestrictedToken token; - ASSERT_EQ(ERROR_SUCCESS, token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_SUCCESS), token.Init(NULL)); - ASSERT_EQ(ERROR_ALREADY_INITIALIZED, token.Init(NULL)); + ASSERT_EQ(static_cast(ERROR_ALREADY_INITIALIZED), token.Init(NULL)); } } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc index 93b212efaf3c..4a3d05c639bc 100644 --- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc @@ -10,7 +10,6 @@ #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" @@ -19,13 +18,10 @@ namespace sandbox { -DWORD CreateRestrictedToken(HANDLE *token_handle, - TokenLevel security_level, +DWORD CreateRestrictedToken(TokenLevel security_level, IntegrityLevel integrity_level, - TokenType token_type) { - if (!token_handle) - return ERROR_BAD_ARGUMENTS; - + TokenType token_type, + base::win::ScopedHandle* token) { RestrictedToken restricted_token; restricted_token.Init(NULL); // Initialized with the current process token @@ -123,12 +119,11 @@ DWORD CreateRestrictedToken(HANDLE *token_handle, switch (token_type) { case PRIMARY: { - err_code = restricted_token.GetRestrictedTokenHandle(token_handle); + err_code = restricted_token.GetRestrictedToken(token); break; } case IMPERSONATION: { - err_code = restricted_token.GetRestrictedTokenHandleForImpersonation( - token_handle); + err_code = restricted_token.GetRestrictedTokenForImpersonation(token); break; } default: { @@ -140,99 +135,6 @@ DWORD CreateRestrictedToken(HANDLE *token_handle, 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) { @@ -309,16 +211,17 @@ DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) { if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid)) return ::GetLastError(); - TOKEN_MANDATORY_LABEL label = {0}; + TOKEN_MANDATORY_LABEL label = {}; 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); + auto last_error = ::GetLastError(); ::LocalFree(integrity_sid); - return result ? ERROR_SUCCESS : ::GetLastError(); + return result ? ERROR_SUCCESS : last_error; } DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { @@ -342,4 +245,67 @@ DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { 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 security_desc_buffer(length_needed); + PSECURITY_DESCRIPTOR security_desc = + reinterpret_cast(&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(&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/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h index 69462b4da28b..b4e4fded9560 100644 --- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h +++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h @@ -8,6 +8,7 @@ #include #include +#include "base/win/scoped_handle.h" #include "sandbox/win/src/restricted_token.h" #include "sandbox/win/src/security_level.h" @@ -27,41 +28,15 @@ enum TokenType { // 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. +// |token| 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, +DWORD CreateRestrictedToken(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); + TokenType token_type, + base::win::ScopedHandle* token); // Sets the integrity label on a object handle. DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, @@ -82,6 +57,19 @@ const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level); // 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/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h index e3261944030a..193605d1ea5b 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h @@ -21,7 +21,6 @@ #include -#include "base/basictypes.h" #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/sandbox_types.h" diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h b/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h index 7a0280f90802..f5888ffcda09 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h @@ -5,6 +5,7 @@ #ifndef SANDBOX_SRC_SANDBOX_FACTORY_H__ #define SANDBOX_SRC_SANDBOX_FACTORY_H__ +#include "base/macros.h" #include "sandbox/win/src/sandbox.h" // SandboxFactory is a set of static methods to get access to the broker diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc index 8162a194d645..62f2422ca432 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc @@ -4,6 +4,11 @@ #include "sandbox/win/src/sandbox_nt_util.h" +#include +#include + +#include + #include "base/win/pe_image.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/target_services.h" @@ -74,7 +79,6 @@ void* AllocateNearTo(void* source, size_t size) { #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. @@ -133,8 +137,7 @@ bool MapGlobalMemory() { 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); + VERIFY_SUCCESS(g_nt.UnmapViewOfSection(NtCurrentProcess, memory)); } DCHECK_NT(g_shared_IPC_size > 0); g_shared_policy_memory = reinterpret_cast(g_shared_IPC_memory) @@ -215,9 +218,82 @@ NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { 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( + 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, + wchar_t** out_name, + uint32_t* attributes, HANDLE* root) { if (!InitHeap()) return STATUS_NO_MEMORY; @@ -265,7 +341,7 @@ NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object, return ret; } -NTSTATUS GetProcessId(HANDLE process, ULONG *process_id) { +NTSTATUS GetProcessId(HANDLE process, DWORD *process_id) { PROCESS_BASIC_INFORMATION proc_info; ULONG bytes_returned; @@ -283,7 +359,7 @@ bool IsSameProcess(HANDLE process) { if (NtCurrentProcess == process) return true; - static ULONG s_process_id = 0; + static DWORD s_process_id = 0; if (!s_process_id) { NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id); @@ -291,7 +367,7 @@ bool IsSameProcess(HANDLE process) { return false; } - ULONG process_id; + DWORD process_id; NTSTATUS ret = GetProcessId(process, &process_id); if (!NT_SUCCESS(ret)) return false; @@ -360,7 +436,7 @@ UNICODE_STRING* AnsiToUnicode(const char* string) { return out_string; } -UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { +UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags) { // PEImage's dtor won't be run during SEH unwinding, but that's OK. #pragma warning(push) #pragma warning(disable: 4509) @@ -397,7 +473,7 @@ UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags) { UNICODE_STRING* GetBackingFilePath(PVOID address) { // We'll start with something close to max_path charactes for the name. - ULONG buffer_bytes = MAX_PATH * 2; + SIZE_T buffer_bytes = MAX_PATH * 2; for (;;) { MEMORY_SECTION_NAME* section_name = reinterpret_cast( @@ -406,7 +482,7 @@ UNICODE_STRING* GetBackingFilePath(PVOID address) { if (!section_name) return NULL; - ULONG returned_bytes; + SIZE_T returned_bytes; NTSTATUS ret = g_nt.QueryVirtualMemory(NtCurrentProcess, address, MemorySectionName, section_name, buffer_bytes, &returned_bytes); @@ -456,7 +532,7 @@ UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) { // Based on the code above, size_bytes should always be small enough // to make the static_cast below safe. - DCHECK_NT(kuint16max > size_bytes); + DCHECK_NT(UINT16_MAX > size_bytes); char* str_buffer = new(NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)]; if (!str_buffer) return NULL; @@ -512,8 +588,9 @@ NTSTATUS AutoProtectMemory::RevertProtection() { return ret; } -bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, - uint32 file_info_class) { +bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, + DWORD length, + uint32_t file_info_class) { if (FileRenameInformation != file_info_class) return false; @@ -533,7 +610,7 @@ bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, // Check if it starts with \\??\\. We don't support relative paths. if (file_info->FileNameLength < sizeof(kPathPrefix) || - file_info->FileNameLength > kuint16max) + file_info->FileNameLength > UINT16_MAX) return false; if (file_info->FileName[0] != kPathPrefix[0] || @@ -549,15 +626,13 @@ bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, DWORD length, void* operator new(size_t size, sandbox::AllocationType type, void* near_to) { - using namespace sandbox; - void* result = NULL; - if (NT_ALLOC == type) { - if (InitHeap()) { + if (type == sandbox::NT_ALLOC) { + if (sandbox::InitHeap()) { // Use default flags for the allocation. - result = g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); + result = sandbox::g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); } - } else if (NT_PAGE == type) { + } else if (type == sandbox::NT_PAGE) { result = AllocateNearTo(near_to, size); } else { NOTREACHED_NT(); @@ -571,37 +646,31 @@ void* operator new(size_t size, sandbox::AllocationType type, } void operator delete(void* memory, sandbox::AllocationType type) { - using namespace sandbox; - - if (NT_ALLOC == type) { + if (type == sandbox::NT_ALLOC) { // Use default flags. - VERIFY(g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); - } else if (NT_PAGE == type) { + VERIFY(sandbox::g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory)); + } else if (type == sandbox::NT_PAGE) { void* base = memory; SIZE_T size = 0; - VERIFY_SUCCESS(g_nt.FreeVirtualMemory(NtCurrentProcess, &base, &size, - MEM_RELEASE)); + VERIFY_SUCCESS(sandbox::g_nt.FreeVirtualMemory(NtCurrentProcess, &base, + &size, MEM_RELEASE)); } else { NOTREACHED_NT(); } } -void operator delete(void* memory, sandbox::AllocationType type, +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, +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); -} +void __cdecl operator delete(void* memory, + void* buffer, + sandbox::AllocationType type) {} diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h index 7c543f2ac308..3e0238206768 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h @@ -6,8 +6,10 @@ #define SANDBOX_SRC_SANDBOX_NT_UTIL_H_ #include +#include +#include -#include "base/basictypes.h" +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/sandbox_nt_types.h" @@ -31,9 +33,9 @@ void __cdecl operator delete(void* memory, void* buffer, // 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 +// VERIFY_NT and VERIFY_SUCCESS 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 +// returning a bool, while VERIFY_SUCCESS expects an action returning // NTSTATUS. #ifndef NDEBUG #define DCHECK_NT(condition) { (condition) ? (void)0 : __debugbreak(); } @@ -101,7 +103,14 @@ 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); + wchar_t** out_name, + uint32_t* 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(); @@ -129,7 +138,7 @@ enum MappedModuleFlags { // } // InsertYourLogicHere(name); // operator delete(name, NT_ALLOC); -UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32* flags); +UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* 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 @@ -177,8 +186,9 @@ class 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); +bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info, + DWORD length, + uint32_t file_info_class); } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h index 22a2049c2d10..cc39c6283950 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h @@ -5,9 +5,11 @@ #ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_H_ #define SANDBOX_WIN_SRC_SANDBOX_POLICY_H_ +#include +#include + #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/security_level.h" @@ -75,12 +77,12 @@ class TargetPolicy { // 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 + // win32's RevertToSelf(). 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 + // RevertToSelf(). 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 @@ -130,7 +132,8 @@ class TargetPolicy { // 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; + virtual ResultCode SetJobLevel(JobLevel job_level, + uint32_t 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 @@ -183,6 +186,10 @@ class TargetPolicy { // 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 @@ -238,6 +245,12 @@ class TargetPolicy { // 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 diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc index d3c920e6429c..28662b42d957 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc @@ -5,35 +5,34 @@ #include "sandbox/win/src/sandbox_policy_base.h" #include +#include +#include -#include "base/basictypes.h" #include "base/callback.h" #include "base/logging.h" +#include "base/macros.h" +#include "base/stl_util.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/job.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.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/sync_dispatcher.h" +#include "sandbox/win/src/sandbox_utils.h" #include "sandbox/win/src/sync_policy.h" #include "sandbox/win/src/target_process.h" +#include "sandbox/win/src/top_level_dispatcher.h" #include "sandbox/win/src/window.h" namespace { @@ -67,8 +66,45 @@ bool IsInheritableHandle(HANDLE 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 + namespace sandbox { SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level; @@ -98,60 +134,26 @@ PolicyBase::PolicyBase() mitigations_(0), delayed_mitigations_(0), policy_maker_(NULL), - policy_(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; + dispatcher_.reset(new TopLevelDispatcher(this)); } 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_); } @@ -181,7 +183,7 @@ TokenLevel PolicyBase::GetLockdownTokenLevel() const{ return lockdown_level_; } -ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32 ui_exceptions) { +ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) { if (memory_limit_ && job_level == JOB_NONE) { return SBOX_ERROR_BAD_PARAMS; } @@ -310,6 +312,10 @@ 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, @@ -331,6 +337,25 @@ ResultCode PolicyBase::SetCapability(const wchar_t* 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)) @@ -395,63 +420,58 @@ ResultCode PolicyBase::AddKernelObjectToClose(const base::char16* handle_type, return handle_closer_.AddHandle(handle_type, handle_name); } -// When an IPC is ready in any of the targets we get called. We manage an array -// of IPC dispatchers which are keyed on the IPC tag so we normally delegate -// to the appropriate dispatcher unless we can handle the IPC call ourselves. -Dispatcher* PolicyBase::OnMessageReady(IPCParams* ipc, - CallbackGeneric* callback) { - DCHECK(callback); - static const IPCParams ping1 = {IPC_PING1_TAG, UINT32_TYPE}; - static const IPCParams ping2 = {IPC_PING2_TAG, INOUTPTR_TYPE}; +void* PolicyBase::AddHandleToShare(HANDLE handle) { + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return nullptr; - if (ping1.Matches(ipc) || ping2.Matches(ipc)) { - *callback = reinterpret_cast( - static_cast(&PolicyBase::Ping)); - return this; - } + if (!handle) + return nullptr; - Dispatcher* dispatch = GetDispatcher(ipc->ipc_tag); - if (!dispatch) { - NOTREACHED(); - return NULL; + HANDLE duped_handle = nullptr; + if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), + &duped_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { + return nullptr; } - return dispatch->OnMessageReady(ipc, callback); + handles_to_share_.push_back(new base::win::ScopedHandle(duped_handle)); + return duped_handle; } -// 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); +const HandleList& PolicyBase::GetHandlesBeingShared() { + return handles_to_share_; } -ResultCode PolicyBase::MakeJobObject(HANDLE* job) { +void PolicyBase::ClearSharedHandles() { + STLDeleteElements(&handles_to_share_); +} + +ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* 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) { + if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC; - } - *job = job_obj.Detach(); + + *job = job_obj.Take(); } else { - *job = NULL; + *job = base::win::ScopedHandle(); } return SBOX_ALL_OK; } -ResultCode PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { +ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, + base::win::ScopedHandle* lockdown, + base::win::ScopedHandle* lowbox) { + 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); + DWORD result = CreateRestrictedToken(lockdown_level_, integrity_level_, + PRIMARY, lockdown); if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC; @@ -476,25 +496,54 @@ ResultCode PolicyBase::MakeTokens(HANDLE* initial, HANDLE* lockdown) { 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; + *initial = base::win::ScopedHandle(); return SBOX_ALL_OK; } + 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->Get(), + 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); + lowbox->Set(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); + result = CreateRestrictedToken(initial_level_, integrity_level_, + IMPERSONATION, initial); + if (ERROR_SUCCESS != result) return SBOX_ERROR_GENERIC; - } + return SBOX_ALL_OK; } @@ -505,6 +554,10 @@ const AppContainerAttributes* PolicyBase::GetAppContainer() const { return appcontainer_list_.get(); } +PSID PolicyBase::GetLowBoxSid() const { + return lowbox_sid_; +} + bool PolicyBase::AddTarget(TargetProcess* target) { if (NULL != policy_) policy_maker_->Done(); @@ -521,7 +574,8 @@ bool PolicyBase::AddTarget(TargetProcess* target) { return false; // Initialize the sandbox infrastructure for the target. - if (ERROR_SUCCESS != target->Init(this, policy_, kIPCMemSize, kPolMemSize)) + if (ERROR_SUCCESS != + target->Init(dispatcher_.get(), policy_, kIPCMemSize, kPolMemSize)) return false; g_shared_delayed_integrity_level = delayed_integrity_level_; @@ -602,46 +656,13 @@ 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(arg1); - if (sizeof(uint32) != io_buffer->Size()) - return false; - - uint32* cookie = reinterpret_cast(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 (policy_->entry[i] && !dispatcher_->SetupService(&manager, i)) + return false; } } diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h index 54b0b0bc0d86..b2d1b7c853de 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h @@ -6,13 +6,17 @@ #define SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_ #include +#include +#include #include #include -#include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.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" @@ -28,64 +32,60 @@ class LowLevelPolicy; class TargetProcess; struct PolicyGlobal; -// We act as a policy dispatcher, implementing the handler for the "ping" IPC, -// so we have to provide the appropriate handler on the OnMessageReady method. -// There is a static_cast for the handler, and the compiler only performs the -// cast if the first base class is Dispatcher. -class PolicyBase : public Dispatcher, public TargetPolicy { +typedef std::vector HandleList; + +class PolicyBase final : public TargetPolicy { public: PolicyBase(); // TargetPolicy: - virtual void AddRef() override; - virtual void Release() override; - virtual ResultCode SetTokenLevel(TokenLevel initial, - TokenLevel lockdown) override; - virtual TokenLevel GetInitialTokenLevel() const override; - virtual TokenLevel GetLockdownTokenLevel() const override; - virtual ResultCode SetJobLevel(JobLevel job_level, - uint32 ui_exceptions) override; - virtual ResultCode SetJobMemoryLimit(size_t memory_limit) override; - virtual ResultCode SetAlternateDesktop(bool alternate_winstation) override; - virtual base::string16 GetAlternateDesktop() const override; - virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) override; - virtual void DestroyAlternateDesktop() override; - virtual ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) override; - virtual IntegrityLevel GetIntegrityLevel() const override; - virtual ResultCode SetDelayedIntegrityLevel( - IntegrityLevel integrity_level) override; - virtual ResultCode SetAppContainer(const wchar_t* sid) override; - virtual ResultCode SetCapability(const wchar_t* sid) override; - virtual ResultCode SetProcessMitigations(MitigationFlags flags) override; - virtual MitigationFlags GetProcessMitigations() override; - virtual ResultCode SetDelayedProcessMitigations( - MitigationFlags flags) override; - virtual MitigationFlags GetDelayedProcessMitigations() const override; - virtual void SetStrictInterceptions() override; - virtual ResultCode SetStdoutHandle(HANDLE handle) override; - virtual ResultCode SetStderrHandle(HANDLE handle) override; - virtual ResultCode AddRule(SubSystem subsystem, Semantics semantics, - const wchar_t* pattern) override; - virtual ResultCode AddDllToUnload(const wchar_t* dll_name) override; - virtual ResultCode AddKernelObjectToClose( - const base::char16* handle_type, - const base::char16* handle_name) override; - - // Dispatcher: - virtual Dispatcher* OnMessageReady(IPCParams* ipc, - CallbackGeneric* callback) override; - virtual bool SetupService(InterceptionManager* manager, int service) override; + 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_t 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; // Creates a Job object with the level specified in a previous call to // SetJobLevel(). - ResultCode MakeJobObject(HANDLE* job); + ResultCode MakeJobObject(base::win::ScopedHandle* job); // Creates the two tokens with the levels specified in a previous call to - // SetTokenLevel(). - ResultCode MakeTokens(HANDLE* initial, HANDLE* lockdown); + // SetTokenLevel(). Also creates a lowbox token if specified based on the + // lowbox SID. + ResultCode MakeTokens(base::win::ScopedHandle* initial, + base::win::ScopedHandle* lockdown, + base::win::ScopedHandle* lowbox); const AppContainerAttributes* GetAppContainer() 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); @@ -100,15 +100,15 @@ class PolicyBase : public Dispatcher, public TargetPolicy { HANDLE GetStdoutHandle(); HANDLE GetStderrHandle(); + // Returns the list of handles being shared with the target process. + const HandleList& GetHandlesBeingShared(); + + // Closes the handles being shared with the target and clears out the list. + void ClearSharedHandles(); + private: ~PolicyBase(); - // Test IPC providers. - bool Ping(IPCInfo* ipc, void* cookie); - - // Returns a dispatcher from ipc_targets_. - Dispatcher* GetDispatcher(int ipc_tag); - // Sets up interceptions for a new target. bool SetupAllInterceptions(TargetProcess* target); @@ -131,7 +131,7 @@ class PolicyBase : public Dispatcher, public TargetPolicy { TokenLevel lockdown_level_; TokenLevel initial_level_; JobLevel job_level_; - uint32 ui_exceptions_; + uint32_t ui_exceptions_; size_t memory_limit_; bool use_alternate_desktop_; bool use_alternate_winstation_; @@ -144,8 +144,6 @@ class PolicyBase : public Dispatcher, public TargetPolicy { 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. @@ -158,11 +156,19 @@ class PolicyBase : public Dispatcher, public TargetPolicy { HandleCloser handle_closer_; std::vector capabilities_; scoped_ptr appcontainer_list_; + PSID lowbox_sid_; + base::win::ScopedHandle lowbox_directory_; + scoped_ptr dispatcher_; 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. + HandleList handles_to_share_; + DISALLOW_COPY_AND_ASSIGN(PolicyBase); }; diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc new file mode 100644 index 000000000000..b3f977374b33 --- /dev/null +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc @@ -0,0 +1,22 @@ +// Copyright (c) 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/win/src/sandbox_rand.h" + +#include + +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include +#undef SystemFunction036 + +namespace sandbox { + +bool GetRandom(unsigned int* random_value) { + return RtlGenRandom(random_value, sizeof(unsigned int)) != FALSE; +} + +} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h new file mode 100644 index 000000000000..7c4febcd5982 --- /dev/null +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h @@ -0,0 +1,16 @@ +// Copyright (c) 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_SRC_SANDBOX_RAND_H_ +#define SANDBOX_SRC_SANDBOX_RAND_H_ + + +namespace sandbox { + +// Generate a random value in |random_value|. Returns true on success. +bool GetRandom(unsigned int* random_value); + +} // namespace sandbox + +#endif // SANDBOX_SRC_SANDBOX_RAND_H_ diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_types.h b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h index 22840cef0773..b749b9ca5189 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_types.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h @@ -45,6 +45,8 @@ enum ResultCode { 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 }; @@ -52,13 +54,14 @@ enum ResultCode { // 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_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_WARMUP = 7013, // Failed to warmup. SBOX_FATAL_LAST }; diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc index a1b77d65b68e..6057caffc18f 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc @@ -15,7 +15,8 @@ void InitObjectAttribs(const base::string16& name, ULONG attributes, HANDLE root, OBJECT_ATTRIBUTES* obj_attr, - UNICODE_STRING* uni_name) { + UNICODE_STRING* uni_name, + SECURITY_QUALITY_OF_SERVICE* security_qos) { static RtlInitUnicodeStringFunction RtlInitUnicodeString; if (!RtlInitUnicodeString) { HMODULE ntdll = ::GetModuleHandle(kNtdllName); @@ -25,6 +26,7 @@ void InitObjectAttribs(const base::string16& name, } RtlInitUnicodeString(uni_name, name.c_str()); InitializeObjectAttributes(obj_attr, uni_name, attributes, root, NULL); + obj_attr->SecurityQualityOfService = security_qos; } } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h index 2a17d63b8f92..b1c1f3134dae 100644 --- a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h +++ b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h @@ -8,7 +8,6 @@ #include #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "sandbox/win/src/nt_internals.h" @@ -18,7 +17,8 @@ void InitObjectAttribs(const base::string16& name, ULONG attributes, HANDLE root, OBJECT_ATTRIBUTES* obj_attr, - UNICODE_STRING* uni_name); + UNICODE_STRING* uni_name, + SECURITY_QUALITY_OF_SERVICE* security_qos); } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/security_level.h b/security/sandbox/chromium/sandbox/win/src/security_level.h index da84b75252b5..26ec306a621b 100644 --- a/security/sandbox/chromium/sandbox/win/src/security_level.h +++ b/security/sandbox/chromium/sandbox/win/src/security_level.h @@ -5,7 +5,7 @@ #ifndef SANDBOX_SRC_SECURITY_LEVEL_H_ #define SANDBOX_SRC_SECURITY_LEVEL_H_ -#include "base/basictypes.h" +#include namespace sandbox { @@ -138,7 +138,7 @@ enum JobLevel { // 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; +typedef uint64_t MitigationFlags; // Permanently enables DEP for the target process. Corresponds to // PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE. @@ -199,6 +199,11 @@ const MitigationFlags MITIGATION_EXTENSION_DLL_DISABLE = 0x00000400; // 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/security/sandbox/chromium/sandbox/win/src/service_resolver.h b/security/sandbox/chromium/sandbox/win/src/service_resolver.h index 76daee40cb42..7ac5a2490da9 100644 --- a/security/sandbox/chromium/sandbox/win/src/service_resolver.h +++ b/security/sandbox/chromium/sandbox/win/src/service_resolver.h @@ -5,6 +5,9 @@ #ifndef SANDBOX_SRC_SERVICE_RESOLVER_H__ #define SANDBOX_SRC_SERVICE_RESOLVER_H__ +#include + +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/resolver.h" @@ -16,32 +19,34 @@ 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) {} - virtual ~ServiceResolverThunk() {} + : ntdll_base_(NULL), + process_(process), + relaxed_(relaxed), + relative_jump_(0) {} + ~ServiceResolverThunk() override {} // 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); + 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. - virtual NTSTATUS ResolveInterceptor(const void* module, - const char* function_name, - const void** address); + NTSTATUS ResolveInterceptor(const void* module, + const char* function_name, + const void** address) override; // Implementation of Resolver::ResolveTarget. - virtual NTSTATUS ResolveTarget(const void* module, - const char* function_name, - void** address); + NTSTATUS ResolveTarget(const void* module, + const char* function_name, + void** address) override; // Implementation of Resolver::GetThunkSize. - virtual size_t GetThunkSize() const; + size_t GetThunkSize() const override; // Call this to set up ntdll_base_ which will allow for local patches. virtual void AllowLocalPatches(); @@ -95,10 +100,10 @@ class Wow64ResolverThunk : public ServiceResolverThunk { // The service resolver needs a child process to write to. Wow64ResolverThunk(HANDLE process, bool relaxed) : ServiceResolverThunk(process, relaxed) {} - virtual ~Wow64ResolverThunk() {} + ~Wow64ResolverThunk() override {} private: - virtual bool IsFunctionAService(void* local_thunk) const; + bool IsFunctionAService(void* local_thunk) const override; DISALLOW_COPY_AND_ASSIGN(Wow64ResolverThunk); }; @@ -110,10 +115,10 @@ class Wow64W8ResolverThunk : public ServiceResolverThunk { // The service resolver needs a child process to write to. Wow64W8ResolverThunk(HANDLE process, bool relaxed) : ServiceResolverThunk(process, relaxed) {} - virtual ~Wow64W8ResolverThunk() {} + ~Wow64W8ResolverThunk() override {} private: - virtual bool IsFunctionAService(void* local_thunk) const; + bool IsFunctionAService(void* local_thunk) const override; DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk); }; @@ -125,14 +130,29 @@ class Win8ResolverThunk : public ServiceResolverThunk { // The service resolver needs a child process to write to. Win8ResolverThunk(HANDLE process, bool relaxed) : ServiceResolverThunk(process, relaxed) {} - virtual ~Win8ResolverThunk() {} + ~Win8ResolverThunk() override {} private: - virtual bool IsFunctionAService(void* local_thunk) const; + bool IsFunctionAService(void* local_thunk) const override; DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk); }; +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on WOW64 for Windows 10. +class Wow64W10ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Wow64W10ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + ~Wow64W10ResolverThunk() override {} + + private: + bool IsFunctionAService(void* local_thunk) const override; + + DISALLOW_COPY_AND_ASSIGN(Wow64W10ResolverThunk); +}; + } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc index be9de6b9224f..f809227e9c78 100644 --- a/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc +++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc @@ -4,6 +4,9 @@ #include "sandbox/win/src/service_resolver.h" +#include + +#include "base/bit_cast.h" #include "base/memory/scoped_ptr.h" #include "sandbox/win/src/win_utils.h" @@ -18,7 +21,6 @@ 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; @@ -30,8 +32,6 @@ 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 { @@ -121,11 +121,27 @@ struct Wow64EntryW8 { BYTE nop; }; +// Service code for a 32 bit process running on 64 bit Windows 10. +struct Wow64EntryW10 { + // 00 b828000000 mov eax, 28h + // 05 bab0d54877 mov edx, 7748D5B0h + // 09 ffd2 call edx + // 0b c22800 ret 28h + BYTE mov_eax; // = B8 + ULONG service_id; + BYTE mov_edx; // = BA + ULONG mov_edx_param; + USHORT call_edx; // = FF D2 + BYTE ret; // = C2 + USHORT num_params; +}; + // Make sure that relaxed patching works as expected. const size_t kMinServiceSize = offsetof(ServiceEntry, ret); -COMPILE_ASSERT(sizeof(ServiceEntryW8) >= kMinServiceSize, wrong_service_len); -COMPILE_ASSERT(sizeof(Wow64Entry) >= kMinServiceSize, wrong_service_len); -COMPILE_ASSERT(sizeof(Wow64EntryW8) >= kMinServiceSize, wrong_service_len); +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 { @@ -164,8 +180,9 @@ NTSTATUS ServiceResolverThunk::Setup(const void* target_module, thunk_buffer.get()); if (!IsFunctionAService(&thunk->original) && - (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) + (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) { return STATUS_UNSUCCESSFUL; + } ret = PerformPatch(thunk, thunk_storage); @@ -209,8 +226,9 @@ bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { ServiceEntry function_code; SIZE_T read; if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) + sizeof(function_code), &read)) { return false; + } if (sizeof(function_code) != read) return false; @@ -219,16 +237,18 @@ bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { kMovEdx != function_code.mov_edx || (kCallPtrEdx != function_code.call_ptr_edx && kCallEdx != function_code.call_ptr_edx) || - kRet != function_code.ret) + 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(function_code.stub), - &ki_system_call, sizeof(ki_system_call), &read)) + &ki_system_call, sizeof(ki_system_call), &read)) { return false; + } if (sizeof(ki_system_call) != read) return false; @@ -237,8 +257,10 @@ bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { // 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(ki_system_call), &module_1)) + bit_cast(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 @@ -296,8 +318,9 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, // copy the local thunk buffer to the child SIZE_T written; if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, - thunk_size, &written)) + thunk_size, &written)) { return STATUS_UNSUCCESSFUL; + } if (thunk_size != written) return STATUS_UNSUCCESSFUL; @@ -322,8 +345,9 @@ bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, ServiceEntry function_code; SIZE_T read; if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) + sizeof(function_code), &read)) { return false; + } if (sizeof(function_code) != read) return false; @@ -357,16 +381,19 @@ bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { Wow64Entry function_code; SIZE_T read; if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) + 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) + kCallFs2 != function_code.call_fs2 || + kCallFs3 != function_code.call_fs3) { return false; + } if ((kAddEsp1 == function_code.add_esp1 && kAddEsp2 == function_code.add_esp2 && @@ -383,8 +410,9 @@ bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const { Wow64EntryW8 function_code; SIZE_T read; if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) + sizeof(function_code), &read)) { return false; + } if (sizeof(function_code) != read) return false; @@ -404,8 +432,9 @@ bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { ServiceEntryW8 function_code; SIZE_T read; if (!::ReadProcessMemory(process_, target_, &function_code, - sizeof(function_code), &read)) + sizeof(function_code), &read)) { return false; + } if (sizeof(function_code) != read) return false; @@ -423,4 +452,27 @@ bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { return true; } +bool Wow64W10ResolverThunk::IsFunctionAService(void* local_thunk) const { + Wow64EntryW10 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 || + kCallEdx != function_code.call_edx || + kRet != function_code.ret) { + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + return true; +} + } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc index c0e684c795b2..25ee9db26c5e 100644 --- a/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc +++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/service_resolver.h" +#include + #include "base/memory/scoped_ptr.h" #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/win_utils.h" @@ -139,9 +141,9 @@ NTSTATUS ServiceResolverThunk::Setup(const void* target_module, 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); + NTSTATUS ret = + Init(target_module, interceptor_module, target_name, interceptor_name, + interceptor_entry_point, thunk_storage, storage_bytes); if (!NT_SUCCESS(ret)) return ret; @@ -213,7 +215,7 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, void* remote_thunk) { // Patch the original code. ServiceEntry local_service; - DCHECK_NT(GetInternalThunkSize() >= sizeof(local_service)); + DCHECK_NT(GetInternalThunkSize() <= sizeof(local_service)); if (!SetInternalThunk(&local_service, sizeof(local_service), NULL, interceptor_)) return STATUS_UNSUCCESSFUL; diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc index c7ac7eab1690..25d087573238 100644 --- a/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc @@ -4,7 +4,10 @@ // This file contains unit tests for ServiceResolverThunk. -#include "base/basictypes.h" +#include + +#include "base/bit_cast.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/win/windows_version.h" #include "sandbox/win/src/resolver.h" @@ -38,12 +41,12 @@ class ResolverThunkTest : public T { 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); + ret = T::Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, thunk_storage, + storage_bytes); EXPECT_EQ(STATUS_SUCCESS, ret); - target_ = fake_target_; + this->target_ = fake_target_; return ret; }; @@ -61,6 +64,7 @@ typedef ResolverThunkTest WinXpResolverTest; typedef ResolverThunkTest Win8ResolverTest; typedef ResolverThunkTest Wow64ResolverTest; typedef ResolverThunkTest Wow64W8ResolverTest; +typedef ResolverThunkTest Wow64W10ResolverTest; #endif const BYTE kJump32 = 0xE9; @@ -135,6 +139,8 @@ sandbox::ServiceResolverThunk* GetTestResolver(bool 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_WIN10) + return new Wow64W10ResolverTest(relaxed); if (os_info->version() >= base::win::VERSION_WIN8) return new Wow64W8ResolverTest(relaxed); return new Wow64ResolverTest(relaxed); diff --git a/security/sandbox/chromium/sandbox/win/src/shared_handles.cc b/security/sandbox/chromium/sandbox/win/src/shared_handles.cc deleted file mode 100644 index 423b67bab30e..000000000000 --- a/security/sandbox/chromium/sandbox/win/src/shared_handles.cc +++ /dev/null @@ -1,67 +0,0 @@ -// 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(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/security/sandbox/chromium/sandbox/win/src/shared_handles.h b/security/sandbox/chromium/sandbox/win/src/shared_handles.h deleted file mode 100644 index 2c76bfb28025..000000000000 --- a/security/sandbox/chromium/sandbox/win/src/shared_handles.h +++ /dev/null @@ -1,108 +0,0 @@ -// 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 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/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc index 9d37bbda7d40..eac13937c204 100644 --- a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc +++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc @@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include -#include "sandbox/win/src/sharedmem_ipc_client.h" -#include "sandbox/win/src/sandbox.h" + +#include "base/logging.h" #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/crosscall_params.h" -#include "base/logging.h" +#include "sandbox/win/src/sandbox.h" +#include "sandbox/win/src/sharedmem_ipc_client.h" namespace sandbox { @@ -32,7 +34,6 @@ void SharedMemIPCClient::FreeBuffer(void* buffer) { ChannelControl* channel = control_->channels; LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel); DCHECK_NE(kFreeChannel, static_cast(result)); - result; } // The constructor simply casts the shared memory to the internal @@ -90,7 +91,7 @@ ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params, } else { // The server has crashed and windows has signaled the mutex as // abandoned. - ::InterlockedExchange(&channel[num].state, kAbandonnedChannel); + ::InterlockedExchange(&channel[num].state, kAbandonedChannel); control_->server_alive = 0; return SBOX_ERROR_CHANNEL_ERROR; } diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h index a0c5f2f78de5..67fd0635970f 100644 --- a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h +++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h @@ -5,6 +5,9 @@ #ifndef SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ #define SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__ +#include +#include + #include "sandbox/win/src/crosscall_params.h" #include "sandbox/win/src/sandbox.h" @@ -62,7 +65,7 @@ enum ChannelState { // not used right now kReadyChannel, // IPC abandoned by client side - kAbandonnedChannel + kAbandonedChannel }; // The next two constants control the time outs for the IPC. @@ -81,7 +84,7 @@ struct ChannelControl { // the client waits on the pong event for the IPC answer back HANDLE pong_event; // the IPC unique identifier - uint32 ipc_tag; + uint32_t ipc_tag; }; struct IPCControl { diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc index 5ce7da5d5814..cf2d800e5dd6 100644 --- a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc +++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc @@ -2,15 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #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 "base/stl_util.h" #include "sandbox/win/src/crosscall_params.h" #include "sandbox/win/src/crosscall_server.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/sharedmem_ipc_server.h" namespace { // This handle must not be closed. @@ -19,16 +23,20 @@ volatile HANDLE g_alive_mutex = NULL; namespace sandbox { +SharedMemIPCServer::ServerControl::ServerControl() { +} + +SharedMemIPCServer::ServerControl::~ServerControl() { +} + 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 @@ -51,21 +59,15 @@ SharedMemIPCServer::~SharedMemIPCServer() { // 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; - } + STLDeleteElements(&server_contexts_); if (client_control_) ::UnmapViewOfFile(client_control_); } -bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size, - uint32 channel_size) { +bool SharedMemIPCServer::Init(void* shared_mem, + uint32_t shared_size, + uint32_t channel_size) { // The shared memory needs to be at least as big as a channel. if (shared_size < channel_size) { return false; @@ -111,9 +113,11 @@ bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size, 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) + // 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). In particular, target_process_ + // is a raw handle that is not owned by this object (it's owned by the + // owner of this object), and we are storing it in multiple places. service_context->shared_base = reinterpret_cast(shared_mem); service_context->channel_size = channel_size; service_context->channel = client_context; @@ -122,11 +126,10 @@ bool SharedMemIPCServer::Init(void* shared_mem, uint32 shared_size, 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, + thread_provider_->RegisterWait(this, service_context->ping_event.Get(), ThreadPingEventReady, service_context); } if (!::DuplicateHandle(::GetCurrentProcess(), g_alive_mutex, @@ -164,8 +167,8 @@ bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params, if (kMaxIpcParams < params->GetParamsCount()) return false; - for (uint32 i = 0; i < params->GetParamsCount(); i++) { - uint32 size; + for (uint32_t i = 0; i < params->GetParamsCount(); i++) { + uint32_t size; ArgType type; args[i] = params->GetRawParameter(i, &size, &type); if (args[i]) { @@ -182,7 +185,7 @@ bool GetArgs(CrossCallParamsEx* params, IPCParams* ipc_params, break; } case UINT32_TYPE: { - uint32 data; + uint32_t data; if (!params->GetParameter32(i, &data)) { ReleaseArgs(ipc_params, args); return false; @@ -221,7 +224,7 @@ bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context, CrossCallReturn* call_result) { // Set the default error code; SetCallError(SBOX_ERROR_INVALID_IPC, call_result); - uint32 output_size = 0; + uint32_t 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. @@ -232,7 +235,7 @@ bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context, if (!params.get()) return false; - uint32 tag = params->GetTag(); + uint32_t tag = params->GetTag(); static_assert(0 == INVALID_TYPE, "incorrect type enum"); IPCParams ipc_params = {0}; ipc_params.ipc_tag = tag; @@ -397,24 +400,28 @@ void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context, CrossCallParams* call_params = reinterpret_cast(buffer); memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result)); ::InterlockedExchange(&service_context->channel->state, kAckChannel); - ::SetEvent(service_context->pong_event); + ::SetEvent(service_context->pong_event.Get()); } -bool SharedMemIPCServer::MakeEvents(HANDLE* server_ping, HANDLE* server_pong, +bool SharedMemIPCServer::MakeEvents(base::win::ScopedHandle* server_ping, + base::win::ScopedHandle* 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)) { + server_ping->Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); + if (!::DuplicateHandle(::GetCurrentProcess(), server_ping->Get(), + 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)) { + + server_pong->Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); + if (!::DuplicateHandle(::GetCurrentProcess(), server_pong->Get(), + target_process_, client_pong, kDesiredAccess, FALSE, + 0)) { return false; } return true; diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h index 94d6959b8125..5afca1d6c306 100644 --- a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h +++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h @@ -5,10 +5,13 @@ #ifndef SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ #define SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_ +#include + #include -#include "base/basictypes.h" #include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/win/scoped_handle.h" #include "sandbox/win/src/crosscall_params.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sharedmem_ipc_client.h" @@ -36,20 +39,23 @@ namespace sandbox { class SharedMemIPCServer { public: // Creates the IPC server. - // target_process: handle to the target process. It must be suspended. + // target_process: handle to the target process. It must be suspended. It is + // unfortunate to receive a raw handle (and store it inside this object) as + // that dilutes ownership of the process, but in practice a SharedMemIPCServer + // is owned by TargetProcess, which calls this method, and owns the handle, so + // everything is safe. If that changes, we should break this dependency and + // duplicate the handle instead. // 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); + 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); + bool Init(void* shared_mem, uint32_t shared_size, uint32_t channel_size); private: // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes @@ -63,7 +69,8 @@ class SharedMemIPCServer { // Makes the client and server events. This function is called once // per channel. - bool MakeEvents(HANDLE* server_ping, HANDLE* server_pong, + bool MakeEvents(base::win::ScopedHandle* server_ping, + base::win::ScopedHandle* server_pong, HANDLE* client_ping, HANDLE* client_pong); // A copy this structure is maintained per channel. @@ -72,12 +79,15 @@ class SharedMemIPCServer { // static method without worrying about converting back to a member function // call or about threading issues. struct ServerControl { + ServerControl(); + ~ServerControl(); + // This channel server ping event. - HANDLE ping_event; + base::win::ScopedHandle ping_event; // This channel server pong event. - HANDLE pong_event; + base::win::ScopedHandle pong_event; // The size of this channel. - uint32 channel_size; + uint32_t channel_size; // The pointer to the actual channel data. char* channel_buffer; // The pointer to the base of the shared memory. @@ -113,9 +123,6 @@ class SharedMemIPCServer { // 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_; diff --git a/security/sandbox/chromium/sandbox/win/src/sid.cc b/security/sandbox/chromium/sandbox/win/src/sid.cc index 261605d547b9..19c645bf67e7 100644 --- a/security/sandbox/chromium/sandbox/win/src/sid.cc +++ b/security/sandbox/chromium/sandbox/win/src/sid.cc @@ -16,7 +16,7 @@ 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); + (void)result; } const SID *Sid::GetPSID() const { diff --git a/security/sandbox/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp b/security/sandbox/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp index 999d76bb21a8..b5016009d60a 100644 --- a/security/sandbox/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp +++ b/security/sandbox/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp @@ -6,6 +6,8 @@ #include "sandbox/win/src/sidestep/preamble_patcher.h" +#include + #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sidestep/mini_disassembler.h" diff --git a/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.cc b/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.cc index 828c000a7cf4..d5da2fbe95a5 100644 --- a/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.cc +++ b/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/sidestep_resolver.h" +#include + #include "base/win/pe_image.h" #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sidestep/preamble_patcher.h" diff --git a/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.h b/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.h index 30d8cb3fbe1c..8088272ee4ed 100644 --- a/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.h +++ b/security/sandbox/chromium/sandbox/win/src/sidestep_resolver.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_SIDESTEP_RESOLVER_H__ #define SANDBOX_SRC_SIDESTEP_RESOLVER_H__ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/resolver.h" @@ -15,20 +17,20 @@ namespace sandbox { class SidestepResolverThunk : public ResolverThunk { public: SidestepResolverThunk() {} - virtual ~SidestepResolverThunk() {} + ~SidestepResolverThunk() override {} // 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); + 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. - virtual size_t GetThunkSize() const; + size_t GetThunkSize() const override; private: DISALLOW_COPY_AND_ASSIGN(SidestepResolverThunk); @@ -41,20 +43,20 @@ class SidestepResolverThunk : public ResolverThunk { class SmartSidestepResolverThunk : public SidestepResolverThunk { public: SmartSidestepResolverThunk() {} - virtual ~SmartSidestepResolverThunk() {} + ~SmartSidestepResolverThunk() override {} // 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); + 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. - virtual size_t GetThunkSize() const; + size_t GetThunkSize() const override; private: // Performs the actual call to the interceptor if the conditions are correct diff --git a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc index b83055dc305d..2e5d1c50f2dd 100644 --- a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc +++ b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/sync_dispatcher.h" +#include + #include "base/win/windows_version.h" #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/interception.h" @@ -20,14 +22,12 @@ 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(&SyncDispatcher::CreateEvent) - }; + {IPC_CREATEEVENT_TAG, {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE}}, + reinterpret_cast(&SyncDispatcher::CreateEvent)}; static const IPCCall open_params = { - {IPC_OPENEVENT_TAG, WCHAR_TYPE, UINT32_TYPE}, - reinterpret_cast(&SyncDispatcher::OpenEvent) - }; + {IPC_OPENEVENT_TAG, {WCHAR_TYPE, UINT32_TYPE}}, + reinterpret_cast(&SyncDispatcher::OpenEvent)}; ipc_calls_.push_back(create_params); ipc_calls_.push_back(open_params); @@ -44,8 +44,8 @@ bool SyncDispatcher::SetupService(InterceptionManager* manager, bool SyncDispatcher::CreateEvent(IPCInfo* ipc, base::string16* name, - uint32 event_type, - uint32 initial_state) { + uint32_t event_type, + uint32_t initial_state) { const wchar_t* event_name = name->c_str(); CountedParameterSet params; params[NameBased::NAME] = ParamPickerMake(event_name); @@ -62,7 +62,7 @@ bool SyncDispatcher::CreateEvent(IPCInfo* ipc, bool SyncDispatcher::OpenEvent(IPCInfo* ipc, base::string16* name, - uint32 desired_access) { + uint32_t desired_access) { const wchar_t* event_name = name->c_str(); CountedParameterSet params; diff --git a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h index 56b566468907..8a2a0e582765 100644 --- a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h +++ b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h @@ -5,7 +5,9 @@ #ifndef SANDBOX_SRC_SYNC_DISPATCHER_H_ #define SANDBOX_SRC_SYNC_DISPATCHER_H_ -#include "base/basictypes.h" +#include + +#include "base/macros.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/sandbox_policy_base.h" @@ -16,20 +18,20 @@ namespace sandbox { class SyncDispatcher : public Dispatcher { public: explicit SyncDispatcher(PolicyBase* policy_base); - ~SyncDispatcher() {} + ~SyncDispatcher() override {} // Dispatcher interface. - virtual bool SetupService(InterceptionManager* manager, int service); + 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); + bool CreateEvent(IPCInfo* ipc, + base::string16* name, + uint32_t event_type, + uint32_t initial_state); // Processes IPC requests coming from calls to OpenEvent in the target. - bool OpenEvent(IPCInfo* ipc, base::string16* name, uint32 desired_access); + bool OpenEvent(IPCInfo* ipc, base::string16* name, uint32_t desired_access); PolicyBase* policy_base_; DISALLOW_COPY_AND_ASSIGN(SyncDispatcher); diff --git a/security/sandbox/chromium/sandbox/win/src/sync_interception.cc b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc index 5e73df195905..420d185ab229 100644 --- a/security/sandbox/chromium/sandbox/win/src/sync_interception.cc +++ b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc @@ -4,6 +4,8 @@ #include "sandbox/win/src/sync_interception.h" +#include + #include "sandbox/win/src/crosscall_client.h" #include "sandbox/win/src/ipc_tags.h" #include "sandbox/win/src/policy_params.h" @@ -12,12 +14,11 @@ #include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sharedmem_ipc_client.h" #include "sandbox/win/src/target_services.h" -#include "mozilla/sandboxing/sandboxLogging.h" namespace sandbox { ResultCode ProxyCreateEvent(LPCWSTR name, - uint32 initial_state, + uint32_t initial_state, EVENT_TYPE event_type, void* ipc_memory, CrossCallReturn* answer) { @@ -34,7 +35,7 @@ ResultCode ProxyCreateEvent(LPCWSTR name, } ResultCode ProxyOpenEvent(LPCWSTR name, - uint32 desired_access, + uint32_t desired_access, void* ipc_memory, CrossCallReturn* answer) { CountedParameterSet params; @@ -63,10 +64,6 @@ NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent, if (status != STATUS_ACCESS_DENIED || !object_attributes) return status; - mozilla::sandboxing::LogBlocked("NtCreatEvent", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -84,7 +81,7 @@ NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent, object_attribs_copy.RootDirectory = NULL; wchar_t* name = NULL; - uint32 attributes = 0; + uint32_t attributes = 0; NTSTATUS ret = AllocAndCopyName(&object_attribs_copy, &name, &attributes, NULL); if (!NT_SUCCESS(ret) || name == NULL) @@ -106,9 +103,6 @@ NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent, } __except(EXCEPTION_EXECUTE_HANDLER) { break; } - mozilla::sandboxing::LogAllowed("NtCreateEvent", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); } while (false); return status; @@ -123,10 +117,6 @@ NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent, if (status != STATUS_ACCESS_DENIED || !object_attributes) return status; - mozilla::sandboxing::LogBlocked("NtOpenEvent", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); - // // We don't trust that the IPC can work this early. if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled()) return status; @@ -144,7 +134,7 @@ NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent, object_attribs_copy.RootDirectory = NULL; wchar_t* name = NULL; - uint32 attributes = 0; + uint32_t attributes = 0; NTSTATUS ret = AllocAndCopyName(&object_attribs_copy, &name, &attributes, NULL); if (!NT_SUCCESS(ret) || name == NULL) @@ -165,9 +155,6 @@ NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent, } __except(EXCEPTION_EXECUTE_HANDLER) { break; } - mozilla::sandboxing::LogAllowed("NtOpenEvent", - object_attributes->ObjectName->Buffer, - object_attributes->ObjectName->Length); } while (false); return status; diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy.cc b/security/sandbox/chromium/sandbox/win/src/sync_policy.cc index 5c7e0fe41191..7ef094ff73e5 100644 --- a/security/sandbox/chromium/sandbox/win/src/sync_policy.cc +++ b/security/sandbox/chromium/sandbox/win/src/sync_policy.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include "sandbox/win/src/sync_policy.h" @@ -41,40 +43,33 @@ NTSTATUS ResolveSymbolicLink(const base::string16& directory_name, UNICODE_STRING symbolic_link_directory_string = {}; InitObjectAttribs(directory_name, OBJ_CASE_INSENSITIVE, NULL, &symbolic_link_directory_attributes, - &symbolic_link_directory_string); + &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; + if (!NT_SUCCESS(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); + &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; + CHECK(NT_SUCCESS(NtClose(symbolic_link_directory))); + if (!NT_SUCCESS(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; + CHECK(NT_SUCCESS(NtClose(symbolic_link))); return status; } @@ -83,13 +78,10 @@ NTSTATUS ResolveSymbolicLink(const base::string16& directory_name, target_path.Buffer = new wchar_t[target_path.MaximumLength + 1]; status = NtQuerySymbolicLinkObject(symbolic_link, &target_path, &target_length); - if (status == STATUS_SUCCESS) { + if (NT_SUCCESS(status)) target->assign(target_path.Buffer, target_length); - } else { - DLOG(ERROR) << "Failed to resolve symbolic link. Error: " << status; - } - NtClose(symbolic_link); + CHECK(NT_SUCCESS(NtClose(symbolic_link))); delete[] target_path.Buffer; return status; } @@ -112,7 +104,7 @@ NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) { NTSTATUS status = ResolveSymbolicLink(L"\\Sessions\\BNOLINKS", base::StringPrintf(L"%d", session_id), &base_named_objects_path); - if (status != STATUS_SUCCESS) { + if (!NT_SUCCESS(status)) { DLOG(ERROR) << "Failed to resolve BaseNamedObjects path. Error: " << status; return status; @@ -121,11 +113,11 @@ NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) { UNICODE_STRING directory_name = {}; OBJECT_ATTRIBUTES object_attributes = {}; InitObjectAttribs(base_named_objects_path, OBJ_CASE_INSENSITIVE, NULL, - &object_attributes, &directory_name); + &object_attributes, &directory_name, NULL); status = NtOpenDirectoryObject(&base_named_objects_handle, DIRECTORY_ALL_ACCESS, &object_attributes); - if (status == STATUS_SUCCESS) + if (NT_SUCCESS(status)) *directory = base_named_objects_handle; return status; } @@ -155,8 +147,8 @@ bool SyncPolicy::GenerateRules(const wchar_t* name, 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; + uint32_t allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL; + uint32_t restricted_flags = ~allowed_flags; open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND); } @@ -178,10 +170,10 @@ bool SyncPolicy::GenerateRules(const wchar_t* name, NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &event_name, - uint32 event_type, - uint32 initial_state, - HANDLE *handle) { + const base::string16& event_name, + uint32_t event_type, + uint32_t initial_state, + HANDLE* handle) { NtCreateEventFunction NtCreateEvent = NULL; ResolveNTFunctionPtr("NtCreateEvent", &NtCreateEvent); @@ -198,7 +190,7 @@ NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result, UNICODE_STRING unicode_event_name = {}; OBJECT_ATTRIBUTES object_attributes = {}; InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory, - &object_attributes, &unicode_event_name); + &object_attributes, &unicode_event_name, NULL); HANDLE local_handle = NULL; status = NtCreateEvent(&local_handle, EVENT_ALL_ACCESS, &object_attributes, @@ -217,9 +209,9 @@ NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result, NTSTATUS SyncPolicy::OpenEventAction(EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &event_name, - uint32 desired_access, - HANDLE *handle) { + const base::string16& event_name, + uint32_t desired_access, + HANDLE* handle) { NtOpenEventFunction NtOpenEvent = NULL; ResolveNTFunctionPtr("NtOpenEvent", &NtOpenEvent); @@ -236,7 +228,7 @@ NTSTATUS SyncPolicy::OpenEventAction(EvalResult eval_result, UNICODE_STRING unicode_event_name = {}; OBJECT_ATTRIBUTES object_attributes = {}; InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory, - &object_attributes, &unicode_event_name); + &object_attributes, &unicode_event_name, NULL); HANDLE local_handle = NULL; status = NtOpenEvent(&local_handle, desired_access, &object_attributes); diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy.h b/security/sandbox/chromium/sandbox/win/src/sync_policy.h index e370e4bdacb0..24e5c7d87b61 100644 --- a/security/sandbox/chromium/sandbox/win/src/sync_policy.h +++ b/security/sandbox/chromium/sandbox/win/src/sync_policy.h @@ -5,9 +5,10 @@ #ifndef SANDBOX_SRC_SYNC_POLICY_H__ #define SANDBOX_SRC_SYNC_POLICY_H__ +#include + #include -#include "base/basictypes.h" #include "base/strings/string16.h" #include "sandbox/win/src/crosscall_server.h" #include "sandbox/win/src/nt_internals.h" @@ -35,15 +36,15 @@ class SyncPolicy { // 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); + const base::string16& event_name, + uint32_t event_type, + uint32_t initial_state, + HANDLE* handle); static NTSTATUS OpenEventAction(EvalResult eval_result, const ClientInfo& client_info, - const base::string16 &event_name, - uint32 desired_access, - HANDLE *handle); + const base::string16& event_name, + uint32_t desired_access, + HANDLE* handle); }; } // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc index 5368943c1cd2..875bc4b2c695 100644 --- a/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc @@ -29,7 +29,8 @@ SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv) { return SBOX_TEST_SUCCEEDED; if (ERROR_ACCESS_DENIED == error_open || - ERROR_BAD_PATHNAME == error_open) + ERROR_BAD_PATHNAME == error_open || + ERROR_FILE_NOT_FOUND == error_open) return SBOX_TEST_DENIED; return SBOX_TEST_FAILED; @@ -71,9 +72,10 @@ SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t **argv) { if (event_open.IsValid() && event_create.IsValid()) return SBOX_TEST_SUCCEEDED; - if (event_open.IsValid() && !event_create.IsValid() || - !event_open.IsValid() && event_create.IsValid()) + 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()) @@ -88,7 +90,7 @@ SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t **argv) { } // Tests the creation of events using all the possible combinations. -TEST(SyncPolicyTest, DISABLED_TestEvent) { +TEST(SyncPolicyTest, TestEvent) { TestRunner runner; EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, TargetPolicy::EVENTS_ALLOW_ANY, @@ -112,7 +114,7 @@ TEST(SyncPolicyTest, DISABLED_TestEvent) { } // Tests opening events with read only access. -TEST(SyncPolicyTest, DISABLED_TestEventReadOnly) { +TEST(SyncPolicyTest, TestEventReadOnly) { TestRunner runner; EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC, TargetPolicy::EVENTS_ALLOW_READONLY, diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.cc b/security/sandbox/chromium/sandbox/win/src/target_process.cc index 2c5bf3b1f973..e27655e7ecfb 100644 --- a/security/sandbox/chromium/sandbox/win/src/target_process.cc +++ b/security/sandbox/chromium/sandbox/win/src/target_process.cc @@ -4,16 +4,20 @@ #include "sandbox/win/src/target_process.h" -#include "base/basictypes.h" +#include +#include + +#include "base/macros.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/crosscall_server.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 { @@ -35,7 +39,7 @@ void CopyPolicyToTarget(const void* source, size_t size, void* dest) { } } -} +} // namespace namespace sandbox { @@ -63,18 +67,20 @@ void* GetBaseAddress(const wchar_t* exe_name, void* entry_point) { 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), +TargetProcess::TargetProcess(base::win::ScopedHandle initial_token, + base::win::ScopedHandle lockdown_token, + base::win::ScopedHandle lowbox_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.Pass()), + initial_token_(initial_token.Pass()), + lowbox_token_(lowbox_token.Pass()), job_(job), thread_pool_(thread_pool), - base_address_(NULL) { -} + base_address_(NULL) {} TargetProcess::~TargetProcess() { DWORD exit_code = 0; @@ -97,7 +103,7 @@ TargetProcess::~TargetProcess() { // that. if (shared_section_.IsValid()) shared_section_.Take(); - ipc_server_.release(); + ignore_result(ipc_server_.release()); sandbox_process_info_.TakeProcessHandle(); return; } @@ -115,6 +121,12 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, bool inherit_handles, const base::win::StartupInformation& startup_info, base::win::ScopedProcessInformation* target_info) { + if (lowbox_token_.IsValid() && + base::win::GetVersion() < base::win::VERSION_WIN8) { + // We don't allow lowbox_token below Windows 8. + return ERROR_INVALID_PARAMETER; + } + exe_name_.reset(_wcsdup(exe_path)); // the command line needs to be writable by CreateProcess(). @@ -134,21 +146,17 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, } PROCESS_INFORMATION temp_process_info = {}; - if (!::CreateProcessAsUserW(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. + if (!::CreateProcessAsUserW(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); - lockdown_token_.Close(); DWORD win_result = ERROR_SUCCESS; @@ -200,6 +208,25 @@ DWORD TargetProcess::Create(const wchar_t* exe_path, return win_result; } + if (lowbox_token_.IsValid()) { + PROCESS_ACCESS_TOKEN process_access_token; + process_access_token.thread = process_info.thread_handle(); + process_access_token.token = lowbox_token_.Get(); + + NtSetInformationProcess SetInformationProcess = NULL; + ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); + + NTSTATUS status = SetInformationProcess( + process_info.process_handle(), + static_cast(NtProcessInformationAccessToken), + &process_access_token, sizeof(process_access_token)); + if (!NT_SUCCESS(status)) { + win_result = ERROR_INVALID_TOKEN; + ::TerminateProcess(process_info.process_handle(), 0); // exit code + return win_result; + } + } + base_address_ = GetBaseAddress(exe_path, entry_point); sandbox_process_info_.Set(process_info.Take()); return win_result; @@ -226,8 +253,6 @@ ResultCode TargetProcess::TransferVariable(const char* name, void* address, size_t offset = reinterpret_cast(child_var) - reinterpret_cast(module); child_var = reinterpret_cast(MainModule()) + offset; -#else - UNREFERENCED_PARAMETER(name); #endif SIZE_T written; @@ -243,8 +268,10 @@ ResultCode TargetProcess::TransferVariable(const char* name, void* address, // 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) { +DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, + void* policy, + uint32_t shared_IPC_size, + uint32_t 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 @@ -261,7 +288,7 @@ DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy, return ::GetLastError(); } - DWORD access = FILE_MAP_READ | FILE_MAP_WRITE; + DWORD access = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY; HANDLE target_shared_section; if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(), sandbox_process_info_.process_handle(), @@ -309,7 +336,7 @@ DWORD TargetProcess::Init(Dispatcher* ipc_dispatcher, void* policy, ipc_server_.reset( new SharedMemIPCServer(sandbox_process_info_.process_handle(), sandbox_process_info_.process_id(), - job_, thread_pool_, ipc_dispatcher)); + thread_pool_, ipc_dispatcher)); if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize)) return ERROR_NOT_ENOUGH_MEMORY; @@ -328,7 +355,9 @@ void TargetProcess::Terminate() { } TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) { - TargetProcess* target = new TargetProcess(NULL, NULL, NULL, NULL); + TargetProcess* target = + new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(), + base::win::ScopedHandle(), NULL, NULL); PROCESS_INFORMATION process_info = {}; process_info.hProcess = process; target->sandbox_process_info_.Set(process_info); diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.h b/security/sandbox/chromium/sandbox/win/src/target_process.h index 9a8dded55a28..c00f2cea8e2f 100644 --- a/security/sandbox/chromium/sandbox/win/src/target_process.h +++ b/security/sandbox/chromium/sandbox/win/src/target_process.h @@ -6,8 +6,10 @@ #define SANDBOX_WIN_SRC_TARGET_PROCESS_H_ #include +#include +#include -#include "base/basictypes.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/win/scoped_handle.h" #include "base/win/scoped_process_information.h" @@ -32,8 +34,12 @@ class ThreadProvider; // 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, + // The constructor takes ownership of |initial_token|, |lockdown_token| + // and |lowbox_token|. + TargetProcess(base::win::ScopedHandle initial_token, + base::win::ScopedHandle lockdown_token, + base::win::ScopedHandle lowbox_token, + HANDLE job, ThreadProvider* thread_pool); ~TargetProcess(); @@ -56,8 +62,10 @@ class TargetProcess { // 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); + DWORD Init(Dispatcher* ipc_dispatcher, + void* policy, + uint32_t shared_IPC_size, + uint32_t shared_policy_size); // Returns the handle to the target process. HANDLE Process() const { @@ -102,6 +110,9 @@ class TargetProcess { // 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_; + // The lowbox token associated with the process. This token is set after the + // process creation. + base::win::ScopedHandle lowbox_token_; // Kernel handle to the shared memory used by the IPC server. base::win::ScopedHandle shared_section_; // Job object containing the target process. diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc index 2f8477984a57..7537245b655b 100644 --- a/security/sandbox/chromium/sandbox/win/src/target_services.cc +++ b/security/sandbox/chromium/sandbox/win/src/target_services.cc @@ -7,8 +7,9 @@ #include #include +#include -#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" @@ -16,9 +17,9 @@ #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_nt_util.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 { @@ -46,11 +47,11 @@ bool FlushCachedRegHandles() { } // Checks if we have handle entries pending and runs the closer. -bool CloseOpenHandles() { +// Updates is_csrss_connected based on which handle types are closed. +bool CloseOpenHandles(bool* is_csrss_connected) { if (sandbox::HandleCloserAgent::NeedsHandlesClosed()) { sandbox::HandleCloserAgent handle_closer; - - handle_closer.InitializeHandlesToClose(); + handle_closer.InitializeHandlesToClose(is_csrss_connected); if (!handle_closer.CloseHandles()) return false; } @@ -58,6 +59,45 @@ bool CloseOpenHandles() { return true; } +// GetUserDefaultLocaleName is not available on WIN XP. So we'll +// load it on-the-fly. +const wchar_t kKernel32DllName[] = L"kernel32.dll"; +typedef decltype(GetUserDefaultLocaleName)* GetUserDefaultLocaleNameFunction; + +// Warm up language subsystems before the sandbox is turned on. +// Tested on Win8.1 x64: +// This needs to happen after RevertToSelf() is called, because (at least) in +// the case of GetUserDefaultLCID() it checks the TEB to see if the process is +// impersonating (TEB!IsImpersonating). If it is, the cached locale information +// is not used, nor is it set. Therefore, calls after RevertToSelf() will not +// have warmed-up values to use. +bool WarmupWindowsLocales() { + // NOTE(liamjm): When last checked (Win 8.1 x64) it wasn't necessary to + // warmup all of these functions, but let's not assume that. + ::GetUserDefaultLangID(); + ::GetUserDefaultLCID(); + if (base::win::GetVersion() >= base::win::VERSION_VISTA) { + static GetUserDefaultLocaleNameFunction GetUserDefaultLocaleName_func = + NULL; + if (!GetUserDefaultLocaleName_func) { + HMODULE kernel32_dll = ::GetModuleHandle(kKernel32DllName); + if (!kernel32_dll) { + return false; + } + GetUserDefaultLocaleName_func = + reinterpret_cast( + GetProcAddress(kernel32_dll, "GetUserDefaultLocaleName")); + if (!GetUserDefaultLocaleName_func) { + return false; + } + } + wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0}; + return (0 != GetUserDefaultLocaleName_func( + localeName, LOCALE_NAME_MAX_LENGTH * sizeof(wchar_t))); + } + 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 @@ -95,8 +135,12 @@ void TargetServicesBase::LowerToken() { ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES); if (ERROR_SUCCESS != ::RegDisablePredefinedCache()) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE); - if (!CloseOpenHandles()) + if (!WarmupWindowsLocales()) + ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_WARMUP); + bool is_csrss_connected = true; + if (!CloseOpenHandles(&is_csrss_connected)) ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES); + process_state_.SetCsrssConnected(is_csrss_connected); // Enabling mitigations must happen last otherwise handle closing breaks if (g_shared_delayed_mitigations && !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations)) @@ -124,8 +168,8 @@ bool TargetServicesBase::TestIPCPing(int version) { CrossCallReturn answer = {0}; if (1 == version) { - uint32 tick1 = ::GetTickCount(); - uint32 cookie = 717115; + uint32_t tick1 = ::GetTickCount(); + uint32_t cookie = 717115; ResultCode code = CrossCall(ipc, IPC_PING1_TAG, cookie, &answer); if (SBOX_ALL_OK != code) { @@ -138,7 +182,7 @@ bool TargetServicesBase::TestIPCPing(int version) { } // 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(); + uint32_t tick2 = ::GetTickCount(); if (tick2 >= tick1) { if ((answer.extended[0].unsigned_int < tick1) || (answer.extended[0].unsigned_int > tick2)) { @@ -150,7 +194,7 @@ bool TargetServicesBase::TestIPCPing(int version) { return false; } } else if (2 == version) { - uint32 cookie = 717111; + uint32_t cookie = 717111; InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie)); ResultCode code = CrossCall(ipc, IPC_PING2_TAG, counted_buffer, &answer); @@ -166,7 +210,7 @@ bool TargetServicesBase::TestIPCPing(int version) { return true; } -ProcessState::ProcessState() : process_state_(0) { +ProcessState::ProcessState() : process_state_(0), csrss_connected_(true) { } bool ProcessState::IsKernel32Loaded() const { @@ -181,6 +225,10 @@ bool ProcessState::RevertedToSelf() const { return process_state_ > 2; } +bool ProcessState::IsCsrssConnected() const { + return csrss_connected_; +} + void ProcessState::SetKernel32Loaded() { if (!process_state_) process_state_ = 1; @@ -196,6 +244,11 @@ void ProcessState::SetRevertedToSelf() { process_state_ = 3; } +void ProcessState::SetCsrssConnected(bool csrss_connected) { + csrss_connected_ = csrss_connected; +} + + ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle, DWORD target_process_id, HANDLE* target_handle, diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.h b/security/sandbox/chromium/sandbox/win/src/target_services.h index 557eaf1dc469..37169485d6c3 100644 --- a/security/sandbox/chromium/sandbox/win/src/target_services.h +++ b/security/sandbox/chromium/sandbox/win/src/target_services.h @@ -5,7 +5,7 @@ #ifndef SANDBOX_SRC_TARGET_SERVICES_H__ #define SANDBOX_SRC_TARGET_SERVICES_H__ -#include "base/basictypes.h" +#include "base/macros.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/win_utils.h" @@ -20,13 +20,17 @@ class ProcessState { bool InitCalled() const; // Returns true if LowerToken has been called. bool RevertedToSelf() const; + // Returns true if Csrss is connected. + bool IsCsrssConnected() const; // Set the current state. void SetKernel32Loaded(); void SetInitCalled(); void SetRevertedToSelf(); + void SetCsrssConnected(bool csrss_connected); private: int process_state_; + bool csrss_connected_; DISALLOW_COPY_AND_ASSIGN(ProcessState); }; @@ -39,14 +43,14 @@ class TargetServicesBase : public TargetServices { TargetServicesBase(); // Public interface of TargetServices. - virtual ResultCode Init(); - virtual void LowerToken(); - virtual ProcessState* GetState(); - virtual ResultCode DuplicateHandle(HANDLE source_handle, - DWORD target_process_id, - HANDLE* target_handle, - DWORD desired_access, - DWORD options); + 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(); diff --git a/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc b/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc index f439810851fe..d32fdda5b627 100644 --- a/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include "sandbox/win/src/win2k_threadpool.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,22 +21,22 @@ namespace sandbox { TEST(IPCTest, ThreadPoolRegisterTest1) { Win2kThreadPool thread_pool; - EXPECT_EQ(0, thread_pool.OutstandingWaits()); + EXPECT_EQ(0u, thread_pool.OutstandingWaits()); HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - uint32 context = 0; + uint32_t context = 0; EXPECT_FALSE(thread_pool.RegisterWait(0, event1, EmptyCallBack, &context)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); + EXPECT_EQ(0u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.RegisterWait(this, event1, EmptyCallBack, &context)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_EQ(1u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.RegisterWait(this, event2, EmptyCallBack, &context)); - EXPECT_EQ(2, thread_pool.OutstandingWaits()); + EXPECT_EQ(2u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.UnRegisterWaits(this)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); + EXPECT_EQ(0u, thread_pool.OutstandingWaits()); EXPECT_EQ(TRUE, ::CloseHandle(event1)); EXPECT_EQ(TRUE, ::CloseHandle(event2)); @@ -47,22 +49,22 @@ TEST(IPCTest, ThreadPoolRegisterTest2) { HANDLE event1 = ::CreateEventW(NULL, FALSE, FALSE, NULL); HANDLE event2 = ::CreateEventW(NULL, FALSE, FALSE, NULL); - uint32 context = 0; - uint32 c1 = 0; - uint32 c2 = 0; + uint32_t context = 0; + uint32_t c1 = 0; + uint32_t c2 = 0; EXPECT_TRUE(thread_pool.RegisterWait(&c1, event1, EmptyCallBack, &context)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_EQ(1u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.RegisterWait(&c2, event2, EmptyCallBack, &context)); - EXPECT_EQ(2, thread_pool.OutstandingWaits()); + EXPECT_EQ(2u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_EQ(1u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2)); - EXPECT_EQ(1, thread_pool.OutstandingWaits()); + EXPECT_EQ(1u, thread_pool.OutstandingWaits()); EXPECT_TRUE(thread_pool.UnRegisterWaits(&c1)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); + EXPECT_EQ(0u, thread_pool.OutstandingWaits()); EXPECT_EQ(TRUE, ::CloseHandle(event1)); EXPECT_EQ(TRUE, ::CloseHandle(event2)); @@ -83,9 +85,10 @@ TEST(IPCTest, ThreadPoolSignalAndWaitTest) { EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, FALSE)); EXPECT_TRUE(thread_pool.UnRegisterWaits(this)); - EXPECT_EQ(0, thread_pool.OutstandingWaits()); + EXPECT_EQ(0u, thread_pool.OutstandingWaits()); - EXPECT_EQ(WAIT_TIMEOUT, ::SignalObjectAndWait(event1, event2, 1000, FALSE)); + EXPECT_EQ(static_cast(WAIT_TIMEOUT), + ::SignalObjectAndWait(event1, event2, 1000, FALSE)); EXPECT_EQ(TRUE, ::CloseHandle(event1)); EXPECT_EQ(TRUE, ::CloseHandle(event2)); diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc new file mode 100644 index 000000000000..e6e224b4df8a --- /dev/null +++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc @@ -0,0 +1,146 @@ +// 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/win/src/top_level_dispatcher.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/win/src/crosscall_server.h" +#include "sandbox/win/src/filesystem_dispatcher.h" +#include "sandbox/win/src/handle_dispatcher.h" +#include "sandbox/win/src/interception.h" +#include "sandbox/win/src/internal_types.h" +#include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/named_pipe_dispatcher.h" +#include "sandbox/win/src/process_mitigations_win32k_dispatcher.h" +#include "sandbox/win/src/process_thread_dispatcher.h" +#include "sandbox/win/src/registry_dispatcher.h" +#include "sandbox/win/src/sandbox_policy_base.h" +#include "sandbox/win/src/sync_dispatcher.h" + +namespace sandbox { + +TopLevelDispatcher::TopLevelDispatcher(PolicyBase* policy) : policy_(policy) { + // Initialize the IPC dispatcher array. + memset(ipc_targets_, 0, sizeof(ipc_targets_)); + Dispatcher* dispatcher; + + dispatcher = new FilesystemDispatcher(policy_); + 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; + filesystem_dispatcher_.reset(dispatcher); + + dispatcher = new NamedPipeDispatcher(policy_); + ipc_targets_[IPC_CREATENAMEDPIPEW_TAG] = dispatcher; + named_pipe_dispatcher_.reset(dispatcher); + + dispatcher = new ThreadProcessDispatcher(policy_); + 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; + thread_process_dispatcher_.reset(dispatcher); + + dispatcher = new SyncDispatcher(policy_); + ipc_targets_[IPC_CREATEEVENT_TAG] = dispatcher; + ipc_targets_[IPC_OPENEVENT_TAG] = dispatcher; + sync_dispatcher_.reset(dispatcher); + + dispatcher = new RegistryDispatcher(policy_); + ipc_targets_[IPC_NTCREATEKEY_TAG] = dispatcher; + ipc_targets_[IPC_NTOPENKEY_TAG] = dispatcher; + registry_dispatcher_.reset(dispatcher); + + dispatcher = new HandleDispatcher(policy_); + ipc_targets_[IPC_DUPLICATEHANDLEPROXY_TAG] = dispatcher; + handle_dispatcher_.reset(dispatcher); + + dispatcher = new ProcessMitigationsWin32KDispatcher(policy_); + ipc_targets_[IPC_GDI_GDIDLLINITIALIZE_TAG] = dispatcher; + ipc_targets_[IPC_GDI_GETSTOCKOBJECT_TAG] = dispatcher; + ipc_targets_[IPC_USER_REGISTERCLASSW_TAG] = dispatcher; + process_mitigations_win32k_dispatcher_.reset(dispatcher); +} + +TopLevelDispatcher::~TopLevelDispatcher() {} + +// 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* TopLevelDispatcher::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( + static_cast(&TopLevelDispatcher::Ping)); + return this; + } + + Dispatcher* dispatcher = GetDispatcher(ipc->ipc_tag); + if (!dispatcher) { + NOTREACHED(); + return NULL; + } + return dispatcher->OnMessageReady(ipc, callback); +} + +// Delegate to the appropriate dispatcher. +bool TopLevelDispatcher::SetupService(InterceptionManager* manager, + int service) { + if (IPC_PING1_TAG == service || IPC_PING2_TAG == service) + return true; + + Dispatcher* dispatcher = GetDispatcher(service); + if (!dispatcher) { + NOTREACHED(); + return false; + } + return dispatcher->SetupService(manager, service); +} + +// 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 TopLevelDispatcher::Ping(IPCInfo* ipc, void* arg1) { + switch (ipc->ipc_tag) { + case IPC_PING1_TAG: { + IPCInt ipc_int(arg1); + uint32_t 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(arg1); + if (sizeof(uint32_t) != io_buffer->Size()) + return false; + + uint32_t* cookie = reinterpret_cast(io_buffer->Buffer()); + *cookie = (*cookie) * 3; + return true; + } + default: + return false; + } +} + +Dispatcher* TopLevelDispatcher::GetDispatcher(int ipc_tag) { + if (ipc_tag >= IPC_LAST_TAG || ipc_tag <= IPC_UNUSED_TAG) + return NULL; + + return ipc_targets_[ipc_tag]; +} + +} // namespace sandbox diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h new file mode 100644 index 000000000000..c9306de1cb28 --- /dev/null +++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h @@ -0,0 +1,51 @@ +// 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_SRC_TOP_LEVEL_DISPATCHER_H__ +#define SANDBOX_SRC_TOP_LEVEL_DISPATCHER_H__ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "sandbox/win/src/crosscall_server.h" +#include "sandbox/win/src/interception.h" +#include "sandbox/win/src/ipc_tags.h" +#include "sandbox/win/src/sandbox_policy_base.h" + +namespace sandbox { + +// Top level dispatcher which hands requests to the appropriate service +// dispatchers. +class TopLevelDispatcher : public Dispatcher { + public: + // |policy| must outlive this class. + explicit TopLevelDispatcher(PolicyBase* policy); + ~TopLevelDispatcher() override; + + Dispatcher* OnMessageReady(IPCParams* ipc, + CallbackGeneric* callback) override; + bool SetupService(InterceptionManager* manager, int service) override; + + private: + // Test IPC provider. + bool Ping(IPCInfo* ipc, void* cookie); + + // Returns a dispatcher from ipc_targets_. + Dispatcher* GetDispatcher(int ipc_tag); + + PolicyBase* policy_; + scoped_ptr filesystem_dispatcher_; + scoped_ptr named_pipe_dispatcher_; + scoped_ptr thread_process_dispatcher_; + scoped_ptr sync_dispatcher_; + scoped_ptr registry_dispatcher_; + scoped_ptr handle_dispatcher_; + scoped_ptr process_mitigations_win32k_dispatcher_; + Dispatcher* ipc_targets_[IPC_LAST_TAG]; + + DISALLOW_COPY_AND_ASSIGN(TopLevelDispatcher); +}; + +} // namespace sandbox + +#endif // SANDBOX_SRC_TOP_LEVEL_DISPATCHER_H__ diff --git a/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc b/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc index 620016c6bd8b..7e398f686f19 100644 --- a/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc +++ b/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc @@ -40,8 +40,7 @@ SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t **argv) { return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; } -// Flaky on windows, see http://crbug.com/80569. -TEST(UnloadDllTest, DISABLED_BaselineAvicapDll) { +TEST(UnloadDllTest, BaselineAvicapDll) { TestRunner runner; runner.SetTestState(BEFORE_REVERT); runner.SetTimeout(2000); @@ -56,8 +55,7 @@ TEST(UnloadDllTest, DISABLED_BaselineAvicapDll) { EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll")); } -// Flaky on windows, see http://crbug.com/80569. -TEST(UnloadDllTest, DISABLED_UnloadAviCapDllNoPatching) { +TEST(UnloadDllTest, UnloadAviCapDllNoPatching) { TestRunner runner; runner.SetTestState(BEFORE_REVERT); runner.SetTimeout(2000); @@ -67,8 +65,7 @@ TEST(UnloadDllTest, DISABLED_UnloadAviCapDllNoPatching) { EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll")); } -// Flaky: http://crbug.com/38404 -TEST(UnloadDllTest, DISABLED_UnloadAviCapDllWithPatching) { +TEST(UnloadDllTest, UnloadAviCapDllWithPatching) { TestRunner runner; runner.SetTimeout(2000); runner.SetTestState(BEFORE_REVERT); diff --git a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc index 24cbacf2b467..35de38a0f136 100644 --- a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc +++ b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc @@ -4,10 +4,16 @@ #include "sandbox/win/src/win2k_threadpool.h" +#include + #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) { diff --git a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h index 0abb358e6909..c4d539dd7fee 100644 --- a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h +++ b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h @@ -5,8 +5,11 @@ #ifndef SANDBOX_SRC_WIN2K_THREADPOOL_H_ #define SANDBOX_SRC_WIN2K_THREADPOOL_H_ -#include +#include + #include +#include +#include "base/macros.h" #include "sandbox/win/src/crosscall_server.h" namespace sandbox { @@ -24,16 +27,15 @@ namespace sandbox { // This implementation simply thunks to the nice thread pool API of win2k. class Win2kThreadPool : public ThreadProvider { public: - Win2kThreadPool() { - ::InitializeCriticalSection(&lock_); - } - virtual ~Win2kThreadPool(); + Win2kThreadPool(); + ~Win2kThreadPool() override; - virtual bool RegisterWait(const void* cookie, HANDLE waitable_object, - CrossCallIPCCallback callback, - void* context); + bool RegisterWait(const void* cookie, + HANDLE waitable_object, + CrossCallIPCCallback callback, + void* context) override; - virtual bool UnRegisterWaits(void* cookie); + bool UnRegisterWaits(void* cookie) override; // Returns the total number of wait objects associated with // the thread pool. @@ -50,6 +52,7 @@ class Win2kThreadPool : public ThreadProvider { PoolObjects pool_objects_; // This lock protects the list of pool wait objects. CRITICAL_SECTION lock_; + DISALLOW_COPY_AND_ASSIGN(Win2kThreadPool); }; diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.cc b/security/sandbox/chromium/sandbox/win/src/win_utils.cc index 98870ac7e52d..3717a971c94f 100644 --- a/security/sandbox/chromium/sandbox/win/src/win_utils.cc +++ b/security/sandbox/chromium/sandbox/win/src/win_utils.cc @@ -4,8 +4,11 @@ #include "sandbox/win/src/win_utils.h" +#include + #include +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_util.h" #include "base/win/pe_image.h" @@ -156,13 +159,10 @@ bool ResolveRegistryName(base::string16 name, base::string16* resolved_name) { // \??\c:\some\foo\bar // \Device\HarddiskVolume0\some\foo\bar // \??\HarddiskVolume0\some\foo\bar -// \??\UNC\SERVER\Share\some\foo\bar -DWORD IsReparsePoint(const base::string16& full_path, bool* result) { +DWORD IsReparsePoint(const base::string16& full_path) { // Check if it's a pipe. We can't query the attributes of a pipe. - if (IsPipe(full_path)) { - *result = FALSE; - return ERROR_SUCCESS; - } + if (IsPipe(full_path)) + return ERROR_NOT_A_REPARSE_POINT; base::string16 path; bool nt_path = IsNTPath(full_path, &path); @@ -172,25 +172,18 @@ DWORD IsReparsePoint(const base::string16& full_path, bool* result) { if (!has_drive && !is_device_path && !nt_path) return ERROR_INVALID_NAME; + bool added_implied_device = false; if (!has_drive) { - // Add Win32 device namespace prefix, required for some Windows APIs. - path.insert(0, kNTDotPrefix); + path = base::string16(kNTDotPrefix) + path; + added_implied_device = true; } - // Ensure that volume path matches start of path. - wchar_t vol_path[MAX_PATH]; - if (!::GetVolumePathNameW(path.c_str(), vol_path, MAX_PATH)) { - return ERROR_INVALID_NAME; - } - size_t vol_path_len = wcslen(vol_path); - if (!EqualPath(path, vol_path, vol_path_len)) { - return ERROR_INVALID_NAME; - } - - // vol_path will include a trailing slash, so reduce size for loop check. - --vol_path_len; + 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(); @@ -198,20 +191,23 @@ DWORD IsReparsePoint(const base::string16& full_path, bool* result) { 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; } - path.resize(path.rfind(L'\\')); - } while (path.size() > vol_path_len); // Skip root dir. + passed_once = true; + last_pos = path.rfind(L'\\'); + } while (last_pos > 2); // Skip root dir. - *result = false; - return ERROR_SUCCESS; + return ERROR_NOT_A_REPARSE_POINT; } // We get a |full_path| of the forms accepted by IsReparsePoint(), and the name @@ -229,11 +225,11 @@ bool SameObject(HANDLE handle, const wchar_t* full_path) { DCHECK_NT(!path.empty()); // This may end with a backslash. - if (path.back() == L'\\') { - path.pop_back(); - } + const wchar_t kBackslash = '\\'; + if (path[path.length() - 1] == kBackslash) + path = path.substr(0, path.length() - 1); - // Perfect match (case-insensitive check). + // Perfect match (case-insesitive check). if (EqualPath(actual_path, path)) return true; @@ -242,78 +238,71 @@ bool SameObject(HANDLE handle, const wchar_t* full_path) { if (!has_drive && nt_path) { base::string16 simple_actual_path; - if (IsDevicePath(path, &path)) { - if (IsDevicePath(actual_path, &simple_actual_path)) { - // Perfect match (case-insensitive check). - return (EqualPath(simple_actual_path, path)); - } else { - return false; - } - } else { - // Add Win32 device namespace for GetVolumePathName. - path.insert(0, kNTDotPrefix); - } + if (!IsDevicePath(actual_path, &simple_actual_path)) + return false; + + // Perfect match (case-insesitive check). + return (EqualPath(simple_actual_path, path)); } - // Get the volume path in the same format as actual_path. - wchar_t vol_path[MAX_PATH]; - if (!::GetVolumePathName(path.c_str(), vol_path, MAX_PATH)) { + if (!has_drive) return false; - } - size_t vol_path_len = wcslen(vol_path); - base::string16 nt_vol; - if (!GetNtPathFromWin32Path(vol_path, &nt_vol)) { + + // 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(wcslen(vol_name)); // The two paths should be the same length. - if (nt_vol.size() + path.size() - vol_path_len != actual_path.size()) { + if (vol_length + path.size() - 2 != actual_path.size()) return false; - } - // Check the volume matches. - if (!EqualPath(actual_path, nt_vol.c_str(), nt_vol.size())) { + // Check up to the drive letter. + if (!EqualPath(actual_path, vol_name, vol_length)) return false; - } - // Check the path after the volume matches. - if (!EqualPath(actual_path, nt_vol.size(), path, vol_path_len)) { + // 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); +bool ConvertToLongPath(base::string16* path) { + if (IsPipe(*path)) return true; - } - base::string16 path; - if (IsDevicePath(short_path, &path)) + base::string16 temp_path; + if (IsDevicePath(*path, &temp_path)) return false; - bool is_nt_path = IsNTPath(path, &path); + bool is_nt_path = IsNTPath(temp_path, &temp_path); bool added_implied_device = false; - if (!StartsWithDriveLetter(path) && is_nt_path) { - path = base::string16(kNTDotPrefix) + path; + if (!StartsWithDriveLetter(temp_path) && is_nt_path) { + temp_path = base::string16(kNTDotPrefix) + temp_path; added_implied_device = true; } DWORD size = MAX_PATH; scoped_ptr long_path_buf(new wchar_t[size]); - DWORD return_value = ::GetLongPathName(path.c_str(), long_path_buf.get(), + DWORD return_value = ::GetLongPathName(temp_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); + return_value = ::GetLongPathName(temp_path.c_str(), long_path_buf.get(), + size); } DWORD last_error = ::GetLastError(); @@ -321,31 +310,31 @@ bool ConvertToLongPath(const base::string16& short_path, ERROR_PATH_NOT_FOUND == last_error || ERROR_INVALID_NAME == last_error)) { // The file does not exist, but maybe a sub path needs to be expanded. - base::string16::size_type last_slash = path.rfind(L'\\'); + base::string16::size_type last_slash = temp_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)) + base::string16 begin = temp_path.substr(0, last_slash); + base::string16 end = temp_path.substr(last_slash); + if (!ConvertToLongPath(&begin)) return false; // Ok, it worked. Let's reset the return value. - path = begin + end; + temp_path = begin + end; return_value = 1; } else if (0 != return_value) { - path = long_path_buf.get(); + temp_path = long_path_buf.get(); } if (return_value != 0) { if (added_implied_device) - RemoveImpliedDevice(&path); + RemoveImpliedDevice(&temp_path); if (is_nt_path) { - *long_path = kNTPrefix; - *long_path += path; + *path = kNTPrefix; + *path += temp_path; } else { - *long_path = path; + *path = temp_path; } return true; @@ -362,13 +351,14 @@ bool GetPathFromHandle(HANDLE handle, base::string16* path) { 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 name_ptr; + scoped_ptr name_ptr; if (size) { - name = reinterpret_cast(new BYTE[size]); - name_ptr.reset(name); + name_ptr.reset(new BYTE[size]); + name = reinterpret_cast(name_ptr.get()); // Query the name information a second time to get the name of the // object referenced by the handle. diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.h b/security/sandbox/chromium/sandbox/win/src/win_utils.h index 3e4565f46f4e..f7895c1b8b1d 100644 --- a/security/sandbox/chromium/sandbox/win/src/win_utils.h +++ b/security/sandbox/chromium/sandbox/win/src/win_utils.h @@ -6,9 +6,10 @@ #define SANDBOX_SRC_WIN_UTILS_H_ #include +#include #include -#include "base/basictypes.h" +#include "base/macros.h" #include "base/strings/string16.h" namespace sandbox { @@ -66,16 +67,15 @@ class SingletonBase { // Convert a short path (C:\path~1 or \\??\\c:\path~1) to the long version of // the path. If the path is not a valid filesystem path, the function returns -// false and the output parameter is not modified. -bool ConvertToLongPath(const base::string16& short_path, - base::string16* long_path); +// false and argument is not modified. +bool ConvertToLongPath(base::string16* 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. +// Returns ERROR_SUCCESS if the path contains a reparse point, +// ERROR_NOT_A_REPARSE_POINT if there's no reparse point in this path, or an +// 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); +DWORD IsReparsePoint(const base::string16& full_path); // Returns true if the handle corresponds to the object pointed by this path. bool SameObject(HANDLE handle, const wchar_t* full_path); diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc b/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc index 37366544d47a..fb72eb33859d 100644 --- a/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc +++ b/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc @@ -22,17 +22,16 @@ TEST(WinUtils, IsReparsePoint) { 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); + EXPECT_EQ(static_cast(ERROR_NOT_A_REPARSE_POINT), + IsReparsePoint(my_folder)); - // 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)); + EXPECT_EQ(static_cast(ERROR_NOT_A_REPARSE_POINT), + IsReparsePoint(not_found)); base::string16 new_file = base::string16(my_folder) + L"\\foo"; - EXPECT_EQ(ERROR_SUCCESS, IsReparsePoint(new_file, &result)); - EXPECT_FALSE(result); + EXPECT_EQ(static_cast(ERROR_NOT_A_REPARSE_POINT), + IsReparsePoint(new_file)); // Replace the directory with a reparse point to %temp%. HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS, @@ -43,8 +42,7 @@ TEST(WinUtils, IsReparsePoint) { 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_EQ(static_cast(ERROR_SUCCESS), IsReparsePoint(new_file)); EXPECT_TRUE(DeleteReparsePoint(dir)); EXPECT_TRUE(::CloseHandle(dir)); @@ -81,3 +79,33 @@ TEST(WinUtils, SameObject) { 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/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.cc b/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.cc index 033b9d771e15..0424d9fdddeb 100644 --- a/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.cc +++ b/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.cc @@ -4,6 +4,10 @@ #include "sandbox/win/wow_helper/service64_resolver.h" +#include +#include + +#include "base/bit_cast.h" #include "base/memory/scoped_ptr.h" #include "sandbox/win/wow_helper/target_code.h" diff --git a/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.h b/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.h index abd7efd813c6..32ee46f8e67d 100644 --- a/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.h +++ b/security/sandbox/chromium/sandbox/win/wow_helper/service64_resolver.h @@ -5,6 +5,9 @@ #ifndef SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ #define SANDBOX_WOW_HELPER_SERVICE64_RESOLVER_H__ +#include + +#include "base/macros.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/resolver.h" diff --git a/security/sandbox/chromium/sandbox/win/wow_helper/wow_helper.cc b/security/sandbox/chromium/sandbox/win/wow_helper/wow_helper.cc index ea73e8487c2e..af76cbc1358f 100644 --- a/security/sandbox/chromium/sandbox/win/wow_helper/wow_helper.cc +++ b/security/sandbox/chromium/sandbox/win/wow_helper/wow_helper.cc @@ -9,6 +9,7 @@ // setup the interceptions (32-bit) properly on the target. #include +#include #include @@ -61,7 +62,7 @@ int PatchNtdll(HANDLE child, void* thunk, size_t thunk_bytes) { // It should be noted that we don't wait until the real work is done; this // program quits as soon as the 64-bit interception is performed. int wWinMain(HINSTANCE, HINSTANCE, wchar_t* command_line, int) { - COMPILE_ASSERT(sizeof(void*) > sizeof(DWORD), unsupported_32_bits); + static_assert(sizeof(void*) > sizeof(DWORD), "unsupported 32 bits"); if (!command_line) return 1; diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp index cb9bc4b5a653..fbd468a9ed90 100644 --- a/security/sandbox/linux/Sandbox.cpp +++ b/security/sandbox/linux/Sandbox.cpp @@ -35,15 +35,18 @@ #include "mozilla/SandboxInfo.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.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/policy_compiler.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" +#include "sandbox/linux/bpf_dsl/seccomp_macros.h" #include "sandbox/linux/seccomp-bpf/trap.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" #if defined(ANDROID) -#include "sandbox/linux/services/android_ucontext.h" +#include "sandbox/linux/system_headers/linux_ucontext.h" #endif -#include "sandbox/linux/services/linux_syscalls.h" #ifdef MOZ_ASAN // Copy libsanitizer declarations to avoid depending on ASAN headers. @@ -463,9 +466,9 @@ SetCurrentProcessSandbox(UniquePtr aPolicy) // lifetime, but does not take ownership of them. sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(), sandbox::Trap::Registry()); - auto program = compiler.Compile(); + sandbox::CodeGen::Program program = compiler.Compile(); if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) { - sandbox::bpf_dsl::DumpBPF::PrintProgram(*program); + sandbox::bpf_dsl::DumpBPF::PrintProgram(program); } InstallSigSysHandler(); @@ -479,10 +482,10 @@ SetCurrentProcessSandbox(UniquePtr aPolicy) #endif // The syscall takes a C-style array, so copy the vector into one. - size_t programLen = program->size(); + size_t programLen = program.size(); UniquePtr flatProgram(new sock_filter[programLen]); - for (auto i = program->begin(); i != program->end(); ++i) { - flatProgram[i - program->begin()] = *i; + for (auto i = program.begin(); i != program.end(); ++i) { + flatProgram[i - program.begin()] = *i; } sock_fprog fprog; diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp index 5ebf1725fffb..b57c1bde9d8d 100644 --- a/security/sandbox/linux/SandboxFilter.cpp +++ b/security/sandbox/linux/SandboxFilter.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -26,8 +27,8 @@ #include #include "sandbox/linux/bpf_dsl/bpf_dsl.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/services/linux_syscalls.h" +#include "sandbox/linux/system_headers/linux_seccomp.h" +#include "sandbox/linux/system_headers/linux_syscalls.h" using namespace sandbox::bpf_dsl; #define CASES SANDBOX_BPF_DSL_CASES @@ -437,8 +438,9 @@ public: return Some(Allow()); } Arg domain(0), type(1); - return Some(If(domain == AF_UNIX && - (type == SOCK_STREAM || type == SOCK_SEQPACKET), Allow()) + return Some(If(AllOf(domain == AF_UNIX, + AnyOf(type == SOCK_STREAM, type == SOCK_SEQPACKET)), + Allow()) .Else(InvalidSyscall())); } diff --git a/security/sandbox/linux/SandboxFilterUtil.h b/security/sandbox/linux/SandboxFilterUtil.h index 35386619fa8a..fb9afa79f7f4 100644 --- a/security/sandbox/linux/SandboxFilterUtil.h +++ b/security/sandbox/linux/SandboxFilterUtil.h @@ -13,8 +13,8 @@ // between different Linux architectures. #include "mozilla/Maybe.h" -#include "sandbox/linux/services/linux_syscalls.h" #include "sandbox/linux/bpf_dsl/policy.h" +#include "sandbox/linux/system_headers/linux_syscalls.h" namespace mozilla { diff --git a/security/sandbox/linux/SandboxLogging.cpp b/security/sandbox/linux/SandboxLogging.cpp index b2f3102de9d3..19196a75ac8e 100644 --- a/security/sandbox/linux/SandboxLogging.cpp +++ b/security/sandbox/linux/SandboxLogging.cpp @@ -11,6 +11,7 @@ #endif #include #include +#include #include #include diff --git a/security/sandbox/linux/SandboxUtil.cpp b/security/sandbox/linux/SandboxUtil.cpp index 131057308db8..2357df3a1812 100644 --- a/security/sandbox/linux/SandboxUtil.cpp +++ b/security/sandbox/linux/SandboxUtil.cpp @@ -18,7 +18,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Unused.h" -#include "sandbox/linux/services/linux_syscalls.h" +#include "sandbox/linux/system_headers/linux_syscalls.h" namespace mozilla { diff --git a/security/sandbox/linux/common/SandboxInfo.cpp b/security/sandbox/linux/common/SandboxInfo.cpp index 7341b74cfacc..6f645c2b17bd 100644 --- a/security/sandbox/linux/common/SandboxInfo.cpp +++ b/security/sandbox/linux/common/SandboxInfo.cpp @@ -20,8 +20,8 @@ #include "mozilla/Assertions.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Telemetry.h" -#include "sandbox/linux/seccomp-bpf/linux_seccomp.h" -#include "sandbox/linux/services/linux_syscalls.h" +#include "sandbox/linux/system_headers/linux_seccomp.h" +#include "sandbox/linux/system_headers/linux_syscalls.h" #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" diff --git a/security/sandbox/linux/moz.build b/security/sandbox/linux/moz.build index 60bbb0e075b5..f7426da3d246 100644 --- a/security/sandbox/linux/moz.build +++ b/security/sandbox/linux/moz.build @@ -42,6 +42,7 @@ SOURCES += [ '../chromium/base/synchronization/lock_impl_posix.cc', '../chromium/base/synchronization/waitable_event_posix.cc', '../chromium/base/third_party/icu/icu_utf.cc', + '../chromium/base/threading/platform_thread_internal_posix.cc', '../chromium/base/threading/platform_thread_linux.cc', '../chromium/base/threading/platform_thread_posix.cc', '../chromium/base/threading/thread_collision_warner.cc', @@ -51,16 +52,15 @@ SOURCES += [ '../chromium/base/time/time.cc', '../chromium/base/time/time_posix.cc', '../chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc', + '../chromium/sandbox/linux/bpf_dsl/codegen.cc', '../chromium/sandbox/linux/bpf_dsl/dump_bpf.cc', '../chromium/sandbox/linux/bpf_dsl/policy.cc', '../chromium/sandbox/linux/bpf_dsl/policy_compiler.cc', - '../chromium/sandbox/linux/seccomp-bpf/basicblock.cc', - '../chromium/sandbox/linux/seccomp-bpf/codegen.cc', + '../chromium/sandbox/linux/bpf_dsl/syscall_set.cc', '../chromium/sandbox/linux/seccomp-bpf/die.cc', - '../chromium/sandbox/linux/seccomp-bpf/errorcode.cc', '../chromium/sandbox/linux/seccomp-bpf/syscall.cc', - '../chromium/sandbox/linux/seccomp-bpf/syscall_iterator.cc', '../chromium/sandbox/linux/seccomp-bpf/trap.cc', + '../chromium/sandbox/linux/services/syscall_wrappers.cc', 'broker/SandboxBrokerCommon.cpp', 'LinuxCapabilities.cpp', 'Sandbox.cpp', @@ -88,6 +88,9 @@ if CONFIG['CLANG_CXX']: if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wno-shadow'] + SOURCES['../chromium/sandbox/linux/services/syscall_wrappers.cc'].flags += [ + '-Wno-empty-body', + ] # gcc lto likes to put the top level asm in syscall.cc in a different partition # from the function using it which breaks the build. Work around that by diff --git a/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt b/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt index 7c2a6694f83d..b0c673adc2f6 100644 --- a/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt +++ b/security/sandbox/modifications-to-chromium-to-reapply-after-upstream-merge.txt @@ -3,9 +3,6 @@ Also, please update any existing links to their actual mozilla-central changeset https://hg.mozilla.org/mozilla-central/rev/efa7518e43b0 https://hg.mozilla.org/mozilla-central/rev/421446ab2347 -https://hg.mozilla.org/mozilla-central/rev/beb74056ac2d https://hg.mozilla.org/mozilla-central/rev/7df8d6639971 -https://hg.mozilla.org/mozilla-central/rev/1178c11561bc https://hg.mozilla.org/mozilla-central/rev/afa4f68de47c -https://bugzilla.mozilla.org/show_bug.cgi?id=1035125 bug1035125part7.patch https://bugzilla.mozilla.org/show_bug.cgi?id=1273852 diff --git a/security/sandbox/moz-chromium-commit-status.txt b/security/sandbox/moz-chromium-commit-status.txt index 14161eec7ad3..7f7b2dfd8cf2 100644 --- a/security/sandbox/moz-chromium-commit-status.txt +++ b/security/sandbox/moz-chromium-commit-status.txt @@ -1,13 +1,3 @@ Chromium Commit Directory / File (relative to security/sandbox/) ---------------------------------------- ------------------------------------------------ -df7cc6c04725630dd4460f29d858a77507343b24 chromium -b533d6533585377edd63ec6500469f6c4fba602a chromium/sandbox/win/src/sharedmem_ipc_server.cc -034bd64db1806d85b2ceacc736074ac07722af4a chromium/sandbox/win/src/service_resolver_64.cc -de2078cfbbb6770791d32575a1a72a288e6d66a6 chromium/sandbox/win/src/target_services.cc -de2078cfbbb6770791d32575a1a72a288e6d66a6 chromium/sandbox/win/src/target_services.h -0e49d029d5a1a25d971880b9e44d67ac70b31a80 chromium/sandbox/win/src/file_policy_test.cc -0e49d029d5a1a25d971880b9e44d67ac70b31a80 chromium/sandbox/win/src/filesystem_policy.cc -0e49d029d5a1a25d971880b9e44d67ac70b31a80 chromium/sandbox/win/src/win_utils.cc -0e49d029d5a1a25d971880b9e44d67ac70b31a80 chromium/sandbox/win/src/win_utils.h -3181ba39ee787e1b40f4aea4be23f4f666ad0945 chromium/base/win/windows_version.cc -3181ba39ee787e1b40f4aea4be23f4f666ad0945 chromium/base/win/windows_version.h +4ec79b7f2379a60cdc15599e93255c0fa417f1ed chromium diff --git a/security/sandbox/moz.build b/security/sandbox/moz.build index 2276572d8135..7120b573ed5b 100644 --- a/security/sandbox/moz.build +++ b/security/sandbox/moz.build @@ -32,6 +32,7 @@ elif CONFIG['OS_ARCH'] == 'WINNT': ] SOURCES += [ + 'chromium-shim/base/files/file_path.cpp', 'chromium-shim/base/logging.cpp', 'chromium-shim/sandbox/win/sandboxLogging.cpp', 'chromium/base/at_exit.cc', @@ -85,7 +86,6 @@ elif CONFIG['OS_ARCH'] == 'WINNT': 'chromium/sandbox/win/src/handle_dispatcher.cc', 'chromium/sandbox/win/src/handle_interception.cc', 'chromium/sandbox/win/src/handle_policy.cc', - 'chromium/sandbox/win/src/handle_table.cc', 'chromium/sandbox/win/src/interception.cc', 'chromium/sandbox/win/src/interception_agent.cc', 'chromium/sandbox/win/src/job.cc', @@ -114,9 +114,9 @@ elif CONFIG['OS_ARCH'] == 'WINNT': 'chromium/sandbox/win/src/sandbox_globals.cc', 'chromium/sandbox/win/src/sandbox_nt_util.cc', 'chromium/sandbox/win/src/sandbox_policy_base.cc', + 'chromium/sandbox/win/src/sandbox_rand.cc', 'chromium/sandbox/win/src/sandbox_utils.cc', 'chromium/sandbox/win/src/service_resolver.cc', - 'chromium/sandbox/win/src/shared_handles.cc', 'chromium/sandbox/win/src/sharedmem_ipc_client.cc', 'chromium/sandbox/win/src/sharedmem_ipc_server.cc', 'chromium/sandbox/win/src/sid.cc', @@ -126,6 +126,7 @@ elif CONFIG['OS_ARCH'] == 'WINNT': 'chromium/sandbox/win/src/target_interceptions.cc', 'chromium/sandbox/win/src/target_process.cc', 'chromium/sandbox/win/src/target_services.cc', + 'chromium/sandbox/win/src/top_level_dispatcher.cc', 'chromium/sandbox/win/src/win2k_threadpool.cc', 'chromium/sandbox/win/src/win_utils.cc', 'chromium/sandbox/win/src/window.cc',