summaryrefslogtreecommitdiffstats
path: root/security/sandbox
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /security/sandbox
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/sandbox')
-rw-r--r--security/sandbox/chromium-shim/base/allocator/buildflags.h20
-rw-r--r--security/sandbox/chromium-shim/base/allocator/partition_allocator/page_allocator.h21
-rw-r--r--security/sandbox/chromium-shim/base/debug/activity_tracker.h61
-rw-r--r--security/sandbox/chromium-shim/base/debug/crash_logging.cpp22
-rw-r--r--security/sandbox/chromium-shim/base/debug/debugging_buildflags.h21
-rw-r--r--security/sandbox/chromium-shim/base/debug/stack_trace.h30
-rw-r--r--security/sandbox/chromium-shim/base/feature_list.h52
-rw-r--r--security/sandbox/chromium-shim/base/file_version_info_win.cpp90
-rw-r--r--security/sandbox/chromium-shim/base/file_version_info_win.h54
-rw-r--r--security/sandbox/chromium-shim/base/files/file_path.cpp217
-rw-r--r--security/sandbox/chromium-shim/base/files/file_util.h11
-rw-r--r--security/sandbox/chromium-shim/base/gtest_prod_util.h22
-rw-r--r--security/sandbox/chromium-shim/base/logging.cpp160
-rw-r--r--security/sandbox/chromium-shim/base/logging_buildflags.h20
-rw-r--r--security/sandbox/chromium-shim/base/memory/shared_memory_tracker.h40
-rw-r--r--security/sandbox/chromium-shim/base/metrics/histogram_functions.h20
-rw-r--r--security/sandbox/chromium-shim/base/metrics/histogram_macros.h16
-rw-r--r--security/sandbox/chromium-shim/base/observer_list.h12
-rw-r--r--security/sandbox/chromium-shim/base/process/launch.h25
-rw-r--r--security/sandbox/chromium-shim/base/process/memory_win.cpp17
-rw-r--r--security/sandbox/chromium-shim/base/scoped_native_library.h31
-rw-r--r--security/sandbox/chromium-shim/base/synchronization/synchronization_buildflags.h17
-rw-r--r--security/sandbox/chromium-shim/base/third_party/nspr/prtime.h8
-rw-r--r--security/sandbox/chromium-shim/base/third_party/nspr/prtypes.h8
-rw-r--r--security/sandbox/chromium-shim/base/threading/platform_thread_linux.cpp69
-rw-r--r--security/sandbox/chromium-shim/base/threading/scoped_blocking_call.h47
-rw-r--r--security/sandbox/chromium-shim/base/trace_event/heap_profiler_allocation_context_tracker.h32
-rw-r--r--security/sandbox/chromium-shim/base/tracked_objects.h23
-rw-r--r--security/sandbox/chromium-shim/base/win/base_win_buildflags.h17
-rw-r--r--security/sandbox/chromium-shim/base/win/registry.h48
-rw-r--r--security/sandbox/chromium-shim/base/win/sdkdecls.h368
-rw-r--r--security/sandbox/chromium-shim/base/win/win_util.cpp42
-rw-r--r--security/sandbox/chromium-shim/base/win/win_util.h26
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/add_WOW64_flags_to_allowed_registry_read_flags.patch34
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/add_interception_logging.patch810
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/allow_ntpath_in_SignedPolicy_GenerateRules.patch82
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/allow_rules_for_network_drive_and_non_file_devices.patch190
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/arm64_set_LoaderThreads.patch99
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/change_to_DCHECK_in_CloseHandleWrapper.patch38
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/linux_32bit_arg_fixup.patch84
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/move_shared_memory_duplication_after_initialization.patch94
-rw-r--r--security/sandbox/chromium-shim/patches/after_update/patch_order.txt8
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/aarch64_control_flow_guard.patch65
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/add_CET_STRICT_MODE.patch94
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/add_option_to_not_use_restricting_sids.patch281
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/add_return_in_QueryCancellationTraitsForNonCancellables_to_satisfy_build.patch29
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/add_support_for_random_restricted_SID.patch461
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/allow_env_changes.patch217
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/allow_read_only_all_paths_rule.patch142
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/allow_reparse_points.patch186
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/broker_complex_line_breaks.patch502
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/derive_sid_from_name.patch74
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/ifdef_out_AppContainerProfileBase_testing_functions.patch79
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/ifdef_out_FromStringInternal.patch52
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/ifdef_out_SequenceChecker_code.patch36
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/include_atomic_header_in_platform_thread.patch27
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/lower_SDK_version_requirement.patch34
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_capitalization.patch74
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_cast_getprocaddress.patch34
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_copy_s.patch34
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_disable_one_try.patch51
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_missing_windows_types_defines.patch37
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_offsetof.patch182
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/mingw_operator_new.patch58
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/more_chromium_linux_x86_x64_syscalls.patch91
-rwxr-xr-xsecurity/sandbox/chromium-shim/patches/with_update/patch_order.txt32
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/remove_extraneous_backslash_introduced_by_clang_tidy.patch34
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/remove_include_delayimp_h_from_pe_image_cc.patch32
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/remove_unused_functions_from_StrtodTrimmed.patch48
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/replace_ScopedNativeLibrary_in_ApplyMitigationsToCurrentThread.patch59
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/revert_TargetNtSetInformationThread_change.patch39
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/revert_Token_serialization_and_deserialization.patch100
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/revert_removal_of_app_dir_for_DLL_load.patch74
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/revert_remove_AddTargetPeer.patch310
-rw-r--r--security/sandbox/chromium-shim/patches/with_update/revert_remove_BrokerDuplicateHandle.patch743
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h101
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/loggingTypes.h27
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp89
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h50
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_common.h31
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.cc58
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.h38
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.cc108
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.h19
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.cc66
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.h35
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/sandbox_policy_diagnostic.h31
-rw-r--r--security/sandbox/chromium-shim/sandbox/win/src/sidestep_resolver.h58
-rw-r--r--security/sandbox/chromium/LICENSE27
-rw-r--r--security/sandbox/chromium/base/at_exit.cc114
-rw-r--r--security/sandbox/chromium/base/at_exit.h87
-rw-r--r--security/sandbox/chromium/base/atomic_ref_count.h69
-rw-r--r--security/sandbox/chromium/base/atomic_sequence_num.h33
-rw-r--r--security/sandbox/chromium/base/atomicops.h150
-rw-r--r--security/sandbox/chromium/base/atomicops_internals_portable.h219
-rw-r--r--security/sandbox/chromium/base/atomicops_internals_x86_msvc.h179
-rw-r--r--security/sandbox/chromium/base/base_export.h29
-rw-r--r--security/sandbox/chromium/base/base_paths.h55
-rw-r--r--security/sandbox/chromium/base/base_paths_win.h53
-rw-r--r--security/sandbox/chromium/base/base_switches.cc149
-rw-r--r--security/sandbox/chromium/base/base_switches.h60
-rw-r--r--security/sandbox/chromium/base/bind.h470
-rw-r--r--security/sandbox/chromium/base/bind_helpers.h69
-rw-r--r--security/sandbox/chromium/base/bind_internal.h1050
-rw-r--r--security/sandbox/chromium/base/bit_cast.h77
-rw-r--r--security/sandbox/chromium/base/bits.h209
-rw-r--r--security/sandbox/chromium/base/callback.h149
-rw-r--r--security/sandbox/chromium/base/callback_forward.h28
-rw-r--r--security/sandbox/chromium/base/callback_internal.cc101
-rw-r--r--security/sandbox/chromium/base/callback_internal.h194
-rw-r--r--security/sandbox/chromium/base/compiler_specific.h298
-rw-r--r--security/sandbox/chromium/base/containers/adapters.h55
-rw-r--r--security/sandbox/chromium/base/containers/buffer_iterator.h145
-rw-r--r--security/sandbox/chromium/base/containers/checked_iterators.h205
-rw-r--r--security/sandbox/chromium/base/containers/circular_deque.h1112
-rw-r--r--security/sandbox/chromium/base/containers/span.h530
-rw-r--r--security/sandbox/chromium/base/containers/stack.h23
-rw-r--r--security/sandbox/chromium/base/containers/util.h21
-rw-r--r--security/sandbox/chromium/base/containers/vector_buffer.h188
-rw-r--r--security/sandbox/chromium/base/cpu.cc312
-rw-r--r--security/sandbox/chromium/base/cpu.h104
-rw-r--r--security/sandbox/chromium/base/debug/alias.cc16
-rw-r--r--security/sandbox/chromium/base/debug/alias.h44
-rw-r--r--security/sandbox/chromium/base/debug/crash_logging.h104
-rw-r--r--security/sandbox/chromium/base/debug/debugger.h50
-rw-r--r--security/sandbox/chromium/base/debug/leak_annotations.h46
-rw-r--r--security/sandbox/chromium/base/debug/profiler.cc180
-rw-r--r--security/sandbox/chromium/base/debug/profiler.h76
-rw-r--r--security/sandbox/chromium/base/environment.cc123
-rw-r--r--security/sandbox/chromium/base/environment.h61
-rw-r--r--security/sandbox/chromium/base/file_descriptor_posix.h61
-rw-r--r--security/sandbox/chromium/base/files/file_path.h484
-rw-r--r--security/sandbox/chromium/base/files/file_path_constants.cc25
-rw-r--r--security/sandbox/chromium/base/format_macros.h97
-rw-r--r--security/sandbox/chromium/base/guid.h46
-rw-r--r--security/sandbox/chromium/base/hash/hash.cc167
-rw-r--r--security/sandbox/chromium/base/hash/hash.h86
-rw-r--r--security/sandbox/chromium/base/immediate_crash.h168
-rw-r--r--security/sandbox/chromium/base/lazy_instance.h210
-rw-r--r--security/sandbox/chromium/base/lazy_instance_helpers.cc64
-rw-r--r--security/sandbox/chromium/base/lazy_instance_helpers.h101
-rw-r--r--security/sandbox/chromium/base/location.cc96
-rw-r--r--security/sandbox/chromium/base/location.h142
-rw-r--r--security/sandbox/chromium/base/logging.h1077
-rw-r--r--security/sandbox/chromium/base/macros.h48
-rw-r--r--security/sandbox/chromium/base/memory/aligned_memory.h60
-rw-r--r--security/sandbox/chromium/base/memory/free_deleter.h25
-rw-r--r--security/sandbox/chromium/base/memory/platform_shared_memory_region.cc62
-rw-r--r--security/sandbox/chromium/base/memory/platform_shared_memory_region.h301
-rw-r--r--security/sandbox/chromium/base/memory/platform_shared_memory_region_win.cc343
-rw-r--r--security/sandbox/chromium/base/memory/ptr_util.h23
-rw-r--r--security/sandbox/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h52
-rw-r--r--security/sandbox/chromium/base/memory/ref_counted.cc105
-rw-r--r--security/sandbox/chromium/base/memory/ref_counted.h463
-rw-r--r--security/sandbox/chromium/base/memory/scoped_refptr.h375
-rw-r--r--security/sandbox/chromium/base/memory/shared_memory_mapping.cc115
-rw-r--r--security/sandbox/chromium/base/memory/shared_memory_mapping.h252
-rw-r--r--security/sandbox/chromium/base/memory/singleton.h279
-rw-r--r--security/sandbox/chromium/base/memory/unsafe_shared_memory_region.cc80
-rw-r--r--security/sandbox/chromium/base/memory/unsafe_shared_memory_region.h127
-rw-r--r--security/sandbox/chromium/base/memory/weak_ptr.h395
-rw-r--r--security/sandbox/chromium/base/no_destructor.h98
-rw-r--r--security/sandbox/chromium/base/numerics/checked_math.h393
-rw-r--r--security/sandbox/chromium/base/numerics/checked_math_impl.h567
-rw-r--r--security/sandbox/chromium/base/numerics/clamped_math.h264
-rw-r--r--security/sandbox/chromium/base/numerics/clamped_math_impl.h341
-rw-r--r--security/sandbox/chromium/base/numerics/safe_conversions.h358
-rw-r--r--security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h51
-rw-r--r--security/sandbox/chromium/base/numerics/safe_conversions_impl.h851
-rw-r--r--security/sandbox/chromium/base/numerics/safe_math.h12
-rw-r--r--security/sandbox/chromium/base/numerics/safe_math_arm_impl.h122
-rw-r--r--security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h157
-rw-r--r--security/sandbox/chromium/base/numerics/safe_math_shared_impl.h240
-rw-r--r--security/sandbox/chromium/base/optional.h937
-rw-r--r--security/sandbox/chromium/base/os_compat_android.h21
-rw-r--r--security/sandbox/chromium/base/path_service.h94
-rw-r--r--security/sandbox/chromium/base/posix/can_lower_nice_to.cc60
-rw-r--r--security/sandbox/chromium/base/posix/can_lower_nice_to.h19
-rw-r--r--security/sandbox/chromium/base/posix/eintr_wrapper.h68
-rw-r--r--security/sandbox/chromium/base/posix/safe_strerror.cc128
-rw-r--r--security/sandbox/chromium/base/posix/safe_strerror.h44
-rw-r--r--security/sandbox/chromium/base/process/environment_internal.cc128
-rw-r--r--security/sandbox/chromium/base/process/environment_internal.h52
-rw-r--r--security/sandbox/chromium/base/process/kill.h162
-rw-r--r--security/sandbox/chromium/base/process/memory.h89
-rw-r--r--security/sandbox/chromium/base/process/process.h223
-rw-r--r--security/sandbox/chromium/base/process/process_handle.h142
-rw-r--r--security/sandbox/chromium/base/process/process_handle_win.cc52
-rw-r--r--security/sandbox/chromium/base/rand_util.h78
-rw-r--r--security/sandbox/chromium/base/rand_util_win.cc38
-rw-r--r--security/sandbox/chromium/base/scoped_clear_last_error.h58
-rw-r--r--security/sandbox/chromium/base/scoped_clear_last_error_win.cc22
-rw-r--r--security/sandbox/chromium/base/sequence_checker.h143
-rw-r--r--security/sandbox/chromium/base/sequence_checker_impl.h63
-rw-r--r--security/sandbox/chromium/base/sequence_token.h115
-rw-r--r--security/sandbox/chromium/base/sequenced_task_runner.h201
-rw-r--r--security/sandbox/chromium/base/sequenced_task_runner_helpers.h42
-rw-r--r--security/sandbox/chromium/base/single_thread_task_runner.h36
-rw-r--r--security/sandbox/chromium/base/stl_util.h681
-rw-r--r--security/sandbox/chromium/base/strings/char_traits.h92
-rw-r--r--security/sandbox/chromium/base/strings/nullable_string16.cc33
-rw-r--r--security/sandbox/chromium/base/strings/nullable_string16.h55
-rw-r--r--security/sandbox/chromium/base/strings/safe_sprintf.cc682
-rw-r--r--security/sandbox/chromium/base/strings/safe_sprintf.h246
-rw-r--r--security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc765
-rw-r--r--security/sandbox/chromium/base/strings/string16.cc87
-rw-r--r--security/sandbox/chromium/base/strings/string16.h229
-rw-r--r--security/sandbox/chromium/base/strings/string_number_conversions.cc545
-rw-r--r--security/sandbox/chromium/base/strings/string_number_conversions.h157
-rw-r--r--security/sandbox/chromium/base/strings/string_piece.cc426
-rw-r--r--security/sandbox/chromium/base/strings/string_piece.h513
-rw-r--r--security/sandbox/chromium/base/strings/string_piece_forward.h24
-rw-r--r--security/sandbox/chromium/base/strings/string_split.cc254
-rw-r--r--security/sandbox/chromium/base/strings/string_split.h169
-rw-r--r--security/sandbox/chromium/base/strings/string_util.cc1157
-rw-r--r--security/sandbox/chromium/base/strings/string_util.h568
-rw-r--r--security/sandbox/chromium/base/strings/string_util_constants.cc54
-rw-r--r--security/sandbox/chromium/base/strings/string_util_posix.h37
-rw-r--r--security/sandbox/chromium/base/strings/string_util_win.h44
-rw-r--r--security/sandbox/chromium/base/strings/stringprintf.cc225
-rw-r--r--security/sandbox/chromium/base/strings/stringprintf.h74
-rw-r--r--security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc155
-rw-r--r--security/sandbox/chromium/base/strings/utf_string_conversion_utils.h103
-rw-r--r--security/sandbox/chromium/base/strings/utf_string_conversions.cc342
-rw-r--r--security/sandbox/chromium/base/strings/utf_string_conversions.h54
-rw-r--r--security/sandbox/chromium/base/synchronization/atomic_flag.h50
-rw-r--r--security/sandbox/chromium/base/synchronization/condition_variable.h135
-rw-r--r--security/sandbox/chromium/base/synchronization/condition_variable_posix.cc149
-rw-r--r--security/sandbox/chromium/base/synchronization/lock.cc38
-rw-r--r--security/sandbox/chromium/base/synchronization/lock.h133
-rw-r--r--security/sandbox/chromium/base/synchronization/lock_impl.h175
-rw-r--r--security/sandbox/chromium/base/synchronization/lock_impl_posix.cc133
-rw-r--r--security/sandbox/chromium/base/synchronization/lock_impl_win.cc40
-rw-r--r--security/sandbox/chromium/base/synchronization/waitable_event.h291
-rw-r--r--security/sandbox/chromium/base/synchronization/waitable_event_posix.cc445
-rw-r--r--security/sandbox/chromium/base/task_runner.h136
-rw-r--r--security/sandbox/chromium/base/template_util.h188
-rw-r--r--security/sandbox/chromium/base/third_party/cityhash/COPYING19
-rw-r--r--security/sandbox/chromium/base/third_party/cityhash/city.cc532
-rw-r--r--security/sandbox/chromium/base/third_party/cityhash/city.h129
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/LICENSE26
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.cc641
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.h84
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.cc796
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.h152
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.cc175
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.h64
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/diy-fp.h137
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-conversion.h34
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.cc428
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.h396
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.cc665
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.h88
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.cc405
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.h56
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/ieee.h402
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.cc764
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.h226
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc588
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.h50
-rw-r--r--security/sandbox/chromium/base/third_party/double_conversion/double-conversion/utils.h364
-rw-r--r--security/sandbox/chromium/base/third_party/dynamic_annotations/LICENSE28
-rw-r--r--security/sandbox/chromium/base/third_party/dynamic_annotations/dynamic_annotations.h595
-rw-r--r--security/sandbox/chromium/base/third_party/icu/LICENSE76
-rw-r--r--security/sandbox/chromium/base/third_party/icu/icu_utf.cc131
-rw-r--r--security/sandbox/chromium/base/third_party/icu/icu_utf.h442
-rw-r--r--security/sandbox/chromium/base/third_party/superfasthash/LICENSE27
-rw-r--r--security/sandbox/chromium/base/third_party/superfasthash/README.chromium29
-rw-r--r--security/sandbox/chromium/base/third_party/superfasthash/superfasthash.c84
-rw-r--r--security/sandbox/chromium/base/third_party/valgrind/LICENSE39
-rw-r--r--security/sandbox/chromium/base/third_party/valgrind/valgrind.h4792
-rw-r--r--security/sandbox/chromium/base/thread_annotations.h264
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread.cc51
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread.h259
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc39
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread_internal_posix.h62
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread_posix.cc361
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread_win.cc463
-rw-r--r--security/sandbox/chromium/base/threading/platform_thread_win.h23
-rw-r--r--security/sandbox/chromium/base/threading/thread_checker_impl.h74
-rw-r--r--security/sandbox/chromium/base/threading/thread_collision_warner.cc64
-rw-r--r--security/sandbox/chromium/base/threading/thread_collision_warner.h252
-rw-r--r--security/sandbox/chromium/base/threading/thread_id_name_manager.cc147
-rw-r--r--security/sandbox/chromium/base/threading/thread_id_name_manager.h94
-rw-r--r--security/sandbox/chromium/base/threading/thread_local.h136
-rw-r--r--security/sandbox/chromium/base/threading/thread_local_internal.h80
-rw-r--r--security/sandbox/chromium/base/threading/thread_local_storage.cc461
-rw-r--r--security/sandbox/chromium/base/threading/thread_local_storage.h175
-rw-r--r--security/sandbox/chromium/base/threading/thread_local_storage_posix.cc30
-rw-r--r--security/sandbox/chromium/base/threading/thread_local_storage_win.cc107
-rw-r--r--security/sandbox/chromium/base/threading/thread_restrictions.cc258
-rw-r--r--security/sandbox/chromium/base/threading/thread_restrictions.h680
-rw-r--r--security/sandbox/chromium/base/time/time.cc433
-rw-r--r--security/sandbox/chromium/base/time/time.h1077
-rw-r--r--security/sandbox/chromium/base/time/time_exploded_posix.cc287
-rw-r--r--security/sandbox/chromium/base/time/time_now_posix.cc122
-rw-r--r--security/sandbox/chromium/base/time/time_override.h74
-rw-r--r--security/sandbox/chromium/base/time/time_win.cc810
-rw-r--r--security/sandbox/chromium/base/time/time_win_features.cc14
-rw-r--r--security/sandbox/chromium/base/time/time_win_features.h20
-rw-r--r--security/sandbox/chromium/base/token.cc28
-rw-r--r--security/sandbox/chromium/base/token.h72
-rw-r--r--security/sandbox/chromium/base/tuple.h112
-rw-r--r--security/sandbox/chromium/base/unguessable_token.cc39
-rw-r--r--security/sandbox/chromium/base/unguessable_token.h120
-rw-r--r--security/sandbox/chromium/base/version.cc194
-rw-r--r--security/sandbox/chromium/base/version.h77
-rw-r--r--security/sandbox/chromium/base/win/current_module.h17
-rw-r--r--security/sandbox/chromium/base/win/pe_image.cc652
-rw-r--r--security/sandbox/chromium/base/win/pe_image.h308
-rw-r--r--security/sandbox/chromium/base/win/scoped_handle.cc44
-rw-r--r--security/sandbox/chromium/base/win/scoped_handle.h184
-rw-r--r--security/sandbox/chromium/base/win/scoped_handle_verifier.cc238
-rw-r--r--security/sandbox/chromium/base/win/scoped_handle_verifier.h88
-rw-r--r--security/sandbox/chromium/base/win/scoped_process_information.cc107
-rw-r--r--security/sandbox/chromium/base/win/scoped_process_information.h75
-rw-r--r--security/sandbox/chromium/base/win/startup_information.cc59
-rw-r--r--security/sandbox/chromium/base/win/startup_information.h53
-rw-r--r--security/sandbox/chromium/base/win/static_constants.cc13
-rw-r--r--security/sandbox/chromium/base/win/static_constants.h21
-rw-r--r--security/sandbox/chromium/base/win/windows_types.h278
-rw-r--r--security/sandbox/chromium/base/win/windows_version.cc313
-rw-r--r--security/sandbox/chromium/base/win/windows_version.h187
-rw-r--r--security/sandbox/chromium/build/build_config.h205
-rw-r--r--security/sandbox/chromium/build/buildflag.h47
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc343
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h338
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_forward.h37
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_impl.h67
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.cc147
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.h119
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/cons.h137
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc159
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h29
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/errorcode.h37
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h63
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.cc19
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.h37
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc481
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h155
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h354
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc150
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h103
-rw-r--r--security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h73
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h56
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h124
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc155
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc93
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.h68
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc259
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h113
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc66
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h62
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc481
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h166
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc249
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc394
-rw-r--r--security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h86
-rw-r--r--security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc264
-rw-r--r--security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.h89
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h1197
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h1623
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_ucontext.h60
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/capability.h42
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h85
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/linux_filter.h140
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h84
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/linux_seccomp.h110
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/linux_signal.h150
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/linux_syscalls.h39
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/linux_ucontext.h22
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h1731
-rw-r--r--security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h1418
-rw-r--r--security/sandbox/chromium/sandbox/sandbox_export.h26
-rw-r--r--security/sandbox/chromium/sandbox/win/src/acl.cc171
-rw-r--r--security/sandbox/chromium/sandbox/win/src/acl.h64
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_profile.h74
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc337
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h94
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_test.cc342
-rw-r--r--security/sandbox/chromium/sandbox/win/src/broker_services.cc745
-rw-r--r--security/sandbox/chromium/sandbox/win/src/broker_services.h105
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_client.h509
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_params.h315
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_server.cc345
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_server.h261
-rw-r--r--security/sandbox/chromium/sandbox/win/src/eat_resolver.cc88
-rw-r--r--security/sandbox/chromium/sandbox/win/src/eat_resolver.h49
-rw-r--r--security/sandbox/chromium/sandbox/win/src/file_policy_test.cc705
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc302
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h76
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc412
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_interception.h67
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc443
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_policy.h112
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer.cc185
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer.h76
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc239
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc297
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc93
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h41
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc49
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_interception.cc48
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_interception.h24
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_policy.cc93
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_policy.h39
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc114
-rw-r--r--security/sandbox/chromium/sandbox/win/src/heap_helper.cc124
-rw-r--r--security/sandbox/chromium/sandbox/win/src/heap_helper.h26
-rw-r--r--security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc118
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception.cc512
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception.h290
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_agent.cc234
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_agent.h87
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_internal.h77
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_unittest.cc263
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interceptors.h73
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interceptors_64.cc531
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interceptors_64.h330
-rw-r--r--security/sandbox/chromium/sandbox/win/src/internal_types.h68
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_args.cc96
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_args.h24
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc58
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_tags.h59
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc632
-rw-r--r--security/sandbox/chromium/sandbox/win/src/job.cc117
-rw-r--r--security/sandbox/chromium/sandbox/win/src/job.h66
-rw-r--r--security/sandbox/chromium/sandbox/win/src/job_unittest.cc197
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc95
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc80
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h41
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc89
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h43
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc121
-rw-r--r--security/sandbox/chromium/sandbox/win/src/nt_internals.h983
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_broker.cc123
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_broker.h27
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc450
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h379
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_params.h190
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc103
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h143
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc103
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_low_level.cc355
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_low_level.h189
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc684
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc364
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_params.h70
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target.cc138
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target_test.cc486
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations.cc622
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations.h56
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc592
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h89
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc523
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h151
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc410
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h91
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_policy_test.cc548
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc275
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h69
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc520
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_interception.h101
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc269
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_policy.h91
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc167
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h51
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_interception.cc261
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_interception.h38
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_policy.cc230
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_policy.h56
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc322
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver.cc63
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver.h107
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver_32.cc95
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver_64.cc95
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token.cc432
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token.h207
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc829
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc480
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h105
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox.cc47
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox.h228
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox.vcproj648
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_factory.h52
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc18
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h47
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc755
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h220
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_policy.h296
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc832
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h198
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc22
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_rand.h15
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_types.h199
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc32
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_utils.h24
-rw-r--r--security/sandbox/chromium/sandbox/win/src/security_capabilities.cc33
-rw-r--r--security/sandbox/chromium/sandbox/win/src/security_capabilities.h34
-rw-r--r--security/sandbox/chromium/sandbox/win/src/security_level.h300
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver.cc47
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver.h158
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc476
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc290
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc278
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc193
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h140
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc346
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h137
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sid.cc163
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sid.h74
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sid_unittest.cc182
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc68
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h37
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_interception.cc97
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_interception.h30
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_policy.cc102
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_policy.h39
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc82
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h44
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_interception.cc177
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_interception.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy.cc243
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy.h49
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc145
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy_test.h18
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_interceptions.cc136
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_interceptions.h43
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_process.cc393
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_process.h143
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_services.cc264
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_services.h73
-rw-r--r--security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc97
-rw-r--r--security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc178
-rw-r--r--security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h54
-rw-r--r--security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc100
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc67
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h61
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win_utils.cc619
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win_utils.h156
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc258
-rw-r--r--security/sandbox/chromium/sandbox/win/src/window.cc147
-rw-r--r--security/sandbox/chromium/sandbox/win/src/window.h37
-rw-r--r--security/sandbox/common/SandboxSettings.cpp229
-rw-r--r--security/sandbox/common/SandboxSettings.h47
-rw-r--r--security/sandbox/common/components.conf23
-rw-r--r--security/sandbox/common/moz.build54
-rw-r--r--security/sandbox/common/mozISandboxSettings.idl32
-rw-r--r--security/sandbox/common/test/PSandboxTesting.ipdl20
-rw-r--r--security/sandbox/common/test/SandboxTest.cpp362
-rw-r--r--security/sandbox/common/test/SandboxTest.h45
-rw-r--r--security/sandbox/common/test/SandboxTestingChild.cpp194
-rw-r--r--security/sandbox/common/test/SandboxTestingChild.h86
-rw-r--r--security/sandbox/common/test/SandboxTestingChildTests.h876
-rw-r--r--security/sandbox/common/test/SandboxTestingParent.cpp125
-rw-r--r--security/sandbox/common/test/SandboxTestingParent.h53
-rw-r--r--security/sandbox/common/test/SandboxTestingThread.h53
-rw-r--r--security/sandbox/common/test/mozISandboxTest.idl28
-rw-r--r--security/sandbox/linux/LinuxSched.h35
-rw-r--r--security/sandbox/linux/Sandbox.cpp771
-rw-r--r--security/sandbox/linux/Sandbox.h76
-rw-r--r--security/sandbox/linux/SandboxBrokerClient.cpp274
-rw-r--r--security/sandbox/linux/SandboxBrokerClient.h57
-rw-r--r--security/sandbox/linux/SandboxChrootProto.h24
-rw-r--r--security/sandbox/linux/SandboxFilter.cpp2101
-rw-r--r--security/sandbox/linux/SandboxFilter.h46
-rw-r--r--security/sandbox/linux/SandboxFilterUtil.cpp142
-rw-r--r--security/sandbox/linux/SandboxFilterUtil.h248
-rw-r--r--security/sandbox/linux/SandboxHooks.cpp110
-rw-r--r--security/sandbox/linux/SandboxInfo.cpp201
-rw-r--r--security/sandbox/linux/SandboxInfo.h70
-rw-r--r--security/sandbox/linux/SandboxInternal.h28
-rw-r--r--security/sandbox/linux/SandboxLogging.cpp141
-rw-r--r--security/sandbox/linux/SandboxLogging.h81
-rw-r--r--security/sandbox/linux/SandboxOpenedFiles.cpp77
-rw-r--r--security/sandbox/linux/SandboxOpenedFiles.h97
-rw-r--r--security/sandbox/linux/SandboxReporterClient.cpp88
-rw-r--r--security/sandbox/linux/SandboxReporterClient.h47
-rw-r--r--security/sandbox/linux/broker/SandboxBroker.cpp1097
-rw-r--r--security/sandbox/linux/broker/SandboxBroker.h180
-rw-r--r--security/sandbox/linux/broker/SandboxBrokerCommon.cpp155
-rw-r--r--security/sandbox/linux/broker/SandboxBrokerCommon.h77
-rw-r--r--security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp1017
-rw-r--r--security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h36
-rw-r--r--security/sandbox/linux/broker/SandboxBrokerRealpath.cpp277
-rw-r--r--security/sandbox/linux/broker/SandboxBrokerUtils.h32
-rw-r--r--security/sandbox/linux/broker/moz.build37
-rw-r--r--security/sandbox/linux/glue/SandboxCrash.cpp118
-rw-r--r--security/sandbox/linux/glue/SandboxPrefBridge.cpp50
-rw-r--r--security/sandbox/linux/glue/moz.build35
-rw-r--r--security/sandbox/linux/gtest/TestBroker.cpp689
-rw-r--r--security/sandbox/linux/gtest/TestBrokerPolicy.cpp95
-rw-r--r--security/sandbox/linux/gtest/TestLogging.cpp56
-rw-r--r--security/sandbox/linux/gtest/moz.build26
-rw-r--r--security/sandbox/linux/interfaces/moz.build11
-rw-r--r--security/sandbox/linux/interfaces/mozISandboxReporter.idl65
-rw-r--r--security/sandbox/linux/launch/LinuxCapabilities.cpp26
-rw-r--r--security/sandbox/linux/launch/LinuxCapabilities.h122
-rw-r--r--security/sandbox/linux/launch/SandboxLaunch.cpp715
-rw-r--r--security/sandbox/linux/launch/SandboxLaunch.h29
-rw-r--r--security/sandbox/linux/launch/moz.build33
-rw-r--r--security/sandbox/linux/moz.build146
-rw-r--r--security/sandbox/linux/reporter/SandboxReporter.cpp299
-rw-r--r--security/sandbox/linux/reporter/SandboxReporter.h86
-rw-r--r--security/sandbox/linux/reporter/SandboxReporterCommon.h66
-rw-r--r--security/sandbox/linux/reporter/SandboxReporterWrappers.cpp199
-rw-r--r--security/sandbox/linux/reporter/components.conf13
-rw-r--r--security/sandbox/linux/reporter/moz.build34
-rw-r--r--security/sandbox/mac/Sandbox.h90
-rw-r--r--security/sandbox/mac/Sandbox.mm802
-rw-r--r--security/sandbox/mac/SandboxPolicyContent.h399
-rw-r--r--security/sandbox/mac/SandboxPolicyGMP.h101
-rw-r--r--security/sandbox/mac/SandboxPolicyRDD.h199
-rw-r--r--security/sandbox/mac/SandboxPolicySocket.h150
-rw-r--r--security/sandbox/mac/SandboxPolicyUtility.h83
-rw-r--r--security/sandbox/mac/moz.build19
-rw-r--r--security/sandbox/moz.build215
-rw-r--r--security/sandbox/test/browser.ini26
-rw-r--r--security/sandbox/test/browser_bug1393259.js200
-rw-r--r--security/sandbox/test/browser_bug1717599_XDG-CONFIG-DIRS.ini9
-rw-r--r--security/sandbox/test/browser_bug1717599_XDG-CONFIG-HOME.ini9
-rw-r--r--security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-DIRS.js11
-rw-r--r--security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-HOME.js11
-rw-r--r--security/sandbox/test/browser_content_sandbox_fs.js56
-rw-r--r--security/sandbox/test/browser_content_sandbox_fs_snap.js31
-rw-r--r--security/sandbox/test/browser_content_sandbox_fs_tests.js698
-rw-r--r--security/sandbox/test/browser_content_sandbox_fs_xdg.js31
-rw-r--r--security/sandbox/test/browser_content_sandbox_syscalls.js436
-rw-r--r--security/sandbox/test/browser_content_sandbox_utils.js464
-rw-r--r--security/sandbox/test/browser_sandbox_test.js59
-rw-r--r--security/sandbox/test/browser_snap.ini14
-rw-r--r--security/sandbox/test/browser_xdg.ini14
-rw-r--r--security/sandbox/test/bug1393259.html19
-rwxr-xr-xsecurity/sandbox/test/mac_register_font.py85
-rw-r--r--security/sandbox/win/SandboxInitialization.cpp202
-rw-r--r--security/sandbox/win/SandboxInitialization.h52
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl36
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp97
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h35
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp83
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h48
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp27
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h33
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp35
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h41
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/moz.build30
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp170
-rw-r--r--security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h75
-rw-r--r--security/sandbox/win/src/sandboxbroker/moz.build21
-rw-r--r--security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp2029
-rw-r--r--security/sandbox/win/src/sandboxbroker/sandboxBroker.h137
-rw-r--r--security/sandbox/win/src/sandboxtarget/moz.build22
-rw-r--r--security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp64
-rw-r--r--security/sandbox/win/src/sandboxtarget/sandboxTarget.h80
657 files changed, 128718 insertions, 0 deletions
diff --git a/security/sandbox/chromium-shim/base/allocator/buildflags.h b/security/sandbox/chromium-shim/base/allocator/buildflags.h
new file mode 100644
index 0000000000..1799e64067
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/allocator/buildflags.h
@@ -0,0 +1,20 @@
+/* -*- 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, with
+// only the build flags we require.
+
+// Generated by build/write_buildflag_header.py
+// From "//base/allocator:buildflags"
+
+#ifndef BASE_ALLOCATOR_BUILDFLAGS_H_
+#define BASE_ALLOCATOR_BUILDFLAGS_H_
+
+#include "build/buildflag.h"
+
+#define BUILDFLAG_INTERNAL_USE_TCMALLOC() (0)
+
+#endif // BASE_ALLOCATOR_BUILDFLAGS_H_
diff --git a/security/sandbox/chromium-shim/base/allocator/partition_allocator/page_allocator.h b/security/sandbox/chromium-shim/base/allocator/partition_allocator/page_allocator.h
new file mode 100644
index 0000000000..e3bb27d897
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/allocator/partition_allocator/page_allocator.h
@@ -0,0 +1,21 @@
+/* -*- 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/allocator/partition_allocator/page_allocator.h.
+// To provide to an empty ReleaseReservation function, because we never call
+// ReserveAddressSpace.
+
+#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
+#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
+
+namespace base {
+
+BASE_EXPORT void ReleaseReservation() {}
+
+} // namespace base
+
+#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_H_
diff --git a/security/sandbox/chromium-shim/base/debug/activity_tracker.h b/security/sandbox/chromium-shim/base/debug/activity_tracker.h
new file mode 100644
index 0000000000..b59cd12cc0
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/debug/activity_tracker.h
@@ -0,0 +1,61 @@
+/* -*- 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/debug/activity_tracker.h.
+// To provide a class required in base/synchronization/lock_impl_win.cc
+// ScopedLockAcquireActivity. We don't use activity tracking.
+
+#ifndef BASE_DEBUG_ACTIVITY_TRACKER_H_
+#define BASE_DEBUG_ACTIVITY_TRACKER_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+
+namespace base {
+class PlatformThreadHandle;
+class WaitableEvent;
+
+namespace internal {
+class LockImpl;
+}
+
+namespace debug {
+
+class BASE_EXPORT GlobalActivityTracker {
+ public:
+ static bool IsEnabled() { return false; }
+ DISALLOW_COPY_AND_ASSIGN(GlobalActivityTracker);
+};
+
+class BASE_EXPORT ScopedLockAcquireActivity
+{
+ public:
+ ALWAYS_INLINE
+ explicit ScopedLockAcquireActivity(const base::internal::LockImpl* lock) {}
+ DISALLOW_COPY_AND_ASSIGN(ScopedLockAcquireActivity);
+};
+
+class BASE_EXPORT ScopedEventWaitActivity
+{
+ public:
+ ALWAYS_INLINE
+ explicit ScopedEventWaitActivity(const base::WaitableEvent* event) {}
+ DISALLOW_COPY_AND_ASSIGN(ScopedEventWaitActivity);
+};
+
+class BASE_EXPORT ScopedThreadJoinActivity
+{
+ public:
+ ALWAYS_INLINE
+ explicit ScopedThreadJoinActivity(const base::PlatformThreadHandle* thread) {}
+ DISALLOW_COPY_AND_ASSIGN(ScopedThreadJoinActivity);
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_ACTIVITY_TRACKER_H_
diff --git a/security/sandbox/chromium-shim/base/debug/crash_logging.cpp b/security/sandbox/chromium-shim/base/debug/crash_logging.cpp
new file mode 100644
index 0000000000..b2c3f7afa2
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/debug/crash_logging.cpp
@@ -0,0 +1,22 @@
+/* -*- 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 base/debug/crash_logging.cc
+
+#include "base/debug/crash_logging.h"
+
+namespace base {
+namespace debug {
+
+CrashKeyString* AllocateCrashKeyString(const char name[],
+ CrashKeySize value_length) {
+ return nullptr;
+}
+
+void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) {}
+
+} // namespace debug
+} // namespace base
diff --git a/security/sandbox/chromium-shim/base/debug/debugging_buildflags.h b/security/sandbox/chromium-shim/base/debug/debugging_buildflags.h
new file mode 100644
index 0000000000..a2b89849a5
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/debug/debugging_buildflags.h
@@ -0,0 +1,21 @@
+/* -*- 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, with
+// only the build flags we require.
+
+// Generated by build/write_buildflag_header.py
+// From "//base:debugging_buildflags"
+
+#ifndef BASE_DEBUG_DEBUGGING_BUILDFLAGS_H_
+#define BASE_DEBUG_DEBUGGING_BUILDFLAGS_H_
+
+#include "build/buildflag.h"
+
+#define BUILDFLAG_INTERNAL_ENABLE_LOCATION_SOURCE() (1)
+#define BUILDFLAG_INTERNAL_ENABLE_PROFILING() (0)
+
+#endif // BASE_DEBUG_DEBUGGING_BUILDFLAGS_H_
diff --git a/security/sandbox/chromium-shim/base/debug/stack_trace.h b/security/sandbox/chromium-shim/base/debug/stack_trace.h
new file mode 100644
index 0000000000..2ab1485204
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/debug/stack_trace.h
@@ -0,0 +1,30 @@
+/* -*- 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/debug/stack_trace.h
+// to provide a dummy class StackTrace.
+
+#ifndef BASE_DEBUG_STACK_TRACE_H_
+#define BASE_DEBUG_STACK_TRACE_H_
+
+#include <iosfwd>
+
+namespace base {
+namespace debug {
+
+class BASE_EXPORT StackTrace {
+ public:
+ StackTrace() {};
+
+#if !defined(__UCLIBC__) & !defined(_AIX)
+ void OutputToStream(std::ostream*) const {}
+#endif
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_STACK_TRACE_H_
diff --git a/security/sandbox/chromium-shim/base/feature_list.h b/security/sandbox/chromium-shim/base/feature_list.h
new file mode 100644
index 0000000000..ada5c22f24
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/feature_list.h
@@ -0,0 +1,52 @@
+/* -*- 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 cut down version of base/feature_list.h.
+// This just returns the default state for a feature.
+
+#ifndef BASE_FEATURE_LIST_H_
+#define BASE_FEATURE_LIST_H_
+
+#include "base/macros.h"
+
+namespace base {
+
+// Specifies whether a given feature is enabled or disabled by default.
+enum FeatureState {
+ FEATURE_DISABLED_BY_DEFAULT,
+ FEATURE_ENABLED_BY_DEFAULT,
+};
+
+// The Feature struct is used to define the default state for a feature. See
+// comment below for more details. There must only ever be one struct instance
+// for a given feature name - generally defined as a constant global variable or
+// file static. It should never be used as a constexpr as it breaks
+// pointer-based identity lookup.
+struct BASE_EXPORT Feature {
+ // The name of the feature. This should be unique to each feature and is used
+ // for enabling/disabling features via command line flags and experiments.
+ // It is strongly recommended to use CamelCase style for feature names, e.g.
+ // "MyGreatFeature".
+ const char* const name;
+
+ // The default state (i.e. enabled or disabled) for this feature.
+ const FeatureState default_state;
+};
+
+class BASE_EXPORT FeatureList {
+ public:
+ static bool IsEnabled(const Feature& feature) {
+ return feature.default_state == FEATURE_ENABLED_BY_DEFAULT;
+ }
+
+ static FeatureList* GetInstance() { return nullptr; }
+
+ DISALLOW_COPY_AND_ASSIGN(FeatureList);
+};
+
+} // namespace base
+
+#endif // BASE_FEATURE_LIST_H_
diff --git a/security/sandbox/chromium-shim/base/file_version_info_win.cpp b/security/sandbox/chromium-shim/base/file_version_info_win.cpp
new file mode 100644
index 0000000000..bc4d6a4fe0
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/file_version_info_win.cpp
@@ -0,0 +1,90 @@
+/* -*- 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 partial implementation of Chromium's source file
+// base/file_version_info_win.cc
+
+#include "base/file_version_info_win.h"
+
+#include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/scoped_blocking_call.h"
+
+#include "mozilla/Unused.h"
+
+namespace {
+
+struct LanguageAndCodePage {
+ WORD language;
+ WORD code_page;
+};
+
+// Returns the \VarFileInfo\Translation value extracted from the
+// VS_VERSION_INFO resource in |data|.
+LanguageAndCodePage* GetTranslate(const void* data) {
+ static constexpr wchar_t kTranslation[] = L"\\VarFileInfo\\Translation";
+ LPVOID translate = nullptr;
+ UINT dummy_size;
+ if (::VerQueryValue(data, kTranslation, &translate, &dummy_size))
+ return static_cast<LanguageAndCodePage*>(translate);
+ return nullptr;
+}
+
+const VS_FIXEDFILEINFO& GetVsFixedFileInfo(const void* data) {
+ static constexpr wchar_t kRoot[] = L"\\";
+ LPVOID fixed_file_info = nullptr;
+ UINT dummy_size;
+ CHECK(::VerQueryValue(data, kRoot, &fixed_file_info, &dummy_size));
+ return *static_cast<VS_FIXEDFILEINFO*>(fixed_file_info);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<FileVersionInfoWin>
+FileVersionInfoWin::CreateFileVersionInfoWin(const base::FilePath& file_path) {
+ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+ base::BlockingType::MAY_BLOCK);
+
+ DWORD dummy;
+ const wchar_t* path = file_path.value().c_str();
+ const DWORD length = ::GetFileVersionInfoSize(path, &dummy);
+ if (length == 0)
+ return nullptr;
+
+ std::vector<uint8_t> data(length, 0);
+
+ if (!::GetFileVersionInfo(path, dummy, length, data.data()))
+ return nullptr;
+
+ const LanguageAndCodePage* translate = GetTranslate(data.data());
+ if (!translate)
+ return nullptr;
+
+ return base::WrapUnique(new FileVersionInfoWin(
+ std::move(data), translate->language, translate->code_page));
+}
+
+base::Version FileVersionInfoWin::GetFileVersion() const {
+ return base::Version({HIWORD(fixed_file_info_.dwFileVersionMS),
+ LOWORD(fixed_file_info_.dwFileVersionMS),
+ HIWORD(fixed_file_info_.dwFileVersionLS),
+ LOWORD(fixed_file_info_.dwFileVersionLS)});
+}
+
+FileVersionInfoWin::FileVersionInfoWin(std::vector<uint8_t>&& data,
+ WORD language,
+ WORD code_page)
+ : owned_data_(std::move(data)),
+ data_(owned_data_.data()),
+ language_(language),
+ code_page_(code_page),
+ fixed_file_info_(GetVsFixedFileInfo(data_)) {
+ DCHECK(!owned_data_.empty());
+
+ mozilla::Unused << language_;
+ mozilla::Unused << code_page_;
+}
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 0000000000..9f41901864
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/file_version_info_win.h
@@ -0,0 +1,54 @@
+/* -*- 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 partial implementation of Chromium's source file
+// base/file_version_info_win.h.
+
+#ifndef BASE_FILE_VERSION_INFO_WIN_H_
+#define BASE_FILE_VERSION_INFO_WIN_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/version.h"
+
+#include "mozilla/Assertions.h"
+
+struct tagVS_FIXEDFILEINFO;
+typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO;
+
+namespace base {
+class FilePath;
+}
+
+class FileVersionInfoWin {
+ public:
+ static std::unique_ptr<FileVersionInfoWin> CreateFileVersionInfoWin(
+ const base::FilePath& file_path);
+
+ // Get file version number in dotted version format.
+ base::Version GetFileVersion() const;
+
+ private:
+ // |data| is a VS_VERSION_INFO resource. |language| and |code_page| are
+ // extracted from the \VarFileInfo\Translation value of |data|.
+ FileVersionInfoWin(std::vector<uint8_t>&& data,
+ WORD language,
+ WORD code_page);
+
+ const std::vector<uint8_t> owned_data_;
+ const void* const data_;
+ const WORD language_;
+ const WORD code_page_;
+
+ // This is a reference for a portion of |data_|.
+ const VS_FIXEDFILEINFO& fixed_file_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileVersionInfoWin);
+};
+
+#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 0000000000..bcbfecab99
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/files/file_path.cpp
@@ -0,0 +1,217 @@
+// 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 partial implementation of Chromium's source file
+// base/file/file_path.cc.
+
+#include "base/files/file_path.h"
+
+namespace base {
+
+using StringType = FilePath::StringType;
+using StringPieceType = FilePath::StringPieceType;
+
+namespace {
+
+const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0');
+
+// If this FilePath contains a drive letter specification, returns the
+// position of the last character of the drive letter specification,
+// otherwise returns npos. This can only be true on Windows, when a pathname
+// begins with a letter followed by a colon. On other platforms, this always
+// returns npos.
+StringPieceType::size_type FindDriveLetter(StringPieceType path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ // This is dependent on an ASCII-based character set, but that's a
+ // reasonable assumption. iswalpha can be too inclusive here.
+ if (path.length() >= 2 && path[1] == L':' &&
+ ((path[0] >= L'A' && path[0] <= L'Z') ||
+ (path[0] >= L'a' && path[0] <= L'z'))) {
+ return 1;
+ }
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+ return StringType::npos;
+}
+
+bool IsPathAbsolute(StringPieceType path) {
+#if defined(FILE_PATH_USES_DRIVE_LETTERS)
+ StringType::size_type letter = FindDriveLetter(path);
+ if (letter != StringType::npos) {
+ // Look for a separator right after the drive specification.
+ return path.length() > letter + 1 &&
+ FilePath::IsSeparator(path[letter + 1]);
+ }
+ // Look for a pair of leading separators.
+ return path.length() > 1 &&
+ FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]);
+#else // FILE_PATH_USES_DRIVE_LETTERS
+ // Look for a separator in the first position.
+ return path.length() > 0 && FilePath::IsSeparator(path[0]);
+#endif // FILE_PATH_USES_DRIVE_LETTERS
+}
+
+} // namespace
+
+FilePath::FilePath() = default;
+
+FilePath::FilePath(const FilePath& that) = default;
+FilePath::FilePath(FilePath&& that) noexcept = default;
+
+FilePath::FilePath(StringPieceType path) : path_(path) {
+ StringType::size_type nul_pos = path_.find(kStringTerminator);
+ if (nul_pos != StringType::npos)
+ path_.erase(nul_pos, StringType::npos);
+}
+
+FilePath::~FilePath() = default;
+
+FilePath& FilePath::operator=(const FilePath& that) = default;
+
+FilePath& FilePath::operator=(FilePath&& that) = default;
+
+// static
+bool FilePath::IsSeparator(CharType character) {
+ for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
+ if (character == kSeparators[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
+// guaranteed to not modify their input strings, and in fact are implemented
+// differently in this regard on different platforms. Don't use them, but
+// adhere to their behavior.
+FilePath FilePath::DirName() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // The drive letter, if any, always needs to remain in the output. If there
+ // is no drive letter, as will always be the case on platforms which do not
+ // support drive letters, letter will be npos, or -1, so the comparisons and
+ // resizes below using letter will still be valid.
+ StringType::size_type letter = FindDriveLetter(new_path.path_);
+
+ StringType::size_type last_separator =
+ new_path.path_.find_last_of(kSeparators, StringType::npos,
+ kSeparatorsLength - 1);
+ if (last_separator == StringType::npos) {
+ // path_ is in the current directory.
+ new_path.path_.resize(letter + 1);
+ } else if (last_separator == letter + 1) {
+ // path_ is in the root directory.
+ new_path.path_.resize(letter + 2);
+ } else if (last_separator == letter + 2 &&
+ IsSeparator(new_path.path_[letter + 1])) {
+ // path_ is in "//" (possibly with a drive letter); leave the double
+ // separator intact indicating alternate root.
+ new_path.path_.resize(letter + 3);
+ } else if (last_separator != 0) {
+ // path_ is somewhere else, trim the basename.
+ new_path.path_.resize(last_separator);
+ }
+
+ new_path.StripTrailingSeparatorsInternal();
+ if (!new_path.path_.length())
+ new_path.path_ = kCurrentDirectory;
+
+ return new_path;
+}
+
+FilePath FilePath::BaseName() const {
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // The drive letter, if any, is always stripped.
+ StringType::size_type letter = FindDriveLetter(new_path.path_);
+ if (letter != StringType::npos) {
+ new_path.path_.erase(0, letter + 1);
+ }
+
+ // Keep everything after the final separator, but if the pathname is only
+ // one character and it's a separator, leave it alone.
+ StringType::size_type last_separator =
+ new_path.path_.find_last_of(kSeparators, StringType::npos,
+ kSeparatorsLength - 1);
+ if (last_separator != StringType::npos &&
+ last_separator < new_path.path_.length() - 1) {
+ new_path.path_.erase(0, last_separator + 1);
+ }
+
+ return new_path;
+}
+
+FilePath FilePath::Append(StringPieceType component) const {
+ StringPieceType appended = component;
+ StringType without_nuls;
+
+ StringType::size_type nul_pos = component.find(kStringTerminator);
+ if (nul_pos != StringPieceType::npos) {
+ without_nuls = StringType(component.substr(0, nul_pos));
+ appended = StringPieceType(without_nuls);
+ }
+
+ DCHECK(!IsPathAbsolute(appended));
+
+ if (path_.compare(kCurrentDirectory) == 0 && !appended.empty()) {
+ // Append normally doesn't do any normalization, but as a special case,
+ // when appending to kCurrentDirectory, just return a new path for the
+ // component argument. Appending component to kCurrentDirectory would
+ // serve no purpose other than needlessly lengthening the path, and
+ // it's likely in practice to wind up with FilePath objects containing
+ // only kCurrentDirectory when calling DirName on a single relative path
+ // component.
+ return FilePath(appended);
+ }
+
+ FilePath new_path(path_);
+ new_path.StripTrailingSeparatorsInternal();
+
+ // Don't append a separator if the path is empty (indicating the current
+ // directory) or if the path component is empty (indicating nothing to
+ // append).
+ if (!appended.empty() && !new_path.path_.empty()) {
+ // Don't append a separator if the path still ends with a trailing
+ // separator after stripping (indicating the root directory).
+ if (!IsSeparator(new_path.path_.back())) {
+ // Don't append a separator if the path is just a drive letter.
+ if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
+ new_path.path_.append(1, kSeparators[0]);
+ }
+ }
+ }
+
+ new_path.path_.append(appended.data(), appended.size());
+ return new_path;
+}
+
+FilePath FilePath::Append(const FilePath& component) const {
+ return Append(component.value());
+}
+
+void FilePath::StripTrailingSeparatorsInternal() {
+ // If there is no drive letter, start will be 1, which will prevent stripping
+ // the leading separator if there is only one separator. If there is a drive
+ // letter, start will be set appropriately to prevent stripping the first
+ // separator following the drive letter, if a separator immediately follows
+ // the drive letter.
+ StringType::size_type start = FindDriveLetter(path_) + 2;
+
+ StringType::size_type last_stripped = StringType::npos;
+ for (StringType::size_type pos = path_.length();
+ pos > start && IsSeparator(path_[pos - 1]);
+ --pos) {
+ // If the string only has two separators and they're at the beginning,
+ // don't strip them, unless the string began with more than two separators.
+ if (pos != start + 1 || last_stripped == start + 2 ||
+ !IsSeparator(path_[start - 1])) {
+ path_.resize(pos - 1);
+ last_stripped = pos;
+ }
+ }
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium-shim/base/files/file_util.h b/security/sandbox/chromium-shim/base/files/file_util.h
new file mode 100644
index 0000000000..bd6ce0c0a8
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/files/file_util.h
@@ -0,0 +1,11 @@
+/* -*- 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 cut down version of Chromium source file base/files/file_util.h
+// This is included in base/memory/shared_memory.h, but it only actually
+// requires the include for base/files/file_path.h.
+
+#include "base/files/file_path.h"
diff --git a/security/sandbox/chromium-shim/base/gtest_prod_util.h b/security/sandbox/chromium-shim/base/gtest_prod_util.h
new file mode 100644
index 0000000000..b45a1586b0
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/gtest_prod_util.h
@@ -0,0 +1,22 @@
+/* -*- 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/. */
+
+#ifndef BASE_GTEST_PROD_UTIL_H_
+#define BASE_GTEST_PROD_UTIL_H_
+
+#ifndef FRIEND_TEST
+#define FRIEND_TEST(A, B)
+#endif
+
+#ifndef FRIEND_TEST_ALL_PREFIXES
+#define FRIEND_TEST_ALL_PREFIXES(test_case_name, test_name)
+#endif
+
+#ifndef FORWARD_DECLARE_TEST
+#define FORWARD_DECLARE_TEST(test_case_name, test_name)
+#endif
+
+#endif // BASE_GTEST_PROD_UTIL_H_
diff --git a/security/sandbox/chromium-shim/base/logging.cpp b/security/sandbox/chromium-shim/base/logging.cpp
new file mode 100644
index 0000000000..9ac163c0ea
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/logging.cpp
@@ -0,0 +1,160 @@
+/* -*- 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 stripped down version of the Chromium source file base/logging.cc
+// This prevents dependency on the Chromium logging and dependency creep in
+// general.
+// At some point we should find a way to hook this into our own logging see
+// bug 1013988.
+// The formatting in this file matches the original Chromium file to aid future
+// merging.
+
+#include "base/logging.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#if defined(OS_POSIX)
+#include <errno.h>
+#endif
+
+#if defined(OS_WIN)
+#include "base/strings/utf_string_conversions.h"
+#endif
+
+#include <algorithm>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Unused.h"
+
+namespace logging {
+
+namespace {
+
+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
+
+// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
+// an object of the correct type on the LHS of the unused part of the ternary
+// operator.
+std::ostream* g_swallow_stream;
+
+void SetMinLogLevel(int level) {
+ g_min_log_level = std::min(LOG_FATAL, level);
+}
+
+int GetMinLogLevel() {
+ 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) {
+ return 0;
+}
+
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(
+ const int&, const int&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&, const unsigned int&, const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&, const std::string&, const char* name);
+
+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;
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+ std::string* result)
+ : severity_(severity), file_(file), line_(line) {
+ delete result;
+}
+
+LogMessage::~LogMessage() {
+ if (severity_ == LOG_FATAL) {
+ MOZ_CRASH("Hit fatal chromium sandbox condition.");
+ }
+}
+
+SystemErrorCode GetLastSystemErrorCode() {
+#if defined(OS_WIN)
+ return ::GetLastError();
+#elif defined(OS_POSIX)
+ return errno;
+#else
+#error Not implemented
+#endif
+}
+
+#if defined(OS_WIN)
+Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ log_message_(file, line, severity) {
+ mozilla::Unused << err_;
+}
+
+Win32ErrorLogMessage::~Win32ErrorLogMessage() {
+}
+#elif defined(OS_POSIX)
+ErrnoLogMessage::ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err)
+ : err_(err),
+ log_message_(file, line, severity) {
+ mozilla::Unused << err_;
+}
+
+ErrnoLogMessage::~ErrnoLogMessage() {
+}
+#endif // OS_WIN
+
+void RawLog(int level, const char* message) {
+}
+
+} // namespace logging
+
+#if defined(OS_WIN)
+std::ostream& std::operator<<(std::ostream& out, const wchar_t* wstr) {
+ return out << base::WideToUTF8(std::wstring(wstr));
+}
+#endif
diff --git a/security/sandbox/chromium-shim/base/logging_buildflags.h b/security/sandbox/chromium-shim/base/logging_buildflags.h
new file mode 100644
index 0000000000..4c7b5451a3
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/logging_buildflags.h
@@ -0,0 +1,20 @@
+/* -*- 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, with
+// only the build flags we require.
+
+// Generated by build/write_buildflag_header.py
+// From "//base:logging_buildflags"
+
+#ifndef BASE_LOGGING_BUILDFLAGS_H_
+#define BASE_LOGGING_BUILDFLAGS_H_
+
+#include "build/buildflag.h"
+
+#define BUILDFLAG_INTERNAL_ENABLE_LOG_ERROR_NOT_REACHED() (0)
+
+#endif // BASE_LOGGING_BUILDFLAGS_H_
diff --git a/security/sandbox/chromium-shim/base/memory/shared_memory_tracker.h b/security/sandbox/chromium-shim/base/memory/shared_memory_tracker.h
new file mode 100644
index 0000000000..0439410ebb
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/memory/shared_memory_tracker.h
@@ -0,0 +1,40 @@
+/* -*- 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/memory/shared_memory_tracker.h.
+// To provide a class required in base/memory/shared_memory_win.cc.
+// The class is used for memory tracking and dumping, which we don't use and
+// has significant dependencies.
+
+#ifndef BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
+#define BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
+
+namespace base {
+
+// SharedMemoryTracker tracks shared memory usage.
+class BASE_EXPORT SharedMemoryTracker {
+ public:
+ // Returns a singleton instance.
+ static SharedMemoryTracker* GetInstance()
+ {
+ static SharedMemoryTracker* instance = new SharedMemoryTracker;
+ return instance;
+ }
+
+ void IncrementMemoryUsage(const SharedMemoryMapping& mapping) {};
+
+ void DecrementMemoryUsage(const SharedMemoryMapping& mapping) {};
+
+ private:
+ SharedMemoryTracker() {};
+ ~SharedMemoryTracker() = default;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemoryTracker);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_SHARED_MEMORY_TRACKER_H_
diff --git a/security/sandbox/chromium-shim/base/metrics/histogram_functions.h b/security/sandbox/chromium-shim/base/metrics/histogram_functions.h
new file mode 100644
index 0000000000..408e51b408
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/metrics/histogram_functions.h
@@ -0,0 +1,20 @@
+/* -*- 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/metrics/histogram_functions.h.
+// To provide to empty histogram functions required for compilation.
+// We don't require Chromiums histogram collection code.
+
+#ifndef BASE_METRICS_HISTOGRAM_FUNCTIONS_H_
+#define BASE_METRICS_HISTOGRAM_FUNCTIONS_H_
+
+namespace base {
+
+BASE_EXPORT void UmaHistogramSparse(const std::string& name, int sample) {}
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_FUNCTIONS_H_
diff --git a/security/sandbox/chromium-shim/base/metrics/histogram_macros.h b/security/sandbox/chromium-shim/base/metrics/histogram_macros.h
new file mode 100644
index 0000000000..725d7f8076
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/metrics/histogram_macros.h
@@ -0,0 +1,16 @@
+/* -*- 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/metrics/histogram_macros.h.
+// To provide empty histogram macros required for compilation.
+// We don't require Chromiums histogram collection code.
+
+#ifndef BASE_METRICS_HISTOGRAM_MACROS_H_
+#define BASE_METRICS_HISTOGRAM_MACROS_H_
+
+#define UMA_HISTOGRAM_ENUMERATION(name, sample) do { } while (0)
+
+#endif // BASE_METRICS_HISTOGRAM_MACROS_H_
diff --git a/security/sandbox/chromium-shim/base/observer_list.h b/security/sandbox/chromium-shim/base/observer_list.h
new file mode 100644
index 0000000000..1d539dacb9
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/observer_list.h
@@ -0,0 +1,12 @@
+/* -*- 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 cut down version of //base/observer_list.h
+
+#ifndef BASE_OBSERVER_LIST_H_
+#define BASE_OBSERVER_LIST_H_
+
+#endif // BASE_OBSERVER_LIST_H_
diff --git a/security/sandbox/chromium-shim/base/process/launch.h b/security/sandbox/chromium-shim/base/process/launch.h
new file mode 100644
index 0000000000..5ead4b40e9
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/process/launch.h
@@ -0,0 +1,25 @@
+/* -*- 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 reduced version of Chromium's //base/process/launch.h
+// to satisfy compiler.
+
+#ifndef BASE_PROCESS_LAUNCH_H_
+#define BASE_PROCESS_LAUNCH_H_
+
+#include <vector>
+
+#include "base/environment.h"
+
+namespace base {
+
+#if defined(OS_WIN)
+typedef std::vector<HANDLE> HandlesToInheritVector;
+#endif
+
+} // namespace base
+
+#endif // BASE_PROCESS_LAUNCH_H_
diff --git a/security/sandbox/chromium-shim/base/process/memory_win.cpp b/security/sandbox/chromium-shim/base/process/memory_win.cpp
new file mode 100644
index 0000000000..26987fc410
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/process/memory_win.cpp
@@ -0,0 +1,17 @@
+/* -*- 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/. */
+
+#include "base/process/memory.h"
+
+#include "mozilla/Assertions.h"
+
+namespace base {
+
+void TerminateBecauseOutOfMemory(size_t size) {
+ MOZ_CRASH("Hit base::TerminateBecauseOutOfMemory");
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium-shim/base/scoped_native_library.h b/security/sandbox/chromium-shim/base/scoped_native_library.h
new file mode 100644
index 0000000000..d6fdf478ec
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/scoped_native_library.h
@@ -0,0 +1,31 @@
+/* -*- 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 cut down version of Chromium source file base/scoped_native_library.h
+// The chromium sandbox only requires ScopedNativeLibrary class to automatically
+// unload the library, which we can achieve with UniquePtr.
+
+#ifndef BASE_SCOPED_NATIVE_LIBRARY_H_
+#define BASE_SCOPED_NATIVE_LIBRARY_H_
+
+#include "mozilla/UniquePtr.h"
+
+namespace base {
+
+struct HModuleFreePolicy
+{
+ typedef HMODULE pointer;
+ void operator()(pointer hModule)
+ {
+ ::FreeLibrary(hModule);
+ }
+};
+
+typedef mozilla::UniquePtr<HMODULE, HModuleFreePolicy> ScopedNativeLibrary;
+
+} // namespace base
+
+#endif // BASE_SCOPED_NATIVE_LIBRARY_H_
diff --git a/security/sandbox/chromium-shim/base/synchronization/synchronization_buildflags.h b/security/sandbox/chromium-shim/base/synchronization/synchronization_buildflags.h
new file mode 100644
index 0000000000..4b79551541
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/synchronization/synchronization_buildflags.h
@@ -0,0 +1,17 @@
+/* -*- 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 dummy version of a file that is generated by the chromium build
+// from base/BUILD.gn.
+
+#ifndef BASE_SYNCHRONIZATION_SYNCHRONIZATION_BUILDFLAGS_H_
+#define BASE_SYNCHRONIZATION_SYNCHRONIZATION_BUILDFLAGS_H_
+
+#include "build/buildflag.h"
+
+#define BUILDFLAG_INTERNAL_ENABLE_MUTEX_PRIORITY_INHERITANCE() (0)
+
+#endif // BASE_SYNCHRONIZATION_SYNCHRONIZATION_BUILDFLAGS_H_
diff --git a/security/sandbox/chromium-shim/base/third_party/nspr/prtime.h b/security/sandbox/chromium-shim/base/third_party/nspr/prtime.h
new file mode 100644
index 0000000000..9a18a36376
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/third_party/nspr/prtime.h
@@ -0,0 +1,8 @@
+/* -*- 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/. */
+
+// Grab the copy from in our tree
+#include "pr/include/prtime.h"
diff --git a/security/sandbox/chromium-shim/base/third_party/nspr/prtypes.h b/security/sandbox/chromium-shim/base/third_party/nspr/prtypes.h
new file mode 100644
index 0000000000..6aec5e08fb
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/third_party/nspr/prtypes.h
@@ -0,0 +1,8 @@
+/* -*- 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/. */
+
+// Grab the copy from in our tree
+#include "pr/include/prtypes.h"
diff --git a/security/sandbox/chromium-shim/base/threading/platform_thread_linux.cpp b/security/sandbox/chromium-shim/base/threading/platform_thread_linux.cpp
new file mode 100644
index 0000000000..aed65a06bd
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/threading/platform_thread_linux.cpp
@@ -0,0 +1,69 @@
+/* -*- 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 cut down version of Chromium source file base/threading/platform_thread_linux.h
+// with only the functions required. It also has a dummy implementation of
+// SetCurrentThreadPriorityForPlatform, which should not be called.
+
+#include "base/threading/platform_thread.h"
+
+#include "base/threading/platform_thread_internal_posix.h"
+
+#include "mozilla/Assertions.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+const struct sched_param kRealTimePrio = {8};
+} // namespace
+
+const ThreadPriorityToNiceValuePair kThreadPriorityToNiceValueMap[4] = {
+ {ThreadPriority::BACKGROUND, 10},
+ {ThreadPriority::NORMAL, 0},
+ {ThreadPriority::DISPLAY, -8},
+ {ThreadPriority::REALTIME_AUDIO, -10},
+};
+
+
+Optional<bool> CanIncreaseCurrentThreadPriorityForPlatform(
+ ThreadPriority priority) {
+ MOZ_CRASH();
+}
+
+bool SetCurrentThreadPriorityForPlatform(ThreadPriority priority) {
+ MOZ_CRASH();
+}
+
+Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform() {
+ 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) {
+ return base::make_optional(ThreadPriority::REALTIME_AUDIO);
+ }
+ return base::nullopt;
+}
+
+} // namespace internal
+
+void InitThreading() {}
+
+void TerminateOnThread() {}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(THREAD_SANITIZER)
+ return 0;
+#else
+ // ThreadSanitizer bloats the stack heavily. Evidence has been that the
+ // default stack size isn't enough for some browser tests.
+ return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux).
+#endif
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium-shim/base/threading/scoped_blocking_call.h b/security/sandbox/chromium-shim/base/threading/scoped_blocking_call.h
new file mode 100644
index 0000000000..519850d34a
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/threading/scoped_blocking_call.h
@@ -0,0 +1,47 @@
+/* -*- 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/threading/scoped_blocking_call.h
+// To provide to a dummy ScopedBlockingCall class. This prevents dependency
+// creep and we don't use the rest of the blocking call checking.
+
+#ifndef BASE_THREADING_SCOPED_BLOCKING_CALL_H
+#define BASE_THREADING_SCOPED_BLOCKING_CALL_H
+
+#include "base/base_export.h"
+#include "base/location.h"
+
+namespace base {
+
+enum class BlockingType {
+ // The call might block (e.g. file I/O that might hit in memory cache).
+ MAY_BLOCK,
+ // The call will definitely block (e.g. cache already checked and now pinging
+ // server synchronously).
+ WILL_BLOCK
+};
+
+class BASE_EXPORT ScopedBlockingCall {
+ public:
+ ScopedBlockingCall(const Location& from_here, BlockingType blocking_type) {};
+ ~ScopedBlockingCall() {};
+};
+
+namespace internal {
+
+class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives {
+ public:
+ ScopedBlockingCallWithBaseSyncPrimitives(const Location& from_here,
+ BlockingType blocking_type) {}
+ ~ScopedBlockingCallWithBaseSyncPrimitives() {};
+};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_THREADING_SCOPED_BLOCKING_CALL_H
diff --git a/security/sandbox/chromium-shim/base/trace_event/heap_profiler_allocation_context_tracker.h b/security/sandbox/chromium-shim/base/trace_event/heap_profiler_allocation_context_tracker.h
new file mode 100644
index 0000000000..bb2cca0ec4
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/trace_event/heap_profiler_allocation_context_tracker.h
@@ -0,0 +1,32 @@
+/* -*- 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/trace_event/heap_profiler_allocation_context_tracker.h.
+// To provide a function required in base/threading/thread_id_name_manager.cc
+// SetCurrentThreadName. We don't use the heap profiler.
+
+#ifndef BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_
+#define BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_
+
+namespace base {
+namespace trace_event {
+
+// The allocation context tracker keeps track of thread-local context for heap
+// profiling. It includes a pseudo stack of trace events. On every allocation
+// the tracker provides a snapshot of its context in the form of an
+// |AllocationContext| that is to be stored together with the allocation
+// details.
+class BASE_EXPORT AllocationContextTracker {
+ public:
+ static void SetCurrentThreadName(const char* name) {}
+
+ DISALLOW_COPY_AND_ASSIGN(AllocationContextTracker);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_HEAP_PROFILER_ALLOCATION_CONTEXT_TRACKER_H_
diff --git a/security/sandbox/chromium-shim/base/tracked_objects.h b/security/sandbox/chromium-shim/base/tracked_objects.h
new file mode 100644
index 0000000000..092c9533f6
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/tracked_objects.h
@@ -0,0 +1,23 @@
+/* -*- 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/. */
+
+#ifndef _SECURITY_SANDBOX_TRACKED_OBJECTS_H_
+#define _SECURITY_SANDBOX_TRACKED_OBJECTS_H_
+
+#include "mozilla/Assertions.h"
+
+namespace tracked_objects
+{
+ class ThreadData
+ {
+ public:
+ static void InitializeThreadContext(const std::string& name)
+ {
+ MOZ_CRASH();
+ }
+ };
+}
+#endif
diff --git a/security/sandbox/chromium-shim/base/win/base_win_buildflags.h b/security/sandbox/chromium-shim/base/win/base_win_buildflags.h
new file mode 100644
index 0000000000..93d3e11a45
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/win/base_win_buildflags.h
@@ -0,0 +1,17 @@
+/* -*- 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 dummy version of a file that is generated by the chromium build
+// from base/win/BUILD.gn.
+
+#ifndef BASE_WIN_BASE_WIN_BUILDFLAGS_H_
+#define BASE_WIN_BASE_WIN_BUILDFLAGS_H_
+
+#include "build/buildflag.h"
+
+#define BUILDFLAG_INTERNAL_SINGLE_MODULE_MODE_HANDLE_VERIFIER() (0)
+
+#endif // BASE_WIN_BASE_WIN_BUILDFLAGS_H_
diff --git a/security/sandbox/chromium-shim/base/win/registry.h b/security/sandbox/chromium-shim/base/win/registry.h
new file mode 100644
index 0000000000..e5d0f26ed2
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/win/registry.h
@@ -0,0 +1,48 @@
+/* -*- 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 stripped down version of Chromium source file base/win/registry.h
+// Within our copy of Chromium files this is only used in base/win/windows_version.cc
+// in OSInfo::processor_model_name, which we don't use.
+// It is also used in GetUBR, which is used as the VersionNumber.patch, which
+// again is not needed by the sandbox.
+
+#ifndef BASE_WIN_REGISTRY_H_
+#define BASE_WIN_REGISTRY_H_
+
+#include <winerror.h>
+
+namespace base {
+namespace win {
+
+class BASE_EXPORT RegKey {
+ public:
+ RegKey() {};
+ RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) {}
+ ~RegKey() {}
+
+ LONG Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
+ return ERROR_CANTOPEN;
+ }
+
+ LONG ReadValueDW(const wchar_t* name, DWORD* out_value) const
+ {
+ return ERROR_CANTREAD;
+ }
+
+ LONG ReadValue(const wchar_t* name, std::wstring* out_value) const
+ {
+ return ERROR_CANTREAD;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RegKey);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_REGISTRY_H_
diff --git a/security/sandbox/chromium-shim/base/win/sdkdecls.h b/security/sandbox/chromium-shim/base/win/sdkdecls.h
new file mode 100644
index 0000000000..b7aa855e62
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/win/sdkdecls.h
@@ -0,0 +1,368 @@
+/* -*- 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/. */
+
+#ifndef _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_
+#define _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_
+
+#include <windows.h>
+
+// This file contains definitions required for things dynamically loaded
+// while building or targetting lower platform versions or lower SDKs.
+
+#if (_WIN32_WINNT < 0x0602)
+#define ProcThreadAttributeSecurityCapabilities 9
+#define PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES \
+ ProcThreadAttributeValue (ProcThreadAttributeSecurityCapabilities, FALSE, TRUE, FALSE)
+
+#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_MASK (0x00000003 << 8)
+#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_DEFER (0x00000000 << 8)
+#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON (0x00000001 << 8)
+#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_OFF (0x00000002 << 8)
+#define PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS (0x00000003 << 8)
+#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_MASK (0x00000003 << 12)
+#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_DEFER (0x00000000 << 12)
+#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON (0x00000001 << 12)
+#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_OFF (0x00000002 << 12)
+#define PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_RESERVED (0x00000003 << 12)
+#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_MASK (0x00000003 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_DEFER (0x00000000 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON (0x00000001 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_OFF (0x00000002 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_RESERVED (0x00000003 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_MASK (0x00000003 << 20)
+#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_DEFER (0x00000000 << 20)
+#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON (0x00000001 << 20)
+#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_OFF (0x00000002 << 20)
+#define PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_RESERVED (0x00000003 << 20)
+#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_MASK (0x00000003 << 24)
+#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_DEFER (0x00000000 << 24)
+#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON (0x00000001 << 24)
+#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_OFF (0x00000002 << 24)
+#define PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_RESERVED (0x00000003 << 24)
+#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_MASK (0x00000003 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_DEFER (0x00000000 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON (0x00000001 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_OFF (0x00000002 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_RESERVED (0x00000003 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_MASK (0x00000003uLL << 32)
+#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_DEFER (0x00000000uLL << 32)
+#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON (0x00000001uLL << 32)
+#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_OFF (0x00000002uLL << 32)
+#define PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_RESERVED (0x00000003uLL << 32)
+
+typedef struct _MEMORY_PRIORITY_INFORMATION {
+ ULONG MemoryPriority;
+} MEMORY_PRIORITY_INFORMATION, *PMEMORY_PRIORITY_INFORMATION;
+
+WINBASEAPI
+BOOL
+WINAPI
+GetThreadInformation(
+ _In_ HANDLE hThread,
+ _In_ THREAD_INFORMATION_CLASS ThreadInformationClass,
+ _Out_writes_bytes_(ThreadInformationSize) LPVOID ThreadInformation,
+ _In_ DWORD ThreadInformationSize
+ );
+
+WINBASEAPI
+BOOL
+WINAPI
+SetThreadInformation(
+ _In_ HANDLE hThread,
+ _In_ THREAD_INFORMATION_CLASS ThreadInformationClass,
+ _In_reads_bytes_(ThreadInformationSize) LPVOID ThreadInformation,
+ _In_ DWORD ThreadInformationSize
+);
+
+// Check if we're including >= win8 winnt.h
+#ifndef NTDDI_WIN8
+
+typedef struct _SECURITY_CAPABILITIES {
+ PSID AppContainerSid;
+ PSID_AND_ATTRIBUTES Capabilities;
+ DWORD CapabilityCount;
+ DWORD Reserved;
+} SECURITY_CAPABILITIES, *PSECURITY_CAPABILITIES, *LPSECURITY_CAPABILITIES;
+
+typedef enum _PROCESS_MITIGATION_POLICY {
+ ProcessDEPPolicy,
+ ProcessASLRPolicy,
+ ProcessReserved1MitigationPolicy,
+ ProcessStrictHandleCheckPolicy,
+ ProcessSystemCallDisablePolicy,
+ ProcessMitigationOptionsMask,
+ ProcessExtensionPointDisablePolicy,
+ MaxProcessMitigationPolicy
+} PROCESS_MITIGATION_POLICY, *PPROCESS_MITIGATION_POLICY;
+
+#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
+
+typedef struct _PROCESS_MITIGATION_ASLR_POLICY {
+ union {
+ DWORD Flags;
+ struct {
+ DWORD EnableBottomUpRandomization : 1;
+ DWORD EnableForceRelocateImages : 1;
+ DWORD EnableHighEntropy : 1;
+ DWORD DisallowStrippedImages : 1;
+ DWORD ReservedFlags : 28;
+ };
+ };
+} PROCESS_MITIGATION_ASLR_POLICY, *PPROCESS_MITIGATION_ASLR_POLICY;
+
+typedef struct _PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY {
+ union {
+ DWORD Flags;
+ struct {
+ DWORD RaiseExceptionOnInvalidHandleReference : 1;
+ DWORD HandleExceptionsPermanentlyEnabled : 1;
+ DWORD ReservedFlags : 30;
+ };
+ };
+} PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY, *PPROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY;
+
+typedef struct _PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY {
+ union {
+ DWORD Flags;
+ struct {
+ DWORD DisallowWin32kSystemCalls : 1;
+ DWORD ReservedFlags : 31;
+ };
+ };
+} PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY, *PPROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY;
+
+typedef struct _PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY {
+ union {
+ DWORD Flags;
+ struct {
+ DWORD DisableExtensionPoints : 1;
+ DWORD ReservedFlags : 31;
+ };
+ };
+} PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY, *PPROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY;
+
+#endif // NTDDI_WIN8
+
+WINBASEAPI
+BOOL
+WINAPI
+GetProcessMitigationPolicy(
+ _In_ HANDLE hProcess,
+ _In_ PROCESS_MITIGATION_POLICY MitigationPolicy,
+ _Out_writes_bytes_(dwLength) PVOID lpBuffer,
+ _In_ SIZE_T dwLength
+ );
+
+WINBASEAPI
+BOOL
+WINAPI
+SetProcessMitigationPolicy(
+ _In_ PROCESS_MITIGATION_POLICY MitigationPolicy,
+ _In_reads_bytes_(dwLength) PVOID lpBuffer,
+ _In_ SIZE_T dwLength
+ );
+
+#if !defined(_USERENV_)
+#define USERENVAPI DECLSPEC_IMPORT
+#else
+#define USERENVAPI
+#endif
+
+USERENVAPI
+HRESULT
+WINAPI
+CreateAppContainerProfile(_In_ PCWSTR pszAppContainerName,
+ _In_ PCWSTR pszDisplayName,
+ _In_ PCWSTR pszDescription,
+ _In_reads_opt_(dwCapabilityCount)
+ PSID_AND_ATTRIBUTES pCapabilities,
+ _In_ DWORD dwCapabilityCount,
+ _Outptr_ PSID* ppSidAppContainerSid);
+
+USERENVAPI
+HRESULT
+WINAPI
+DeleteAppContainerProfile(
+ _In_ PCWSTR pszAppContainerName);
+
+USERENVAPI
+HRESULT
+WINAPI
+GetAppContainerRegistryLocation(
+ _In_ REGSAM desiredAccess,
+ _Outptr_ PHKEY phAppContainerKey);
+
+USERENVAPI
+HRESULT
+WINAPI
+GetAppContainerFolderPath(
+ _In_ PCWSTR pszAppContainerSid,
+ _Outptr_ PWSTR *ppszPath);
+
+USERENVAPI
+HRESULT
+WINAPI
+DeriveAppContainerSidFromAppContainerName(
+ _In_ PCWSTR pszAppContainerName,
+ _Outptr_ PSID *ppsidAppContainerSid);
+
+#endif // (_WIN32_WINNT < 0x0602)
+
+#if (_WIN32_WINNT < 0x0603)
+//
+// Define dynamic code options.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_MASK (0x00000003uLL << 36)
+#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_DEFER (0x00000000uLL << 36)
+#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON (0x00000001uLL << 36)
+#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_OFF (0x00000002uLL << 36)
+#define PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON_ALLOW_OPT_OUT (0x00000003uLL << 36)
+
+//
+// Define Control Flow Guard (CFG) mitigation policy options. Control Flow
+// Guard allows indirect control transfers to be checked at runtime.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_MASK (0x00000003uLL << 40)
+#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_DEFER (0x00000000uLL << 40)
+#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON (0x00000001uLL << 40)
+#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF (0x00000002uLL << 40)
+#define PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_EXPORT_SUPPRESSION (0x00000003uLL << 40)
+
+//
+// Define module signature options. When enabled, this option will
+// block mapping of non-microsoft binaries.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_MASK (0x00000003uLL << 44)
+#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_DEFER (0x00000000uLL << 44)
+#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON (0x00000001uLL << 44)
+#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_OFF (0x00000002uLL << 44)
+#define PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_RESERVED (0x00000003uLL << 44)
+#endif
+
+#if (_WIN32_WINNT < 0x0A00)
+//
+// Define Font Disable Policy. When enabled, this option will
+// block loading Non System Fonts.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_MASK (0x00000003uLL << 48)
+#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_DEFER (0x00000000uLL << 48)
+#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON (0x00000001uLL << 48)
+#define PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_OFF (0x00000002uLL << 48)
+#define PROCESS_CREATION_MITIGATION_POLICY_AUDIT_NONSYSTEM_FONTS (0x00000003uLL << 48)
+
+//
+// Define remote image load options. When enabled, this option will
+// block mapping of images from remote devices.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_MASK (0x00000003uLL << 52)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_DEFER (0x00000000uLL << 52)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON (0x00000001uLL << 52)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_OFF (0x00000002uLL << 52)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_RESERVED (0x00000003uLL << 52)
+
+//
+// Define low IL image load options. When enabled, this option will
+// block mapping of images that have the low mandatory label.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_MASK (0x00000003uLL << 56)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_DEFER (0x00000000uLL << 56)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON (0x00000001uLL << 56)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_OFF (0x00000002uLL << 56)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_RESERVED (0x00000003uLL << 56)
+
+//
+// Define image load options to prefer System32 images compared to
+// the same images in application directory. When enabled, this option
+// will prefer loading images from system32 folder.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_MASK (0x00000003uLL << 60)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_DEFER (0x00000000uLL << 60)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON (0x00000001uLL << 60)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_OFF (0x00000002uLL << 60)
+#define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_RESERVED (0x00000003uLL << 60)
+
+//
+// Define the restricted indirect branch prediction mitigation policy options.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_MASK (0x00000003ui64 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_DEFER (0x00000000ui64 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON (0x00000001ui64 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_OFF (0x00000002ui64 << 16)
+#define PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_RESERVED (0x00000003ui64 << 16)
+
+//
+// Define the user-mode shadow stack mitigation policy options.
+//
+
+#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_MASK (0x00000003ui64 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_DEFER (0x00000000ui64 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON (0x00000001ui64 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_OFF (0x00000002ui64 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_RESERVED (0x00000003ui64 << 28)
+#define PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_STRICT_MODE (0x00000003ui64 << 28)
+
+//
+// Define Attribute to disable creation of child process
+//
+
+#define PROCESS_CREATION_CHILD_PROCESS_RESTRICTED 0x01
+#define PROCESS_CREATION_CHILD_PROCESS_OVERRIDE 0x02
+
+//
+// Define Attribute for Desktop Appx Overide.
+//
+
+#define PROCESS_CREATION_DESKTOP_APPX_OVERRIDE 0x04
+
+#define ProcThreadAttributeChildProcessPolicy 14
+
+#define PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY \
+ ProcThreadAttributeValue (ProcThreadAttributeChildProcessPolicy, FALSE, TRUE, FALSE)
+
+//
+// Define Attribute to opt out of matching All Application Packages
+//
+
+#define PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT 0x01
+
+#define ProcThreadAttributeAllApplicationPackagesPolicy 15
+
+#define PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY \
+ ProcThreadAttributeValue (ProcThreadAttributeAllApplicationPackagesPolicy, FALSE, TRUE, FALSE)
+
+//
+// Define functions declared only when _WIN32_WINNT >= 0x0A00
+//
+
+WINBASEAPI
+BOOL
+WINAPI
+IsWow64Process2(
+ _In_ HANDLE hProcess,
+ _Out_ USHORT* pProcessMachine,
+ _Out_opt_ USHORT* pNativeMachine
+ );
+
+WINBASEAPI
+BOOL
+WINAPI
+IsUserCetAvailableInEnvironment(
+ _In_ DWORD UserCetEnvironment
+ );
+
+#define USER_CET_ENVIRONMENT_WIN32_PROCESS 0x00000000
+
+#endif // (_WIN32_WINNT < 0x0A00)
+
+#endif // _SECURITY_SANDBOX_BASE_SHIM_SDKDECLS_H_
diff --git a/security/sandbox/chromium-shim/base/win/win_util.cpp b/security/sandbox/chromium-shim/base/win/win_util.cpp
new file mode 100644
index 0000000000..3ea789675d
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/win/win_util.cpp
@@ -0,0 +1,42 @@
+/* -*- 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 partial implementation of Chromium's source file
+// base/win/win_util.cc
+
+#include "base/win/win_util.h"
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+namespace win {
+
+std::wstring GetWindowObjectName(HANDLE handle) {
+ // Get the size of the name.
+ std::wstring object_name;
+
+ DWORD size = 0;
+ ::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
+ if (!size) {
+ DPCHECK(false);
+ return object_name;
+ }
+
+ LOG_ASSERT(size % sizeof(wchar_t) == 0u);
+
+ // Query the name of the object.
+ if (!::GetUserObjectInformation(
+ handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
+ size, &size)) {
+ DPCHECK(false);
+ }
+
+ return object_name;
+}
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium-shim/base/win/win_util.h b/security/sandbox/chromium-shim/base/win/win_util.h
new file mode 100644
index 0000000000..3e91e63d59
--- /dev/null
+++ b/security/sandbox/chromium-shim/base/win/win_util.h
@@ -0,0 +1,26 @@
+/* -*- 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 partial implementation of Chromium's source file
+// base/win/win_util.h
+
+#ifndef BASE_WIN_WIN_UTIL_H_
+#define BASE_WIN_WIN_UTIL_H_
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace win {
+
+// Returns the name of a desktop or a window station.
+BASE_EXPORT std::wstring GetWindowObjectName(HANDLE handle);
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_WIN_UTIL_H_
diff --git a/security/sandbox/chromium-shim/patches/after_update/add_WOW64_flags_to_allowed_registry_read_flags.patch b/security/sandbox/chromium-shim/patches/after_update/add_WOW64_flags_to_allowed_registry_read_flags.patch
new file mode 100644
index 0000000000..7eb643719e
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/add_WOW64_flags_to_allowed_registry_read_flags.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1482405067 0
+# Thu Dec 22 11:11:07 2016 +0000
+# Node ID 43d0efc18f586e1ed90b95c4a52235c4648e96a9
+# Parent 266ef86795979f2ef9b6650d1bb35fb27d11ad86
+Add KEY_WOW64_64Key and KEY_WOW64_32KEY to the Chromium sandbox allowed registry read flags. r=aklotz
+
+Originally landed as changeset:
+https://hg.mozilla.org/mozilla-central/rev/d24db55deb85
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.cc b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
+--- a/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
++++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
+@@ -15,17 +15,18 @@
+ #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_t kAllowedRegFlags =
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_READ |
+- GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
++ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL | KEY_WOW64_64KEY |
++ KEY_WOW64_32KEY;
+
+ // Opens the key referenced by |obj_attributes| with |access| and
+ // checks what permission was given. Remove the WRITE flags and update
+ // |access| with the new value.
+ NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes,
+ DWORD* access) {
+ NtOpenKeyFunction NtOpenKey = nullptr;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
diff --git a/security/sandbox/chromium-shim/patches/after_update/add_interception_logging.patch b/security/sandbox/chromium-shim/patches/after_update/add_interception_logging.patch
new file mode 100644
index 0000000000..344fd569d7
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/add_interception_logging.patch
@@ -0,0 +1,810 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1417281138 0
+# Sat Nov 29 17:12:18 2014 +0000
+# Node ID 4ea2e332affe4b74bd37fbf2fee8da0b1c94e115
+# Parent 5eec91873c96c2cbfc856ba86335fa068c89d6ce
+Re-apply - Logging changes to the Chromium interception code. r=tabraldes
+
+Originally landed as changset:
+https://hg.mozilla.org/mozilla-central/rev/0f763c186855
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
+@@ -10,16 +10,17 @@
+ #include "sandbox/win/src/filesystem_policy.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"
+
+ // This status occurs when trying to access a network share on the machine from
+ // which it is shared.
+ #define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS)0xC0000201L)
+
+ namespace sandbox {
+
+ NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+@@ -37,16 +38,20 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCre
+ // Check if the process can open it first.
+ NTSTATUS status = orig_CreateFile(
+ file, desired_access, object_attributes, io_status, allocation_size,
+ file_attributes, sharing, disposition, options, ea_buffer, ea_length);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+@@ -96,16 +101,19 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCre
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
++ mozilla::sandboxing::LogAllowed("NtCreateFile",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+@@ -115,16 +123,20 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenF
+ ULONG options) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes,
+ io_status, sharing, options);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+@@ -171,31 +183,38 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenF
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
++ mozilla::sandboxing::LogAllowed("NtOpenFile",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI
+ TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE))
+ break;
+
+@@ -227,32 +246,39 @@ TargetNtQueryAttributesFile(NtQueryAttri
+ ResultCode code = CrossCall(ipc, IpcTag::NTQUERYATTRIBUTESFILE, name.get(),
+ attributes, file_info, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
++ mozilla::sandboxing::LogAllowed("NtQueryAttributesFile",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryFullAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status =
+ orig_QueryFullAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION),
+ WRITE))
+ break;
+@@ -284,16 +310,20 @@ NTSTATUS WINAPI TargetNtQueryFullAttribu
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTQUERYFULLATTRIBUTESFILE,
+ name.get(), attributes, file_info, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
++
++ mozilla::sandboxing::LogAllowed("NtQueryFullAttributesFile",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI
+ TargetNtSetInformationFile(NtSetInformationFileFunction orig_SetInformationFile,
+ HANDLE file,
+@@ -302,16 +332,18 @@ TargetNtSetInformationFile(NtSetInformat
+ ULONG length,
+ FILE_INFORMATION_CLASS file_info_class) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_SetInformationFile(file, io_status, file_info, length,
+ file_info_class);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
++ mozilla::sandboxing::LogBlocked("NtSetInformationFile");
++
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+@@ -366,14 +398,15 @@ TargetNtSetInformationFile(NtSetInformat
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTSETINFO_RENAME, file, io_status_buffer,
+ file_info_buffer, length, file_info_class, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
++ mozilla::sandboxing::LogAllowed("NtSetInformationFile");
+ } while (false);
+
+ return status;
+ }
+
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
+@@ -5,16 +5,17 @@
+ #include "sandbox/win/src/handle_interception.h"
+
+ #include "sandbox/win/src/crosscall_client.h"
+ #include "sandbox/win/src/ipc_tags.h"
+ #include "sandbox/win/src/sandbox_factory.h"
+ #include "sandbox/win/src/sandbox_nt_util.h"
+ #include "sandbox/win/src/sharedmem_ipc_client.h"
+ #include "sandbox/win/src/target_services.h"
++#include "mozilla/sandboxing/sandboxLogging.h"
+
+ namespace sandbox {
+
+ ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+@@ -29,17 +30,19 @@ ResultCode DuplicateHandleProxy(HANDLE s
+ ResultCode code = CrossCall(ipc, IpcTag::DUPLICATEHANDLEPROXY,
+ source_handle, target_process_id,
+ desired_access, options, &answer);
+ if (SBOX_ALL_OK != code)
+ return code;
+
+ 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;
+ }
+
+ } // namespace sandbox
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
+@@ -7,16 +7,17 @@
+ #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 {
+
+ HANDLE WINAPI
+ TargetCreateNamedPipeW(CreateNamedPipeWFunction orig_CreateNamedPipeW,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+@@ -26,16 +27,18 @@ TargetCreateNamedPipeW(CreateNamedPipeWF
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = orig_CreateNamedPipeW(
+ pipe_name, open_mode, pipe_mode, max_instance, out_buffer_size,
+ in_buffer_size, default_timeout, security_attributes);
+ if (INVALID_HANDLE_VALUE != pipe)
+ return pipe;
+
++ 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;
+
+ DWORD original_error = ::GetLastError();
+
+ // We don't support specific Security Attributes.
+ if (security_attributes)
+@@ -61,16 +64,17 @@ TargetCreateNamedPipeW(CreateNamedPipeWF
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+
+ if (ERROR_SUCCESS != answer.win32_result)
+ return INVALID_HANDLE_VALUE;
+
++ mozilla::sandboxing::LogAllowed("CreateNamedPipeW", pipe_name);
+ return answer.handle;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
+@@ -10,16 +10,17 @@
+ #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 {
+
+ SANDBOX_INTERCEPT NtExports g_nt;
+
+ // Hooks NtOpenThread and proxy the call to the broker if it's trying to
+ // open a thread in the same process.
+ NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
+@@ -27,16 +28,17 @@ NTSTATUS WINAPI TargetNtOpenThread(NtOpe
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status =
+ orig_OpenThread(thread, desired_access, object_attributes, client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
++ mozilla::sandboxing::LogBlocked("NtOpenThread");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32_t thread_id = 0;
+ bool should_break = false;
+@@ -91,16 +93,17 @@ NTSTATUS WINAPI TargetNtOpenThread(NtOpe
+
+ __try {
+ // Write the output parameters.
+ *thread = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
++ mozilla::sandboxing::LogAllowed("NtOpenThread");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+ }
+
+ // Hooks NtOpenProcess and proxy the call to the broker if it's trying to
+ // open the current process.
+@@ -176,16 +179,17 @@ NTSTATUS WINAPI
+ TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessToken(process, desired_access, token);
+ if (NT_SUCCESS(status))
+ return status;
+
++ mozilla::sandboxing::LogBlocked("NtOpenProcessToken");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+@@ -207,16 +211,17 @@ TargetNtOpenProcessToken(NtOpenProcessTo
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
++ mozilla::sandboxing::LogAllowed("NtOpenProcessToken");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI
+ TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,
+@@ -224,16 +229,17 @@ TargetNtOpenProcessTokenEx(NtOpenProcess
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access,
+ handle_attributes, token);
+ if (NT_SUCCESS(status))
+ return status;
+
++ mozilla::sandboxing::LogBlocked("NtOpenProcessTokenEx");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+@@ -255,16 +261,17 @@ TargetNtOpenProcessTokenEx(NtOpenProcess
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
++ mozilla::sandboxing::LogAllowed("NtOpenProcessTokenEx");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+ }
+
+ BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
+ LPCWSTR application_name,
+@@ -280,16 +287,18 @@ BOOL WINAPI TargetCreateProcessW(CreateP
+ 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();
+
+@@ -320,16 +329,17 @@ BOOL WINAPI TargetCreateProcessW(CreateP
+ cur_dir, current_directory, proc_info, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return false;
+
++ mozilla::sandboxing::LogAllowed("CreateProcessW", application_name);
+ return true;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return false;
+ }
+
+ BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
+@@ -346,16 +356,18 @@ BOOL WINAPI TargetCreateProcessA(CreateP
+ if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() &&
+ orig_CreateProcessA(application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags,
+ environment, current_directory, startup_info,
+ process_information)) {
+ 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();
+
+@@ -420,16 +432,17 @@ BOOL WINAPI TargetCreateProcessA(CreateP
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return false;
+
++ mozilla::sandboxing::LogAllowed("CreateProcessA", application_name);
+ return true;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return false;
+ }
+
+ HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
+diff --git a/security/sandbox/chromium/sandbox/win/src/registry_interception.cc b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
+@@ -9,16 +9,17 @@
+ #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 {
+
+ NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+@@ -27,16 +28,22 @@ NTSTATUS WINAPI TargetNtCreateKey(NtCrea
+ PULONG disposition) {
+ // Check if the process can create it first.
+ NTSTATUS status =
+ orig_CreateKey(key, desired_access, object_attributes, title_index,
+ class_name, create_options, disposition);
+ if (NT_SUCCESS(status))
+ return status;
+
++ 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;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+@@ -114,16 +121,19 @@ NTSTATUS WINAPI TargetNtCreateKey(NtCrea
+
+ if (disposition)
+ *disposition = answer.extended[0].unsigned_int;
+
+ status = answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
++ mozilla::sandboxing::LogAllowed("NtCreateKey",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+@@ -193,30 +203,39 @@ NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS
+ break;
+
+ __try {
+ *key = answer.handle;
+ status = answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
++ mozilla::sandboxing::LogAllowed("NtOpenKey[Ex]",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenKey(key, desired_access, object_attributes);
+ if (NT_SUCCESS(status))
+ return status;
+
++ 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);
+ }
+
+ NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options) {
+@@ -225,12 +244,18 @@ NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpen
+ orig_OpenKeyEx(key, desired_access, object_attributes, open_options);
+
+ // We do not support open_options at this time. The 2 current known values
+ // are REG_OPTION_CREATE_LINK, to open a symbolic link, and
+ // REG_OPTION_BACKUP_RESTORE to open the key with special privileges.
+ if (NT_SUCCESS(status) || open_options != 0)
+ return status;
+
++ 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);
+ }
+
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/signed_interception.cc b/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
+@@ -9,16 +9,17 @@
+ #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 {
+
+ NTSTATUS WINAPI
+ TargetNtCreateSection(NtCreateSectionFunction orig_CreateSection,
+ PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+@@ -37,16 +38,18 @@ TargetNtCreateSection(NtCreateSectionFun
+ break;
+ if (maximum_size)
+ break;
+ if (section_page_protection != PAGE_EXECUTE)
+ break;
+ if (allocation_attributes != SEC_IMAGE)
+ break;
+
++ mozilla::sandboxing::LogBlocked("NtCreateSection");
++
+ // IPC must be fully started.
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> path;
+
+ if (!NtGetPathFromHandle(file_handle, &path))
+@@ -73,16 +76,17 @@ TargetNtCreateSection(NtCreateSectionFun
+ if (code != SBOX_ALL_OK)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *section_handle = answer.handle;
++ mozilla::sandboxing::LogAllowed("NtCreateSection");
+ return answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ // Fall back to the original API in all failure cases.
+ return orig_CreateSection(section_handle, desired_access, object_attributes,
+diff --git a/security/sandbox/chromium/sandbox/win/src/sync_interception.cc b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
+@@ -9,16 +9,17 @@
+ #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 {
+
+ ResultCode ProxyCreateEvent(LPCWSTR name,
+ uint32_t initial_state,
+ EVENT_TYPE event_type,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+@@ -59,16 +60,20 @@ NTSTATUS WINAPI TargetNtCreateEvent(NtCr
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NTSTATUS status =
+ orig_CreateEvent(event_handle, desired_access, object_attributes,
+ event_type, initial_state);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
++ 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;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+@@ -97,30 +102,37 @@ NTSTATUS WINAPI TargetNtCreateEvent(NtCr
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
++ mozilla::sandboxing::LogAllowed("NtCreateEvent",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NTSTATUS status =
+ orig_OpenEvent(event_handle, desired_access, object_attributes);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
++ 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;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+@@ -149,14 +161,17 @@ NTSTATUS WINAPI TargetNtOpenEvent(NtOpen
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
++ mozilla::sandboxing::LogAllowed("NtOpenEvent",
++ object_attributes->ObjectName->Buffer,
++ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+ }
+
+ } // namespace sandbox
diff --git a/security/sandbox/chromium-shim/patches/after_update/allow_ntpath_in_SignedPolicy_GenerateRules.patch b/security/sandbox/chromium-shim/patches/after_update/allow_ntpath_in_SignedPolicy_GenerateRules.patch
new file mode 100644
index 0000000000..8e6a951467
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/allow_ntpath_in_SignedPolicy_GenerateRules.patch
@@ -0,0 +1,82 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1605814807 28800
+# Thu Nov 19 11:40:07 2020 -0800
+# Node ID 29b049665db1f28ffdfce319ad48912d4a024e23
+# Parent 94435953fb89c1fe147c6b76a9ecb61f59625d30
+Bug 1620114 - Allow an NT path string to be passed to SignedPolicy::GenerateRules. r=bobowen
+so that our SandboxBroker can add a policy rule with an NT path directly.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/signed_policy.cc b/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
+--- a/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
++++ b/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
+@@ -7,39 +7,63 @@
+ #include <stdint.h>
+
+ #include <string>
+
+ #include "sandbox/win/src/ipc_tags.h"
+ #include "sandbox/win/src/policy_engine_opcodes.h"
+ #include "sandbox/win/src/policy_params.h"
+ #include "sandbox/win/src/sandbox_policy.h"
++#include "sandbox/win/src/sandbox_utils.h"
+ #include "sandbox/win/src/win_utils.h"
+
++namespace {
++
++bool IsValidNtPath(const base::FilePath& name) {
++ UNICODE_STRING uni_name;
++ OBJECT_ATTRIBUTES obj_attr;
++ sandbox::InitObjectAttribs(name.value(), OBJ_CASE_INSENSITIVE, nullptr,
++ &obj_attr, &uni_name, nullptr);
++
++ NtQueryAttributesFileFunction NtQueryAttributesFile = nullptr;
++ ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
++ FILE_BASIC_INFORMATION file_info;
++ return NtQueryAttributesFile &&
++ NT_SUCCESS(NtQueryAttributesFile(&obj_attr, &file_info));
++}
++
++} // namespace
++
+ namespace sandbox {
+
+ bool SignedPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ // Only support one semantic.
+ if (TargetPolicy::SIGNED_ALLOW_LOAD != semantics) {
+ return false;
+ }
+
+ base::FilePath file_path(name);
++ base::FilePath nt_filename;
+ std::wstring nt_path_name;
+- if (!GetNtPathFromWin32Path(file_path.DirName().value().c_str(),
+- &nt_path_name))
++ if (GetNtPathFromWin32Path(file_path.DirName().value().c_str(),
++ &nt_path_name)) {
++ base::FilePath nt_path(nt_path_name);
++ nt_filename = nt_path.Append(file_path.BaseName());
++ } else if (IsValidNtPath(file_path)) {
++ nt_filename = std::move(file_path);
++ } else {
+ return false;
+- base::FilePath nt_path(nt_path_name);
+- std::wstring nt_filename = nt_path.Append(file_path.BaseName()).value();
++ }
++
+ // Create a rule to ASK_BROKER if name matches.
+ PolicyRule signed_policy(ASK_BROKER);
+- if (!signed_policy.AddStringMatch(IF, NameBased::NAME, nt_filename.c_str(),
+- CASE_INSENSITIVE)) {
++ if (!signed_policy.AddStringMatch(
++ IF, NameBased::NAME, nt_filename.value().c_str(), CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::NTCREATESECTION, &signed_policy)) {
+ return false;
+ }
+
+ return true;
+ }
diff --git a/security/sandbox/chromium-shim/patches/after_update/allow_rules_for_network_drive_and_non_file_devices.patch b/security/sandbox/chromium-shim/patches/after_update/allow_rules_for_network_drive_and_non_file_devices.patch
new file mode 100644
index 0000000000..8d497e1ff9
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/allow_rules_for_network_drive_and_non_file_devices.patch
@@ -0,0 +1,190 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1454317140 0
+# Mon Feb 01 08:59:00 2016 +0000
+# Node ID 9870a92ea5f352ab5a841003a30ab52c8deb589e
+# Parent d62b6a3a0c58528a8bf864bb5ab6bb9faada972b
+Change to allow network drives in sandbox rules with non-file device fix. r=aklotz
+
+Originally landed in changeset:
+https://hg.mozilla.org/mozilla-central/rev/c70d06fa5302
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.cc b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
+--- a/security/sandbox/chromium/sandbox/win/src/win_utils.cc
++++ b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
+@@ -194,61 +194,66 @@ bool ResolveRegistryName(std::wstring na
+
+ return false;
+ }
+
+ // |full_path| can have any of the following forms:
+ // \??\c:\some\foo\bar
+ // \Device\HarddiskVolume0\some\foo\bar
+ // \??\HarddiskVolume0\some\foo\bar
++// \??\UNC\SERVER\Share\some\foo\bar
+ DWORD IsReparsePoint(const std::wstring& full_path) {
+ // Check if it's a pipe. We can't query the attributes of a pipe.
+ if (IsPipe(full_path))
+ return ERROR_NOT_A_REPARSE_POINT;
+
+ std::wstring path;
+ bool nt_path = IsNTPath(full_path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+ bool is_device_path = IsDevicePath(path, &path);
+
+ if (!has_drive && !is_device_path && !nt_path)
+ return ERROR_INVALID_NAME;
+
+- bool added_implied_device = false;
+ if (!has_drive) {
+- path = std::wstring(kNTDotPrefix) + path;
+- added_implied_device = true;
++ // Add Win32 device namespace prefix, required for some Windows APIs.
++ path.insert(0, kNTDotPrefix);
+ }
+
+- std::wstring::size_type last_pos = std::wstring::npos;
+- bool passed_once = false;
++ // Ensure that volume path matches start of path.
++ wchar_t vol_path[MAX_PATH];
++ if (!::GetVolumePathNameW(path.c_str(), vol_path, MAX_PATH)) {
++ // This will fail if this is a device that isn't volume related, which can't
++ // then be a reparse point.
++ return is_device_path ? ERROR_NOT_A_REPARSE_POINT : ERROR_INVALID_NAME;
++ }
++
++ // vol_path includes a trailing slash, so reduce size for path and loop check.
++ size_t vol_path_len = wcslen(vol_path) - 1;
++ if (!EqualPath(path, vol_path, vol_path_len)) {
++ return ERROR_INVALID_NAME;
++ }
+
+ do {
+- path = path.substr(0, last_pos);
+-
+ DWORD attributes = ::GetFileAttributes(path.c_str());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ DWORD error = ::GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND &&
++ error != ERROR_INVALID_FUNCTION &&
+ error != ERROR_INVALID_NAME) {
+ // Unexpected error.
+- if (passed_once && added_implied_device &&
+- (path.rfind(L'\\') == kNTDotPrefixLen - 1)) {
+- break;
+- }
+ return error;
+ }
+ } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
+ // This is a reparse point.
+ return ERROR_SUCCESS;
+ }
+
+- passed_once = true;
+- last_pos = path.rfind(L'\\');
+- } while (last_pos > 2); // Skip root dir.
++ path.resize(path.rfind(L'\\'));
++ } while (path.size() > vol_path_len); // Skip root dir.
+
+ return ERROR_NOT_A_REPARSE_POINT;
+ }
+
+ // We get a |full_path| of the forms accepted by IsReparsePoint(), and the name
+ // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
+ bool SameObject(HANDLE handle, const wchar_t* full_path) {
+ // Check if it's a pipe.
+@@ -258,63 +263,67 @@ bool SameObject(HANDLE handle, const wch
+ std::wstring actual_path;
+ if (!GetPathFromHandle(handle, &actual_path))
+ return false;
+
+ std::wstring path(full_path);
+ DCHECK_NT(!path.empty());
+
+ // This may end with a backslash.
+- const wchar_t kBackslash = '\\';
+- if (path.back() == kBackslash)
+- path = path.substr(0, path.length() - 1);
++ if (path.back() == L'\\') {
++ path.pop_back();
++ }
+
+- // Perfect match (case-insesitive check).
++ // Perfect match (case-insensitive check).
+ if (EqualPath(actual_path, path))
+ return true;
+
+ bool nt_path = IsNTPath(path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+
+ if (!has_drive && nt_path) {
+ std::wstring simple_actual_path;
+- if (!IsDevicePath(actual_path, &simple_actual_path))
+- return false;
+-
+- // Perfect match (case-insesitive check).
+- return (EqualPath(simple_actual_path, 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 (!has_drive)
++ // 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)) {
+ return false;
+-
+- // We only need 3 chars, but let's alloc a buffer for four.
+- wchar_t drive[4] = {0};
+- wchar_t vol_name[MAX_PATH];
+- memcpy(drive, &path[0], 2 * sizeof(*drive));
+-
+- // We'll get a double null terminated string.
+- DWORD vol_length = ::QueryDosDeviceW(drive, vol_name, MAX_PATH);
+- if (vol_length < 2 || vol_length == MAX_PATH)
++ }
++ size_t vol_path_len = wcslen(vol_path);
++ base::string16 nt_vol;
++ if (!GetNtPathFromWin32Path(vol_path, &nt_vol)) {
+ return false;
+-
+- // Ignore the nulls at the end.
+- vol_length = static_cast<DWORD>(wcslen(vol_name));
++ }
+
+ // The two paths should be the same length.
+- if (vol_length + path.size() - 2 != actual_path.size())
++ if (nt_vol.size() + path.size() - vol_path_len != actual_path.size()) {
+ return false;
++ }
+
+- // Check up to the drive letter.
+- if (!EqualPath(actual_path, vol_name, vol_length))
++ // Check the volume matches.
++ if (!EqualPath(actual_path, nt_vol.c_str(), nt_vol.size())) {
+ return false;
++ }
+
+- // Check the path after the drive letter.
+- if (!EqualPath(actual_path, vol_length, path, 2))
++ // Check the path after the volume matches.
++ if (!EqualPath(actual_path, nt_vol.size(), path, vol_path_len)) {
+ return false;
++ }
+
+ return true;
+ }
+
+ // Just make a best effort here. There are lots of corner cases that we're
+ // not expecting - and will fail to make long.
+ bool ConvertToLongPath(std::wstring* native_path,
+ const std::wstring* drive_letter) {
diff --git a/security/sandbox/chromium-shim/patches/after_update/arm64_set_LoaderThreads.patch b/security/sandbox/chromium-shim/patches/after_update/arm64_set_LoaderThreads.patch
new file mode 100644
index 0000000000..4d1817db17
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/arm64_set_LoaderThreads.patch
@@ -0,0 +1,99 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1549645620 0
+# Fri Feb 08 17:07:00 2019 +0000
+# Node ID fb5e7c1090a7ddfde22fd2fb5f8a957ccc61fa64
+# Parent 5ef34aa8c8918649528048dd60907862a4355e29
+Bug 1515088 Part 2: Set LoaderThreads to 1 in the RTL_USER_PROCESS_PARAMETERS structure on child process start-up. r=aklotz
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.cc b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
+--- a/security/sandbox/chromium/sandbox/win/src/win_utils.cc
++++ b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
+@@ -456,20 +456,21 @@ bool GetNtPathFromWin32Path(const std::w
+ bool rv = GetPathFromHandle(file, nt_path);
+ ::CloseHandle(file);
+ return rv;
+ }
+
+ bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+- size_t length) {
++ size_t length,
++ DWORD writeProtection) {
+ // First, remove the protections.
+ DWORD old_protection;
+- if (!::VirtualProtectEx(child_process, address, length, PAGE_WRITECOPY,
++ if (!::VirtualProtectEx(child_process, address, length, writeProtection,
+ &old_protection))
+ return false;
+
+ SIZE_T written;
+ bool ok =
+ ::WriteProcessMemory(child_process, address, buffer, length, &written) &&
+ (length == written);
+
+@@ -544,16 +545,40 @@ void* GetProcessBaseAddress(HANDLE proce
+ &bytes_read) ||
+ (sizeof(magic) != bytes_read)) {
+ return nullptr;
+ }
+
+ if (magic[0] != 'M' || magic[1] != 'Z')
+ return nullptr;
+
++#if defined(_M_ARM64)
++ // Windows 10 on ARM64 has multi-threaded DLL loading that does not work with
++ // the sandbox. (On x86 this gets disabled by hook detection code that was not
++ // ported to ARM64). This overwrites the LoaderThreads value in the process
++ // parameters part of the PEB, if it is set to the default of 0 (which
++ // actually means it defaults to 4 loading threads). This is an undocumented
++ // field so there is a, probably small, risk that it might change or move in
++ // the future. In order to slightly guard against that we only update if the
++ // value is currently 0.
++ auto processParameters = reinterpret_cast<uint8_t*>(peb.ProcessParameters);
++ const uint32_t loaderThreadsOffset = 0x40c;
++ uint32_t maxLoaderThreads = 0;
++ BOOL memoryRead = ::ReadProcessMemory(
++ process, processParameters + loaderThreadsOffset, &maxLoaderThreads,
++ sizeof(maxLoaderThreads), &bytes_read);
++ if (memoryRead && (sizeof(maxLoaderThreads) == bytes_read) &&
++ (maxLoaderThreads == 0)) {
++ maxLoaderThreads = 1;
++ WriteProtectedChildMemory(process, processParameters + loaderThreadsOffset,
++ &maxLoaderThreads, sizeof(maxLoaderThreads),
++ PAGE_READWRITE);
++ }
++#endif
++
+ return base_address;
+ }
+
+ DWORD GetTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS info_class,
+ std::unique_ptr<BYTE[]>* buffer) {
+ // Get the required buffer size.
+ DWORD size = 0;
+diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.h b/security/sandbox/chromium/sandbox/win/src/win_utils.h
+--- a/security/sandbox/chromium/sandbox/win/src/win_utils.h
++++ b/security/sandbox/chromium/sandbox/win/src/win_utils.h
+@@ -111,17 +111,18 @@ HKEY GetReservedKeyFromName(const std::w
+ bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name);
+
+ // Writes |length| bytes from the provided |buffer| into the address space of
+ // |child_process|, at the specified |address|, preserving the original write
+ // protection attributes. Returns true on success.
+ bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+- size_t length);
++ size_t length,
++ DWORD writeProtection = PAGE_WRITECOPY);
+
+ // Allocates |buffer_bytes| in child (PAGE_READWRITE) and copies data
+ // from |local_buffer| in this process into |child|. |remote_buffer|
+ // contains the address in the chile. If a zero byte copy is
+ // requested |true| is returned and no allocation or copying is
+ // attempted. Returns false if allocation or copying fails. If
+ // copying fails, the allocation will be reversed.
+ bool CopyToChildMemory(HANDLE child,
diff --git a/security/sandbox/chromium-shim/patches/after_update/change_to_DCHECK_in_CloseHandleWrapper.patch b/security/sandbox/chromium-shim/patches/after_update/change_to_DCHECK_in_CloseHandleWrapper.patch
new file mode 100644
index 0000000000..3d6bfaa54f
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/change_to_DCHECK_in_CloseHandleWrapper.patch
@@ -0,0 +1,38 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1563194469 -3600
+# Mon Jul 15 13:41:09 2019 +0100
+# Node ID 6d4e1a08b36e4191bd5ba7a338965f42f09162a6
+# Parent 7d9b5d8c9b9b36b135237292785537fc13f40226
+Bug 1564899: Make CloseHandleWrapper CHECK a DCHECK on non-Nightly builds. r=handyman!
+
+This is because we are hitting it frequently during PolicyBase::OnJobEmpty and
+currently we can't work out how this can happen.
+
+diff --git a/security/sandbox/chromium/base/win/scoped_handle_verifier.cc b/security/sandbox/chromium/base/win/scoped_handle_verifier.cc
+--- a/security/sandbox/chromium/base/win/scoped_handle_verifier.cc
++++ b/security/sandbox/chromium/base/win/scoped_handle_verifier.cc
+@@ -65,17 +65,23 @@ ScopedHandleVerifier* ScopedHandleVerifi
+ if (!g_active_verifier)
+ ScopedHandleVerifier::InstallVerifier();
+
+ return g_active_verifier;
+ }
+
+ bool CloseHandleWrapper(HANDLE handle) {
+ if (!::CloseHandle(handle))
++ // Making this DCHECK on non-Nighly as we are hitting this frequently,
++ // looks like we are closing handles twice somehow. See bug 1564899.
++#if defined(NIGHTLY_BUILD)
+ CHECK(false); // CloseHandle failed.
++#else
++ DCHECK(false); // CloseHandle failed.
++#endif
+ return true;
+ }
+
+ // Assigns the g_active_verifier global within the GetLock() lock.
+ // If |existing_verifier| is non-null then |enabled| is ignored.
+ void ThreadSafeAssignOrCreateScopedHandleVerifier(
+ ScopedHandleVerifier* existing_verifier,
+ bool enabled) {
diff --git a/security/sandbox/chromium-shim/patches/after_update/linux_32bit_arg_fixup.patch b/security/sandbox/chromium-shim/patches/after_update/linux_32bit_arg_fixup.patch
new file mode 100644
index 0000000000..5cc66ad09b
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/linux_32bit_arg_fixup.patch
@@ -0,0 +1,84 @@
+commit e0a00891b67ec162a17aa241a83b171b313de9fe
+Author: Jed Davis <jld@mozilla.com>
+Date: Mon Apr 18 18:00:10 2022 -0600
+
+ Make the sandbox fix up non-extended 32-bit types.
+
+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 347304889eae4..b909fc37f6174 100644
+--- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc
++++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc
+@@ -19,6 +19,7 @@
+ #include "sandbox/linux/bpf_dsl/policy.h"
+ #include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+ #include "sandbox/linux/bpf_dsl/syscall_set.h"
++#include "sandbox/linux/seccomp-bpf/syscall.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"
+@@ -318,8 +319,7 @@ CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno,
+ // Special logic for sanity checking the upper 32-bits of 32-bit system
+ // call arguments.
+
+- // TODO(mdempsky): Compile Unexpected64bitArgument() just per program.
+- CodeGen::Node invalid_64bit = Unexpected64bitArgument();
++ CodeGen::Node invalid_64bit = Unexpected64bitArgument(argno);
+
+ const uint32_t upper = SECCOMP_ARG_MSB_IDX(argno);
+ const uint32_t lower = SECCOMP_ARG_LSB_IDX(argno);
+@@ -335,8 +335,13 @@ CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno,
+ BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
+ }
+
+- // On 64-bit platforms, the upper 32-bits may be 0 or ~0; but we only allow
+- // ~0 if the sign bit of the lower 32-bits is set too:
++ // On 64-bit platforms, the ABI (at least on x86_64) allows any value
++ // for the upper half, but to avoid potential vulnerabilties if an
++ // argument is incorrectly tested as a 32-bit type, we require it to be
++ // either zero-extended or sign-extended. That is, the upper 32-bits
++ // may be 0 or ~0; but we only allow ~0 if the sign bit of the lower
++ // 32-bits is set too:
++ //
+ // LDW [upper]
+ // JEQ 0, passed, (next)
+ // JEQ ~0, (next), invalid
+@@ -424,8 +429,18 @@ CodeGen::Node PolicyCompiler::MaskedEqualHalf(int argno,
+ BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
+ }
+
+-CodeGen::Node PolicyCompiler::Unexpected64bitArgument() {
+- return CompileResult(panic_func_("Unexpected 64bit argument detected"));
++CodeGen::Node PolicyCompiler::Unexpected64bitArgument(int argno) {
++ // This situation is unlikely, but possible. Return to userspace,
++ // zero-extend the problematic argument, and re-issue the syscall.
++ return CompileResult(bpf_dsl::Trap(
++ [](const arch_seccomp_data& args_ref, void* aux) -> intptr_t {
++ arch_seccomp_data args = args_ref;
++ int argno = (int)(intptr_t)aux;
++ args.args[argno] &= 0xFFFFFFFF;
++ return Syscall::Call(args.nr, args.args[0], args.args[1], args.args[2],
++ args.args[3], args.args[4], args.args[5]);
++ },
++ (void*)(intptr_t)argno));
+ }
+
+ CodeGen::Node PolicyCompiler::Return(uint32_t ret) {
+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 48b1d780d956f..2acf878474a7d 100644
+--- a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h
++++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h
+@@ -132,9 +132,11 @@ class SANDBOX_EXPORT PolicyCompiler {
+ CodeGen::Node passed,
+ CodeGen::Node failed);
+
+- // 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();
++ // Returns the CodeGen::Node that is used to handle the case where a
++ // system call argument was expected to be a 32-bit type, but the
++ // value in the 64-bit register doesn't correspond to a
++ // zero-extended or sign-extended 32-bit value.
++ CodeGen::Node Unexpected64bitArgument(int argno);
+
+ const Policy* policy_;
+ TrapRegistry* registry_;
diff --git a/security/sandbox/chromium-shim/patches/after_update/move_shared_memory_duplication_after_initialization.patch b/security/sandbox/chromium-shim/patches/after_update/move_shared_memory_duplication_after_initialization.patch
new file mode 100644
index 0000000000..f8250b788d
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/move_shared_memory_duplication_after_initialization.patch
@@ -0,0 +1,94 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1577387989 0
+# Thu Dec 26 19:19:49 2019 +0000
+# Node ID 32adf437117bdca54be4959813acbb604f65137f
+# Parent 214214029beb6cca606e11ba519d11cc7dbb37af
+Bug 1605867: Don't duplicate IPC shared memory when we might fail to launch the process correctly. r=handyman
+
+Differential Revision: https://phabricator.services.mozilla.com/D58271
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.cc b/security/sandbox/chromium/sandbox/win/src/target_process.cc
+--- a/security/sandbox/chromium/sandbox/win/src/target_process.cc
++++ b/security/sandbox/chromium/sandbox/win/src/target_process.cc
+@@ -286,45 +286,28 @@ ResultCode TargetProcess::Init(Dispatche
+ shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
+ PAGE_READWRITE | SEC_COMMIT, 0,
+ shared_mem_size, nullptr));
+ if (!shared_section_.IsValid()) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_CREATE_FILE_MAPPING;
+ }
+
+- 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(),
+- &target_shared_section, access, false, 0)) {
+- *win_error = ::GetLastError();
+- return SBOX_ERROR_DUPLICATE_SHARED_SECTION;
+- }
+-
+ void* shared_memory = ::MapViewOfFile(
+ shared_section_.Get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
+ if (!shared_memory) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_MAP_VIEW_OF_SHARED_SECTION;
+ }
+
+ CopyPolicyToTarget(policy, shared_policy_size,
+ reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
+
+ ResultCode ret;
+ // Set the global variables in the target. These are not used on the broker.
+- g_shared_section = target_shared_section;
+- ret = TransferVariable("g_shared_section", &g_shared_section,
+- sizeof(g_shared_section));
+- g_shared_section = nullptr;
+- if (SBOX_ALL_OK != ret) {
+- *win_error = ::GetLastError();
+- return ret;
+- }
+ g_shared_IPC_size = shared_IPC_size;
+ ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
+ sizeof(g_shared_IPC_size));
+ g_shared_IPC_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+@@ -339,16 +322,34 @@ ResultCode TargetProcess::Init(Dispatche
+
+ ipc_server_.reset(new SharedMemIPCServer(
+ sandbox_process_info_.process_handle(),
+ sandbox_process_info_.process_id(), thread_pool_, ipc_dispatcher));
+
+ if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
+ return SBOX_ERROR_NO_SPACE;
+
++ 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(),
++ &target_shared_section, access, false, 0)) {
++ *win_error = ::GetLastError();
++ return SBOX_ERROR_DUPLICATE_SHARED_SECTION;
++ }
++
++ g_shared_section = target_shared_section;
++ ret = TransferVariable("g_shared_section", &g_shared_section,
++ sizeof(g_shared_section));
++ g_shared_section = nullptr;
++ if (SBOX_ALL_OK != ret) {
++ *win_error = ::GetLastError();
++ return ret;
++ }
++
+ // After this point we cannot use this handle anymore.
+ ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
+
+ return SBOX_ALL_OK;
+ }
+
+ void TargetProcess::Terminate() {
+ if (!sandbox_process_info_.IsValid())
diff --git a/security/sandbox/chromium-shim/patches/after_update/patch_order.txt b/security/sandbox/chromium-shim/patches/after_update/patch_order.txt
new file mode 100644
index 0000000000..4266bee9c0
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/after_update/patch_order.txt
@@ -0,0 +1,8 @@
+add_interception_logging.patch
+allow_rules_for_network_drive_and_non_file_devices.patch
+add_WOW64_flags_to_allowed_registry_read_flags.patch
+arm64_set_LoaderThreads.patch
+change_to_DCHECK_in_CloseHandleWrapper.patch
+move_shared_memory_duplication_after_initialization.patch
+allow_ntpath_in_SignedPolicy_GenerateRules.patch
+linux_32bit_arg_fixup.patch
diff --git a/security/sandbox/chromium-shim/patches/with_update/aarch64_control_flow_guard.patch b/security/sandbox/chromium-shim/patches/with_update/aarch64_control_flow_guard.patch
new file mode 100644
index 0000000000..5a5c2f95de
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/aarch64_control_flow_guard.patch
@@ -0,0 +1,65 @@
+# HG changeset patch
+# User David Major <dmajor@mozilla.com>
+# Date 1560264749 -3600
+# Tue Jun 11 15:52:29 2019 +0100
+# Node ID 6acdba6bd34e773d5e2d6a8461e3679a33340f77
+# Parent a0adb2e7f668ed430948ae1ffaa42ec011ffde50
+Bug 1523526: Don't allow CFG on old releases of Windows for arm64
+
+There's a bug in ole32.dll on arm64 versions of Windows prior to 1809, that crashes our content processes if we enable CFG. We've reported the issue, but even if it gets fixed, we can't assume users will have the update.
+
+This patch uses process mitigation policy flags to disable CFG on arm64 before 1809. Based on testing, we only need to do this in the sandbox for child processes, and it's not strictly necessary for the launcher stub to set the flag on the main process. But I've included that anyway as a guard against some yet-undiscovered scenario that might hit the issue and make the browser unusable.
+
+The effects of this patch won't be visible until we actually enable CFG in a subsequent landing.
+
+Differential Revision: https://phabricator.services.mozilla.com/D29474
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+--- a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
++++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+@@ -431,16 +431,21 @@ void ConvertProcessMitigationsToPolicy(M
+
+ // Mitigations >= Win8.1:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN8_1) {
+ if (flags & MITIGATION_DYNAMIC_CODE_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON;
+ }
++
++ if (flags & MITIGATION_CONTROL_FLOW_GUARD_DISABLE) {
++ *policy_value_1 |=
++ PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF;
++ }
+ }
+
+ // Mitigations >= Win10:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10) {
+ if (flags & MITIGATION_NONSYSTEM_FONT_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON;
+diff --git a/security/sandbox/chromium/sandbox/win/src/security_level.h b/security/sandbox/chromium/sandbox/win/src/security_level.h
+--- a/security/sandbox/chromium/sandbox/win/src/security_level.h
++++ b/security/sandbox/chromium/sandbox/win/src/security_level.h
+@@ -282,11 +282,20 @@ const MitigationFlags MITIGATION_IMAGE_L
+ const MitigationFlags MITIGATION_IMAGE_LOAD_PREFER_SYS32 = 0x00100000;
+
+ // Prevents hyperthreads from interfering with indirect branch predictions.
+ // (SPECTRE Variant 2 mitigation.) Corresponds to
+ // PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON.
+ const MitigationFlags MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION =
+ 0x00200000;
+
++// Begin Mozilla-added flags.
++// Working down from the high bit to avoid conflict with new upstream flags.
++
++// Disable Control Flow Guard. This may seem more like an anti-mitigation, but
++// this flag allows code to make targeted changes to CFG to avoid bugs, while
++// leaving it enabled in the common case. Corresponds to
++// PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON.
++const MitigationFlags MITIGATION_CONTROL_FLOW_GUARD_DISABLE = 0x80000000;
++
+ } // namespace sandbox
+
+ #endif // SANDBOX_SRC_SECURITY_LEVEL_H_
diff --git a/security/sandbox/chromium-shim/patches/with_update/add_CET_STRICT_MODE.patch b/security/sandbox/chromium-shim/patches/with_update/add_CET_STRICT_MODE.patch
new file mode 100644
index 0000000000..fc0dee5f36
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/add_CET_STRICT_MODE.patch
@@ -0,0 +1,94 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1611849321 0
+# Thu Jan 28 15:55:21 2021 +0000
+# Node ID c9195d88e6c67ef2c23c12e307bc16b94d696f50
+# Parent 37557864a6845bb8068904e44e8a7dd16746d211
+Bug 1716024 p1: Add MITIGATION_CET_COMPAT_MODE to chromium sandbox code. r=handyman!
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+--- a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
++++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+@@ -80,16 +80,37 @@ bool IsRunning32bitEmulatedOnArm64() {
+ if (!retval)
+ return false;
+ if (native_machine == IMAGE_FILE_MACHINE_ARM64)
+ return true;
+ #endif // defined(ARCH_CPU_X86)
+ return false;
+ }
+
++// Returns true if user-mode Hardware-enforced Stack Protection is available for
++// the Win32 environment.
++bool IsUserCetWin32Available() {
++ static bool cetAvailable = []() -> bool {
++ using IsUserCetAvailableInEnvironmentFunction =
++ decltype(&IsUserCetAvailableInEnvironment);
++
++ IsUserCetAvailableInEnvironmentFunction is_user_cet_available =
++ reinterpret_cast<IsUserCetAvailableInEnvironmentFunction>(
++ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
++ "IsUserCetAvailableInEnvironment"));
++ if (!is_user_cet_available) {
++ return false;
++ }
++
++ return is_user_cet_available(USER_CET_ENVIRONMENT_WIN32_PROCESS);
++ }();
++
++ return cetAvailable;
++}
++
+ } // namespace
+
+ namespace sandbox {
+
+ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPostStartup(flags))
+ return false;
+
+@@ -487,16 +508,25 @@ void ConvertProcessMitigationsToPolicy(M
+ // the underlying hardware does not support the implementation.
+ // Windows just does its best under the hood for the given hardware.
+ if (flags & MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION) {
+ *policy_value_2 |=
+ PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON;
+ }
+ }
+
++ // Mitigations >= Win10 20H1
++ //----------------------------------------------------------------------------
++ if (version >= base::win::Version::WIN10_20H1) {
++ if (flags & MITIGATION_CET_COMPAT_MODE && IsUserCetWin32Available()) {
++ *policy_value_2 |=
++ PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON;
++ }
++ }
++
+ // When done setting policy flags, sanity check supported policies on this
+ // machine, and then update |size|.
+
+ const ULONG64* supported = GetSupportedMitigations();
+
+ *policy_value_1 = *policy_value_1 & supported[0];
+ *policy_value_2 = *policy_value_2 & supported[1];
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/security_level.h b/security/sandbox/chromium/sandbox/win/src/security_level.h
+--- a/security/sandbox/chromium/sandbox/win/src/security_level.h
++++ b/security/sandbox/chromium/sandbox/win/src/security_level.h
+@@ -286,11 +286,15 @@ const MitigationFlags MITIGATION_RESTRIC
+ // Working down from the high bit to avoid conflict with new upstream flags.
+
+ // Disable Control Flow Guard. This may seem more like an anti-mitigation, but
+ // this flag allows code to make targeted changes to CFG to avoid bugs, while
+ // leaving it enabled in the common case. Corresponds to
+ // PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON.
+ const MitigationFlags MITIGATION_CONTROL_FLOW_GUARD_DISABLE = 0x80000000;
+
++// This enables CET User Shadow Stack for compatible modules and corresponds to
++// PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON.
++const MitigationFlags MITIGATION_CET_COMPAT_MODE = 0x40000000;
++
+ } // namespace sandbox
+
+ #endif // SANDBOX_SRC_SECURITY_LEVEL_H_
diff --git a/security/sandbox/chromium-shim/patches/with_update/add_option_to_not_use_restricting_sids.patch b/security/sandbox/chromium-shim/patches/with_update/add_option_to_not_use_restricting_sids.patch
new file mode 100644
index 0000000000..fb12534687
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/add_option_to_not_use_restricting_sids.patch
@@ -0,0 +1,281 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1499762660 -3600
+# Tue Jul 11 09:44:20 2017 +0100
+# Node ID 4fb5bb81a2626a6262813bb556e2e059c2323562
+# Parent 45f3ef4037e040c820c0dd8eec6cff9d0745ae41
+Bug 1366701 - Add option to Windows chromium sandbox policy to not use restricting SIDs. r=jimm
+
+This originally landed in changeset:
+https://hg.mozilla.org/mozilla-central/rev/14374cd9497a
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
+--- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
++++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
+@@ -51,16 +51,17 @@ DWORD GetObjectSecurityDescriptor(HANDLE
+
+ } // namespace
+
+ DWORD CreateRestrictedToken(HANDLE effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
++ bool use_restricting_sids,
+ base::win::ScopedHandle* token) {
+ RestrictedToken restricted_token;
+ restricted_token.Init(effective_token);
+ if (lockdown_default_dacl)
+ restricted_token.SetLockdownDefaultDacl();
+
+ std::vector<std::wstring> privilege_exceptions;
+ std::vector<Sid> sid_exceptions;
+@@ -73,19 +74,22 @@ DWORD CreateRestrictedToken(HANDLE effec
+ deny_sids = false;
+ remove_privileges = false;
+ break;
+ }
+ case USER_RESTRICTED_SAME_ACCESS: {
+ deny_sids = false;
+ remove_privileges = false;
+
+- unsigned err_code = restricted_token.AddRestrictingSidAllSids();
+- if (ERROR_SUCCESS != err_code)
+- return err_code;
++ if (use_restricting_sids) {
++ unsigned err_code = restricted_token.AddRestrictingSidAllSids();
++ if (ERROR_SUCCESS != err_code) {
++ return err_code;
++ }
++ }
+
+ break;
+ }
+ case USER_NON_ADMIN: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+@@ -108,49 +112,57 @@ DWORD CreateRestrictedToken(HANDLE effec
+ break;
+ }
+ case USER_INTERACTIVE: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+- restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+- restricted_token.AddRestrictingSid(WinWorldSid);
+- restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+- restricted_token.AddRestrictingSidCurrentUser();
+- restricted_token.AddRestrictingSidLogonSession();
++ if (use_restricting_sids) {
++ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
++ restricted_token.AddRestrictingSid(WinWorldSid);
++ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
++ restricted_token.AddRestrictingSidCurrentUser();
++ restricted_token.AddRestrictingSidLogonSession();
++ }
+ break;
+ }
+ case USER_LIMITED: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+- restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+- restricted_token.AddRestrictingSid(WinWorldSid);
+- restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
++ if (use_restricting_sids) {
++ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
++ restricted_token.AddRestrictingSid(WinWorldSid);
++ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+
+- // This token has to be able to create objects in BNO.
+- // Unfortunately, on Vista+, it needs the current logon sid
+- // in the token to achieve this. You should also set the process to be
+- // low integrity level so it can't access object created by other
+- // processes.
+- restricted_token.AddRestrictingSidLogonSession();
++ // This token has to be able to create objects in BNO.
++ // Unfortunately, on Vista+, it needs the current logon sid
++ // in the token to achieve this. You should also set the process to be
++ // low integrity level so it can't access object created by other
++ // processes.
++ restricted_token.AddRestrictingSidLogonSession();
++ }
+ break;
+ }
+ case USER_RESTRICTED: {
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddUserSidForDenyOnly();
+- restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
++ if (use_restricting_sids) {
++ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
++ }
+ break;
+ }
+ case USER_LOCKDOWN: {
+ restricted_token.AddUserSidForDenyOnly();
+- restricted_token.AddRestrictingSid(WinNullSid);
++ if (use_restricting_sids) {
++ restricted_token.AddRestrictingSid(WinNullSid);
++ }
+ break;
+ }
+ default: { return ERROR_BAD_ARGUMENTS; }
+ }
+
+ DWORD err_code = ERROR_SUCCESS;
+ if (deny_sids) {
+ err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
+--- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
++++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
+@@ -33,16 +33,17 @@ enum TokenType { IMPERSONATION = 0, PRIM
+ // 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 effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
++ bool use_restricting_sids,
+ base::win::ScopedHandle* token);
+
+ // Sets the integrity label on a object handle.
+ DWORD SetObjectIntegrityLabel(HANDLE handle,
+ SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid);
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+@@ -101,16 +101,21 @@ class TargetPolicy {
+ virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0;
+
+ // Returns the initial token level.
+ virtual TokenLevel GetInitialTokenLevel() const = 0;
+
+ // Returns the lockdown token level.
+ virtual TokenLevel GetLockdownTokenLevel() const = 0;
+
++ // Sets that we should not use restricting SIDs in the access tokens. We need
++ // to do this in some circumstances even though it weakens the sandbox.
++ // The default is to use them.
++ virtual void SetDoNotUseRestrictingSIDs() = 0;
++
+ // Sets the security level of the Job Object to which the target process will
+ // belong. This setting is permanent and cannot be changed once the target
+ // process is spawned. The job controls the global security settings which
+ // can not be specified in the token security profile.
+ // job_level: the security level for the job. See the explanation of each
+ // level in the JobLevel definition.
+ // ui_exceptions: specify what specific rights that are disabled in the
+ // chosen job_level that need to be granted. Use this parameter to avoid
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+@@ -152,16 +152,20 @@ ResultCode PolicyBase::SetTokenLevel(Tok
+ TokenLevel PolicyBase::GetInitialTokenLevel() const {
+ return initial_level_;
+ }
+
+ TokenLevel PolicyBase::GetLockdownTokenLevel() const {
+ return lockdown_level_;
+ }
+
++void PolicyBase::SetDoNotUseRestrictingSIDs() {
++ use_restricting_sids_ = false;
++}
++
+ ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) {
+ if (memory_limit_ && job_level == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ job_level_ = job_level;
+ ui_exceptions_ = ui_exceptions;
+ return SBOX_ALL_OK;
+ }
+@@ -413,17 +417,18 @@ ResultCode PolicyBase::MakeJobObject(bas
+
+ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
+ base::win::ScopedHandle* lockdown,
+ base::win::ScopedHandle* lowbox) {
+ // 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(effective_token_, lockdown_level_, integrity_level_,
+- PRIMARY, lockdown_default_dacl_, lockdown);
++ PRIMARY, lockdown_default_dacl_,
++ use_restricting_sids_, lockdown);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN;
+
+ // If we're launching on the alternate desktop we need to make sure the
+ // integrity label on the object is no higher than the sandboxed process's
+ // integrity level. So, we lower the label on the desktop process if it's
+ // not already low enough for our process.
+ if (use_alternate_desktop_ && integrity_level_ != INTEGRITY_LEVEL_LAST) {
+@@ -482,17 +487,18 @@ ResultCode PolicyBase::MakeTokens(base::
+ }
+ }
+
+ // 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(effective_token_, initial_level_, integrity_level_,
+- IMPERSONATION, lockdown_default_dacl_, initial);
++ IMPERSONATION, lockdown_default_dacl_,
++ use_restricting_sids_, initial);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN;
+
+ return SBOX_ALL_OK;
+ }
+
+ PSID PolicyBase::GetLowBoxSid() const {
+ return lowbox_sid_;
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
+@@ -41,16 +41,17 @@ class PolicyBase final : public TargetPo
+ PolicyBase();
+
+ // TargetPolicy:
+ void AddRef() override;
+ void Release() override;
+ ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) override;
+ TokenLevel GetInitialTokenLevel() const override;
+ TokenLevel GetLockdownTokenLevel() const override;
++ void SetDoNotUseRestrictingSIDs() final;
+ ResultCode SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) override;
+ JobLevel GetJobLevel() const override;
+ ResultCode SetJobMemoryLimit(size_t memory_limit) override;
+ ResultCode SetAlternateDesktop(bool alternate_winstation) override;
+ std::wstring GetAlternateDesktop() const override;
+ ResultCode CreateAlternateDesktop(bool alternate_winstation) override;
+ void DestroyAlternateDesktop() override;
+ ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) override;
+@@ -134,16 +135,17 @@ class PolicyBase final : public TargetPo
+ // The policy takes ownership of them.
+ typedef std::list<TargetProcess*> TargetSet;
+ TargetSet targets_;
+ // Standard object-lifetime reference counter.
+ volatile LONG ref_count;
+ // The user-defined global policy settings.
+ TokenLevel lockdown_level_;
+ TokenLevel initial_level_;
++ bool use_restricting_sids_ = true;
+ JobLevel job_level_;
+ uint32_t ui_exceptions_;
+ size_t memory_limit_;
+ bool use_alternate_desktop_;
+ bool use_alternate_winstation_;
+ // Helps the file system policy initialization.
+ bool file_system_init_;
+ bool relaxed_interceptions_;
diff --git a/security/sandbox/chromium-shim/patches/with_update/add_return_in_QueryCancellationTraitsForNonCancellables_to_satisfy_build.patch b/security/sandbox/chromium-shim/patches/with_update/add_return_in_QueryCancellationTraitsForNonCancellables_to_satisfy_build.patch
new file mode 100644
index 0000000000..c5ee583b01
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/add_return_in_QueryCancellationTraitsForNonCancellables_to_satisfy_build.patch
@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1560260462 -3600
+# Tue Jun 11 14:41:02 2019 +0100
+# Node ID cb6cbf2c60077e833f472c82c1f437a794ac5266
+# Parent 71ac3df6aadbce233034b169646b66160c5944dc
+Bug 1552160: Add return after NOTREACHED() in QueryCancellationTraitsForNonCancellables to prevent build error. r=jld
+
+diff --git a/security/sandbox/chromium/base/callback_internal.cc b/security/sandbox/chromium/base/callback_internal.cc
+--- a/security/sandbox/chromium/base/callback_internal.cc
++++ b/security/sandbox/chromium/base/callback_internal.cc
+@@ -16,16 +16,17 @@ bool QueryCancellationTraitsForNonCancel
+ BindStateBase::CancellationQueryMode mode) {
+ switch (mode) {
+ case BindStateBase::IS_CANCELLED:
+ return false;
+ case BindStateBase::MAYBE_VALID:
+ return true;
+ }
+ NOTREACHED();
++ return false;
+ }
+
+ } // namespace
+
+ void BindStateBaseRefCountTraits::Destruct(const BindStateBase* bind_state) {
+ bind_state->destructor_(bind_state);
+ }
+
diff --git a/security/sandbox/chromium-shim/patches/with_update/add_support_for_random_restricted_SID.patch b/security/sandbox/chromium-shim/patches/with_update/add_support_for_random_restricted_SID.patch
new file mode 100644
index 0000000000..39f6b2538d
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/add_support_for_random_restricted_SID.patch
@@ -0,0 +1,461 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1584045580 0
+# Thu Mar 12 20:39:40 2020 +0000
+# Node ID c996dbc3e3663fb372feb8e171562e86b09583b6
+# Parent f96efa1d9f5c676c0ee8fd80044a494258eff3d3
+Bug 1557282 Part 1: Take chromium commit c1ce57ea5d31208af589b4839390a44ab20b0c8f. r=handyman,gcp
+
+This adds AddRestrictingRandomSid feature, which fixes our issues with
+SetLockdownDefaultDacl, apart from when we are running from a network drive.
+
+Differential Revision: https://phabricator.services.mozilla.com/D66610
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
+--- a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
++++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
+@@ -141,16 +141,24 @@ DWORD RestrictedToken::GetRestrictedToke
+ } else {
+ // Modify the default dacl on the token to contain Restricted.
+ if (!AddSidToDefaultDacl(new_token.Get(), WinRestrictedCodeSid,
+ GRANT_ACCESS, GENERIC_ALL)) {
+ return ::GetLastError();
+ }
+ }
+
++ for (const auto& default_dacl_sid : sids_for_default_dacl_) {
++ if (!AddSidToDefaultDacl(new_token.Get(), std::get<0>(default_dacl_sid),
++ std::get<1>(default_dacl_sid),
++ std::get<2>(default_dacl_sid))) {
++ return ::GetLastError();
++ }
++ }
++
+ // Add user to default dacl.
+ if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL))
+ return ::GetLastError();
+
+ DWORD error = SetTokenIntegrityLevel(new_token.Get(), integrity_level_);
+ if (ERROR_SUCCESS != error)
+ return error;
+
+@@ -405,9 +413,20 @@ DWORD RestrictedToken::SetIntegrityLevel
+ integrity_level_ = integrity_level;
+ return ERROR_SUCCESS;
+ }
+
+ void RestrictedToken::SetLockdownDefaultDacl() {
+ lockdown_default_dacl_ = true;
+ }
+
++DWORD RestrictedToken::AddDefaultDaclSid(const Sid& sid,
++ ACCESS_MODE access_mode,
++ ACCESS_MASK access) {
++ DCHECK(init_);
++ if (!init_)
++ return ERROR_NO_TOKEN;
++
++ sids_for_default_dacl_.push_back(std::make_tuple(sid, access_mode, access));
++ return ERROR_SUCCESS;
++}
++
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.h b/security/sandbox/chromium/sandbox/win/src/restricted_token.h
+--- a/security/sandbox/chromium/sandbox/win/src/restricted_token.h
++++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.h
+@@ -2,16 +2,17 @@
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+
+ #ifndef SANDBOX_SRC_RESTRICTED_TOKEN_H_
+ #define SANDBOX_SRC_RESTRICTED_TOKEN_H_
+
+ #include <windows.h>
+
++#include <tuple>
+ #include <vector>
+
+ #include <string>
+
+ #include "base/macros.h"
+ #include "base/win/scoped_handle.h"
+ #include "sandbox/win/src/restricted_token_utils.h"
+ #include "sandbox/win/src/security_level.h"
+@@ -169,23 +170,31 @@ class RestrictedToken {
+ // Sets the token integrity level. This is only valid on Vista. The integrity
+ // level cannot be higher than your current integrity level.
+ DWORD SetIntegrityLevel(IntegrityLevel integrity_level);
+
+ // Set a flag which indicates the created token should have a locked down
+ // default DACL when created.
+ void SetLockdownDefaultDacl();
+
++ // Add a SID to the default DACL. These SIDs are added regardless of the
++ // SetLockdownDefaultDacl state.
++ DWORD AddDefaultDaclSid(const Sid& sid,
++ ACCESS_MODE access_mode,
++ ACCESS_MASK access);
++
+ private:
+ // The list of restricting sids in the restricted token.
+ std::vector<Sid> sids_to_restrict_;
+ // The list of privileges to remove in the restricted token.
+ std::vector<LUID> privileges_to_disable_;
+ // The list of sids to mark as Deny Only in the restricted token.
+ std::vector<Sid> sids_for_deny_only_;
++ // The list of sids to add to the default DACL of the restricted token.
++ std::vector<std::tuple<Sid, ACCESS_MODE, ACCESS_MASK>> sids_for_default_dacl_;
+ // The token to restrict. Can only be set in a constructor.
+ 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)
+ bool init_;
+ // Lockdown the default DACL when creating new tokens.
+ bool lockdown_default_dacl_;
+diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
+--- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
++++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
+@@ -51,22 +51,29 @@ DWORD GetObjectSecurityDescriptor(HANDLE
+
+ } // namespace
+
+ DWORD CreateRestrictedToken(HANDLE effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
++ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token) {
+ RestrictedToken restricted_token;
+ restricted_token.Init(effective_token);
+ if (lockdown_default_dacl)
+ restricted_token.SetLockdownDefaultDacl();
++ if (unique_restricted_sid) {
++ restricted_token.AddDefaultDaclSid(Sid(unique_restricted_sid), GRANT_ACCESS,
++ GENERIC_ALL);
++ restricted_token.AddDefaultDaclSid(Sid(WinCreatorOwnerRightsSid),
++ GRANT_ACCESS, READ_CONTROL);
++ }
+
+ std::vector<std::wstring> privilege_exceptions;
+ std::vector<Sid> sid_exceptions;
+
+ bool deny_sids = true;
+ bool remove_privileges = true;
+
+ switch (security_level) {
+@@ -118,50 +125,60 @@ DWORD CreateRestrictedToken(HANDLE effec
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
++ if (unique_restricted_sid)
++ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LIMITED: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
++ if (unique_restricted_sid)
++ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+
+ // This token has to be able to create objects in BNO.
+ // Unfortunately, on Vista+, it needs the current logon sid
+ // in the token to achieve this. You should also set the process to be
+ // low integrity level so it can't access object created by other
+ // processes.
+ restricted_token.AddRestrictingSidLogonSession();
++ } else {
++ restricted_token.AddUserSidForDenyOnly();
+ }
+ break;
+ }
+ case USER_RESTRICTED: {
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
++ if (unique_restricted_sid)
++ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LOCKDOWN: {
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinNullSid);
++ if (unique_restricted_sid)
++ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ default: { return ERROR_BAD_ARGUMENTS; }
+ }
+
+ DWORD err_code = ERROR_SUCCESS;
+ if (deny_sids) {
+diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
+--- a/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
++++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
+@@ -33,16 +33,17 @@ enum TokenType { IMPERSONATION = 0, PRIM
+ // 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 effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
++ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token);
+
+ // Sets the integrity label on a object handle.
+ DWORD SetObjectIntegrityLabel(HANDLE handle,
+ SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid);
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+@@ -256,16 +256,20 @@ class TargetPolicy {
+ // ownership of the handle.
+ virtual void AddHandleToShare(HANDLE handle) = 0;
+
+ // Locks down the default DACL of the created lockdown and initial tokens
+ // to restrict what other processes are allowed to access a process' kernel
+ // resources.
+ virtual void SetLockdownDefaultDacl() = 0;
+
++ // Adds a restricting random SID to the restricted SIDs list as well as
++ // the default DACL.
++ virtual void AddRestrictingRandomSid() = 0;
++
+ // Enable OPM API redirection when in Win32k lockdown.
+ virtual void SetEnableOPMRedirection() = 0;
+ // Enable OPM API emulation when in Win32k lockdown.
+ virtual bool GetEnableOPMRedirection() = 0;
+
+ // Configure policy to use an AppContainer profile. |package_name| is the
+ // name of the profile to use. Specifying True for |create_profile| ensures
+ // the profile exists, if set to False process creation will fail if the
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+@@ -105,16 +105,17 @@ PolicyBase::PolicyBase()
+ delayed_integrity_level_(INTEGRITY_LEVEL_LAST),
+ mitigations_(0),
+ delayed_mitigations_(0),
+ is_csrss_connected_(true),
+ policy_maker_(nullptr),
+ policy_(nullptr),
+ lowbox_sid_(nullptr),
+ lockdown_default_dacl_(false),
++ add_restricting_random_sid_(false),
+ enable_opm_redirection_(false),
+ effective_token_(nullptr) {
+ ::InitializeCriticalSection(&lock_);
+ dispatcher_.reset(new TopLevelDispatcher(this));
+ }
+
+ PolicyBase::~PolicyBase() {
+ TargetSet::iterator it;
+@@ -389,16 +390,20 @@ void PolicyBase::AddHandleToShare(HANDLE
+
+ handles_to_share_.push_back(handle);
+ }
+
+ void PolicyBase::SetLockdownDefaultDacl() {
+ lockdown_default_dacl_ = true;
+ }
+
++void PolicyBase::AddRestrictingRandomSid() {
++ add_restricting_random_sid_ = true;
++}
++
+ const base::HandlesToInheritVector& PolicyBase::GetHandlesBeingShared() {
+ return handles_to_share_;
+ }
+
+ ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) {
+ if (job_level_ == JOB_NONE) {
+ job->Close();
+ return SBOX_ALL_OK;
+@@ -413,22 +418,26 @@ ResultCode PolicyBase::MakeJobObject(bas
+
+ *job = job_obj.Take();
+ return SBOX_ALL_OK;
+ }
+
+ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
+ base::win::ScopedHandle* lockdown,
+ base::win::ScopedHandle* lowbox) {
++ Sid random_sid = Sid::GenerateRandomSid();
++ PSID random_sid_ptr = nullptr;
++ if (add_restricting_random_sid_)
++ random_sid_ptr = random_sid.GetPSID();
++
+ // 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(effective_token_, lockdown_level_, integrity_level_,
+- PRIMARY, lockdown_default_dacl_,
+- use_restricting_sids_, lockdown);
++ DWORD result = CreateRestrictedToken(
++ effective_token_, lockdown_level_, integrity_level_, PRIMARY,
++ lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, lockdown);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN;
+
+ // If we're launching on the alternate desktop we need to make sure the
+ // integrity label on the object is no higher than the sandboxed process's
+ // integrity level. So, we lower the label on the desktop process if it's
+ // not already low enough for our process.
+ if (use_alternate_desktop_ && integrity_level_ != INTEGRITY_LEVEL_LAST) {
+@@ -485,20 +494,19 @@ ResultCode PolicyBase::MakeTokens(base::
+ TOKEN_ALL_ACCESS)) {
+ return SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL;
+ }
+ }
+
+ // 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(effective_token_, initial_level_, integrity_level_,
+- IMPERSONATION, lockdown_default_dacl_,
+- use_restricting_sids_, initial);
++ result = CreateRestrictedToken(
++ effective_token_, initial_level_, integrity_level_, IMPERSONATION,
++ lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, initial);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN;
+
+ return SBOX_ALL_OK;
+ }
+
+ PSID PolicyBase::GetLowBoxSid() const {
+ return lowbox_sid_;
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
+@@ -69,16 +69,17 @@ class PolicyBase final : public TargetPo
+ ResultCode AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) override;
+ ResultCode AddDllToUnload(const wchar_t* dll_name) override;
+ ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) override;
+ void AddHandleToShare(HANDLE handle) override;
+ void SetLockdownDefaultDacl() override;
++ void AddRestrictingRandomSid() override;
+ void SetEnableOPMRedirection() override;
+ bool GetEnableOPMRedirection() override;
+ ResultCode AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) override;
+ scoped_refptr<AppContainerProfile> GetAppContainerProfile() override;
+ void SetEffectiveToken(HANDLE token) override;
+
+ // Get the AppContainer profile as its internal type.
+@@ -165,16 +166,17 @@ class PolicyBase final : public TargetPo
+ // This is a map of handle-types to names that we need to close in the
+ // target process. A null set means we need to close all handles of the
+ // given type.
+ HandleCloser handle_closer_;
+ PSID lowbox_sid_;
+ base::win::ScopedHandle lowbox_directory_;
+ std::unique_ptr<Dispatcher> dispatcher_;
+ bool lockdown_default_dacl_;
++ bool add_restricting_random_sid_;
+
+ static HDESK alternate_desktop_handle_;
+ static HWINSTA alternate_winstation_handle_;
+ static HDESK alternate_desktop_local_winstation_handle_;
+ static IntegrityLevel alternate_desktop_integrity_level_label_;
+ static IntegrityLevel
+ alternate_desktop_local_winstation_integrity_level_label_;
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/sid.cc b/security/sandbox/chromium/sandbox/win/src/sid.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sid.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sid.cc
+@@ -2,18 +2,20 @@
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+
+ #include "sandbox/win/src/sid.h"
+
+ #include <memory>
+
+ #include <sddl.h>
++#include <stdlib.h>
+
+ #include "base/logging.h"
++#include "base/rand_util.h"
+ #include "base/win/windows_version.h"
+ #include "sandbox/win/src/win_utils.h"
+
+ namespace sandbox {
+
+ namespace {
+
+ DWORD WellKnownCapabilityToRid(WellKnownCapabilities capability) {
+@@ -127,16 +129,24 @@ Sid Sid::FromSubAuthorities(PSID_IDENTIF
+
+ Sid Sid::AllRestrictedApplicationPackages() {
+ SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_APP_PACKAGE_AUTHORITY};
+ DWORD sub_authorities[] = {SECURITY_APP_PACKAGE_BASE_RID,
+ SECURITY_BUILTIN_PACKAGE_ANY_RESTRICTED_PACKAGE};
+ return FromSubAuthorities(&package_authority, 2, sub_authorities);
+ }
+
++Sid Sid::GenerateRandomSid() {
++ SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_NULL_SID_AUTHORITY};
++ DWORD sub_authorities[4] = {};
++ base::RandBytes(&sub_authorities, sizeof(sub_authorities));
++ return FromSubAuthorities(&package_authority, _countof(sub_authorities),
++ sub_authorities);
++}
++
+ PSID Sid::GetPSID() const {
+ return const_cast<BYTE*>(sid_);
+ }
+
+ bool Sid::IsValid() const {
+ return !!::IsValidSid(GetPSID());
+ }
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/sid.h b/security/sandbox/chromium/sandbox/win/src/sid.h
+--- a/security/sandbox/chromium/sandbox/win/src/sid.h
++++ b/security/sandbox/chromium/sandbox/win/src/sid.h
+@@ -47,16 +47,18 @@ class Sid {
+ // Create a Sid from a SDDL format string, such as S-1-1-0.
+ static Sid FromSddlString(const wchar_t* sddl_sid);
+ // Create a Sid from a set of sub authorities.
+ static Sid FromSubAuthorities(PSID_IDENTIFIER_AUTHORITY identifier_authority,
+ BYTE sub_authority_count,
+ PDWORD sub_authorities);
+ // Create the restricted all application packages sid.
+ static Sid AllRestrictedApplicationPackages();
++ // Generate a random SID value.
++ static Sid GenerateRandomSid();
+
+ // Returns sid_.
+ PSID GetPSID() const;
+
+ // Gets whether the sid is valid.
+ bool IsValid() const;
+
+ // Converts the SID to a SDDL format string.
diff --git a/security/sandbox/chromium-shim/patches/with_update/allow_env_changes.patch b/security/sandbox/chromium-shim/patches/with_update/allow_env_changes.patch
new file mode 100644
index 0000000000..99fe5e99bc
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/allow_env_changes.patch
@@ -0,0 +1,217 @@
+# HG changeset patch
+# User Gian-Carlo Pascutto <gcp@mozilla.com>
+# Date 1515402436 -3600
+# Mon Jan 08 10:07:16 2018 +0100
+# Node ID 205e7ae2a6bc5ed1cdd1a982a12d99f52ce33258
+# Parent a89071894b4904a0130139a03147d4a6cb5c3bfc
+Bug 1297740.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+--- a/security/sandbox/chromium/sandbox/win/src/broker_services.cc
++++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+@@ -414,16 +414,17 @@ DWORD WINAPI BrokerServicesBase::TargetE
+ NOTREACHED();
+ return 0;
+ }
+
+ // SpawnTarget does all the interesting sandbox setup and creates the target
+ // process inside the sandbox.
+ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
++ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target_info) {
+ if (!exe_path)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!policy)
+@@ -609,17 +610,17 @@ ResultCode BrokerServicesBase::SpawnTarg
+ // Brokerservices does not own the target object. It is owned by the Policy.
+ base::win::ScopedProcessInformation process_info;
+ TargetProcess* target = new TargetProcess(
+ std::move(initial_token), std::move(lockdown_token), job.Get(),
+ thread_pool_.get(),
+ profile ? profile->GetImpersonationCapabilities() : std::vector<Sid>());
+
+ result = target->Create(exe_path, command_line, inherit_handles, startup_info,
+- &process_info, last_error);
++ &process_info, env_map, last_error);
+
+ if (result != SBOX_ALL_OK) {
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (lowbox_token.IsValid()) {
+ *last_warning = target->AssignLowBoxToken(lowbox_token);
+diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.h b/security/sandbox/chromium/sandbox/win/src/broker_services.h
+--- a/security/sandbox/chromium/sandbox/win/src/broker_services.h
++++ b/security/sandbox/chromium/sandbox/win/src/broker_services.h
+@@ -7,16 +7,17 @@
+
+ #include <list>
+ #include <map>
+ #include <memory>
+ #include <set>
+ #include <utility>
+
+ #include "base/compiler_specific.h"
++#include "base/environment.h"
+ #include "base/macros.h"
+ #include "base/memory/scoped_refptr.h"
+ #include "base/win/scoped_handle.h"
+ #include "sandbox/win/src/crosscall_server.h"
+ #include "sandbox/win/src/job.h"
+ #include "sandbox/win/src/sandbox.h"
+ #include "sandbox/win/src/sandbox_policy_base.h"
+ #include "sandbox/win/src/sharedmem_ipc_server.h"
+@@ -39,16 +40,17 @@ class BrokerServicesBase final : public
+
+ ~BrokerServicesBase();
+
+ // BrokerServices interface.
+ ResultCode Init() override;
+ scoped_refptr<TargetPolicy> CreatePolicy() override;
+ ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
++ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) override;
+ ResultCode WaitForAllTargets() override;
+ ResultCode AddTargetPeer(HANDLE peer_process) override;
+
+ // Checks if the supplied process ID matches one of the broker's active
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+@@ -84,16 +84,17 @@ class BrokerServices {
+ // parameter will hold the last Win32 error value.
+ // target: returns the resulting target process information such as process
+ // handle and PID just as if CreateProcess() had been called. The caller is
+ // responsible for closing the handles returned in this structure.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ virtual ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
++ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) = 0;
+
+ // This call blocks (waits) for all the targets to terminate.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.cc b/security/sandbox/chromium/sandbox/win/src/target_process.cc
+--- a/security/sandbox/chromium/sandbox/win/src/target_process.cc
++++ b/security/sandbox/chromium/sandbox/win/src/target_process.cc
+@@ -9,16 +9,17 @@
+
+ #include <memory>
+ #include <utility>
+ #include <vector>
+
+ #include "base/macros.h"
+ #include "base/memory/free_deleter.h"
+ #include "base/numerics/safe_conversions.h"
++#include "base/process/environment_internal.h"
+ #include "base/win/startup_information.h"
+ #include "base/win/windows_version.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/restricted_token_utils.h"
+ #include "sandbox/win/src/sandbox_types.h"
+ #include "sandbox/win/src/security_capabilities.h"
+@@ -137,16 +138,17 @@ TargetProcess::~TargetProcess() {
+ // Creates the target (child) process suspended and assigns it to the job
+ // object.
+ ResultCode TargetProcess::Create(
+ const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info,
++ base::EnvironmentMap& env_changes,
+ DWORD* win_error) {
+ exe_name_.reset(_wcsdup(exe_path));
+
+ // the command line needs to be writable by CreateProcess().
+ std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
+
+ // Start the target process suspended.
+ DWORD flags =
+@@ -156,22 +158,29 @@ ResultCode TargetProcess::Create(
+ flags |= EXTENDED_STARTUPINFO_PRESENT;
+
+ if (job_ && base::win::GetVersion() < base::win::Version::WIN8) {
+ // Windows 8 implements nested jobs, but for older systems we need to
+ // break out of any job we're in to enforce our restrictions.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
++ LPTCH original_environment = GetEnvironmentStrings();
++ base::NativeEnvironmentString new_environment =
++ base::internal::AlterEnvironment(original_environment, env_changes);
++ // Ignore return value? What can we do?
++ FreeEnvironmentStrings(original_environment);
++ LPVOID new_env_ptr = (void*)new_environment.data();
++
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(),
+ nullptr, // No security attribute.
+ nullptr, // No thread attribute.
+ inherit_handles, flags,
+- nullptr, // Use the environment of the caller.
++ new_env_ptr,
+ nullptr, // Use current directory of the caller.
+ startup_info.startup_info(),
+ &temp_process_info)) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_CREATE_PROCESS;
+ }
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.h b/security/sandbox/chromium/sandbox/win/src/target_process.h
+--- a/security/sandbox/chromium/sandbox/win/src/target_process.h
++++ b/security/sandbox/chromium/sandbox/win/src/target_process.h
+@@ -9,16 +9,17 @@
+
+ #include <stddef.h>
+ #include <stdint.h>
+
+ #include <memory>
+ #include <vector>
+
+ #include "base/macros.h"
++#include "base/environment.h"
+ #include "base/memory/free_deleter.h"
+ #include "base/win/scoped_handle.h"
+ #include "base/win/scoped_process_information.h"
+ #include "sandbox/win/src/crosscall_server.h"
+ #include "sandbox/win/src/sandbox_types.h"
+
+ namespace base {
+ namespace win {
+@@ -54,16 +55,17 @@ class TargetProcess {
+ void Release() {}
+
+ // Creates the new target process. The process is created suspended.
+ ResultCode Create(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info,
++ base::EnvironmentMap& env_map,
+ DWORD* win_error);
+
+ // Assign a new lowbox token to the process post creation. The process
+ // must still be in its initial suspended state, however this still
+ // might fail in the presence of third-party software.
+ ResultCode AssignLowBoxToken(const base::win::ScopedHandle& token);
+
+ // Destroys the target process.
diff --git a/security/sandbox/chromium-shim/patches/with_update/allow_read_only_all_paths_rule.patch b/security/sandbox/chromium-shim/patches/with_update/allow_read_only_all_paths_rule.patch
new file mode 100644
index 0000000000..b147e5f9fe
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/allow_read_only_all_paths_rule.patch
@@ -0,0 +1,142 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1490686576 -3600
+# Tue Mar 28 08:36:16 2017 +0100
+# Node ID 698d43688097e19ac64db71a094905035cac4891
+# Parent 96707276b26997ea2a8e9fd8fdacc0c863717e7b
+Allow a special all paths rule in the Windows process sandbox when using semantics FILES_ALLOW_READONLY. r=jimm
+
+This also changes the read only related status checks in filesystem_interception.cc
+to include STATUS_NETWORK_OPEN_RESTRICTION (0xC0000201), which gets returned in
+some cases and fails because we never ask the broker.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
+--- a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
++++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
+@@ -11,16 +11,20 @@
+ #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"
+
++// This status occurs when trying to access a network share on the machine from
++// which it is shared.
++#define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS)0xC0000201L)
++
+ namespace sandbox {
+
+ NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+@@ -29,17 +33,18 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCre
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_CreateFile(
+ file, desired_access, object_attributes, io_status, allocation_size,
+ file_attributes, sharing, disposition, options, ea_buffer, ea_length);
+- if (STATUS_ACCESS_DENIED != status)
++ if (STATUS_ACCESS_DENIED != status &&
++ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+@@ -106,17 +111,18 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenF
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes,
+ io_status, sharing, options);
+- if (STATUS_ACCESS_DENIED != status)
++ if (STATUS_ACCESS_DENIED != status &&
++ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+@@ -176,17 +182,18 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenF
+ }
+
+ NTSTATUS WINAPI
+ TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes);
+- if (STATUS_ACCESS_DENIED != status)
++ if (STATUS_ACCESS_DENIED != status &&
++ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE))
+@@ -232,17 +239,18 @@ TargetNtQueryAttributesFile(NtQueryAttri
+
+ NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryFullAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status =
+ orig_QueryFullAttributes(object_attributes, file_attributes);
+- if (STATUS_ACCESS_DENIED != status)
++ if (STATUS_ACCESS_DENIED != status &&
++ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION),
+diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
+--- a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
++++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
+@@ -77,17 +77,21 @@ namespace sandbox {
+ bool FileSystemPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+- if (!PreProcessName(&mod_name)) {
++ // Don't pre-process the path name and check for reparse points if it is the
++ // special case of allowing read access to all paths.
++ if (!(semantics == TargetPolicy::FILES_ALLOW_READONLY
++ && mod_name.compare(L"*") == 0)
++ && !PreProcessName(&mod_name)) {
+ // The path to be added might contain a reparse point.
+ NOTREACHED();
+ return false;
+ }
+
+ // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
+ // infrastructure to normalize names. In any case we need to escape the
+ // question marks.
diff --git a/security/sandbox/chromium-shim/patches/with_update/allow_reparse_points.patch b/security/sandbox/chromium-shim/patches/with_update/allow_reparse_points.patch
new file mode 100644
index 0000000000..e3645d2cd7
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/allow_reparse_points.patch
@@ -0,0 +1,186 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1631294898 -3600
+# Fri Sep 10 18:28:18 2021 +0100
+# Node ID adbc9b3051ab7f3c9360f65fe0fc26bd9d9dd499
+# Parent 004b5bea4e78db7ecd665173ce4cf6aa0a1af199
+Bug 1695556 p1: Allow reparse points in chromium sandbox code.
+
+Differential Revision: https://phabricator.services.mozilla.com/D135692
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
+--- a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
++++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
+@@ -87,17 +87,16 @@ bool FilesystemDispatcher::NtCreateFile(
+ std::wstring* 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;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ uint32_t broker = BROKER_TRUE;
+ CountedParameterSet<OpenFile> params;
+@@ -141,17 +140,16 @@ bool FilesystemDispatcher::NtCreateFile(
+
+ bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc,
+ std::wstring* 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;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ uint32_t broker = BROKER_TRUE;
+ uint32_t create_disposition = FILE_OPEN;
+@@ -196,17 +194,16 @@ bool FilesystemDispatcher::NtOpenFile(IP
+ bool FilesystemDispatcher::NtQueryAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info) {
+ if (sizeof(FILE_BASIC_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(name)) {
+- // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+@@ -245,17 +242,16 @@ bool FilesystemDispatcher::NtQueryAttrib
+ bool FilesystemDispatcher::NtQueryFullAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info) {
+ if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(name)) {
+- // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+@@ -307,17 +303,16 @@ bool FilesystemDispatcher::NtSetInformat
+
+ if (!IsSupportedRenameCall(rename_info, length, info_class))
+ return false;
+
+ std::wstring name;
+ name.assign(rename_info->FileName,
+ rename_info->FileNameLength / sizeof(rename_info->FileName[0]));
+ if (!PreProcessName(&name)) {
+- // The path requested might contain a reparse point.
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name.c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
+--- a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
++++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
+@@ -1,16 +1,17 @@
+ // 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/filesystem_policy.h"
+
+ #include <stdint.h>
+
++#include <algorithm>
+ #include <string>
+
+ #include "base/logging.h"
+ #include "base/stl_util.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"
+@@ -39,22 +40,16 @@ NTSTATUS NtCreateFileInTarget(HANDLE* ta
+ NTSTATUS status =
+ NtCreateFile(&local_handle, desired_access, obj_attributes,
+ io_status_block, nullptr, file_attributes, share_access,
+ create_disposition, create_options, ea_buffer, ea_length);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+- if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) {
+- // The handle points somewhere else. Fail the operation.
+- ::CloseHandle(local_handle);
+- return STATUS_ACCESS_DENIED;
+- }
+-
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_file_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+ }
+
+@@ -400,23 +395,32 @@ bool FileSystemPolicy::SetInformationFil
+ static_cast<FILE_INFORMATION_CLASS>(info_class);
+ *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length,
+ file_info_class);
+
+ return true;
+ }
+
+ bool PreProcessName(std::wstring* path) {
+- ConvertToLongPath(path);
++ // We now allow symbolic links to be opened via the broker, so we can no
++ // longer rely on the same object check where we checked the path of the
++ // opened file against the original. We don't specify a root when creating
++ // OBJECT_ATTRIBUTES from file names for brokering so they must be fully
++ // qualified and we can just check for the parent directory double dot between
++ // two backslashes. NtCreateFile doesn't seem to allow it anyway, but this is
++ // just an extra precaution. It also doesn't seem to allow the forward slash,
++ // but this is also used for checking policy rules, so we just replace forward
++ // slashes with backslashes.
++ std::replace(path->begin(), path->end(), L'/', L'\\');
++ if (path->find(L"\\..\\") != std::wstring::npos) {
++ return false;
++ }
+
+- if (ERROR_NOT_A_REPARSE_POINT == IsReparsePoint(*path))
+- return true;
+-
+- // We can't process a reparsed file.
+- return false;
++ ConvertToLongPath(path);
++ return true;
+ }
+
+ std::wstring FixNTPrefixForMatch(const std::wstring& name) {
+ std::wstring mod_name = name;
+
+ // NT prefix escaped for rule matcher
+ const wchar_t kNTPrefixEscaped[] = L"\\/?/?\\";
+ const int kNTPrefixEscapedLen = base::size(kNTPrefixEscaped) - 1;
diff --git a/security/sandbox/chromium-shim/patches/with_update/broker_complex_line_breaks.patch b/security/sandbox/chromium-shim/patches/with_update/broker_complex_line_breaks.patch
new file mode 100644
index 0000000000..4d350fa8bc
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/broker_complex_line_breaks.patch
@@ -0,0 +1,502 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1632737723 -3600
+# Mon Sep 27 11:15:23 2021 +0100
+# Node ID 096696bc1648dbacdfab881c4ed8fe770ebe58b1
+# Parent 254b1fc8768f67d208af199135276abae9aabc0c
+Bug 1713973 p2: Add Uniscribe Line Breaking via chromium-sandbox IPC. r=toshi!,r=jfkthame!
+
+This adds a new cross call using the chromium shared memory IPC to proxy use of
+the Uniscribe line breaker, because it cannot be used in the content process
+with win32k lockdown enabled.
+
+If the text being processed is too long to fit into the IPC params then it is
+processed in chunks.
+
+This change implements an INPTR_TYPE in the sandbox, which appears to have
+been removed at some point.
+It also fixes a bug in OpcodeFactory::MakeOpAction, so that a null param is
+passed and we can use an empty parameter set.
+
+New files are in chromium-shim as these are most likely to require changes and
+this means we will not have to update the main chromium patch.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_client.h b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
+--- a/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
++++ b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
+@@ -39,20 +39,16 @@
+ // interpretation of the answer is private to client and server.
+ //
+ // The return value is ALL_OK if the IPC was delivered to the server, other
+ // return codes indicate that the IPC transport failed to deliver it.
+ namespace sandbox {
+
+ enum class IpcTag;
+
+-// this is the assumed channel size. This can be overridden in a given
+-// IPC implementation.
+-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
+ // IPC. These template facility can be made more sophisticated as need arises.
+
+ // The default copy helper. It catches the general case where no other
+ // specialized template matches better. We set the type to UINT32_TYPE, so this
+ // only works with objects whose size is 32 bits.
+ template <typename T>
+@@ -207,16 +203,42 @@ class CopyHelper<const wchar_t[n]> : pub
+ // parameters.
+ class InOutCountedBuffer : public CountedBuffer {
+ public:
+ InOutCountedBuffer(void* buffer, uint32_t size)
+ : CountedBuffer(buffer, size) {}
+ };
+
+ // This copy helper template specialization catches the cases where the
++// parameter is a an input buffer.
++template <>
++class CopyHelper<CountedBuffer> {
++ public:
++ CopyHelper(const CountedBuffer t) : t_(t) {}
++
++ // Returns the pointer to the start of the string.
++ const void* GetStart() const { return t_.Buffer(); }
++
++ // Update not required so just return true;
++ bool Update(void* buffer) { return true; }
++
++ // Returns the size of the string in bytes. We define a nullptr string to
++ // be of zero length.
++ uint32_t GetSize() const { return t_.Size(); }
++
++ // Returns true if the current type is used as an In or InOut parameter.
++ bool IsInOut() { return false; }
++
++ ArgType GetType() { return INPTR_TYPE; }
++
++ private:
++ const CountedBuffer t_;
++};
++
++// This copy helper template specialization catches the cases where the
+ // parameter is a an input/output buffer.
+ template <>
+ class CopyHelper<InOutCountedBuffer> {
+ public:
+ CopyHelper(const InOutCountedBuffer t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_.Buffer(); }
+diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
+--- a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
++++ b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
+@@ -41,16 +41,20 @@
+ // them are not supported.
+ //
+ // Another limitation of CrossCall is that the return value and output
+ // parameters can only be uint32_t integers. Returning complex structures or
+ // strings is not supported.
+
+ namespace sandbox {
+
++// this is the assumed channel size. This can be overridden in a given
++// IPC implementation.
++const uint32_t kIPCChannelSize = 1024;
++
+ // This is the list of all imported symbols from ntdll.dll.
+ SANDBOX_INTERCEPT NtExports g_nt;
+
+ namespace {
+
+ // Increases |value| until there is no need for padding given an int64_t
+ // alignment. Returns the increased value.
+ inline uint32_t Align(uint32_t value) {
+@@ -216,16 +220,21 @@ class ActualCallParams : public CrossCal
+ // Testing-only constructor. Allows setting the |number_params| to a
+ // wrong value.
+ ActualCallParams(IpcTag tag, uint32_t number_params)
+ : CrossCallParams(tag, number_params) {
+ param_info_[0].offset_ =
+ static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
+ }
+
++ static constexpr size_t MaxParamsSize() {
++ return sizeof(
++ ActualCallParams<NUMBER_PARAMS, kIPCChannelSize>::parameters_);
++ }
++
+ // Testing-only method. Allows setting the apparent size to a wrong value.
+ // returns the previous size.
+ 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;
+ }
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
+--- a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
++++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
+@@ -301,17 +301,17 @@ bool CrossCallParamsEx::GetParameterStr(
+
+ bool CrossCallParamsEx::GetParameterPtr(uint32_t index,
+ uint32_t expected_size,
+ void** pointer) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+
+- if ((size != expected_size) || (INOUTPTR_TYPE != type))
++ if ((size != expected_size) || (INOUTPTR_TYPE != type && INPTR_TYPE != type))
+ return false;
+
+ if (!start)
+ return false;
+
+ *pointer = start;
+ return true;
+ }
+diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_args.cc b/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
+--- a/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
++++ b/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
+@@ -15,16 +15,17 @@ namespace sandbox {
+ void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) {
+ for (size_t i = 0; i < kMaxIpcParams; i++) {
+ switch (ipc_params->args[i]) {
+ case WCHAR_TYPE: {
+ delete reinterpret_cast<std::wstring*>(args[i]);
+ args[i] = nullptr;
+ break;
+ }
++ case INPTR_TYPE:
+ case INOUTPTR_TYPE: {
+ delete reinterpret_cast<CountedBuffer*>(args[i]);
+ args[i] = nullptr;
+ break;
+ }
+ default:
+ break;
+ }
+@@ -69,16 +70,17 @@ bool GetArgs(CrossCallParamsEx* params,
+ void* data;
+ if (!params->GetParameterVoidPtr(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data;
+ break;
+ }
++ case INPTR_TYPE:
+ case INOUTPTR_TYPE: {
+ if (!args[i]) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ CountedBuffer* buffer = new CountedBuffer(args[i], size);
+ args[i] = buffer;
+ break;
+diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_tags.h b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
+--- a/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
++++ b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
+@@ -41,16 +41,17 @@ enum class IpcTag {
+ GDI_GETCERTIFICATESIZE,
+ GDI_DESTROYOPMPROTECTEDOUTPUT,
+ GDI_CONFIGUREOPMPROTECTEDOUTPUT,
+ GDI_GETOPMINFORMATION,
+ GDI_GETOPMRANDOMNUMBER,
+ GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ NTCREATESECTION,
++ GETCOMPLEXLINEBREAKS,
+ LAST
+ };
+
+ constexpr size_t kMaxServiceCount = 64;
+ constexpr size_t kMaxIpcTag = static_cast<size_t>(IpcTag::LAST);
+ static_assert(kMaxIpcTag <= kMaxServiceCount, "kMaxServiceCount is too low");
+
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
+--- a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
++++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
+@@ -78,17 +78,17 @@ EvalResult OpcodeEval<OP_ALWAYS_TRUE>(Po
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Opcode OpAction:
+ // Does not require input parameter.
+ // Argument 0 contains the actual action to return.
+
+ PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, uint32_t options) {
+- PolicyOpcode* opcode = MakeBase(OP_ACTION, options, 0);
++ PolicyOpcode* opcode = MakeBase(OP_ACTION, options, -1);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, action);
+ return opcode;
+ }
+
+ template <>
+ EvalResult OpcodeEval<OP_ACTION>(PolicyOpcode* opcode,
+diff --git a/security/sandbox/chromium/sandbox/win/src/policy_params.h b/security/sandbox/chromium/sandbox/win/src/policy_params.h
+--- a/security/sandbox/chromium/sandbox/win/src/policy_params.h
++++ b/security/sandbox/chromium/sandbox/win/src/policy_params.h
+@@ -56,11 +56,15 @@ POLPARAMS_BEGIN(OpenKey)
+ POLPARAMS_END(OpenKey)
+
+ // Policy parameter for name-based policies.
+ POLPARAMS_BEGIN(HandleTarget)
+ POLPARAM(NAME)
+ POLPARAM(TARGET)
+ POLPARAMS_END(HandleTarget)
+
++// Policy parameters where no parameter based checks are done.
++POLPARAMS_BEGIN(EmptyParams)
++POLPARAMS_END(EmptyParams)
++
+ } // namespace sandbox
+
+ #endif // SANDBOX_SRC_POLICY_PARAMS_H__
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+@@ -176,16 +176,19 @@ class TargetServices {
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) = 0;
+
++ virtual ResultCode GetComplexLineBreaks(const WCHAR* text, uint32_t length,
++ uint8_t* break_before) = 0;
++
+ protected:
+ ~TargetServices() {}
+ };
+
+ class PolicyInfo {
+ public:
+ // Returns a JSON representation of the policy snapshot.
+ // This pointer has the same lifetime as this PolicyInfo object.
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+@@ -27,17 +27,18 @@ class TargetPolicy {
+ enum SubSystem {
+ SUBSYS_FILES, // Creation and opening of files and pipes.
+ SUBSYS_NAMED_PIPES, // Creation of named pipes.
+ SUBSYS_PROCESS, // Creation of child processes.
+ SUBSYS_REGISTRY, // Creation and opening of registry keys.
+ SUBSYS_SYNC, // Creation of named sync objects.
+ SUBSYS_HANDLES, // Duplication of handles to other processes.
+ SUBSYS_WIN32K_LOCKDOWN, // Win32K Lockdown related policy.
+- SUBSYS_SIGNED_BINARY // Signed binary policy.
++ SUBSYS_SIGNED_BINARY, // Signed binary policy.
++ SUBSYS_LINE_BREAK // Complex line break policy.
+ };
+
+ // Allowable semantics when a rule is matched.
+ enum Semantics {
+ FILES_ALLOW_ANY, // Allows open or create for any kind of access that
+ // the file system supports.
+ FILES_ALLOW_READONLY, // Allows open or create with read access only.
+ FILES_ALLOW_QUERY, // Allows access to query the attributes of a file.
+@@ -60,17 +61,18 @@ class TargetPolicy {
+ REG_ALLOW_READONLY, // Allows readonly access to a registry key.
+ REG_ALLOW_ANY, // Allows read and write access to a registry key.
+ FAKE_USER_GDI_INIT, // Fakes user32 and gdi32 initialization. This can
+ // be used to allow the DLLs to load and initialize
+ // even if the process cannot access that subsystem.
+ IMPLEMENT_OPM_APIS, // Implements FAKE_USER_GDI_INIT and also exposes
+ // IPC calls to handle Output Protection Manager
+ // APIs.
+- SIGNED_ALLOW_LOAD // Allows loading the module when CIG is enabled.
++ SIGNED_ALLOW_LOAD, // Allows loading the module when CIG is enabled.
++ LINE_BREAK_ALLOW // Allow complex line break brokering.
+ };
+
+ // Increments the reference count of this object. The reference count must
+ // be incremented if this interface is given to another component.
+ virtual void AddRef() = 0;
+
+ // Decrements the reference count of this object. When the reference count
+ // is zero the object is automatically destroyed.
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+@@ -15,16 +15,17 @@
+ #include "base/strings/stringprintf.h"
+ #include "base/win/win_util.h"
+ #include "base/win/windows_version.h"
+ #include "sandbox/win/src/acl.h"
+ #include "sandbox/win/src/filesystem_policy.h"
+ #include "sandbox/win/src/handle_policy.h"
+ #include "sandbox/win/src/interception.h"
+ #include "sandbox/win/src/job.h"
++#include "sandbox/win/src/line_break_policy.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.h"
+ #include "sandbox/win/src/process_mitigations_win32k_policy.h"
+ #include "sandbox/win/src/process_thread_policy.h"
+ #include "sandbox/win/src/registry_policy.h"
+@@ -809,16 +810,23 @@ ResultCode PolicyBase::AddRuleInternal(S
+ "policy rules.";
+ if (!SignedPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ }
+ break;
+ }
++ case SUBSYS_LINE_BREAK: {
++ if (!LineBreakPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
++ NOTREACHED();
++ return SBOX_ERROR_BAD_PARAMS;
++ }
++ break;
++ }
+
+ default: { return SBOX_ERROR_UNSUPPORTED; }
+ }
+
+ return SBOX_ALL_OK;
+ }
+
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc
+--- a/security/sandbox/chromium/sandbox/win/src/target_services.cc
++++ b/security/sandbox/chromium/sandbox/win/src/target_services.cc
+@@ -9,16 +9,17 @@
+ #include <process.h>
+ #include <stdint.h>
+
+ #include "base/win/windows_version.h"
+ #include "sandbox/win/src/crosscall_client.h"
+ #include "sandbox/win/src/handle_closer_agent.h"
+ #include "sandbox/win/src/handle_interception.h"
+ #include "sandbox/win/src/heap_helper.h"
++#include "sandbox/win/src/line_break_interception.h"
+ #include "sandbox/win/src/ipc_tags.h"
+ #include "sandbox/win/src/process_mitigations.h"
+ #include "sandbox/win/src/restricted_token_utils.h"
+ #include "sandbox/win/src/sandbox.h"
+ #include "sandbox/win/src/sandbox_nt_util.h"
+ #include "sandbox/win/src/sandbox_types.h"
+ #include "sandbox/win/src/sharedmem_ipc_client.h"
+
+@@ -240,19 +241,24 @@ void ProcessState::SetRevertedToSelf() {
+ if (process_state_ < ProcessStateInternal::REVERTED_TO_SELF)
+ process_state_ = ProcessStateInternal::REVERTED_TO_SELF;
+ }
+
+ void ProcessState::SetCsrssConnected(bool csrss_connected) {
+ csrss_connected_ = csrss_connected;
+ }
+
+-
+ ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ return sandbox::DuplicateHandleProxy(source_handle, target_process_id,
+ target_handle, desired_access, options);
+ }
+
++ResultCode TargetServicesBase::GetComplexLineBreaks(const WCHAR* text,
++ uint32_t length,
++ uint8_t* break_before) {
++ return sandbox::GetComplexLineBreaksProxy(text, length, break_before);
++}
++
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.h b/security/sandbox/chromium/sandbox/win/src/target_services.h
+--- a/security/sandbox/chromium/sandbox/win/src/target_services.h
++++ b/security/sandbox/chromium/sandbox/win/src/target_services.h
+@@ -45,16 +45,18 @@ class TargetServicesBase : public Target
+ 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;
++ ResultCode GetComplexLineBreaks(const WCHAR* text, uint32_t length,
++ uint8_t* break_before) final;
+
+ // Factory method.
+ static TargetServicesBase* GetInstance();
+
+ // Sends a simple IPC Message that has a well-known answer. Returns true
+ // if the IPC was successful and false otherwise. There are 2 versions of
+ // this test: 1 and 2. The first one send a simple message while the
+ // second one send a message with an in/out param.
+diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
+--- a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
++++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
+@@ -9,16 +9,17 @@
+
+ #include "base/logging.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/line_break_dispatcher.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/signed_dispatcher.h"
+ #include "sandbox/win/src/sync_dispatcher.h"
+
+@@ -90,16 +91,20 @@ TopLevelDispatcher::TopLevelDispatcher(P
+ IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(
+ IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS)] = dispatcher;
+ process_mitigations_win32k_dispatcher_.reset(dispatcher);
+
+ dispatcher = new SignedDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATESECTION)] = dispatcher;
+ signed_dispatcher_.reset(dispatcher);
++
++ dispatcher = new LineBreakDispatcher(policy_);
++ ipc_targets_[static_cast<size_t>(IpcTag::GETCOMPLEXLINEBREAKS)] = dispatcher;
++ line_break_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,
+diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h
+--- a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h
++++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h
+@@ -38,16 +38,17 @@ class TopLevelDispatcher : public Dispat
+ std::unique_ptr<Dispatcher> filesystem_dispatcher_;
+ std::unique_ptr<Dispatcher> named_pipe_dispatcher_;
+ std::unique_ptr<Dispatcher> thread_process_dispatcher_;
+ std::unique_ptr<Dispatcher> sync_dispatcher_;
+ std::unique_ptr<Dispatcher> registry_dispatcher_;
+ std::unique_ptr<Dispatcher> handle_dispatcher_;
+ std::unique_ptr<Dispatcher> process_mitigations_win32k_dispatcher_;
+ std::unique_ptr<Dispatcher> signed_dispatcher_;
++ std::unique_ptr<Dispatcher> line_break_dispatcher_;
+ Dispatcher* ipc_targets_[kMaxIpcTag];
+
+ DISALLOW_COPY_AND_ASSIGN(TopLevelDispatcher);
+ };
+
+ } // namespace sandbox
+
+ #endif // SANDBOX_SRC_TOP_LEVEL_DISPATCHER_H__
diff --git a/security/sandbox/chromium-shim/patches/with_update/derive_sid_from_name.patch b/security/sandbox/chromium-shim/patches/with_update/derive_sid_from_name.patch
new file mode 100644
index 0000000000..e798262861
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/derive_sid_from_name.patch
@@ -0,0 +1,74 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1677499923 0
+# Mon Feb 27 12:12:03 2023 +0000
+Expose Sid::FromNamedCapability through broker services.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+--- a/security/sandbox/chromium/sandbox/win/src/broker_services.cc
++++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+@@ -730,9 +730,16 @@ ResultCode BrokerServicesBase::GetPolicy
+ return SBOX_ERROR_GENERIC;
+ }
+
+ // Ownership has passed to tracker thread.
+ receiver.release();
+ return SBOX_ALL_OK;
+ }
+
++bool BrokerServicesBase::DeriveCapabilitySidFromName(const wchar_t* name,
++ PSID derived_sid,
++ DWORD sid_buffer_length) {
++ return ::CopySid(sid_buffer_length, derived_sid,
++ Sid::FromNamedCapability(name).GetPSID());
++}
++
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.h b/security/sandbox/chromium/sandbox/win/src/broker_services.h
+--- a/security/sandbox/chromium/sandbox/win/src/broker_services.h
++++ b/security/sandbox/chromium/sandbox/win/src/broker_services.h
+@@ -57,16 +57,19 @@ class BrokerServicesBase final : public
+ // target processes. We use this method for the specific purpose of
+ // checking if we can safely duplicate a handle to the supplied process
+ // in DuplicateHandleProxyAction.
+ bool IsSafeDuplicationTarget(DWORD process_id);
+
+ ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) override;
+
++ bool DeriveCapabilitySidFromName(const wchar_t* name, PSID derived_sid,
++ DWORD sid_buffer_length) override;
++
+ private:
+ // The routine that the worker thread executes. It is in charge of
+ // notifications and cleanup-related tasks.
+ static DWORD WINAPI TargetEventsThread(PVOID param);
+
+ // The completion port used by the job objects to communicate events to
+ // the worker thread.
+ base::win::ScopedHandle job_port_;
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+@@ -117,16 +117,21 @@ class BrokerServices {
+ // called to accept the results of the call.
+ // Returns:
+ // ALL_OK if the request was dispatched. All other return values
+ // imply failure, and the responder will not receive its completion
+ // callback.
+ virtual ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) = 0;
+
++ // Derive a capability PSID from the given string.
++ virtual bool DeriveCapabilitySidFromName(const wchar_t* name,
++ PSID derived_sid,
++ DWORD sid_buffer_length) = 0;
++
+ protected:
+ ~BrokerServices() {}
+ };
+
+ // TargetServices models the current process from the perspective
+ // of a target process. To obtain a pointer to it use
+ // Sandbox::GetTargetServices(). Note that this call returns a non-null
+ // pointer only if this process is in fact a target. A process is a target
diff --git a/security/sandbox/chromium-shim/patches/with_update/ifdef_out_AppContainerProfileBase_testing_functions.patch b/security/sandbox/chromium-shim/patches/with_update/ifdef_out_AppContainerProfileBase_testing_functions.patch
new file mode 100644
index 0000000000..325d23cc19
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/ifdef_out_AppContainerProfileBase_testing_functions.patch
@@ -0,0 +1,79 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1560259052 -3600
+# Tue Jun 11 14:17:32 2019 +0100
+# Node ID ca1bafe49015cb6625648274f32959e4160a6ce9
+# Parent 3ec022faaf83642e3c1894d83ff99926bada990c
+Hash if out testing functions that cause dependency creep. r=aklotz
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
+--- a/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
++++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
+@@ -3,17 +3,19 @@
+ // found in the LICENSE file.
+
+ #include <memory>
+
+ #include <aclapi.h>
+ #include <userenv.h>
+
+ #include "base/strings/stringprintf.h"
++#if !defined(MOZ_SANDBOX)
+ #include "base/win/scoped_co_mem.h"
++#endif
+ #include "base/win/scoped_handle.h"
+ #include "sandbox/win/src/app_container_profile_base.h"
+ #include "sandbox/win/src/restricted_token_utils.h"
+ #include "sandbox/win/src/win_utils.h"
+
+ namespace sandbox {
+
+ namespace {
+@@ -167,39 +169,47 @@ bool AppContainerProfileBase::GetRegistr
+ HKEY key_handle;
+ if (FAILED(get_app_container_registry_location(desired_access, &key_handle)))
+ return false;
+ key->Set(key_handle);
+ return true;
+ }
+
+ bool AppContainerProfileBase::GetFolderPath(base::FilePath* file_path) {
++#if defined(MOZ_SANDBOX)
++ IMMEDIATE_CRASH();
++#else
+ static GetAppContainerFolderPathFunc* get_app_container_folder_path =
+ reinterpret_cast<GetAppContainerFolderPathFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "GetAppContainerFolderPath"));
+ if (!get_app_container_folder_path)
+ return false;
+ std::wstring sddl_str;
+ if (!package_sid_.ToSddlString(&sddl_str))
+ return false;
+ base::win::ScopedCoMem<wchar_t> path_str;
+ if (FAILED(get_app_container_folder_path(sddl_str.c_str(), &path_str)))
+ return false;
+ *file_path = base::FilePath(path_str.get());
+ return true;
++#endif
+ }
+
+ bool AppContainerProfileBase::GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) {
++#if defined(MOZ_SANDBOX)
++ IMMEDIATE_CRASH();
++#else
+ std::wstring sddl_str;
+ if (!package_sid_.ToSddlString(&sddl_str))
+ return false;
+ *pipe_path = base::FilePath(base::StringPrintf(L"\\\\.\\pipe\\%ls\\%ls",
+ sddl_str.c_str(), pipe_name));
+ return true;
++#endif
+ }
+
+ bool AppContainerProfileBase::AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) {
+ GENERIC_MAPPING generic_mapping;
diff --git a/security/sandbox/chromium-shim/patches/with_update/ifdef_out_FromStringInternal.patch b/security/sandbox/chromium-shim/patches/with_update/ifdef_out_FromStringInternal.patch
new file mode 100644
index 0000000000..411d896570
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/ifdef_out_FromStringInternal.patch
@@ -0,0 +1,52 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1509027042 -3600
+# Thu Oct 26 15:10:42 2017 +0100
+# Node ID 34b1e1189bcbb3b8ecbfc4c9decc1c6dfc46c1e6
+# Parent c3dc5b64a97fe0526ab8826bdcb47740592472b7
+Don't compile base::Time::FromStringInternal. r=aklotz
+
+This has a dependency on nspr, which causes issues.
+
+Originally landed in changeset:
+https://hg.mozilla.org/mozilla-central/rev/477b991bf6fa7b4511768649c9bf37c7275d30d9
+
+diff --git a/security/sandbox/chromium/base/time/time.cc b/security/sandbox/chromium/base/time/time.cc
+--- a/security/sandbox/chromium/base/time/time.cc
++++ b/security/sandbox/chromium/base/time/time.cc
+@@ -281,16 +281,17 @@ Time Time::Midnight(bool is_local) const
+ if (FromExploded(is_local, exploded, &out_time))
+ return out_time;
+ }
+ // This function must not fail.
+ NOTREACHED();
+ return Time();
+ }
+
++#if !defined(MOZ_SANDBOX)
+ // static
+ bool Time::FromStringInternal(const char* time_string,
+ bool is_local,
+ Time* parsed_time) {
+ DCHECK((time_string != nullptr) && (parsed_time != nullptr));
+
+ if (time_string[0] == '\0')
+ return false;
+@@ -301,16 +302,17 @@ bool Time::FromStringInternal(const char
+ &result_time);
+ if (PR_SUCCESS != result)
+ return false;
+
+ result_time += kTimeTToMicrosecondsOffset;
+ *parsed_time = Time(result_time);
+ return true;
+ }
++#endif
+
+ // static
+ bool Time::ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs) {
+ return lhs.year == rhs.year && lhs.month == rhs.month &&
+ lhs.day_of_month == rhs.day_of_month && lhs.hour == rhs.hour &&
+ lhs.minute == rhs.minute && lhs.second == rhs.second &&
+ lhs.millisecond == rhs.millisecond;
+ }
diff --git a/security/sandbox/chromium-shim/patches/with_update/ifdef_out_SequenceChecker_code.patch b/security/sandbox/chromium-shim/patches/with_update/ifdef_out_SequenceChecker_code.patch
new file mode 100644
index 0000000000..62216e9af7
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/ifdef_out_SequenceChecker_code.patch
@@ -0,0 +1,36 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1509027043 -3600
+# Thu Oct 26 15:10:43 2017 +0100
+# Node ID cbe274e5b95c1c207597a0fbb4a80905d6d4dacc
+# Parent bbbba04e693f3819bcb6dd70ea27d3cab194e4cb
+This removes sequence checking on RefCountedBase in DEBUG builds. r=aklotz
+
+We don't currently make use of it and it brings in many dependencies.
+
+diff --git a/security/sandbox/chromium/base/memory/ref_counted.cc b/security/sandbox/chromium/base/memory/ref_counted.cc
+--- a/security/sandbox/chromium/base/memory/ref_counted.cc
++++ b/security/sandbox/chromium/base/memory/ref_counted.cc
+@@ -53,18 +53,22 @@ bool RefCountedThreadSafeBase::Release()
+ }
+ void RefCountedThreadSafeBase::AddRefWithCheck() const {
+ AddRefWithCheckImpl();
+ }
+ #endif
+
+ #if DCHECK_IS_ON()
+ bool RefCountedBase::CalledOnValidSequence() const {
++#if defined(MOZ_SANDBOX)
++ return true;
++#else
+ return sequence_checker_.CalledOnValidSequence() ||
+ g_cross_thread_ref_count_access_allow_count.load() != 0;
++#endif
+ }
+ #endif
+
+ } // namespace subtle
+
+ #if DCHECK_IS_ON()
+ ScopedAllowCrossThreadRefCountAccess::ScopedAllowCrossThreadRefCountAccess() {
+ ++g_cross_thread_ref_count_access_allow_count;
diff --git a/security/sandbox/chromium-shim/patches/with_update/include_atomic_header_in_platform_thread.patch b/security/sandbox/chromium-shim/patches/with_update/include_atomic_header_in_platform_thread.patch
new file mode 100644
index 0000000000..e088e99680
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/include_atomic_header_in_platform_thread.patch
@@ -0,0 +1,27 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1560260570 -3600
+# Tue Jun 11 14:42:50 2019 +0100
+# Node ID 7baa38185938e45ab128ec3975ae139753c8ad67
+# Parent cb568f9b29f8c2c84c72c49b7a565d8081929f04
+Bug 1552160: Fix missing atomic include in chromium platform_thread.cc. r=jld
+
+diff --git a/security/sandbox/chromium/base/threading/platform_thread.cc b/security/sandbox/chromium/base/threading/platform_thread.cc
+--- a/security/sandbox/chromium/base/threading/platform_thread.cc
++++ b/security/sandbox/chromium/base/threading/platform_thread.cc
+@@ -1,14 +1,15 @@
+ // Copyright 2018 The Chromium Authors. 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.h"
+
++#include <atomic>
+ #include <memory>
+
+ #include "base/feature_list.h"
+
+ namespace base {
+
+ namespace {
+
diff --git a/security/sandbox/chromium-shim/patches/with_update/lower_SDK_version_requirement.patch b/security/sandbox/chromium-shim/patches/with_update/lower_SDK_version_requirement.patch
new file mode 100644
index 0000000000..185e128d83
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/lower_SDK_version_requirement.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1588735588 25200
+# Tue May 05 20:26:28 2020 -0700
+# Node ID 8214c0253f550d73b5e79dfd825b09f5c1a06fbd
+# Parent 2d5ee142bde533ba4f93afaae081a444eac0abe2
+Lower SDK version requirement from 19H1 to RS4. r=bobowen
+
+We still use 10.0.17134.0 SDK while Chromium requires 10.0.18362.0 or higher.
+
+diff --git a/security/sandbox/chromium/base/win/windows_version.cc b/security/sandbox/chromium/base/win/windows_version.cc
+--- a/security/sandbox/chromium/base/win/windows_version.cc
++++ b/security/sandbox/chromium/base/win/windows_version.cc
+@@ -17,18 +17,18 @@
+ #include "base/strings/string_util.h"
+ #include "base/strings/utf_string_conversions.h"
+ #include "base/win/registry.h"
+
+ #if !defined(__clang__) && _MSC_FULL_VER < 191125507
+ #error VS 2017 Update 3.2 or higher is required
+ #endif
+
+-#if !defined(NTDDI_WIN10_19H1)
+-#error Windows 10.0.18362.0 SDK or higher required.
++#if !defined(NTDDI_WIN10_RS4)
++#error Windows 10.0.17134.0 SDK or higher required.
+ #endif
+
+ namespace base {
+ namespace win {
+
+ namespace {
+
+ // The values under the CurrentVersion registry hive are mirrored under
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_capitalization.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_capitalization.patch
new file mode 100644
index 0000000000..0c27032307
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_capitalization.patch
@@ -0,0 +1,74 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1516825559 21600
+# Wed Jan 24 14:25:59 2018 -0600
+# Node ID 9ce534c9f572dfb5abd3e409d9cfec069ccee0cd
+# Parent 6413cb580dccd986c61e6dbdc72fc370765b8f10
+Bug 1431797 Correct the capitalization of headers inside the chromium code so MinGW can compile
+
+diff --git a/security/sandbox/chromium/base/rand_util_win.cc b/security/sandbox/chromium/base/rand_util_win.cc
+--- a/security/sandbox/chromium/base/rand_util_win.cc
++++ b/security/sandbox/chromium/base/rand_util_win.cc
+@@ -7,17 +7,17 @@
+ #include <windows.h>
+ #include <stddef.h>
+ #include <stdint.h>
+
+ // #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 <NTSecAPI.h>
++#include <ntsecapi.h>
+ #undef SystemFunction036
+
+ #include <algorithm>
+ #include <limits>
+
+ #include "base/logging.h"
+
+ namespace base {
+diff --git a/security/sandbox/chromium/base/win/pe_image.h b/security/sandbox/chromium/base/win/pe_image.h
+--- a/security/sandbox/chromium/base/win/pe_image.h
++++ b/security/sandbox/chromium/base/win/pe_image.h
+@@ -14,17 +14,17 @@
+ #include <windows.h>
+
+ #include <stdint.h>
+
+ #if defined(_WIN32_WINNT_WIN8)
+ // The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h.
+ #undef FACILITY_VISUALCPP
+ #endif
+-#include <DelayIMP.h>
++#include <delayimp.h>
+
+ namespace base {
+ namespace win {
+
+ // This class is a wrapper for the Portable Executable File Format (PE).
+ // Its main purpose is to provide an easy way to work with imports and exports
+ // from a file, mapped in memory as image.
+ class PEImage {
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc
+@@ -5,17 +5,17 @@
+ #include "sandbox/win/src/sandbox_rand.h"
+
+ #include <windows.h>
+
+ // #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 <NTSecAPI.h>
++#include <ntsecapi.h>
+ #undef SystemFunction036
+
+ namespace sandbox {
+
+ bool GetRandom(unsigned int* random_value) {
+ return RtlGenRandom(random_value, sizeof(unsigned int)) != false;
+ }
+
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_cast_getprocaddress.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_cast_getprocaddress.patch
new file mode 100644
index 0000000000..1251be114f
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_cast_getprocaddress.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1516720544 21600
+# Tue Jan 23 09:15:44 2018 -0600
+# Node ID 2b4556cb7407c196522e52cfd286ee88c3bb6e72
+# Parent 60aa47b111918d4e30f7e363359d1dcc3a3f277d
+Bug 1432295 Cast GetProcAddress to (void*) r?bobowen
+
+error: invalid conversion from 'FARPROC {aka int (__attribute__((__stdcall__)) *)()}' to 'void*' [-fpermissive]
+
+According to http://stackoverflow.com/questions/13958081/, msvc does the fixup
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.cc b/security/sandbox/chromium/sandbox/win/src/target_process.cc
+--- a/security/sandbox/chromium/sandbox/win/src/target_process.cc
++++ b/security/sandbox/chromium/sandbox/win/src/target_process.cc
+@@ -231,17 +231,17 @@ ResultCode TargetProcess::TransferVariab
+
+ void* child_var = address;
+
+ #if SANDBOX_EXPORTS
+ HMODULE module = ::LoadLibrary(exe_name_.get());
+ if (!module)
+ return SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE;
+
+- child_var = ::GetProcAddress(module, name);
++ child_var = reinterpret_cast<void*>(::GetProcAddress(module, name));
+ ::FreeLibrary(module);
+
+ if (!child_var)
+ return SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS;
+
+ size_t offset =
+ reinterpret_cast<char*>(child_var) - reinterpret_cast<char*>(module);
+ child_var = reinterpret_cast<char*>(MainModule()) + offset;
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_copy_s.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_copy_s.patch
new file mode 100644
index 0000000000..12e62e8b15
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_copy_s.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1516394893 21600
+# Fri Jan 19 14:48:13 2018 -0600
+# Node ID bd0817bb5b0c5681c4c49817363e6ddd6efac82c
+# Parent c64ea5b2e26b203eff2f0b9d85fef99ae3a094f9
+Bug 1431825 Map _Copy_s to copy for basic_string compatibility on MinGW r?bobowen
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_types.h b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_types.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h
+@@ -5,16 +5,22 @@
+ #ifndef SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+ #define SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+
+ #include "base/process/kill.h"
+ #include "base/process/launch.h"
+
+ namespace sandbox {
+
++#ifdef __MINGW32__
++// Map Microsoft's proprietary more-safe version of copy() back to
++// the std::basic_string method
++#define _Copy_s copy
++#endif
++
+ // Operation result codes returned by the sandbox API.
+ //
+ // Note: These codes are listed in a histogram and any new codes should be added
+ // at the end. If the underlying type is changed then the forward declaration in
+ // sandbox_init.h must be updated.
+ //
+ enum ResultCode : int {
+ SBOX_ALL_OK = 0,
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_disable_one_try.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_disable_one_try.patch
new file mode 100644
index 0000000000..d5b700ea8f
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_disable_one_try.patch
@@ -0,0 +1,51 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1516389982 21600
+# Fri Jan 19 13:26:22 2018 -0600
+# Node ID 3ca7306d73ebc1ce47ccdc62ee8cbb69a9bfbb2c
+# Parent 6aa6c7d894609140ccde2e9e50eba8c25a9caeb5
+Bug 1431803 Disable a specific __try block on MinGW r?bobowen
+
+This function is a technique to name a thread for debugging purposes,
+and it always throws an exception (and then continues). On MinGW
+we don't want it to throw an exception, so we do nothing.
+
+This means on MinGW we won't get nice thread naming during debugging,
+but we'll limp along.
+
+MozReview-Commit-ID: JRKY4wp7sdu
+
+diff --git a/security/sandbox/chromium/base/threading/platform_thread_win.cc b/security/sandbox/chromium/base/threading/platform_thread_win.cc
+--- a/security/sandbox/chromium/base/threading/platform_thread_win.cc
++++ b/security/sandbox/chromium/base/threading/platform_thread_win.cc
+@@ -32,27 +32,30 @@ typedef struct tagTHREADNAME_INFO {
+ } THREADNAME_INFO;
+
+ // The SetThreadDescription API was brought in version 1607 of Windows 10.
+ typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
+ PCWSTR lpThreadDescription);
+
+ // This function has try handling, so it is separated out of its caller.
+ void SetNameInternal(PlatformThreadId thread_id, const char* name) {
++ //This function is only used for debugging purposes, as you can find by its caller
++#ifndef __MINGW32__
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = thread_id;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
+ reinterpret_cast<DWORD_PTR*>(&info));
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
++#endif
+ }
+
+ struct ThreadParams {
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+ ThreadPriority priority;
+ };
+
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_missing_windows_types_defines.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_missing_windows_types_defines.patch
new file mode 100644
index 0000000000..30220a0660
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_missing_windows_types_defines.patch
@@ -0,0 +1,37 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1558294860 -3600
+# Sun May 19 20:41:00 2019 +0100
+# Node ID 331daa6926b2d495959a0aebbf034958a9bc1e2a
+# Parent e71e4e7a914c2d2515bf84be6ad045febabb6dfc
+Bug 1552160: Add missing defines from concurrencysal.h and specstrings.h in windows_type.h on MinGW.
+
+diff --git a/security/sandbox/chromium/base/win/windows_types.h b/security/sandbox/chromium/base/win/windows_types.h
+--- a/security/sandbox/chromium/base/win/windows_types.h
++++ b/security/sandbox/chromium/base/win/windows_types.h
+@@ -4,17 +4,25 @@
+
+ // This file contains defines and typedefs that allow popular Windows types to
+ // be used without the overhead of including windows.h.
+
+ #ifndef BASE_WIN_WINDOWS_TYPES_H
+ #define BASE_WIN_WINDOWS_TYPES_H
+
+ // Needed for function prototypes.
++#if defined(__MINGW32__)
++// MinGW doesn't have this file yet, but we only need this define.
++// Bug 1552706 tracks removing this and the one below.
++#define _Releases_exclusive_lock_(lock)
++// MinGW doesn't appear to have this in specstrings.h either.
++#define _Post_equals_last_error_
++#else
+ #include <concurrencysal.h>
++#endif
+ #include <sal.h>
+ #include <specstrings.h>
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ // typedef and define the most commonly used Windows integer types.
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_offsetof.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_offsetof.patch
new file mode 100644
index 0000000000..89072da69b
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_offsetof.patch
@@ -0,0 +1,182 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1528394907 18000
+# Thu Jun 07 13:08:27 2018 -0500
+# Node ID ffb6c5c06905538fb887464e9553e7b47cdf7575
+# Parent 1987e062f1e5bf2998bb8e9d96353c5ccb0cc281
+Bug 1461421 Use OffsetOf to calculate the location of parameters_ rather than making assumptions about the parent class r?bobowen
+
+MozReview-Commit-ID: D7REZiAIMpN
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
+--- a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
++++ b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
+@@ -78,16 +78,17 @@ union MultiType {
+ ULONG_PTR ulong_ptr;
+ };
+
+ // Maximum number of IPC parameters currently supported.
+ // To increase this value, we have to:
+ // - Add another Callback typedef to Dispatcher.
+ // - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
+ // - Add another case to the switch in GetActualAndMaxBufferSize
++// - Add another case to the switch in GetMinDeclaredActualCallParamsSize
+ const int kMaxIpcParams = 9;
+
+ // Contains the information about a parameter in the ipc buffer.
+ struct ParamInfo {
+ ArgType type_;
+ uint32_t offset_;
+ uint32_t size_;
+ };
+@@ -287,16 +288,18 @@ class ActualCallParams : public CrossCal
+ protected:
+ ActualCallParams() : CrossCallParams(IpcTag::UNUSED, NUMBER_PARAMS) {}
+
+ private:
+ ParamInfo param_info_[NUMBER_PARAMS + 1];
+ char parameters_[BLOCK_SIZE - sizeof(CrossCallParams) -
+ sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
+ DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
++
++ friend uint32_t GetMinDeclaredActualCallParamsSize(uint32_t param_count);
+ };
+
+ 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
+--- a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
++++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
+@@ -28,30 +28,31 @@ namespace {
+
+ // The buffer for a message must match the max channel size.
+ const size_t kMaxBufferSize = sandbox::kIPCChannelSize;
+
+ } // namespace
+
+ namespace sandbox {
+
++// The template types are used to calculate the maximum expected size.
++typedef ActualCallParams<0, kMaxBufferSize> ActualCP0;
++typedef ActualCallParams<1, kMaxBufferSize> ActualCP1;
++typedef ActualCallParams<2, kMaxBufferSize> ActualCP2;
++typedef ActualCallParams<3, kMaxBufferSize> ActualCP3;
++typedef ActualCallParams<4, kMaxBufferSize> ActualCP4;
++typedef ActualCallParams<5, kMaxBufferSize> ActualCP5;
++typedef ActualCallParams<6, kMaxBufferSize> ActualCP6;
++typedef ActualCallParams<7, kMaxBufferSize> ActualCP7;
++typedef ActualCallParams<8, kMaxBufferSize> ActualCP8;
++typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;
++
+ // Returns the actual size for the parameters in an IPC buffer. Returns
+ // zero if the |param_count| is zero or too big.
+ 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;
+- typedef ActualCallParams<3, kMaxBufferSize> ActualCP3;
+- typedef ActualCallParams<4, kMaxBufferSize> ActualCP4;
+- typedef ActualCallParams<5, kMaxBufferSize> ActualCP5;
+- typedef ActualCallParams<6, kMaxBufferSize> ActualCP6;
+- typedef ActualCallParams<7, kMaxBufferSize> ActualCP7;
+- typedef ActualCallParams<8, kMaxBufferSize> ActualCP8;
+- typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;
+-
+ // Retrieve the actual size and the maximum size of the params buffer.
+ switch (param_count) {
+ case 0:
+ return 0;
+ case 1:
+ return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize();
+ case 2:
+ return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize();
+@@ -69,16 +70,45 @@ uint32_t GetActualBufferSize(uint32_t pa
+ return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize();
+ case 9:
+ return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize();
+ default:
+ return 0;
+ }
+ }
+
++// Returns the minimum size for the parameters in an IPC buffer. Returns
++// zero if the |param_count| is less than zero or too big.
++uint32_t GetMinDeclaredActualCallParamsSize(uint32_t param_count) {
++ switch (param_count) {
++ case 0:
++ return offsetof(ActualCP0, parameters_);
++ case 1:
++ return offsetof(ActualCP1, parameters_);
++ case 2:
++ return offsetof(ActualCP2, parameters_);
++ case 3:
++ return offsetof(ActualCP3, parameters_);
++ case 4:
++ return offsetof(ActualCP4, parameters_);
++ case 5:
++ return offsetof(ActualCP5, parameters_);
++ case 6:
++ return offsetof(ActualCP6, parameters_);
++ case 7:
++ return offsetof(ActualCP7, parameters_);
++ case 8:
++ return offsetof(ActualCP8, parameters_);
++ case 9:
++ return offsetof(ActualCP9, parameters_);
++ default:
++ return 0;
++ }
++}
++
+ // Verifies that the declared sizes of an IPC buffer are within range.
+ 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
+ // integer overflow.
+@@ -133,18 +163,17 @@ CrossCallParamsEx* CrossCallParamsEx::Cr
+ // will catch memory access violations so we don't crash.
+ __try {
+ CrossCallParams* call_params =
+ reinterpret_cast<CrossCallParams*>(buffer_base);
+
+ // Check against the minimum size given the number of stated params
+ // if too small we bail out.
+ param_count = call_params->GetParamsCount();
+- min_declared_size =
+- sizeof(CrossCallParams) + ((param_count + 1) * sizeof(ParamInfo));
++ min_declared_size = GetMinDeclaredActualCallParamsSize(param_count);
+
+ // Initial check for the buffer being big enough to determine the actual
+ // buffer size.
+ if (buffer_size < min_declared_size)
+ return nullptr;
+
+ // Retrieve the declared size which if it fails returns 0.
+ declared_size = GetActualBufferSize(param_count, buffer_base);
+@@ -158,18 +187,17 @@ CrossCallParamsEx* CrossCallParamsEx::Cr
+ copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem);
+ memcpy(backing_mem, call_params, declared_size);
+
+ // Avoid compiler optimizations across this point. Any value stored in
+ // memory should be stored for real, and values previously read from memory
+ // should be actually read.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+
+- min_declared_size =
+- sizeof(CrossCallParams) + ((param_count + 1) * sizeof(ParamInfo));
++ min_declared_size = GetMinDeclaredActualCallParamsSize(param_count);
+
+ // Check that the copied buffer is still valid.
+ if (copied_params->GetParamsCount() != param_count ||
+ GetActualBufferSize(param_count, backing_mem) != declared_size ||
+ !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) {
+ delete[] backing_mem;
+ return nullptr;
+ }
diff --git a/security/sandbox/chromium-shim/patches/with_update/mingw_operator_new.patch b/security/sandbox/chromium-shim/patches/with_update/mingw_operator_new.patch
new file mode 100644
index 0000000000..ab223901ed
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/mingw_operator_new.patch
@@ -0,0 +1,58 @@
+# HG changeset patch
+# User Tom Ritter <tom@mozilla.com>
+# Date 1489000606 0
+# Wed Mar 08 19:16:46 2017 +0000
+# Node ID 522c35c24e2a46d97430b5f15e7703bc1c33784c
+# Parent a99512c712f6580537e3133e5fd1adc091583e95
+Bug 1230910 Declare operator new [](size_t, sandbox::AllocationType, void*)
+
+MozReview-Commit-ID: GCKj5Ao2Y2n
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
+@@ -663,16 +663,21 @@ void* operator new(size_t size, sandbox:
+
+ // TODO: Returning nullptr from operator new has undefined behavior, but
+ // the Allocate() functions called above can return nullptr. Consider checking
+ // for nullptr here and crashing or throwing.
+
+ return result;
+ }
+
++void* operator new [](size_t size, sandbox::AllocationType type,
++ void* near_to) {
++ return operator new(size, type, near_to);
++}
++
+ void operator delete(void* memory, sandbox::AllocationType type) {
+ if (type == sandbox::NT_ALLOC) {
+ // Use default flags.
+ 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(sandbox::g_nt.FreeVirtualMemory(NtCurrentProcess, &base,
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h
+@@ -13,16 +13,19 @@
+ #include "base/macros.h"
+ #include "sandbox/win/src/nt_internals.h"
+ #include "sandbox/win/src/sandbox_nt_types.h"
+
+ // Placement new and delete to be used from ntdll interception code.
+ void* __cdecl operator new(size_t size,
+ sandbox::AllocationType type,
+ void* near_to = nullptr);
++void* __cdecl operator new[](size_t size,
++ sandbox::AllocationType type,
++ void* near_to = nullptr);
+ void __cdecl operator delete(void* memory, sandbox::AllocationType type);
+ // Add operator delete that matches the placement form of the operator new
+ // above. This is required by compiler to generate code to call operator delete
+ // in case the object's constructor throws an exception.
+ // See http://msdn.microsoft.com/en-us/library/cxdxz3x6.aspx
+ void __cdecl operator delete(void* memory,
+ sandbox::AllocationType type,
+ void* near_to);
diff --git a/security/sandbox/chromium-shim/patches/with_update/more_chromium_linux_x86_x64_syscalls.patch b/security/sandbox/chromium-shim/patches/with_update/more_chromium_linux_x86_x64_syscalls.patch
new file mode 100644
index 0000000000..4b32171d04
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/more_chromium_linux_x86_x64_syscalls.patch
@@ -0,0 +1,91 @@
+# HG changeset patch
+# User Gian-Carlo Pascutto <gcp@mozilla.com>
+# Date 1573118511 0
+# Thu Nov 07 09:21:51 2019 +0000
+# Node ID a0be746532f437055e4190cc8db802ad1239405e
+# Parent f5df610ae207f14f233874e2f1502c137b4f94ab
+Bug 1591117 - Report ENOSYS on statx, but allow membarrier. r=jld
+
+Differential Revision: https://phabricator.services.mozilla.com/D50623
+
+diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h
+--- a/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h
++++ b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h
+@@ -1,13 +1,17 @@
+ // Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+
+-// Generated from the Linux kernel's syscall_64.tbl.
++/* Constructed by running a vim macro over
++ linux-kernel/arch/x86/entry/syscalls/syscall_64.tbl
++ version 39a38bcba4ab6e5285b07675b0e42c96eec35e67
++ which is close to Linux 5.4.
++*/
+ #ifndef SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+ #define SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+
+ #if !defined(__x86_64__)
+ #error "Including header on wrong architecture"
+ #endif
+
+ #if !defined(__NR_read)
+@@ -1345,10 +1349,57 @@
+ #if !defined(__NR_io_pgetevents)
+ #define __NR_io_pgetevents 333
+ #endif
+
+ #if !defined(__NR_rseq)
+ #define __NR_rseq 334
+ #endif
+
++#if !defined(__NR_pidfd_send_signal)
++#define __NR_pidfd_send_signal 424
++#endif
++
++#if !defined(__NR_io_uring_setup)
++#define __NR_io_uring_setup 425
++#endif
++
++#if !defined(__NR_io_uring_enter)
++#define __NR_io_uring_enter 426
++#endif
++
++#if !defined(__NR_io_uring_register)
++#define __NR_io_uring_register 427
++#endif
++
++#if !defined(__NR_open_tree)
++#define __NR_open_tree 428
++#endif
++
++#if !defined(__NR_move_mount)
++#define __NR_move_mount 429
++#endif
++
++#if !defined(__NR_fsopen)
++#define __NR_fsopen 430
++#endif
++
++#if !defined(__NR_fsconfig)
++#define __NR_fsconfig 431
++#endif
++
++#if !defined(__NR_fsmount)
++#define __NR_fsmount 432
++#endif
++
++#if !defined(__NR_fspick)
++#define __NR_fspick 433
++#endif
++
++#if !defined(__NR_pidfd_open)
++#define __NR_pidfd_open 434
++#endif
++
++#if !defined(__NR_clone3)
++#define __NR_clone3 435
++#endif
++
+ #endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+-
diff --git a/security/sandbox/chromium-shim/patches/with_update/patch_order.txt b/security/sandbox/chromium-shim/patches/with_update/patch_order.txt
new file mode 100755
index 0000000000..6098ea5c2d
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/patch_order.txt
@@ -0,0 +1,32 @@
+revert_remove_AddTargetPeer.patch
+revert_remove_BrokerDuplicateHandle.patch
+replace_ScopedNativeLibrary_in_ApplyMitigationsToCurrentThread.patch
+ifdef_out_FromStringInternal.patch
+add_option_to_not_use_restricting_sids.patch
+ifdef_out_SequenceChecker_code.patch
+allow_read_only_all_paths_rule.patch
+revert_TargetNtSetInformationThread_change.patch
+mingw_copy_s.patch
+mingw_operator_new.patch
+mingw_cast_getprocaddress.patch
+mingw_capitalization.patch
+mingw_disable_one_try.patch
+mingw_offsetof.patch
+allow_env_changes.patch
+ifdef_out_AppContainerProfileBase_testing_functions.patch
+mingw_missing_windows_types_defines.patch
+add_return_in_QueryCancellationTraitsForNonCancellables_to_satisfy_build.patch
+include_atomic_header_in_platform_thread.patch
+aarch64_control_flow_guard.patch
+revert_removal_of_app_dir_for_DLL_load.patch
+more_chromium_linux_x86_x64_syscalls.patch
+add_support_for_random_restricted_SID.patch
+revert_Token_serialization_and_deserialization.patch
+remove_unused_functions_from_StrtodTrimmed.patch
+remove_extraneous_backslash_introduced_by_clang_tidy.patch
+remove_include_delayimp_h_from_pe_image_cc.patch
+lower_SDK_version_requirement.patch
+add_CET_STRICT_MODE.patch
+broker_complex_line_breaks.patch
+allow_reparse_points.patch
+derive_sid_from_name.patch
diff --git a/security/sandbox/chromium-shim/patches/with_update/remove_extraneous_backslash_introduced_by_clang_tidy.patch b/security/sandbox/chromium-shim/patches/with_update/remove_extraneous_backslash_introduced_by_clang_tidy.patch
new file mode 100644
index 0000000000..431a5e102c
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/remove_extraneous_backslash_introduced_by_clang_tidy.patch
@@ -0,0 +1,34 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1588867789 25200
+# Thu May 07 09:09:49 2020 -0700
+# Node ID 29fbfefe6f5f533fb5aa4339015cea4746ad6493
+# Parent 044c15e89ecca19afc1750c439f4e82879679462
+Remove Extraneous Backslash Introduced by clang-tidy in ScopedHandle. r=bobowen
+
+Need the following commit to compile with Mingw, which has not reached
+the stable channel yet.
+https://chromium.googlesource.com/chromium/src.git/+/1620fe70c299f1f18b2f2c652d16739f6e3c5f78
+
+diff --git a/security/sandbox/chromium/base/win/scoped_handle.h b/security/sandbox/chromium/base/win/scoped_handle.h
+--- a/security/sandbox/chromium/base/win/scoped_handle.h
++++ b/security/sandbox/chromium/base/win/scoped_handle.h
+@@ -15,17 +15,17 @@
+ #include "base/macros.h"
+
+ // TODO(rvargas): remove this with the rest of the verifier.
+ #if defined(COMPILER_MSVC)
+ #include <intrin.h>
+ #define BASE_WIN_GET_CALLER _ReturnAddress()
+ #elif defined(COMPILER_GCC)
+ #define BASE_WIN_GET_CALLER \
+- __builtin_extract_return_addr(\ __builtin_return_address(0))
++ __builtin_extract_return_addr(__builtin_return_address(0))
+ #endif
+
+ namespace base {
+ 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 two additions:
diff --git a/security/sandbox/chromium-shim/patches/with_update/remove_include_delayimp_h_from_pe_image_cc.patch b/security/sandbox/chromium-shim/patches/with_update/remove_include_delayimp_h_from_pe_image_cc.patch
new file mode 100644
index 0000000000..4f08f57011
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/remove_include_delayimp_h_from_pe_image_cc.patch
@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1588871424 25200
+# Thu May 07 10:10:24 2020 -0700
+# Node ID 2d5ee142bde533ba4f93afaae081a444eac0abe2
+# Parent 29fbfefe6f5f533fb5aa4339015cea4746ad6493
+Don't include delayimp.h twice from //base/win/pe_image.cc to compile with Mingw. r=bobowen
+
+The second include was introduced by
+https://chromium.googlesource.com/chromium/src.git/+/5c23d46846111ea16aaf2a9b45355cca5ddbf6d8
+
+diff --git a/security/sandbox/chromium/base/win/pe_image.cc b/security/sandbox/chromium/base/win/pe_image.cc
+--- a/security/sandbox/chromium/base/win/pe_image.cc
++++ b/security/sandbox/chromium/base/win/pe_image.cc
+@@ -2,17 +2,16 @@
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+
+ // This file implements PEImage, a generic class to manipulate PE files.
+ // This file was adapted from GreenBorder's Code.
+
+ #include "base/win/pe_image.h"
+
+-#include <delayimp.h>
+ #include <stddef.h>
+ #include <set>
+ #include <string>
+
+ #include "base/no_destructor.h"
+ #include "base/win/current_module.h"
+
+ namespace base {
diff --git a/security/sandbox/chromium-shim/patches/with_update/remove_unused_functions_from_StrtodTrimmed.patch b/security/sandbox/chromium-shim/patches/with_update/remove_unused_functions_from_StrtodTrimmed.patch
new file mode 100644
index 0000000000..a097360ac5
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/remove_unused_functions_from_StrtodTrimmed.patch
@@ -0,0 +1,48 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1588733379 25200
+# Tue May 05 19:49:39 2020 -0700
+# Node ID 044c15e89ecca19afc1750c439f4e82879679462
+# Parent a18431660425e41c26c716413aac0294987c985a
+Remove unused functions from //base/third_party/double_conversion/double-conversion to compile. r=bobowen
+
+diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc
+--- a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc
++++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc
+@@ -445,36 +445,18 @@ static bool ComputeGuess(Vector<const ch
+ return true;
+ }
+ if (*guess == Double::Infinity()) {
+ return true;
+ }
+ return false;
+ }
+
+-static bool IsDigit(const char d) {
+- return ('0' <= d) && (d <= '9');
+-}
+-
+-static bool IsNonZeroDigit(const char d) {
+- return ('1' <= d) && (d <= '9');
+-}
+-
+-static bool AssertTrimmedDigits(const Vector<const char>& buffer) {
+- for(int i = 0; i < buffer.length(); ++i) {
+- if(!IsDigit(buffer[i])) {
+- return false;
+- }
+- }
+- return (buffer.length() == 0) || (IsNonZeroDigit(buffer[0]) && IsNonZeroDigit(buffer[buffer.length()-1]));
+-}
+-
+ double StrtodTrimmed(Vector<const char> trimmed, int exponent) {
+ DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits);
+- DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed));
+ double guess;
+ const bool is_correct = ComputeGuess(trimmed, exponent, &guess);
+ if (is_correct) {
+ return guess;
+ }
+ DiyFp upper_boundary = Double(guess).UpperBoundary();
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+ if (comparison < 0) {
diff --git a/security/sandbox/chromium-shim/patches/with_update/replace_ScopedNativeLibrary_in_ApplyMitigationsToCurrentThread.patch b/security/sandbox/chromium-shim/patches/with_update/replace_ScopedNativeLibrary_in_ApplyMitigationsToCurrentThread.patch
new file mode 100644
index 0000000000..47418009d6
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/replace_ScopedNativeLibrary_in_ApplyMitigationsToCurrentThread.patch
@@ -0,0 +1,59 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1589672273 25200
+# Sat May 16 16:37:53 2020 -0700
+# Node ID c14ef8304c36fdc2570b77b63b36114cff2d070d
+# Parent 90b5f63770f52fab163adaed1d5812b2887b335a
+Use GetModuleHandle/GetProcAddress in ApplyMitigationsToCurrentThread. r=bobowen
+
+This patch removes the use of base::ScopedNativeLibrary from
+sandbox::ApplyMitigationsToCurrentThread because to avoid
+new dependencies.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+--- a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
++++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+@@ -5,18 +5,16 @@
+ #include "sandbox/win/src/process_mitigations.h"
+
+ #include <stddef.h>
+ #include <windows.h>
+ #include <wow64apiset.h>
+
+ #include <algorithm>
+
+-#include "base/files/file_path.h"
+-#include "base/scoped_native_library.h"
+ #include "base/win/windows_version.h"
+ #include "build/build_config.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 {
+@@ -321,22 +319,19 @@ bool ApplyMitigationsToCurrentThread(Mit
+ return true;
+
+ // Enable dynamic code per-thread policies.
+ if (flags & MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD) {
+ DWORD thread_policy = THREAD_DYNAMIC_CODE_ALLOW;
+
+ // NOTE: SetThreadInformation API only exists on >= Win8. Dynamically
+ // get function handle.
+- base::ScopedNativeLibrary dll(base::FilePath(L"kernel32.dll"));
+- if (!dll.is_valid())
+- return false;
+ SetThreadInformationFunction set_thread_info_function =
+- reinterpret_cast<SetThreadInformationFunction>(
+- dll.GetFunctionPointer("SetThreadInformation"));
++ reinterpret_cast<SetThreadInformationFunction>(::GetProcAddress(
++ ::GetModuleHandleA("kernel32.dll"), "SetThreadInformation"));
+ if (!set_thread_info_function)
+ return false;
+
+ // NOTE: Must use the pseudo-handle here, a thread HANDLE won't work.
+ if (!set_thread_info_function(::GetCurrentThread(), ThreadDynamicCodePolicy,
+ &thread_policy, sizeof(thread_policy))) {
+ return false;
+ }
diff --git a/security/sandbox/chromium-shim/patches/with_update/revert_TargetNtSetInformationThread_change.patch b/security/sandbox/chromium-shim/patches/with_update/revert_TargetNtSetInformationThread_change.patch
new file mode 100644
index 0000000000..60bb45e3af
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/revert_TargetNtSetInformationThread_change.patch
@@ -0,0 +1,39 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1510058662 0
+# Tue Nov 07 12:44:22 2017 +0000
+# Node ID 5b2b8b6c509a1025ef6d6ba208b093d4c4359186
+# Parent 2c3a28eab0bfcaa5a14771454f83703ae938da6c
+Revert commit f7540af7428f4b146136ec19b781886693f8c03f changes to policy_target.cc for causing issues with CoInitializeSecurity. r=aklotz
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target.cc b/security/sandbox/chromium/sandbox/win/src/policy_target.cc
+--- a/security/sandbox/chromium/sandbox/win/src/policy_target.cc
++++ b/security/sandbox/chromium/sandbox/win/src/policy_target.cc
+@@ -78,16 +78,26 @@ NTSTATUS WINAPI TargetNtSetInformationTh
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes) {
+ do {
+ if (SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ break;
+ if (ThreadImpersonationToken != thread_info_class)
+ break;
++ if (!thread_information)
++ break;
++ HANDLE token;
++ if (sizeof(token) > thread_information_bytes)
++ break;
++
++ NTSTATUS ret = CopyData(&token, thread_information, sizeof(token));
++ if (!NT_SUCCESS(ret) || NULL != token)
++ break;
++
+ // This is a revert to self.
+ return STATUS_SUCCESS;
+ } while (false);
+
+ return orig_SetInformationThread(
+ thread, thread_info_class, thread_information, thread_information_bytes);
+ }
+
+ // Hooks NtOpenThreadToken to force the open_as_self parameter to be set to
diff --git a/security/sandbox/chromium-shim/patches/with_update/revert_Token_serialization_and_deserialization.patch b/security/sandbox/chromium-shim/patches/with_update/revert_Token_serialization_and_deserialization.patch
new file mode 100644
index 0000000000..c2d96dda78
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/revert_Token_serialization_and_deserialization.patch
@@ -0,0 +1,100 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1588530677 25200
+# Sun May 03 11:31:17 2020 -0700
+# Node ID a18431660425e41c26c716413aac0294987c985a
+# Parent e149b1937231ccc3c1c07f45acf0e7e71117854f
+Revert chromium's ffe1d0eb42d1d75f2b6a3b4145eff69f235a19ee. r=bobowen
+
+Undoing the following commit as it brings more dependency but unused in our code.
+https://chromium.googlesource.com/chromium/src.git/+/ffe1d0eb42d1d75f2b6a3b4145eff69f235a19ee
+
+diff --git a/security/sandbox/chromium/base/token.cc b/security/sandbox/chromium/base/token.cc
+--- a/security/sandbox/chromium/base/token.cc
++++ b/security/sandbox/chromium/base/token.cc
+@@ -1,17 +1,16 @@
+ // Copyright 2018 The Chromium Authors. 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/token.h"
+
+ #include <inttypes.h>
+
+-#include "base/pickle.h"
+ #include "base/rand_util.h"
+ #include "base/strings/stringprintf.h"
+
+ namespace base {
+
+ // static
+ Token Token::CreateRandom() {
+ Token token;
+@@ -21,26 +20,9 @@ Token Token::CreateRandom() {
+ base::RandBytes(&token, sizeof(token));
+ return token;
+ }
+
+ std::string Token::ToString() const {
+ return base::StringPrintf("%016" PRIX64 "%016" PRIX64, high_, low_);
+ }
+
+-void WriteTokenToPickle(Pickle* pickle, const Token& token) {
+- pickle->WriteUInt64(token.high());
+- pickle->WriteUInt64(token.low());
+-}
+-
+-Optional<Token> ReadTokenFromPickle(PickleIterator* pickle_iterator) {
+- uint64_t high;
+- if (!pickle_iterator->ReadUInt64(&high))
+- return nullopt;
+-
+- uint64_t low;
+- if (!pickle_iterator->ReadUInt64(&low))
+- return nullopt;
+-
+- return Token(high, low);
+-}
+-
+ } // namespace base
+diff --git a/security/sandbox/chromium/base/token.h b/security/sandbox/chromium/base/token.h
+--- a/security/sandbox/chromium/base/token.h
++++ b/security/sandbox/chromium/base/token.h
+@@ -7,17 +7,16 @@
+
+ #include <stdint.h>
+
+ #include <iosfwd>
+ #include <tuple>
+
+ #include "base/base_export.h"
+ #include "base/hash/hash.h"
+-#include "base/optional.h"
+
+ namespace base {
+
+ // A Token is a randomly chosen 128-bit integer. This class supports generation
+ // from a cryptographically strong random source, or constexpr construction over
+ // fixed values (e.g. to store a pre-generated constant value). Tokens are
+ // similar in spirit and purpose to UUIDs, without many of the constraints and
+ // expectations (such as byte layout and string representation) clasically
+@@ -63,19 +62,11 @@ class BASE_EXPORT Token {
+
+ // For use in std::unordered_map.
+ struct TokenHash {
+ size_t operator()(const base::Token& token) const {
+ return base::HashInts64(token.high(), token.low());
+ }
+ };
+
+-class Pickle;
+-class PickleIterator;
+-
+-// For serializing and deserializing Token values.
+-BASE_EXPORT void WriteTokenToPickle(Pickle* pickle, const Token& token);
+-BASE_EXPORT Optional<Token> ReadTokenFromPickle(
+- PickleIterator* pickle_iterator);
+-
+ } // namespace base
+
+ #endif // BASE_TOKEN_H_
diff --git a/security/sandbox/chromium-shim/patches/with_update/revert_removal_of_app_dir_for_DLL_load.patch b/security/sandbox/chromium-shim/patches/with_update/revert_removal_of_app_dir_for_DLL_load.patch
new file mode 100644
index 0000000000..c5de8c9041
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/revert_removal_of_app_dir_for_DLL_load.patch
@@ -0,0 +1,74 @@
+# HG changeset patch
+# User Bob Owen <bobowencode@gmail.com>
+# Date 1564062993 -3600
+# Thu Jul 25 14:56:33 2019 +0100
+# Node ID aa8f8da7b00f1f751bf4a7c8a2cc58b290a328e0
+# Parent 69ac304560c98a733d44a0245fe9782dc6a465e2
+Bug 1565848: Revert latest change to MITIGATION_DLL_SEARCH_ORDER. r=handyman!
+
+This is until any regressions can be fixed, see bug 1568850.
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+--- a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
++++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
+@@ -72,26 +72,17 @@ bool ApplyProcessMitigationsToCurrentPro
+
+ if (flags & MITIGATION_DLL_SEARCH_ORDER) {
+ SetDefaultDllDirectoriesFunction set_default_dll_directories =
+ reinterpret_cast<SetDefaultDllDirectoriesFunction>(
+ ::GetProcAddress(module, "SetDefaultDllDirectories"));
+
+ // Check for SetDefaultDllDirectories since it requires KB2533623.
+ if (set_default_dll_directories) {
+-#if defined(COMPONENT_BUILD)
+- const DWORD directory_flags = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
+-#else
+- // In a non-component build, all DLLs will be loaded manually, or via
+- // manifest definition, so these flags can be stronger. This prevents DLL
+- // planting in the application directory.
+- const DWORD directory_flags =
+- LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS;
+-#endif
+- if (!set_default_dll_directories(directory_flags) &&
++ if (!set_default_dll_directories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+ }
+
+ // Set the heap to terminate on corruption
+ if (flags & MITIGATION_HEAP_TERMINATE) {
+diff --git a/security/sandbox/chromium/sandbox/win/src/security_level.h b/security/sandbox/chromium/sandbox/win/src/security_level.h
+--- a/security/sandbox/chromium/sandbox/win/src/security_level.h
++++ b/security/sandbox/chromium/sandbox/win/src/security_level.h
+@@ -192,25 +192,20 @@ const MitigationFlags MITIGATION_BOTTOM_
+ // PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON
+ const MitigationFlags MITIGATION_HIGH_ENTROPY_ASLR = 0x00000080;
+
+ // Immediately raises an exception on a bad handle reference. Must be
+ // enabled after startup. Corresponds to
+ // PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON.
+ const MitigationFlags MITIGATION_STRICT_HANDLE_CHECKS = 0x00000100;
+
+-// Strengthens the DLL search order. See
+-// http://msdn.microsoft.com/en-us/library/windows/desktop/hh310515. In a
+-// component build - sets this to LOAD_LIBRARY_SEARCH_DEFAULT_DIRS allowing
+-// additional directories to be added via Windows AddDllDirectory() function,
+-// but preserving current load order. In a non-component build, all DLLs should
+-// be loaded manually, so strenthen to LOAD_LIBRARY_SEARCH_SYSTEM32 |
+-// LOAD_LIBRARY_SEARCH_USER_DIRS, removing LOAD_LIBRARY_SEARCH_APPLICATION_DIR,
+-// preventing DLLs being implicitly loaded from the application path. Must be
+-// enabled after startup.
++// Sets the DLL search order to LOAD_LIBRARY_SEARCH_DEFAULT_DIRS. Additional
++// directories can be added via the Windows AddDllDirectory() function.
++// http://msdn.microsoft.com/en-us/library/windows/desktop/hh310515
++// Must be enabled after startup.
+ const MitigationFlags MITIGATION_DLL_SEARCH_ORDER = 0x00000200;
+
+ // 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 = 0x00000400;
+
+ // Prevents the process from making Win32k calls. Corresponds to
diff --git a/security/sandbox/chromium-shim/patches/with_update/revert_remove_AddTargetPeer.patch b/security/sandbox/chromium-shim/patches/with_update/revert_remove_AddTargetPeer.patch
new file mode 100644
index 0000000000..04020b60b7
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/revert_remove_AddTargetPeer.patch
@@ -0,0 +1,310 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1589671259 25200
+# Sat May 16 16:20:59 2020 -0700
+# Node ID 0b5183a01df78cc85264f2eae2c4d8e407bb1112
+# Parent d093cd9ccfcf06f4a1f0d7f1a4bd0f143ef92b4b
+Add BrokerServicesBase::IsSafeDuplicationTarget. r=bobowen
+
+This patch adds BrokerServicesBase::IsSafeDuplicationTarget and
+BrokerServicesBase::AddTargetPeer using the new ProcessTracker introduced by
+https://chromium.googlesource.com/chromium/src.git/+/3d8382cf9dd44cf9c05e43e42c500f4825e1fed8
+We need these methods for HandlePolicy which is added as a different patch.
+
+Chromium used to have AddTargetPeer and IsActiveTarget, but removed by
+the following commits because they were no longer used in Chromium.
+https://chromium.googlesource.com/chromium/src.git/+/996b42db5296bd3d11b3d7fde1a4602bbcefed2c
+https://chromium.googlesource.com/chromium/src.git/+/e615a1152ac6e10f1a91f0629fb8b5ca223ffbdc
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+--- a/security/sandbox/chromium/sandbox/win/src/broker_services.cc
++++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
+@@ -154,16 +154,18 @@ namespace sandbox {
+ BrokerServicesBase::BrokerServicesBase() {}
+
+ // 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 (job_port_.IsValid() || thread_pool_)
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
++ ::InitializeCriticalSection(&lock_);
++
+ job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0));
+ if (!job_port_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ no_targets_.Set(::CreateEventW(nullptr, true, false, nullptr));
+
+ job_thread_.Set(::CreateThread(nullptr, 0, // Default security and stack.
+ TargetEventsThread, this, 0, nullptr));
+@@ -191,16 +193,17 @@ BrokerServicesBase::~BrokerServicesBase(
+
+ if (job_thread_.IsValid() &&
+ WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_.Get(), 1000)) {
+ // Cannot clean broker services.
+ NOTREACHED();
+ return;
+ }
+ thread_pool_.reset();
++ ::DeleteCriticalSection(&lock_);
+ }
+
+ scoped_refptr<TargetPolicy> BrokerServicesBase::CreatePolicy() {
+ // If you change the type of the object being created here you must also
+ // change the downcast to it in SpawnTarget().
+ scoped_refptr<TargetPolicy> policy(new PolicyBase);
+ // PolicyBase starts with refcount 1.
+ policy->Release();
+@@ -283,16 +286,21 @@ DWORD WINAPI BrokerServicesBase::TargetE
+ if (1 == target_counter) {
+ ::ResetEvent(no_targets);
+ }
+ break;
+ }
+
+ case JOB_OBJECT_MSG_EXIT_PROCESS:
+ case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: {
++ {
++ AutoLock lock(&broker->lock_);
++ broker->active_targets_.erase(
++ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
++ }
+ size_t erase_result = child_process_ids.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ if (erase_result != 1U) {
+ // The process was untracked e.g. a child process of the target.
+ --untracked_target_counter;
+ DCHECK(untracked_target_counter >= 0);
+ }
+ --target_counter;
+@@ -348,27 +356,31 @@ DWORD WINAPI BrokerServicesBase::TargetE
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ processes.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_PROCESS_SIGNALLED == key) {
+ ProcessTracker* tracker =
+ static_cast<ProcessTracker*>(reinterpret_cast<void*>(ovl));
+
++ {
++ AutoLock lock(&broker->lock_);
++ broker->active_targets_.erase(tracker->process_id);
++ }
++
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+
+ // PID is unique until the process handle is closed in dtor.
+ processes.erase(std::remove_if(processes.begin(), processes.end(),
+ [&](auto&& p) -> bool {
+ return p->process_id ==
+ tracker->process_id;
+ }),
+ processes.end());
+-
+ } else if (THREAD_CTRL_GET_POLICY_INFO == key) {
+ // Clone the policies for sandbox diagnostics.
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver;
+ receiver.reset(static_cast<PolicyDiagnosticsReceiver*>(
+ reinterpret_cast<void*>(ovl)));
+ // The PollicyInfo ctor copies essential information from the trackers.
+ auto policy_list = std::make_unique<PolicyDiagnosticList>();
+ for (auto&& process_tracker : processes) {
+@@ -637,47 +649,79 @@ ResultCode BrokerServicesBase::SpawnTarg
+ // the tracker. The worker thread takes ownership of these objects.
+ CHECK(::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker)));
+ // 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));
++
++ AutoLock lock(&lock_);
++ active_targets_.insert(process_info.process_id());
+ } else {
+- // Duplicate the process handle to give the tracking machinery
+- // something valid to wait on in the tracking thread.
+- HANDLE tmp_process_handle = INVALID_HANDLE_VALUE;
+- if (!::DuplicateHandle(::GetCurrentProcess(), process_info.process_handle(),
+- ::GetCurrentProcess(), &tmp_process_handle,
+- SYNCHRONIZE, false, 0 /*no options*/)) {
+- *last_error = ::GetLastError();
++ result = AddTargetPeerInternal(process_info.process_handle(),
++ process_info.process_id(),
++ policy_base, last_error);
++ if (result != SBOX_ALL_OK) {
+ // This may fail in the same way as Job associated processes.
+ // crbug.com/480639.
+ SpawnCleanup(target);
+- return SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE;
++ return result;
+ }
+- base::win::ScopedHandle dup_process_handle(tmp_process_handle);
+- ProcessTracker* tracker = new ProcessTracker(
+- policy_base, process_info.process_id(), std::move(dup_process_handle));
+- // The tracker and policy will leak if this call fails.
+- ::PostQueuedCompletionStatus(job_port_.Get(), 0,
+- THREAD_CTRL_NEW_PROCESS_TRACKER,
+- reinterpret_cast<LPOVERLAPPED>(tracker));
+ }
+
+ *target_info = process_info.Take();
+ return result;
+ }
+
+ ResultCode BrokerServicesBase::WaitForAllTargets() {
+ ::WaitForSingleObject(no_targets_.Get(), INFINITE);
+ return SBOX_ALL_OK;
+ }
+
++bool BrokerServicesBase::IsSafeDuplicationTarget(DWORD process_id) {
++ AutoLock lock(&lock_);
++ return active_targets_.find(process_id) != active_targets_.end();
++}
++
++ResultCode BrokerServicesBase::AddTargetPeerInternal(
++ HANDLE peer_process_handle,
++ DWORD peer_process_id,
++ scoped_refptr<PolicyBase> policy_base,
++ DWORD* last_error) {
++ // Duplicate the process handle to give the tracking machinery
++ // something valid to wait on in the tracking thread.
++ HANDLE tmp_process_handle = INVALID_HANDLE_VALUE;
++ if (!::DuplicateHandle(::GetCurrentProcess(), peer_process_handle,
++ ::GetCurrentProcess(), &tmp_process_handle,
++ SYNCHRONIZE, false, 0 /*no options*/)) {
++ *last_error = ::GetLastError();
++ return SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE;
++ }
++ base::win::ScopedHandle dup_process_handle(tmp_process_handle);
++ ProcessTracker* tracker = new ProcessTracker(
++ policy_base, peer_process_id, std::move(dup_process_handle));
++ // The tracker and policy will leak if this call fails.
++ ::PostQueuedCompletionStatus(job_port_.Get(), 0,
++ THREAD_CTRL_NEW_PROCESS_TRACKER,
++ reinterpret_cast<LPOVERLAPPED>(tracker));
++
++ AutoLock lock(&lock_);
++ active_targets_.insert(peer_process_id);
++
++ return SBOX_ALL_OK;
++}
++
++ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
++ DWORD last_error;
++ return AddTargetPeerInternal(peer_process, ::GetProcessId(peer_process),
++ nullptr, &last_error);
++}
++
+ ResultCode BrokerServicesBase::GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) {
+ CHECK(job_thread_.IsValid());
+ // Post to the job thread.
+ if (!::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_GET_POLICY_INFO,
+ reinterpret_cast<LPOVERLAPPED>(receiver.get()))) {
+ receiver->OnError(SBOX_ERROR_GENERIC);
+diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.h b/security/sandbox/chromium/sandbox/win/src/broker_services.h
+--- a/security/sandbox/chromium/sandbox/win/src/broker_services.h
++++ b/security/sandbox/chromium/sandbox/win/src/broker_services.h
+@@ -13,16 +13,17 @@
+
+ #include "base/compiler_specific.h"
+ #include "base/macros.h"
+ #include "base/memory/scoped_refptr.h"
+ #include "base/win/scoped_handle.h"
+ #include "sandbox/win/src/crosscall_server.h"
+ #include "sandbox/win/src/job.h"
+ #include "sandbox/win/src/sandbox.h"
++#include "sandbox/win/src/sandbox_policy_base.h"
+ #include "sandbox/win/src/sharedmem_ipc_server.h"
+ #include "sandbox/win/src/win2k_threadpool.h"
+ #include "sandbox/win/src/win_utils.h"
+
+ namespace sandbox {
+
+ // BrokerServicesBase ---------------------------------------------------------
+ // Broker implementation version 0
+@@ -43,16 +44,24 @@ class BrokerServicesBase final : public
+ scoped_refptr<TargetPolicy> CreatePolicy() override;
+ ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) override;
+ ResultCode WaitForAllTargets() override;
++ ResultCode AddTargetPeer(HANDLE peer_process) override;
++
++ // Checks if the supplied process ID matches one of the broker's active
++ // target processes. We use this method for the specific purpose of
++ // checking if we can safely duplicate a handle to the supplied process
++ // in DuplicateHandleProxyAction.
++ bool IsSafeDuplicationTarget(DWORD process_id);
++
+ ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) override;
+
+ private:
+ // The routine that the worker thread executes. It is in charge of
+ // notifications and cleanup-related tasks.
+ static DWORD WINAPI TargetEventsThread(PVOID param);
+
+@@ -65,14 +74,27 @@ class BrokerServicesBase final : public
+ base::win::ScopedHandle no_targets_;
+
+ // Handle to the worker thread that reacts to job notifications.
+ base::win::ScopedHandle job_thread_;
+
+ // Provides a pool of threads that are used to wait on the IPC calls.
+ std::unique_ptr<ThreadProvider> thread_pool_;
+
++ // The set representing the broker's active target processes including
++ // both sandboxed and unsandboxed peer processes.
++ std::set<DWORD> active_targets_;
++
++ // Lock used to protect active_targets_ from being simultaneously accessed
++ // by multiple threads.
++ CRITICAL_SECTION lock_;
++
++ ResultCode AddTargetPeerInternal(HANDLE peer_process_handle,
++ DWORD peer_process_id,
++ scoped_refptr<PolicyBase> policy_base,
++ DWORD* last_error);
++
+ DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase);
+ };
+
+ } // namespace sandbox
+
+ #endif // SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+@@ -96,16 +96,24 @@ class BrokerServices {
+
+ // This call blocks (waits) for all the targets to terminate.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode WaitForAllTargets() = 0;
+
++ // Adds an unsandboxed process as a peer for policy decisions (e.g.
++ // HANDLES_DUP_ANY policy).
++ // Returns:
++ // ALL_OK if successful. All other return values imply failure.
++ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
++ // more information.
++ virtual ResultCode AddTargetPeer(HANDLE peer_process) = 0;
++
+ // This call creates a snapshot of policies managed by the sandbox and
+ // returns them via a helper class.
+ // Parameters:
+ // receiver: The |PolicyDiagnosticsReceiver| implementation will be
+ // called to accept the results of the call.
+ // Returns:
+ // ALL_OK if the request was dispatched. All other return values
+ // imply failure, and the responder will not receive its completion
diff --git a/security/sandbox/chromium-shim/patches/with_update/revert_remove_BrokerDuplicateHandle.patch b/security/sandbox/chromium-shim/patches/with_update/revert_remove_BrokerDuplicateHandle.patch
new file mode 100644
index 0000000000..970c0d1db2
--- /dev/null
+++ b/security/sandbox/chromium-shim/patches/with_update/revert_remove_BrokerDuplicateHandle.patch
@@ -0,0 +1,743 @@
+# HG changeset patch
+# User Toshihito Kikuchi <tkikuchi@mozilla.com>
+# Date 1589671733 25200
+# Sat May 16 16:28:53 2020 -0700
+# Node ID 91bb5c3807cfe657cc24c9a3c217dd1f57db6d5c
+# Parent 22eb0bf7180801edf775be44cf299a50e01eb7bf
+Reinstate sandbox::TargetServices::BrokerDuplicateHandle. r=bobowen
+
+This patch reverts the commit removing sandbox::TargetServices::BrokerDuplicateHandle
+and applies the new IpcTag type.
+
+https://chromium.googlesource.com/chromium/src.git/+/569193665184525ca366e65d0735f5c851106e43
+https://chromium.googlesource.com/chromium/src.git/+/c8cff7f9663ce6d1ef35e5c717f43c867c3906eb
+
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc
+@@ -0,0 +1,93 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "sandbox/win/src/handle_dispatcher.h"
++
++#include <stdint.h>
++
++#include "base/win/scoped_handle.h"
++#include "sandbox/win/src/handle_interception.h"
++#include "sandbox/win/src/handle_policy.h"
++#include "sandbox/win/src/ipc_tags.h"
++#include "sandbox/win/src/policy_broker.h"
++#include "sandbox/win/src/policy_params.h"
++#include "sandbox/win/src/sandbox.h"
++#include "sandbox/win/src/sandbox_nt_util.h"
++#include "sandbox/win/src/sandbox_types.h"
++#include "sandbox/win/src/sandbox_utils.h"
++
++namespace sandbox {
++
++HandleDispatcher::HandleDispatcher(PolicyBase* policy_base)
++ : policy_base_(policy_base) {
++ static const IPCCall duplicate_handle_proxy = {
++ {IpcTag::DUPLICATEHANDLEPROXY,
++ {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}},
++ reinterpret_cast<CallbackGeneric>(
++ &HandleDispatcher::DuplicateHandleProxy)};
++
++ ipc_calls_.push_back(duplicate_handle_proxy);
++}
++
++bool HandleDispatcher::SetupService(InterceptionManager* manager,
++ IpcTag service) {
++ // We perform no interceptions for handles right now.
++ switch (service) {
++ case IpcTag::DUPLICATEHANDLEPROXY:
++ return true;
++
++ default:
++ return false;
++ }
++}
++
++bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc,
++ HANDLE source_handle,
++ uint32_t target_process_id,
++ uint32_t desired_access,
++ uint32_t options) {
++ static NtQueryObject QueryObject = NULL;
++ if (!QueryObject)
++ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
++
++ // Get a copy of the handle for use in the broker process.
++ HANDLE handle_temp;
++ if (!::DuplicateHandle(ipc->client_info->process, source_handle,
++ ::GetCurrentProcess(), &handle_temp,
++ 0, FALSE, DUPLICATE_SAME_ACCESS | options)) {
++ ipc->return_info.win32_result = ::GetLastError();
++ return false;
++ }
++ options &= ~DUPLICATE_CLOSE_SOURCE;
++ base::win::ScopedHandle handle(handle_temp);
++
++ // Get the object type (32 characters is safe; current max is 14).
++ BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)];
++ OBJECT_TYPE_INFORMATION* type_info =
++ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer);
++ ULONG size = sizeof(buffer) - sizeof(wchar_t);
++ NTSTATUS error =
++ QueryObject(handle.Get(), ObjectTypeInformation, type_info, size, &size);
++ if (!NT_SUCCESS(error)) {
++ ipc->return_info.nt_status = error;
++ return false;
++ }
++ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
++
++ CountedParameterSet<HandleTarget> params;
++ params[HandleTarget::NAME] = ParamPickerMake(type_info->Name.Buffer);
++ params[HandleTarget::TARGET] = ParamPickerMake(target_process_id);
++
++ EvalResult eval = policy_base_->EvalPolicy(IpcTag::DUPLICATEHANDLEPROXY,
++ params.GetBase());
++ ipc->return_info.win32_result =
++ HandlePolicy::DuplicateHandleProxyAction(eval, handle.Get(),
++ target_process_id,
++ &ipc->return_info.handle,
++ desired_access, options);
++ return true;
++}
++
++} // namespace sandbox
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h
+@@ -0,0 +1,41 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_
++#define SANDBOX_SRC_HANDLE_DISPATCHER_H_
++
++#include <stdint.h>
++
++#include "base/macros.h"
++#include "sandbox/win/src/crosscall_server.h"
++#include "sandbox/win/src/sandbox_policy_base.h"
++
++namespace sandbox {
++
++// This class handles handle-related IPC calls.
++class HandleDispatcher : public Dispatcher {
++ public:
++ explicit HandleDispatcher(PolicyBase* policy_base);
++ ~HandleDispatcher() override {}
++
++ // Dispatcher interface.
++ bool SetupService(InterceptionManager* manager, IpcTag service) override;
++
++ private:
++ // Processes IPC requests coming from calls to
++ // TargetServices::DuplicateHandle() in the target.
++ bool DuplicateHandleProxy(IPCInfo* ipc,
++ HANDLE source_handle,
++ uint32_t target_process_id,
++ uint32_t desired_access,
++ uint32_t options);
++
++ PolicyBase* policy_base_;
++ DISALLOW_COPY_AND_ASSIGN(HandleDispatcher);
++};
++
++} // namespace sandbox
++
++#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
+@@ -0,0 +1,45 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "sandbox/win/src/handle_interception.h"
++
++#include "sandbox/win/src/crosscall_client.h"
++#include "sandbox/win/src/ipc_tags.h"
++#include "sandbox/win/src/sandbox_factory.h"
++#include "sandbox/win/src/sandbox_nt_util.h"
++#include "sandbox/win/src/sharedmem_ipc_client.h"
++#include "sandbox/win/src/target_services.h"
++
++namespace sandbox {
++
++ResultCode DuplicateHandleProxy(HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options) {
++ *target_handle = NULL;
++
++ void* memory = GetGlobalIPCMemory();
++ if (NULL == memory)
++ return SBOX_ERROR_NO_SPACE;
++
++ SharedMemIPCClient ipc(memory);
++ CrossCallReturn answer = {0};
++ ResultCode code = CrossCall(ipc, IpcTag::DUPLICATEHANDLEPROXY,
++ source_handle, target_process_id,
++ desired_access, options, &answer);
++ if (SBOX_ALL_OK != code)
++ return code;
++
++ if (answer.win32_result) {
++ ::SetLastError(answer.win32_result);
++ return SBOX_ERROR_GENERIC;
++ }
++
++ *target_handle = answer.handle;
++ return SBOX_ALL_OK;
++}
++
++} // namespace sandbox
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.h b/security/sandbox/chromium/sandbox/win/src/handle_interception.h
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.h
+@@ -0,0 +1,24 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "sandbox/win/src/nt_internals.h"
++#include "sandbox/win/src/sandbox_types.h"
++
++#ifndef SANDBOX_SRC_HANDLE_INTERCEPTION_H_
++#define SANDBOX_SRC_HANDLE_INTERCEPTION_H_
++
++namespace sandbox {
++
++// TODO(jschuh) Add an interception to catch dangerous DuplicateHandle calls.
++
++ResultCode DuplicateHandleProxy(HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options);
++
++} // namespace sandbox
++
++#endif // SANDBOX_SRC_HANDLE_INTERCEPTION_H_
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.cc b/security/sandbox/chromium/sandbox/win/src/handle_policy.cc
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.cc
+@@ -0,0 +1,93 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "sandbox/win/src/handle_policy.h"
++
++#include <string>
++
++#include "base/win/scoped_handle.h"
++#include "sandbox/win/src/broker_services.h"
++#include "sandbox/win/src/ipc_tags.h"
++#include "sandbox/win/src/policy_engine_opcodes.h"
++#include "sandbox/win/src/policy_params.h"
++#include "sandbox/win/src/sandbox_types.h"
++#include "sandbox/win/src/sandbox_utils.h"
++
++namespace sandbox {
++
++bool HandlePolicy::GenerateRules(const wchar_t* type_name,
++ TargetPolicy::Semantics semantics,
++ LowLevelPolicy* policy) {
++ PolicyRule duplicate_rule(ASK_BROKER);
++
++ switch (semantics) {
++ case TargetPolicy::HANDLES_DUP_ANY: {
++ if (!duplicate_rule.AddNumberMatch(IF_NOT, HandleTarget::TARGET,
++ ::GetCurrentProcessId(), EQUAL)) {
++ return false;
++ }
++ break;
++ }
++
++ case TargetPolicy::HANDLES_DUP_BROKER: {
++ if (!duplicate_rule.AddNumberMatch(IF, HandleTarget::TARGET,
++ ::GetCurrentProcessId(), EQUAL)) {
++ return false;
++ }
++ break;
++ }
++
++ default:
++ return false;
++ }
++ if (!duplicate_rule.AddStringMatch(IF, HandleTarget::NAME, type_name,
++ CASE_INSENSITIVE)) {
++ return false;
++ }
++ if (!policy->AddRule(IpcTag::DUPLICATEHANDLEPROXY, &duplicate_rule)) {
++ return false;
++ }
++ return true;
++}
++
++DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result,
++ HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options) {
++ // The only action supported is ASK_BROKER which means duplicate the handle.
++ if (ASK_BROKER != eval_result) {
++ return ERROR_ACCESS_DENIED;
++ }
++
++ base::win::ScopedHandle remote_target_process;
++ if (target_process_id != ::GetCurrentProcessId()) {
++ // Sandboxed children are dynamic, so we check that manually.
++ if (!BrokerServicesBase::GetInstance()->IsSafeDuplicationTarget(
++ target_process_id)) {
++ return ERROR_ACCESS_DENIED;
++ }
++
++ remote_target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE,
++ target_process_id));
++ if (!remote_target_process.IsValid())
++ return ::GetLastError();
++ }
++
++ // If the policy didn't block us and we have no valid target, then the broker
++ // (this process) is the valid target.
++ HANDLE target_process = remote_target_process.IsValid() ?
++ remote_target_process.Get() : ::GetCurrentProcess();
++ if (!::DuplicateHandle(::GetCurrentProcess(), source_handle, target_process,
++ target_handle, desired_access, FALSE,
++ options)) {
++ return ::GetLastError();
++ }
++
++ return ERROR_SUCCESS;
++}
++
++} // namespace sandbox
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.h b/security/sandbox/chromium/sandbox/win/src/handle_policy.h
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.h
+@@ -0,0 +1,39 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#ifndef SANDBOX_SRC_HANDLE_POLICY_H_
++#define SANDBOX_SRC_HANDLE_POLICY_H_
++
++#include <string>
++
++#include "sandbox/win/src/crosscall_server.h"
++#include "sandbox/win/src/policy_low_level.h"
++#include "sandbox/win/src/sandbox_policy.h"
++
++namespace sandbox {
++
++enum EvalResult;
++
++// This class centralizes most of the knowledge related to handle policy.
++class HandlePolicy {
++ public:
++ // Creates the required low-level policy rules to evaluate a high-level
++ // policy rule for handles, in particular duplicate action.
++ static bool GenerateRules(const wchar_t* type_name,
++ TargetPolicy::Semantics semantics,
++ LowLevelPolicy* policy);
++
++ // Processes a 'TargetPolicy::DuplicateHandle()' request from the target.
++ static DWORD DuplicateHandleProxyAction(EvalResult eval_result,
++ HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options);
++};
++
++} // namespace sandbox
++
++#endif // SANDBOX_SRC_HANDLE_POLICY_H_
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc
+new file mode 100644
+--- /dev/null
++++ b/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc
+@@ -0,0 +1,114 @@
++// Copyright (c) 2012 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++#include "base/strings/stringprintf.h"
++#include "sandbox/win/src/handle_policy.h"
++#include "sandbox/win/src/nt_internals.h"
++#include "sandbox/win/src/sandbox.h"
++#include "sandbox/win/src/sandbox_factory.h"
++#include "sandbox/win/src/sandbox_policy.h"
++#include "sandbox/win/src/win_utils.h"
++#include "sandbox/win/tests/common/controller.h"
++#include "testing/gtest/include/gtest/gtest.h"
++
++namespace sandbox {
++
++// Just waits for the supplied number of milliseconds.
++SBOX_TESTS_COMMAND int Handle_WaitProcess(int argc, wchar_t **argv) {
++ if (argc != 1)
++ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
++
++ ::Sleep(::wcstoul(argv[0], NULL, 10));
++ return SBOX_TEST_TIMED_OUT;
++}
++
++// Attempts to duplicate an event handle into the target process.
++SBOX_TESTS_COMMAND int Handle_DuplicateEvent(int argc, wchar_t **argv) {
++ if (argc != 1)
++ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
++
++ // Create a test event to use as a handle.
++ base::win::ScopedHandle test_event;
++ test_event.Set(::CreateEvent(NULL, TRUE, TRUE, NULL));
++ if (!test_event.IsValid())
++ return SBOX_TEST_FIRST_ERROR;
++
++ // Get the target process ID.
++ DWORD target_process_id = ::wcstoul(argv[0], NULL, 10);
++
++ HANDLE handle = NULL;
++ ResultCode result = SandboxFactory::GetTargetServices()->DuplicateHandle(
++ test_event.Get(), target_process_id, &handle, 0, DUPLICATE_SAME_ACCESS);
++
++ return (result == SBOX_ALL_OK) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
++}
++
++// Tests that duplicating an object works only when the policy allows it.
++TEST(HandlePolicyTest, DuplicateHandle) {
++ TestRunner target;
++ TestRunner runner;
++
++ // Kick off an asynchronous target process for testing.
++ target.SetAsynchronous(true);
++ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
++
++ // First test that we fail to open the event.
++ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
++ target.process_id());
++ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
++
++ // Now successfully open the event after adding a duplicate handle rule.
++ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
++ TargetPolicy::HANDLES_DUP_ANY,
++ L"Event"));
++ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
++}
++
++// Tests that duplicating an object works only when the policy allows it.
++TEST(HandlePolicyTest, DuplicatePeerHandle) {
++ TestRunner target;
++ TestRunner runner;
++
++ // Kick off an asynchronous target process for testing.
++ target.SetAsynchronous(true);
++ target.SetUnsandboxed(true);
++ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
++
++ // First test that we fail to open the event.
++ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
++ target.process_id());
++ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
++
++ // Now successfully open the event after adding a duplicate handle rule.
++ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
++ TargetPolicy::HANDLES_DUP_ANY,
++ L"Event"));
++ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
++}
++
++// Tests that duplicating an object works only when the policy allows it.
++TEST(HandlePolicyTest, DuplicateBrokerHandle) {
++ TestRunner runner;
++
++ // First test that we fail to open the event.
++ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
++ ::GetCurrentProcessId());
++ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
++
++ // Add the peer rule and make sure we fail again.
++ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
++ TargetPolicy::HANDLES_DUP_ANY,
++ L"Event"));
++ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
++
++
++ // Now successfully open the event after adding a broker handle rule.
++ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
++ TargetPolicy::HANDLES_DUP_BROKER,
++ L"Event"));
++ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
++}
++
++} // namespace sandbox
++
+diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_tags.h b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
+--- a/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
++++ b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
+@@ -23,16 +23,17 @@ enum class IpcTag {
+ NTOPENPROCESS,
+ NTOPENPROCESSTOKEN,
+ NTOPENPROCESSTOKENEX,
+ CREATEPROCESSW,
+ CREATEEVENT,
+ OPENEVENT,
+ NTCREATEKEY,
+ NTOPENKEY,
++ DUPLICATEHANDLEPROXY,
+ GDI_GDIDLLINITIALIZE,
+ GDI_GETSTOCKOBJECT,
+ USER_REGISTERCLASSW,
+ CREATETHREAD,
+ USER_ENUMDISPLAYMONITORS,
+ USER_ENUMDISPLAYDEVICES,
+ USER_GETMONITORINFO,
+ GDI_CREATEOPMPROTECTEDOUTPUTS,
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
+@@ -161,16 +161,30 @@ class TargetServices {
+ // fails the current process could be terminated immediately.
+ virtual void LowerToken() = 0;
+
+ // Returns the ProcessState object. Through that object it's possible to have
+ // information about the current state of the process, such as whether
+ // LowerToken has been called or not.
+ virtual ProcessState* GetState() = 0;
+
++ // Requests the broker to duplicate the supplied handle into the target
++ // process. The target process must be an active sandbox child process
++ // and the source process must have a corresponding policy allowing
++ // handle duplication for this object type.
++ // Returns:
++ // ALL_OK if successful. All other return values imply failure.
++ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
++ // more information.
++ virtual ResultCode DuplicateHandle(HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options) = 0;
++
+ protected:
+ ~TargetServices() {}
+ };
+
+ class PolicyInfo {
+ public:
+ // Returns a JSON representation of the policy snapshot.
+ // This pointer has the same lifetime as this PolicyInfo object.
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
+@@ -25,28 +25,32 @@ class TargetPolicy {
+ // exactly like the CreateProcess API does. See the comment at the top of
+ // process_thread_dispatcher.cc for more details.
+ enum SubSystem {
+ SUBSYS_FILES, // Creation and opening of files and pipes.
+ SUBSYS_NAMED_PIPES, // Creation of named pipes.
+ SUBSYS_PROCESS, // Creation of child processes.
+ SUBSYS_REGISTRY, // Creation and opening of registry keys.
+ SUBSYS_SYNC, // Creation of named sync objects.
++ SUBSYS_HANDLES, // Duplication of handles to other processes.
+ SUBSYS_WIN32K_LOCKDOWN, // Win32K Lockdown related policy.
+ SUBSYS_SIGNED_BINARY // Signed binary policy.
+ };
+
+ // Allowable semantics when a rule is matched.
+ enum Semantics {
+ FILES_ALLOW_ANY, // Allows open or create for any kind of access that
+ // the file system supports.
+ FILES_ALLOW_READONLY, // Allows open or create with read access only.
+ FILES_ALLOW_QUERY, // Allows access to query the attributes of a file.
+ FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics
+ // only.
++ HANDLES_DUP_ANY, // Allows duplicating handles opened with any
++ // access permissions.
++ HANDLES_DUP_BROKER, // Allows duplicating handles to the broker process.
+ NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe.
+ PROCESS_MIN_EXEC, // Allows to create a process with minimal rights
+ // over the resulting process and thread handles.
+ // No other parameters besides the command line are
+ // passed to the child process.
+ PROCESS_ALL_EXEC, // Allows the creation of a process and return full
+ // access on the returned handles.
+ // This flag can be used only when the main token of
+diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+--- a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
++++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
+@@ -12,16 +12,17 @@
+ #include "base/logging.h"
+ #include "base/macros.h"
+ #include "base/stl_util.h"
+ #include "base/strings/stringprintf.h"
+ #include "base/win/win_util.h"
+ #include "base/win/windows_version.h"
+ #include "sandbox/win/src/acl.h"
+ #include "sandbox/win/src/filesystem_policy.h"
++#include "sandbox/win/src/handle_policy.h"
+ #include "sandbox/win/src/interception.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.h"
+ #include "sandbox/win/src/process_mitigations_win32k_policy.h"
+@@ -754,16 +755,24 @@ ResultCode PolicyBase::AddRuleInternal(S
+ }
+ case SUBSYS_REGISTRY: {
+ if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
++ case SUBSYS_HANDLES: {
++ if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
++ NOTREACHED();
++ return SBOX_ERROR_BAD_PARAMS;
++ }
++ break;
++ }
++
+ case SUBSYS_WIN32K_LOCKDOWN: {
+ // Win32k intercept rules only supported on Windows 8 and above. This must
+ // match the version checks in process_mitigations.cc for consistency.
+ if (base::win::GetVersion() >= base::win::Version::WIN8) {
+ DCHECK_EQ(MITIGATION_WIN32K_DISABLE,
+ mitigations_ & MITIGATION_WIN32K_DISABLE)
+ << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
+ "rules.";
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc
+--- a/security/sandbox/chromium/sandbox/win/src/target_services.cc
++++ b/security/sandbox/chromium/sandbox/win/src/target_services.cc
+@@ -7,16 +7,17 @@
+ #include <new>
+
+ #include <process.h>
+ #include <stdint.h>
+
+ #include "base/win/windows_version.h"
+ #include "sandbox/win/src/crosscall_client.h"
+ #include "sandbox/win/src/handle_closer_agent.h"
++#include "sandbox/win/src/handle_interception.h"
+ #include "sandbox/win/src/heap_helper.h"
+ #include "sandbox/win/src/ipc_tags.h"
+ #include "sandbox/win/src/process_mitigations.h"
+ #include "sandbox/win/src/restricted_token_utils.h"
+ #include "sandbox/win/src/sandbox.h"
+ #include "sandbox/win/src/sandbox_nt_util.h"
+ #include "sandbox/win/src/sandbox_types.h"
+ #include "sandbox/win/src/sharedmem_ipc_client.h"
+@@ -239,9 +240,19 @@ void ProcessState::SetRevertedToSelf() {
+ if (process_state_ < ProcessStateInternal::REVERTED_TO_SELF)
+ process_state_ = ProcessStateInternal::REVERTED_TO_SELF;
+ }
+
+ void ProcessState::SetCsrssConnected(bool csrss_connected) {
+ csrss_connected_ = csrss_connected;
+ }
+
++
++ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options) {
++ return sandbox::DuplicateHandleProxy(source_handle, target_process_id,
++ target_handle, desired_access, options);
++}
++
+ } // namespace sandbox
+diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.h b/security/sandbox/chromium/sandbox/win/src/target_services.h
+--- a/security/sandbox/chromium/sandbox/win/src/target_services.h
++++ b/security/sandbox/chromium/sandbox/win/src/target_services.h
+@@ -40,16 +40,21 @@ class ProcessState {
+ class TargetServicesBase : public TargetServices {
+ public:
+ TargetServicesBase();
+
+ // Public interface of TargetServices.
+ ResultCode Init() override;
+ void LowerToken() override;
+ ProcessState* GetState() override;
++ ResultCode DuplicateHandle(HANDLE source_handle,
++ DWORD target_process_id,
++ HANDLE* target_handle,
++ DWORD desired_access,
++ DWORD options) override;
+
+ // Factory method.
+ static TargetServicesBase* GetInstance();
+
+ // Sends a simple IPC Message that has a well-known answer. Returns true
+ // if the IPC was successful and false otherwise. There are 2 versions of
+ // this test: 1 and 2. The first one send a simple message while the
+ // second one send a message with an in/out param.
+diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
+--- a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
++++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
+@@ -5,16 +5,17 @@
+ #include "sandbox/win/src/top_level_dispatcher.h"
+
+ #include <stdint.h>
+ #include <string.h>
+
+ #include "base/logging.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"
+@@ -55,16 +56,20 @@ TopLevelDispatcher::TopLevelDispatcher(P
+ ipc_targets_[static_cast<size_t>(IpcTag::OPENEVENT)] = dispatcher;
+ sync_dispatcher_.reset(dispatcher);
+
+ dispatcher = new RegistryDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATEKEY)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENKEY)] = dispatcher;
+ registry_dispatcher_.reset(dispatcher);
+
++ dispatcher = new HandleDispatcher(policy_);
++ ipc_targets_[static_cast<size_t>(IpcTag::DUPLICATEHANDLEPROXY)] = dispatcher;
++ handle_dispatcher_.reset(dispatcher);
++
+ dispatcher = new ProcessMitigationsWin32KDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GDIDLLINITIALIZE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETSTOCKOBJECT)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_REGISTERCLASSW)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_ENUMDISPLAYMONITORS)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_ENUMDISPLAYDEVICES)] =
+ dispatcher;
diff --git a/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h b/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h
new file mode 100644
index 0000000000..3c5f8eea89
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h
@@ -0,0 +1,101 @@
+/* -*- 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/. */
+
+#ifndef security_sandbox_loggingCallbacks_h__
+#define security_sandbox_loggingCallbacks_h__
+
+#include <sstream>
+
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/sandboxing/loggingTypes.h"
+#include "nsContentUtils.h"
+
+#include "mozilla/StackWalk.h"
+
+namespace mozilla {
+
+static LazyLogModule sSandboxTargetLog("SandboxTarget");
+
+#define LOG_D(...) MOZ_LOG(sSandboxTargetLog, LogLevel::Debug, (__VA_ARGS__))
+
+namespace sandboxing {
+
+// NS_WalkStackCallback to write a formatted stack frame to an ostringstream.
+static void
+StackFrameToOStringStream(uint32_t aFrameNumber, void* aPC, void* aSP,
+ void* aClosure)
+{
+ std::ostringstream* stream = static_cast<std::ostringstream*>(aClosure);
+ MozCodeAddressDetails details;
+ char buf[1024];
+ MozDescribeCodeAddress(aPC, &details);
+ MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+ *stream << std::endl << "--" << buf;
+ stream->flush();
+}
+
+// Log to the browser console and, if DEBUG build, stderr.
+static void
+Log(const char* aMessageType,
+ const char* aFunctionName,
+ const char* aContext,
+ const bool aShouldLogStackTrace = false,
+ const void* aFirstFramePC = nullptr)
+{
+ std::ostringstream msgStream;
+ msgStream << "Process Sandbox " << aMessageType << ": " << aFunctionName;
+ if (aContext) {
+ msgStream << " for : " << aContext;
+ }
+
+#if defined(MOZ_SANDBOX)
+ // We can only log the stack trace on process types where we know that the
+ // sandbox won't prevent it.
+ if (XRE_IsContentProcess() && aShouldLogStackTrace) {
+ auto stackTraceDepth =
+ StaticPrefs::security_sandbox_windows_log_stackTraceDepth();
+ if (stackTraceDepth) {
+ msgStream << std::endl << "Stack Trace:";
+ MozStackWalk(StackFrameToOStringStream, aFirstFramePC, stackTraceDepth,
+ &msgStream);
+ }
+ }
+#endif
+ std::string msg = msgStream.str();
+#if defined(DEBUG)
+ // Use NS_DebugBreak directly as we want child process prefix, but not source
+ // file or line number.
+ NS_DebugBreak(NS_DEBUG_WARNING, nullptr, msg.c_str(), nullptr, -1);
+#endif
+
+ if (nsContentUtils::IsInitialized()) {
+ nsContentUtils::LogMessageToConsole(msg.c_str());
+ }
+
+ // As we don't always have the facility to log to console use MOZ_LOG as well.
+ LOG_D("%s", msg.c_str());
+}
+
+// Initialize sandbox logging if required.
+static void
+InitLoggingIfRequired(ProvideLogFunctionCb aProvideLogFunctionCb)
+{
+ if (!aProvideLogFunctionCb) {
+ return;
+ }
+
+ if (Preferences::GetBool("security.sandbox.logging.enabled") ||
+ PR_GetEnv("MOZ_SANDBOX_LOGGING")) {
+ aProvideLogFunctionCb(Log);
+ }
+}
+
+} // sandboxing
+} // mozilla
+
+#endif // security_sandbox_loggingCallbacks_h__
diff --git a/security/sandbox/chromium-shim/sandbox/win/loggingTypes.h b/security/sandbox/chromium-shim/sandbox/win/loggingTypes.h
new file mode 100644
index 0000000000..7b75648fc9
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/loggingTypes.h
@@ -0,0 +1,27 @@
+/* -*- 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/. */
+
+#ifndef security_sandbox_loggingTypes_h__
+#define security_sandbox_loggingTypes_h__
+
+#include <stdint.h>
+
+namespace mozilla {
+namespace sandboxing {
+
+// We are using callbacks here that are passed in from the core code to prevent
+// a circular dependency in the linking during the build.
+typedef void (*LogFunction) (const char* aMessageType,
+ const char* aFunctionName,
+ const char* aContext,
+ const bool aShouldLogStackTrace,
+ const void* aFirstFramePC);
+typedef void (*ProvideLogFunctionCb) (LogFunction aLogFunction);
+
+} // sandboxing
+} // mozilla
+
+#endif // security_sandbox_loggingTypes_h__
diff --git a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp
new file mode 100644
index 0000000000..a556f9e772
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp
@@ -0,0 +1,89 @@
+/* -*- 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/. */
+
+#include "sandboxLogging.h"
+
+#include "base/strings/utf_string_conversions.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/StackWalk.h"
+
+namespace mozilla {
+namespace sandboxing {
+
+static LogFunction sLogFunction = nullptr;
+
+void
+ProvideLogFunction(LogFunction aLogFunction)
+{
+ sLogFunction = aLogFunction;
+}
+
+static void
+LogBlocked(const char* aFunctionName, const char* aContext, const void* aFirstFramePC)
+{
+ if (sLogFunction) {
+ sLogFunction("BLOCKED", aFunctionName, aContext,
+ /* aShouldLogStackTrace */ true, aFirstFramePC);
+ }
+}
+
+MOZ_NEVER_INLINE void
+LogBlocked(const char* aFunctionName, const char* aContext)
+{
+ if (sLogFunction) {
+ LogBlocked(aFunctionName, aContext, CallerPC());
+ }
+}
+
+MOZ_NEVER_INLINE void
+LogBlocked(const char* aFunctionName, const wchar_t* aContext)
+{
+ if (sLogFunction) {
+ LogBlocked(aFunctionName, base::WideToUTF8(aContext).c_str(), CallerPC());
+ }
+}
+
+MOZ_NEVER_INLINE void
+LogBlocked(const char* aFunctionName, const wchar_t* aContext,
+ uint16_t aLengthInBytes)
+{
+ if (sLogFunction) {
+ LogBlocked(aFunctionName,
+ base::WideToUTF8(std::wstring(aContext, aLengthInBytes / sizeof(wchar_t))).c_str(),
+ CallerPC());
+ }
+}
+
+void
+LogAllowed(const char* aFunctionName, const char* aContext)
+{
+ if (sLogFunction) {
+ sLogFunction("Broker ALLOWED", aFunctionName, aContext,
+ /* aShouldLogStackTrace */ false, nullptr);
+ }
+}
+
+void
+LogAllowed(const char* aFunctionName, const wchar_t* aContext)
+{
+ if (sLogFunction) {
+ LogAllowed(aFunctionName, base::WideToUTF8(aContext).c_str());
+ }
+}
+
+void
+LogAllowed(const char* aFunctionName, const wchar_t* aContext,
+ uint16_t aLengthInBytes)
+{
+ if (sLogFunction) {
+ LogAllowed(aFunctionName,
+ base::WideToUTF8(std::wstring(aContext, aLengthInBytes / sizeof(wchar_t))).c_str());
+ }
+}
+
+} // sandboxing
+} // mozilla
diff --git a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h
new file mode 100644
index 0000000000..31c4ddb076
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+
+/*
+ * Set of helper methods to implement logging for Windows sandbox.
+ */
+
+#ifndef security_sandbox_sandboxLogging_h__
+#define security_sandbox_sandboxLogging_h__
+
+#include "loggingTypes.h"
+
+namespace sandbox {
+class TargetPolicy;
+}
+
+namespace mozilla {
+namespace sandboxing {
+
+// This is used to pass a LogCallback to the sandboxing code, as the logging
+// requires code to which we cannot link directly.
+void ProvideLogFunction(LogFunction aLogFunction);
+
+// Log a "BLOCKED" msg to the browser console and, if DEBUG build, stderr.
+// If the logging of a stack trace is enabled then a trace starting from the
+// caller of the relevant LogBlocked overload will be logged, which should
+// normally be the function that triggered the interception.
+void LogBlocked(const char* aFunctionName, const char* aContext = nullptr);
+
+// Convenience functions to convert to char*.
+void LogBlocked(const char* aFunctionName, const wchar_t* aContext);
+void LogBlocked(const char* aFunctionName, const wchar_t* aContext,
+ uint16_t aLengthInBytes);
+
+// Log a "ALLOWED" msg to the browser console and, if DEBUG build, stderr.
+void LogAllowed(const char* aFunctionName, const char* aContext = nullptr);
+
+// Convenience functions to convert to char*.
+void LogAllowed(const char* aFunctionName, const wchar_t* aContext);
+void LogAllowed(const char* aFunctionName, const wchar_t* aContext,
+ uint16_t aLengthInBytes);
+
+
+} // sandboxing
+} // mozilla
+
+#endif // security_sandbox_sandboxLogging_h__
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_common.h b/security/sandbox/chromium-shim/sandbox/win/src/line_break_common.h
new file mode 100644
index 0000000000..b712239dde
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_common.h
@@ -0,0 +1,31 @@
+/* -*- 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/. */
+
+#ifndef SANDBOX_SRC_LINE_BREAK_COMMON_H_
+#define SANDBOX_SRC_LINE_BREAK_COMMON_H_
+
+#include "sandbox/win/src/crosscall_params.h"
+
+namespace sandbox {
+
+#if defined(MOZ_DEBUG)
+// Set a low max brokered length for testing to exercise the chunking code.
+static const std::ptrdiff_t kMaxBrokeredLen = 50;
+
+#else
+// Parameters are stored aligned to sizeof(int64_t).
+// So to calculate the maximum length we can use when brokering to the parent,
+// we take the max params buffer size, take off 8 for the aligned length and 6
+// and 7 to allow for the maximum padding that can be added to the text and
+// break before buffers. We then divide by three, because the text characters
+// are wchar_t and the break before elements are uint8_t.
+static const std::ptrdiff_t kMaxBrokeredLen =
+ (ActualCallParams<3, kIPCChannelSize>::MaxParamsSize() - 8 - 6 - 7) / 3;
+#endif
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_LINE_BREAK_COMMON_H_
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.cc b/security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.cc
new file mode 100644
index 0000000000..94401d18fa
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.cc
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+#include "sandbox/win/src/line_break_dispatcher.h"
+
+#include "sandbox/win/src/line_break_common.h"
+#include "sandbox/win/src/line_break_policy.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+
+namespace sandbox {
+
+LineBreakDispatcher::LineBreakDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall get_complex_line_breaks = {
+ {IpcTag::GETCOMPLEXLINEBREAKS, {INPTR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &LineBreakDispatcher::GetComplexLineBreaksCall)};
+
+ ipc_calls_.push_back(get_complex_line_breaks);
+}
+
+bool LineBreakDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ // We perform no interceptions for line breaking right now.
+ switch (service) {
+ case IpcTag::GETCOMPLEXLINEBREAKS:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool LineBreakDispatcher::GetComplexLineBreaksCall(
+ IPCInfo* ipc, CountedBuffer* text_buf, uint32_t length,
+ CountedBuffer* break_before_buf) {
+ if (length > kMaxBrokeredLen ||
+ text_buf->Size() != length * sizeof(wchar_t) ||
+ break_before_buf->Size() != length) {
+ return false;
+ }
+
+ CountedParameterSet<EmptyParams> params;
+ EvalResult eval =
+ policy_base_->EvalPolicy(IpcTag::GETCOMPLEXLINEBREAKS, params.GetBase());
+ auto* text = static_cast<wchar_t*>(text_buf->Buffer());
+ auto* break_before = static_cast<uint8_t*>(break_before_buf->Buffer());
+ ipc->return_info.win32_result =
+ LineBreakPolicy::GetComplexLineBreaksProxyAction(eval, text, length,
+ break_before);
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.h b/security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.h
new file mode 100644
index 0000000000..774b5c5b56
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_dispatcher.h
@@ -0,0 +1,38 @@
+/* -*- 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/. */
+
+#ifndef SANDBOX_SRC_LINE_BREAK_DISPATCHER_H_
+#define SANDBOX_SRC_LINE_BREAK_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles line break related IPC calls.
+class LineBreakDispatcher final : public Dispatcher {
+ public:
+ explicit LineBreakDispatcher(PolicyBase* policy_base);
+ ~LineBreakDispatcher() final {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) final;
+
+ private:
+ // Processes IPC requests coming from calls to
+ // TargetServices::GetComplexLineBreaks() in the target.
+ bool GetComplexLineBreaksCall(IPCInfo* ipc, CountedBuffer* text_buf,
+ uint32_t length,
+ CountedBuffer* break_before_buf);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(LineBreakDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_LINE_BREAK_DISPATCHER_H_
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.cc b/security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.cc
new file mode 100644
index 0000000000..f2dcda0dc9
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.cc
@@ -0,0 +1,108 @@
+/* -*- 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/. */
+
+#include "sandbox/win/src/line_break_interception.h"
+
+#include <winnls.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/line_break_common.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+namespace sandbox {
+
+static const int kBreakSearchRange = 32;
+
+ResultCode GetComplexLineBreaksProxy(const wchar_t* aText, uint32_t aLength,
+ uint8_t* aBreakBefore) {
+ // Make sure that a test length for kMaxBrokeredLen hasn't been set too small
+ // allowing for a surrogate pair at the end of a chunk as well.
+ DCHECK(kMaxBrokeredLen > kBreakSearchRange + 1);
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory) {
+ return SBOX_ERROR_NO_SPACE;
+ }
+
+ memset(aBreakBefore, false, aLength);
+
+ SharedMemIPCClient ipc(memory);
+
+ uint8_t* breakBeforeIter = aBreakBefore;
+ const wchar_t* textIterEnd = aText + aLength;
+ do {
+ // Next chunk is either the remaining text or kMaxBrokeredLen long.
+ const wchar_t* textIter = aText + (breakBeforeIter - aBreakBefore);
+ const wchar_t* chunkEnd = textIter + kMaxBrokeredLen;
+ if (chunkEnd < textIterEnd) {
+ // Make sure we don't split a surrogate pair.
+ if (IS_HIGH_SURROGATE(*(chunkEnd - 1))) {
+ --chunkEnd;
+ }
+ } else {
+ // This chunk handles all the (remaining) text.
+ chunkEnd = textIterEnd;
+ }
+
+ // Uniscribe seems to often (perhaps always) set the first element to a
+ // break, so we use chunk_start_reset to hold the known value of the first
+ // element of a chunk and reset it after Uniscribe processing. The only time
+ // we don't start from an already processed element is the first call, but
+ // resetting this to false is correct because whether we can break before
+ // the first character is decided by our caller.
+ uint8_t chunk_start_reset = *breakBeforeIter;
+
+ uint32_t len = chunkEnd - textIter;
+ // CountedBuffer takes a wchar_t* even though it doesn't change the buffer.
+ CountedBuffer textBuf(const_cast<wchar_t*>(textIter),
+ sizeof(wchar_t) * len);
+ InOutCountedBuffer breakBeforeBuf(breakBeforeIter, len);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::GETCOMPLEXLINEBREAKS, textBuf, len,
+ breakBeforeBuf, &answer);
+ if (SBOX_ALL_OK != code) {
+ return code;
+ }
+
+ if (answer.win32_result) {
+ ::SetLastError(answer.win32_result);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ *breakBeforeIter = chunk_start_reset;
+
+ if (chunkEnd == textIterEnd) {
+ break;
+ }
+
+ // We couldn't process all of the text in one go, so back up by 32 chars and
+ // look for a break, then continue from that position. We back up 32 chars
+ // to try to avoid any false breaks at the end of the buffer caused by us
+ // splitting it into chunks.
+ uint8_t* processedToEnd = breakBeforeIter + len;
+ breakBeforeIter = processedToEnd - kBreakSearchRange;
+ while (!*breakBeforeIter) {
+ if (++breakBeforeIter == processedToEnd) {
+ // We haven't found a break in the search range, so go back to the start
+ // of our search range to try and ensure we don't get any false breaks
+ // at the start of the new chunk.
+ breakBeforeIter = processedToEnd - kBreakSearchRange;
+ // Make sure we don't split a surrogate pair.
+ if (IS_LOW_SURROGATE(
+ *(aText + (breakBeforeIter - aBreakBefore)))) {
+ ++breakBeforeIter;
+ }
+ break;
+ }
+ }
+ } while (true);
+
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.h b/security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.h
new file mode 100644
index 0000000000..87681e2e90
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_interception.h
@@ -0,0 +1,19 @@
+/* -*- 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/. */
+
+#ifndef SANDBOX_SRC_LINE_BREAK_INTERCEPTION_H_
+#define SANDBOX_SRC_LINE_BREAK_INTERCEPTION_H_
+
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+ResultCode GetComplexLineBreaksProxy(const wchar_t* text, uint32_t length,
+ uint8_t* break_before);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_LINE_BREAK_INTERCEPTION_H_
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.cc b/security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.cc
new file mode 100644
index 0000000000..5533232643
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.cc
@@ -0,0 +1,66 @@
+/* -*- 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/. */
+
+#include "sandbox/win/src/line_break_policy.h"
+
+#include <algorithm>
+#include <array>
+#include <usp10.h>
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/line_break_common.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+
+namespace sandbox {
+
+bool LineBreakPolicy::GenerateRules(const wchar_t* null,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ if (TargetPolicy::LINE_BREAK_ALLOW != semantics) {
+ return false;
+ }
+
+ PolicyRule line_break_rule(ASK_BROKER);
+ if (!policy->AddRule(IpcTag::GETCOMPLEXLINEBREAKS, &line_break_rule)) {
+ return false;
+ }
+ return true;
+}
+
+/* static */ DWORD LineBreakPolicy::GetComplexLineBreaksProxyAction(
+ EvalResult eval_result, const wchar_t* text, uint32_t length,
+ uint8_t* break_before) {
+ // The only action supported is ASK_BROKER which means call the line breaker.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ int outItems = 0;
+ std::array<SCRIPT_ITEM, kMaxBrokeredLen + 1> items;
+ HRESULT result = ::ScriptItemize(text, length, kMaxBrokeredLen, nullptr,
+ nullptr, items.data(), &outItems);
+ if (result != 0) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ std::array<SCRIPT_LOGATTR, kMaxBrokeredLen> slas;
+ for (int iItem = 0; iItem < outItems; ++iItem) {
+ uint32_t endOffset = items[iItem + 1].iCharPos;
+ uint32_t startOffset = items[iItem].iCharPos;
+ if (FAILED(::ScriptBreak(text + startOffset, endOffset - startOffset,
+ &items[iItem].a, &slas[startOffset]))) {
+ return ERROR_ACCESS_DENIED;
+ }
+ }
+
+ std::transform(slas.data(), slas.data() + length, break_before,
+ [](const SCRIPT_LOGATTR& sla) { return sla.fSoftBreak; });
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.h b/security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.h
new file mode 100644
index 0000000000..89fbf9b207
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/line_break_policy.h
@@ -0,0 +1,35 @@
+/* -*- 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/. */
+
+#ifndef SANDBOX_SRC_LINE_BREAK_POLICY_H_
+#define SANDBOX_SRC_LINE_BREAK_POLICY_H_
+
+#include "base/win/windows_types.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum EvalResult;
+
+class LineBreakPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for complex line breaks.
+ static bool GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Processes a TargetServices::GetComplexLineBreaks() request from the target.
+ static DWORD GetComplexLineBreaksProxyAction(EvalResult eval_result,
+ const wchar_t* text,
+ uint32_t length,
+ uint8_t* break_before);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_LINE_BREAK_POLICY_H_
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/sandbox_policy_diagnostic.h b/security/sandbox/chromium-shim/sandbox/win/src/sandbox_policy_diagnostic.h
new file mode 100644
index 0000000000..5b37ccc556
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/sandbox_policy_diagnostic.h
@@ -0,0 +1,31 @@
+/* -*- 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 partial implementation of Chromium's source file
+// //sandbox/win/src/sandbox_policy_diagnostic.h
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_
+
+#include "mozilla/Assertions.h"
+
+namespace sandbox {
+
+class PolicyBase;
+
+class PolicyDiagnostic final : public PolicyInfo {
+ public:
+ PolicyDiagnostic(PolicyBase*) {}
+ ~PolicyDiagnostic() override = default;
+ const char* JsonString() override { MOZ_CRASH(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PolicyDiagnostic);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_DIAGNOSTIC_H_
diff --git a/security/sandbox/chromium-shim/sandbox/win/src/sidestep_resolver.h b/security/sandbox/chromium-shim/sandbox/win/src/sidestep_resolver.h
new file mode 100644
index 0000000000..fe38484b0e
--- /dev/null
+++ b/security/sandbox/chromium-shim/sandbox/win/src/sidestep_resolver.h
@@ -0,0 +1,58 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+// This is a dummy version of the Chromium source file
+// sandbox/win/src/sidestep_resolver.h, which contains classes that are never
+// actually used. We crash in the member functions to ensure this.
+// Formatting and guards closely match original file for easy comparison.
+
+#ifndef SANDBOX_SRC_SIDESTEP_RESOLVER_H__
+#define SANDBOX_SRC_SIDESTEP_RESOLVER_H__
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+#include "mozilla/Assertions.h"
+
+namespace sandbox {
+
+class SidestepResolverThunk : public ResolverThunk {
+ public:
+ SidestepResolverThunk() {}
+ ~SidestepResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) override { MOZ_CRASH(); }
+
+ size_t GetThunkSize() const override { MOZ_CRASH(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SidestepResolverThunk);
+};
+
+class SmartSidestepResolverThunk : public SidestepResolverThunk {
+ public:
+ SmartSidestepResolverThunk() {}
+ ~SmartSidestepResolverThunk() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SmartSidestepResolverThunk);
+};
+
+} // namespace sandbox
+
+
+#endif // SANDBOX_SRC_SIDESTEP_RESOLVER_H__
diff --git a/security/sandbox/chromium/LICENSE b/security/sandbox/chromium/LICENSE
new file mode 100644
index 0000000000..a32e00ce6b
--- /dev/null
+++ b/security/sandbox/chromium/LICENSE
@@ -0,0 +1,27 @@
+// 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
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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/at_exit.cc b/security/sandbox/chromium/base/at_exit.cc
new file mode 100644
index 0000000000..eb7d26cdc7
--- /dev/null
+++ b/security/sandbox/chromium/base/at_exit.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/at_exit.h"
+
+#include <stddef.h>
+#include <ostream>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/logging.h"
+
+namespace base {
+
+// Keep a stack of registered AtExitManagers. We always operate on the most
+// recent, and we should never have more than one outside of testing (for a
+// statically linked version of this library). Testing may use the shadow
+// version of the constructor, and if we are building a dynamic library we may
+// end up with multiple AtExitManagers on the same process. We don't protect
+// this for thread-safe access, since it will only be modified in testing.
+static AtExitManager* g_top_manager = nullptr;
+
+static bool g_disable_managers = false;
+
+AtExitManager::AtExitManager() : next_manager_(g_top_manager) {
+// If multiple modules instantiate AtExitManagers they'll end up living in this
+// module... they have to coexist.
+#if !defined(COMPONENT_BUILD)
+ DCHECK(!g_top_manager);
+#endif
+ g_top_manager = this;
+}
+
+AtExitManager::~AtExitManager() {
+ if (!g_top_manager) {
+ NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
+ return;
+ }
+ DCHECK_EQ(this, g_top_manager);
+
+ if (!g_disable_managers)
+ ProcessCallbacksNow();
+ g_top_manager = next_manager_;
+}
+
+// static
+void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
+ DCHECK(func);
+ RegisterTask(base::BindOnce(func, param));
+}
+
+// static
+void AtExitManager::RegisterTask(base::OnceClosure task) {
+ if (!g_top_manager) {
+ NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
+ return;
+ }
+
+ AutoLock lock(g_top_manager->lock_);
+#if DCHECK_IS_ON()
+ DCHECK(!g_top_manager->processing_callbacks_);
+#endif
+ g_top_manager->stack_.push(std::move(task));
+}
+
+// static
+void AtExitManager::ProcessCallbacksNow() {
+ if (!g_top_manager) {
+ NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
+ return;
+ }
+
+ // Callbacks may try to add new callbacks, so run them without holding
+ // |lock_|. This is an error and caught by the DCHECK in RegisterTask(), but
+ // handle it gracefully in release builds so we don't deadlock.
+ base::stack<base::OnceClosure> tasks;
+ {
+ AutoLock lock(g_top_manager->lock_);
+ tasks.swap(g_top_manager->stack_);
+#if DCHECK_IS_ON()
+ g_top_manager->processing_callbacks_ = true;
+#endif
+ }
+
+ // Relax the cross-thread access restriction to non-thread-safe RefCount.
+ // It's safe since all other threads should be terminated at this point.
+ ScopedAllowCrossThreadRefCountAccess allow_cross_thread_ref_count_access;
+
+ while (!tasks.empty()) {
+ std::move(tasks.top()).Run();
+ tasks.pop();
+ }
+
+#if DCHECK_IS_ON()
+ AutoLock lock(g_top_manager->lock_);
+ // Expect that all callbacks have been run.
+ DCHECK(g_top_manager->stack_.empty());
+ g_top_manager->processing_callbacks_ = false;
+#endif
+}
+
+void AtExitManager::DisableAllAtExitManagers() {
+ AutoLock lock(g_top_manager->lock_);
+ g_disable_managers = true;
+}
+
+AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
+ DCHECK(shadow || !g_top_manager);
+ g_top_manager = this;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/at_exit.h b/security/sandbox/chromium/base/at_exit.h
new file mode 100644
index 0000000000..fa652ac0c9
--- /dev/null
+++ b/security/sandbox/chromium/base/at_exit.h
@@ -0,0 +1,87 @@
+// 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_AT_EXIT_H_
+#define BASE_AT_EXIT_H_
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/containers/stack.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+
+namespace base {
+
+// This class provides a facility similar to the CRT atexit(), except that
+// we control when the callbacks are executed. Under Windows for a DLL they
+// happen at a really bad time and under the loader lock. This facility is
+// mostly used by base::Singleton.
+//
+// The usage is simple. Early in the main() or WinMain() scope create an
+// AtExitManager object on the stack:
+// int main(...) {
+// base::AtExitManager exit_manager;
+//
+// }
+// When the exit_manager object goes out of scope, all the registered
+// callbacks and singleton destructors will be called.
+
+class BASE_EXPORT AtExitManager {
+ public:
+ typedef void (*AtExitCallbackType)(void*);
+
+ AtExitManager();
+
+ // The dtor calls all the registered callbacks. Do not try to register more
+ // callbacks after this point.
+ ~AtExitManager();
+
+ // Registers the specified function to be called at exit. The prototype of
+ // the callback function is void func(void*).
+ static void RegisterCallback(AtExitCallbackType func, void* param);
+
+ // Registers the specified task to be called at exit.
+ static void RegisterTask(base::OnceClosure task);
+
+ // Calls the functions registered with RegisterCallback in LIFO order. It
+ // is possible to register new callbacks after calling this function.
+ static void ProcessCallbacksNow();
+
+ // Disable all registered at-exit callbacks. This is used only in a single-
+ // process mode.
+ static void DisableAllAtExitManagers();
+
+ protected:
+ // This constructor will allow this instance of AtExitManager to be created
+ // even if one already exists. This should only be used for testing!
+ // AtExitManagers are kept on a global stack, and it will be removed during
+ // destruction. This allows you to shadow another AtExitManager.
+ explicit AtExitManager(bool shadow);
+
+ private:
+ base::Lock lock_;
+
+ base::stack<base::OnceClosure> stack_ GUARDED_BY(lock_);
+
+#if DCHECK_IS_ON()
+ bool processing_callbacks_ GUARDED_BY(lock_) = false;
+#endif
+
+ // Stack of managers to allow shadowing.
+ AtExitManager* const next_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(AtExitManager);
+};
+
+#if defined(UNIT_TEST)
+class ShadowingAtExitManager : public AtExitManager {
+ public:
+ ShadowingAtExitManager() : AtExitManager(true) {}
+};
+#endif // defined(UNIT_TEST)
+
+} // namespace base
+
+#endif // BASE_AT_EXIT_H_
diff --git a/security/sandbox/chromium/base/atomic_ref_count.h b/security/sandbox/chromium/base/atomic_ref_count.h
new file mode 100644
index 0000000000..5e48c82380
--- /dev/null
+++ b/security/sandbox/chromium/base/atomic_ref_count.h
@@ -0,0 +1,69 @@
+// 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 is a low level implementation of atomic semantics for reference
+// counting. Please use base/memory/ref_counted.h directly instead.
+
+#ifndef BASE_ATOMIC_REF_COUNT_H_
+#define BASE_ATOMIC_REF_COUNT_H_
+
+#include <atomic>
+
+namespace base {
+
+class AtomicRefCount {
+ public:
+ constexpr AtomicRefCount() : ref_count_(0) {}
+ explicit constexpr AtomicRefCount(int initial_value)
+ : ref_count_(initial_value) {}
+
+ // Increment a reference count.
+ // Returns the previous value of the count.
+ int Increment() { return Increment(1); }
+
+ // Increment a reference count by "increment", which must exceed 0.
+ // Returns the previous value of the count.
+ int Increment(int increment) {
+ return ref_count_.fetch_add(increment, std::memory_order_relaxed);
+ }
+
+ // Decrement a reference count, and return whether the result is non-zero.
+ // Insert barriers to ensure that state written before the reference count
+ // became zero will be visible to a thread that has just made the count zero.
+ bool Decrement() {
+ // TODO(jbroman): Technically this doesn't need to be an acquire operation
+ // unless the result is 1 (i.e., the ref count did indeed reach zero).
+ // However, there are toolchain issues that make that not work as well at
+ // present (notably TSAN doesn't like it).
+ return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
+ }
+
+ // Return whether the reference count is one. If the reference count is used
+ // in the conventional way, a refrerence count of 1 implies that the current
+ // thread owns the reference and no other thread shares it. This call
+ // performs the test for a reference count of one, and performs the memory
+ // barrier needed for the owning thread to act on the object, knowing that it
+ // has exclusive access to the object.
+ bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; }
+
+ // Return whether the reference count is zero. With conventional object
+ // referencing counting, the object will be destroyed, so the reference count
+ // should never be zero. Hence this is generally used for a debug check.
+ bool IsZero() const {
+ return ref_count_.load(std::memory_order_acquire) == 0;
+ }
+
+ // Returns the current reference count (with no barriers). This is subtle, and
+ // should be used only for debugging.
+ int SubtleRefCountForDebug() const {
+ return ref_count_.load(std::memory_order_relaxed);
+ }
+
+ private:
+ std::atomic_int ref_count_;
+};
+
+} // namespace base
+
+#endif // 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
new file mode 100644
index 0000000000..717e37a60b
--- /dev/null
+++ b/security/sandbox/chromium/base/atomic_sequence_num.h
@@ -0,0 +1,33 @@
+// 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_ATOMIC_SEQUENCE_NUM_H_
+#define BASE_ATOMIC_SEQUENCE_NUM_H_
+
+#include <atomic>
+
+#include "base/macros.h"
+
+namespace base {
+
+// AtomicSequenceNumber is a thread safe increasing sequence number generator.
+// Its constructor doesn't emit a static initializer, so it's safe to use as a
+// global variable or static member.
+class AtomicSequenceNumber {
+ public:
+ constexpr AtomicSequenceNumber() = default;
+
+ // Returns an increasing sequence number starts from 0 for each call.
+ // This function can be called from any thread without data race.
+ inline int GetNext() { return seq_.fetch_add(1, std::memory_order_relaxed); }
+
+ private:
+ std::atomic_int seq_{0};
+
+ DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber);
+};
+
+} // namespace base
+
+#endif // BASE_ATOMIC_SEQUENCE_NUM_H_
diff --git a/security/sandbox/chromium/base/atomicops.h b/security/sandbox/chromium/base/atomicops.h
new file mode 100644
index 0000000000..429e2457fc
--- /dev/null
+++ b/security/sandbox/chromium/base/atomicops.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For atomic operations on reference counts, see atomic_refcount.h.
+// For atomic operations on sequence numbers, see atomic_sequence_num.h.
+
+// The routines exported by this module are subtle. If you use them, even if
+// you get the code right, it will depend on careful reasoning about atomicity
+// and memory ordering; it will be less readable, and harder to maintain. If
+// you plan to use these routines, you should have a good reason, such as solid
+// evidence that performance would otherwise suffer, or there being no
+// alternative. You should assume only properties explicitly guaranteed by the
+// specifications in this file. You are almost certainly _not_ writing code
+// just for the x86; if you assume x86 semantics, x86 hardware bugs and
+// implementations on other archtectures will cause your code to break. If you
+// do not know what you are doing, avoid these routines, and use a Mutex.
+//
+// It is incorrect to make direct assignments to/from an atomic variable.
+// You should use one of the Load or Store routines. The NoBarrier
+// versions are provided when no barriers are needed:
+// NoBarrier_Store()
+// NoBarrier_Load()
+// Although there are currently no compiler enforcement, you are encouraged
+// to use these.
+//
+
+#ifndef BASE_ATOMICOPS_H_
+#define BASE_ATOMICOPS_H_
+
+#include <stdint.h>
+
+// 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 <cstddef>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace subtle {
+
+typedef int32_t Atomic32;
+#ifdef ARCH_CPU_64_BITS
+// We need to be able to go between Atomic64 and AtomicWord implicitly. This
+// means Atomic64 and AtomicWord should be the same type on 64-bit.
+#if defined(__ILP32__) || defined(OS_NACL)
+// NaCl's intptr_t is not actually 64-bits on 64-bit!
+// http://code.google.com/p/nativeclient/issues/detail?id=1162
+typedef int64_t Atomic64;
+#else
+typedef intptr_t Atomic64;
+#endif
+#endif
+
+// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
+// Atomic64 routines below, depending on your architecture.
+typedef intptr_t AtomicWord;
+
+// Atomically execute:
+// result = *ptr;
+// if (*ptr == old_value)
+// *ptr = new_value;
+// return result;
+//
+// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
+// Always return the old value of "*ptr"
+//
+// This routine implies no memory barriers.
+Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value);
+
+// Atomically store new_value into *ptr, returning the previous value held in
+// *ptr. This routine implies no memory barriers.
+Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
+
+// Atomically increment *ptr by "increment". Returns the new value of
+// *ptr with the increment applied. This routine implies no memory barriers.
+Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment);
+
+Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment);
+
+// These following lower-level operations are typically useful only to people
+// implementing higher-level synchronization operations like spinlocks,
+// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or
+// a store with appropriate memory-ordering instructions. "Acquire" operations
+// ensure that no later memory access can be reordered ahead of the operation.
+// "Release" operations ensure that no previous memory access can be reordered
+// after the operation. "Barrier" operations have both "Acquire" and "Release"
+// semantics.
+Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value);
+Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value);
+
+void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
+void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
+void Release_Store(volatile Atomic32* ptr, Atomic32 value);
+
+Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
+Atomic32 Acquire_Load(volatile const Atomic32* ptr);
+Atomic32 Release_Load(volatile const Atomic32* ptr);
+
+// 64-bit atomic operations (only available on 64-bit processors).
+#ifdef ARCH_CPU_64_BITS
+Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value);
+Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
+Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
+Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
+
+Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value);
+Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value);
+void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value);
+void Acquire_Store(volatile Atomic64* ptr, Atomic64 value);
+void Release_Store(volatile Atomic64* ptr, Atomic64 value);
+Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
+Atomic64 Acquire_Load(volatile const Atomic64* ptr);
+Atomic64 Release_Load(volatile const Atomic64* ptr);
+#endif // ARCH_CPU_64_BITS
+
+} // namespace subtle
+} // namespace base
+
+#if defined(OS_WIN) && defined(ARCH_CPU_X86_FAMILY)
+// TODO(jfb): Try to use base/atomicops_internals_portable.h everywhere.
+// https://crbug.com/559247.
+# include "base/atomicops_internals_x86_msvc.h"
+#else
+# include "base/atomicops_internals_portable.h"
+#endif
+
+// On some platforms we need additional declarations to make
+// AtomicWord compatible with our other Atomic* types.
+#if defined(OS_MACOSX) || defined(OS_OPENBSD)
+#include "base/atomicops_internals_atomicword_compat.h"
+#endif
+
+#endif // BASE_ATOMICOPS_H_
diff --git a/security/sandbox/chromium/base/atomicops_internals_portable.h b/security/sandbox/chromium/base/atomicops_internals_portable.h
new file mode 100644
index 0000000000..3b75be32c4
--- /dev/null
+++ b/security/sandbox/chromium/base/atomicops_internals_portable.h
@@ -0,0 +1,219 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is an internal atomic implementation, use atomicops.h instead.
+//
+// This implementation uses C++11 atomics' member functions. The code base is
+// currently written assuming atomicity revolves around accesses instead of
+// C++11's memory locations. The burden is on the programmer to ensure that all
+// memory locations accessed atomically are never accessed non-atomically (tsan
+// should help with this).
+//
+// TODO(jfb) Modify the atomicops.h API and user code to declare atomic
+// locations as truly atomic. See the static_assert below.
+//
+// Of note in this implementation:
+// * All NoBarrier variants are implemented as relaxed.
+// * All Barrier variants are implemented as sequentially-consistent.
+// * Compare exchange's failure ordering is always the same as the success one
+// (except for release, which fails as relaxed): using a weaker ordering is
+// only valid under certain uses of compare exchange.
+// * Acquire store doesn't exist in the C11 memory model, it is instead
+// implemented as a relaxed store followed by a sequentially consistent
+// fence.
+// * Release load doesn't exist in the C11 memory model, it is instead
+// implemented as sequentially consistent fence followed by a relaxed load.
+// * Atomic increment is expected to return the post-incremented value, whereas
+// C11 fetch add returns the previous value. The implementation therefore
+// needs to increment twice (which the compiler should be able to detect and
+// optimize).
+
+#ifndef BASE_ATOMICOPS_INTERNALS_PORTABLE_H_
+#define BASE_ATOMICOPS_INTERNALS_PORTABLE_H_
+
+#include <atomic>
+
+#include "build/build_config.h"
+
+namespace base {
+namespace subtle {
+
+// This implementation is transitional and maintains the original API for
+// atomicops.h. This requires casting memory locations to the atomic types, and
+// assumes that the API and the C++11 implementation are layout-compatible,
+// which isn't true for all implementations or hardware platforms. The static
+// assertion should detect this issue, were it to fire then this header
+// shouldn't be used.
+//
+// TODO(jfb) If this header manages to stay committed then the API should be
+// modified, and all call sites updated.
+typedef volatile std::atomic<Atomic32>* AtomicLocation32;
+static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32),
+ "incompatible 32-bit atomic layout");
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ ((AtomicLocation32)ptr)
+ ->compare_exchange_strong(old_value,
+ new_value,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed);
+ return old_value;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ return ((AtomicLocation32)ptr)
+ ->exchange(new_value, std::memory_order_relaxed);
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return increment +
+ ((AtomicLocation32)ptr)
+ ->fetch_add(increment, std::memory_order_relaxed);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return increment + ((AtomicLocation32)ptr)->fetch_add(increment);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ ((AtomicLocation32)ptr)
+ ->compare_exchange_strong(old_value,
+ new_value,
+ std::memory_order_acquire,
+ std::memory_order_acquire);
+ return old_value;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ ((AtomicLocation32)ptr)
+ ->compare_exchange_strong(old_value,
+ new_value,
+ std::memory_order_release,
+ std::memory_order_relaxed);
+ return old_value;
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+ ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed);
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ ((AtomicLocation32)ptr)->store(value, std::memory_order_release);
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ return ((AtomicLocation32)ptr)->load(std::memory_order_acquire);
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed);
+}
+
+#if defined(ARCH_CPU_64_BITS)
+
+typedef volatile std::atomic<Atomic64>* AtomicLocation64;
+static_assert(sizeof(*(AtomicLocation64) nullptr) == sizeof(Atomic64),
+ "incompatible 64-bit atomic layout");
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ ((AtomicLocation64)ptr)
+ ->compare_exchange_strong(old_value,
+ new_value,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed);
+ return old_value;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ return ((AtomicLocation64)ptr)
+ ->exchange(new_value, std::memory_order_relaxed);
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return increment +
+ ((AtomicLocation64)ptr)
+ ->fetch_add(increment, std::memory_order_relaxed);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return increment + ((AtomicLocation64)ptr)->fetch_add(increment);
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ ((AtomicLocation64)ptr)
+ ->compare_exchange_strong(old_value,
+ new_value,
+ std::memory_order_acquire,
+ std::memory_order_acquire);
+ return old_value;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ ((AtomicLocation64)ptr)
+ ->compare_exchange_strong(old_value,
+ new_value,
+ std::memory_order_release,
+ std::memory_order_relaxed);
+ return old_value;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed);
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed);
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ ((AtomicLocation64)ptr)->store(value, std::memory_order_release);
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ return ((AtomicLocation64)ptr)->load(std::memory_order_acquire);
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed);
+}
+
+#endif // defined(ARCH_CPU_64_BITS)
+} // namespace subtle
+} // namespace base
+
+#endif // BASE_ATOMICOPS_INTERNALS_PORTABLE_H_
diff --git a/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h b/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h
new file mode 100644
index 0000000000..d9846f64b8
--- /dev/null
+++ b/security/sandbox/chromium/base/atomicops_internals_x86_msvc.h
@@ -0,0 +1,179 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
+#define BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
+
+#include "base/win/windows_types.h"
+
+#include <intrin.h>
+
+#include <atomic>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace subtle {
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ LONG result = _InterlockedCompareExchange(
+ reinterpret_cast<volatile LONG*>(ptr),
+ static_cast<LONG>(new_value),
+ static_cast<LONG>(old_value));
+ return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ LONG result = _InterlockedExchange(
+ reinterpret_cast<volatile LONG*>(ptr),
+ static_cast<LONG>(new_value));
+ return static_cast<Atomic32>(result);
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return _InterlockedExchangeAdd(
+ reinterpret_cast<volatile LONG*>(ptr),
+ static_cast<LONG>(increment)) + increment;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+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 Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+ NoBarrier_AtomicExchange(ptr, value);
+ // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+ *ptr = value; // works w/o barrier for current Intel chips as of June 2005
+ // 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;
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ return *ptr;
+}
+
+#if defined(_WIN64)
+
+// 64-bit low-level operations on 64-bit platform.
+
+static_assert(sizeof(Atomic64) == sizeof(PVOID), "atomic word is atomic");
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ PVOID result = _InterlockedCompareExchangePointer(
+ reinterpret_cast<volatile PVOID*>(ptr),
+ reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
+ return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ PVOID result =
+ _InterlockedExchangePointer(reinterpret_cast<volatile PVOID*>(ptr),
+ reinterpret_cast<PVOID>(new_value));
+ return reinterpret_cast<Atomic64>(result);
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return _InterlockedExchangeAdd64(reinterpret_cast<volatile LONGLONG*>(ptr),
+ static_cast<LONGLONG>(increment)) +
+ increment;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ return Barrier_AtomicIncrement(ptr, increment);
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+ NoBarrier_AtomicExchange(ptr, value);
+ // acts as a barrier in this implementation
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+ *ptr = value; // works w/o barrier for current Intel chips as of June 2005
+
+ // 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
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ Atomic64 value = *ptr;
+ return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ return *ptr;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+}
+
+
+#endif // defined(_WIN64)
+
+} // 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
new file mode 100644
index 0000000000..cf7ebd7816
--- /dev/null
+++ b/security/sandbox/chromium/base/base_export.h
@@ -0,0 +1,29 @@
+// 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_BASE_EXPORT_H_
+#define BASE_BASE_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(BASE_IMPLEMENTATION)
+#define BASE_EXPORT __declspec(dllexport)
+#else
+#define BASE_EXPORT __declspec(dllimport)
+#endif // defined(BASE_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(BASE_IMPLEMENTATION)
+#define BASE_EXPORT __attribute__((visibility("default")))
+#else
+#define BASE_EXPORT
+#endif // defined(BASE_IMPLEMENTATION)
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define BASE_EXPORT
+#endif
+
+#endif // BASE_BASE_EXPORT_H_
diff --git a/security/sandbox/chromium/base/base_paths.h b/security/sandbox/chromium/base/base_paths.h
new file mode 100644
index 0000000000..2a163f48d4
--- /dev/null
+++ b/security/sandbox/chromium/base/base_paths.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BASE_PATHS_H_
+#define BASE_BASE_PATHS_H_
+
+// This file declares path keys for the base module. These can be used with
+// the PathService to access various special directories and files.
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/base_paths_win.h"
+#elif defined(OS_MACOSX)
+#include "base/base_paths_mac.h"
+#elif defined(OS_ANDROID)
+#include "base/base_paths_android.h"
+#endif
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "base/base_paths_posix.h"
+#endif
+
+namespace base {
+
+enum BasePathKey {
+ PATH_START = 0,
+
+ DIR_CURRENT, // Current directory.
+ DIR_EXE, // Directory containing FILE_EXE.
+ DIR_MODULE, // Directory containing FILE_MODULE.
+ DIR_ASSETS, // Directory that contains application assets.
+ DIR_TEMP, // Temporary directory.
+ DIR_HOME, // User's root home directory. On Windows this will look
+ // like "C:\Users\<user>" which isn't necessarily a great
+ // place to put files.
+ FILE_EXE, // Path and filename of the current executable.
+ FILE_MODULE, // Path and filename of the module containing the code for
+ // the PathService (which could differ from FILE_EXE if the
+ // PathService were compiled into a shared object, for
+ // example).
+ DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful
+ // for tests that need to locate various resources. It
+ // should not be used outside of test code.
+ DIR_USER_DESKTOP, // The current user's Desktop.
+
+ DIR_TEST_DATA, // Used only for testing.
+
+ PATH_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_H_
diff --git a/security/sandbox/chromium/base/base_paths_win.h b/security/sandbox/chromium/base/base_paths_win.h
new file mode 100644
index 0000000000..2db16a6271
--- /dev/null
+++ b/security/sandbox/chromium/base/base_paths_win.h
@@ -0,0 +1,53 @@
+// 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_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
+// directories and files.
+
+namespace base {
+
+enum {
+ PATH_WIN_START = 100,
+
+ DIR_WINDOWS, // Windows directory, usually "c:\windows"
+ DIR_SYSTEM, // Usually c:\windows\system32"
+ // 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:\ProgramData\Microsoft\Windows\
+ // Start Menu\Programs"
+ DIR_START_MENU, // Usually "C:\Users\<user>\AppData\Roaming\
+ // Microsoft\Windows\Start Menu\Programs"
+ DIR_APP_DATA, // Application Data directory under the user
+ // profile.
+ DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory
+ // under the user profile.
+ DIR_COMMON_APP_DATA, // Usually "C:\ProgramData".
+ DIR_APP_SHORTCUTS, // Where tiles on the start screen are stored,
+ // only for Windows 8. Maps to "Local\AppData\
+ // Microsoft\Windows\Application Shortcuts\".
+ 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.
+ DIR_IMPLICIT_APP_SHORTCUTS, // The implicit user pinned shortcut directory.
+ DIR_WINDOWS_FONTS, // Usually C:\Windows\Fonts.
+
+ PATH_WIN_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_WIN_H_
diff --git a/security/sandbox/chromium/base/base_switches.cc b/security/sandbox/chromium/base/base_switches.cc
new file mode 100644
index 0000000000..6a47487961
--- /dev/null
+++ b/security/sandbox/chromium/base/base_switches.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/base_switches.h"
+#include "build/build_config.h"
+
+namespace switches {
+
+// Delays execution of TaskPriority::BEST_EFFORT tasks until shutdown.
+const char kDisableBestEffortTasks[] = "disable-best-effort-tasks";
+
+// Disables the crash reporting.
+const char kDisableBreakpad[] = "disable-breakpad";
+
+// Comma-separated list of feature names to disable. See also kEnableFeatures.
+const char kDisableFeatures[] = "disable-features";
+
+// Force disabling of low-end device mode when set.
+const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
+
+// Indicates that crash reporting should be enabled. On platforms where helper
+// processes cannot access to files needed to make this decision, this flag is
+// generated internally.
+const char kEnableCrashReporter[] = "enable-crash-reporter";
+
+// Comma-separated list of feature names to enable. See also kDisableFeatures.
+const char kEnableFeatures[] = "enable-features";
+
+// Force low-end device mode when set.
+const char kEnableLowEndDeviceMode[] = "enable-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";
+
+// Generates full memory crash dump.
+const char kFullMemoryCrashReport[] = "full-memory-crash-report";
+
+// Logs information about all tasks posted with TaskPriority::BEST_EFFORT. Use
+// this to diagnose issues that are thought to be caused by
+// TaskPriority::BEST_EFFORT execution fences. Note: Tasks posted to a
+// non-BEST_EFFORT UpdateableSequencedTaskRunner whose priority is later lowered
+// to BEST_EFFORT are not logged.
+const char kLogBestEffortTasks[] = "log-best-effort-tasks";
+
+// Suppresses all error dialogs when present.
+const char kNoErrorDialogs[] = "noerrdialogs";
+
+// Starts the sampling based profiler for the browser process at startup. This
+// will only work if chrome has been built with the gn arg enable_profiling =
+// true. The output will go to the value of kProfilingFile.
+const char kProfilingAtStart[] = "profiling-at-start";
+
+// Specifies a location for profiling output. This will only work if chrome has
+// been built with the gyp variable profiling=1 or gn arg enable_profiling=true.
+//
+// {pid} if present will be replaced by the pid of the process.
+// {count} if present will be incremented each time a profile is generated
+// for this process.
+// The default is chrome-profile-{pid} for the browser and test-profile-{pid}
+// for tests.
+const char kProfilingFile[] = "profiling-file";
+
+// Controls whether profile data is periodically flushed to a file. Normally
+// the data gets written on exit but cases exist where chromium doesn't exit
+// cleanly (especially when using single-process). A time in seconds can be
+// specified.
+const char kProfilingFlush[] = "profiling-flush";
+
+// When running certain tests that spawn child processes, this switch indicates
+// to the test framework that the current process is a child process.
+const char kTestChildProcess[] = "test-child-process";
+
+// When running certain tests that spawn child processes, this switch indicates
+// to the test framework that the current process should not initialize ICU to
+// avoid creating any scoped handles too early in startup.
+const char kTestDoNotInitializeIcu[] = "test-do-not-initialize-icu";
+
+// 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";
+
+// Specifies the file name for --trace-to-file. If unspecified, it will
+// go to a default file name.
+const char kTraceToFileName[] = "trace-to-file-name";
+
+// Gives the default maximal active V-logging level; 0 is the default.
+// Normally positive values are used for V-logging levels.
+const char kV[] = "v";
+
+// Gives the per-module maximal V-logging levels to override the value
+// given by --v. E.g. "my_module=2,foo*=3" would change the logging
+// level for all code in source files "my_module.*" and "foo*.*"
+// ("-inl" suffixes are also disregarded for this matching).
+//
+// Any pattern containing a forward or backward slash will be tested
+// against the whole pathname and not just the module. E.g.,
+// "*/foo/bar/*=2" would change the logging level for all code in
+// source files under a "foo/bar" directory.
+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";
+
+#if defined(OS_WIN)
+// Disable high-resolution timer on Windows.
+const char kDisableHighResTimer[] = "disable-highres-timer";
+
+// Disables the USB keyboard detection for blocking the OSK on Win8+.
+const char kDisableUsbKeyboardDetect[] = "disable-usb-keyboard-detect";
+#endif
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+// The /dev/shm partition is too small in certain VM environments, causing
+// Chrome to fail or crash (see http://crbug.com/715363). Use this flag to
+// work-around this issue (a temporary directory will always be used to create
+// anonymous shared memory files).
+const char kDisableDevShmUsage[] = "disable-dev-shm-usage";
+#endif
+
+#if defined(OS_POSIX)
+// Used for turning on Breakpad crash reporting in a debug environment where
+// crash reporting is typically compiled but disabled.
+const char kEnableCrashReporterForTesting[] =
+ "enable-crash-reporter-for-testing";
+#endif
+
+#if defined(OS_ANDROID)
+// Enables the reached code profiler that samples all threads in all processes
+// to determine which functions are almost never executed.
+const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler";
+#endif
+
+#if defined(OS_LINUX)
+// Controls whether or not retired instruction counts are surfaced for threads
+// in trace events on Linux.
+//
+// This flag requires the BPF sandbox to be disabled.
+const char kEnableThreadInstructionCount[] = "enable-thread-instruction-count";
+#endif
+
+} // namespace switches
diff --git a/security/sandbox/chromium/base/base_switches.h b/security/sandbox/chromium/base/base_switches.h
new file mode 100644
index 0000000000..b1923efc1e
--- /dev/null
+++ b/security/sandbox/chromium/base/base_switches.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines all the "base" command-line switches.
+
+#ifndef BASE_BASE_SWITCHES_H_
+#define BASE_BASE_SWITCHES_H_
+
+#include "build/build_config.h"
+
+namespace switches {
+
+extern const char kDisableBestEffortTasks[];
+extern const char kDisableBreakpad[];
+extern const char kDisableFeatures[];
+extern const char kDisableLowEndDeviceMode[];
+extern const char kEnableCrashReporter[];
+extern const char kEnableFeatures[];
+extern const char kEnableLowEndDeviceMode[];
+extern const char kForceFieldTrials[];
+extern const char kFullMemoryCrashReport[];
+extern const char kLogBestEffortTasks[];
+extern const char kNoErrorDialogs[];
+extern const char kProfilingAtStart[];
+extern const char kProfilingFile[];
+extern const char kProfilingFlush[];
+extern const char kTestChildProcess[];
+extern const char kTestDoNotInitializeIcu[];
+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 kDisableHighResTimer[];
+extern const char kDisableUsbKeyboardDetect[];
+#endif
+
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+extern const char kDisableDevShmUsage[];
+#endif
+
+#if defined(OS_POSIX)
+extern const char kEnableCrashReporterForTesting[];
+#endif
+
+#if defined(OS_ANDROID)
+extern const char kEnableReachedCodeProfiler[];
+extern const char kOrderfileMemoryOptimization[];
+#endif
+
+#if defined(OS_LINUX)
+extern const char kEnableThreadInstructionCount[];
+#endif
+
+} // namespace switches
+
+#endif // BASE_BASE_SWITCHES_H_
diff --git a/security/sandbox/chromium/base/bind.h b/security/sandbox/chromium/base/bind.h
new file mode 100644
index 0000000000..0bbc2aceb1
--- /dev/null
+++ b/security/sandbox/chromium/base/bind.h
@@ -0,0 +1,470 @@
+// 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_BIND_H_
+#define BASE_BIND_H_
+
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "base/bind_internal.h"
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc)
+#include "base/mac/scoped_block.h"
+#endif
+
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// Overview:
+// base::BindOnce() and base::BindRepeating() are helpers for creating
+// base::OnceCallback and base::RepeatingCallback objects respectively.
+//
+// For a runnable object of n-arity, the base::Bind*() family allows partial
+// application of the first m arguments. The remaining n - m arguments must be
+// passed when invoking the callback with Run().
+//
+// // The first argument is bound at callback creation; the remaining
+// // two must be passed when calling Run() on the callback object.
+// base::OnceCallback<long(int, long)> cb = base::BindOnce(
+// [](short x, int y, long z) { return x * y * z; }, 42);
+//
+// When binding to a method, the receiver object must also be specified at
+// callback creation time. When Run() is invoked, the method will be invoked on
+// the specified receiver object.
+//
+// class C : public base::RefCounted<C> { void F(); };
+// auto instance = base::MakeRefCounted<C>();
+// auto cb = base::BindOnce(&C::F, instance);
+// std::move(cb).Run(); // Identical to instance->F()
+//
+// base::Bind is currently a type alias for base::BindRepeating(). In the
+// future, we expect to flip this to default to base::BindOnce().
+//
+// See //docs/callback.md for the full documentation.
+//
+// -----------------------------------------------------------------------------
+// Implementation notes
+// -----------------------------------------------------------------------------
+//
+// If you're reading the implementation, before proceeding further, you should
+// read the top comment of base/bind_internal.h for a definition of common
+// terms and concepts.
+
+namespace base {
+
+namespace internal {
+
+// IsOnceCallback<T> is a std::true_type if |T| is a OnceCallback.
+template <typename T>
+struct IsOnceCallback : std::false_type {};
+
+template <typename Signature>
+struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {};
+
+// Helper to assert that parameter |i| of type |Arg| can be bound, which means:
+// - |Arg| can be retained internally as |Storage|.
+// - |Arg| can be forwarded as |Unwrapped| to |Param|.
+template <size_t i,
+ typename Arg,
+ typename Storage,
+ typename Unwrapped,
+ typename Param>
+struct AssertConstructible {
+ private:
+ static constexpr bool param_is_forwardable =
+ std::is_constructible<Param, Unwrapped>::value;
+ // Unlike the check for binding into storage below, the check for
+ // forwardability drops the const qualifier for repeating callbacks. This is
+ // to try to catch instances where std::move()--which forwards as a const
+ // reference with repeating callbacks--is used instead of base::Passed().
+ static_assert(
+ param_is_forwardable ||
+ !std::is_constructible<Param, std::decay_t<Unwrapped>&&>::value,
+ "Bound argument |i| is move-only but will be forwarded by copy. "
+ "Ensure |Arg| is bound using base::Passed(), not std::move().");
+ static_assert(
+ param_is_forwardable,
+ "Bound argument |i| of type |Arg| cannot be forwarded as "
+ "|Unwrapped| to the bound functor, which declares it as |Param|.");
+
+ static constexpr bool arg_is_storable =
+ std::is_constructible<Storage, Arg>::value;
+ static_assert(arg_is_storable ||
+ !std::is_constructible<Storage, std::decay_t<Arg>&&>::value,
+ "Bound argument |i| is move-only but will be bound by copy. "
+ "Ensure |Arg| is mutable and bound using std::move().");
+ static_assert(arg_is_storable,
+ "Bound argument |i| of type |Arg| cannot be converted and "
+ "bound as |Storage|.");
+};
+
+// Takes three same-length TypeLists, and applies AssertConstructible for each
+// triples.
+template <typename Index,
+ typename Args,
+ typename UnwrappedTypeList,
+ typename ParamsList>
+struct AssertBindArgsValidity;
+
+template <size_t... Ns,
+ typename... Args,
+ typename... Unwrapped,
+ typename... Params>
+struct AssertBindArgsValidity<std::index_sequence<Ns...>,
+ TypeList<Args...>,
+ TypeList<Unwrapped...>,
+ TypeList<Params...>>
+ : AssertConstructible<Ns, Args, std::decay_t<Args>, Unwrapped, Params>... {
+ static constexpr bool ok = true;
+};
+
+// The implementation of TransformToUnwrappedType below.
+template <bool is_once, typename T>
+struct TransformToUnwrappedTypeImpl;
+
+template <typename T>
+struct TransformToUnwrappedTypeImpl<true, T> {
+ using StoredType = std::decay_t<T>;
+ using ForwardType = StoredType&&;
+ using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
+};
+
+template <typename T>
+struct TransformToUnwrappedTypeImpl<false, T> {
+ using StoredType = std::decay_t<T>;
+ using ForwardType = const StoredType&;
+ using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
+};
+
+// Transform |T| into `Unwrapped` type, which is passed to the target function.
+// Example:
+// In is_once == true case,
+// `int&&` -> `int&&`,
+// `const int&` -> `int&&`,
+// `OwnedWrapper<int>&` -> `int*&&`.
+// In is_once == false case,
+// `int&&` -> `const int&`,
+// `const int&` -> `const int&`,
+// `OwnedWrapper<int>&` -> `int* const &`.
+template <bool is_once, typename T>
+using TransformToUnwrappedType =
+ typename TransformToUnwrappedTypeImpl<is_once, T>::Unwrapped;
+
+// Transforms |Args| into `Unwrapped` types, and packs them into a TypeList.
+// If |is_method| is true, tries to dereference the first argument to support
+// smart pointers.
+template <bool is_once, bool is_method, typename... Args>
+struct MakeUnwrappedTypeListImpl {
+ using Type = TypeList<TransformToUnwrappedType<is_once, Args>...>;
+};
+
+// Performs special handling for this pointers.
+// Example:
+// int* -> int*,
+// std::unique_ptr<int> -> int*.
+template <bool is_once, typename Receiver, typename... Args>
+struct MakeUnwrappedTypeListImpl<is_once, true, Receiver, Args...> {
+ using UnwrappedReceiver = TransformToUnwrappedType<is_once, Receiver>;
+ using Type = TypeList<decltype(&*std::declval<UnwrappedReceiver>()),
+ TransformToUnwrappedType<is_once, Args>...>;
+};
+
+template <bool is_once, bool is_method, typename... Args>
+using MakeUnwrappedTypeList =
+ typename MakeUnwrappedTypeListImpl<is_once, is_method, Args...>::Type;
+
+// Used below in BindImpl to determine whether to use Invoker::Run or
+// Invoker::RunOnce.
+// Note: Simply using `kIsOnce ? &Invoker::RunOnce : &Invoker::Run` does not
+// work, since the compiler needs to check whether both expressions are
+// well-formed. Using `Invoker::Run` with a OnceCallback triggers a
+// static_assert, which is why the ternary expression does not compile.
+// TODO(crbug.com/752720): Remove this indirection once we have `if constexpr`.
+template <typename Invoker>
+constexpr auto GetInvokeFunc(std::true_type) {
+ return Invoker::RunOnce;
+}
+
+template <typename Invoker>
+constexpr auto GetInvokeFunc(std::false_type) {
+ return Invoker::Run;
+}
+
+template <template <typename> class CallbackT,
+ typename Functor,
+ typename... Args>
+decltype(auto) BindImpl(Functor&& functor, Args&&... args) {
+ // This block checks if each |args| matches to the corresponding params of the
+ // target function. This check does not affect the behavior of Bind, but its
+ // error message should be more readable.
+ static constexpr bool kIsOnce = IsOnceCallback<CallbackT<void()>>::value;
+ using Helper = internal::BindTypeHelper<Functor, Args...>;
+ using FunctorTraits = typename Helper::FunctorTraits;
+ using BoundArgsList = typename Helper::BoundArgsList;
+ using UnwrappedArgsList =
+ internal::MakeUnwrappedTypeList<kIsOnce, FunctorTraits::is_method,
+ Args&&...>;
+ using BoundParamsList = typename Helper::BoundParamsList;
+ static_assert(internal::AssertBindArgsValidity<
+ std::make_index_sequence<Helper::num_bounds>, BoundArgsList,
+ UnwrappedArgsList, BoundParamsList>::ok,
+ "The bound args need to be convertible to the target params.");
+
+ using BindState = internal::MakeBindStateType<Functor, Args...>;
+ using UnboundRunType = MakeUnboundRunType<Functor, Args...>;
+ using Invoker = internal::Invoker<BindState, UnboundRunType>;
+ using CallbackType = CallbackT<UnboundRunType>;
+
+ // Store the invoke func into PolymorphicInvoke before casting it to
+ // InvokeFuncStorage, so that we can ensure its type matches to
+ // PolymorphicInvoke, to which CallbackType will cast back.
+ using PolymorphicInvoke = typename CallbackType::PolymorphicInvoke;
+ PolymorphicInvoke invoke_func =
+ GetInvokeFunc<Invoker>(std::integral_constant<bool, kIsOnce>());
+
+ using InvokeFuncStorage = internal::BindStateBase::InvokeFuncStorage;
+ return CallbackType(BindState::Create(
+ reinterpret_cast<InvokeFuncStorage>(invoke_func),
+ std::forward<Functor>(functor), std::forward<Args>(args)...));
+}
+
+} // namespace internal
+
+// Bind as OnceCallback.
+template <typename Functor, typename... Args>
+inline OnceCallback<MakeUnboundRunType<Functor, Args...>> BindOnce(
+ Functor&& functor,
+ Args&&... args) {
+ static_assert(!internal::IsOnceCallback<std::decay_t<Functor>>() ||
+ (std::is_rvalue_reference<Functor&&>() &&
+ !std::is_const<std::remove_reference_t<Functor>>()),
+ "BindOnce requires non-const rvalue for OnceCallback binding."
+ " I.e.: base::BindOnce(std::move(callback)).");
+
+ return internal::BindImpl<OnceCallback>(std::forward<Functor>(functor),
+ std::forward<Args>(args)...);
+}
+
+// Bind as RepeatingCallback.
+template <typename Functor, typename... Args>
+inline RepeatingCallback<MakeUnboundRunType<Functor, Args...>>
+BindRepeating(Functor&& functor, Args&&... args) {
+ static_assert(
+ !internal::IsOnceCallback<std::decay_t<Functor>>(),
+ "BindRepeating cannot bind OnceCallback. Use BindOnce with std::move().");
+
+ return internal::BindImpl<RepeatingCallback>(std::forward<Functor>(functor),
+ std::forward<Args>(args)...);
+}
+
+// Unannotated Bind.
+// TODO(tzik): Deprecate this and migrate to OnceCallback and
+// RepeatingCallback, once they get ready.
+template <typename Functor, typename... Args>
+inline Callback<MakeUnboundRunType<Functor, Args...>>
+Bind(Functor&& functor, Args&&... args) {
+ return base::BindRepeating(std::forward<Functor>(functor),
+ std::forward<Args>(args)...);
+}
+
+// Special cases for binding to a base::Callback without extra bound arguments.
+template <typename Signature>
+OnceCallback<Signature> BindOnce(OnceCallback<Signature> callback) {
+ return callback;
+}
+
+template <typename Signature>
+OnceCallback<Signature> BindOnce(RepeatingCallback<Signature> callback) {
+ return callback;
+}
+
+template <typename Signature>
+RepeatingCallback<Signature> BindRepeating(
+ RepeatingCallback<Signature> callback) {
+ return callback;
+}
+
+template <typename Signature>
+Callback<Signature> Bind(Callback<Signature> callback) {
+ return callback;
+}
+
+// Unretained() allows binding a non-refcounted class, and to disable
+// refcounting on arguments that are refcounted objects.
+//
+// EXAMPLE OF Unretained():
+//
+// class Foo {
+// public:
+// void func() { cout << "Foo:f" << endl; }
+// };
+//
+// // In some function somewhere.
+// Foo foo;
+// OnceClosure foo_callback =
+// BindOnce(&Foo::func, Unretained(&foo));
+// std::move(foo_callback).Run(); // Prints "Foo:f".
+//
+// Without the Unretained() wrapper on |&foo|, the above call would fail
+// to compile because Foo does not support the AddRef() and Release() methods.
+template <typename T>
+static inline internal::UnretainedWrapper<T> Unretained(T* o) {
+ return internal::UnretainedWrapper<T>(o);
+}
+
+// RetainedRef() accepts a ref counted object and retains a reference to it.
+// When the callback is called, the object is passed as a raw pointer.
+//
+// EXAMPLE OF RetainedRef():
+//
+// void foo(RefCountedBytes* bytes) {}
+//
+// scoped_refptr<RefCountedBytes> bytes = ...;
+// OnceClosure callback = BindOnce(&foo, base::RetainedRef(bytes));
+// std::move(callback).Run();
+//
+// Without RetainedRef, the scoped_refptr would try to implicitly convert to
+// a raw pointer and fail compilation:
+//
+// OnceClosure callback = BindOnce(&foo, bytes); // ERROR!
+template <typename T>
+static inline internal::RetainedRefWrapper<T> RetainedRef(T* o) {
+ return internal::RetainedRefWrapper<T>(o);
+}
+template <typename T>
+static inline internal::RetainedRefWrapper<T> RetainedRef(scoped_refptr<T> o) {
+ return internal::RetainedRefWrapper<T>(std::move(o));
+}
+
+// Owned() transfers ownership of an object to the callback resulting from
+// bind; the object will be deleted when the callback is deleted.
+//
+// EXAMPLE OF Owned():
+//
+// void foo(int* arg) { cout << *arg << endl }
+//
+// int* pn = new int(1);
+// RepeatingClosure foo_callback = BindRepeating(&foo, Owned(pn));
+//
+// foo_callback.Run(); // Prints "1"
+// foo_callback.Run(); // Prints "1"
+// *pn = 2;
+// foo_callback.Run(); // Prints "2"
+//
+// foo_callback.Reset(); // |pn| is deleted. Also will happen when
+// // |foo_callback| goes out of scope.
+//
+// Without Owned(), someone would have to know to delete |pn| when the last
+// reference to the callback is deleted.
+template <typename T>
+static inline internal::OwnedWrapper<T> Owned(T* o) {
+ return internal::OwnedWrapper<T>(o);
+}
+
+template <typename T, typename Deleter>
+static inline internal::OwnedWrapper<T, Deleter> Owned(
+ std::unique_ptr<T, Deleter>&& ptr) {
+ return internal::OwnedWrapper<T, Deleter>(std::move(ptr));
+}
+
+// Passed() is for transferring movable-but-not-copyable types (eg. unique_ptr)
+// through a RepeatingCallback. Logically, this signifies a destructive transfer
+// of the state of the argument into the target function. Invoking
+// RepeatingCallback::Run() twice on a callback that was created with a Passed()
+// argument will CHECK() because the first invocation would have already
+// transferred ownership to the target function.
+//
+// Note that Passed() is not necessary with BindOnce(), as std::move() does the
+// same thing. Avoid Passed() in favor of std::move() with BindOnce().
+//
+// EXAMPLE OF Passed():
+//
+// void TakesOwnership(std::unique_ptr<Foo> arg) { }
+// std::unique_ptr<Foo> CreateFoo() { return std::make_unique<Foo>();
+// }
+//
+// auto f = std::make_unique<Foo>();
+//
+// // |cb| is given ownership of Foo(). |f| is now NULL.
+// // You can use std::move(f) in place of &f, but it's more verbose.
+// RepeatingClosure cb = BindRepeating(&TakesOwnership, Passed(&f));
+//
+// // Run was never called so |cb| still owns Foo() and deletes
+// // it on Reset().
+// cb.Reset();
+//
+// // |cb| is given a new Foo created by CreateFoo().
+// cb = BindRepeating(&TakesOwnership, Passed(CreateFoo()));
+//
+// // |arg| in TakesOwnership() is given ownership of Foo(). |cb|
+// // no longer owns Foo() and, if reset, would not delete Foo().
+// cb.Run(); // Foo() is now transferred to |arg| and deleted.
+// cb.Run(); // This CHECK()s since Foo() already been used once.
+//
+// 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 <typename T,
+ std::enable_if_t<!std::is_lvalue_reference<T>::value>* = nullptr>
+static inline internal::PassedWrapper<T> Passed(T&& scoper) {
+ return internal::PassedWrapper<T>(std::move(scoper));
+}
+template <typename T>
+static inline internal::PassedWrapper<T> Passed(T* scoper) {
+ return internal::PassedWrapper<T>(std::move(*scoper));
+}
+
+// IgnoreResult() is used to adapt a function or callback with a return type to
+// one with a void return. This is most useful if you have a function with,
+// say, a pesky ignorable bool return that you want to use with PostTask or
+// something else that expect a callback with a void return.
+//
+// EXAMPLE OF IgnoreResult():
+//
+// int DoSomething(int arg) { cout << arg << endl; }
+//
+// // Assign to a callback with a void return type.
+// OnceCallback<void(int)> cb = BindOnce(IgnoreResult(&DoSomething));
+// std::move(cb).Run(1); // Prints "1".
+//
+// // Prints "2" on |ml|.
+// ml->PostTask(FROM_HERE, BindOnce(IgnoreResult(&DoSomething), 2);
+template <typename T>
+static inline internal::IgnoreResultHelper<T> IgnoreResult(T data) {
+ return internal::IgnoreResultHelper<T>(std::move(data));
+}
+
+#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc)
+
+// RetainBlock() is used to adapt an Objective-C block when Automated Reference
+// Counting (ARC) is disabled. This is unnecessary when ARC is enabled, as the
+// BindOnce and BindRepeating already support blocks then.
+//
+// EXAMPLE OF RetainBlock():
+//
+// // Wrap the block and bind it to a callback.
+// OnceCallback<void(int)> cb =
+// BindOnce(RetainBlock(^(int n) { NSLog(@"%d", n); }));
+// std::move(cb).Run(1); // Logs "1".
+template <typename R, typename... Args>
+base::mac::ScopedBlock<R (^)(Args...)> RetainBlock(R (^block)(Args...)) {
+ return base::mac::ScopedBlock<R (^)(Args...)>(block,
+ base::scoped_policy::RETAIN);
+}
+
+#endif // defined(OS_MACOSX) && !HAS_FEATURE(objc_arc)
+
+} // namespace base
+
+#endif // BASE_BIND_H_
diff --git a/security/sandbox/chromium/base/bind_helpers.h b/security/sandbox/chromium/base/bind_helpers.h
new file mode 100644
index 0000000000..37065a07ab
--- /dev/null
+++ b/security/sandbox/chromium/base/bind_helpers.h
@@ -0,0 +1,69 @@
+// 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_BIND_HELPERS_H_
+#define BASE_BIND_HELPERS_H_
+
+#include <stddef.h>
+
+#include <type_traits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
+
+// This defines a set of simple functions and utilities that people want when
+// using {Once,Repeating}Callback<> and Bind{Once,Repeating}().
+
+namespace base {
+
+// Creates a null callback.
+class BASE_EXPORT NullCallback {
+ public:
+ template <typename R, typename... Args>
+ operator RepeatingCallback<R(Args...)>() const {
+ return RepeatingCallback<R(Args...)>();
+ }
+ template <typename R, typename... Args>
+ operator OnceCallback<R(Args...)>() const {
+ return OnceCallback<R(Args...)>();
+ }
+};
+
+// Creates a callback that does nothing when called.
+class BASE_EXPORT DoNothing {
+ public:
+ template <typename... Args>
+ operator RepeatingCallback<void(Args...)>() const {
+ return Repeatedly<Args...>();
+ }
+ template <typename... Args>
+ operator OnceCallback<void(Args...)>() const {
+ return Once<Args...>();
+ }
+ // Explicit way of specifying a specific callback type when the compiler can't
+ // deduce it.
+ template <typename... Args>
+ static RepeatingCallback<void(Args...)> Repeatedly() {
+ return BindRepeating([](Args... args) {});
+ }
+ template <typename... Args>
+ static OnceCallback<void(Args...)> Once() {
+ return BindOnce([](Args... args) {});
+ }
+};
+
+// Useful for creating a Closure that will delete a pointer when invoked. Only
+// use this when necessary. In most cases MessageLoop::DeleteSoon() is a better
+// fit.
+template <typename T>
+void DeletePointer(T* obj) {
+ delete obj;
+}
+
+} // namespace base
+
+#endif // BASE_BIND_HELPERS_H_
diff --git a/security/sandbox/chromium/base/bind_internal.h b/security/sandbox/chromium/base/bind_internal.h
new file mode 100644
index 0000000000..bed151a8dc
--- /dev/null
+++ b/security/sandbox/chromium/base/bind_internal.h
@@ -0,0 +1,1050 @@
+// 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_BIND_INTERNAL_H_
+#define BASE_BIND_INTERNAL_H_
+
+#include <stddef.h>
+
+#include <functional>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_internal.h"
+#include "base/compiler_specific.h"
+#include "base/memory/raw_scoped_refptr_mismatch_checker.h"
+#include "base/memory/weak_ptr.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX) && !HAS_FEATURE(objc_arc)
+#include "base/mac/scoped_block.h"
+#endif
+
+// See base/callback.h for user documentation.
+//
+//
+// CONCEPTS:
+// Functor -- A movable type representing something that should be called.
+// All function pointers and Callback<> are functors even if the
+// invocation syntax differs.
+// RunType -- A function type (as opposed to function _pointer_ type) for
+// a Callback<>::Run(). Usually just a convenience typedef.
+// (Bound)Args -- A set of types that stores the arguments.
+//
+// Types:
+// ForceVoidReturn<> -- Helper class for translating function signatures to
+// equivalent forms with a "void" return type.
+// FunctorTraits<> -- Type traits used to determine the correct RunType and
+// invocation manner for a Functor. This is where function
+// signature adapters are applied.
+// InvokeHelper<> -- Take a Functor + arguments and actully invokes it.
+// Handle the differing syntaxes needed for WeakPtr<>
+// support. This is separate from Invoker to avoid creating
+// multiple version of Invoker<>.
+// Invoker<> -- Unwraps the curried parameters and executes the Functor.
+// BindState<> -- Stores the curried parameters, and is the main entry point
+// into the Bind() system.
+
+#if defined(OS_WIN)
+namespace Microsoft {
+namespace WRL {
+template <typename>
+class ComPtr;
+} // namespace WRL
+} // namespace Microsoft
+#endif
+
+namespace base {
+
+template <typename T>
+struct IsWeakReceiver;
+
+template <typename>
+struct BindUnwrapTraits;
+
+template <typename Functor, typename BoundArgsTuple, typename SFINAE = void>
+struct CallbackCancellationTraits;
+
+namespace internal {
+
+template <typename Functor, typename SFINAE = void>
+struct FunctorTraits;
+
+template <typename T>
+class UnretainedWrapper {
+ public:
+ explicit UnretainedWrapper(T* o) : ptr_(o) {}
+ T* get() const { return ptr_; }
+
+ private:
+ T* ptr_;
+};
+
+template <typename T>
+class RetainedRefWrapper {
+ public:
+ explicit RetainedRefWrapper(T* o) : ptr_(o) {}
+ explicit RetainedRefWrapper(scoped_refptr<T> o) : ptr_(std::move(o)) {}
+ T* get() const { return ptr_.get(); }
+
+ private:
+ scoped_refptr<T> ptr_;
+};
+
+template <typename T>
+struct IgnoreResultHelper {
+ explicit IgnoreResultHelper(T functor) : functor_(std::move(functor)) {}
+ explicit operator bool() const { return !!functor_; }
+
+ T functor_;
+};
+
+template <typename T, typename Deleter = std::default_delete<T>>
+class OwnedWrapper {
+ public:
+ explicit OwnedWrapper(T* o) : ptr_(o) {}
+ explicit OwnedWrapper(std::unique_ptr<T, Deleter>&& ptr)
+ : ptr_(std::move(ptr)) {}
+ T* get() const { return ptr_.get(); }
+
+ private:
+ std::unique_ptr<T, Deleter> ptr_;
+};
+
+// PassedWrapper is a copyable adapter for a scoper that ignores const.
+//
+// It is needed to get around the fact that Bind() takes a const reference to
+// all its arguments. Because Bind() takes a const reference to avoid
+// unnecessary copies, it is incompatible with movable-but-not-copyable
+// types; doing a destructive "move" of the type into Bind() would violate
+// the const correctness.
+//
+// This conundrum cannot be solved without either C++11 rvalue references or
+// a O(2^n) blowup of Bind() templates to handle each combination of regular
+// types and movable-but-not-copyable types. Thus we introduce a wrapper type
+// that is copyable to transmit the correct type information down into
+// BindState<>. Ignoring const in this type makes sense because it is only
+// created when we are explicitly trying to do a destructive move.
+//
+// Two notes:
+// 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 <typename T>
+class PassedWrapper {
+ public:
+ explicit PassedWrapper(T&& scoper)
+ : is_valid_(true), scoper_(std::move(scoper)) {}
+ PassedWrapper(PassedWrapper&& other)
+ : is_valid_(other.is_valid_), scoper_(std::move(other.scoper_)) {}
+ T Take() const {
+ CHECK(is_valid_);
+ is_valid_ = false;
+ return std::move(scoper_);
+ }
+
+ private:
+ mutable bool is_valid_;
+ mutable T scoper_;
+};
+
+template <typename T>
+using Unwrapper = BindUnwrapTraits<std::decay_t<T>>;
+
+template <typename T>
+decltype(auto) Unwrap(T&& o) {
+ return Unwrapper<T>::Unwrap(std::forward<T>(o));
+}
+
+// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
+// method. It is used internally by Bind() to select the correct
+// InvokeHelper that will no-op itself in the event the WeakPtr<> for
+// the target object is invalidated.
+//
+// The first argument should be the type of the object that will be received by
+// the method.
+template <bool is_method, typename... Args>
+struct IsWeakMethod : std::false_type {};
+
+template <typename T, typename... Args>
+struct IsWeakMethod<true, T, Args...> : IsWeakReceiver<T> {};
+
+// Packs a list of types to hold them in a single type.
+template <typename... Types>
+struct TypeList {};
+
+// Used for DropTypeListItem implementation.
+template <size_t n, typename List>
+struct DropTypeListItemImpl;
+
+// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.
+template <size_t n, typename T, typename... List>
+struct DropTypeListItemImpl<n, TypeList<T, List...>>
+ : DropTypeListItemImpl<n - 1, TypeList<List...>> {};
+
+template <typename T, typename... List>
+struct DropTypeListItemImpl<0, TypeList<T, List...>> {
+ using Type = TypeList<T, List...>;
+};
+
+template <>
+struct DropTypeListItemImpl<0, TypeList<>> {
+ using Type = TypeList<>;
+};
+
+// A type-level function that drops |n| list item from given TypeList.
+template <size_t n, typename List>
+using DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type;
+
+// Used for TakeTypeListItem implementation.
+template <size_t n, typename List, typename... Accum>
+struct TakeTypeListItemImpl;
+
+// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.
+template <size_t n, typename T, typename... List, typename... Accum>
+struct TakeTypeListItemImpl<n, TypeList<T, List...>, Accum...>
+ : TakeTypeListItemImpl<n - 1, TypeList<List...>, Accum..., T> {};
+
+template <typename T, typename... List, typename... Accum>
+struct TakeTypeListItemImpl<0, TypeList<T, List...>, Accum...> {
+ using Type = TypeList<Accum...>;
+};
+
+template <typename... Accum>
+struct TakeTypeListItemImpl<0, TypeList<>, Accum...> {
+ using Type = TypeList<Accum...>;
+};
+
+// A type-level function that takes first |n| list item from given TypeList.
+// E.g. TakeTypeListItem<3, TypeList<A, B, C, D>> is evaluated to
+// TypeList<A, B, C>.
+template <size_t n, typename List>
+using TakeTypeListItem = typename TakeTypeListItemImpl<n, List>::Type;
+
+// Used for ConcatTypeLists implementation.
+template <typename List1, typename List2>
+struct ConcatTypeListsImpl;
+
+template <typename... Types1, typename... Types2>
+struct ConcatTypeListsImpl<TypeList<Types1...>, TypeList<Types2...>> {
+ using Type = TypeList<Types1..., Types2...>;
+};
+
+// A type-level function that concats two TypeLists.
+template <typename List1, typename List2>
+using ConcatTypeLists = typename ConcatTypeListsImpl<List1, List2>::Type;
+
+// Used for MakeFunctionType implementation.
+template <typename R, typename ArgList>
+struct MakeFunctionTypeImpl;
+
+template <typename R, typename... Args>
+struct MakeFunctionTypeImpl<R, TypeList<Args...>> {
+ // 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 <typename R, typename ArgList>
+using MakeFunctionType = typename MakeFunctionTypeImpl<R, ArgList>::Type;
+
+// Used for ExtractArgs and ExtractReturnType.
+template <typename Signature>
+struct ExtractArgsImpl;
+
+template <typename R, typename... Args>
+struct ExtractArgsImpl<R(Args...)> {
+ using ReturnType = R;
+ using ArgsList = TypeList<Args...>;
+};
+
+// A type-level function that extracts function arguments into a TypeList.
+// E.g. ExtractArgs<R(A, B, C)> is evaluated to TypeList<A, B, C>.
+template <typename Signature>
+using ExtractArgs = typename ExtractArgsImpl<Signature>::ArgsList;
+
+// A type-level function that extracts the return type of a function.
+// E.g. ExtractReturnType<R(A, B, C)> is evaluated to R.
+template <typename Signature>
+using ExtractReturnType = typename ExtractArgsImpl<Signature>::ReturnType;
+
+template <typename Callable,
+ typename Signature = decltype(&Callable::operator())>
+struct ExtractCallableRunTypeImpl;
+
+template <typename Callable, typename R, typename... Args>
+struct ExtractCallableRunTypeImpl<Callable, R (Callable::*)(Args...)> {
+ using Type = R(Args...);
+};
+
+template <typename Callable, typename R, typename... Args>
+struct ExtractCallableRunTypeImpl<Callable, R (Callable::*)(Args...) const> {
+ using Type = R(Args...);
+};
+
+// Evaluated to RunType of the given callable type.
+// Example:
+// auto f = [](int, char*) { return 0.1; };
+// ExtractCallableRunType<decltype(f)>
+// is evaluated to
+// double(int, char*);
+template <typename Callable>
+using ExtractCallableRunType =
+ typename ExtractCallableRunTypeImpl<Callable>::Type;
+
+// IsCallableObject<Functor> is std::true_type if |Functor| has operator().
+// Otherwise, it's std::false_type.
+// Example:
+// IsCallableObject<void(*)()>::value is false.
+//
+// struct Foo {};
+// IsCallableObject<void(Foo::*)()>::value is false.
+//
+// int i = 0;
+// auto f = [i]() {};
+// IsCallableObject<decltype(f)>::value is false.
+template <typename Functor, typename SFINAE = void>
+struct IsCallableObject : std::false_type {};
+
+template <typename Callable>
+struct IsCallableObject<Callable, void_t<decltype(&Callable::operator())>>
+ : std::true_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 <typename... Args>
+struct HasRefCountedTypeAsRawPtr : std::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 <typename T, typename... Args>
+struct HasRefCountedTypeAsRawPtr<T, Args...>
+ : std::conditional_t<NeedsScopedRefptrButGetsRawPtr<T>::value,
+ std::true_type,
+ HasRefCountedTypeAsRawPtr<Args...>> {};
+
+// ForceVoidReturn<>
+//
+// Set of templates that support forcing the function return type to void.
+template <typename Sig>
+struct ForceVoidReturn;
+
+template <typename R, typename... Args>
+struct ForceVoidReturn<R(Args...)> {
+ using RunType = void(Args...);
+};
+
+// FunctorTraits<>
+//
+// See description at top of file.
+template <typename Functor, typename SFINAE>
+struct FunctorTraits;
+
+// For empty callable types.
+// This specialization is intended to allow binding captureless lambdas, based
+// on the fact that captureless lambdas are empty while capturing lambdas are
+// not. This also allows any functors as far as it's an empty class.
+// Example:
+//
+// // Captureless lambdas are allowed.
+// []() {return 42;};
+//
+// // Capturing lambdas are *not* allowed.
+// int x;
+// [x]() {return x;};
+//
+// // Any empty class with operator() is allowed.
+// struct Foo {
+// void operator()() const {}
+// // No non-static member variable and no virtual functions.
+// };
+template <typename Functor>
+struct FunctorTraits<Functor,
+ std::enable_if_t<IsCallableObject<Functor>::value &&
+ std::is_empty<Functor>::value>> {
+ using RunType = ExtractCallableRunType<Functor>;
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = false;
+
+ template <typename RunFunctor, typename... RunArgs>
+ static ExtractReturnType<RunType> Invoke(RunFunctor&& functor,
+ RunArgs&&... args) {
+ return std::forward<RunFunctor>(functor)(std::forward<RunArgs>(args)...);
+ }
+};
+
+// For functions.
+template <typename R, typename... Args>
+struct FunctorTraits<R (*)(Args...)> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename Function, typename... RunArgs>
+ static R Invoke(Function&& function, RunArgs&&... args) {
+ return std::forward<Function>(function)(std::forward<RunArgs>(args)...);
+ }
+};
+
+#if defined(OS_WIN) && !defined(ARCH_CPU_64_BITS)
+
+// For functions.
+template <typename R, typename... Args>
+struct FunctorTraits<R(__stdcall*)(Args...)> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename... RunArgs>
+ static R Invoke(R(__stdcall* function)(Args...), RunArgs&&... args) {
+ return function(std::forward<RunArgs>(args)...);
+ }
+};
+
+// For functions.
+template <typename R, typename... Args>
+struct FunctorTraits<R(__fastcall*)(Args...)> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename... RunArgs>
+ static R Invoke(R(__fastcall* function)(Args...), RunArgs&&... args) {
+ return function(std::forward<RunArgs>(args)...);
+ }
+};
+
+#endif // defined(OS_WIN) && !defined(ARCH_CPU_64_BITS)
+
+#if defined(OS_MACOSX)
+
+// Support for Objective-C blocks. There are two implementation depending
+// on whether Automated Reference Counting (ARC) is enabled. When ARC is
+// enabled, then the block itself can be bound as the compiler will ensure
+// its lifetime will be correctly managed. Otherwise, require the block to
+// be wrapped in a base::mac::ScopedBlock (via base::RetainBlock) that will
+// correctly manage the block lifetime.
+//
+// The two implementation ensure that the One Definition Rule (ODR) is not
+// broken (it is not possible to write a template base::RetainBlock that would
+// work correctly both with ARC enabled and disabled).
+
+#if HAS_FEATURE(objc_arc)
+
+template <typename R, typename... Args>
+struct FunctorTraits<R (^)(Args...)> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename BlockType, typename... RunArgs>
+ static R Invoke(BlockType&& block, RunArgs&&... args) {
+ // According to LLVM documentation (§ 6.3), "local variables of automatic
+ // storage duration do not have precise lifetime." Use objc_precise_lifetime
+ // to ensure that the Objective-C block is not deallocated until it has
+ // finished executing even if the Callback<> is destroyed during the block
+ // execution.
+ // https://clang.llvm.org/docs/AutomaticReferenceCounting.html#precise-lifetime-semantics
+ __attribute__((objc_precise_lifetime)) R (^scoped_block)(Args...) = block;
+ return scoped_block(std::forward<RunArgs>(args)...);
+ }
+};
+
+#else // HAS_FEATURE(objc_arc)
+
+template <typename R, typename... Args>
+struct FunctorTraits<base::mac::ScopedBlock<R (^)(Args...)>> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename BlockType, typename... RunArgs>
+ static R Invoke(BlockType&& block, RunArgs&&... args) {
+ // Copy the block to ensure that the Objective-C block is not deallocated
+ // until it has finished executing even if the Callback<> is destroyed
+ // during the block execution.
+ base::mac::ScopedBlock<R (^)(Args...)> scoped_block(block);
+ return scoped_block.get()(std::forward<RunArgs>(args)...);
+ }
+};
+
+#endif // HAS_FEATURE(objc_arc)
+#endif // defined(OS_MACOSX)
+
+// For methods.
+template <typename R, typename Receiver, typename... Args>
+struct FunctorTraits<R (Receiver::*)(Args...)> {
+ using RunType = R(Receiver*, Args...);
+ static constexpr bool is_method = true;
+ static constexpr bool is_nullable = true;
+
+ template <typename Method, typename ReceiverPtr, typename... RunArgs>
+ static R Invoke(Method method,
+ ReceiverPtr&& receiver_ptr,
+ RunArgs&&... args) {
+ return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);
+ }
+};
+
+// For const methods.
+template <typename R, typename Receiver, typename... Args>
+struct FunctorTraits<R (Receiver::*)(Args...) const> {
+ using RunType = R(const Receiver*, Args...);
+ static constexpr bool is_method = true;
+ static constexpr bool is_nullable = true;
+
+ template <typename Method, typename ReceiverPtr, typename... RunArgs>
+ static R Invoke(Method method,
+ ReceiverPtr&& receiver_ptr,
+ RunArgs&&... args) {
+ return ((*receiver_ptr).*method)(std::forward<RunArgs>(args)...);
+ }
+};
+
+#ifdef __cpp_noexcept_function_type
+// noexcept makes a distinct function type in C++17.
+// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and
+// different in C++17.
+template <typename R, typename... Args>
+struct FunctorTraits<R (*)(Args...) noexcept> : FunctorTraits<R (*)(Args...)> {
+};
+
+template <typename R, typename Receiver, typename... Args>
+struct FunctorTraits<R (Receiver::*)(Args...) noexcept>
+ : FunctorTraits<R (Receiver::*)(Args...)> {};
+
+template <typename R, typename Receiver, typename... Args>
+struct FunctorTraits<R (Receiver::*)(Args...) const noexcept>
+ : FunctorTraits<R (Receiver::*)(Args...) const> {};
+#endif
+
+// For IgnoreResults.
+template <typename T>
+struct FunctorTraits<IgnoreResultHelper<T>> : FunctorTraits<T> {
+ using RunType =
+ typename ForceVoidReturn<typename FunctorTraits<T>::RunType>::RunType;
+
+ template <typename IgnoreResultType, typename... RunArgs>
+ static void Invoke(IgnoreResultType&& ignore_result_helper,
+ RunArgs&&... args) {
+ FunctorTraits<T>::Invoke(
+ std::forward<IgnoreResultType>(ignore_result_helper).functor_,
+ std::forward<RunArgs>(args)...);
+ }
+};
+
+// For OnceCallbacks.
+template <typename R, typename... Args>
+struct FunctorTraits<OnceCallback<R(Args...)>> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename CallbackType, typename... RunArgs>
+ static R Invoke(CallbackType&& callback, RunArgs&&... args) {
+ DCHECK(!callback.is_null());
+ return std::forward<CallbackType>(callback).Run(
+ std::forward<RunArgs>(args)...);
+ }
+};
+
+// For RepeatingCallbacks.
+template <typename R, typename... Args>
+struct FunctorTraits<RepeatingCallback<R(Args...)>> {
+ using RunType = R(Args...);
+ static constexpr bool is_method = false;
+ static constexpr bool is_nullable = true;
+
+ template <typename CallbackType, typename... RunArgs>
+ static R Invoke(CallbackType&& callback, RunArgs&&... args) {
+ DCHECK(!callback.is_null());
+ return std::forward<CallbackType>(callback).Run(
+ std::forward<RunArgs>(args)...);
+ }
+};
+
+template <typename Functor>
+using MakeFunctorTraits = FunctorTraits<std::decay_t<Functor>>;
+
+// InvokeHelper<>
+//
+// There are 2 logical InvokeHelper<> specializations: normal, WeakCalls.
+//
+// The normal type just calls the underlying runnable.
+//
+// WeakCalls need special syntax that is applied to the first argument to check
+// if they should no-op themselves.
+template <bool is_weak_call, typename ReturnType>
+struct InvokeHelper;
+
+template <typename ReturnType>
+struct InvokeHelper<false, ReturnType> {
+ template <typename Functor, typename... RunArgs>
+ static inline ReturnType MakeItSo(Functor&& functor, RunArgs&&... args) {
+ using Traits = MakeFunctorTraits<Functor>;
+ return Traits::Invoke(std::forward<Functor>(functor),
+ std::forward<RunArgs>(args)...);
+ }
+};
+
+template <typename ReturnType>
+struct InvokeHelper<true, ReturnType> {
+ // WeakCalls are only supported for functions with a void return type.
+ // Otherwise, the function result would be undefined if the the WeakPtr<>
+ // is invalidated.
+ static_assert(std::is_void<ReturnType>::value,
+ "weak_ptrs can only bind to methods without return values");
+
+ template <typename Functor, typename BoundWeakPtr, typename... RunArgs>
+ static inline void MakeItSo(Functor&& functor,
+ BoundWeakPtr&& weak_ptr,
+ RunArgs&&... args) {
+ if (!weak_ptr)
+ return;
+ using Traits = MakeFunctorTraits<Functor>;
+ Traits::Invoke(std::forward<Functor>(functor),
+ std::forward<BoundWeakPtr>(weak_ptr),
+ std::forward<RunArgs>(args)...);
+ }
+};
+
+// Invoker<>
+//
+// See description at the top of the file.
+template <typename StorageType, typename UnboundRunType>
+struct Invoker;
+
+template <typename StorageType, typename R, typename... UnboundArgs>
+struct Invoker<StorageType, R(UnboundArgs...)> {
+ static R RunOnce(BindStateBase* base,
+ PassingType<UnboundArgs>... unbound_args) {
+ // 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.
+ StorageType* storage = static_cast<StorageType*>(base);
+ static constexpr size_t num_bound_args =
+ std::tuple_size<decltype(storage->bound_args_)>::value;
+ return RunImpl(std::move(storage->functor_),
+ std::move(storage->bound_args_),
+ std::make_index_sequence<num_bound_args>(),
+ std::forward<UnboundArgs>(unbound_args)...);
+ }
+
+ static R Run(BindStateBase* base, PassingType<UnboundArgs>... unbound_args) {
+ // 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.
+ const StorageType* storage = static_cast<StorageType*>(base);
+ static constexpr size_t num_bound_args =
+ std::tuple_size<decltype(storage->bound_args_)>::value;
+ return RunImpl(storage->functor_, storage->bound_args_,
+ std::make_index_sequence<num_bound_args>(),
+ std::forward<UnboundArgs>(unbound_args)...);
+ }
+
+ private:
+ template <typename Functor, typename BoundArgsTuple, size_t... indices>
+ static inline R RunImpl(Functor&& functor,
+ BoundArgsTuple&& bound,
+ std::index_sequence<indices...>,
+ UnboundArgs&&... unbound_args) {
+ static constexpr bool is_method = MakeFunctorTraits<Functor>::is_method;
+
+ using DecayedArgsTuple = std::decay_t<BoundArgsTuple>;
+ static constexpr bool is_weak_call =
+ IsWeakMethod<is_method,
+ std::tuple_element_t<indices, DecayedArgsTuple>...>();
+
+ return InvokeHelper<is_weak_call, R>::MakeItSo(
+ std::forward<Functor>(functor),
+ Unwrap(std::get<indices>(std::forward<BoundArgsTuple>(bound)))...,
+ std::forward<UnboundArgs>(unbound_args)...);
+ }
+};
+
+// Extracts necessary type info from Functor and BoundArgs.
+// Used to implement MakeUnboundRunType, BindOnce and BindRepeating.
+template <typename Functor, typename... BoundArgs>
+struct BindTypeHelper {
+ static constexpr size_t num_bounds = sizeof...(BoundArgs);
+ using FunctorTraits = MakeFunctorTraits<Functor>;
+
+ // Example:
+ // When Functor is `double (Foo::*)(int, const std::string&)`, and BoundArgs
+ // is a template pack of `Foo*` and `int16_t`:
+ // - RunType is `double(Foo*, int, const std::string&)`,
+ // - ReturnType is `double`,
+ // - RunParamsList is `TypeList<Foo*, int, const std::string&>`,
+ // - BoundParamsList is `TypeList<Foo*, int>`,
+ // - UnboundParamsList is `TypeList<const std::string&>`,
+ // - BoundArgsList is `TypeList<Foo*, int16_t>`,
+ // - UnboundRunType is `double(const std::string&)`.
+ using RunType = typename FunctorTraits::RunType;
+ using ReturnType = ExtractReturnType<RunType>;
+
+ using RunParamsList = ExtractArgs<RunType>;
+ using BoundParamsList = TakeTypeListItem<num_bounds, RunParamsList>;
+ using UnboundParamsList = DropTypeListItem<num_bounds, RunParamsList>;
+
+ using BoundArgsList = TypeList<BoundArgs...>;
+
+ using UnboundRunType = MakeFunctionType<ReturnType, UnboundParamsList>;
+};
+
+template <typename Functor>
+std::enable_if_t<FunctorTraits<Functor>::is_nullable, bool> IsNull(
+ const Functor& functor) {
+ return !functor;
+}
+
+template <typename Functor>
+std::enable_if_t<!FunctorTraits<Functor>::is_nullable, bool> IsNull(
+ const Functor&) {
+ return false;
+}
+
+// Used by QueryCancellationTraits below.
+template <typename Functor, typename BoundArgsTuple, size_t... indices>
+bool QueryCancellationTraitsImpl(BindStateBase::CancellationQueryMode mode,
+ const Functor& functor,
+ const BoundArgsTuple& bound_args,
+ std::index_sequence<indices...>) {
+ switch (mode) {
+ case BindStateBase::IS_CANCELLED:
+ return CallbackCancellationTraits<Functor, BoundArgsTuple>::IsCancelled(
+ functor, std::get<indices>(bound_args)...);
+ case BindStateBase::MAYBE_VALID:
+ return CallbackCancellationTraits<Functor, BoundArgsTuple>::MaybeValid(
+ functor, std::get<indices>(bound_args)...);
+ }
+ NOTREACHED();
+}
+
+// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns
+// true if the callback |base| represents is canceled.
+template <typename BindStateType>
+bool QueryCancellationTraits(const BindStateBase* base,
+ BindStateBase::CancellationQueryMode mode) {
+ const BindStateType* storage = static_cast<const BindStateType*>(base);
+ static constexpr size_t num_bound_args =
+ std::tuple_size<decltype(storage->bound_args_)>::value;
+ return QueryCancellationTraitsImpl(
+ mode, storage->functor_, storage->bound_args_,
+ std::make_index_sequence<num_bound_args>());
+}
+
+// The base case of BanUnconstructedRefCountedReceiver that checks nothing.
+template <typename Functor, typename Receiver, typename... Unused>
+std::enable_if_t<
+ !(MakeFunctorTraits<Functor>::is_method &&
+ std::is_pointer<std::decay_t<Receiver>>::value &&
+ IsRefCountedType<std::remove_pointer_t<std::decay_t<Receiver>>>::value)>
+BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {}
+
+template <typename Functor>
+void BanUnconstructedRefCountedReceiver() {}
+
+// Asserts that Callback is not the first owner of a ref-counted receiver.
+template <typename Functor, typename Receiver, typename... Unused>
+std::enable_if_t<
+ MakeFunctorTraits<Functor>::is_method &&
+ std::is_pointer<std::decay_t<Receiver>>::value &&
+ IsRefCountedType<std::remove_pointer_t<std::decay_t<Receiver>>>::value>
+BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
+ DCHECK(receiver);
+
+ // It's error prone to make the implicit first reference to ref-counted types.
+ // In the example below, base::BindOnce() makes the implicit first reference
+ // to the ref-counted Foo. If PostTask() failed or the posted task ran fast
+ // enough, the newly created instance can be destroyed before |oo| makes
+ // another reference.
+ // Foo::Foo() {
+ // base::PostTask(FROM_HERE, base::BindOnce(&Foo::Bar, this));
+ // }
+ //
+ // scoped_refptr<Foo> oo = new Foo();
+ //
+ // Instead of doing like above, please consider adding a static constructor,
+ // and keep the first reference alive explicitly.
+ // // static
+ // scoped_refptr<Foo> Foo::Create() {
+ // auto foo = base::WrapRefCounted(new Foo());
+ // base::PostTask(FROM_HERE, base::BindOnce(&Foo::Bar, foo));
+ // return foo;
+ // }
+ //
+ // Foo::Foo() {}
+ //
+ // scoped_refptr<Foo> oo = Foo::Create();
+ DCHECK(receiver->HasAtLeastOneRef())
+ << "base::Bind{Once,Repeating}() refuses to create the first reference "
+ "to ref-counted objects. That typically happens around PostTask() in "
+ "their constructor, and such objects can be destroyed before `new` "
+ "returns if the task resolves fast enough.";
+}
+
+// BindState<>
+//
+// This stores all the state passed into Bind().
+template <typename Functor, typename... BoundArgs>
+struct BindState final : BindStateBase {
+ using IsCancellable = std::integral_constant<
+ bool,
+ CallbackCancellationTraits<Functor,
+ std::tuple<BoundArgs...>>::is_cancellable>;
+
+ template <typename ForwardFunctor, typename... ForwardBoundArgs>
+ static BindState* Create(BindStateBase::InvokeFuncStorage invoke_func,
+ ForwardFunctor&& functor,
+ ForwardBoundArgs&&... bound_args) {
+ // Ban ref counted receivers that were not yet fully constructed to avoid
+ // a common pattern of racy situation.
+ BanUnconstructedRefCountedReceiver<ForwardFunctor>(bound_args...);
+
+ // IsCancellable is std::false_type if
+ // CallbackCancellationTraits<>::IsCancelled returns always false.
+ // Otherwise, it's std::true_type.
+ return new BindState(IsCancellable{}, invoke_func,
+ std::forward<ForwardFunctor>(functor),
+ std::forward<ForwardBoundArgs>(bound_args)...);
+ }
+
+ Functor functor_;
+ std::tuple<BoundArgs...> bound_args_;
+
+ private:
+ template <typename ForwardFunctor, typename... ForwardBoundArgs>
+ explicit BindState(std::true_type,
+ BindStateBase::InvokeFuncStorage invoke_func,
+ ForwardFunctor&& functor,
+ ForwardBoundArgs&&... bound_args)
+ : BindStateBase(invoke_func,
+ &Destroy,
+ &QueryCancellationTraits<BindState>),
+ functor_(std::forward<ForwardFunctor>(functor)),
+ bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
+ DCHECK(!IsNull(functor_));
+ }
+
+ template <typename ForwardFunctor, typename... ForwardBoundArgs>
+ explicit BindState(std::false_type,
+ BindStateBase::InvokeFuncStorage invoke_func,
+ ForwardFunctor&& functor,
+ ForwardBoundArgs&&... bound_args)
+ : BindStateBase(invoke_func, &Destroy),
+ functor_(std::forward<ForwardFunctor>(functor)),
+ bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
+ DCHECK(!IsNull(functor_));
+ }
+
+ ~BindState() = default;
+
+ static void Destroy(const BindStateBase* self) {
+ delete static_cast<const BindState*>(self);
+ }
+};
+
+// Used to implement MakeBindStateType.
+template <bool is_method, typename Functor, typename... BoundArgs>
+struct MakeBindStateTypeImpl;
+
+template <typename Functor, typename... BoundArgs>
+struct MakeBindStateTypeImpl<false, Functor, BoundArgs...> {
+ static_assert(!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value,
+ "A parameter is a refcounted type and needs scoped_refptr.");
+ using Type = BindState<std::decay_t<Functor>, std::decay_t<BoundArgs>...>;
+};
+
+template <typename Functor>
+struct MakeBindStateTypeImpl<true, Functor> {
+ using Type = BindState<std::decay_t<Functor>>;
+};
+
+template <typename Functor, typename Receiver, typename... BoundArgs>
+struct MakeBindStateTypeImpl<true, Functor, Receiver, BoundArgs...> {
+ private:
+ using DecayedReceiver = std::decay_t<Receiver>;
+
+ static_assert(!std::is_array<std::remove_reference_t<Receiver>>::value,
+ "First bound argument to a method cannot be an array.");
+ static_assert(
+ !std::is_pointer<DecayedReceiver>::value ||
+ IsRefCountedType<std::remove_pointer_t<DecayedReceiver>>::value,
+ "Receivers may not be raw pointers. If using a raw pointer here is safe"
+ " and has no lifetime concerns, use base::Unretained() and document why"
+ " it's safe.");
+ static_assert(!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value,
+ "A parameter is a refcounted type and needs scoped_refptr.");
+
+ public:
+ using Type = BindState<
+ std::decay_t<Functor>,
+ std::conditional_t<std::is_pointer<DecayedReceiver>::value,
+ scoped_refptr<std::remove_pointer_t<DecayedReceiver>>,
+ DecayedReceiver>,
+ std::decay_t<BoundArgs>...>;
+};
+
+template <typename Functor, typename... BoundArgs>
+using MakeBindStateType =
+ typename MakeBindStateTypeImpl<MakeFunctorTraits<Functor>::is_method,
+ Functor,
+ BoundArgs...>::Type;
+
+} // namespace internal
+
+// An injection point to control |this| pointer behavior on a method invocation.
+// If IsWeakReceiver<> is true_type for |T| and |T| is used for a receiver of a
+// method, base::Bind cancels the method invocation if the receiver is tested as
+// false.
+// E.g. Foo::bar() is not called:
+// struct Foo : base::SupportsWeakPtr<Foo> {
+// void bar() {}
+// };
+//
+// WeakPtr<Foo> oo = nullptr;
+// base::BindOnce(&Foo::bar, oo).Run();
+template <typename T>
+struct IsWeakReceiver : std::false_type {};
+
+template <typename T>
+struct IsWeakReceiver<std::reference_wrapper<T>> : IsWeakReceiver<T> {};
+
+template <typename T>
+struct IsWeakReceiver<WeakPtr<T>> : std::true_type {};
+
+// An injection point to control how bound objects passed to the target
+// function. BindUnwrapTraits<>::Unwrap() is called for each bound objects right
+// before the target function is invoked.
+template <typename>
+struct BindUnwrapTraits {
+ template <typename T>
+ static T&& Unwrap(T&& o) {
+ return std::forward<T>(o);
+ }
+};
+
+template <typename T>
+struct BindUnwrapTraits<internal::UnretainedWrapper<T>> {
+ static T* Unwrap(const internal::UnretainedWrapper<T>& o) { return o.get(); }
+};
+
+template <typename T>
+struct BindUnwrapTraits<std::reference_wrapper<T>> {
+ static T& Unwrap(std::reference_wrapper<T> o) { return o.get(); }
+};
+
+template <typename T>
+struct BindUnwrapTraits<internal::RetainedRefWrapper<T>> {
+ static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); }
+};
+
+template <typename T, typename Deleter>
+struct BindUnwrapTraits<internal::OwnedWrapper<T, Deleter>> {
+ static T* Unwrap(const internal::OwnedWrapper<T, Deleter>& o) {
+ return o.get();
+ }
+};
+
+template <typename T>
+struct BindUnwrapTraits<internal::PassedWrapper<T>> {
+ static T Unwrap(const internal::PassedWrapper<T>& o) { return o.Take(); }
+};
+
+#if defined(OS_WIN)
+template <typename T>
+struct BindUnwrapTraits<Microsoft::WRL::ComPtr<T>> {
+ static T* Unwrap(const Microsoft::WRL::ComPtr<T>& ptr) { return ptr.Get(); }
+};
+#endif
+
+// CallbackCancellationTraits allows customization of Callback's cancellation
+// semantics. By default, callbacks are not cancellable. A specialization should
+// set is_cancellable = true and implement an IsCancelled() that returns if the
+// callback should be cancelled.
+template <typename Functor, typename BoundArgsTuple, typename SFINAE>
+struct CallbackCancellationTraits {
+ static constexpr bool is_cancellable = false;
+};
+
+// Specialization for method bound to weak pointer receiver.
+template <typename Functor, typename... BoundArgs>
+struct CallbackCancellationTraits<
+ Functor,
+ std::tuple<BoundArgs...>,
+ std::enable_if_t<
+ internal::IsWeakMethod<internal::FunctorTraits<Functor>::is_method,
+ BoundArgs...>::value>> {
+ static constexpr bool is_cancellable = true;
+
+ template <typename Receiver, typename... Args>
+ static bool IsCancelled(const Functor&,
+ const Receiver& receiver,
+ const Args&...) {
+ return !receiver;
+ }
+
+ template <typename Receiver, typename... Args>
+ static bool MaybeValid(const Functor&,
+ const Receiver& receiver,
+ const Args&...) {
+ return receiver.MaybeValid();
+ }
+};
+
+// Specialization for a nested bind.
+template <typename Signature, typename... BoundArgs>
+struct CallbackCancellationTraits<OnceCallback<Signature>,
+ std::tuple<BoundArgs...>> {
+ static constexpr bool is_cancellable = true;
+
+ template <typename Functor>
+ static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
+ return functor.IsCancelled();
+ }
+
+ template <typename Functor>
+ static bool MaybeValid(const Functor& functor, const BoundArgs&...) {
+ return functor.MaybeValid();
+ }
+};
+
+template <typename Signature, typename... BoundArgs>
+struct CallbackCancellationTraits<RepeatingCallback<Signature>,
+ std::tuple<BoundArgs...>> {
+ static constexpr bool is_cancellable = true;
+
+ template <typename Functor>
+ static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
+ return functor.IsCancelled();
+ }
+
+ template <typename Functor>
+ static bool MaybeValid(const Functor& functor, const BoundArgs&...) {
+ return functor.MaybeValid();
+ }
+};
+
+// Returns a RunType of bound functor.
+// E.g. MakeUnboundRunType<R(A, B, C), A, B> is evaluated to R(C).
+template <typename Functor, typename... BoundArgs>
+using MakeUnboundRunType =
+ typename internal::BindTypeHelper<Functor, BoundArgs...>::UnboundRunType;
+
+} // namespace base
+
+#endif // BASE_BIND_INTERNAL_H_
diff --git a/security/sandbox/chromium/base/bit_cast.h b/security/sandbox/chromium/base/bit_cast.h
new file mode 100644
index 0000000000..90dd925e86
--- /dev/null
+++ b/security/sandbox/chromium/base/bit_cast.h
@@ -0,0 +1,77 @@
+// 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 <string.h>
+#include <type_traits>
+
+#include "base/compiler_specific.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+
+// bit_cast<Dest,Source> is a template function that implements the equivalent
+// of "*reinterpret_cast<Dest*>(&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<int32_t>(f);
+// // i = 0x40490fdb
+//
+// The classical address-casting method is:
+//
+// // WRONG
+// float f = 3.14159265358979; // WRONG
+// int i = * reinterpret_cast<int*>(&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<int*>(&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.
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+ static_assert(sizeof(Dest) == sizeof(Source),
+ "bit_cast requires source and destination to be the same size");
+ static_assert(base::is_trivially_copyable<Dest>::value,
+ "bit_cast requires the destination type to be copyable");
+ static_assert(base::is_trivially_copyable<Source>::value,
+ "bit_cast requires the source type to be copyable");
+
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+#endif // BASE_BIT_CAST_H_
diff --git a/security/sandbox/chromium/base/bits.h b/security/sandbox/chromium/base/bits.h
new file mode 100644
index 0000000000..d2c5ac9caa
--- /dev/null
+++ b/security/sandbox/chromium/base/bits.h
@@ -0,0 +1,209 @@
+// 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.
+
+// This file defines some bit utilities.
+
+#ifndef BASE_BITS_H_
+#define BASE_BITS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <type_traits>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+#include <intrin.h>
+#endif
+
+namespace base {
+namespace bits {
+
+// Returns true iff |value| is a power of 2.
+template <typename T,
+ typename = typename std::enable_if<std::is_integral<T>::value>>
+constexpr inline bool IsPowerOfTwo(T value) {
+ // From "Hacker's Delight": Section 2.1 Manipulating Rightmost Bits.
+ //
+ // Only positive integers with a single bit set are powers of two. If only one
+ // bit is set in x (e.g. 0b00000100000000) then |x-1| will have that bit set
+ // to zero and all bits to its right set to 1 (e.g. 0b00000011111111). Hence
+ // |x & (x-1)| is 0 iff x is a power of two.
+ return value > 0 && (value & (value - 1)) == 0;
+}
+
+// Round up |size| to a multiple of alignment, which must be a power of two.
+inline size_t Align(size_t size, size_t alignment) {
+ DCHECK(IsPowerOfTwo(alignment));
+ return (size + alignment - 1) & ~(alignment - 1);
+}
+
+// Round down |size| to a multiple of alignment, which must be a power of two.
+inline size_t AlignDown(size_t size, size_t alignment) {
+ DCHECK(IsPowerOfTwo(alignment));
+ return size & ~(alignment - 1);
+}
+
+// CountLeadingZeroBits(value) returns the number of zero bits following the
+// most significant 1 bit in |value| if |value| is non-zero, otherwise it
+// returns {sizeof(T) * 8}.
+// Example: 00100010 -> 2
+//
+// CountTrailingZeroBits(value) returns the number of zero bits preceding the
+// least significant 1 bit in |value| if |value| is non-zero, otherwise it
+// returns {sizeof(T) * 8}.
+// Example: 00100010 -> 1
+//
+// C does not have an operator to do this, but fortunately the various
+// compilers have built-ins that map to fast underlying processor instructions.
+#if defined(COMPILER_MSVC)
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+ typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
+ unsigned>::type
+ CountLeadingZeroBits(T x) {
+ static_assert(bits > 0, "invalid instantiation");
+ unsigned long index;
+ return LIKELY(_BitScanReverse(&index, static_cast<uint32_t>(x)))
+ ? (31 - index - (32 - bits))
+ : bits;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+ typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
+ unsigned>::type
+ CountLeadingZeroBits(T x) {
+ static_assert(bits > 0, "invalid instantiation");
+ unsigned long index;
+// MSVC only supplies _BitScanReverse64 when building for a 64-bit target.
+#if defined(ARCH_CPU_64_BITS)
+ return LIKELY(_BitScanReverse64(&index, static_cast<uint64_t>(x)))
+ ? (63 - index)
+ : 64;
+#else
+ uint32_t left = static_cast<uint32_t>(x >> 32);
+ if (LIKELY(_BitScanReverse(&index, left)))
+ return 31 - index;
+
+ uint32_t right = static_cast<uint32_t>(x);
+ if (LIKELY(_BitScanReverse(&index, right)))
+ return 63 - index;
+
+ return 64;
+#endif
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+ typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 4,
+ unsigned>::type
+ CountTrailingZeroBits(T x) {
+ static_assert(bits > 0, "invalid instantiation");
+ unsigned long index;
+ return LIKELY(_BitScanForward(&index, static_cast<uint32_t>(x))) ? index
+ : bits;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+ typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) == 8,
+ unsigned>::type
+ CountTrailingZeroBits(T x) {
+ static_assert(bits > 0, "invalid instantiation");
+ unsigned long index;
+// MSVC only supplies _BitScanForward64 when building for a 64-bit target.
+#if defined(ARCH_CPU_64_BITS)
+ return LIKELY(_BitScanForward64(&index, static_cast<uint64_t>(x))) ? index
+ : 64;
+#else
+ uint32_t right = static_cast<uint32_t>(x);
+ if (LIKELY(_BitScanForward(&index, right)))
+ return index;
+
+ uint32_t left = static_cast<uint32_t>(x >> 32);
+ if (LIKELY(_BitScanForward(&index, left)))
+ return 32 + index;
+
+ return 64;
+#endif
+}
+
+ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
+ return CountLeadingZeroBits(x);
+}
+
+ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
+ return CountLeadingZeroBits(x);
+}
+
+#elif defined(COMPILER_GCC)
+
+// __builtin_clz has undefined behaviour for an input of 0, even though there's
+// clearly a return value that makes sense, and even though some processor clz
+// instructions have defined behaviour for 0. We could drop to raw __asm__ to
+// do better, but we'll avoid doing that unless we see proof that we need to.
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+ typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
+ unsigned>::type
+ CountLeadingZeroBits(T value) {
+ static_assert(bits > 0, "invalid instantiation");
+ return LIKELY(value)
+ ? bits == 64
+ ? __builtin_clzll(static_cast<uint64_t>(value))
+ : __builtin_clz(static_cast<uint32_t>(value)) - (32 - bits)
+ : bits;
+}
+
+template <typename T, unsigned bits = sizeof(T) * 8>
+ALWAYS_INLINE
+ typename std::enable_if<std::is_unsigned<T>::value && sizeof(T) <= 8,
+ unsigned>::type
+ CountTrailingZeroBits(T value) {
+ return LIKELY(value) ? bits == 64
+ ? __builtin_ctzll(static_cast<uint64_t>(value))
+ : __builtin_ctz(static_cast<uint32_t>(value))
+ : bits;
+}
+
+ALWAYS_INLINE uint32_t CountLeadingZeroBits32(uint32_t x) {
+ return CountLeadingZeroBits(x);
+}
+
+ALWAYS_INLINE uint64_t CountLeadingZeroBits64(uint64_t x) {
+ return CountLeadingZeroBits(x);
+}
+
+#endif
+
+ALWAYS_INLINE size_t CountLeadingZeroBitsSizeT(size_t x) {
+ return CountLeadingZeroBits(x);
+}
+
+ALWAYS_INLINE size_t CountTrailingZeroBitsSizeT(size_t x) {
+ return CountTrailingZeroBits(x);
+}
+
+// Returns the integer i such as 2^i <= n < 2^(i+1)
+inline int Log2Floor(uint32_t n) {
+ return 31 - CountLeadingZeroBits(n);
+}
+
+// Returns the integer i such as 2^(i-1) < n <= 2^i
+inline int Log2Ceiling(uint32_t n) {
+ // When n == 0, we want the function to return -1.
+ // When n == 0, (n - 1) will underflow to 0xFFFFFFFF, which is
+ // why the statement below starts with (n ? 32 : -1).
+ return (n ? 32 : -1) - CountLeadingZeroBits(n - 1);
+}
+
+} // namespace bits
+} // namespace base
+
+#endif // BASE_BITS_H_
diff --git a/security/sandbox/chromium/base/callback.h b/security/sandbox/chromium/base/callback.h
new file mode 100644
index 0000000000..1427faaaea
--- /dev/null
+++ b/security/sandbox/chromium/base/callback.h
@@ -0,0 +1,149 @@
+// 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.
+//
+// NOTE: Header files that do not require the full definition of
+// base::{Once,Repeating}Callback or base::{Once,Repeating}Closure should
+// #include "base/callback_forward.h" instead of this file.
+
+#ifndef BASE_CALLBACK_H_
+#define BASE_CALLBACK_H_
+
+#include <stddef.h>
+
+#include "base/callback_forward.h"
+#include "base/callback_internal.h"
+
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// Overview:
+// A callback is similar in concept to a function pointer: it wraps a runnable
+// object such as a function, method, lambda, or even another callback, allowing
+// the runnable object to be invoked later via the callback object.
+//
+// Unlike function pointers, callbacks are created with base::BindOnce() or
+// base::BindRepeating() and support partial function application.
+//
+// A base::OnceCallback may be Run() at most once; a base::RepeatingCallback may
+// be Run() any number of times. |is_null()| is guaranteed to return true for a
+// moved-from callback.
+//
+// // The lambda takes two arguments, but the first argument |x| is bound at
+// // callback creation.
+// base::OnceCallback<int(int)> cb = base::BindOnce([] (int x, int y) {
+// return x + y;
+// }, 1);
+// // Run() only needs the remaining unbound argument |y|.
+// printf("1 + 2 = %d\n", std::move(cb).Run(2)); // Prints 3
+// printf("cb is null? %s\n",
+// cb.is_null() ? "true" : "false"); // Prints true
+// std::move(cb).Run(2); // Crashes since |cb| has already run.
+//
+// Callbacks also support cancellation. A common use is binding the receiver
+// object as a WeakPtr<T>. If that weak pointer is invalidated, calling Run()
+// will be a no-op. Note that |IsCancelled()| and |is_null()| are distinct:
+// simply cancelling a callback will not also make it null.
+//
+// base::Callback is currently a type alias for base::RepeatingCallback. In the
+// future, we expect to flip this to default to base::OnceCallback.
+//
+// See //docs/callback.md for the full documentation.
+
+namespace base {
+
+template <typename R, typename... Args>
+class OnceCallback<R(Args...)> : public internal::CallbackBase {
+ public:
+ using RunType = R(Args...);
+ using PolymorphicInvoke = R (*)(internal::BindStateBase*,
+ internal::PassingType<Args>...);
+
+ constexpr OnceCallback() = default;
+ OnceCallback(std::nullptr_t) = delete;
+
+ explicit OnceCallback(internal::BindStateBase* bind_state)
+ : internal::CallbackBase(bind_state) {}
+
+ OnceCallback(const OnceCallback&) = delete;
+ OnceCallback& operator=(const OnceCallback&) = delete;
+
+ OnceCallback(OnceCallback&&) noexcept = default;
+ OnceCallback& operator=(OnceCallback&&) noexcept = default;
+
+ OnceCallback(RepeatingCallback<RunType> other)
+ : internal::CallbackBase(std::move(other)) {}
+
+ OnceCallback& operator=(RepeatingCallback<RunType> other) {
+ static_cast<internal::CallbackBase&>(*this) = std::move(other);
+ return *this;
+ }
+
+ R Run(Args... args) const & {
+ static_assert(!sizeof(*this),
+ "OnceCallback::Run() may only be invoked on a non-const "
+ "rvalue, i.e. std::move(callback).Run().");
+ NOTREACHED();
+ }
+
+ R Run(Args... args) && {
+ // Move the callback instance into a local variable before the invocation,
+ // that ensures the internal state is cleared after the invocation.
+ // It's not safe to touch |this| after the invocation, since running the
+ // bound function may destroy |this|.
+ OnceCallback cb = std::move(*this);
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
+ return f(cb.bind_state_.get(), std::forward<Args>(args)...);
+ }
+};
+
+template <typename R, typename... Args>
+class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
+ public:
+ using RunType = R(Args...);
+ using PolymorphicInvoke = R (*)(internal::BindStateBase*,
+ internal::PassingType<Args>...);
+
+ constexpr RepeatingCallback() = default;
+ RepeatingCallback(std::nullptr_t) = delete;
+
+ explicit RepeatingCallback(internal::BindStateBase* bind_state)
+ : internal::CallbackBaseCopyable(bind_state) {}
+
+ // Copyable and movable.
+ RepeatingCallback(const RepeatingCallback&) = default;
+ RepeatingCallback& operator=(const RepeatingCallback&) = default;
+ RepeatingCallback(RepeatingCallback&&) noexcept = default;
+ RepeatingCallback& operator=(RepeatingCallback&&) noexcept = default;
+
+ bool operator==(const RepeatingCallback& other) const {
+ return EqualsInternal(other);
+ }
+
+ bool operator!=(const RepeatingCallback& other) const {
+ return !operator==(other);
+ }
+
+ R Run(Args... args) const & {
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(this->polymorphic_invoke());
+ return f(this->bind_state_.get(), std::forward<Args>(args)...);
+ }
+
+ R Run(Args... args) && {
+ // Move the callback instance into a local variable before the invocation,
+ // that ensures the internal state is cleared after the invocation.
+ // It's not safe to touch |this| after the invocation, since running the
+ // bound function may destroy |this|.
+ RepeatingCallback cb = std::move(*this);
+ PolymorphicInvoke f =
+ reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
+ return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_CALLBACK_H_
diff --git a/security/sandbox/chromium/base/callback_forward.h b/security/sandbox/chromium/base/callback_forward.h
new file mode 100644
index 0000000000..d0f230cedb
--- /dev/null
+++ b/security/sandbox/chromium/base/callback_forward.h
@@ -0,0 +1,28 @@
+// 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_CALLBACK_FORWARD_H_
+#define BASE_CALLBACK_FORWARD_H_
+
+namespace base {
+
+template <typename Signature>
+class OnceCallback;
+
+template <typename Signature>
+class RepeatingCallback;
+
+template <typename Signature>
+using Callback = RepeatingCallback<Signature>;
+
+// Syntactic sugar to make OnceClosure<void()> and RepeatingClosure<void()>
+// easier to declare since they will be used in a lot of APIs with delayed
+// execution.
+using OnceClosure = OnceCallback<void()>;
+using RepeatingClosure = RepeatingCallback<void()>;
+using Closure = Callback<void()>;
+
+} // namespace base
+
+#endif // BASE_CALLBACK_FORWARD_H_
diff --git a/security/sandbox/chromium/base/callback_internal.cc b/security/sandbox/chromium/base/callback_internal.cc
new file mode 100644
index 0000000000..d710682e1f
--- /dev/null
+++ b/security/sandbox/chromium/base/callback_internal.cc
@@ -0,0 +1,101 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/callback_internal.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+bool QueryCancellationTraitsForNonCancellables(
+ const BindStateBase*,
+ BindStateBase::CancellationQueryMode mode) {
+ switch (mode) {
+ case BindStateBase::IS_CANCELLED:
+ return false;
+ case BindStateBase::MAYBE_VALID:
+ return true;
+ }
+ NOTREACHED();
+ return false;
+}
+
+} // namespace
+
+void BindStateBaseRefCountTraits::Destruct(const BindStateBase* bind_state) {
+ bind_state->destructor_(bind_state);
+}
+
+BindStateBase::BindStateBase(InvokeFuncStorage polymorphic_invoke,
+ void (*destructor)(const BindStateBase*))
+ : BindStateBase(polymorphic_invoke,
+ destructor,
+ &QueryCancellationTraitsForNonCancellables) {}
+
+BindStateBase::BindStateBase(
+ InvokeFuncStorage polymorphic_invoke,
+ void (*destructor)(const BindStateBase*),
+ bool (*query_cancellation_traits)(const BindStateBase*,
+ CancellationQueryMode))
+ : polymorphic_invoke_(polymorphic_invoke),
+ destructor_(destructor),
+ query_cancellation_traits_(query_cancellation_traits) {}
+
+CallbackBase& CallbackBase::operator=(CallbackBase&& c) noexcept = default;
+CallbackBase::CallbackBase(const CallbackBaseCopyable& c)
+ : bind_state_(c.bind_state_) {}
+
+CallbackBase& CallbackBase::operator=(const CallbackBaseCopyable& c) {
+ bind_state_ = c.bind_state_;
+ return *this;
+}
+
+CallbackBase::CallbackBase(CallbackBaseCopyable&& c) noexcept
+ : bind_state_(std::move(c.bind_state_)) {}
+
+CallbackBase& CallbackBase::operator=(CallbackBaseCopyable&& c) noexcept {
+ bind_state_ = std::move(c.bind_state_);
+ return *this;
+}
+
+void CallbackBase::Reset() {
+ // NULL the bind_state_ last, since it may be holding the last ref to whatever
+ // object owns us, and we may be deleted after that.
+ bind_state_ = nullptr;
+}
+
+bool CallbackBase::IsCancelled() const {
+ DCHECK(bind_state_);
+ return bind_state_->IsCancelled();
+}
+
+bool CallbackBase::MaybeValid() const {
+ DCHECK(bind_state_);
+ return bind_state_->MaybeValid();
+}
+
+bool CallbackBase::EqualsInternal(const CallbackBase& other) const {
+ return bind_state_ == other.bind_state_;
+}
+
+CallbackBase::~CallbackBase() = default;
+
+CallbackBaseCopyable::CallbackBaseCopyable(const CallbackBaseCopyable& c) {
+ bind_state_ = c.bind_state_;
+}
+
+CallbackBaseCopyable& CallbackBaseCopyable::operator=(
+ const CallbackBaseCopyable& c) {
+ bind_state_ = c.bind_state_;
+ return *this;
+}
+
+CallbackBaseCopyable& CallbackBaseCopyable::operator=(
+ CallbackBaseCopyable&& c) noexcept = default;
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/callback_internal.h b/security/sandbox/chromium/base/callback_internal.h
new file mode 100644
index 0000000000..fdfdf7f817
--- /dev/null
+++ b/security/sandbox/chromium/base/callback_internal.h
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains utility functions and classes that help the
+// implementation, and management of the Callback objects.
+
+#ifndef BASE_CALLBACK_INTERNAL_H_
+#define BASE_CALLBACK_INTERNAL_H_
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+
+namespace base {
+
+struct FakeBindState;
+
+namespace internal {
+
+class BindStateBase;
+class FinallyExecutorCommon;
+class ThenAndCatchExecutorCommon;
+
+template <typename ReturnType>
+class PostTaskExecutor;
+
+template <typename Functor, typename... BoundArgs>
+struct BindState;
+
+class CallbackBase;
+class CallbackBaseCopyable;
+
+struct BindStateBaseRefCountTraits {
+ static void Destruct(const BindStateBase*);
+};
+
+template <typename T>
+using PassingType = std::conditional_t<std::is_scalar<T>::value, T, T&&>;
+
+// BindStateBase is used to provide an opaque handle that the Callback
+// class can use to represent a function object with bound arguments. It
+// behaves as an existential type that is used by a corresponding
+// 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."
+// At the base level, the only task is to add reference counting data. Avoid
+// using or inheriting any virtual functions. 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 BASE_EXPORT BindStateBase
+ : public RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits> {
+ public:
+ REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();
+
+ enum CancellationQueryMode {
+ IS_CANCELLED,
+ MAYBE_VALID,
+ };
+
+ using InvokeFuncStorage = void(*)();
+
+ private:
+ BindStateBase(InvokeFuncStorage polymorphic_invoke,
+ void (*destructor)(const BindStateBase*));
+ BindStateBase(InvokeFuncStorage polymorphic_invoke,
+ void (*destructor)(const BindStateBase*),
+ bool (*query_cancellation_traits)(const BindStateBase*,
+ CancellationQueryMode mode));
+
+ ~BindStateBase() = default;
+
+ friend struct BindStateBaseRefCountTraits;
+ friend class RefCountedThreadSafe<BindStateBase, BindStateBaseRefCountTraits>;
+
+ friend class CallbackBase;
+ friend class CallbackBaseCopyable;
+
+ // Whitelist subclasses that access the destructor of BindStateBase.
+ template <typename Functor, typename... BoundArgs>
+ friend struct BindState;
+ friend struct ::base::FakeBindState;
+
+ bool IsCancelled() const {
+ return query_cancellation_traits_(this, IS_CANCELLED);
+ }
+
+ bool MaybeValid() const {
+ return query_cancellation_traits_(this, MAYBE_VALID);
+ }
+
+ // In C++, it is safe to cast function pointers to function pointers of
+ // 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.
+ InvokeFuncStorage polymorphic_invoke_;
+
+ // Pointer to a function that will properly destroy |this|.
+ void (*destructor_)(const BindStateBase*);
+ bool (*query_cancellation_traits_)(const BindStateBase*,
+ CancellationQueryMode mode);
+
+ DISALLOW_COPY_AND_ASSIGN(BindStateBase);
+};
+
+// Holds the Callback methods that don't require specialization to reduce
+// template bloat.
+// CallbackBase<MoveOnly> is a direct base class of MoveOnly callbacks, and
+// CallbackBase<Copyable> uses CallbackBase<MoveOnly> for its implementation.
+class BASE_EXPORT CallbackBase {
+ public:
+ inline CallbackBase(CallbackBase&& c) noexcept;
+ CallbackBase& operator=(CallbackBase&& c) noexcept;
+
+ explicit CallbackBase(const CallbackBaseCopyable& c);
+ CallbackBase& operator=(const CallbackBaseCopyable& c);
+
+ explicit CallbackBase(CallbackBaseCopyable&& c) noexcept;
+ CallbackBase& operator=(CallbackBaseCopyable&& c) noexcept;
+
+ // Returns true if Callback is null (doesn't refer to anything).
+ bool is_null() const { return !bind_state_; }
+ explicit operator bool() const { return !is_null(); }
+
+ // Returns true if the callback invocation will be nop due to an cancellation.
+ // It's invalid to call this on uninitialized callback.
+ //
+ // Must be called on the Callback's destination sequence.
+ bool IsCancelled() const;
+
+ // If this returns false, the callback invocation will be a nop due to a
+ // cancellation. This may(!) still return true, even on a cancelled callback.
+ //
+ // This function is thread-safe.
+ bool MaybeValid() const;
+
+ // Returns the Callback into an uninitialized state.
+ void Reset();
+
+ protected:
+ friend class FinallyExecutorCommon;
+ friend class ThenAndCatchExecutorCommon;
+
+ template <typename ReturnType>
+ friend class PostTaskExecutor;
+
+ using InvokeFuncStorage = BindStateBase::InvokeFuncStorage;
+
+ // Returns true if this callback equals |other|. |other| may be null.
+ bool EqualsInternal(const CallbackBase& other) const;
+
+ constexpr inline CallbackBase();
+
+ // Allow initializing of |bind_state_| via the constructor to avoid default
+ // initialization of the scoped_refptr.
+ explicit inline CallbackBase(BindStateBase* bind_state);
+
+ InvokeFuncStorage polymorphic_invoke() const {
+ return bind_state_->polymorphic_invoke_;
+ }
+
+ // Force the destructor to be instantiated inside this translation unit so
+ // that our subclasses will not get inlined versions. Avoids more template
+ // bloat.
+ ~CallbackBase();
+
+ scoped_refptr<BindStateBase> bind_state_;
+};
+
+constexpr CallbackBase::CallbackBase() = default;
+CallbackBase::CallbackBase(CallbackBase&&) noexcept = default;
+CallbackBase::CallbackBase(BindStateBase* bind_state)
+ : bind_state_(AdoptRef(bind_state)) {}
+
+// CallbackBase<Copyable> is a direct base class of Copyable Callbacks.
+class BASE_EXPORT CallbackBaseCopyable : public CallbackBase {
+ public:
+ CallbackBaseCopyable(const CallbackBaseCopyable& c);
+ CallbackBaseCopyable(CallbackBaseCopyable&& c) noexcept = default;
+ CallbackBaseCopyable& operator=(const CallbackBaseCopyable& c);
+ CallbackBaseCopyable& operator=(CallbackBaseCopyable&& c) noexcept;
+
+ protected:
+ constexpr CallbackBaseCopyable() = default;
+ explicit CallbackBaseCopyable(BindStateBase* bind_state)
+ : CallbackBase(bind_state) {}
+ ~CallbackBaseCopyable() = default;
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_CALLBACK_INTERNAL_H_
diff --git a/security/sandbox/chromium/base/compiler_specific.h b/security/sandbox/chromium/base/compiler_specific.h
new file mode 100644
index 0000000000..5f931c8704
--- /dev/null
+++ b/security/sandbox/chromium/base/compiler_specific.h
@@ -0,0 +1,298 @@
+// 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_COMPILER_SPECIFIC_H_
+#define BASE_COMPILER_SPECIFIC_H_
+
+#include "build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+
+#if !defined(__clang__)
+#error "Only clang-cl is supported on Windows, see https://crbug.com/988071"
+#endif
+
+// Macros for suppressing and disabling warnings on MSVC.
+//
+// Warning numbers are enumerated at:
+// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
+//
+// The warning pragma:
+// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
+//
+// Using __pragma instead of #pragma inside macros:
+// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
+
+// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
+// The warning remains disabled until popped by MSVC_POP_WARNING.
+#define MSVC_PUSH_DISABLE_WARNING(n) \
+ __pragma(warning(push)) __pragma(warning(disable : n))
+
+// Pop effects of innermost MSVC_PUSH_* macro.
+#define MSVC_POP_WARNING() __pragma(warning(pop))
+
+#else // Not MSVC
+
+#define MSVC_PUSH_DISABLE_WARNING(n)
+#define MSVC_POP_WARNING()
+#define MSVC_DISABLE_OPTIMIZE()
+#define MSVC_ENABLE_OPTIMIZE()
+
+#endif // COMPILER_MSVC
+
+// These macros can be helpful when investigating compiler bugs or when
+// investigating issues in local optimized builds, by temporarily disabling
+// optimizations for a single function or file. These macros should never be
+// used to permanently work around compiler bugs or other mysteries, and should
+// not be used in landed changes.
+#if !defined(OFFICIAL_BUILD)
+#if defined(__clang__)
+#define DISABLE_OPTIMIZE() __pragma(clang optimize off)
+#define ENABLE_OPTIMIZE() __pragma(clang optimize on)
+#elif defined(COMPILER_MSVC)
+#define DISABLE_OPTIMIZE() __pragma(optimize("", off))
+#define ENABLE_OPTIMIZE() __pragma(optimize("", on))
+#else
+// These macros are not currently available for other compiler options.
+#endif
+// These macros are not available in official builds.
+#endif // !defined(OFFICIAL_BUILD)
+
+// 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.)
+// Use like:
+// int x = ...;
+// ALLOW_UNUSED_LOCAL(x);
+#define ALLOW_UNUSED_LOCAL(x) (void)x
+
+// 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) || defined(__clang__)
+#define ALLOW_UNUSED_TYPE __attribute__((unused))
+#else
+#define ALLOW_UNUSED_TYPE
+#endif
+
+// Annotate a function indicating it should not be inlined.
+// Use like:
+// NOINLINE void DoStuff() { ... }
+#if defined(COMPILER_GCC)
+#define NOINLINE __attribute__((noinline))
+#elif defined(COMPILER_MSVC)
+#define NOINLINE __declspec(noinline)
+#else
+#define NOINLINE
+#endif
+
+#if defined(COMPILER_GCC) && defined(NDEBUG)
+#define ALWAYS_INLINE inline __attribute__((__always_inline__))
+#elif defined(COMPILER_MSVC) && defined(NDEBUG)
+#define ALWAYS_INLINE __forceinline
+#else
+#define ALWAYS_INLINE inline
+#endif
+
+// Specify memory alignment for structs, classes, etc.
+// Use like:
+// class ALIGNAS(16) MyClass { ... }
+// ALIGNAS(16) int array[4];
+//
+// In most places you can use the C++11 keyword "alignas", which is preferred.
+//
+// But compilers have trouble mixing __attribute__((...)) syntax with
+// alignas(...) syntax.
+//
+// Doesn't work in clang or gcc:
+// struct alignas(16) __attribute__((packed)) S { char c; };
+// Works in clang but not gcc:
+// struct __attribute__((packed)) alignas(16) S2 { char c; };
+// Works in clang and gcc:
+// struct alignas(16) S3 { char c; } __attribute__((packed));
+//
+// There are also some attributes that must be specified *before* a class
+// definition: visibility (used for exporting functions/classes) is one of
+// these attributes. This means that it is not possible to use alignas() with a
+// class that is marked as exported.
+#if defined(COMPILER_MSVC)
+#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#elif defined(COMPILER_GCC)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#endif
+
+// 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 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
+#endif
+
+// Tell the compiler a function is using a printf-style format string.
+// |format_param| is the one-based index of the format string parameter;
+// |dots_param| is the one-based index of the "..." parameter.
+// For v*printf functions (which take a va_list), pass 0 for dots_param.
+// (This is undocumented but matches what the system C headers do.)
+// For member functions, the implicit this parameter counts as index 1.
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define PRINTF_FORMAT(format_param, dots_param) \
+ __attribute__((format(printf, format_param, dots_param)))
+#else
+#define PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+// WPRINTF_FORMAT is the same, but for wide format strings.
+// This doesn't appear to yet be implemented in any compiler.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
+#define WPRINTF_FORMAT(format_param, dots_param)
+// If available, it would look like:
+// __attribute__((format(wprintf, format_param, dots_param)))
+
+// Sanitizers annotations.
+#if defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+#define NO_SANITIZE(what) __attribute__((no_sanitize(what)))
+#endif
+#endif
+#if !defined(NO_SANITIZE)
+#define NO_SANITIZE(what)
+#endif
+
+// MemorySanitizer annotations.
+#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
+#include <sanitizer/msan_interface.h>
+
+// 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, 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, size)
+#define MSAN_CHECK_MEM_IS_INITIALIZED(p, size)
+#endif // MEMORY_SANITIZER
+
+// DISABLE_CFI_PERF -- Disable Control Flow Integrity for perf reasons.
+#if !defined(DISABLE_CFI_PERF)
+#if defined(__clang__) && defined(OFFICIAL_BUILD)
+#define DISABLE_CFI_PERF __attribute__((no_sanitize("cfi")))
+#else
+#define DISABLE_CFI_PERF
+#endif
+#endif
+
+// Macro useful for writing cross-platform function pointers.
+#if !defined(CDECL)
+#if defined(OS_WIN)
+#define CDECL __cdecl
+#else // defined(OS_WIN)
+#define CDECL
+#endif // defined(OS_WIN)
+#endif // !defined(CDECL)
+
+// Macro for hinting that an expression is likely to be false.
+#if !defined(UNLIKELY)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif // defined(COMPILER_GCC)
+#endif // !defined(UNLIKELY)
+
+#if !defined(LIKELY)
+#if defined(COMPILER_GCC) || defined(__clang__)
+#define LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define LIKELY(x) (x)
+#endif // defined(COMPILER_GCC)
+#endif // !defined(LIKELY)
+
+// Compiler feature-detection.
+// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension
+#if defined(__has_feature)
+#define HAS_FEATURE(FEATURE) __has_feature(FEATURE)
+#else
+#define HAS_FEATURE(FEATURE) 0
+#endif
+
+// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
+#if defined(__clang__)
+#define FALLTHROUGH [[clang::fallthrough]]
+#else
+#define FALLTHROUGH
+#endif
+
+#if defined(COMPILER_GCC)
+#define PRETTY_FUNCTION __PRETTY_FUNCTION__
+#elif defined(COMPILER_MSVC)
+#define PRETTY_FUNCTION __FUNCSIG__
+#else
+// See https://en.cppreference.com/w/c/language/function_definition#func
+#define PRETTY_FUNCTION __func__
+#endif
+
+#if !defined(CPU_ARM_NEON)
+#if defined(__arm__)
+#if !defined(__ARMEB__) && !defined(__ARM_EABI__) && !defined(__EABI__) && \
+ !defined(__VFP_FP__) && !defined(_WIN32_WCE) && !defined(ANDROID)
+#error Chromium does not support middle endian architecture
+#endif
+#if defined(__ARM_NEON__)
+#define CPU_ARM_NEON 1
+#endif
+#endif // defined(__arm__)
+#endif // !defined(CPU_ARM_NEON)
+
+#if !defined(HAVE_MIPS_MSA_INTRINSICS)
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+#define HAVE_MIPS_MSA_INTRINSICS 1
+#endif
+#endif
+
+#if defined(__clang__) && __has_attribute(uninitialized)
+// Attribute "uninitialized" disables -ftrivial-auto-var-init=pattern for
+// the specified variable.
+// Library-wide alternative is
+// 'configs -= [ "//build/config/compiler:default_init_stack_vars" ]' in .gn
+// file.
+//
+// See "init_stack_vars" in build/config/compiler/BUILD.gn and
+// http://crbug.com/977230
+// "init_stack_vars" is enabled for non-official builds and we hope to enable it
+// in official build in 2020 as well. The flag writes fixed pattern into
+// uninitialized parts of all local variables. In rare cases such initialization
+// is undesirable and attribute can be used:
+// 1. Degraded performance
+// In most cases compiler is able to remove additional stores. E.g. if memory is
+// never accessed or properly initialized later. Preserved stores mostly will
+// not affect program performance. However if compiler failed on some
+// performance critical code we can get a visible regression in a benchmark.
+// 2. memset, memcpy calls
+// Compiler may replaces some memory writes with memset or memcpy calls. This is
+// not -ftrivial-auto-var-init specific, but it can happen more likely with the
+// flag. It can be a problem if code is not linked with C run-time library.
+//
+// Note: The flag is security risk mitigation feature. So in future the
+// attribute uses should be avoided when possible. However to enable this
+// mitigation on the most of the code we need to be less strict now and minimize
+// number of exceptions later. So if in doubt feel free to use attribute, but
+// please document the problem for someone who is going to cleanup it later.
+// E.g. platform, bot, benchmark or test name in patch description or next to
+// the attribute.
+#define STACK_UNINITIALIZED __attribute__((uninitialized))
+#else
+#define STACK_UNINITIALIZED
+#endif
+
+#endif // BASE_COMPILER_SPECIFIC_H_
diff --git a/security/sandbox/chromium/base/containers/adapters.h b/security/sandbox/chromium/base/containers/adapters.h
new file mode 100644
index 0000000000..ec33481752
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/adapters.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_CONTAINERS_ADAPTERS_H_
+#define BASE_CONTAINERS_ADAPTERS_H_
+
+#include <stddef.h>
+
+#include <iterator>
+#include <utility>
+
+#include "base/macros.h"
+
+namespace base {
+
+namespace internal {
+
+// Internal adapter class for implementing base::Reversed.
+template <typename T>
+class ReversedAdapter {
+ public:
+ using Iterator = decltype(std::rbegin(std::declval<T&>()));
+
+ explicit ReversedAdapter(T& t) : t_(t) {}
+ ReversedAdapter(const ReversedAdapter& ra) : t_(ra.t_) {}
+
+ Iterator begin() const { return std::rbegin(t_); }
+ Iterator end() const { return std::rend(t_); }
+
+ private:
+ T& t_;
+
+ DISALLOW_ASSIGN(ReversedAdapter);
+};
+
+} // namespace internal
+
+// Reversed returns a container adapter usable in a range-based "for" statement
+// for iterating a reversible container in reverse order.
+//
+// Example:
+//
+// std::vector<int> v = ...;
+// for (int i : base::Reversed(v)) {
+// // iterates through v from back to front
+// }
+template <typename T>
+internal::ReversedAdapter<T> Reversed(T& t) {
+ return internal::ReversedAdapter<T>(t);
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_ADAPTERS_H_
diff --git a/security/sandbox/chromium/base/containers/buffer_iterator.h b/security/sandbox/chromium/base/containers/buffer_iterator.h
new file mode 100644
index 0000000000..a4fd670190
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/buffer_iterator.h
@@ -0,0 +1,145 @@
+// Copyright 2019 The Chromium Authors. 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_CONTAINERS_BUFFER_ITERATOR_H_
+#define BASE_CONTAINERS_BUFFER_ITERATOR_H_
+
+#include <type_traits>
+
+#include "base/bit_cast.h"
+#include "base/containers/span.h"
+#include "base/numerics/checked_math.h"
+
+namespace base {
+
+// BufferIterator is a bounds-checked container utility to access variable-
+// length, heterogeneous structures contained within a buffer. If the data are
+// homogeneous, use base::span<> instead.
+//
+// After being created with a weakly-owned buffer, BufferIterator returns
+// pointers to structured data within the buffer. After each method call that
+// returns data in the buffer, the iterator position is advanced by the byte
+// size of the object (or span of objects) returned. If there are not enough
+// bytes remaining in the buffer to return the requested object(s), a nullptr
+// or empty span is returned.
+//
+// This class is similar to base::Pickle, which should be preferred for
+// serializing to disk. Pickle versions its header and does not support writing
+// structures, which are problematic for serialization due to struct padding and
+// version shear concerns.
+//
+// Example usage:
+//
+// std::vector<uint8_t> buffer(4096);
+// if (!ReadSomeData(&buffer, buffer.size())) {
+// LOG(ERROR) << "Failed to read data.";
+// return false;
+// }
+//
+// BufferIterator<uint8_t> iterator(buffer);
+// uint32_t* num_items = iterator.Object<uint32_t>();
+// if (!num_items) {
+// LOG(ERROR) << "No num_items field."
+// return false;
+// }
+//
+// base::span<const item_struct> items =
+// iterator.Span<item_struct>(*num_items);
+// if (items.size() != *num_items) {
+// LOG(ERROR) << "Not enough items.";
+// return false;
+// }
+//
+// // ... validate the objects in |items|.
+template <typename B>
+class BufferIterator {
+ public:
+ static_assert(std::is_same<std::remove_const_t<B>, char>::value ||
+ std::is_same<std::remove_const_t<B>, unsigned char>::value,
+ "Underlying buffer type must be char-type.");
+
+ BufferIterator() {}
+ BufferIterator(B* data, size_t size)
+ : BufferIterator(make_span(data, size)) {}
+ explicit BufferIterator(span<B> buffer)
+ : buffer_(buffer), remaining_(buffer) {}
+ ~BufferIterator() {}
+
+ // Returns a pointer to a mutable structure T in the buffer at the current
+ // position. On success, the iterator position is advanced by sizeof(T). If
+ // there are not sizeof(T) bytes remaining in the buffer, returns nullptr.
+ template <typename T,
+ typename =
+ typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+ T* MutableObject() {
+ size_t size = sizeof(T);
+ size_t next_position;
+ if (!CheckAdd(position(), size).AssignIfValid(&next_position))
+ return nullptr;
+ if (next_position > total_size())
+ return nullptr;
+ T* t = bit_cast<T*>(remaining_.data());
+ remaining_ = remaining_.subspan(size);
+ return t;
+ }
+
+ // Returns a const pointer to an object of type T in the buffer at the current
+ // position.
+ template <typename T,
+ typename =
+ typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+ const T* Object() {
+ return MutableObject<const T>();
+ }
+
+ // Returns a span of |count| T objects in the buffer at the current position.
+ // On success, the iterator position is advanced by |sizeof(T) * count|. If
+ // there are not enough bytes remaining in the buffer to fulfill the request,
+ // returns an empty span.
+ template <typename T,
+ typename =
+ typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+ span<T> MutableSpan(size_t count) {
+ size_t size;
+ if (!CheckMul(sizeof(T), count).AssignIfValid(&size))
+ return span<T>();
+ size_t next_position;
+ if (!CheckAdd(position(), size).AssignIfValid(&next_position))
+ return span<T>();
+ if (next_position > total_size())
+ return span<T>();
+ auto result = span<T>(bit_cast<T*>(remaining_.data()), count);
+ remaining_ = remaining_.subspan(size);
+ return result;
+ }
+
+ // Returns a span to |count| const objects of type T in the buffer at the
+ // current position.
+ template <typename T,
+ typename =
+ typename std::enable_if_t<std::is_trivially_copyable<T>::value>>
+ span<const T> Span(size_t count) {
+ return MutableSpan<const T>(count);
+ }
+
+ // Resets the iterator position to the absolute offset |to|.
+ void Seek(size_t to) { remaining_ = buffer_.subspan(to); }
+
+ // Returns the total size of the underlying buffer.
+ size_t total_size() { return buffer_.size(); }
+
+ // Returns the current position in the buffer.
+ size_t position() { return buffer_.size_bytes() - remaining_.size_bytes(); }
+
+ private:
+ // The original buffer that the iterator was constructed with.
+ const span<B> buffer_;
+ // A subspan of |buffer_| containing the remaining bytes to iterate over.
+ span<B> remaining_;
+ // Copy and assign allowed.
+};
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_BUFFER_ITERATOR_H_
diff --git a/security/sandbox/chromium/base/containers/checked_iterators.h b/security/sandbox/chromium/base/containers/checked_iterators.h
new file mode 100644
index 0000000000..cdfd2909d4
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/checked_iterators.h
@@ -0,0 +1,205 @@
+// Copyright 2018 The Chromium Authors. 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_CONTAINERS_CHECKED_ITERATORS_H_
+#define BASE_CONTAINERS_CHECKED_ITERATORS_H_
+
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#include "base/containers/util.h"
+#include "base/logging.h"
+
+namespace base {
+
+template <typename T>
+class CheckedContiguousIterator {
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = std::remove_cv_t<T>;
+ using pointer = T*;
+ using reference = T&;
+ using iterator_category = std::random_access_iterator_tag;
+
+ // Required for converting constructor below.
+ template <typename U>
+ friend class CheckedContiguousIterator;
+
+ constexpr CheckedContiguousIterator() = default;
+ constexpr CheckedContiguousIterator(T* start, const T* end)
+ : CheckedContiguousIterator(start, start, end) {}
+ constexpr CheckedContiguousIterator(const T* start, T* current, const T* end)
+ : start_(start), current_(current), end_(end) {
+ CHECK_LE(start, current);
+ CHECK_LE(current, end);
+ }
+ constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
+ default;
+
+ // Converting constructor allowing conversions like CCI<T> to CCI<const T>,
+ // but disallowing CCI<const T> to CCI<T> or CCI<Derived> to CCI<Base>, which
+ // are unsafe. Furthermore, this is the same condition as used by the
+ // converting constructors of std::span<T> and std::unique_ptr<T[]>.
+ // See https://wg21.link/n4042 for details.
+ template <
+ typename U,
+ std::enable_if_t<std::is_convertible<U (*)[], T (*)[]>::value>* = nullptr>
+ constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other)
+ : start_(other.start_), current_(other.current_), end_(other.end_) {
+ // We explicitly don't delegate to the 3-argument constructor here. Its
+ // CHECKs would be redundant, since we expect |other| to maintain its own
+ // invariant. However, DCHECKs never hurt anybody. Presumably.
+ DCHECK_LE(other.start_, other.current_);
+ DCHECK_LE(other.current_, other.end_);
+ }
+
+ ~CheckedContiguousIterator() = default;
+
+ constexpr CheckedContiguousIterator& operator=(
+ const CheckedContiguousIterator& other) = default;
+
+ constexpr bool operator==(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ == other.current_;
+ }
+
+ constexpr bool operator!=(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ != other.current_;
+ }
+
+ constexpr bool operator<(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ < other.current_;
+ }
+
+ constexpr bool operator<=(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ <= other.current_;
+ }
+
+ constexpr bool operator>(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ > other.current_;
+ }
+
+ constexpr bool operator>=(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ >= other.current_;
+ }
+
+ constexpr CheckedContiguousIterator& operator++() {
+ CHECK_NE(current_, end_);
+ ++current_;
+ return *this;
+ }
+
+ constexpr CheckedContiguousIterator operator++(int) {
+ CheckedContiguousIterator old = *this;
+ ++*this;
+ return old;
+ }
+
+ constexpr CheckedContiguousIterator& operator--() {
+ CHECK_NE(current_, start_);
+ --current_;
+ return *this;
+ }
+
+ constexpr CheckedContiguousIterator operator--(int) {
+ CheckedContiguousIterator old = *this;
+ --*this;
+ return old;
+ }
+
+ constexpr CheckedContiguousIterator& operator+=(difference_type rhs) {
+ if (rhs > 0) {
+ CHECK_LE(rhs, end_ - current_);
+ } else {
+ CHECK_LE(-rhs, current_ - start_);
+ }
+ current_ += rhs;
+ return *this;
+ }
+
+ constexpr CheckedContiguousIterator operator+(difference_type rhs) const {
+ CheckedContiguousIterator it = *this;
+ it += rhs;
+ return it;
+ }
+
+ constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
+ if (rhs < 0) {
+ CHECK_LE(-rhs, end_ - current_);
+ } else {
+ CHECK_LE(rhs, current_ - start_);
+ }
+ current_ -= rhs;
+ return *this;
+ }
+
+ constexpr CheckedContiguousIterator operator-(difference_type rhs) const {
+ CheckedContiguousIterator it = *this;
+ it -= rhs;
+ return it;
+ }
+
+ constexpr friend difference_type operator-(
+ const CheckedContiguousIterator& lhs,
+ const CheckedContiguousIterator& rhs) {
+ CHECK_EQ(lhs.start_, rhs.start_);
+ CHECK_EQ(lhs.end_, rhs.end_);
+ return lhs.current_ - rhs.current_;
+ }
+
+ constexpr reference operator*() const {
+ CHECK_NE(current_, end_);
+ return *current_;
+ }
+
+ constexpr pointer operator->() const {
+ CHECK_NE(current_, end_);
+ return current_;
+ }
+
+ constexpr reference operator[](difference_type rhs) const {
+ CHECK_GE(rhs, 0);
+ CHECK_LT(rhs, end_ - current_);
+ return current_[rhs];
+ }
+
+ static bool IsRangeMoveSafe(const CheckedContiguousIterator& from_begin,
+ const CheckedContiguousIterator& from_end,
+ const CheckedContiguousIterator& to)
+ WARN_UNUSED_RESULT {
+ if (from_end < from_begin)
+ return false;
+ const auto from_begin_uintptr = get_uintptr(from_begin.current_);
+ const auto from_end_uintptr = get_uintptr(from_end.current_);
+ const auto to_begin_uintptr = get_uintptr(to.current_);
+ const auto to_end_uintptr =
+ get_uintptr((to + std::distance(from_begin, from_end)).current_);
+
+ return to_begin_uintptr >= from_end_uintptr ||
+ to_end_uintptr <= from_begin_uintptr;
+ }
+
+ private:
+ constexpr void CheckComparable(const CheckedContiguousIterator& other) const {
+ CHECK_EQ(start_, other.start_);
+ CHECK_EQ(end_, other.end_);
+ }
+
+ const T* start_ = nullptr;
+ T* current_ = nullptr;
+ const T* end_ = nullptr;
+};
+
+template <typename T>
+using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>;
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_
diff --git a/security/sandbox/chromium/base/containers/circular_deque.h b/security/sandbox/chromium/base/containers/circular_deque.h
new file mode 100644
index 0000000000..0d452b56be
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/circular_deque.h
@@ -0,0 +1,1112 @@
+// Copyright 2017 The Chromium Authors. 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_CONTAINERS_CIRCULAR_DEQUE_H_
+#define BASE_CONTAINERS_CIRCULAR_DEQUE_H_
+
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "base/containers/vector_buffer.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/template_util.h"
+
+// base::circular_deque is similar to std::deque. Unlike std::deque, the
+// storage is provided in a flat circular buffer conceptually similar to a
+// vector. The beginning and end will wrap around as necessary so that
+// pushes and pops will be constant time as long as a capacity expansion is
+// not required.
+//
+// The API should be identical to std::deque with the following differences:
+//
+// - ITERATORS ARE NOT STABLE. Mutating the container will invalidate all
+// iterators.
+//
+// - Insertions may resize the vector and so are not constant time (std::deque
+// guarantees constant time for insertions at the ends).
+//
+// - Container-wide comparisons are not implemented. If you want to compare
+// two containers, use an algorithm so the expensive iteration is explicit.
+//
+// If you want a similar container with only a queue API, use base::queue in
+// base/containers/queue.h.
+//
+// Constructors:
+// circular_deque();
+// circular_deque(size_t count);
+// circular_deque(size_t count, const T& value);
+// circular_deque(InputIterator first, InputIterator last);
+// circular_deque(const circular_deque&);
+// circular_deque(circular_deque&&);
+// circular_deque(std::initializer_list<value_type>);
+//
+// Assignment functions:
+// circular_deque& operator=(const circular_deque&);
+// circular_deque& operator=(circular_deque&&);
+// circular_deque& operator=(std::initializer_list<T>);
+// void assign(size_t count, const T& value);
+// void assign(InputIterator first, InputIterator last);
+// void assign(std::initializer_list<T> value);
+//
+// Random accessors:
+// T& at(size_t);
+// const T& at(size_t) const;
+// T& operator[](size_t);
+// const T& operator[](size_t) const;
+//
+// End accessors:
+// T& front();
+// const T& front() const;
+// T& back();
+// const T& back() const;
+//
+// Iterator functions:
+// iterator begin();
+// const_iterator begin() const;
+// const_iterator cbegin() const;
+// iterator end();
+// const_iterator end() const;
+// const_iterator cend() const;
+// reverse_iterator rbegin();
+// const_reverse_iterator rbegin() const;
+// const_reverse_iterator crbegin() const;
+// reverse_iterator rend();
+// const_reverse_iterator rend() const;
+// const_reverse_iterator crend() const;
+//
+// Memory management:
+// void reserve(size_t); // SEE IMPLEMENTATION FOR SOME GOTCHAS.
+// size_t capacity() const;
+// void shrink_to_fit();
+//
+// Size management:
+// void clear();
+// bool empty() const;
+// size_t size() const;
+// void resize(size_t);
+// void resize(size_t count, const T& value);
+//
+// Positional insert and erase:
+// void insert(const_iterator pos, size_type count, const T& value);
+// void insert(const_iterator pos,
+// InputIterator first, InputIterator last);
+// iterator insert(const_iterator pos, const T& value);
+// iterator insert(const_iterator pos, T&& value);
+// iterator emplace(const_iterator pos, Args&&... args);
+// iterator erase(const_iterator pos);
+// iterator erase(const_iterator first, const_iterator last);
+//
+// End insert and erase:
+// void push_front(const T&);
+// void push_front(T&&);
+// void push_back(const T&);
+// void push_back(T&&);
+// T& emplace_front(Args&&...);
+// T& emplace_back(Args&&...);
+// void pop_front();
+// void pop_back();
+//
+// General:
+// void swap(circular_deque&);
+
+namespace base {
+
+template <class T>
+class circular_deque;
+
+namespace internal {
+
+// Start allocating nonempty buffers with this many entries. This is the
+// external capacity so the internal buffer will be one larger (= 4) which is
+// more even for the allocator. See the descriptions of internal vs. external
+// capacity on the comment above the buffer_ variable below.
+constexpr size_t kCircularBufferInitialCapacity = 3;
+
+template <typename T>
+class circular_deque_const_iterator {
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = T;
+ using pointer = const T*;
+ using reference = const T&;
+ using iterator_category = std::random_access_iterator_tag;
+
+ circular_deque_const_iterator() : parent_deque_(nullptr), index_(0) {
+#if DCHECK_IS_ON()
+ created_generation_ = 0;
+#endif // DCHECK_IS_ON()
+ }
+
+ // Dereferencing.
+ const T& operator*() const {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndex(index_);
+ return parent_deque_->buffer_[index_];
+ }
+ const T* operator->() const {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndex(index_);
+ return &parent_deque_->buffer_[index_];
+ }
+ const value_type& operator[](difference_type i) const { return *(*this + i); }
+
+ // Increment and decrement.
+ circular_deque_const_iterator& operator++() {
+ Increment();
+ return *this;
+ }
+ circular_deque_const_iterator operator++(int) {
+ circular_deque_const_iterator ret = *this;
+ Increment();
+ return ret;
+ }
+ circular_deque_const_iterator& operator--() {
+ Decrement();
+ return *this;
+ }
+ circular_deque_const_iterator operator--(int) {
+ circular_deque_const_iterator ret = *this;
+ Decrement();
+ return ret;
+ }
+
+ // Random access mutation.
+ friend circular_deque_const_iterator operator+(
+ const circular_deque_const_iterator& iter,
+ difference_type offset) {
+ circular_deque_const_iterator ret = iter;
+ ret.Add(offset);
+ return ret;
+ }
+ circular_deque_const_iterator& operator+=(difference_type offset) {
+ Add(offset);
+ return *this;
+ }
+ friend circular_deque_const_iterator operator-(
+ const circular_deque_const_iterator& iter,
+ difference_type offset) {
+ circular_deque_const_iterator ret = iter;
+ ret.Add(-offset);
+ return ret;
+ }
+ circular_deque_const_iterator& operator-=(difference_type offset) {
+ Add(-offset);
+ return *this;
+ }
+
+ friend std::ptrdiff_t operator-(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.OffsetFromBegin() - rhs.OffsetFromBegin();
+ }
+
+ // Comparisons.
+ friend bool operator==(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.index_ == rhs.index_;
+ }
+ friend bool operator!=(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ return !(lhs == rhs);
+ }
+ friend bool operator<(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.OffsetFromBegin() < rhs.OffsetFromBegin();
+ }
+ friend bool operator<=(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ return !(lhs > rhs);
+ }
+ friend bool operator>(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ lhs.CheckComparable(rhs);
+ return lhs.OffsetFromBegin() > rhs.OffsetFromBegin();
+ }
+ friend bool operator>=(const circular_deque_const_iterator& lhs,
+ const circular_deque_const_iterator& rhs) {
+ return !(lhs < rhs);
+ }
+
+ protected:
+ friend class circular_deque<T>;
+
+ circular_deque_const_iterator(const circular_deque<T>* parent, size_t index)
+ : parent_deque_(parent), index_(index) {
+#if DCHECK_IS_ON()
+ created_generation_ = parent->generation_;
+#endif // DCHECK_IS_ON()
+ }
+
+ // Returns the offset from the beginning index of the buffer to the current
+ // item.
+ size_t OffsetFromBegin() const {
+ if (index_ >= parent_deque_->begin_)
+ return index_ - parent_deque_->begin_; // On the same side as begin.
+ return parent_deque_->buffer_.capacity() - parent_deque_->begin_ + index_;
+ }
+
+ // Most uses will be ++ and -- so use a simplified implementation.
+ void Increment() {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndex(index_);
+ index_++;
+ if (index_ == parent_deque_->buffer_.capacity())
+ index_ = 0;
+ }
+ void Decrement() {
+ CheckUnstableUsage();
+ parent_deque_->CheckValidIndexOrEnd(index_);
+ if (index_ == 0)
+ index_ = parent_deque_->buffer_.capacity() - 1;
+ else
+ index_--;
+ }
+ void Add(difference_type delta) {
+ CheckUnstableUsage();
+#if DCHECK_IS_ON()
+ if (delta <= 0)
+ parent_deque_->CheckValidIndexOrEnd(index_);
+ else
+ parent_deque_->CheckValidIndex(index_);
+#endif
+ // It should be valid to add 0 to any iterator, even if the container is
+ // empty and the iterator points to end(). The modulo below will divide
+ // by 0 if the buffer capacity is empty, so it's important to check for
+ // this case explicitly.
+ if (delta == 0)
+ return;
+
+ difference_type new_offset = OffsetFromBegin() + delta;
+ DCHECK(new_offset >= 0 &&
+ new_offset <= static_cast<difference_type>(parent_deque_->size()));
+ index_ = (new_offset + parent_deque_->begin_) %
+ parent_deque_->buffer_.capacity();
+ }
+
+#if DCHECK_IS_ON()
+ void CheckUnstableUsage() const {
+ DCHECK(parent_deque_);
+ // Since circular_deque doesn't guarantee stability, any attempt to
+ // dereference this iterator after a mutation (i.e. the generation doesn't
+ // match the original) in the container is illegal.
+ DCHECK_EQ(created_generation_, parent_deque_->generation_)
+ << "circular_deque iterator dereferenced after mutation.";
+ }
+ void CheckComparable(const circular_deque_const_iterator& other) const {
+ DCHECK_EQ(parent_deque_, other.parent_deque_);
+ // Since circular_deque doesn't guarantee stability, two iterators that
+ // are compared must have been generated without mutating the container.
+ // If this fires, the container was mutated between generating the two
+ // iterators being compared.
+ DCHECK_EQ(created_generation_, other.created_generation_);
+ }
+#else
+ inline void CheckUnstableUsage() const {}
+ inline void CheckComparable(const circular_deque_const_iterator&) const {}
+#endif // DCHECK_IS_ON()
+
+ const circular_deque<T>* parent_deque_;
+ size_t index_;
+
+#if DCHECK_IS_ON()
+ // The generation of the parent deque when this iterator was created. The
+ // container will update the generation for every modification so we can
+ // test if the container was modified by comparing them.
+ uint64_t created_generation_;
+#endif // DCHECK_IS_ON()
+};
+
+template <typename T>
+class circular_deque_iterator : public circular_deque_const_iterator<T> {
+ using base = circular_deque_const_iterator<T>;
+
+ public:
+ friend class circular_deque<T>;
+
+ using difference_type = std::ptrdiff_t;
+ using value_type = T;
+ using pointer = T*;
+ using reference = T&;
+ using iterator_category = std::random_access_iterator_tag;
+
+ // Expose the base class' constructor.
+ circular_deque_iterator() : circular_deque_const_iterator<T>() {}
+
+ // Dereferencing.
+ T& operator*() const { return const_cast<T&>(base::operator*()); }
+ T* operator->() const { return const_cast<T*>(base::operator->()); }
+ T& operator[](difference_type i) {
+ return const_cast<T&>(base::operator[](i));
+ }
+
+ // Random access mutation.
+ friend circular_deque_iterator operator+(const circular_deque_iterator& iter,
+ difference_type offset) {
+ circular_deque_iterator ret = iter;
+ ret.Add(offset);
+ return ret;
+ }
+ circular_deque_iterator& operator+=(difference_type offset) {
+ base::Add(offset);
+ return *this;
+ }
+ friend circular_deque_iterator operator-(const circular_deque_iterator& iter,
+ difference_type offset) {
+ circular_deque_iterator ret = iter;
+ ret.Add(-offset);
+ return ret;
+ }
+ circular_deque_iterator& operator-=(difference_type offset) {
+ base::Add(-offset);
+ return *this;
+ }
+
+ // Increment and decrement.
+ circular_deque_iterator& operator++() {
+ base::Increment();
+ return *this;
+ }
+ circular_deque_iterator operator++(int) {
+ circular_deque_iterator ret = *this;
+ base::Increment();
+ return ret;
+ }
+ circular_deque_iterator& operator--() {
+ base::Decrement();
+ return *this;
+ }
+ circular_deque_iterator operator--(int) {
+ circular_deque_iterator ret = *this;
+ base::Decrement();
+ return ret;
+ }
+
+ private:
+ circular_deque_iterator(const circular_deque<T>* parent, size_t index)
+ : circular_deque_const_iterator<T>(parent, index) {}
+};
+
+} // namespace internal
+
+template <typename T>
+class circular_deque {
+ private:
+ using VectorBuffer = internal::VectorBuffer<T>;
+
+ public:
+ using value_type = T;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = value_type*;
+ using const_pointer = const value_type*;
+
+ using iterator = internal::circular_deque_iterator<T>;
+ using const_iterator = internal::circular_deque_const_iterator<T>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ // ---------------------------------------------------------------------------
+ // Constructor
+
+ constexpr circular_deque() = default;
+
+ // Constructs with |count| copies of |value| or default constructed version.
+ circular_deque(size_type count) { resize(count); }
+ circular_deque(size_type count, const T& value) { resize(count, value); }
+
+ // Range constructor.
+ template <class InputIterator>
+ circular_deque(InputIterator first, InputIterator last) {
+ assign(first, last);
+ }
+
+ // Copy/move.
+ circular_deque(const circular_deque& other) : buffer_(other.size() + 1) {
+ assign(other.begin(), other.end());
+ }
+ circular_deque(circular_deque&& other) noexcept
+ : buffer_(std::move(other.buffer_)),
+ begin_(other.begin_),
+ end_(other.end_) {
+ other.begin_ = 0;
+ other.end_ = 0;
+ }
+
+ circular_deque(std::initializer_list<value_type> init) { assign(init); }
+
+ ~circular_deque() { DestructRange(begin_, end_); }
+
+ // ---------------------------------------------------------------------------
+ // Assignments.
+ //
+ // All of these may invalidate iterators and references.
+
+ circular_deque& operator=(const circular_deque& other) {
+ if (&other == this)
+ return *this;
+
+ reserve(other.size());
+ assign(other.begin(), other.end());
+ return *this;
+ }
+ circular_deque& operator=(circular_deque&& other) noexcept {
+ if (&other == this)
+ return *this;
+
+ // We're about to overwrite the buffer, so don't free it in clear to
+ // avoid doing it twice.
+ ClearRetainCapacity();
+ buffer_ = std::move(other.buffer_);
+ begin_ = other.begin_;
+ end_ = other.end_;
+
+ other.begin_ = 0;
+ other.end_ = 0;
+
+ IncrementGeneration();
+ return *this;
+ }
+ circular_deque& operator=(std::initializer_list<value_type> ilist) {
+ reserve(ilist.size());
+ assign(std::begin(ilist), std::end(ilist));
+ return *this;
+ }
+
+ void assign(size_type count, const value_type& value) {
+ ClearRetainCapacity();
+ reserve(count);
+ for (size_t i = 0; i < count; i++)
+ emplace_back(value);
+ IncrementGeneration();
+ }
+
+ // This variant should be enabled only when InputIterator is an iterator.
+ template <typename InputIterator>
+ typename std::enable_if<::base::internal::is_iterator<InputIterator>::value,
+ void>::type
+ assign(InputIterator first, InputIterator last) {
+ // Possible future enhancement, dispatch on iterator tag type. For forward
+ // iterators we can use std::difference to preallocate the space required
+ // and only do one copy.
+ ClearRetainCapacity();
+ for (; first != last; ++first)
+ emplace_back(*first);
+ IncrementGeneration();
+ }
+
+ void assign(std::initializer_list<value_type> value) {
+ reserve(std::distance(value.begin(), value.end()));
+ assign(value.begin(), value.end());
+ }
+
+ // ---------------------------------------------------------------------------
+ // Accessors.
+ //
+ // Since this class assumes no exceptions, at() and operator[] are equivalent.
+
+ const value_type& at(size_type i) const {
+ DCHECK(i < size());
+ size_t right_size = buffer_.capacity() - begin_;
+ if (begin_ <= end_ || i < right_size)
+ return buffer_[begin_ + i];
+ return buffer_[i - right_size];
+ }
+ value_type& at(size_type i) {
+ return const_cast<value_type&>(as_const(*this).at(i));
+ }
+
+ value_type& operator[](size_type i) {
+ return const_cast<value_type&>(as_const(*this)[i]);
+ }
+
+ const value_type& operator[](size_type i) const { return at(i); }
+
+ value_type& front() {
+ DCHECK(!empty());
+ return buffer_[begin_];
+ }
+ const value_type& front() const {
+ DCHECK(!empty());
+ return buffer_[begin_];
+ }
+
+ value_type& back() {
+ DCHECK(!empty());
+ return *(--end());
+ }
+ const value_type& back() const {
+ DCHECK(!empty());
+ return *(--end());
+ }
+
+ // ---------------------------------------------------------------------------
+ // Iterators.
+
+ iterator begin() { return iterator(this, begin_); }
+ const_iterator begin() const { return const_iterator(this, begin_); }
+ const_iterator cbegin() const { return const_iterator(this, begin_); }
+
+ iterator end() { return iterator(this, end_); }
+ const_iterator end() const { return const_iterator(this, end_); }
+ const_iterator cend() const { return const_iterator(this, end_); }
+
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(end());
+ }
+ const_reverse_iterator crbegin() const { return rbegin(); }
+
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(begin());
+ }
+ const_reverse_iterator crend() const { return rend(); }
+
+ // ---------------------------------------------------------------------------
+ // Memory management.
+
+ // IMPORTANT NOTE ON reserve(...): This class implements auto-shrinking of
+ // the buffer when elements are deleted and there is "too much" wasted space.
+ // So if you call reserve() with a large size in anticipation of pushing many
+ // elements, but pop an element before the queue is full, the capacity you
+ // reserved may be lost.
+ //
+ // As a result, it's only worthwhile to call reserve() when you're adding
+ // many things at once with no intermediate operations.
+ void reserve(size_type new_capacity) {
+ if (new_capacity > capacity())
+ SetCapacityTo(new_capacity);
+ }
+
+ size_type capacity() const {
+ // One item is wasted to indicate end().
+ return buffer_.capacity() == 0 ? 0 : buffer_.capacity() - 1;
+ }
+
+ void shrink_to_fit() {
+ if (empty()) {
+ // Optimize empty case to really delete everything if there was
+ // something.
+ if (buffer_.capacity())
+ buffer_ = VectorBuffer();
+ } else {
+ SetCapacityTo(size());
+ }
+ }
+
+ // ---------------------------------------------------------------------------
+ // Size management.
+
+ // This will additionally reset the capacity() to 0.
+ void clear() {
+ // This can't resize(0) because that requires a default constructor to
+ // compile, which not all contained classes may implement.
+ ClearRetainCapacity();
+ buffer_ = VectorBuffer();
+ }
+
+ bool empty() const { return begin_ == end_; }
+
+ size_type size() const {
+ if (begin_ <= end_)
+ return end_ - begin_;
+ return buffer_.capacity() - begin_ + end_;
+ }
+
+ // When reducing size, the elements are deleted from the end. When expanding
+ // size, elements are added to the end with |value| or the default
+ // constructed version. Even when using resize(count) to shrink, a default
+ // constructor is required for the code to compile, even though it will not
+ // be called.
+ //
+ // There are two versions rather than using a default value to avoid
+ // creating a temporary when shrinking (when it's not needed). Plus if
+ // the default constructor is desired when expanding usually just calling it
+ // for each element is faster than making a default-constructed temporary and
+ // copying it.
+ void resize(size_type count) {
+ // SEE BELOW VERSION if you change this. The code is mostly the same.
+ if (count > size()) {
+ // This could be slighly more efficient but expanding a queue with
+ // identical elements is unusual and the extra computations of emplacing
+ // one-by-one will typically be small relative to calling the constructor
+ // for every item.
+ ExpandCapacityIfNecessary(count - size());
+ while (size() < count)
+ emplace_back();
+ } else if (count < size()) {
+ size_t new_end = (begin_ + count) % buffer_.capacity();
+ DestructRange(new_end, end_);
+ end_ = new_end;
+
+ ShrinkCapacityIfNecessary();
+ }
+ IncrementGeneration();
+ }
+ void resize(size_type count, const value_type& value) {
+ // SEE ABOVE VERSION if you change this. The code is mostly the same.
+ if (count > size()) {
+ ExpandCapacityIfNecessary(count - size());
+ while (size() < count)
+ emplace_back(value);
+ } else if (count < size()) {
+ size_t new_end = (begin_ + count) % buffer_.capacity();
+ DestructRange(new_end, end_);
+ end_ = new_end;
+
+ ShrinkCapacityIfNecessary();
+ }
+ IncrementGeneration();
+ }
+
+ // ---------------------------------------------------------------------------
+ // Insert and erase.
+ //
+ // Insertion and deletion in the middle is O(n) and invalidates all existing
+ // iterators.
+ //
+ // The implementation of insert isn't optimized as much as it could be. If
+ // the insertion requires that the buffer be grown, it will first be grown
+ // and everything moved, and then the items will be inserted, potentially
+ // moving some items twice. This simplifies the implemntation substantially
+ // and means less generated templatized code. Since this is an uncommon
+ // operation for deques, and already relatively slow, it doesn't seem worth
+ // the benefit to optimize this.
+
+ void insert(const_iterator pos, size_type count, const T& value) {
+ ValidateIterator(pos);
+
+ // Optimize insert at the beginning.
+ if (pos == begin()) {
+ ExpandCapacityIfNecessary(count);
+ for (size_t i = 0; i < count; i++)
+ push_front(value);
+ return;
+ }
+
+ iterator insert_cur(this, pos.index_);
+ iterator insert_end;
+ MakeRoomFor(count, &insert_cur, &insert_end);
+ while (insert_cur < insert_end) {
+ new (&buffer_[insert_cur.index_]) T(value);
+ ++insert_cur;
+ }
+
+ IncrementGeneration();
+ }
+
+ // This enable_if keeps this call from getting confused with the (pos, count,
+ // value) version when value is an integer.
+ template <class InputIterator>
+ typename std::enable_if<::base::internal::is_iterator<InputIterator>::value,
+ void>::type
+ insert(const_iterator pos, InputIterator first, InputIterator last) {
+ ValidateIterator(pos);
+
+ size_t inserted_items = std::distance(first, last);
+ if (inserted_items == 0)
+ return; // Can divide by 0 when doing modulo below, so return early.
+
+ // Make a hole to copy the items into.
+ iterator insert_cur;
+ iterator insert_end;
+ if (pos == begin()) {
+ // Optimize insert at the beginning, nothing needs to be shifted and the
+ // hole is the |inserted_items| block immediately before |begin_|.
+ ExpandCapacityIfNecessary(inserted_items);
+ insert_end = begin();
+ begin_ =
+ (begin_ + buffer_.capacity() - inserted_items) % buffer_.capacity();
+ insert_cur = begin();
+ } else {
+ insert_cur = iterator(this, pos.index_);
+ MakeRoomFor(inserted_items, &insert_cur, &insert_end);
+ }
+
+ // Copy the items.
+ while (insert_cur < insert_end) {
+ new (&buffer_[insert_cur.index_]) T(*first);
+ ++insert_cur;
+ ++first;
+ }
+
+ IncrementGeneration();
+ }
+
+ // These all return an iterator to the inserted item. Existing iterators will
+ // be invalidated.
+ iterator insert(const_iterator pos, const T& value) {
+ return emplace(pos, value);
+ }
+ iterator insert(const_iterator pos, T&& value) {
+ return emplace(pos, std::move(value));
+ }
+ template <class... Args>
+ iterator emplace(const_iterator pos, Args&&... args) {
+ ValidateIterator(pos);
+
+ // Optimize insert at beginning which doesn't require shifting.
+ if (pos == cbegin()) {
+ emplace_front(std::forward<Args>(args)...);
+ return begin();
+ }
+
+ // Do this before we make the new iterators we return.
+ IncrementGeneration();
+
+ iterator insert_begin(this, pos.index_);
+ iterator insert_end;
+ MakeRoomFor(1, &insert_begin, &insert_end);
+ new (&buffer_[insert_begin.index_]) T(std::forward<Args>(args)...);
+
+ return insert_begin;
+ }
+
+ // Calling erase() won't automatically resize the buffer smaller like resize
+ // or the pop functions. Erase is slow and relatively uncommon, and for
+ // normal deque usage a pop will normally be done on a regular basis that
+ // will prevent excessive buffer usage over long periods of time. It's not
+ // worth having the extra code for every template instantiation of erase()
+ // to resize capacity downward to a new buffer.
+ iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
+ iterator erase(const_iterator first, const_iterator last) {
+ ValidateIterator(first);
+ ValidateIterator(last);
+
+ IncrementGeneration();
+
+ // First, call the destructor on the deleted items.
+ if (first.index_ == last.index_) {
+ // Nothing deleted. Need to return early to avoid falling through to
+ // moving items on top of themselves.
+ return iterator(this, first.index_);
+ } else if (first.index_ < last.index_) {
+ // Contiguous range.
+ buffer_.DestructRange(&buffer_[first.index_], &buffer_[last.index_]);
+ } else {
+ // Deleted range wraps around.
+ buffer_.DestructRange(&buffer_[first.index_],
+ &buffer_[buffer_.capacity()]);
+ buffer_.DestructRange(&buffer_[0], &buffer_[last.index_]);
+ }
+
+ if (first.index_ == begin_) {
+ // This deletion is from the beginning. Nothing needs to be copied, only
+ // begin_ needs to be updated.
+ begin_ = last.index_;
+ return iterator(this, last.index_);
+ }
+
+ // In an erase operation, the shifted items all move logically to the left,
+ // so move them from left-to-right.
+ iterator move_src(this, last.index_);
+ iterator move_src_end = end();
+ iterator move_dest(this, first.index_);
+ for (; move_src < move_src_end; move_src++, move_dest++) {
+ buffer_.MoveRange(&buffer_[move_src.index_],
+ &buffer_[move_src.index_ + 1],
+ &buffer_[move_dest.index_]);
+ }
+
+ end_ = move_dest.index_;
+
+ // Since we did not reallocate and only changed things after the erase
+ // element(s), the input iterator's index points to the thing following the
+ // deletion.
+ return iterator(this, first.index_);
+ }
+
+ // ---------------------------------------------------------------------------
+ // Begin/end operations.
+
+ void push_front(const T& value) { emplace_front(value); }
+ void push_front(T&& value) { emplace_front(std::move(value)); }
+
+ void push_back(const T& value) { emplace_back(value); }
+ void push_back(T&& value) { emplace_back(std::move(value)); }
+
+ template <class... Args>
+ reference emplace_front(Args&&... args) {
+ ExpandCapacityIfNecessary(1);
+ if (begin_ == 0)
+ begin_ = buffer_.capacity() - 1;
+ else
+ begin_--;
+ IncrementGeneration();
+ new (&buffer_[begin_]) T(std::forward<Args>(args)...);
+ return front();
+ }
+
+ template <class... Args>
+ reference emplace_back(Args&&... args) {
+ ExpandCapacityIfNecessary(1);
+ new (&buffer_[end_]) T(std::forward<Args>(args)...);
+ if (end_ == buffer_.capacity() - 1)
+ end_ = 0;
+ else
+ end_++;
+ IncrementGeneration();
+ return back();
+ }
+
+ void pop_front() {
+ DCHECK(size());
+ buffer_.DestructRange(&buffer_[begin_], &buffer_[begin_ + 1]);
+ begin_++;
+ if (begin_ == buffer_.capacity())
+ begin_ = 0;
+
+ ShrinkCapacityIfNecessary();
+
+ // Technically popping will not invalidate any iterators since the
+ // underlying buffer will be stable. But in the future we may want to add a
+ // feature that resizes the buffer smaller if there is too much wasted
+ // space. This ensures we can make such a change safely.
+ IncrementGeneration();
+ }
+ void pop_back() {
+ DCHECK(size());
+ if (end_ == 0)
+ end_ = buffer_.capacity() - 1;
+ else
+ end_--;
+ buffer_.DestructRange(&buffer_[end_], &buffer_[end_ + 1]);
+
+ ShrinkCapacityIfNecessary();
+
+ // See pop_front comment about why this is here.
+ IncrementGeneration();
+ }
+
+ // ---------------------------------------------------------------------------
+ // General operations.
+
+ void swap(circular_deque& other) {
+ std::swap(buffer_, other.buffer_);
+ std::swap(begin_, other.begin_);
+ std::swap(end_, other.end_);
+ IncrementGeneration();
+ }
+
+ friend void swap(circular_deque& lhs, circular_deque& rhs) { lhs.swap(rhs); }
+
+ private:
+ friend internal::circular_deque_iterator<T>;
+ friend internal::circular_deque_const_iterator<T>;
+
+ // Moves the items in the given circular buffer to the current one. The
+ // source is moved from so will become invalid. The destination buffer must
+ // have already been allocated with enough size.
+ static void MoveBuffer(VectorBuffer& from_buf,
+ size_t from_begin,
+ size_t from_end,
+ VectorBuffer* to_buf,
+ size_t* to_begin,
+ size_t* to_end) {
+ size_t from_capacity = from_buf.capacity();
+
+ *to_begin = 0;
+ if (from_begin < from_end) {
+ // Contiguous.
+ from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_end],
+ to_buf->begin());
+ *to_end = from_end - from_begin;
+ } else if (from_begin > from_end) {
+ // Discontiguous, copy the right side to the beginning of the new buffer.
+ from_buf.MoveRange(&from_buf[from_begin], &from_buf[from_capacity],
+ to_buf->begin());
+ size_t right_size = from_capacity - from_begin;
+ // Append the left side.
+ from_buf.MoveRange(&from_buf[0], &from_buf[from_end],
+ &(*to_buf)[right_size]);
+ *to_end = right_size + from_end;
+ } else {
+ // No items.
+ *to_end = 0;
+ }
+ }
+
+ // Expands the buffer size. This assumes the size is larger than the
+ // number of elements in the vector (it won't call delete on anything).
+ void SetCapacityTo(size_t new_capacity) {
+ // Use the capacity + 1 as the internal buffer size to differentiate
+ // empty and full (see definition of buffer_ below).
+ VectorBuffer new_buffer(new_capacity + 1);
+ MoveBuffer(buffer_, begin_, end_, &new_buffer, &begin_, &end_);
+ buffer_ = std::move(new_buffer);
+ }
+ void ExpandCapacityIfNecessary(size_t additional_elts) {
+ size_t min_new_capacity = size() + additional_elts;
+ if (capacity() >= min_new_capacity)
+ return; // Already enough room.
+
+ min_new_capacity =
+ std::max(min_new_capacity, internal::kCircularBufferInitialCapacity);
+
+ // std::vector always grows by at least 50%. WTF::Deque grows by at least
+ // 25%. We expect queue workloads to generally stay at a similar size and
+ // grow less than a vector might, so use 25%.
+ size_t new_capacity =
+ std::max(min_new_capacity, capacity() + capacity() / 4);
+ SetCapacityTo(new_capacity);
+ }
+
+ void ShrinkCapacityIfNecessary() {
+ // Don't auto-shrink below this size.
+ if (capacity() <= internal::kCircularBufferInitialCapacity)
+ return;
+
+ // Shrink when 100% of the size() is wasted.
+ size_t sz = size();
+ size_t empty_spaces = capacity() - sz;
+ if (empty_spaces < sz)
+ return;
+
+ // Leave 1/4 the size as free capacity, not going below the initial
+ // capacity.
+ size_t new_capacity =
+ std::max(internal::kCircularBufferInitialCapacity, sz + sz / 4);
+ if (new_capacity < capacity()) {
+ // Count extra item to convert to internal capacity.
+ SetCapacityTo(new_capacity);
+ }
+ }
+
+ // Backend for clear() but does not resize the internal buffer.
+ void ClearRetainCapacity() {
+ // This can't resize(0) because that requires a default constructor to
+ // compile, which not all contained classes may implement.
+ DestructRange(begin_, end_);
+ begin_ = 0;
+ end_ = 0;
+ IncrementGeneration();
+ }
+
+ // Calls destructors for the given begin->end indices. The indices may wrap
+ // around. The buffer is not resized, and the begin_ and end_ members are
+ // not changed.
+ void DestructRange(size_t begin, size_t end) {
+ if (end == begin) {
+ return;
+ } else if (end > begin) {
+ buffer_.DestructRange(&buffer_[begin], &buffer_[end]);
+ } else {
+ buffer_.DestructRange(&buffer_[begin], &buffer_[buffer_.capacity()]);
+ buffer_.DestructRange(&buffer_[0], &buffer_[end]);
+ }
+ }
+
+ // Makes room for |count| items starting at |*insert_begin|. Since iterators
+ // are not stable across buffer resizes, |*insert_begin| will be updated to
+ // point to the beginning of the newly opened position in the new array (it's
+ // in/out), and the end of the newly opened position (it's out-only).
+ void MakeRoomFor(size_t count, iterator* insert_begin, iterator* insert_end) {
+ if (count == 0) {
+ *insert_end = *insert_begin;
+ return;
+ }
+
+ // The offset from the beginning will be stable across reallocations.
+ size_t begin_offset = insert_begin->OffsetFromBegin();
+ ExpandCapacityIfNecessary(count);
+
+ insert_begin->index_ = (begin_ + begin_offset) % buffer_.capacity();
+ *insert_end =
+ iterator(this, (insert_begin->index_ + count) % buffer_.capacity());
+
+ // Update the new end and prepare the iterators for copying.
+ iterator src = end();
+ end_ = (end_ + count) % buffer_.capacity();
+ iterator dest = end();
+
+ // Move the elements. This will always involve shifting logically to the
+ // right, so move in a right-to-left order.
+ while (true) {
+ if (src == *insert_begin)
+ break;
+ --src;
+ --dest;
+ buffer_.MoveRange(&buffer_[src.index_], &buffer_[src.index_ + 1],
+ &buffer_[dest.index_]);
+ }
+ }
+
+#if DCHECK_IS_ON()
+ // Asserts the given index is dereferencable. The index is an index into the
+ // buffer, not an index used by operator[] or at() which will be offsets from
+ // begin.
+ void CheckValidIndex(size_t i) const {
+ if (begin_ <= end_)
+ DCHECK(i >= begin_ && i < end_);
+ else
+ DCHECK((i >= begin_ && i < buffer_.capacity()) || i < end_);
+ }
+
+ // Asserts the given index is either dereferencable or points to end().
+ void CheckValidIndexOrEnd(size_t i) const {
+ if (i != end_)
+ CheckValidIndex(i);
+ }
+
+ void ValidateIterator(const const_iterator& i) const {
+ DCHECK(i.parent_deque_ == this);
+ i.CheckUnstableUsage();
+ }
+
+ // See generation_ below.
+ void IncrementGeneration() { generation_++; }
+#else
+ // No-op versions of these functions for release builds.
+ void CheckValidIndex(size_t) const {}
+ void CheckValidIndexOrEnd(size_t) const {}
+ void ValidateIterator(const const_iterator& i) const {}
+ void IncrementGeneration() {}
+#endif
+
+ // Danger, the buffer_.capacity() is the "internal capacity" which is
+ // capacity() + 1 since there is an extra item to indicate the end. Otherwise
+ // being completely empty and completely full are indistinguishable (begin ==
+ // end). We could add a separate flag to avoid it, but that adds significant
+ // extra complexity since every computation will have to check for it. Always
+ // keeping one extra unused element in the buffer makes iterator computations
+ // much simpler.
+ //
+ // Container internal code will want to use buffer_.capacity() for offset
+ // computations rather than capacity().
+ VectorBuffer buffer_;
+ size_type begin_ = 0;
+ size_type end_ = 0;
+
+#if DCHECK_IS_ON()
+ // Incremented every time a modification is made that could affect iterator
+ // invalidations.
+ uint64_t generation_ = 0;
+#endif
+};
+
+// Implementations of base::Erase[If] (see base/stl_util.h).
+template <class T, class Value>
+void Erase(circular_deque<T>& container, const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <class T, class Predicate>
+void EraseIf(circular_deque<T>& container, Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_CIRCULAR_DEQUE_H_
diff --git a/security/sandbox/chromium/base/containers/span.h b/security/sandbox/chromium/base/containers/span.h
new file mode 100644
index 0000000000..ce2e3c47e5
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/span.h
@@ -0,0 +1,530 @@
+// Copyright 2017 The Chromium Authors. 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_CONTAINERS_SPAN_H_
+#define BASE_CONTAINERS_SPAN_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <array>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "base/containers/checked_iterators.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+// [views.constants]
+constexpr size_t dynamic_extent = std::numeric_limits<size_t>::max();
+
+template <typename T, size_t Extent = dynamic_extent>
+class span;
+
+namespace internal {
+
+template <typename T>
+struct ExtentImpl : std::integral_constant<size_t, dynamic_extent> {};
+
+template <typename T, size_t N>
+struct ExtentImpl<T[N]> : std::integral_constant<size_t, N> {};
+
+template <typename T, size_t N>
+struct ExtentImpl<std::array<T, N>> : std::integral_constant<size_t, N> {};
+
+template <typename T, size_t N>
+struct ExtentImpl<base::span<T, N>> : std::integral_constant<size_t, N> {};
+
+template <typename T>
+using Extent = ExtentImpl<std::remove_cv_t<std::remove_reference_t<T>>>;
+
+template <typename T>
+struct IsSpanImpl : std::false_type {};
+
+template <typename T, size_t Extent>
+struct IsSpanImpl<span<T, Extent>> : std::true_type {};
+
+template <typename T>
+using IsSpan = IsSpanImpl<std::decay_t<T>>;
+
+template <typename T>
+struct IsStdArrayImpl : std::false_type {};
+
+template <typename T, size_t N>
+struct IsStdArrayImpl<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+using IsStdArray = IsStdArrayImpl<std::decay_t<T>>;
+
+template <typename T>
+using IsCArray = std::is_array<std::remove_reference_t<T>>;
+
+template <typename From, typename To>
+using IsLegalDataConversion = std::is_convertible<From (*)[], To (*)[]>;
+
+template <typename Container, typename T>
+using ContainerHasConvertibleData = IsLegalDataConversion<
+ std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>,
+ T>;
+
+template <typename Container>
+using ContainerHasIntegralSize =
+ std::is_integral<decltype(base::size(std::declval<Container>()))>;
+
+template <typename From, size_t FromExtent, typename To, size_t ToExtent>
+using EnableIfLegalSpanConversion =
+ std::enable_if_t<(ToExtent == dynamic_extent || ToExtent == FromExtent) &&
+ IsLegalDataConversion<From, To>::value>;
+
+// SFINAE check if Array can be converted to a span<T>.
+template <typename Array, typename T, size_t Extent>
+using EnableIfSpanCompatibleArray =
+ std::enable_if_t<(Extent == dynamic_extent ||
+ Extent == internal::Extent<Array>::value) &&
+ ContainerHasConvertibleData<Array, T>::value>;
+
+// SFINAE check if Container can be converted to a span<T>.
+template <typename Container, typename T>
+using IsSpanCompatibleContainer =
+ std::conditional_t<!IsSpan<Container>::value &&
+ !IsStdArray<Container>::value &&
+ !IsCArray<Container>::value &&
+ ContainerHasConvertibleData<Container, T>::value &&
+ ContainerHasIntegralSize<Container>::value,
+ std::true_type,
+ std::false_type>;
+
+template <typename Container, typename T>
+using EnableIfSpanCompatibleContainer =
+ std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value>;
+
+template <typename Container, typename T, size_t Extent>
+using EnableIfSpanCompatibleContainerAndSpanIsDynamic =
+ std::enable_if_t<IsSpanCompatibleContainer<Container, T>::value &&
+ Extent == dynamic_extent>;
+
+// A helper template for storing the size of a span. Spans with static extents
+// don't require additional storage, since the extent itself is specified in the
+// template parameter.
+template <size_t Extent>
+class ExtentStorage {
+ public:
+ constexpr explicit ExtentStorage(size_t size) noexcept {}
+ constexpr size_t size() const noexcept { return Extent; }
+};
+
+// Specialization of ExtentStorage for dynamic extents, which do require
+// explicit storage for the size.
+template <>
+struct ExtentStorage<dynamic_extent> {
+ constexpr explicit ExtentStorage(size_t size) noexcept : size_(size) {}
+ constexpr size_t size() const noexcept { return size_; }
+
+ private:
+ size_t size_;
+};
+
+} // namespace internal
+
+// A span is a value type that represents an array of elements of type T. Since
+// it only consists of a pointer to memory with an associated size, it is very
+// light-weight. It is cheap to construct, copy, move and use spans, so that
+// users are encouraged to use it as a pass-by-value parameter. A span does not
+// own the underlying memory, so care must be taken to ensure that a span does
+// not outlive the backing store.
+//
+// span is somewhat analogous to StringPiece, but with arbitrary element types,
+// allowing mutation if T is non-const.
+//
+// span is implicitly convertible from C++ arrays, as well as most [1]
+// container-like types that provide a data() and size() method (such as
+// std::vector<T>). A mutable span<T> can also be implicitly converted to an
+// immutable span<const T>.
+//
+// Consider using a span for functions that take a data pointer and size
+// parameter: it allows the function to still act on an array-like type, while
+// allowing the caller code to be a bit more concise.
+//
+// For read-only data access pass a span<const T>: the caller can supply either
+// a span<const T> or a span<T>, while the callee will have a read-only view.
+// For read-write access a mutable span<T> is required.
+//
+// Without span:
+// Read-Only:
+// // std::string HexEncode(const uint8_t* data, size_t size);
+// std::vector<uint8_t> data_buffer = GenerateData();
+// std::string r = HexEncode(data_buffer.data(), data_buffer.size());
+//
+// Mutable:
+// // ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args...);
+// char str_buffer[100];
+// SafeSNPrintf(str_buffer, sizeof(str_buffer), "Pi ~= %lf", 3.14);
+//
+// With span:
+// Read-Only:
+// // std::string HexEncode(base::span<const uint8_t> data);
+// std::vector<uint8_t> data_buffer = GenerateData();
+// std::string r = HexEncode(data_buffer);
+//
+// Mutable:
+// // ssize_t SafeSNPrintf(base::span<char>, const char* fmt, Args...);
+// char str_buffer[100];
+// SafeSNPrintf(str_buffer, "Pi ~= %lf", 3.14);
+//
+// Spans with "const" and pointers
+// -------------------------------
+//
+// Const and pointers can get confusing. Here are vectors of pointers and their
+// corresponding spans:
+//
+// const std::vector<int*> => base::span<int* const>
+// std::vector<const int*> => base::span<const int*>
+// const std::vector<const int*> => base::span<const int* const>
+//
+// Differences from the C++20 draft
+// --------------------------------
+//
+// http://eel.is/c++draft/views contains the latest C++20 draft of std::span.
+// Chromium tries to follow the draft as close as possible. Differences between
+// the draft and the implementation are documented in subsections below.
+//
+// Differences from [span.objectrep]:
+// - as_bytes() and as_writable_bytes() return spans of uint8_t instead of
+// std::byte (std::byte is a C++17 feature)
+//
+// Differences from [span.cons]:
+// - Constructing a static span (i.e. Extent != dynamic_extent) from a dynamic
+// sized container (e.g. std::vector) requires an explicit conversion (in the
+// C++20 draft this is simply UB)
+//
+// Differences from [span.obs]:
+// - empty() is marked with WARN_UNUSED_RESULT instead of [[nodiscard]]
+// ([[nodiscard]] is a C++17 feature)
+//
+// Furthermore, all constructors and methods are marked noexcept due to the lack
+// of exceptions in Chromium.
+//
+// Due to the lack of class template argument deduction guides in C++14
+// appropriate make_span() utility functions are provided.
+
+// [span], class template span
+template <typename T, size_t Extent>
+class span : public internal::ExtentStorage<Extent> {
+ private:
+ using ExtentStorage = internal::ExtentStorage<Extent>;
+
+ public:
+ using element_type = T;
+ using value_type = std::remove_cv_t<T>;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+ using iterator = CheckedContiguousIterator<T>;
+ using const_iterator = CheckedContiguousConstIterator<T>;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ static constexpr size_t extent = Extent;
+
+ // [span.cons], span constructors, copy, assignment, and destructor
+ constexpr span() noexcept : ExtentStorage(0), data_(nullptr) {
+ static_assert(Extent == dynamic_extent || Extent == 0, "Invalid Extent");
+ }
+
+ constexpr span(T* data, size_t size) noexcept
+ : ExtentStorage(size), data_(data) {
+ CHECK(Extent == dynamic_extent || Extent == size);
+ }
+
+ // Artificially templatized to break ambiguity for span(ptr, 0).
+ template <typename = void>
+ constexpr span(T* begin, T* end) noexcept : span(begin, end - begin) {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(begin <= end);
+ }
+
+ template <
+ size_t N,
+ typename = internal::EnableIfSpanCompatibleArray<T (&)[N], T, Extent>>
+ constexpr span(T (&array)[N]) noexcept : span(base::data(array), N) {}
+
+ template <
+ size_t N,
+ typename = internal::
+ EnableIfSpanCompatibleArray<std::array<value_type, N>&, T, Extent>>
+ constexpr span(std::array<value_type, N>& array) noexcept
+ : span(base::data(array), N) {}
+
+ template <size_t N,
+ typename = internal::EnableIfSpanCompatibleArray<
+ const std::array<value_type, N>&,
+ T,
+ Extent>>
+ constexpr span(const std::array<value_type, N>& array) noexcept
+ : span(base::data(array), N) {}
+
+ // Conversion from a container that has compatible base::data() and integral
+ // base::size().
+ template <
+ typename Container,
+ typename =
+ internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<Container&,
+ T,
+ Extent>>
+ constexpr span(Container& container) noexcept
+ : span(base::data(container), base::size(container)) {}
+
+ template <
+ typename Container,
+ typename = internal::EnableIfSpanCompatibleContainerAndSpanIsDynamic<
+ const Container&,
+ T,
+ Extent>>
+ constexpr span(const Container& container) noexcept
+ : span(base::data(container), base::size(container)) {}
+
+ constexpr span(const span& other) noexcept = default;
+
+ // Conversions from spans of compatible types and extents: this allows a
+ // span<T> to be seamlessly used as a span<const T>, but not the other way
+ // around. If extent is not dynamic, OtherExtent has to be equal to Extent.
+ template <
+ typename U,
+ size_t OtherExtent,
+ typename =
+ internal::EnableIfLegalSpanConversion<U, OtherExtent, T, Extent>>
+ constexpr span(const span<U, OtherExtent>& other)
+ : span(other.data(), other.size()) {}
+
+ constexpr span& operator=(const span& other) noexcept = default;
+ ~span() noexcept = default;
+
+ // [span.sub], span subviews
+ template <size_t Count>
+ constexpr span<T, Count> first() const noexcept {
+ static_assert(Extent == dynamic_extent || Count <= Extent,
+ "Count must not exceed Extent");
+ CHECK(Extent != dynamic_extent || Count <= size());
+ return {data(), Count};
+ }
+
+ template <size_t Count>
+ constexpr span<T, Count> last() const noexcept {
+ static_assert(Extent == dynamic_extent || Count <= Extent,
+ "Count must not exceed Extent");
+ CHECK(Extent != dynamic_extent || Count <= size());
+ return {data() + (size() - Count), Count};
+ }
+
+ template <size_t Offset, size_t Count = dynamic_extent>
+ constexpr span<T,
+ (Count != dynamic_extent
+ ? Count
+ : (Extent != dynamic_extent ? Extent - Offset
+ : dynamic_extent))>
+ subspan() const noexcept {
+ static_assert(Extent == dynamic_extent || Offset <= Extent,
+ "Offset must not exceed Extent");
+ static_assert(Extent == dynamic_extent || Count == dynamic_extent ||
+ Count <= Extent - Offset,
+ "Count must not exceed Extent - Offset");
+ CHECK(Extent != dynamic_extent || Offset <= size());
+ CHECK(Extent != dynamic_extent || Count == dynamic_extent ||
+ Count <= size() - Offset);
+ return {data() + Offset, Count != dynamic_extent ? Count : size() - Offset};
+ }
+
+ constexpr span<T, dynamic_extent> first(size_t count) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(count <= size());
+ return {data(), count};
+ }
+
+ constexpr span<T, dynamic_extent> last(size_t count) const noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(count <= size());
+ return {data() + (size() - count), count};
+ }
+
+ constexpr span<T, dynamic_extent> subspan(size_t offset,
+ size_t count = dynamic_extent) const
+ noexcept {
+ // Note: CHECK_LE is not constexpr, hence regular CHECK must be used.
+ CHECK(offset <= size());
+ CHECK(count == dynamic_extent || count <= size() - offset);
+ return {data() + offset, count != dynamic_extent ? count : size() - offset};
+ }
+
+ // [span.obs], span observers
+ constexpr size_t size() const noexcept { return ExtentStorage::size(); }
+ constexpr size_t size_bytes() const noexcept { return size() * sizeof(T); }
+ constexpr bool empty() const noexcept WARN_UNUSED_RESULT {
+ return size() == 0;
+ }
+
+ // [span.elem], span element access
+ constexpr T& operator[](size_t idx) const noexcept {
+ // Note: CHECK_LT is not constexpr, hence regular CHECK must be used.
+ CHECK(idx < size());
+ return *(data() + idx);
+ }
+
+ constexpr T& front() const noexcept {
+ static_assert(Extent == dynamic_extent || Extent > 0,
+ "Extent must not be 0");
+ CHECK(Extent != dynamic_extent || !empty());
+ return *data();
+ }
+
+ constexpr T& back() const noexcept {
+ static_assert(Extent == dynamic_extent || Extent > 0,
+ "Extent must not be 0");
+ CHECK(Extent != dynamic_extent || !empty());
+ return *(data() + size() - 1);
+ }
+
+ constexpr T* data() const noexcept { return data_; }
+
+ // [span.iter], span iterator support
+ constexpr iterator begin() const noexcept {
+ return iterator(data_, data_ + size());
+ }
+ constexpr iterator end() const noexcept {
+ return iterator(data_, data_ + size(), data_ + size());
+ }
+
+ constexpr const_iterator cbegin() const noexcept { return begin(); }
+ constexpr const_iterator cend() const noexcept { return end(); }
+
+ constexpr reverse_iterator rbegin() const noexcept {
+ return reverse_iterator(end());
+ }
+ constexpr reverse_iterator rend() const noexcept {
+ return reverse_iterator(begin());
+ }
+
+ constexpr const_reverse_iterator crbegin() const noexcept {
+ return const_reverse_iterator(cend());
+ }
+ constexpr const_reverse_iterator crend() const noexcept {
+ return const_reverse_iterator(cbegin());
+ }
+
+ private:
+ T* data_;
+};
+
+// span<T, Extent>::extent can not be declared inline prior to C++17, hence this
+// definition is required.
+template <class T, size_t Extent>
+constexpr size_t span<T, Extent>::extent;
+
+// [span.objectrep], views of object representation
+template <typename T, size_t X>
+span<const uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+as_bytes(span<T, X> s) noexcept {
+ return {reinterpret_cast<const uint8_t*>(s.data()), s.size_bytes()};
+}
+
+template <typename T,
+ size_t X,
+ typename = std::enable_if_t<!std::is_const<T>::value>>
+span<uint8_t, (X == dynamic_extent ? dynamic_extent : sizeof(T) * X)>
+as_writable_bytes(span<T, X> s) noexcept {
+ return {reinterpret_cast<uint8_t*>(s.data()), s.size_bytes()};
+}
+
+// Type-deducing helpers for constructing a span.
+template <int&... ExplicitArgumentBarrier, typename T>
+constexpr span<T> make_span(T* data, size_t size) noexcept {
+ return {data, size};
+}
+
+template <int&... ExplicitArgumentBarrier, typename T>
+constexpr span<T> make_span(T* begin, T* end) noexcept {
+ return {begin, end};
+}
+
+// make_span utility function that deduces both the span's value_type and extent
+// from the passed in argument.
+//
+// Usage: auto span = base::make_span(...);
+template <int&... ExplicitArgumentBarrier, typename Container>
+constexpr auto make_span(Container&& container) noexcept {
+ using T =
+ std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
+ using Extent = internal::Extent<Container>;
+ return span<T, Extent::value>(std::forward<Container>(container));
+}
+
+// make_span utility function that allows callers to explicit specify the span's
+// extent, the value_type is deduced automatically. This is useful when passing
+// a dynamically sized container to a method expecting static spans, when the
+// container is known to have the correct size.
+//
+// Note: This will CHECK that N indeed matches size(container).
+//
+// Usage: auto static_span = base::make_span<N>(...);
+template <size_t N, int&... ExplicitArgumentBarrier, typename Container>
+constexpr auto make_span(Container&& container) noexcept {
+ using T =
+ std::remove_pointer_t<decltype(base::data(std::declval<Container>()))>;
+ return span<T, N>(base::data(container), base::size(container));
+}
+
+} // namespace base
+
+// Note: std::tuple_size, std::tuple_element and std::get are specialized for
+// static spans, so that they can be used in C++17's structured bindings. While
+// we don't support C++17 yet, there is no harm in providing these
+// specializations already.
+namespace std {
+
+// [span.tuple], tuple interface
+#if defined(__clang__)
+// Due to https://llvm.org/PR39871 and https://llvm.org/PR41331 and their
+// respective fixes different versions of libc++ declare std::tuple_size and
+// std::tuple_element either as classes or structs. In order to be able to
+// specialize std::tuple_size and std::tuple_element for custom base types we
+// thus need to disable -Wmismatched-tags in order to support all build
+// configurations. Note that this is blessed by the standard in
+// https://timsong-cpp.github.io/cppwp/n4140/dcl.type.elab#3.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template <typename T, size_t X>
+struct tuple_size<base::span<T, X>> : public integral_constant<size_t, X> {};
+
+template <typename T>
+struct tuple_size<base::span<T, base::dynamic_extent>>; // not defined
+
+template <size_t I, typename T, size_t X>
+struct tuple_element<I, base::span<T, X>> {
+ static_assert(
+ base::dynamic_extent != X,
+ "std::tuple_element<> not supported for base::span<T, dynamic_extent>");
+ static_assert(I < X,
+ "Index out of bounds in std::tuple_element<> (base::span)");
+ using type = T;
+};
+#if defined(__clang__)
+#pragma clang diagnostic pop // -Wmismatched-tags
+#endif
+
+template <size_t I, typename T, size_t X>
+constexpr T& get(base::span<T, X> s) noexcept {
+ static_assert(base::dynamic_extent != X,
+ "std::get<> not supported for base::span<T, dynamic_extent>");
+ static_assert(I < X, "Index out of bounds in std::get<> (base::span)");
+ return s[I];
+}
+
+} // namespace std
+
+#endif // BASE_CONTAINERS_SPAN_H_
diff --git a/security/sandbox/chromium/base/containers/stack.h b/security/sandbox/chromium/base/containers/stack.h
new file mode 100644
index 0000000000..5cf06f8251
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/stack.h
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. 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_CONTAINERS_STACK_H_
+#define BASE_CONTAINERS_STACK_H_
+
+#include <stack>
+
+#include "base/containers/circular_deque.h"
+
+namespace base {
+
+// Provides a definition of base::stack that's like std::stack but uses a
+// base::circular_deque instead of std::deque. Since std::stack is just a
+// wrapper for an underlying type, we can just provide a typedef for it that
+// defaults to the base circular_deque.
+template <class T, class Container = circular_deque<T>>
+using stack = std::stack<T, Container>;
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_STACK_H_
diff --git a/security/sandbox/chromium/base/containers/util.h b/security/sandbox/chromium/base/containers/util.h
new file mode 100644
index 0000000000..435db0d1f8
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/util.h
@@ -0,0 +1,21 @@
+// Copyright 2018 The Chromium Authors. 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_CONTAINERS_UTIL_H_
+#define BASE_CONTAINERS_UTIL_H_
+
+#include <stdint.h>
+
+namespace base {
+
+// TODO(crbug.com/817982): What we really need is for checked_math.h to be
+// able to do checked arithmetic on pointers.
+template <typename T>
+static inline uintptr_t get_uintptr(const T* t) {
+ return reinterpret_cast<uintptr_t>(t);
+}
+
+} // namespace base
+
+#endif // BASE_CONTAINERS_UTIL_H_
diff --git a/security/sandbox/chromium/base/containers/vector_buffer.h b/security/sandbox/chromium/base/containers/vector_buffer.h
new file mode 100644
index 0000000000..83cd2ac139
--- /dev/null
+++ b/security/sandbox/chromium/base/containers/vector_buffer.h
@@ -0,0 +1,188 @@
+// Copyright 2017 The Chromium Authors. 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_CONTAINERS_VECTOR_BUFFERS_H_
+#define BASE_CONTAINERS_VECTOR_BUFFERS_H_
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <type_traits>
+#include <utility>
+
+#include "base/containers/util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/checked_math.h"
+
+namespace base {
+namespace internal {
+
+// Internal implementation detail of base/containers.
+//
+// Implements a vector-like buffer that holds a certain capacity of T. Unlike
+// std::vector, VectorBuffer never constructs or destructs its arguments, and
+// can't change sizes. But it does implement templates to assist in efficient
+// moving and destruction of those items manually.
+//
+// In particular, the destructor function does not iterate over the items if
+// there is no destructor. Moves should be implemented as a memcpy/memmove for
+// trivially copyable objects (POD) otherwise, it should be a std::move if
+// possible, and as a last resort it falls back to a copy. This behavior is
+// similar to std::vector.
+//
+// No special consideration is done for noexcept move constructors since
+// we compile without exceptions.
+//
+// The current API does not support moving overlapping ranges.
+template <typename T>
+class VectorBuffer {
+ public:
+ constexpr VectorBuffer() = default;
+
+#if defined(__clang__) && !defined(__native_client__)
+ // This constructor converts an uninitialized void* to a T* which triggers
+ // clang Control Flow Integrity. Since this is as-designed, disable.
+ __attribute__((no_sanitize("cfi-unrelated-cast", "vptr")))
+#endif
+ VectorBuffer(size_t count)
+ : buffer_(reinterpret_cast<T*>(
+ malloc(CheckMul(sizeof(T), count).ValueOrDie()))),
+ capacity_(count) {
+ }
+ VectorBuffer(VectorBuffer&& other) noexcept
+ : buffer_(other.buffer_), capacity_(other.capacity_) {
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ }
+
+ ~VectorBuffer() { free(buffer_); }
+
+ VectorBuffer& operator=(VectorBuffer&& other) {
+ free(buffer_);
+ buffer_ = other.buffer_;
+ capacity_ = other.capacity_;
+
+ other.buffer_ = nullptr;
+ other.capacity_ = 0;
+ return *this;
+ }
+
+ size_t capacity() const { return capacity_; }
+
+ T& operator[](size_t i) {
+ // TODO(crbug.com/817982): Some call sites (at least circular_deque.h) are
+ // calling this with `i == capacity_` as a way of getting `end()`. Therefore
+ // we have to allow this for now (`i <= capacity_`), until we fix those call
+ // sites to use real iterators. This comment applies here and to `const T&
+ // operator[]`, below.
+ CHECK_LE(i, capacity_);
+ return buffer_[i];
+ }
+
+ const T& operator[](size_t i) const {
+ CHECK_LE(i, capacity_);
+ return buffer_[i];
+ }
+
+ T* begin() { return buffer_; }
+ T* end() { return &buffer_[capacity_]; }
+
+ // DestructRange ------------------------------------------------------------
+
+ // Trivially destructible objects need not have their destructors called.
+ template <typename T2 = T,
+ typename std::enable_if<std::is_trivially_destructible<T2>::value,
+ int>::type = 0>
+ void DestructRange(T* begin, T* end) {}
+
+ // Non-trivially destructible objects must have their destructors called
+ // individually.
+ template <typename T2 = T,
+ typename std::enable_if<!std::is_trivially_destructible<T2>::value,
+ int>::type = 0>
+ void DestructRange(T* begin, T* end) {
+ CHECK_LE(begin, end);
+ while (begin != end) {
+ begin->~T();
+ begin++;
+ }
+ }
+
+ // MoveRange ----------------------------------------------------------------
+ //
+ // The destructor will be called (as necessary) for all moved types. The
+ // ranges must not overlap.
+ //
+ // The parameters and begin and end (one past the last) of the input buffer,
+ // and the address of the first element to copy to. There must be sufficient
+ // room in the destination for all items in the range [begin, end).
+
+ // Trivially copyable types can use memcpy. trivially copyable implies
+ // that there is a trivial destructor as we don't have to call it.
+ template <typename T2 = T,
+ typename std::enable_if<base::is_trivially_copyable<T2>::value,
+ int>::type = 0>
+ static void MoveRange(T* from_begin, T* from_end, T* to) {
+ CHECK(!RangesOverlap(from_begin, from_end, to));
+ memcpy(
+ to, from_begin,
+ CheckSub(get_uintptr(from_end), get_uintptr(from_begin)).ValueOrDie());
+ }
+
+ // Not trivially copyable, but movable: call the move constructor and
+ // destruct the original.
+ template <typename T2 = T,
+ typename std::enable_if<std::is_move_constructible<T2>::value &&
+ !base::is_trivially_copyable<T2>::value,
+ int>::type = 0>
+ static void MoveRange(T* from_begin, T* from_end, T* to) {
+ CHECK(!RangesOverlap(from_begin, from_end, to));
+ while (from_begin != from_end) {
+ new (to) T(std::move(*from_begin));
+ from_begin->~T();
+ from_begin++;
+ to++;
+ }
+ }
+
+ // Not movable, not trivially copyable: call the copy constructor and
+ // destruct the original.
+ template <typename T2 = T,
+ typename std::enable_if<!std::is_move_constructible<T2>::value &&
+ !base::is_trivially_copyable<T2>::value,
+ int>::type = 0>
+ static void MoveRange(T* from_begin, T* from_end, T* to) {
+ CHECK(!RangesOverlap(from_begin, from_end, to));
+ while (from_begin != from_end) {
+ new (to) T(*from_begin);
+ from_begin->~T();
+ from_begin++;
+ to++;
+ }
+ }
+
+ private:
+ static bool RangesOverlap(const T* from_begin,
+ const T* from_end,
+ const T* to) {
+ const auto from_begin_uintptr = get_uintptr(from_begin);
+ const auto from_end_uintptr = get_uintptr(from_end);
+ const auto to_uintptr = get_uintptr(to);
+ return !(
+ to >= from_end ||
+ CheckAdd(to_uintptr, CheckSub(from_end_uintptr, from_begin_uintptr))
+ .ValueOrDie() <= from_begin_uintptr);
+ }
+
+ T* buffer_ = nullptr;
+ size_t capacity_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorBuffer);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_CONTAINERS_VECTOR_BUFFERS_H_
diff --git a/security/sandbox/chromium/base/cpu.cc b/security/sandbox/chromium/base/cpu.cc
new file mode 100644
index 0000000000..8cc2028a5f
--- /dev/null
+++ b/security/sandbox/chromium/base/cpu.cc
@@ -0,0 +1,312 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/cpu.h"
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/stl_util.h"
+
+#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+#include "base/files/file_util.h"
+#endif
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#if defined(COMPILER_MSVC)
+#include <intrin.h>
+#include <immintrin.h> // For _xgetbv()
+#endif
+#endif
+
+namespace base {
+
+#if defined(ARCH_CPU_X86_FAMILY)
+namespace internal {
+
+std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
+ const std::string& vendor,
+ int signature) {
+ int family = (signature >> 8) & 0xf;
+ int model = (signature >> 4) & 0xf;
+ int ext_family = 0;
+ int ext_model = 0;
+
+ // The "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
+ // specifies the Extended Model is defined only when the Base Family is
+ // 06h or 0Fh.
+ // The "AMD CPUID Specification" specifies that the Extended Model is
+ // defined only when Base Family is 0Fh.
+ // Both manuals define the display model as
+ // {ExtendedModel[3:0],BaseModel[3:0]} in that case.
+ if (family == 0xf || (family == 0x6 && vendor == "GenuineIntel")) {
+ ext_model = (signature >> 16) & 0xf;
+ model += ext_model << 4;
+ }
+ // Both the "Intel 64 and IA-32 Architectures Developer's Manual: Vol. 2A"
+ // and the "AMD CPUID Specification" specify that the Extended Family is
+ // defined only when the Base Family is 0Fh.
+ // Both manuals define the display family as {0000b,BaseFamily[3:0]} +
+ // ExtendedFamily[7:0] in that case.
+ if (family == 0xf) {
+ ext_family = (signature >> 20) & 0xff;
+ family += ext_family;
+ }
+
+ return {family, model, ext_family, ext_model};
+}
+
+} // namespace internal
+#endif // defined(ARCH_CPU_X86_FAMILY)
+
+CPU::CPU()
+ : signature_(0),
+ type_(0),
+ family_(0),
+ model_(0),
+ stepping_(0),
+ ext_model_(0),
+ ext_family_(0),
+ has_mmx_(false),
+ has_sse_(false),
+ has_sse2_(false),
+ has_sse3_(false),
+ has_ssse3_(false),
+ has_sse41_(false),
+ has_sse42_(false),
+ has_popcnt_(false),
+ has_avx_(false),
+ has_avx2_(false),
+ has_aesni_(false),
+ has_non_stop_time_stamp_counter_(false),
+ is_running_in_vm_(false),
+ cpu_vendor_("unknown") {
+ Initialize();
+}
+
+namespace {
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#if !defined(COMPILER_MSVC)
+
+#if defined(__pic__) && defined(__i386__)
+
+void __cpuid(int cpu_info[4], int info_type) {
+ __asm__ volatile(
+ "mov %%ebx, %%edi\n"
+ "cpuid\n"
+ "xchg %%edi, %%ebx\n"
+ : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]),
+ "=d"(cpu_info[3])
+ : "a"(info_type), "c"(0));
+}
+
+#else
+
+void __cpuid(int cpu_info[4], int info_type) {
+ __asm__ volatile("cpuid\n"
+ : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
+ "=d"(cpu_info[3])
+ : "a"(info_type), "c"(0));
+}
+
+#endif
+#endif // !defined(COMPILER_MSVC)
+
+// 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_t xgetbv(uint32_t xcr) {
+#if defined(COMPILER_MSVC)
+ return _xgetbv(xcr);
+#else
+ uint32_t eax, edx;
+
+ __asm__ volatile (
+ "xgetbv" : "=a"(eax), "=d"(edx) : "c"(xcr));
+ return (static_cast<uint64_t>(edx) << 32) | eax;
+#endif // defined(COMPILER_MSVC)
+}
+
+#endif // ARCH_CPU_X86_FAMILY
+
+#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+std::string* CpuInfoBrand() {
+ static std::string* brand = []() {
+ // This function finds the value from /proc/cpuinfo under the key "model
+ // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
+ // and later for arm64) and is shown once per CPU. "Processor" is used in
+ // earler versions and is shown only once at the top of /proc/cpuinfo
+ // regardless of the number CPUs.
+ const char kModelNamePrefix[] = "model name\t: ";
+ const char kProcessorPrefix[] = "Processor\t: ";
+
+ std::string contents;
+ ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
+ DCHECK(!contents.empty());
+
+ std::istringstream iss(contents);
+ std::string line;
+ while (std::getline(iss, line)) {
+ if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0)
+ return new std::string(line.substr(strlen(kModelNamePrefix)));
+ if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)
+ return new std::string(line.substr(strlen(kProcessorPrefix)));
+ }
+
+ return new std::string();
+ }();
+
+ return brand;
+}
+#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) ||
+ // defined(OS_LINUX))
+
+} // namespace
+
+void CPU::Initialize() {
+#if defined(ARCH_CPU_X86_FAMILY)
+ int cpu_info[4] = {-1};
+ // This array is used to temporarily hold the vendor name and then the brand
+ // name. Thus it has to be big enough for both use cases. There are
+ // static_asserts below for each of the use cases to make sure this array is
+ // big enough.
+ char cpu_string[sizeof(cpu_info) * 3 + 1];
+
+ // __cpuid with an InfoType argument of 0 returns the number of
+ // valid Ids in CPUInfo[0] and the CPU identification string in
+ // the other three array elements. The CPU identification string is
+ // not in linear order. The code below arranges the information
+ // in a human readable form. The human readable order is CPUInfo[1] |
+ // CPUInfo[3] | CPUInfo[2]. CPUInfo[2] and CPUInfo[3] are swapped
+ // before using memcpy() to copy these three array elements to |cpu_string|.
+ __cpuid(cpu_info, 0);
+ int num_ids = cpu_info[0];
+ std::swap(cpu_info[2], cpu_info[3]);
+ static constexpr size_t kVendorNameSize = 3 * sizeof(cpu_info[1]);
+ static_assert(kVendorNameSize < base::size(cpu_string),
+ "cpu_string too small");
+ memcpy(cpu_string, &cpu_info[1], kVendorNameSize);
+ cpu_string[kVendorNameSize] = '\0';
+ cpu_vendor_ = cpu_string;
+
+ // 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;
+ type_ = (cpu_info[0] >> 12) & 0x3;
+ std::tie(family_, model_, ext_family_, ext_model_) =
+ internal::ComputeX86FamilyAndModel(cpu_vendor_, signature_);
+ has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
+ has_sse_ = (cpu_info[3] & 0x02000000) != 0;
+ has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
+ has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
+ has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
+ has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
+ has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
+ has_popcnt_ = (cpu_info[2] & 0x00800000) != 0;
+
+ // "Hypervisor Present Bit: Bit 31 of ECX of CPUID leaf 0x1."
+ // See https://lwn.net/Articles/301888/
+ // This is checking for any hypervisor. Hypervisors may choose not to
+ // announce themselves. Hypervisors trap CPUID and sometimes return
+ // different results to underlying hardware.
+ is_running_in_vm_ = (cpu_info[2] & 0x80000000) != 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
+ // c) XSAVE is enabled by the kernel.
+ // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
+ //
+ // In addition, we have observed some crashes with the xgetbv instruction
+ // even after following Intel's example code. (See crbug.com/375968.)
+ // Because of that, we also test the XSAVE bit because its description in
+ // the CPUID documentation suggests that it signals xgetbv support.
+ has_avx_ =
+ (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.
+ __cpuid(cpu_info, 0x80000000);
+ const int max_parameter = cpu_info[0];
+
+ static constexpr int kParameterStart = 0x80000002;
+ static constexpr int kParameterEnd = 0x80000004;
+ static constexpr int kParameterSize = kParameterEnd - kParameterStart + 1;
+ static_assert(kParameterSize * sizeof(cpu_info) + 1 == base::size(cpu_string),
+ "cpu_string has wrong size");
+
+ if (max_parameter >= kParameterEnd) {
+ size_t i = 0;
+ for (int parameter = kParameterStart; parameter <= kParameterEnd;
+ ++parameter) {
+ __cpuid(cpu_info, parameter);
+ memcpy(&cpu_string[i], cpu_info, sizeof(cpu_info));
+ i += sizeof(cpu_info);
+ }
+ cpu_string[i] = '\0';
+ cpu_brand_ = cpu_string;
+ }
+
+ static constexpr int kParameterContainingNonStopTimeStampCounter = 0x80000007;
+ if (max_parameter >= kParameterContainingNonStopTimeStampCounter) {
+ __cpuid(cpu_info, kParameterContainingNonStopTimeStampCounter);
+ has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
+ }
+
+ if (!has_non_stop_time_stamp_counter_ && is_running_in_vm_) {
+ int cpu_info_hv[4] = {};
+ __cpuid(cpu_info_hv, 0x40000000);
+ if (cpu_info_hv[1] == 0x7263694D && // Micr
+ cpu_info_hv[2] == 0x666F736F && // osof
+ cpu_info_hv[3] == 0x76482074) { // t Hv
+ // If CPUID says we have a variant TSC and a hypervisor has identified
+ // itself and the hypervisor says it is Microsoft Hyper-V, then treat
+ // TSC as invariant.
+ //
+ // Microsoft Hyper-V hypervisor reports variant TSC as there are some
+ // scenarios (eg. VM live migration) where the TSC is variant, but for
+ // our purposes we can treat it as invariant.
+ has_non_stop_time_stamp_counter_ = true;
+ }
+ }
+#elif defined(ARCH_CPU_ARM_FAMILY)
+#if (defined(OS_ANDROID) || defined(OS_LINUX))
+ cpu_brand_ = *CpuInfoBrand();
+#elif defined(OS_WIN)
+ // Windows makes high-resolution thread timing information available in
+ // user-space.
+ has_non_stop_time_stamp_counter_ = true;
+#endif
+#endif
+}
+
+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;
+ if (has_ssse3()) return SSSE3;
+ if (has_sse3()) return SSE3;
+ if (has_sse2()) return SSE2;
+ if (has_sse()) return SSE;
+ return PENTIUM;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/cpu.h b/security/sandbox/chromium/base/cpu.h
new file mode 100644
index 0000000000..4fcda6904f
--- /dev/null
+++ b/security/sandbox/chromium/base/cpu.h
@@ -0,0 +1,104 @@
+// 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_CPU_H_
+#define BASE_CPU_H_
+
+#include <string>
+#include <tuple>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+namespace base {
+
+#if defined(ARCH_CPU_X86_FAMILY)
+namespace internal {
+
+// Compute the CPU family and model based on the vendor and CPUID signature.
+// Returns in order: family, model, extended family, extended model.
+BASE_EXPORT std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
+ const std::string& vendor,
+ int signature);
+
+} // namespace internal
+#endif // defined(ARCH_CPU_X86_FAMILY)
+
+// Query information about the processor.
+class BASE_EXPORT CPU final {
+ public:
+ CPU();
+
+ enum IntelMicroArchitecture {
+ PENTIUM,
+ SSE,
+ SSE2,
+ SSE3,
+ SSSE3,
+ SSE41,
+ SSE42,
+ AVX,
+ AVX2,
+ MAX_INTEL_MICRO_ARCHITECTURE
+ };
+
+ // Accessors for CPU information.
+ const std::string& vendor_name() const { return cpu_vendor_; }
+ int signature() const { return signature_; }
+ int stepping() const { return stepping_; }
+ int model() const { return model_; }
+ int family() const { return family_; }
+ int type() const { return type_; }
+ int extended_model() const { return ext_model_; }
+ int extended_family() const { return ext_family_; }
+ bool has_mmx() const { return has_mmx_; }
+ bool has_sse() const { return has_sse_; }
+ bool has_sse2() const { return has_sse2_; }
+ bool has_sse3() const { return has_sse3_; }
+ bool has_ssse3() const { return has_ssse3_; }
+ bool has_sse41() const { return has_sse41_; }
+ bool has_sse42() const { return has_sse42_; }
+ bool has_popcnt() const { return has_popcnt_; }
+ bool has_avx() const { return has_avx_; }
+ 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_;
+ }
+ bool is_running_in_vm() const { return is_running_in_vm_; }
+
+ IntelMicroArchitecture GetIntelMicroArchitecture() const;
+ const std::string& cpu_brand() const { return cpu_brand_; }
+
+ private:
+ // Query the processor for CPUID information.
+ void Initialize();
+
+ int signature_; // raw form of type, family, model, and stepping
+ int type_; // process type
+ int family_; // family of the processor
+ int model_; // model of processor
+ int stepping_; // processor revision number
+ int ext_model_;
+ int ext_family_;
+ bool has_mmx_;
+ bool has_sse_;
+ bool has_sse2_;
+ bool has_sse3_;
+ bool has_ssse3_;
+ bool has_sse41_;
+ bool has_sse42_;
+ bool has_popcnt_;
+ bool has_avx_;
+ bool has_avx2_;
+ bool has_aesni_;
+ bool has_non_stop_time_stamp_counter_;
+ bool is_running_in_vm_;
+ std::string cpu_vendor_;
+ std::string cpu_brand_;
+};
+
+} // namespace base
+
+#endif // BASE_CPU_H_
diff --git a/security/sandbox/chromium/base/debug/alias.cc b/security/sandbox/chromium/base/debug/alias.cc
new file mode 100644
index 0000000000..f808c50345
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/alias.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/alias.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace debug {
+
+// This file/function should be excluded from LTO/LTCG to ensure that the
+// compiler can't see this function's implementation when compiling calls to it.
+NOINLINE void Alias(const void* var) {}
+
+} // namespace debug
+} // namespace base
diff --git a/security/sandbox/chromium/base/debug/alias.h b/security/sandbox/chromium/base/debug/alias.h
new file mode 100644
index 0000000000..35af2c23bf
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/alias.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEBUG_ALIAS_H_
+#define BASE_DEBUG_ALIAS_H_
+
+#include "base/base_export.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+namespace debug {
+
+// Make the optimizer think that var is aliased. This is to prevent it from
+// optimizing out local variables that would not otherwise be live at the point
+// of a potential crash.
+// base::debug::Alias should only be used for local variables, not globals,
+// object members, or function return values - these must be copied to locals if
+// you want to ensure they are recorded in crash dumps.
+// Note that if the local variable is a pointer then its value will be retained
+// but the memory that it points to will probably not be saved in the crash
+// dump - by default only stack memory is saved. Therefore the aliasing
+// technique is usually only worthwhile with non-pointer variables. If you have
+// a pointer to an object and you want to retain the object's state you need to
+// copy the object or its fields to local variables. Example usage:
+// int last_error = err_;
+// base::debug::Alias(&last_error);
+// DEBUG_ALIAS_FOR_CSTR(name_copy, p->name, 16);
+// CHECK(false);
+void BASE_EXPORT Alias(const void* var);
+
+} // namespace debug
+} // namespace base
+
+// Convenience macro that copies the null-terminated string from |c_str| into a
+// stack-allocated char array named |var_name| that holds up to |char_count|
+// characters and should be preserved in memory dumps.
+#define DEBUG_ALIAS_FOR_CSTR(var_name, c_str, char_count) \
+ char var_name[char_count]; \
+ ::base::strlcpy(var_name, (c_str), base::size(var_name)); \
+ ::base::debug::Alias(var_name);
+
+#endif // BASE_DEBUG_ALIAS_H_
diff --git a/security/sandbox/chromium/base/debug/crash_logging.h b/security/sandbox/chromium/base/debug/crash_logging.h
new file mode 100644
index 0000000000..9c6cd758da
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/crash_logging.h
@@ -0,0 +1,104 @@
+// 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_DEBUG_CRASH_LOGGING_H_
+#define BASE_DEBUG_CRASH_LOGGING_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+namespace debug {
+
+// A crash key is an annotation that is carried along with a crash report, to
+// provide additional debugging information beyond a stack trace. Crash keys
+// have a name and a string value.
+//
+// The preferred API is //components/crash/core/common:crash_key, however not
+// all clients can hold a direct dependency on that target. The API provided
+// in this file indirects the dependency.
+//
+// Example usage:
+// static CrashKeyString* crash_key =
+// AllocateCrashKeyString("name", CrashKeySize::Size32);
+// SetCrashKeyString(crash_key, "value");
+// ClearCrashKeyString(crash_key);
+
+// The maximum length for a crash key's value must be one of the following
+// pre-determined values.
+enum class CrashKeySize {
+ Size32 = 32,
+ Size64 = 64,
+ Size256 = 256,
+};
+
+struct CrashKeyString;
+
+// Allocates a new crash key with the specified |name| with storage for a
+// value up to length |size|. This will return null if the crash key system is
+// not initialized.
+BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[],
+ CrashKeySize size);
+
+// Stores |value| into the specified |crash_key|. The |crash_key| may be null
+// if AllocateCrashKeyString() returned null. If |value| is longer than the
+// size with which the key was allocated, it will be truncated.
+BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key,
+ base::StringPiece value);
+
+// Clears any value that was stored in |crash_key|. The |crash_key| may be
+// null.
+BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key);
+
+// A scoper that sets the specified key to value for the lifetime of the
+// object, and clears it on destruction.
+class BASE_EXPORT ScopedCrashKeyString {
+ public:
+ ScopedCrashKeyString(CrashKeyString* crash_key, base::StringPiece value);
+ ~ScopedCrashKeyString();
+
+ private:
+ CrashKeyString* const crash_key_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// The following declarations are used to initialize the crash key system
+// in //base by providing implementations for the above functions.
+
+// The virtual interface that provides the implementation for the crash key
+// API. This is implemented by a higher-layer component, and the instance is
+// set using the function below.
+class CrashKeyImplementation {
+ public:
+ virtual ~CrashKeyImplementation() = default;
+
+ virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0;
+ virtual void Set(CrashKeyString* crash_key, base::StringPiece value) = 0;
+ virtual void Clear(CrashKeyString* crash_key) = 0;
+};
+
+// Initializes the crash key system in base by replacing the existing
+// implementation, if it exists, with |impl|. The |impl| is copied into base.
+BASE_EXPORT void SetCrashKeyImplementation(
+ std::unique_ptr<CrashKeyImplementation> impl);
+
+// The base structure for a crash key, storing the allocation metadata.
+struct CrashKeyString {
+ constexpr CrashKeyString(const char name[], CrashKeySize size)
+ : name(name), size(size) {}
+ const char* const name;
+ const CrashKeySize size;
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_CRASH_LOGGING_H_
diff --git a/security/sandbox/chromium/base/debug/debugger.h b/security/sandbox/chromium/base/debug/debugger.h
new file mode 100644
index 0000000000..efc9b40cb9
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/debugger.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a cross platform interface for helper functions related to
+// 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_
+
+#include "base/base_export.h"
+
+namespace base {
+namespace debug {
+
+// Waits wait_seconds seconds for a debugger to attach to the current process.
+// When silent is false, an exception is thrown when a debugger is detected.
+BASE_EXPORT bool WaitForDebugger(int wait_seconds, bool silent);
+
+// Returns true if the given process is being run under a debugger.
+//
+// On OS X, the underlying mechanism doesn't work when the sandbox is enabled.
+// To get around this, this function caches its value.
+//
+// WARNING: Because of this, on OS X, a call MUST be made to this function
+// BEFORE the sandbox is enabled.
+BASE_EXPORT bool BeingDebugged();
+
+// Break into the debugger, assumes a debugger is present.
+BASE_EXPORT void BreakDebugger();
+
+// Used in test code, this controls whether showing dialogs and breaking into
+// the debugger is suppressed for debug errors, even in debug mode (normally
+// release mode doesn't do this stuff -- this is controlled separately).
+// Normally UI is not suppressed. This is normally used when running automated
+// tests where we want a crash rather than a dialog or a debugger.
+BASE_EXPORT void SetSuppressDebugUI(bool suppress);
+BASE_EXPORT bool IsDebugUISuppressed();
+
+// If a debugger is present, verifies that it is properly set up, and DCHECK()s
+// if misconfigured. Currently only verifies that //tools/gdb/gdbinit has been
+// sourced when using gdb on Linux and //tools/lldb/lldbinit.py has been sourced
+// when using lldb on macOS.
+BASE_EXPORT void VerifyDebugger();
+
+} // namespace debug
+} // namespace base
+
+#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
new file mode 100644
index 0000000000..dc502461d0
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/leak_annotations.h
@@ -0,0 +1,46 @@
+// 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_DEBUG_LEAK_ANNOTATIONS_H_
+#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
+
+#include "base/macros.h"
+#include "build/build_config.h"
+
+// This file defines macros which can be used to annotate intentional memory
+// leaks. Support for annotations is implemented in LeakSanitizer. Annotated
+// objects will be treated as a source of live pointers, i.e. any heap objects
+// reachable by following pointers from an annotated object will not be
+// reported as leaks.
+//
+// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope
+// will be annotated as leaks.
+// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will
+// be annotated as a leak.
+
+#if defined(LEAK_SANITIZER) && !defined(OS_NACL)
+
+#include <sanitizer/lsan_interface.h>
+
+class ScopedLeakSanitizerDisabler {
+ public:
+ ScopedLeakSanitizerDisabler() { __lsan_disable(); }
+ ~ScopedLeakSanitizerDisabler() { __lsan_enable(); }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedLeakSanitizerDisabler);
+};
+
+#define ANNOTATE_SCOPED_MEMORY_LEAK \
+ ScopedLeakSanitizerDisabler leak_sanitizer_disabler; static_cast<void>(0)
+
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) __lsan_ignore_object(X);
+
+#else
+
+#define ANNOTATE_SCOPED_MEMORY_LEAK ((void)0)
+#define ANNOTATE_LEAKING_OBJECT_PTR(X) ((void)0)
+
+#endif
+
+#endif // BASE_DEBUG_LEAK_ANNOTATIONS_H_
diff --git a/security/sandbox/chromium/base/debug/profiler.cc b/security/sandbox/chromium/base/debug/profiler.cc
new file mode 100644
index 0000000000..3530d61d07
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/profiler.cc
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/profiler.h"
+
+#include <string>
+
+#include "base/allocator/buildflags.h"
+#include "base/debug/debugging_buildflags.h"
+#include "base/process/process_handle.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/current_module.h"
+#include "base/win/pe_image.h"
+#endif // defined(OS_WIN)
+
+// TODO(peria): Enable profiling on Windows.
+#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_TCMALLOC) && !defined(OS_WIN)
+#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
+#endif
+
+namespace base {
+namespace debug {
+
+// TODO(peria): Enable profiling on Windows.
+#if BUILDFLAG(ENABLE_PROFILING) && BUILDFLAG(USE_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 = NumberToString(GetCurrentProcId());
+ std::string count = NumberToString(profile_count);
+ ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid);
+ ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count);
+ ProfilerStart(full_name.c_str());
+}
+
+void StopProfiling() {
+ ProfilerFlush();
+ ProfilerStop();
+}
+
+void FlushProfiling() {
+ ProfilerFlush();
+}
+
+bool BeingProfiled() {
+ return ProfilingIsEnabledForAllThreads();
+}
+
+void RestartProfilingAfterFork() {
+ ProfilerRegisterThread();
+}
+
+bool IsProfilingSupported() {
+ return true;
+}
+
+#else
+
+void StartProfiling(const std::string& name) {
+}
+
+void StopProfiling() {
+}
+
+void FlushProfiling() {
+}
+
+bool BeingProfiled() {
+ return false;
+}
+
+void RestartProfilingAfterFork() {
+}
+
+bool IsProfilingSupported() {
+ return false;
+}
+
+#endif
+
+#if !defined(OS_WIN)
+
+ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
+ return nullptr;
+}
+
+AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
+ return nullptr;
+}
+
+MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
+ return nullptr;
+}
+
+#else // defined(OS_WIN)
+
+namespace {
+
+struct FunctionSearchContext {
+ const char* name;
+ FARPROC function;
+};
+
+// Callback function to PEImage::EnumImportChunks.
+bool FindResolutionFunctionInImports(
+ const base::win::PEImage &image, const char* module_name,
+ PIMAGE_THUNK_DATA unused_name_table, PIMAGE_THUNK_DATA import_address_table,
+ PVOID cookie) {
+ FunctionSearchContext* context =
+ reinterpret_cast<FunctionSearchContext*>(cookie);
+
+ DCHECK(context);
+ DCHECK(!context->function);
+
+ // Our import address table contains pointers to the functions we import
+ // at this point. Let's retrieve the first such function and use it to
+ // find the module this import was resolved to by the loader.
+ const wchar_t* function_in_module =
+ reinterpret_cast<const wchar_t*>(import_address_table->u1.Function);
+
+ // Retrieve the module by a function in the module.
+ const DWORD kFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
+ HMODULE module = NULL;
+ if (!::GetModuleHandleEx(kFlags, function_in_module, &module)) {
+ // This can happen if someone IAT patches us to a thunk.
+ return true;
+ }
+
+ // See whether this module exports the function we're looking for.
+ FARPROC exported_func = ::GetProcAddress(module, context->name);
+ if (exported_func != NULL) {
+ // We found it, return the function and terminate the enumeration.
+ context->function = exported_func;
+ return false;
+ }
+
+ // Keep going.
+ return true;
+}
+
+template <typename FunctionType>
+FunctionType FindFunctionInImports(const char* function_name) {
+ base::win::PEImage image(CURRENT_MODULE());
+
+ FunctionSearchContext ctx = { function_name, NULL };
+ image.EnumImportChunks(FindResolutionFunctionInImports, &ctx, nullptr);
+
+ return reinterpret_cast<FunctionType>(ctx.function);
+}
+
+} // namespace
+
+ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc() {
+ return FindFunctionInImports<ReturnAddressLocationResolver>(
+ "ResolveReturnAddressLocation");
+}
+
+AddDynamicSymbol GetProfilerAddDynamicSymbolFunc() {
+ return FindFunctionInImports<AddDynamicSymbol>(
+ "AddDynamicSymbol");
+}
+
+MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc() {
+ return FindFunctionInImports<MoveDynamicSymbol>(
+ "MoveDynamicSymbol");
+}
+
+#endif // defined(OS_WIN)
+
+} // namespace debug
+} // namespace base
diff --git a/security/sandbox/chromium/base/debug/profiler.h b/security/sandbox/chromium/base/debug/profiler.h
new file mode 100644
index 0000000000..1229e06234
--- /dev/null
+++ b/security/sandbox/chromium/base/debug/profiler.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_DEBUG_PROFILER_H_
+#define BASE_DEBUG_PROFILER_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+// The Profiler functions allow usage of the underlying sampling based
+// profiler. If the application has not been built with the necessary
+// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions
+// are noops.
+namespace base {
+namespace debug {
+
+// Start profiling with the supplied name.
+// {pid} will be replaced by the process' pid and {count} will be replaced
+// by the count of the profile run (starts at 1 with each process).
+BASE_EXPORT void StartProfiling(const std::string& name);
+
+// Stop profiling and write out data.
+BASE_EXPORT void StopProfiling();
+
+// Force data to be written to file.
+BASE_EXPORT void FlushProfiling();
+
+// Returns true if process is being profiled.
+BASE_EXPORT bool BeingProfiled();
+
+// Reset profiling after a fork, which disables timers.
+BASE_EXPORT void RestartProfilingAfterFork();
+
+// Returns true iff this executable supports profiling.
+BASE_EXPORT bool IsProfilingSupported();
+
+// There's a class of profilers that use "return address swizzling" to get a
+// hook on function exits. This class of profilers uses some form of entry hook,
+// like e.g. binary instrumentation, or a compiler flag, that calls a hook each
+// time a function is invoked. The hook then switches the return address on the
+// stack for the address of an exit hook function, and pushes the original
+// return address to a shadow stack of some type. When in due course the CPU
+// executes a return to the exit hook, the exit hook will do whatever work it
+// does on function exit, then arrange to return to the original return address.
+// This class of profiler does not play well with programs that look at the
+// return address, as does e.g. V8. V8 uses the return address to certain
+// runtime functions to find the JIT code that called it, and from there finds
+// the V8 data structures associated to the JS function involved.
+// A return address resolution function is used to fix this. It allows such
+// programs to resolve a location on stack where a return address originally
+// resided, to the shadow stack location where the profiler stashed it.
+typedef uintptr_t (*ReturnAddressLocationResolver)(
+ uintptr_t return_addr_location);
+
+typedef void (*AddDynamicSymbol)(const void* address,
+ size_t length,
+ const char* name,
+ size_t name_len);
+typedef void (*MoveDynamicSymbol)(const void* address, const void* new_address);
+
+
+// If this binary is instrumented and the instrumentation supplies a function
+// for each of those purposes, find and return the function in question.
+// Otherwise returns NULL.
+BASE_EXPORT ReturnAddressLocationResolver GetProfilerReturnAddrResolutionFunc();
+BASE_EXPORT AddDynamicSymbol GetProfilerAddDynamicSymbolFunc();
+BASE_EXPORT MoveDynamicSymbol GetProfilerMoveDynamicSymbolFunc();
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_PROFILER_H_
diff --git a/security/sandbox/chromium/base/environment.cc b/security/sandbox/chromium/base/environment.cc
new file mode 100644
index 0000000000..8e3afd8983
--- /dev/null
+++ b/security/sandbox/chromium/base/environment.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/environment.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <stdlib.h>
+#endif
+
+namespace base {
+
+namespace {
+
+class EnvironmentImpl : public Environment {
+ public:
+ bool GetVar(StringPiece variable_name, std::string* result) override {
+ if (GetVarImpl(variable_name, result))
+ return true;
+
+ // Some commonly used variable names are uppercase while others
+ // are lowercase, which is inconsistent. Let's try to be helpful
+ // and look for a variable name with the reverse case.
+ // I.e. HTTP_PROXY may be http_proxy for some users/systems.
+ char first_char = variable_name[0];
+ std::string alternate_case_var;
+ if (IsAsciiLower(first_char))
+ alternate_case_var = ToUpperASCII(variable_name);
+ else if (IsAsciiUpper(first_char))
+ alternate_case_var = ToLowerASCII(variable_name);
+ else
+ return false;
+ return GetVarImpl(alternate_case_var, result);
+ }
+
+ bool SetVar(StringPiece variable_name,
+ const std::string& new_value) override {
+ return SetVarImpl(variable_name, new_value);
+ }
+
+ bool UnSetVar(StringPiece variable_name) override {
+ return UnSetVarImpl(variable_name);
+ }
+
+ private:
+ bool GetVarImpl(StringPiece variable_name, std::string* result) {
+#if defined(OS_WIN)
+ DWORD value_length =
+ ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr, 0);
+ if (value_length == 0)
+ return false;
+ if (result) {
+ std::unique_ptr<wchar_t[]> value(new wchar_t[value_length]);
+ ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(),
+ value_length);
+ *result = WideToUTF8(value.get());
+ }
+ return true;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ const char* env_value = getenv(variable_name.data());
+ if (!env_value)
+ return false;
+ // Note that the variable may be defined but empty.
+ if (result)
+ *result = env_value;
+ return true;
+#endif
+ }
+
+ bool SetVarImpl(StringPiece variable_name, const std::string& new_value) {
+#if defined(OS_WIN)
+ // On success, a nonzero value is returned.
+ return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(),
+ UTF8ToWide(new_value).c_str());
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On success, zero is returned.
+ return !setenv(variable_name.data(), new_value.c_str(), 1);
+#endif
+ }
+
+ bool UnSetVarImpl(StringPiece variable_name) {
+#if defined(OS_WIN)
+ // On success, a nonzero value is returned.
+ return !!SetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On success, zero is returned.
+ return !unsetenv(variable_name.data());
+#endif
+ }
+};
+
+} // namespace
+
+namespace env_vars {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// On Posix systems, this variable contains the location of the user's home
+// directory. (e.g, /home/username/).
+const char kHome[] = "HOME";
+#endif
+
+} // namespace env_vars
+
+Environment::~Environment() = default;
+
+// static
+std::unique_ptr<Environment> Environment::Create() {
+ return std::make_unique<EnvironmentImpl>();
+}
+
+bool Environment::HasVar(StringPiece variable_name) {
+ return GetVar(variable_name, nullptr);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/environment.h b/security/sandbox/chromium/base/environment.h
new file mode 100644
index 0000000000..79529a2131
--- /dev/null
+++ b/security/sandbox/chromium/base/environment.h
@@ -0,0 +1,61 @@
+// 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_ENVIRONMENT_H_
+#define BASE_ENVIRONMENT_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace env_vars {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+BASE_EXPORT extern const char kHome[];
+#endif
+
+} // namespace env_vars
+
+class BASE_EXPORT Environment {
+ public:
+ virtual ~Environment();
+
+ // Returns the appropriate platform-specific instance.
+ static std::unique_ptr<Environment> Create();
+
+ // Gets an environment variable's value and stores it in |result|.
+ // Returns false if the key is unset.
+ virtual bool GetVar(StringPiece variable_name, std::string* result) = 0;
+
+ // Syntactic sugar for GetVar(variable_name, nullptr);
+ virtual bool HasVar(StringPiece variable_name);
+
+ // Returns true on success, otherwise returns false. This method should not
+ // be called in a multi-threaded process.
+ virtual bool SetVar(StringPiece variable_name,
+ const std::string& new_value) = 0;
+
+ // Returns true on success, otherwise returns false. This method should not
+ // be called in a multi-threaded process.
+ virtual bool UnSetVar(StringPiece variable_name) = 0;
+};
+
+#if defined(OS_WIN)
+using NativeEnvironmentString = std::wstring;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+using NativeEnvironmentString = std::string;
+#endif
+using EnvironmentMap =
+ std::map<NativeEnvironmentString, NativeEnvironmentString>;
+
+} // namespace base
+
+#endif // BASE_ENVIRONMENT_H_
diff --git a/security/sandbox/chromium/base/file_descriptor_posix.h b/security/sandbox/chromium/base/file_descriptor_posix.h
new file mode 100644
index 0000000000..cb32dde355
--- /dev/null
+++ b/security/sandbox/chromium/base/file_descriptor_posix.h
@@ -0,0 +1,61 @@
+// 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.
+
+#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_
+#define BASE_FILE_DESCRIPTOR_POSIX_H_
+
+#include "base/files/file.h"
+#include "base/files/scoped_file.h"
+
+namespace base {
+
+constexpr int kInvalidFd = -1;
+
+// -----------------------------------------------------------------------------
+// We introduct a special structure for file descriptors in order that we are
+// able to use template specialisation to special-case their handling.
+//
+// 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(kInvalidFd), auto_close(false) {}
+
+ FileDescriptor(int ifd, bool iauto_close) : fd(ifd), auto_close(iauto_close) {
+ }
+
+ FileDescriptor(File file) : fd(file.TakePlatformFile()), auto_close(true) {}
+ explicit FileDescriptor(ScopedFD fd) : fd(fd.release()), auto_close(true) {}
+
+ bool operator==(const FileDescriptor& other) const {
+ return (fd == other.fd && auto_close == other.auto_close);
+ }
+
+ bool operator!=(const FileDescriptor& other) const {
+ return !operator==(other);
+ }
+
+ // A comparison operator so that we can use these as keys in a std::map.
+ bool operator<(const FileDescriptor& other) const {
+ return other.fd < fd;
+ }
+
+ int fd;
+ // If true, this file descriptor should be closed after it has been used. For
+ // example an IPC system might interpret this flag as indicating that the
+ // file descriptor it has been given should be closed after use.
+ bool auto_close;
+};
+
+} // namespace base
+
+#endif // BASE_FILE_DESCRIPTOR_POSIX_H_
diff --git a/security/sandbox/chromium/base/files/file_path.h b/security/sandbox/chromium/base/files/file_path.h
new file mode 100644
index 0000000000..4e23f71a92
--- /dev/null
+++ b/security/sandbox/chromium/base/files/file_path.h
@@ -0,0 +1,484 @@
+// 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.
+
+// FilePath is a container for pathnames stored in a platform's native string
+// type, providing containers for manipulation in according with the
+// platform's conventions for pathnames. It supports the following path
+// types:
+//
+// POSIX Windows
+// --------------- ----------------------------------
+// Fundamental type char[] wchar_t[]
+// Encoding unspecified* UTF-16
+// Separator / \, tolerant of /
+// Drive letters no case-insensitive A-Z followed by :
+// Alternate root // (surprise!) \\, for UNC paths
+//
+// * The encoding need not be specified on POSIX systems, although some
+// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8.
+// Chrome OS also uses UTF-8.
+// Linux does not specify an encoding, but in practice, the locale's
+// character set may be used.
+//
+// For more arcane bits of path trivia, see below.
+//
+// FilePath objects are intended to be used anywhere paths are. An
+// application may pass FilePath objects around internally, masking the
+// underlying differences between systems, only differing in implementation
+// where interfacing directly with the system. For example, a single
+// OpenFile(const FilePath &) function may be made available, allowing all
+// callers to operate without regard to the underlying implementation. On
+// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might
+// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This
+// allows each platform to pass pathnames around without requiring conversions
+// between encodings, which has an impact on performance, but more imporantly,
+// has an impact on correctness on platforms that do not have well-defined
+// encodings for pathnames.
+//
+// Several methods are available to perform common operations on a FilePath
+// object, such as determining the parent directory (DirName), isolating the
+// final path component (BaseName), and appending a relative pathname string
+// to an existing FilePath object (Append). These methods are highly
+// recommended over attempting to split and concatenate strings directly.
+// These methods are based purely on string manipulation and knowledge of
+// platform-specific pathname conventions, and do not consult the filesystem
+// at all, making them safe to use without fear of blocking on I/O operations.
+// These methods do not function as mutators but instead return distinct
+// instances of FilePath objects, and are therefore safe to use on const
+// objects. The objects themselves are safe to share between threads.
+//
+// To aid in initialization of FilePath objects from string literals, a
+// FILE_PATH_LITERAL macro is provided, which accounts for the difference
+// between char[]-based pathnames on POSIX systems and wchar_t[]-based
+// pathnames on Windows.
+//
+// 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
+// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the
+// character array. Example:
+//
+// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt");
+// |
+// | void Function() {
+// | FilePath log_file_path(kLogFileName);
+// | [...]
+// | }
+//
+// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even
+// when the UI language is RTL. This means you always need to pass filepaths
+// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the
+// RTL UI.
+//
+// This is a very common source of bugs, please try to keep this in mind.
+//
+// ARCANE BITS OF PATH TRIVIA
+//
+// - A double leading slash is actually part of the POSIX standard. Systems
+// are allowed to treat // as an alternate root, as Windows does for UNC
+// (network share) paths. Most POSIX systems don't do anything special
+// with two leading slashes, but FilePath handles this case properly
+// 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.267 ("Pathname")
+// and 4.12 ("Pathname Resolution"), available at:
+// 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
+// allow older applications that require drive letters to support UNC paths
+// like \\server\share\path, by permitting c:\\server\share\path as an
+// equivalent. Since the OS treats these paths specially, FilePath needs
+// to do the same. Since Windows can use either / or \ as the separator,
+// FilePath treats c://, c:\\, //, and \\ all equivalently.
+// Reference:
+// The Old New Thing, "Why is a drive letter permitted in front of UNC
+// paths (sometimes)?", available at:
+// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx
+
+#ifndef BASE_FILES_FILE_PATH_H_
+#define BASE_FILES_FILE_PATH_H_
+
+#include <stddef.h>
+
+#include <functional>
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/stl_util.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+// Windows-style drive letter support and pathname separator characters can be
+// enabled and disabled independently, to aid testing. These #defines are
+// here so that the same setting can be used in both the implementation and
+// in the unit test.
+#if defined(OS_WIN)
+#define FILE_PATH_USES_DRIVE_LETTERS
+#define FILE_PATH_USES_WIN_SEPARATORS
+#endif // OS_WIN
+
+// To print path names portably use PRFilePath (based on PRIuS and friends from
+// C99 and format_macros.h) like this:
+// base::StringPrintf("Path is %" PRFilePath ".\n", path.value().c_str());
+#if defined(OS_WIN)
+#define PRFilePath "ls"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define PRFilePath "s"
+#endif // OS_WIN
+
+// Macros for string literal initialization of FilePath::CharType[].
+#if defined(OS_WIN)
+#define FILE_PATH_LITERAL(x) L##x
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define FILE_PATH_LITERAL(x) x
+#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 {
+ public:
+#if defined(OS_WIN)
+ // On Windows, for Unicode-aware applications, native pathnames are wchar_t
+ // arrays encoded in UTF-16.
+ typedef std::wstring StringType;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On most platforms, native pathnames are char arrays, and the encoding
+ // may or may not be specified. On Mac OS X, native pathnames are encoded
+ // in UTF-8.
+ typedef std::string StringType;
+#endif // OS_WIN
+
+ typedef BasicStringPiece<StringType> StringPieceType;
+ typedef StringType::value_type CharType;
+
+ // Null-terminated array of separators used to separate components in
+ // hierarchical paths. Each character in this array is a valid separator,
+ // but kSeparators[0] is treated as the canonical separator and will be used
+ // when composing pathnames.
+ static const CharType kSeparators[];
+
+ // base::size(kSeparators).
+ static const size_t kSeparatorsLength;
+
+ // A special path component meaning "this directory."
+ static const CharType kCurrentDirectory[];
+
+ // A special path component meaning "the parent directory."
+ static const CharType kParentDirectory[];
+
+ // The character used to identify a file extension.
+ static const CharType kExtensionSeparator;
+
+ FilePath();
+ FilePath(const FilePath& that);
+ explicit FilePath(StringPieceType path);
+ ~FilePath();
+ FilePath& operator=(const FilePath& that);
+
+ // Constructs FilePath with the contents of |that|, which is left in valid but
+ // unspecified state.
+ FilePath(FilePath&& that) noexcept;
+ // Replaces the contents with those of |that|, which is left in valid but
+ // unspecified state.
+ FilePath& operator=(FilePath&& that);
+
+ bool operator==(const FilePath& that) const;
+
+ bool operator!=(const FilePath& that) const;
+
+ // Required for some STL containers and operations
+ bool operator<(const FilePath& that) const {
+ return path_ < that.path_;
+ }
+
+ const StringType& value() const { return path_; }
+
+ bool empty() const { return path_.empty(); }
+
+ void clear() { path_.clear(); }
+
+ // Returns true if |character| is in kSeparators.
+ static bool IsSeparator(CharType character);
+
+ // Returns a vector of all of the components of the provided path. It is
+ // equivalent to calling DirName().value() on the path's root component,
+ // and BaseName().value() on each child component.
+ //
+ // To make sure this is lossless so we can differentiate absolute and
+ // relative paths, the root slash will be included even though no other
+ // slashes will be. The precise behavior is:
+ //
+ // Posix: "/foo/bar" -> [ "/", "foo", "bar" ]
+ // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ]
+ void GetComponents(std::vector<FilePath::StringType>* components) const;
+
+ // Returns true if this FilePath is a parent or ancestor of the |child|.
+ // Absolute and relative paths are accepted i.e. /foo is a parent to /foo/bar,
+ // and foo is a parent to foo/bar. Any ancestor is considered a parent i.e. /a
+ // is a parent to both /a/b and /a/b/c. Does not convert paths to absolute,
+ // follow symlinks or directory navigation (e.g. ".."). A path is *NOT* its
+ // own parent.
+ bool IsParent(const FilePath& child) const;
+
+ // If IsParent(child) holds, appends to path (if non-NULL) the
+ // relative path to child and returns true. For example, if parent
+ // holds "/Users/johndoe/Library/Application Support", child holds
+ // "/Users/johndoe/Library/Application Support/Google/Chrome/Default", and
+ // *path holds "/Users/johndoe/Library/Caches", then after
+ // parent.AppendRelativePath(child, path) is called *path will hold
+ // "/Users/johndoe/Library/Caches/Google/Chrome/Default". Otherwise,
+ // returns false.
+ bool AppendRelativePath(const FilePath& child, FilePath* path) const;
+
+ // Returns a FilePath corresponding to the directory containing the path
+ // named by this object, stripping away the file component. If this object
+ // only contains one component, returns a FilePath identifying
+ // kCurrentDirectory. If this object already refers to the root directory,
+ // returns a FilePath identifying the root directory. Please note that this
+ // doesn't resolve directory navigation, e.g. the result for "../a" is "..".
+ FilePath DirName() const WARN_UNUSED_RESULT;
+
+ // Returns a FilePath corresponding to the last path component of this
+ // object, either a file or a directory. If this object already refers to
+ // the root directory, returns a FilePath identifying the root directory;
+ // this is the only situation in which BaseName will return an absolute path.
+ FilePath BaseName() const WARN_UNUSED_RESULT;
+
+ // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
+ // the file has no extension. If non-empty, Extension() will always start
+ // with precisely one ".". The following code should always work regardless
+ // of the value of path. For common double-extensions like .tar.gz and
+ // .user.js, this method returns the combined extension. For a single
+ // component, use FinalExtension().
+ // new_path = path.RemoveExtension().value().append(path.Extension());
+ // 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 WARN_UNUSED_RESULT;
+
+ // Returns the path's file extension, as in Extension(), but will
+ // never return a double extension.
+ //
+ // TODO(davidben): Check all our extension-sensitive code to see if
+ // 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 WARN_UNUSED_RESULT;
+
+ // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
+ // NOTE: this is slightly different from the similar file_util implementation
+ // which returned simply 'jojo'.
+ FilePath RemoveExtension() const WARN_UNUSED_RESULT;
+
+ // Removes the path's file extension, as in RemoveExtension(), but
+ // ignores double extensions.
+ FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT;
+
+ // Inserts |suffix| after the file name portion of |path| but before the
+ // extension. Returns "" if BaseName() == "." or "..".
+ // Examples:
+ // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
+ // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
+ // 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(
+ StringPieceType suffix) const WARN_UNUSED_RESULT;
+ FilePath InsertBeforeExtensionASCII(
+ 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(StringPieceType extension) const WARN_UNUSED_RESULT;
+
+ // Like above, but takes the extension as an ASCII string. See AppendASCII for
+ // details on how this is handled.
+ FilePath AddExtensionASCII(StringPiece 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(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(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
+ // excessive separators if this object's path already ends with a separator.
+ // 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(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
+ // paths is well defined, it can handle ASCII path components as well.
+ // Mac uses UTF8, and since ASCII is a subset of that, it works there as well.
+ // 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(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
+ // a separator character, or with two separator characters. On POSIX
+ // platforms, an absolute path begins with a separator character.
+ bool IsAbsolute() const;
+
+ // Returns true if the patch ends with a path separator character.
+ bool EndsWithSeparator() const WARN_UNUSED_RESULT;
+
+ // Returns a copy of this FilePath that ends with a trailing separator. If
+ // the input path is empty, an empty FilePath will be returned.
+ FilePath AsEndingWithSeparator() const WARN_UNUSED_RESULT;
+
+ // Returns a copy of this FilePath that does not end with a trailing
+ // separator.
+ FilePath StripTrailingSeparators() const WARN_UNUSED_RESULT;
+
+ // Returns true if this FilePath contains an attempt to reference a parent
+ // directory (e.g. has a path component that is "..").
+ bool ReferencesParent() const;
+
+ // Return a Unicode human-readable version of this path.
+ // Warning: you can *not*, in general, go from a display name back to a real
+ // path. Only use this when displaying paths to users, not just when you
+ // want to stuff a string16 into some other API.
+ string16 LossyDisplayName() const;
+
+ // Return the path as ASCII, or the empty string if the path is not ASCII.
+ // This should only be used for cases where the FilePath is representing a
+ // known-ASCII filename.
+ std::string MaybeAsASCII() const;
+
+ // Return the path as UTF-8.
+ //
+ // This function is *unsafe* as there is no way to tell what encoding is
+ // used in file names on POSIX systems other than Mac and Chrome OS,
+ // although UTF-8 is practically used everywhere these days. To mitigate
+ // the encoding issue, this function internally calls
+ // SysNativeMBToWide() on POSIX systems other than Mac and Chrome OS,
+ // per assumption that the current locale's encoding is used in file
+ // names, but this isn't a perfect solution.
+ //
+ // Once it becomes safe to to stop caring about non-UTF-8 file names,
+ // the SysNativeMBToWide() hack will be removed from the code, along
+ // with "Unsafe" in the function name.
+ std::string AsUTF8Unsafe() const;
+
+ // Similar to AsUTF8Unsafe, but returns UTF-16 instead.
+ string16 AsUTF16Unsafe() const;
+
+ // Returns a FilePath object from a path name in UTF-8. This function
+ // should only be used for cases where you are sure that the input
+ // string is UTF-8.
+ //
+ // Like AsUTF8Unsafe(), this function is unsafe. This function
+ // internally calls SysWideToNativeMB() on POSIX systems other than Mac
+ // and Chrome OS, to mitigate the encoding issue. See the comment at
+ // AsUTF8Unsafe() for details.
+ static FilePath FromUTF8Unsafe(StringPiece utf8);
+
+ // Similar to FromUTF8Unsafe, but accepts UTF-16 instead.
+ static FilePath FromUTF16Unsafe(StringPiece16 utf16);
+
+ void WriteToPickle(Pickle* pickle) const;
+ bool ReadFromPickle(PickleIterator* iter);
+
+ // Normalize all path separators to backslash on Windows
+ // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
+ FilePath NormalizePathSeparators() const;
+
+ // Normalize all path separattors to given type on Windows
+ // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
+ FilePath NormalizePathSeparatorsTo(CharType separator) const;
+
+ // Compare two strings in the same way the file system does.
+ // Note that these always ignore case, even on file systems that are case-
+ // sensitive. If case-sensitive comparison is ever needed, add corresponding
+ // methods here.
+ // The methods are written as a static method so that they can also be used
+ // 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(StringPieceType string1,
+ StringPieceType string2);
+ static bool CompareEqualIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
+ return CompareIgnoreCase(string1, string2) == 0;
+ }
+ static bool CompareLessIgnoreCase(StringPieceType string1,
+ StringPieceType string2) {
+ return CompareIgnoreCase(string1, string2) < 0;
+ }
+
+#if defined(OS_MACOSX)
+ // Returns the string in the special canonical decomposed form as defined for
+ // HFS, which is close to, but not quite, decomposition form D. See
+ // 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(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(StringPieceType string1,
+ StringPieceType string2);
+#endif
+
+#if defined(OS_ANDROID)
+ // On android, file selection dialog can return a file with content uri
+ // scheme(starting with content://). Content uri needs to be opened with
+ // ContentResolver to guarantee that the app has appropriate permissions
+ // to access it.
+ // Returns true if the path is a content uri, or false otherwise.
+ bool IsContentUri() const;
+#endif
+
+ private:
+ // Remove trailing separators from this object. If the path is absolute, it
+ // will never be stripped any more than to refer to the absolute root
+ // directory, so "////" will become "/", not "". A leading pair of
+ // separators is never stripped, to support alternate roots. This is used to
+ // support UNC paths on Windows.
+ void StripTrailingSeparatorsInternal();
+
+ StringType path_;
+};
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+ const FilePath& file_path);
+
+} // namespace base
+
+namespace std {
+
+template <>
+struct hash<base::FilePath> {
+ typedef base::FilePath argument_type;
+ typedef std::size_t result_type;
+ result_type operator()(argument_type const& f) const {
+ return hash<base::FilePath::StringType>()(f.value());
+ }
+};
+
+} // namespace std
+
+#endif // BASE_FILES_FILE_PATH_H_
diff --git a/security/sandbox/chromium/base/files/file_path_constants.cc b/security/sandbox/chromium/base/files/file_path_constants.cc
new file mode 100644
index 0000000000..d2d8d31fe1
--- /dev/null
+++ b/security/sandbox/chromium/base/files/file_path_constants.cc
@@ -0,0 +1,25 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/files/file_path.h"
+#include "base/stl_util.h"
+
+namespace base {
+
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
+#else // FILE_PATH_USES_WIN_SEPARATORS
+const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
+#endif // FILE_PATH_USES_WIN_SEPARATORS
+
+const size_t FilePath::kSeparatorsLength = base::size(kSeparators);
+
+const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
+const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
+
+const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/format_macros.h b/security/sandbox/chromium/base/format_macros.h
new file mode 100644
index 0000000000..1279ff7816
--- /dev/null
+++ b/security/sandbox/chromium/base/format_macros.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FORMAT_MACROS_H_
+#define BASE_FORMAT_MACROS_H_
+
+// This file defines the format macros for some integer types.
+
+// To print a 64-bit value in a portable way:
+// int64_t value;
+// printf("xyz:%" PRId64, value);
+// The "d" in the macro corresponds to %d; you can also use PRIu64 etc.
+//
+// For wide strings, prepend "Wide" to the macro:
+// int64_t value;
+// StringPrintf(L"xyz: %" WidePRId64, value);
+//
+// To print a size_t value in a portable way:
+// size_t size;
+// printf("xyz: %" PRIuS, size);
+// The "u" in the macro corresponds to %u, and S is for "size".
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "build/build_config.h"
+
+#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && \
+ (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64)
+#error "inttypes.h has already been included before this header file, but "
+#error "without __STDC_FORMAT_MACROS defined."
+#endif
+
+#if (defined(OS_POSIX) || defined(OS_FUCHSIA)) && !defined(__STDC_FORMAT_MACROS)
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <inttypes.h>
+
+#if defined(OS_WIN)
+
+#if !defined(PRId64) || !defined(PRIu64) || !defined(PRIx64)
+#error "inttypes.h provided by win toolchain should define these."
+#endif
+
+#define WidePRId64 L"I64d"
+#define WidePRIu64 L"I64u"
+#define WidePRIx64 L"I64x"
+
+#if !defined(PRIuS)
+#define PRIuS "Iu"
+#endif
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+// GCC will concatenate wide and narrow strings correctly, so nothing needs to
+// be done here.
+#define WidePRId64 PRId64
+#define WidePRIu64 PRIu64
+#define WidePRIx64 PRIx64
+
+#if !defined(PRIuS)
+#define PRIuS "zu"
+#endif
+
+#endif // defined(OS_WIN)
+
+// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit
+// architectures and Apple does not provides standard format macros and
+// recommends casting. This has many drawbacks, so instead define macros
+// for formatting those types.
+#if defined(OS_MACOSX)
+#if defined(ARCH_CPU_64_BITS)
+#if !defined(PRIdNS)
+#define PRIdNS "ld"
+#endif
+#if !defined(PRIuNS)
+#define PRIuNS "lu"
+#endif
+#if !defined(PRIxNS)
+#define PRIxNS "lx"
+#endif
+#else // defined(ARCH_CPU_64_BITS)
+#if !defined(PRIdNS)
+#define PRIdNS "d"
+#endif
+#if !defined(PRIuNS)
+#define PRIuNS "u"
+#endif
+#if !defined(PRIxNS)
+#define PRIxNS "x"
+#endif
+#endif
+#endif // defined(OS_MACOSX)
+
+#endif // BASE_FORMAT_MACROS_H_
diff --git a/security/sandbox/chromium/base/guid.h b/security/sandbox/chromium/base/guid.h
new file mode 100644
index 0000000000..f50d3335aa
--- /dev/null
+++ b/security/sandbox/chromium/base/guid.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_GUID_H_
+#define BASE_GUID_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// Generate a 128-bit random GUID in the form of version 4 as described in
+// RFC 4122, section 4.4.
+// The format of GUID version 4 must be xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx,
+// where y is one of [8, 9, A, B].
+// The hexadecimal values "a" through "f" are output as lower case characters.
+//
+// A cryptographically secure random source will be used, but consider using
+// UnguessableToken for greater type-safety if GUID format is unnecessary.
+BASE_EXPORT std::string GenerateGUID();
+
+// Returns true if the input string conforms to the version 4 GUID format.
+// Note that this does NOT check if the hexadecimal values "a" through "f"
+// are in lower case characters, as Version 4 RFC says onput they're
+// case insensitive. (Use IsValidGUIDOutputString for checking if the
+// given string is valid output string)
+BASE_EXPORT bool IsValidGUID(base::StringPiece guid);
+BASE_EXPORT bool IsValidGUID(base::StringPiece16 guid);
+
+// Returns true if the input string is valid version 4 GUID output string.
+// This also checks if the hexadecimal values "a" through "f" are in lower
+// case characters.
+BASE_EXPORT bool IsValidGUIDOutputString(base::StringPiece guid);
+
+// For unit testing purposes only. Do not use outside of tests.
+BASE_EXPORT std::string RandomDataToGUIDString(const uint64_t bytes[2]);
+
+} // namespace base
+
+#endif // BASE_GUID_H_
diff --git a/security/sandbox/chromium/base/hash/hash.cc b/security/sandbox/chromium/base/hash/hash.cc
new file mode 100644
index 0000000000..c96f8bc843
--- /dev/null
+++ b/security/sandbox/chromium/base/hash/hash.cc
@@ -0,0 +1,167 @@
+// 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 "base/hash/hash.h"
+
+#include "base/rand_util.h"
+#include "base/third_party/cityhash/city.h"
+#include "build/build_config.h"
+
+// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party
+// code did not come with its own header file, so declaring the function here.)
+// Note: This algorithm is also in Blink under Source/wtf/StringHasher.h.
+extern "C" uint32_t SuperFastHash(const char* data, int len);
+
+namespace base {
+
+namespace {
+
+size_t FastHashImpl(base::span<const uint8_t> data) {
+ // We use the updated CityHash within our namespace (not the deprecated
+ // version from third_party/smhasher).
+#if defined(ARCH_CPU_64_BITS)
+ return base::internal::cityhash_v111::CityHash64(
+ reinterpret_cast<const char*>(data.data()), data.size());
+#else
+ return base::internal::cityhash_v111::CityHash32(
+ reinterpret_cast<const char*>(data.data()), data.size());
+#endif
+}
+
+// Implement hashing for pairs of at-most 32 bit integer values.
+// When size_t is 32 bits, we turn the 64-bit hash code into 32 bits by using
+// multiply-add hashing. This algorithm, as described in
+// Theorem 4.3.3 of the thesis "Über die Komplexität der Multiplikation in
+// eingeschränkten Branchingprogrammmodellen" by Woelfel, is:
+//
+// h32(x32, y32) = (h64(x32, y32) * rand_odd64 + rand16 * 2^16) % 2^64 / 2^32
+//
+// Contact danakj@chromium.org for any questions.
+size_t HashInts32Impl(uint32_t value1, uint32_t value2) {
+ uint64_t value1_64 = value1;
+ uint64_t hash64 = (value1_64 << 32) | value2;
+
+ if (sizeof(size_t) >= sizeof(uint64_t))
+ return static_cast<size_t>(hash64);
+
+ uint64_t odd_random = 481046412LL << 32 | 1025306955LL;
+ uint32_t shift_random = 10121U << 16;
+
+ hash64 = hash64 * odd_random + shift_random;
+ size_t high_bits =
+ static_cast<size_t>(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t))));
+ return high_bits;
+}
+
+// Implement hashing for pairs of up-to 64-bit integer values.
+// We use the compound integer hash method to produce a 64-bit hash code, by
+// 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.
+size_t HashInts64Impl(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_t value1a = static_cast<uint32_t>(value1 & 0xffffffff);
+ uint32_t value1b = static_cast<uint32_t>((value1 >> 32) & 0xffffffff);
+ uint32_t value2a = static_cast<uint32_t>(value2 & 0xffffffff);
+ uint32_t value2b = static_cast<uint32_t>((value2 >> 32) & 0xffffffff);
+
+ uint64_t product1 = static_cast<uint64_t>(value1a) * short_random1;
+ uint64_t product2 = static_cast<uint64_t>(value1b) * short_random2;
+ uint64_t product3 = static_cast<uint64_t>(value2a) * short_random3;
+ uint64_t product4 = static_cast<uint64_t>(value2b) * short_random4;
+
+ uint64_t hash64 = product1 + product2 + product3 + product4;
+
+ if (sizeof(size_t) >= sizeof(uint64_t))
+ return static_cast<size_t>(hash64);
+
+ uint64_t odd_random = 1578233944LL << 32 | 194370989LL;
+ uint32_t shift_random = 20591U << 16;
+
+ hash64 = hash64 * odd_random + shift_random;
+ size_t high_bits =
+ static_cast<size_t>(hash64 >> (8 * (sizeof(uint64_t) - sizeof(size_t))));
+ return high_bits;
+}
+
+// The random seed is used to perturb the output of base::FastHash() and
+// base::HashInts() so that it is only deterministic within the lifetime of a
+// process. This prevents inadvertent dependencies on the underlying
+// implementation, e.g. anything that persists the hash value and expects it to
+// be unchanging will break.
+//
+// Note: this is the same trick absl uses to generate a random seed. This is
+// more robust than using base::RandBytes(), which can fail inside a sandboxed
+// environment. Note that without ASLR, the seed won't be quite as random...
+#if DCHECK_IS_ON()
+constexpr const void* kSeed = &kSeed;
+#endif
+
+template <typename T>
+T Scramble(T input) {
+#if DCHECK_IS_ON()
+ return HashInts64Impl(input, reinterpret_cast<uintptr_t>(kSeed));
+#else
+ return input;
+#endif
+}
+
+} // namespace
+
+size_t FastHash(base::span<const uint8_t> data) {
+ return Scramble(FastHashImpl(data));
+}
+
+uint32_t Hash(const void* data, size_t length) {
+ // Currently our in-memory hash is the same as the persistent hash. The
+ // split between in-memory and persistent hash functions is maintained to
+ // allow the in-memory hash function to be updated in the future.
+ return PersistentHash(data, length);
+}
+
+uint32_t Hash(const std::string& str) {
+ return PersistentHash(as_bytes(make_span(str)));
+}
+
+uint32_t Hash(const string16& str) {
+ return PersistentHash(as_bytes(make_span(str)));
+}
+
+uint32_t PersistentHash(span<const uint8_t> data) {
+ // This hash function must not change, since it is designed to be persistable
+ // to disk.
+ if (data.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ NOTREACHED();
+ return 0;
+ }
+ return ::SuperFastHash(reinterpret_cast<const char*>(data.data()),
+ static_cast<int>(data.size()));
+}
+
+uint32_t PersistentHash(const void* data, size_t length) {
+ return PersistentHash(make_span(static_cast<const uint8_t*>(data), length));
+}
+
+uint32_t PersistentHash(const std::string& str) {
+ return PersistentHash(str.data(), str.size());
+}
+
+size_t HashInts32(uint32_t value1, uint32_t value2) {
+ return Scramble(HashInts32Impl(value1, value2));
+}
+
+// Implement hashing for pairs of up-to 64-bit integer values.
+// We use the compound integer hash method to produce a 64-bit hash code, by
+// 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.
+size_t HashInts64(uint64_t value1, uint64_t value2) {
+ return Scramble(HashInts64Impl(value1, value2));
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/hash/hash.h b/security/sandbox/chromium/base/hash/hash.h
new file mode 100644
index 0000000000..f55ae45b38
--- /dev/null
+++ b/security/sandbox/chromium/base/hash/hash.h
@@ -0,0 +1,86 @@
+// 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_HASH_HASH_H_
+#define BASE_HASH_HASH_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <string>
+#include <utility>
+
+#include "base/base_export.h"
+#include "base/containers/span.h"
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// WARNING: This hash functions should not be used for any cryptographic
+// purpose.
+
+// Deprecated: Computes a hash of a memory buffer, use FastHash() instead.
+// If you need to persist a change on disk or between computers, use
+// PersistentHash().
+// TODO(https://crbug.com/1025358): Migrate client code to new hash function.
+BASE_EXPORT uint32_t Hash(const void* data, size_t length);
+BASE_EXPORT uint32_t Hash(const std::string& str);
+BASE_EXPORT uint32_t Hash(const string16& str);
+
+// Really *fast* and high quality hash.
+// Recommended hash function for general use, we pick the best performant
+// hash for each build target.
+// It is prone to be updated whenever a newer/faster hash function is
+// publicly available.
+// May changed without warning, do not expect stability of outputs.
+BASE_EXPORT size_t FastHash(base::span<const uint8_t> data);
+inline size_t FastHash(StringPiece str) {
+ return FastHash(as_bytes(make_span(str)));
+}
+
+// Computes a hash of a memory buffer. This hash function must not change so
+// that code can use the hashed values for persistent storage purposes or
+// sending across the network. If a new persistent hash function is desired, a
+// new version will have to be added in addition.
+//
+// WARNING: This hash function should not be used for any cryptographic purpose.
+BASE_EXPORT uint32_t PersistentHash(base::span<const uint8_t> data);
+BASE_EXPORT uint32_t PersistentHash(const void* data, size_t length);
+BASE_EXPORT uint32_t PersistentHash(const std::string& str);
+
+// Hash pairs of 32-bit or 64-bit numbers.
+BASE_EXPORT size_t HashInts32(uint32_t value1, uint32_t value2);
+BASE_EXPORT size_t HashInts64(uint64_t value1, uint64_t value2);
+
+template <typename T1, typename T2>
+inline size_t HashInts(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(static_cast<uint32_t>(value1),
+ static_cast<uint32_t>(value2));
+}
+
+// A templated hasher for pairs of integer types. Example:
+//
+// using MyPair = std::pair<int32_t, int32_t>;
+// std::unordered_set<MyPair, base::IntPairHash<MyPair>> set;
+template <typename T>
+struct IntPairHash;
+
+template <typename Type1, typename Type2>
+struct IntPairHash<std::pair<Type1, Type2>> {
+ size_t operator()(std::pair<Type1, Type2> value) const {
+ return HashInts(value.first, value.second);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_HASH_HASH_H_
diff --git a/security/sandbox/chromium/base/immediate_crash.h b/security/sandbox/chromium/base/immediate_crash.h
new file mode 100644
index 0000000000..733110a7ac
--- /dev/null
+++ b/security/sandbox/chromium/base/immediate_crash.h
@@ -0,0 +1,168 @@
+// Copyright 2019 The Chromium Authors. 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_IMMEDIATE_CRASH_H_
+#define BASE_IMMEDIATE_CRASH_H_
+
+#include "build/build_config.h"
+
+// Crashes in the fastest possible way with no attempt at logging.
+// There are several constraints; see http://crbug.com/664209 for more context.
+//
+// - TRAP_SEQUENCE_() must be fatal. It should not be possible to ignore the
+// resulting exception or simply hit 'continue' to skip over it in a debugger.
+// - Different instances of TRAP_SEQUENCE_() must not be folded together, to
+// ensure crash reports are debuggable. Unlike __builtin_trap(), asm volatile
+// blocks will not be folded together.
+// Note: TRAP_SEQUENCE_() previously required an instruction with a unique
+// nonce since unlike clang, GCC folds together identical asm volatile
+// blocks.
+// - TRAP_SEQUENCE_() must produce a signal that is distinct from an invalid
+// memory access.
+// - TRAP_SEQUENCE_() must be treated as a set of noreturn instructions.
+// __builtin_unreachable() is used to provide that hint here. clang also uses
+// this as a heuristic to pack the instructions in the function epilogue to
+// improve code density.
+//
+// Additional properties that are nice to have:
+// - TRAP_SEQUENCE_() should be as compact as possible.
+// - The first instruction of TRAP_SEQUENCE_() should not change, to avoid
+// shifting crash reporting clusters. As a consequence of this, explicit
+// assembly is preferred over intrinsics.
+// Note: this last bullet point may no longer be true, and may be removed in
+// the future.
+
+// Note: TRAP_SEQUENCE Is currently split into two macro helpers due to the fact
+// that clang emits an actual instruction for __builtin_unreachable() on certain
+// platforms (see https://crbug.com/958675). In addition, the int3/bkpt/brk will
+// be removed in followups, so splitting it up like this now makes it easy to
+// land the followups.
+
+#if defined(COMPILER_GCC)
+
+#if defined(OS_NACL)
+
+// Crash report accuracy is not guaranteed on NaCl.
+#define TRAP_SEQUENCE1_() __builtin_trap()
+#define TRAP_SEQUENCE2_() asm volatile("")
+
+#elif defined(ARCH_CPU_X86_FAMILY)
+
+// TODO(https://crbug.com/958675): In theory, it should be possible to use just
+// int3. However, there are a number of crashes with SIGILL as the exception
+// code, so it seems likely that there's a signal handler that allows execution
+// to continue after SIGTRAP.
+#define TRAP_SEQUENCE1_() asm volatile("int3")
+
+#if defined(OS_MACOSX)
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Mac.
+#define TRAP_SEQUENCE2_() asm volatile("")
+#else
+#define TRAP_SEQUENCE2_() asm volatile("ud2")
+#endif // defined(OS_MACOSX)
+
+#elif defined(ARCH_CPU_ARMEL)
+
+// bkpt will generate a SIGBUS when running on armv7 and a SIGTRAP when running
+// as a 32 bit userspace app on arm64. There doesn't seem to be any way to
+// cause a SIGTRAP from userspace without using a syscall (which would be a
+// problem for sandboxing).
+// TODO(https://crbug.com/958675): Remove bkpt from this sequence.
+#define TRAP_SEQUENCE1_() asm volatile("bkpt #0")
+#define TRAP_SEQUENCE2_() asm volatile("udf #0")
+
+#elif defined(ARCH_CPU_ARM64)
+
+// This will always generate a SIGTRAP on arm64.
+// TODO(https://crbug.com/958675): Remove brk from this sequence.
+#define TRAP_SEQUENCE1_() asm volatile("brk #0")
+#define TRAP_SEQUENCE2_() asm volatile("hlt #0")
+
+#else
+
+// Crash report accuracy will not be guaranteed on other architectures, but at
+// least this will crash as expected.
+#define TRAP_SEQUENCE1_() __builtin_trap()
+#define TRAP_SEQUENCE2_() asm volatile("")
+
+#endif // ARCH_CPU_*
+
+#elif defined(COMPILER_MSVC)
+
+#if !defined(__clang__)
+
+// MSVC x64 doesn't support inline asm, so use the MSVC intrinsic.
+#define TRAP_SEQUENCE1_() __debugbreak()
+#define TRAP_SEQUENCE2_()
+
+#elif defined(ARCH_CPU_ARM64)
+
+// Windows ARM64 uses "BRK #F000" as its breakpoint instruction, and
+// __debugbreak() generates that in both VC++ and clang.
+#define TRAP_SEQUENCE1_() __debugbreak()
+// Intentionally empty: __builtin_unreachable() is always part of the sequence
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
+// https://crbug.com/958373
+#define TRAP_SEQUENCE2_() __asm volatile("")
+
+#else
+
+#define TRAP_SEQUENCE1_() asm volatile("int3")
+#define TRAP_SEQUENCE2_() asm volatile("ud2")
+
+#endif // __clang__
+
+#else
+
+#error No supported trap sequence!
+
+#endif // COMPILER_GCC
+
+#define TRAP_SEQUENCE_() \
+ do { \
+ TRAP_SEQUENCE1_(); \
+ TRAP_SEQUENCE2_(); \
+ } while (false)
+
+// CHECK() and the trap sequence can be invoked from a constexpr function.
+// This could make compilation fail on GCC, as it forbids directly using inline
+// asm inside a constexpr function. However, it allows calling a lambda
+// expression including the same asm.
+// The side effect is that the top of the stacktrace will not point to the
+// calling function, but to this anonymous lambda. This is still useful as the
+// full name of the lambda will typically include the name of the function that
+// calls CHECK() and the debugger will still break at the right line of code.
+#if !defined(COMPILER_GCC)
+
+#define WRAPPED_TRAP_SEQUENCE_() TRAP_SEQUENCE_()
+
+#else
+
+#define WRAPPED_TRAP_SEQUENCE_() \
+ do { \
+ [] { TRAP_SEQUENCE_(); }(); \
+ } while (false)
+
+#endif // !defined(COMPILER_GCC)
+
+#if defined(__clang__) || defined(COMPILER_GCC)
+
+// __builtin_unreachable() hints to the compiler that this is noreturn and can
+// be packed in the function epilogue.
+#define IMMEDIATE_CRASH() \
+ ({ \
+ WRAPPED_TRAP_SEQUENCE_(); \
+ __builtin_unreachable(); \
+ })
+
+#else
+
+// This is supporting non-chromium user of logging.h to build with MSVC, like
+// pdfium. On MSVC there is no __builtin_unreachable().
+#define IMMEDIATE_CRASH() WRAPPED_TRAP_SEQUENCE_()
+
+#endif // defined(__clang__) || defined(COMPILER_GCC)
+
+#endif // BASE_IMMEDIATE_CRASH_H_
diff --git a/security/sandbox/chromium/base/lazy_instance.h b/security/sandbox/chromium/base/lazy_instance.h
new file mode 100644
index 0000000000..4449373ead
--- /dev/null
+++ b/security/sandbox/chromium/base/lazy_instance.h
@@ -0,0 +1,210 @@
+// 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.
+//
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DEPRECATED !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// Please don't introduce new instances of LazyInstance<T>. Use a function-local
+// static of type base::NoDestructor<T> instead:
+//
+// Factory& Factory::GetInstance() {
+// static base::NoDestructor<Factory> instance;
+// return *instance;
+// }
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+//
+// The LazyInstance<Type, Traits> class manages a single instance of Type,
+// which will be lazily created on the first time it's accessed. This class is
+// useful for places you would normally use a function-level static, but you
+// need to have guaranteed thread-safety. The Type constructor will only ever
+// be called once, even if two threads are racing to create the object. Get()
+// and Pointer() will always return the same, completely initialized instance.
+// When the instance is constructed it is registered with AtExitManager. The
+// destructor will be called on program exit.
+//
+// LazyInstance is completely thread safe, assuming that you create it safely.
+// The class was designed to be POD initialized, so it shouldn't require a
+// static constructor. It really only makes sense to declare a LazyInstance as
+// a global variable using the LAZY_INSTANCE_INITIALIZER initializer.
+//
+// LazyInstance is similar to Singleton, except it does not have the singleton
+// property. You can have multiple LazyInstance's of the same type, and each
+// will manage a unique instance. It also preallocates the space for Type, as
+// to avoid allocating the Type instance on the heap. This may help with the
+// performance of creating the instance, and reducing heap fragmentation. This
+// requires that Type be a complete type so we can determine the size.
+//
+// Example usage:
+// static LazyInstance<MyClass>::Leaky inst = LAZY_INSTANCE_INITIALIZER;
+// void SomeMethod() {
+// inst.Get().SomeMethod(); // MyClass::SomeMethod()
+//
+// MyClass* ptr = inst.Pointer();
+// ptr->DoDoDo(); // MyClass::DoDoDo
+// }
+
+#ifndef BASE_LAZY_INSTANCE_H_
+#define BASE_LAZY_INSTANCE_H_
+
+#include <new> // For placement new.
+
+#include "base/atomicops.h"
+#include "base/debug/leak_annotations.h"
+#include "base/lazy_instance_helpers.h"
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+
+// LazyInstance uses its own struct initializer-list style static
+// initialization, which does not require a constructor.
+#define LAZY_INSTANCE_INITIALIZER {}
+
+namespace base {
+
+template <typename Type>
+struct LazyInstanceTraitsBase {
+ static Type* New(void* instance) {
+ DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (alignof(Type) - 1), 0u);
+ // Use placement new to initialize our instance in our preallocated space.
+ // The parenthesis is very important here to force POD type initialization.
+ return new (instance) Type();
+ }
+
+ static void CallDestructor(Type* instance) {
+ // Explicitly call the destructor.
+ instance->~Type();
+ }
+};
+
+// We pull out some of the functionality into non-templated functions, so we
+// can implement the more complicated pieces out of line in the .cc file.
+namespace internal {
+
+// This traits class causes destruction the contained Type at process exit via
+// AtExitManager. This is probably generally not what you want. Instead, prefer
+// Leaky below.
+template <typename Type>
+struct DestructorAtExitLazyInstanceTraits {
+ static const bool kRegisterOnExit = true;
+#if DCHECK_IS_ON()
+ static const bool kAllowedToAccessOnNonjoinableThread = false;
+#endif
+
+ static Type* New(void* instance) {
+ return LazyInstanceTraitsBase<Type>::New(instance);
+ }
+
+ static void Delete(Type* instance) {
+ LazyInstanceTraitsBase<Type>::CallDestructor(instance);
+ }
+};
+
+// Use LazyInstance<T>::Leaky for a less-verbose call-site typedef; e.g.:
+// base::LazyInstance<T>::Leaky my_leaky_lazy_instance;
+// instead of:
+// base::LazyInstance<T, base::internal::LeakyLazyInstanceTraits<T> >
+// my_leaky_lazy_instance;
+// (especially when T is MyLongTypeNameImplClientHolderFactory).
+// Only use this internal::-qualified verbose form to extend this traits class
+// (depending on its implementation details).
+template <typename Type>
+struct LeakyLazyInstanceTraits {
+ static const bool kRegisterOnExit = false;
+#if DCHECK_IS_ON()
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+#endif
+
+ static Type* New(void* instance) {
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ return LazyInstanceTraitsBase<Type>::New(instance);
+ }
+ static void Delete(Type* instance) {
+ }
+};
+
+template <typename Type>
+struct ErrorMustSelectLazyOrDestructorAtExitForLazyInstance {};
+
+} // namespace internal
+
+template <
+ typename Type,
+ typename Traits =
+ internal::ErrorMustSelectLazyOrDestructorAtExitForLazyInstance<Type>>
+class LazyInstance {
+ public:
+ // Do not define a destructor, as doing so makes LazyInstance a
+ // non-POD-struct. We don't want that because then a static initializer will
+ // be created to register the (empty) destructor with atexit() under MSVC, for
+ // example. We handle destruction of the contained Type class explicitly via
+ // the OnExit member function, where needed.
+ // ~LazyInstance() {}
+
+ // Convenience typedef to avoid having to repeat Type for leaky lazy
+ // instances.
+ typedef LazyInstance<Type, internal::LeakyLazyInstanceTraits<Type>> Leaky;
+ typedef LazyInstance<Type, internal::DestructorAtExitLazyInstanceTraits<Type>>
+ DestructorAtExit;
+
+ Type& Get() {
+ return *Pointer();
+ }
+
+ Type* Pointer() {
+#if DCHECK_IS_ON()
+ if (!Traits::kAllowedToAccessOnNonjoinableThread)
+ ThreadRestrictions::AssertSingletonAllowed();
+#endif
+
+ return subtle::GetOrCreateLazyPointer(
+ &private_instance_, &Traits::New, private_buf_,
+ Traits::kRegisterOnExit ? OnExit : nullptr, this);
+ }
+
+ // Returns true if the lazy instance has been created. Unlike Get() and
+ // Pointer(), calling IsCreated() will not instantiate the object of Type.
+ bool IsCreated() {
+ // Return true (i.e. "created") if |private_instance_| is either being
+ // created right now (i.e. |private_instance_| has value of
+ // internal::kLazyInstanceStateCreating) or was already created (i.e.
+ // |private_instance_| has any other non-zero value).
+ return 0 != subtle::NoBarrier_Load(&private_instance_);
+ }
+
+ // MSVC gives a warning that the alignment expands the size of the
+ // LazyInstance struct to make the size a multiple of the alignment. This
+ // is expected in this case.
+#if defined(OS_WIN)
+#pragma warning(push)
+#pragma warning(disable: 4324)
+#endif
+
+ // Effectively private: member data is only public to allow the linker to
+ // statically initialize it and to maintain a POD class. DO NOT USE FROM
+ // OUTSIDE THIS CLASS.
+ subtle::AtomicWord private_instance_;
+
+ // Preallocated space for the Type instance.
+ alignas(Type) char private_buf_[sizeof(Type)];
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+ private:
+ Type* instance() {
+ return reinterpret_cast<Type*>(subtle::NoBarrier_Load(&private_instance_));
+ }
+
+ // Adapter function for use with AtExit. This should be called single
+ // threaded, so don't synchronize across threads.
+ // Calling OnExit while the instance is in use by other threads is a mistake.
+ static void OnExit(void* lazy_instance) {
+ LazyInstance<Type, Traits>* me =
+ reinterpret_cast<LazyInstance<Type, Traits>*>(lazy_instance);
+ Traits::Delete(me->instance());
+ subtle::NoBarrier_Store(&me->private_instance_, 0);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_LAZY_INSTANCE_H_
diff --git a/security/sandbox/chromium/base/lazy_instance_helpers.cc b/security/sandbox/chromium/base/lazy_instance_helpers.cc
new file mode 100644
index 0000000000..7b9e0de7c6
--- /dev/null
+++ b/security/sandbox/chromium/base/lazy_instance_helpers.cc
@@ -0,0 +1,64 @@
+// Copyright 2018 The Chromium Authors. 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/lazy_instance_helpers.h"
+
+#include "base/at_exit.h"
+#include "base/atomicops.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+namespace internal {
+
+bool NeedsLazyInstance(subtle::AtomicWord* state) {
+ // Try to create the instance, if we're the first, will go from 0 to
+ // kLazyInstanceStateCreating, otherwise we've already been beaten here.
+ // The memory access has no memory ordering as state 0 and
+ // kLazyInstanceStateCreating have no associated data (memory barriers are
+ // all about ordering of memory accesses to *associated* data).
+ if (subtle::NoBarrier_CompareAndSwap(state, 0, kLazyInstanceStateCreating) ==
+ 0) {
+ // Caller must create instance
+ return true;
+ }
+
+ // It's either in the process of being created, or already created. Spin.
+ // The load has acquire memory ordering as a thread which sees
+ // state_ == STATE_CREATED needs to acquire visibility over
+ // the associated data (buf_). Pairing Release_Store is in
+ // CompleteLazyInstance().
+ if (subtle::Acquire_Load(state) == kLazyInstanceStateCreating) {
+ const base::TimeTicks start = base::TimeTicks::Now();
+ do {
+ const base::TimeDelta elapsed = base::TimeTicks::Now() - start;
+ // Spin with YieldCurrentThread for at most one ms - this ensures maximum
+ // responsiveness. After that spin with Sleep(1ms) so that we don't burn
+ // excessive CPU time - this also avoids infinite loops due to priority
+ // inversions (https://crbug.com/797129).
+ if (elapsed < TimeDelta::FromMilliseconds(1))
+ PlatformThread::YieldCurrentThread();
+ else
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
+ } while (subtle::Acquire_Load(state) == kLazyInstanceStateCreating);
+ }
+ // Someone else created the instance.
+ return false;
+}
+
+void CompleteLazyInstance(subtle::AtomicWord* state,
+ subtle::AtomicWord new_instance,
+ void (*destructor)(void*),
+ void* destructor_arg) {
+ // Instance is created, go from CREATING to CREATED (or reset it if
+ // |new_instance| is null). Releases visibility over |private_buf_| to
+ // readers. Pairing Acquire_Load is in NeedsLazyInstance().
+ subtle::Release_Store(state, new_instance);
+
+ // Make sure that the lazily instantiated object will get destroyed at exit.
+ if (new_instance && destructor)
+ AtExitManager::RegisterCallback(destructor, destructor_arg);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/lazy_instance_helpers.h b/security/sandbox/chromium/base/lazy_instance_helpers.h
new file mode 100644
index 0000000000..5a43d8b1f2
--- /dev/null
+++ b/security/sandbox/chromium/base/lazy_instance_helpers.h
@@ -0,0 +1,101 @@
+// Copyright 2018 The Chromium Authors. 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_LAZY_INSTANCE_INTERNAL_H_
+#define BASE_LAZY_INSTANCE_INTERNAL_H_
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/logging.h"
+
+// Helper methods used by LazyInstance and a few other base APIs for thread-safe
+// lazy construction.
+
+namespace base {
+namespace internal {
+
+// Our AtomicWord doubles as a spinlock, where a value of
+// kLazyInstanceStateCreating means the spinlock is being held for creation.
+constexpr subtle::AtomicWord kLazyInstanceStateCreating = 1;
+
+// Helper for GetOrCreateLazyPointer(). Checks if instance needs to be created.
+// If so returns true otherwise if another thread has beat us, waits for
+// instance to be created and returns false.
+BASE_EXPORT bool NeedsLazyInstance(subtle::AtomicWord* state);
+
+// Helper for GetOrCreateLazyPointer(). After creating an instance, this is
+// called to register the dtor to be called at program exit and to update the
+// atomic state to hold the |new_instance|
+BASE_EXPORT void CompleteLazyInstance(subtle::AtomicWord* state,
+ subtle::AtomicWord new_instance,
+ void (*destructor)(void*),
+ void* destructor_arg);
+
+} // namespace internal
+
+namespace subtle {
+
+// If |state| is uninitialized (zero), constructs a value using
+// |creator_func(creator_arg)|, stores it into |state| and registers
+// |destructor(destructor_arg)| to be called when the current AtExitManager goes
+// out of scope. Then, returns the value stored in |state|. It is safe to have
+// concurrent calls to this function with the same |state|. |creator_func| may
+// return nullptr if it doesn't want to create an instance anymore (e.g. on
+// shutdown), it is from then on required to return nullptr to all callers (ref.
+// StaticMemorySingletonTraits). In that case, callers need to synchronize
+// before |creator_func| may return a non-null instance again (ref.
+// StaticMemorySingletonTraits::ResurectForTesting()).
+// Implementation note on |creator_func/creator_arg|. It makes for ugly adapters
+// but it avoids redundant template instantiations (e.g. saves 27KB in
+// chrome.dll) because linker is able to fold these for multiple Types but
+// couldn't with the more advanced CreatorFunc template type which in turn
+// improves code locality (and application startup) -- ref.
+// https://chromium-review.googlesource.com/c/chromium/src/+/530984/5/base/lazy_instance.h#140,
+// worsened by https://chromium-review.googlesource.com/c/chromium/src/+/868013
+// and caught then as https://crbug.com/804034.
+template <typename Type>
+Type* GetOrCreateLazyPointer(subtle::AtomicWord* state,
+ Type* (*creator_func)(void*),
+ void* creator_arg,
+ void (*destructor)(void*),
+ void* destructor_arg) {
+ DCHECK(state);
+ DCHECK(creator_func);
+
+ // If any bit in the created mask is true, the instance has already been
+ // fully constructed.
+ constexpr subtle::AtomicWord kLazyInstanceCreatedMask =
+ ~internal::kLazyInstanceStateCreating;
+
+ // We will hopefully have fast access when the instance is already created.
+ // Since a thread sees |state| == 0 or kLazyInstanceStateCreating at most
+ // once, the load is taken out of NeedsLazyInstance() as a fast-path. The load
+ // has acquire memory ordering as a thread which sees |state| > creating needs
+ // to acquire visibility over the associated data. Pairing Release_Store is in
+ // CompleteLazyInstance().
+ subtle::AtomicWord instance = subtle::Acquire_Load(state);
+ if (!(instance & kLazyInstanceCreatedMask)) {
+ if (internal::NeedsLazyInstance(state)) {
+ // This thread won the race and is now responsible for creating the
+ // instance and storing it back into |state|.
+ instance =
+ reinterpret_cast<subtle::AtomicWord>((*creator_func)(creator_arg));
+ internal::CompleteLazyInstance(state, instance, destructor,
+ destructor_arg);
+ } else {
+ // This thread lost the race but now has visibility over the constructed
+ // instance (NeedsLazyInstance() doesn't return until the constructing
+ // thread releases the instance via CompleteLazyInstance()).
+ instance = subtle::Acquire_Load(state);
+ DCHECK(instance & kLazyInstanceCreatedMask);
+ }
+ }
+ return reinterpret_cast<Type*>(instance);
+}
+
+} // namespace subtle
+
+} // namespace base
+
+#endif // BASE_LAZY_INSTANCE_INTERNAL_H_
diff --git a/security/sandbox/chromium/base/location.cc b/security/sandbox/chromium/base/location.cc
new file mode 100644
index 0000000000..cf189341c7
--- /dev/null
+++ b/security/sandbox/chromium/base/location.cc
@@ -0,0 +1,96 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/location.h"
+
+#if defined(COMPILER_MSVC)
+#include <intrin.h>
+#endif
+
+#include "base/compiler_specific.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+
+namespace base {
+
+Location::Location() = default;
+Location::Location(const Location& other) = default;
+
+Location::Location(const char* file_name, const void* program_counter)
+ : file_name_(file_name), program_counter_(program_counter) {}
+
+Location::Location(const char* function_name,
+ const char* file_name,
+ int line_number,
+ const void* program_counter)
+ : function_name_(function_name),
+ file_name_(file_name),
+ line_number_(line_number),
+ program_counter_(program_counter) {
+#if !defined(OS_NACL)
+ // The program counter should not be null except in a default constructed
+ // (empty) Location object. This value is used for identity, so if it doesn't
+ // uniquely identify a location, things will break.
+ //
+ // The program counter isn't supported in NaCl so location objects won't work
+ // properly in that context.
+ DCHECK(program_counter);
+#endif
+}
+
+std::string Location::ToString() const {
+ if (has_source_info()) {
+ return std::string(function_name_) + "@" + file_name_ + ":" +
+ NumberToString(line_number_);
+ }
+ return StringPrintf("pc:%p", program_counter_);
+}
+
+#if defined(COMPILER_MSVC)
+#define RETURN_ADDRESS() _ReturnAddress()
+#elif defined(COMPILER_GCC) && !defined(OS_NACL)
+#define RETURN_ADDRESS() \
+ __builtin_extract_return_addr(__builtin_return_address(0))
+#else
+#define RETURN_ADDRESS() nullptr
+#endif
+
+// static
+NOINLINE Location Location::CreateFromHere(const char* file_name) {
+ return Location(file_name, RETURN_ADDRESS());
+}
+
+// static
+NOINLINE Location Location::CreateFromHere(const char* function_name,
+ const char* file_name,
+ int line_number) {
+ return Location(function_name, file_name, line_number, RETURN_ADDRESS());
+}
+
+#if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
+// static
+NOINLINE Location Location::Current(const char* function_name,
+ const char* file_name,
+ int line_number) {
+ return Location(function_name, file_name, line_number, RETURN_ADDRESS());
+}
+#elif SUPPORTS_LOCATION_BUILTINS
+// static
+NOINLINE Location Location::Current(const char* file_name) {
+ return Location(file_name, RETURN_ADDRESS());
+}
+#else
+// static
+NOINLINE Location Location::Current() {
+ return Location(nullptr, RETURN_ADDRESS());
+}
+#endif
+
+//------------------------------------------------------------------------------
+NOINLINE const void* GetProgramCounter() {
+ return RETURN_ADDRESS();
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/location.h b/security/sandbox/chromium/base/location.h
new file mode 100644
index 0000000000..bcc3ca0e14
--- /dev/null
+++ b/security/sandbox/chromium/base/location.h
@@ -0,0 +1,142 @@
+// 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_LOCATION_H_
+#define BASE_LOCATION_H_
+
+#include <stddef.h>
+
+#include <cassert>
+#include <functional>
+#include <string>
+
+#include "base/base_export.h"
+#include "base/debug/debugging_buildflags.h"
+#include "base/hash/hash.h"
+#include "build/build_config.h"
+
+namespace base {
+
+#if defined(__has_builtin)
+// Clang allows detection of these builtins.
+#define SUPPORTS_LOCATION_BUILTINS \
+ (__has_builtin(__builtin_FUNCTION) && __has_builtin(__builtin_FILE) && \
+ __has_builtin(__builtin_LINE))
+#elif defined(COMPILER_GCC) && __GNUC__ >= 7
+// GCC has supported these for a long time, but they point at the function
+// declaration in the case of default arguments, rather than at the call site.
+#define SUPPORTS_LOCATION_BUILTINS 1
+#else
+#define SUPPORTS_LOCATION_BUILTINS 0
+#endif
+
+// Location provides basic info where of an object was constructed, or was
+// significantly brought to life.
+class BASE_EXPORT Location {
+ public:
+ Location();
+ Location(const Location& other);
+
+ // Only initializes the file name and program counter, the source information
+ // will be null for the strings, and -1 for the line number.
+ // TODO(http://crbug.com/760702) remove file name from this constructor.
+ Location(const char* file_name, const void* program_counter);
+
+ // Constructor should be called with a long-lived char*, such as __FILE__.
+ // It assumes the provided value will persist as a global constant, and it
+ // will not make a copy of it.
+ Location(const char* function_name,
+ const char* file_name,
+ int line_number,
+ const void* program_counter);
+
+ // Comparator for hash map insertion. The program counter should uniquely
+ // identify a location.
+ bool operator==(const Location& other) const {
+ return program_counter_ == other.program_counter_;
+ }
+
+ // Returns true if there is source code location info. If this is false,
+ // the Location object only contains a program counter or is
+ // default-initialized (the program counter is also null).
+ bool has_source_info() const { return function_name_ && file_name_; }
+
+ // Will be nullptr for default initialized Location objects and when source
+ // names are disabled.
+ const char* function_name() const { return function_name_; }
+
+ // Will be nullptr for default initialized Location objects and when source
+ // names are disabled.
+ const char* file_name() const { return file_name_; }
+
+ // Will be -1 for default initialized Location objects and when source names
+ // are disabled.
+ int line_number() const { return line_number_; }
+
+ // The address of the code generating this Location object. Should always be
+ // valid except for default initialized Location objects, which will be
+ // nullptr.
+ const void* program_counter() const { return program_counter_; }
+
+ // Converts to the most user-readable form possible. If function and filename
+ // are not available, this will return "pc:<hex address>".
+ std::string ToString() const;
+
+ static Location CreateFromHere(const char* file_name);
+ static Location CreateFromHere(const char* function_name,
+ const char* file_name,
+ int line_number);
+
+#if SUPPORTS_LOCATION_BUILTINS && BUILDFLAG(ENABLE_LOCATION_SOURCE)
+ static Location Current(const char* function_name = __builtin_FUNCTION(),
+ const char* file_name = __builtin_FILE(),
+ int line_number = __builtin_LINE());
+#elif SUPPORTS_LOCATION_BUILTINS
+ static Location Current(const char* file_name = __builtin_FILE());
+#else
+ static Location Current();
+#endif
+
+ private:
+ const char* function_name_ = nullptr;
+ const char* file_name_ = nullptr;
+ int line_number_ = -1;
+ const void* program_counter_ = nullptr;
+};
+
+BASE_EXPORT const void* GetProgramCounter();
+
+// The macros defined here will expand to the current function.
+#if BUILDFLAG(ENABLE_LOCATION_SOURCE)
+
+// Full source information should be included.
+#define FROM_HERE FROM_HERE_WITH_EXPLICIT_FUNCTION(__func__)
+#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \
+ ::base::Location::CreateFromHere(function_name, __FILE__, __LINE__)
+
+#else
+
+// TODO(http://crbug.com/760702) remove the __FILE__ argument from these calls.
+#define FROM_HERE ::base::Location::CreateFromHere(__FILE__)
+#define FROM_HERE_WITH_EXPLICIT_FUNCTION(function_name) \
+ ::base::Location::CreateFromHere(function_name, __FILE__, -1)
+
+#endif
+
+} // namespace base
+
+namespace std {
+
+// Specialization for using Location in hash tables.
+template <>
+struct hash<::base::Location> {
+ std::size_t operator()(const ::base::Location& loc) const {
+ const void* program_counter = loc.program_counter();
+ return base::FastHash(base::as_bytes(base::make_span(&program_counter, 1)));
+ }
+};
+
+} // namespace std
+
+#endif // BASE_LOCATION_H_
diff --git a/security/sandbox/chromium/base/logging.h b/security/sandbox/chromium/base/logging.h
new file mode 100644
index 0000000000..cb24b94833
--- /dev/null
+++ b/security/sandbox/chromium/base/logging.h
@@ -0,0 +1,1077 @@
+// 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_LOGGING_H_
+#define BASE_LOGGING_H_
+
+#include <stddef.h>
+
+#include <cassert>
+#include <cstdint>
+#include <cstring>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/immediate_crash.h"
+#include "base/logging_buildflags.h"
+#include "base/macros.h"
+#include "base/scoped_clear_last_error.h"
+#include "base/strings/string_piece_forward.h"
+#include "base/template_util.h"
+#include "build/build_config.h"
+
+#if defined(OS_CHROMEOS)
+#include <cstdio>
+#endif
+
+//
+// Optional message capabilities
+// -----------------------------
+// Assertion failed messages and fatal errors are displayed in a dialog box
+// before the application exits. However, running this UI creates a message
+// loop, which causes application messages to be processed and potentially
+// dispatched to existing application windows. Since the application is in a
+// bad state when this assertion dialog is displayed, these messages may not
+// get processed and hang the dialog, or the application might go crazy.
+//
+// Therefore, it can be beneficial to display the error dialog in a separate
+// process from the main application. When the logging system needs to display
+// a fatal error dialog box, it will look for a program called
+// "DebugMessage.exe" in the same directory as the application executable. It
+// will run this application with the message as the command line, and will
+// not include the name of the application as is traditional for easier
+// parsing.
+//
+// The code for DebugMessage.exe is only one line. In WinMain, do:
+// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
+//
+// If DebugMessage.exe is not found, the logging code will use a normal
+// MessageBox, potentially causing the problems discussed above.
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging. The way to log things is to stream
+// things to LOG(<a particular severity level>). E.g.,
+//
+// LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+// DLOG(INFO) << "Found cookies";
+//
+// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles. LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+// LOG_ASSERT(assertion);
+// DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// There are "verbose level" logging macros. They look like
+//
+// VLOG(1) << "I'm printed when you run the program with --v=1 or more";
+// VLOG(2) << "I'm printed when you run the program with --v=2 or more";
+//
+// These always log at the INFO log level (when they log at all).
+// The verbose logging can also be turned on module-by-module. For instance,
+// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
+// will cause:
+// a. VLOG(2) and lower messages to be printed from profile.{h,cc}
+// b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
+// c. VLOG(3) and lower messages to be printed from files prefixed with
+// "browser"
+// d. VLOG(4) and lower messages to be printed from files under a
+// "chromeos" directory.
+// e. VLOG(0) and lower messages to be printed from elsewhere
+//
+// The wildcarding functionality shown by (c) supports both '*' (match
+// 0 or more characters) and '?' (match any single character)
+// wildcards. Any pattern containing a forward or backward slash will
+// be tested against the whole pathname and not just the module.
+// E.g., "*/foo/bar/*=2" would change the logging level for all code
+// in source files under a "foo/bar" directory.
+//
+// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
+//
+// if (VLOG_IS_ON(2)) {
+// // do some logging preparation and logging
+// // that can't be accomplished with just VLOG(2) << ...;
+// }
+//
+// There is also a VLOG_IF "verbose level" condition macro for sample
+// cases, when some extra computation and preparation for logs is not
+// needed.
+//
+// VLOG_IF(1, (size > 1024))
+// << "I'm printed when size is more than 1024 and when you run the "
+// "program with --v=1 or more";
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// Lastly, there is:
+//
+// PLOG(ERROR) << "Couldn't do foo";
+// DPLOG(ERROR) << "Couldn't do foo";
+// PLOG_IF(ERROR, cond) << "Couldn't do foo";
+// DPLOG_IF(ERROR, cond) << "Couldn't do foo";
+// PCHECK(condition) << "Couldn't do foo";
+// DPCHECK(condition) << "Couldn't do foo";
+//
+// which append the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+//
+// There is the special severity of DFATAL, which logs FATAL in debug mode,
+// ERROR in normal mode.
+//
+// Output is of the format, for example:
+// [3816:3877:0812/234555.406952:VERBOSE1:drm_device_handle.cc(90)] Succeeded
+// authenticating /dev/dri/card0 in 0 ms with 1 attempt(s)
+//
+// The colon separated fields inside the brackets are as follows:
+// 0. An optional Logfile prefix (not included in this example)
+// 1. Process ID
+// 2. Thread ID
+// 3. The date/time of the log message, in MMDD/HHMMSS.Milliseconds format
+// 4. The log level
+// 5. The filename and line number where the log was instantiated
+//
+// Note that the visibility can be changed by setting preferences in
+// SetLogItems()
+
+namespace logging {
+
+// TODO(avi): do we want to do a unification of character types here?
+#if defined(OS_WIN)
+typedef wchar_t PathChar;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+typedef char PathChar;
+#endif
+
+// A bitmask of potential logging destinations.
+using LoggingDestination = uint32_t;
+// Specifies where logs will be written. Multiple destinations can be specified
+// with bitwise OR.
+// Unless destination is LOG_NONE, all logs with severity ERROR and above will
+// be written to stderr in addition to the specified destination.
+enum : uint32_t {
+ LOG_NONE = 0,
+ LOG_TO_FILE = 1 << 0,
+ LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1,
+ LOG_TO_STDERR = 1 << 2,
+
+ LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR,
+
+// On Windows, use a file next to the exe.
+// On POSIX platforms, where it may not even be possible to locate the
+// executable on disk, use stderr.
+// On Fuchsia, use the Fuchsia logging service.
+#if defined(OS_FUCHSIA) || defined(OS_NACL)
+ LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG,
+#elif defined(OS_WIN)
+ LOG_DEFAULT = LOG_TO_FILE,
+#elif defined(OS_POSIX)
+ LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR,
+#endif
+};
+
+// Indicates that the log file should be locked when being written to.
+// Unless there is only one single-threaded process that is logging to
+// the log file, the file should be locked during writes to make each
+// log output atomic. Other writers will block.
+//
+// All processes writing to the log file must have their locking set for it to
+// work properly. Defaults to LOCK_LOG_FILE.
+enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
+
+// On startup, should we delete or append to an existing log file (if any)?
+// Defaults to APPEND_TO_OLD_LOG_FILE.
+enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
+
+struct BASE_EXPORT LoggingSettings {
+ // Equivalent to logging destination enum, but allows for multiple
+ // destinations.
+ uint32_t logging_dest = LOG_DEFAULT;
+
+ // The four settings below have an effect only when LOG_TO_FILE is
+ // set in |logging_dest|.
+ const PathChar* log_file_path = nullptr;
+ LogLockingState lock_log = LOCK_LOG_FILE;
+ OldFileDeletionState delete_old = APPEND_TO_OLD_LOG_FILE;
+#if defined(OS_CHROMEOS)
+ // Contains an optional file that logs should be written to. If present,
+ // |log_file_path| will be ignored, and the logging system will take ownership
+ // of the FILE. If there's an error writing to this file, no fallback paths
+ // will be opened.
+ FILE* log_file = nullptr;
+#endif
+};
+
+// Define different names for the BaseInitLoggingImpl() function depending on
+// whether NDEBUG is defined or not so that we'll fail to link if someone tries
+// to compile logging.cc with NDEBUG but includes logging.h without defining it,
+// or vice versa.
+#if defined(NDEBUG)
+#define BaseInitLoggingImpl BaseInitLoggingImpl_built_with_NDEBUG
+#else
+#define BaseInitLoggingImpl BaseInitLoggingImpl_built_without_NDEBUG
+#endif
+
+// Implementation of the InitLogging() method declared below. We use a
+// more-specific name so we can #define it above without affecting other code
+// that has named stuff "InitLogging".
+BASE_EXPORT bool BaseInitLoggingImpl(const LoggingSettings& settings);
+
+// Sets the log file name and other global logging state. Calling this function
+// is recommended, and is normally done at the beginning of application init.
+// If you don't call it, all the flags will be initialized to their default
+// values, and there is a race condition that may leak a critical section
+// object if two threads try to do the first log at the same time.
+// See the definition of the enums above for descriptions and default values.
+//
+// The default log file is initialized to "debug.log" in the application
+// directory. You probably don't want this, especially since the program
+// directory may not be writable on an enduser's system.
+//
+// This function may be called a second time to re-direct logging (e.g after
+// loging in to a user partition), however it should never be called more than
+// twice.
+inline bool InitLogging(const LoggingSettings& settings) {
+ return BaseInitLoggingImpl(settings);
+}
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged
+// up to level INFO) if this function is not called.
+// Note that log messages for VLOG(x) are logged at level -x, so setting
+// the min log level to negative values enables verbose logging.
+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();
+
+// Note that |N| is the size *with* the null terminator.
+BASE_EXPORT int GetVlogLevelHelper(const char* file_start, size_t N);
+
+// Gets the current vlog level for the given file (usually taken from __FILE__).
+template <size_t N>
+int GetVlogLevel(const char (&file)[N]) {
+ return GetVlogLevelHelper(file, N);
+}
+
+// Sets the common items you want to be prepended to each log message.
+// process and thread IDs default to off, the timestamp defaults to on.
+// If this function is not called, logging defaults to writing the timestamp
+// only.
+BASE_EXPORT void SetLogItems(bool enable_process_id, bool enable_thread_id,
+ bool enable_timestamp, bool enable_tickcount);
+
+// Sets an optional prefix to add to each log message. |prefix| is not copied
+// and should be a raw string constant. |prefix| must only contain ASCII letters
+// to avoid confusion with PIDs and timestamps. Pass null to remove the prefix.
+// Logging defaults to no prefix.
+BASE_EXPORT void SetLogPrefix(const char* prefix);
+
+// Sets whether or not you'd like to see fatal debug messages popped up in
+// a dialog box or not.
+// Dialogs are not shown by default.
+BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs);
+
+// Sets the Log Assert Handler that will be used to notify of check failures.
+// Resets Log Assert Handler on object destruction.
+// The default handler shows a dialog box and then terminate the process,
+// however clients can use this function to override with their own handling
+// (e.g. a silent one for Unit Tests)
+using LogAssertHandlerFunction =
+ base::RepeatingCallback<void(const char* file,
+ int line,
+ const base::StringPiece message,
+ const base::StringPiece stack_trace)>;
+
+class BASE_EXPORT ScopedLogAssertHandler {
+ public:
+ explicit ScopedLogAssertHandler(LogAssertHandlerFunction handler);
+ ~ScopedLogAssertHandler();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedLogAssertHandler);
+};
+
+// Sets the Log Message Handler that gets passed every log message before
+// it's sent to other log destinations (if any).
+// Returns true to signal that it handled the message and the message
+// should not be sent to other log destinations.
+typedef bool (*LogMessageHandlerFunction)(int severity,
+ const char* file, int line, size_t message_start, const std::string& str);
+BASE_EXPORT void SetLogMessageHandler(LogMessageHandlerFunction handler);
+BASE_EXPORT LogMessageHandlerFunction GetLogMessageHandler();
+
+// The ANALYZER_ASSUME_TRUE(bool arg) macro adds compiler-specific hints
+// to Clang which control what code paths are statically analyzed,
+// and is meant to be used in conjunction with assert & assert-like functions.
+// The expression is passed straight through if analysis isn't enabled.
+//
+// ANALYZER_SKIP_THIS_PATH() suppresses static analysis for the current
+// codepath and any other branching codepaths that might follow.
+#if defined(__clang_analyzer__)
+
+inline constexpr bool AnalyzerNoReturn() __attribute__((analyzer_noreturn)) {
+ return false;
+}
+
+inline constexpr bool AnalyzerAssumeTrue(bool arg) {
+ // AnalyzerNoReturn() is invoked and analysis is terminated if |arg| is
+ // false.
+ return arg || AnalyzerNoReturn();
+}
+
+#define ANALYZER_ASSUME_TRUE(arg) logging::AnalyzerAssumeTrue(!!(arg))
+#define ANALYZER_SKIP_THIS_PATH() \
+ static_cast<void>(::logging::AnalyzerNoReturn())
+#define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
+
+#else // !defined(__clang_analyzer__)
+
+#define ANALYZER_ASSUME_TRUE(arg) (arg)
+#define ANALYZER_SKIP_THIS_PATH()
+#define ANALYZER_ALLOW_UNUSED(var) static_cast<void>(var);
+
+#endif // defined(__clang_analyzer__)
+
+typedef int LogSeverity;
+const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
+// Note: the log severities are used to index into the array of names,
+// see log_severity_names.
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode
+#if defined(NDEBUG)
+const LogSeverity LOG_DFATAL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_INFO, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_WARNING, \
+ ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_ERROR, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_FATAL, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DFATAL, ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ ::logging::ClassName(__FILE__, __LINE__, ::logging::LOG_DCHECK, ##__VA_ARGS__)
+
+#define COMPACT_GOOGLE_LOG_INFO COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
+#define COMPACT_GOOGLE_LOG_WARNING COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
+#define COMPACT_GOOGLE_LOG_FATAL COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_EX_DCHECK(LogMessage)
+
+#if defined(OS_WIN)
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR
+// Needed for LOG_IS_ON(ERROR).
+const LogSeverity LOG_0 = LOG_ERROR;
+#endif
+
+// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,
+// 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::ShouldCreateLogMessage(::logging::LOG_##severity))
+
+// We don't do any caching tricks with VLOG_IS_ON() like the
+// google-glog version since it increases binary size. This means
+// that using the v-logging functions in conjunction with --vmodule
+// may be slow.
+#define VLOG_IS_ON(verboselevel) \
+ ((verboselevel) <= ::logging::GetVlogLevel(__FILE__))
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold. Condition is evaluated once and only once.
+#define LAZY_STREAM(stream, condition) \
+ !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+
+#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_IF(severity, condition) \
+ LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// The VLOG macros log with negative verbosities.
+#define VLOG_STREAM(verbose_level) \
+ ::logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+
+#define VLOG(verbose_level) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VLOG_IF(verbose_level, condition) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+#if defined (OS_WIN)
+#define VPLOG_STREAM(verbose_level) \
+ ::logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define VPLOG_STREAM(verbose_level) \
+ ::logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#endif
+
+#define VPLOG(verbose_level) \
+ LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VPLOG_IF(verbose_level, condition) \
+ LAZY_STREAM(VPLOG_STREAM(verbose_level), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
+
+#define LOG_ASSERT(condition) \
+ LOG_IF(FATAL, !(ANALYZER_ASSUME_TRUE(condition))) \
+ << "Assert failed: " #condition ". "
+
+#if defined(OS_WIN)
+#define PLOG_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#define PLOG_STREAM(severity) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \
+ ::logging::GetLastSystemErrorCode()).stream()
+#endif
+
+#define PLOG(severity) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
+
+#define PLOG_IF(severity, condition) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+BASE_EXPORT extern std::ostream* g_swallow_stream;
+
+// Note that g_swallow_stream is used instead of an arbitrary LOG() stream to
+// avoid the creation of an object with a non-trivial destructor (LogMessage).
+// On MSVC x86 (checked on 2015 Update 3), this causes a few additional
+// pointless instructions to be emitted even at full optimization level, even
+// though the : arm of the ternary operator is clearly never executed. Using a
+// simpler object to be &'d with Voidify() avoids these extra instructions.
+// Using a simpler POD object with a templated operator<< also works to avoid
+// these instructions. However, this causes warnings on statically defined
+// implementations of operator<<(std::ostream, ...) in some .cc files, because
+// they become defined-but-unreferenced functions. A reinterpret_cast of 0 to an
+// ostream* also is not suitable, because some compilers warn of undefined
+// behavior.
+#define EAT_STREAM_PARAMETERS \
+ true ? (void)0 \
+ : ::logging::LogMessageVoidify() & (*::logging::g_swallow_stream)
+
+// Captures the result of a CHECK_EQ (for example) and facilitates testing as a
+// boolean.
+class CheckOpResult {
+ public:
+ // |message| must be non-null if and only if the check failed.
+ constexpr CheckOpResult(std::string* message) : message_(message) {}
+ // Returns true if the check succeeded.
+ constexpr 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.
+//
+// We make sure CHECK et al. always evaluates their arguments, as
+// doing CHECK(FunctionWithSideEffect()) is a common idiom.
+
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG)
+
+// Make all CHECK functions discard their log strings to reduce code bloat, and
+// improve performance, for official release builds.
+//
+// This is not calling BreakDebugger since this is called frequently, and
+// calling an out-of-line function instead of a noreturn inline macro prevents
+// compiler optimizations.
+#define CHECK(condition) \
+ UNLIKELY(!(condition)) ? IMMEDIATE_CRASH() : EAT_STREAM_PARAMETERS
+
+// PCHECK includes the system error code, which is useful for determining
+// why the condition failed. In official builds, preserve only the error code
+// message so that it is available in crash reports. The stringified
+// condition and any additional stream parameters are dropped.
+#define PCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(FATAL), UNLIKELY(!(condition))); \
+ EAT_STREAM_PARAMETERS
+
+#define CHECK_OP(name, op, val1, val2) CHECK((val1) op (val2))
+
+#else // !(OFFICIAL_BUILD && NDEBUG)
+
+// 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(), \
+ !ANALYZER_ASSUME_TRUE(condition))
+
+#define PCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(FATAL), !ANALYZER_ASSUME_TRUE(condition)) \
+ << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+// 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 // !(OFFICIAL_BUILD && NDEBUG)
+
+// This formats a value for a failing CHECK_XX statement. Ordinarily,
+// it uses the definition for operator<<, with a few special cases below.
+template <typename T>
+inline typename std::enable_if<
+ base::internal::SupportsOstreamOperator<const T&>::value &&
+ !std::is_function<typename std::remove_pointer<T>::type>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << v;
+}
+
+// Overload for types that no operator<< but do have .ToString() defined.
+template <typename T>
+inline typename std::enable_if<
+ !base::internal::SupportsOstreamOperator<const T&>::value &&
+ base::internal::SupportsToString<const T&>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << v.ToString();
+}
+
+// Provide an overload for functions and function pointers. Function pointers
+// don't implicitly convert to void* but do implicitly convert to bool, so
+// without this function pointers are always printed as 1 or 0. (MSVC isn't
+// standards-conforming here and converts function pointers to regular
+// pointers, so this is a no-op for MSVC.)
+template <typename T>
+inline typename std::enable_if<
+ std::is_function<typename std::remove_pointer<T>::type>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << reinterpret_cast<const void*>(v);
+}
+
+// We need overloads for enums that don't support operator<<.
+// (i.e. scoped enums where no operator<< overload was declared).
+template <typename T>
+inline typename std::enable_if<
+ !base::internal::SupportsOstreamOperator<const T&>::value &&
+ std::is_enum<T>::value,
+ void>::type
+MakeCheckOpValueString(std::ostream* os, const T& v) {
+ (*os) << static_cast<typename std::underlying_type<T>::type>(v);
+}
+
+// We need an explicit overload for std::nullptr_t.
+BASE_EXPORT void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p);
+
+// Build the error message string. This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline. Caller
+// takes ownership of the returned string.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+ std::ostringstream ss;
+ ss << names << " (";
+ MakeCheckOpValueString(&ss, v1);
+ ss << " vs. ";
+ MakeCheckOpValueString(&ss, v2);
+ ss << ")";
+ std::string* msg = new std::string(ss.str());
+ return msg;
+}
+
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template BASE_EXPORT std::string* MakeCheckOpString<int, int>(
+ const int&, const int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&, const unsigned int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&, const std::string&, const char* name);
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+//
+// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
+// static analysis builds, blocks analysis of the current path if the
+// condition is false.
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <class t1, class t2> \
+ constexpr std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+ const char* names) { \
+ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
+ return nullptr; \
+ else \
+ return ::logging::MakeCheckOpString(v1, v2, names); \
+ } \
+ constexpr std::string* Check##name##Impl(int v1, int v2, \
+ const char* names) { \
+ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
+ return nullptr; \
+ else \
+ return ::logging::MakeCheckOpString(v1, v2, names); \
+ }
+DEFINE_CHECK_OP_IMPL(EQ, ==)
+DEFINE_CHECK_OP_IMPL(NE, !=)
+DEFINE_CHECK_OP_IMPL(LE, <=)
+DEFINE_CHECK_OP_IMPL(LT, < )
+DEFINE_CHECK_OP_IMPL(GE, >=)
+DEFINE_CHECK_OP_IMPL(GT, > )
+#undef DEFINE_CHECK_OP_IMPL
+
+#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#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)
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON() false
+#else
+#define DCHECK_IS_ON() true
+#endif
+
+// Definitions for DLOG et al.
+
+#if DCHECK_IS_ON()
+
+#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
+#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
+#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
+
+#else // DCHECK_IS_ON()
+
+// If !DCHECK_IS_ON(), we want to avoid emitting any references to |condition|
+// (which may reference a variable defined only if DCHECK_IS_ON()).
+// Contrast this with DCHECK et al., which has different behavior.
+
+#define DLOG_IS_ON(severity) false
+#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+
+#endif // DCHECK_IS_ON()
+
+#define DLOG(severity) \
+ LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DPLOG(severity) \
+ LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DVLOG(verboselevel) DVLOG_IF(verboselevel, true)
+
+#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, true)
+
+// Definitions for DCHECK et al.
+
+#if DCHECK_IS_ON()
+
+#if defined(DCHECK_IS_CONFIGURABLE)
+BASE_EXPORT extern LogSeverity LOG_DCHECK;
+#else
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+#endif // defined(DCHECK_IS_CONFIGURABLE)
+
+#else // DCHECK_IS_ON()
+
+// There may be users of LOG_DCHECK that are enabled independently
+// of DCHECK_IS_ON(), so default to FATAL logging for those.
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+
+#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
+// variable warnings if the only use of a variable is in a DCHECK.
+// This behavior is different from DLOG_IF et al.
+//
+// Note that the definition of the DCHECK macros depends on whether or not
+// DCHECK_IS_ON() is true. When DCHECK_IS_ON() is false, the macros use
+// EAT_STREAM_PARAMETERS to avoid expressions that would create temporaries.
+
+#if DCHECK_IS_ON()
+
+#define DCHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
+ << "Check failed: " #condition ". "
+#define DPCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), !ANALYZER_ASSUME_TRUE(condition)) \
+ << "Check failed: " #condition ". "
+
+#else // DCHECK_IS_ON()
+
+#define DCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
+#define DPCHECK(condition) EAT_STREAM_PARAMETERS << !(condition)
+
+#endif // DCHECK_IS_ON()
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+// 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);
+#if DCHECK_IS_ON()
+
+#define DCHECK_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__, ::logging::LOG_DCHECK, \
+ true_if_passed.message()).stream()
+
+#else // DCHECK_IS_ON()
+
+// When DCHECKs aren't enabled, DCHECK_OP still needs to reference operator<<
+// overloads for |val1| and |val2| to avoid potential compiler warnings about
+// unused functions. For the same reason, it also compares |val1| and |val2|
+// using |op|.
+//
+// Note that the contract of DCHECK_EQ, etc is that arguments are only evaluated
+// once. Even though |val1| and |val2| appear twice in this version of the macro
+// expansion, this is OK, since the expression is never actually evaluated.
+#define DCHECK_OP(name, op, val1, val2) \
+ EAT_STREAM_PARAMETERS << (::logging::MakeCheckOpValueString( \
+ ::logging::g_swallow_stream, val1), \
+ ::logging::MakeCheckOpValueString( \
+ ::logging::g_swallow_stream, val2), \
+ (val1)op(val2))
+
+#endif // DCHECK_IS_ON()
+
+// Equality/Inequality checks - compare two values, and log a
+// LOG_DCHECK message including the two values when the result is not
+// as expected. The values must have operator<<(ostream, ...)
+// defined.
+//
+// You may append to the error message like so:
+// DCHECK_NE(1, 2) << "The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here. In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+// DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These don't compile correctly if one of the arguments is a pointer
+// and the other is NULL. In new code, prefer nullptr instead. To
+// work around this for C++98, simply static_cast NULL to the type of the
+// desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#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)
+
+#if BUILDFLAG(ENABLE_LOG_ERROR_NOT_REACHED)
+// 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
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message. You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though. You should use the LOG() macro (and variants thereof)
+// above.
+class BASE_EXPORT LogMessage {
+ public:
+ // 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);
+
+ // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
+ LogMessage(const char* file, int line, LogSeverity severity,
+ std::string* result);
+
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ LogSeverity severity() { return severity_; }
+ std::string str() { return stream_.str(); }
+
+ private:
+ void Init(const char* file, int line);
+
+ LogSeverity severity_;
+ std::ostringstream stream_;
+ size_t message_start_; // Offset of the start of the message (past prefix
+ // info).
+ // The file and line information passed in to the constructor.
+ const char* file_;
+ const int line_;
+ const char* file_basename_;
+
+ // This is useful since the LogMessage class uses a lot of Win32 calls
+ // that will lose the value of GLE and the code that called the log function
+ // will have lost the thread error value when the log call returns.
+ base::internal::ScopedClearLastError last_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() = default;
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) { }
+};
+
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+typedef int SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+BASE_EXPORT SystemErrorCode GetLastSystemErrorCode();
+BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code);
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class BASE_EXPORT Win32ErrorLogMessage {
+ public:
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~Win32ErrorLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Appends a formatted system message of the errno type
+class BASE_EXPORT ErrnoLogMessage {
+ public:
+ ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~ErrnoLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#endif // OS_WIN
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+// statements, there's no guarantee that it will stay closed
+// after this call.
+BASE_EXPORT void CloseLogFile();
+
+#if defined(OS_CHROMEOS)
+// Returns a new file handle that will write to the same destination as the
+// currently open log file. Returns nullptr if logging to a file is disabled,
+// or if opening the file failed. This is intended to be used to initialize
+// logging in child processes that are unable to open files.
+BASE_EXPORT FILE* DuplicateLogFILE();
+#endif
+
+// Async signal safe logging mechanism.
+BASE_EXPORT void RawLog(int level, const char* message);
+
+#define RAW_LOG(level, message) \
+ ::logging::RawLog(::logging::LOG_##level, message)
+
+#define RAW_CHECK(condition) \
+ do { \
+ if (!(condition)) \
+ ::logging::RawLog(::logging::LOG_FATAL, \
+ "Check failed: " #condition "\n"); \
+ } 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
+
+} // namespace logging
+
+// Note that "The behavior of a C++ program is undefined if it adds declarations
+// or definitions to namespace std or to a namespace within namespace std unless
+// otherwise specified." --C++11[namespace.std]
+//
+// We've checked that this particular definition has the intended behavior on
+// our implementations, but it's prone to breaking in the future, and please
+// don't imitate this in your own definitions without checking with some
+// standard library experts.
+namespace std {
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these
+// operators.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+ return out << wstr.c_str();
+}
+} // namespace std
+
+// The NOTIMPLEMENTED() macro annotates codepaths which have not been
+// implemented yet. If output spam is a serious concern,
+// NOTIMPLEMENTED_LOG_ONCE can be used.
+
+#if defined(COMPILER_GCC)
+// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
+// of the current function in the NOTIMPLEMENTED message.
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
+#else
+#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
+#endif
+
+#define NOTIMPLEMENTED() DLOG(ERROR) << NOTIMPLEMENTED_MSG
+#define NOTIMPLEMENTED_LOG_ONCE() \
+ do { \
+ static bool logged_once = false; \
+ DLOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG; \
+ logged_once = true; \
+ } while (0); \
+ EAT_STREAM_PARAMETERS
+
+#endif // BASE_LOGGING_H_
diff --git a/security/sandbox/chromium/base/macros.h b/security/sandbox/chromium/base/macros.h
new file mode 100644
index 0000000000..c67bdbd987
--- /dev/null
+++ b/security/sandbox/chromium/base/macros.h
@@ -0,0 +1,48 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains macros and macro-like constructs (e.g., templates) that
+// are commonly used throughout Chromium source. (It may also contain things
+// that are closely related to things that are commonly used that belong in this
+// file.)
+
+#ifndef BASE_MACROS_H_
+#define BASE_MACROS_H_
+
+// ALL DISALLOW_xxx MACROS ARE DEPRECATED; DO NOT USE IN NEW CODE.
+// Use explicit deletions instead. See the section on copyability/movability in
+// //styleguide/c++/c++-dos-and-donts.md for more information.
+
+// Put this in the declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) \
+ TypeName(const TypeName&) = delete
+
+// Put this in the declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) TypeName& operator=(const TypeName&) = delete
+
+// Put this in the declarations for a class to be uncopyable and unassignable.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ DISALLOW_COPY(TypeName); \
+ DISALLOW_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+// This is especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// 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:
+//
+// std::unique_ptr<MyType> my_var = ...;
+// if (TakeOwnership(my_var.get()) == SUCCESS)
+// ignore_result(my_var.release());
+//
+template<typename T>
+inline void ignore_result(const T&) {
+}
+
+#endif // BASE_MACROS_H_
diff --git a/security/sandbox/chromium/base/memory/aligned_memory.h b/security/sandbox/chromium/base/memory/aligned_memory.h
new file mode 100644
index 0000000000..a242b730be
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/aligned_memory.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_ALIGNED_MEMORY_H_
+#define BASE_MEMORY_ALIGNED_MEMORY_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <type_traits>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+
+// A runtime sized aligned allocation can be created:
+//
+// float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));
+//
+// // ... later, to release the memory:
+// AlignedFree(my_array);
+//
+// Or using unique_ptr:
+//
+// std::unique_ptr<float, AlignedFreeDeleter> my_array(
+// static_cast<float*>(AlignedAlloc(size, alignment)));
+
+namespace base {
+
+// This can be replaced with std::aligned_alloc when we have C++17.
+// Caveat: std::aligned_alloc requires the size parameter be an integral
+// multiple of alignment.
+BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);
+
+inline void AlignedFree(void* ptr) {
+#if defined(COMPILER_MSVC)
+ _aligned_free(ptr);
+#else
+ free(ptr);
+#endif
+}
+
+// Deleter for use with unique_ptr. E.g., use as
+// std::unique_ptr<Foo, base::AlignedFreeDeleter> foo;
+struct AlignedFreeDeleter {
+ inline void operator()(void* ptr) const {
+ AlignedFree(ptr);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_ALIGNED_MEMORY_H_
diff --git a/security/sandbox/chromium/base/memory/free_deleter.h b/security/sandbox/chromium/base/memory/free_deleter.h
new file mode 100644
index 0000000000..5604118865
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/free_deleter.h
@@ -0,0 +1,25 @@
+// 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_MEMORY_FREE_DELETER_H_
+#define BASE_MEMORY_FREE_DELETER_H_
+
+#include <stdlib.h>
+
+namespace base {
+
+// Function object which invokes 'free' on its parameter, which must be
+// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
+//
+// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
+// static_cast<int*>(malloc(sizeof(int))));
+struct FreeDeleter {
+ inline void operator()(void* ptr) const {
+ free(ptr);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_FREE_DELETER_H_
diff --git a/security/sandbox/chromium/base/memory/platform_shared_memory_region.cc b/security/sandbox/chromium/base/memory/platform_shared_memory_region.cc
new file mode 100644
index 0000000000..45647925b3
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/platform_shared_memory_region.cc
@@ -0,0 +1,62 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/platform_shared_memory_region.h"
+
+#include "base/memory/shared_memory_mapping.h"
+#include "base/numerics/checked_math.h"
+
+namespace base {
+namespace subtle {
+
+// static
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::CreateWritable(
+ size_t size) {
+ return Create(Mode::kWritable, size);
+}
+
+// static
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::CreateUnsafe(
+ size_t size) {
+ return Create(Mode::kUnsafe, size);
+}
+
+PlatformSharedMemoryRegion::PlatformSharedMemoryRegion() = default;
+PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
+ PlatformSharedMemoryRegion&& other) = default;
+PlatformSharedMemoryRegion& PlatformSharedMemoryRegion::operator=(
+ PlatformSharedMemoryRegion&& other) = default;
+PlatformSharedMemoryRegion::~PlatformSharedMemoryRegion() = default;
+
+PlatformSharedMemoryRegion::ScopedPlatformHandle
+PlatformSharedMemoryRegion::PassPlatformHandle() {
+ return std::move(handle_);
+}
+
+bool PlatformSharedMemoryRegion::MapAt(off_t offset,
+ size_t size,
+ void** memory,
+ size_t* mapped_size) const {
+ if (!IsValid())
+ return false;
+
+ if (size == 0)
+ return false;
+
+ size_t end_byte;
+ if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) {
+ return false;
+ }
+
+ bool success = MapAtInternal(offset, size, memory, mapped_size);
+ if (success) {
+ DCHECK_EQ(
+ 0U, reinterpret_cast<uintptr_t>(*memory) & (kMapMinimumAlignment - 1));
+ }
+
+ return success;
+}
+
+} // namespace subtle
+} // namespace base
diff --git a/security/sandbox/chromium/base/memory/platform_shared_memory_region.h b/security/sandbox/chromium/base/memory/platform_shared_memory_region.h
new file mode 100644
index 0000000000..220cbdd65e
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/platform_shared_memory_region.h
@@ -0,0 +1,301 @@
+// Copyright 2018 The Chromium Authors. 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_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_
+#define BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_
+
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include <mach/mach.h>
+#include "base/mac/scoped_mach_port.h"
+#elif defined(OS_FUCHSIA)
+#include <lib/zx/vmo.h>
+#elif defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_types.h"
+#elif defined(OS_POSIX)
+#include <sys/types.h>
+#include "base/file_descriptor_posix.h"
+#include "base/files/scoped_file.h"
+#endif
+
+#if defined(OS_LINUX)
+namespace content {
+class SandboxIPCHandler;
+}
+#endif
+
+namespace base {
+namespace subtle {
+
+#if defined(OS_POSIX) && (!defined(OS_MACOSX) || defined(OS_IOS)) && \
+ !defined(OS_ANDROID)
+// Helper structs to keep two descriptors on POSIX. It's needed to support
+// ConvertToReadOnly().
+struct BASE_EXPORT FDPair {
+ // The main shared memory descriptor that is used for mapping. May be either
+ // writable or read-only, depending on region's mode.
+ int fd;
+ // The read-only descriptor, valid only in kWritable mode. Replaces |fd| when
+ // a region is converted to read-only.
+ int readonly_fd;
+};
+
+struct BASE_EXPORT ScopedFDPair {
+ ScopedFDPair();
+ ScopedFDPair(ScopedFD in_fd, ScopedFD in_readonly_fd);
+ ScopedFDPair(ScopedFDPair&&);
+ ScopedFDPair& operator=(ScopedFDPair&&);
+ ~ScopedFDPair();
+
+ FDPair get() const;
+
+ ScopedFD fd;
+ ScopedFD readonly_fd;
+};
+#endif
+
+// Implementation class for shared memory regions.
+//
+// This class does the following:
+//
+// - Wraps and owns a shared memory region platform handle.
+// - Provides a way to allocate a new region of platform shared memory of given
+// size.
+// - Provides a way to create mapping of the region in the current process'
+// address space, under special access-control constraints (see Mode).
+// - Provides methods to help transferring the handle across process boundaries.
+// - Holds a 128-bit unique identifier used to uniquely identify the same
+// kernel region resource across processes (used for memory tracking).
+// - Has a method to retrieve the region's size in bytes.
+//
+// IMPORTANT NOTE: Users should never use this directly, but
+// ReadOnlySharedMemoryRegion, WritableSharedMemoryRegion or
+// UnsafeSharedMemoryRegion since this is an implementation class.
+class BASE_EXPORT PlatformSharedMemoryRegion {
+ public:
+ // Permission mode of the platform handle. Each mode corresponds to one of the
+ // typed shared memory classes:
+ //
+ // * ReadOnlySharedMemoryRegion: A region that can only create read-only
+ // mappings.
+ //
+ // * WritableSharedMemoryRegion: A region that can only create writable
+ // mappings. The region can be demoted to ReadOnlySharedMemoryRegion without
+ // the possibility of promoting back to writable.
+ //
+ // * UnsafeSharedMemoryRegion: A region that can only create writable
+ // mappings. The region cannot be demoted to ReadOnlySharedMemoryRegion.
+ enum class Mode {
+ kReadOnly, // ReadOnlySharedMemoryRegion
+ kWritable, // WritableSharedMemoryRegion
+ kUnsafe, // UnsafeSharedMemoryRegion
+ kMaxValue = kUnsafe
+ };
+
+ // Errors that can occur during Shared Memory construction.
+ // These match tools/metrics/histograms/enums.xml.
+ // This enum is append-only.
+ enum class CreateError {
+ SUCCESS = 0,
+ SIZE_ZERO = 1,
+ SIZE_TOO_LARGE = 2,
+ INITIALIZE_ACL_FAILURE = 3,
+ INITIALIZE_SECURITY_DESC_FAILURE = 4,
+ SET_SECURITY_DESC_FAILURE = 5,
+ CREATE_FILE_MAPPING_FAILURE = 6,
+ REDUCE_PERMISSIONS_FAILURE = 7,
+ ALREADY_EXISTS = 8,
+ ALLOCATE_FILE_REGION_FAILURE = 9,
+ FSTAT_FAILURE = 10,
+ INODES_MISMATCH = 11,
+ GET_SHMEM_TEMP_DIR_FAILURE = 12,
+ kMaxValue = GET_SHMEM_TEMP_DIR_FAILURE
+ };
+
+#if defined(OS_LINUX)
+ // Structure to limit access to executable region creation.
+ struct ExecutableRegion {
+ private:
+ // Creates a new shared memory region the unsafe mode (writable and not and
+ // convertible to read-only), and in addition marked executable. A ScopedFD
+ // to this region is returned. Any any mapping will have to be done
+ // manually, including setting executable permissions if necessary
+ //
+ // This is only used to support sandbox_ipc_linux.cc, and should not be used
+ // anywhere else in chrome. This is restricted via AllowCreateExecutable.
+ // TODO(crbug.com/982879): remove this when NaCl is unshipped.
+ //
+ // Returns an invalid ScopedFD if the call fails.
+ static ScopedFD CreateFD(size_t size);
+
+ friend class content::SandboxIPCHandler;
+ };
+#endif
+
+// Platform-specific shared memory type used by this class.
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ using PlatformHandle = mach_port_t;
+ using ScopedPlatformHandle = mac::ScopedMachSendRight;
+#elif defined(OS_FUCHSIA)
+ using PlatformHandle = zx::unowned_vmo;
+ using ScopedPlatformHandle = zx::vmo;
+#elif defined(OS_WIN)
+ using PlatformHandle = HANDLE;
+ using ScopedPlatformHandle = win::ScopedHandle;
+#elif defined(OS_ANDROID)
+ using PlatformHandle = int;
+ using ScopedPlatformHandle = ScopedFD;
+#else
+ using PlatformHandle = FDPair;
+ using ScopedPlatformHandle = ScopedFDPair;
+#endif
+
+ // The minimum alignment in bytes that any mapped address produced by Map()
+ // and MapAt() is guaranteed to have.
+ enum { kMapMinimumAlignment = 32 };
+
+ // Creates a new PlatformSharedMemoryRegion with corresponding mode and size.
+ // Creating in kReadOnly mode isn't supported because then there will be no
+ // way to modify memory content.
+ static PlatformSharedMemoryRegion CreateWritable(size_t size);
+ static PlatformSharedMemoryRegion CreateUnsafe(size_t size);
+
+ // Returns a new PlatformSharedMemoryRegion that takes ownership of the
+ // |handle|. All parameters must be taken from another valid
+ // PlatformSharedMemoryRegion instance, e.g. |size| must be equal to the
+ // actual region size as allocated by the kernel.
+ // Closes the |handle| and returns an invalid instance if passed parameters
+ // are invalid.
+ static PlatformSharedMemoryRegion Take(ScopedPlatformHandle handle,
+ Mode mode,
+ size_t size,
+ const UnguessableToken& guid);
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && \
+ !(defined(OS_MACOSX) && !defined(OS_IOS))
+ // Specialized version of Take() for POSIX that takes only one file descriptor
+ // instead of pair. Cannot be used with kWritable |mode|.
+ static PlatformSharedMemoryRegion Take(ScopedFD handle,
+ Mode mode,
+ size_t size,
+ const UnguessableToken& guid);
+#endif
+
+ // Default constructor initializes an invalid instance, i.e. an instance that
+ // doesn't wrap any valid platform handle.
+ PlatformSharedMemoryRegion();
+
+ // Move operations are allowed.
+ PlatformSharedMemoryRegion(PlatformSharedMemoryRegion&&);
+ PlatformSharedMemoryRegion& operator=(PlatformSharedMemoryRegion&&);
+
+ // Destructor closes the platform handle. Does nothing if the handle is
+ // invalid.
+ ~PlatformSharedMemoryRegion();
+
+ // Passes ownership of the platform handle to the caller. The current instance
+ // becomes invalid. It's the responsibility of the caller to close the
+ // handle. If the current instance is invalid, ScopedPlatformHandle will also
+ // be invalid.
+ ScopedPlatformHandle PassPlatformHandle() WARN_UNUSED_RESULT;
+
+ // Returns the platform handle. The current instance keeps ownership of this
+ // handle.
+ PlatformHandle GetPlatformHandle() const;
+
+ // Whether the platform handle is valid.
+ bool IsValid() const;
+
+ // Duplicates the platform handle and creates a new PlatformSharedMemoryRegion
+ // with the same |mode_|, |size_| and |guid_| that owns this handle. Returns
+ // invalid region on failure, the current instance remains valid.
+ // Can be called only in kReadOnly and kUnsafe modes, CHECK-fails if is
+ // called in kWritable mode.
+ PlatformSharedMemoryRegion Duplicate() const;
+
+ // Converts the region to read-only. Returns whether the operation succeeded.
+ // Makes the current instance invalid on failure. Can be called only in
+ // kWritable mode, all other modes will CHECK-fail. The object will have
+ // kReadOnly mode after this call on success.
+ bool ConvertToReadOnly();
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // Same as above, but |mapped_addr| is used as a hint to avoid additional
+ // mapping of the memory object.
+ // |mapped_addr| must be mapped location of |memory_object_|. If the location
+ // is unknown, |mapped_addr| should be |nullptr|.
+ bool ConvertToReadOnly(void* mapped_addr);
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+ // Converts the region to unsafe. Returns whether the operation succeeded.
+ // Makes the current instance invalid on failure. Can be called only in
+ // kWritable mode, all other modes will CHECK-fail. The object will have
+ // kUnsafe mode after this call on success.
+ bool ConvertToUnsafe();
+
+ // Maps |size| bytes of the shared memory region starting with the given
+ // |offset| into the caller's address space. |offset| must be aligned to value
+ // of |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out
+ // of the region limits.
+ // Returns true and sets |memory| and |mapped_size| on success, returns false
+ // and leaves output parameters in unspecified state otherwise. The mapped
+ // address is guaranteed to have an alignment of at least
+ // |kMapMinimumAlignment|.
+ bool MapAt(off_t offset,
+ size_t size,
+ void** memory,
+ size_t* mapped_size) const;
+
+ const UnguessableToken& GetGUID() const { return guid_; }
+
+ size_t GetSize() const { return size_; }
+
+ Mode GetMode() const { return mode_; }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest,
+ CreateReadOnlyRegionDeathTest);
+ FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest,
+ CheckPlatformHandlePermissionsCorrespondToMode);
+ static PlatformSharedMemoryRegion Create(Mode mode,
+ size_t size
+#if defined(OS_LINUX)
+ ,
+ bool executable = false
+#endif
+ );
+
+ static bool CheckPlatformHandlePermissionsCorrespondToMode(
+ PlatformHandle handle,
+ Mode mode,
+ size_t size);
+
+ PlatformSharedMemoryRegion(ScopedPlatformHandle handle,
+ Mode mode,
+ size_t size,
+ const UnguessableToken& guid);
+
+ bool MapAtInternal(off_t offset,
+ size_t size,
+ void** memory,
+ size_t* mapped_size) const;
+
+ ScopedPlatformHandle handle_;
+ Mode mode_ = Mode::kReadOnly;
+ size_t size_ = 0;
+ UnguessableToken guid_;
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformSharedMemoryRegion);
+};
+
+} // namespace subtle
+} // namespace base
+
+#endif // BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_
diff --git a/security/sandbox/chromium/base/memory/platform_shared_memory_region_win.cc b/security/sandbox/chromium/base/memory/platform_shared_memory_region_win.cc
new file mode 100644
index 0000000000..c2f3704f91
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/platform_shared_memory_region_win.cc
@@ -0,0 +1,343 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/platform_shared_memory_region.h"
+
+#include <aclapi.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/bits.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/process/process_handle.h"
+#include "base/rand_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+namespace subtle {
+
+namespace {
+
+// Emits UMA metrics about encountered errors. Pass zero (0) for |winerror|
+// if there is no associated Windows error.
+void LogError(PlatformSharedMemoryRegion::CreateError error, DWORD winerror) {
+ UMA_HISTOGRAM_ENUMERATION("SharedMemory.CreateError", error);
+ static_assert(ERROR_SUCCESS == 0, "Windows error code changed!");
+ if (winerror != ERROR_SUCCESS)
+ UmaHistogramSparse("SharedMemory.CreateWinError", winerror);
+}
+
+typedef enum _SECTION_INFORMATION_CLASS {
+ SectionBasicInformation,
+} SECTION_INFORMATION_CLASS;
+
+typedef struct _SECTION_BASIC_INFORMATION {
+ PVOID BaseAddress;
+ ULONG Attributes;
+ LARGE_INTEGER Size;
+} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
+
+typedef ULONG(__stdcall* NtQuerySectionType)(
+ HANDLE SectionHandle,
+ SECTION_INFORMATION_CLASS SectionInformationClass,
+ PVOID SectionInformation,
+ ULONG SectionInformationLength,
+ PULONG ResultLength);
+
+// Returns the length of the memory section starting at the supplied address.
+size_t GetMemorySectionSize(void* address) {
+ MEMORY_BASIC_INFORMATION memory_info;
+ if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
+ return 0;
+ return memory_info.RegionSize -
+ (static_cast<char*>(address) -
+ static_cast<char*>(memory_info.AllocationBase));
+}
+
+// Checks if the section object is safe to map. At the moment this just means
+// it's not an image section.
+bool IsSectionSafeToMap(HANDLE handle) {
+ static NtQuerySectionType nt_query_section_func =
+ reinterpret_cast<NtQuerySectionType>(
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection"));
+ DCHECK(nt_query_section_func);
+
+ // The handle must have SECTION_QUERY access for this to succeed.
+ SECTION_BASIC_INFORMATION basic_information = {};
+ ULONG status =
+ nt_query_section_func(handle, SectionBasicInformation, &basic_information,
+ sizeof(basic_information), nullptr);
+ if (status)
+ return false;
+ return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE;
+}
+
+// Returns a HANDLE on success and |nullptr| on failure.
+// This function is similar to CreateFileMapping, but removes the permissions
+// WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE.
+//
+// A newly created file mapping has two sets of permissions. It has access
+// control permissions (WRITE_DAC, WRITE_OWNER, READ_CONTROL, and DELETE) and
+// file permissions (FILE_MAP_READ, FILE_MAP_WRITE, etc.). The Chrome sandbox
+// prevents HANDLEs with the WRITE_DAC permission from being duplicated into
+// unprivileged processes.
+//
+// In order to remove the access control permissions, after being created the
+// handle is duplicated with only the file access permissions.
+HANDLE CreateFileMappingWithReducedPermissions(SECURITY_ATTRIBUTES* sa,
+ size_t rounded_size,
+ LPCWSTR name) {
+ HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, sa, PAGE_READWRITE, 0,
+ static_cast<DWORD>(rounded_size), name);
+ if (!h) {
+ LogError(
+ PlatformSharedMemoryRegion::CreateError::CREATE_FILE_MAPPING_FAILURE,
+ GetLastError());
+ return nullptr;
+ }
+
+ HANDLE h2;
+ ProcessHandle process = GetCurrentProcess();
+ BOOL success = ::DuplicateHandle(
+ process, h, process, &h2, FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY,
+ FALSE, 0);
+ BOOL rv = ::CloseHandle(h);
+ DCHECK(rv);
+
+ if (!success) {
+ LogError(
+ PlatformSharedMemoryRegion::CreateError::REDUCE_PERMISSIONS_FAILURE,
+ GetLastError());
+ return nullptr;
+ }
+
+ return h2;
+}
+
+} // namespace
+
+// static
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
+ win::ScopedHandle handle,
+ Mode mode,
+ size_t size,
+ const UnguessableToken& guid) {
+ if (!handle.IsValid())
+ return {};
+
+ if (size == 0)
+ return {};
+
+ if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
+ return {};
+
+ if (!IsSectionSafeToMap(handle.Get()))
+ return {};
+
+ CHECK(
+ CheckPlatformHandlePermissionsCorrespondToMode(handle.Get(), mode, size));
+
+ return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
+}
+
+HANDLE PlatformSharedMemoryRegion::GetPlatformHandle() const {
+ return handle_.Get();
+}
+
+bool PlatformSharedMemoryRegion::IsValid() const {
+ return handle_.IsValid();
+}
+
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
+ if (!IsValid())
+ return {};
+
+ CHECK_NE(mode_, Mode::kWritable)
+ << "Duplicating a writable shared memory region is prohibited";
+
+ HANDLE duped_handle;
+ ProcessHandle process = GetCurrentProcess();
+ BOOL success =
+ ::DuplicateHandle(process, handle_.Get(), process, &duped_handle, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!success)
+ return {};
+
+ return PlatformSharedMemoryRegion(win::ScopedHandle(duped_handle), mode_,
+ size_, guid_);
+}
+
+bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
+ if (!IsValid())
+ return false;
+
+ CHECK_EQ(mode_, Mode::kWritable)
+ << "Only writable shared memory region can be converted to read-only";
+
+ win::ScopedHandle handle_copy(handle_.Take());
+
+ HANDLE duped_handle;
+ ProcessHandle process = GetCurrentProcess();
+ BOOL success =
+ ::DuplicateHandle(process, handle_copy.Get(), process, &duped_handle,
+ FILE_MAP_READ | SECTION_QUERY, FALSE, 0);
+ if (!success)
+ return false;
+
+ handle_.Set(duped_handle);
+ mode_ = Mode::kReadOnly;
+ return true;
+}
+
+bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
+ if (!IsValid())
+ return false;
+
+ CHECK_EQ(mode_, Mode::kWritable)
+ << "Only writable shared memory region can be converted to unsafe";
+
+ mode_ = Mode::kUnsafe;
+ return true;
+}
+
+bool PlatformSharedMemoryRegion::MapAtInternal(off_t offset,
+ size_t size,
+ void** memory,
+ size_t* mapped_size) const {
+ bool write_allowed = mode_ != Mode::kReadOnly;
+ // Try to map the shared memory. On the first failure, release any reserved
+ // address space for a single entry.
+ for (int i = 0; i < 2; ++i) {
+ *memory = MapViewOfFile(
+ handle_.Get(), FILE_MAP_READ | (write_allowed ? FILE_MAP_WRITE : 0),
+ static_cast<uint64_t>(offset) >> 32, static_cast<DWORD>(offset), size);
+ if (*memory)
+ break;
+ ReleaseReservation();
+ }
+ if (!*memory) {
+ DPLOG(ERROR) << "Failed executing MapViewOfFile";
+ return false;
+ }
+
+ *mapped_size = GetMemorySectionSize(*memory);
+ return true;
+}
+
+// static
+PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
+ size_t size) {
+ // TODO(crbug.com/210609): NaCl forces us to round up 64k here, wasting 32k
+ // per mapping on average.
+ static const size_t kSectionSize = 65536;
+ if (size == 0) {
+ LogError(CreateError::SIZE_ZERO, 0);
+ return {};
+ }
+
+ // Aligning may overflow so check that the result doesn't decrease.
+ size_t rounded_size = bits::Align(size, kSectionSize);
+ if (rounded_size < size ||
+ rounded_size > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ LogError(CreateError::SIZE_TOO_LARGE, 0);
+ return {};
+ }
+
+ CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
+ "lead to this region being non-modifiable";
+
+ // Add an empty DACL to enforce anonymous read-only sections.
+ ACL dacl;
+ SECURITY_DESCRIPTOR sd;
+ if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
+ LogError(CreateError::INITIALIZE_ACL_FAILURE, GetLastError());
+ return {};
+ }
+ if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
+ LogError(CreateError::INITIALIZE_SECURITY_DESC_FAILURE, GetLastError());
+ return {};
+ }
+ if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE)) {
+ LogError(CreateError::SET_SECURITY_DESC_FAILURE, GetLastError());
+ return {};
+ }
+
+ string16 name;
+ if (win::GetVersion() < win::Version::WIN8_1) {
+ // Windows < 8.1 ignores DACLs on certain unnamed objects (like shared
+ // sections). So, we generate a random name when we need to enforce
+ // read-only.
+ uint64_t rand_values[4];
+ RandBytes(&rand_values, sizeof(rand_values));
+ name = ASCIIToUTF16(StringPrintf("CrSharedMem_%016llx%016llx%016llx%016llx",
+ rand_values[0], rand_values[1],
+ rand_values[2], rand_values[3]));
+ DCHECK(!name.empty());
+ }
+
+ SECURITY_ATTRIBUTES sa = {sizeof(sa), &sd, FALSE};
+ // Ask for the file mapping with reduced permisions to avoid passing the
+ // access control permissions granted by default into unpriviledged process.
+ HANDLE h = CreateFileMappingWithReducedPermissions(
+ &sa, rounded_size, name.empty() ? nullptr : as_wcstr(name));
+ if (h == nullptr) {
+ // The error is logged within CreateFileMappingWithReducedPermissions().
+ return {};
+ }
+
+ win::ScopedHandle scoped_h(h);
+ // Check if the shared memory pre-exists.
+ if (GetLastError() == ERROR_ALREADY_EXISTS) {
+ LogError(CreateError::ALREADY_EXISTS, ERROR_ALREADY_EXISTS);
+ return {};
+ }
+
+ LogError(CreateError::SUCCESS, ERROR_SUCCESS);
+ return PlatformSharedMemoryRegion(std::move(scoped_h), mode, size,
+ UnguessableToken::Create());
+}
+
+// static
+bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
+ PlatformHandle handle,
+ Mode mode,
+ size_t size) {
+ // Call ::DuplicateHandle() with FILE_MAP_WRITE as a desired access to check
+ // if the |handle| has a write access.
+ ProcessHandle process = GetCurrentProcess();
+ HANDLE duped_handle;
+ BOOL success = ::DuplicateHandle(process, handle, process, &duped_handle,
+ FILE_MAP_WRITE, FALSE, 0);
+ if (success) {
+ BOOL rv = ::CloseHandle(duped_handle);
+ DCHECK(rv);
+ }
+
+ bool is_read_only = !success;
+ bool expected_read_only = mode == Mode::kReadOnly;
+
+ if (is_read_only != expected_read_only) {
+ DLOG(ERROR) << "File mapping handle has wrong access rights: it is"
+ << (is_read_only ? " " : " not ") << "read-only but it should"
+ << (expected_read_only ? " " : " not ") << "be";
+ return false;
+ }
+
+ return true;
+}
+
+PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
+ win::ScopedHandle handle,
+ Mode mode,
+ size_t size,
+ const UnguessableToken& guid)
+ : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
+
+} // namespace subtle
+} // namespace base
diff --git a/security/sandbox/chromium/base/memory/ptr_util.h b/security/sandbox/chromium/base/memory/ptr_util.h
new file mode 100644
index 0000000000..42f4f49eeb
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/ptr_util.h
@@ -0,0 +1,23 @@
+// 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_MEMORY_PTR_UTIL_H_
+#define BASE_MEMORY_PTR_UTIL_H_
+
+#include <memory>
+#include <utility>
+
+namespace base {
+
+// Helper to transfer ownership of a raw pointer to a std::unique_ptr<T>.
+// Note that std::unique_ptr<T> has very different semantics from
+// std::unique_ptr<T[]>: do not use this helper for array allocations.
+template <typename T>
+std::unique_ptr<T> WrapUnique(T* ptr) {
+ return std::unique_ptr<T>(ptr);
+}
+
+} // namespace base
+
+#endif // BASE_MEMORY_PTR_UTIL_H_
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
new file mode 100644
index 0000000000..ab8b2abcbb
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/raw_scoped_refptr_mismatch_checker.h
@@ -0,0 +1,52 @@
+// 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_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+#define BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
+
+#include <type_traits>
+
+#include "base/template_util.h"
+
+// It is dangerous to post a task with a T* argument where T is a subtype of
+// RefCounted(Base|ThreadSafeBase), since by the time the parameter is used, the
+// object may already have been deleted since it was not held with a
+// scoped_refptr. Example: http://crbug.com/27191
+// The following set of traits are designed to generate a compile error
+// whenever this antipattern is attempted.
+
+namespace base {
+
+// This is a base internal implementation file used by task.h and callback.h.
+// Not for public consumption, so we wrap it in namespace internal.
+namespace internal {
+
+template <typename T, typename = void>
+struct IsRefCountedType : std::false_type {};
+
+template <typename T>
+struct IsRefCountedType<T,
+ void_t<decltype(std::declval<T*>()->AddRef()),
+ decltype(std::declval<T*>()->Release())>>
+ : std::true_type {};
+
+template <typename T>
+struct NeedsScopedRefptrButGetsRawPtr {
+ static_assert(!std::is_reference<T>::value,
+ "NeedsScopedRefptrButGetsRawPtr requires non-reference type.");
+
+ enum {
+ // Human readable translation: you needed to be a scoped_refptr if you are a
+ // raw pointer type and are convertible to a RefCounted(Base|ThreadSafeBase)
+ // type.
+ value = std::is_pointer<T>::value &&
+ IsRefCountedType<std::remove_pointer_t<T>>::value
+ };
+};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_MEMORY_RAW_SCOPED_REFPTR_MISMATCH_CHECKER_H_
diff --git a/security/sandbox/chromium/base/memory/ref_counted.cc b/security/sandbox/chromium/base/memory/ref_counted.cc
new file mode 100644
index 0000000000..0a8d32ebf0
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/ref_counted.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/ref_counted.h"
+
+#include <limits>
+#include <type_traits>
+
+#include "base/threading/thread_collision_warner.h"
+
+namespace base {
+namespace {
+
+#if DCHECK_IS_ON()
+std::atomic_int g_cross_thread_ref_count_access_allow_count(0);
+#endif
+
+} // namespace
+
+namespace subtle {
+
+bool RefCountedThreadSafeBase::HasOneRef() const {
+ return ref_count_.IsOne();
+}
+
+bool RefCountedThreadSafeBase::HasAtLeastOneRef() const {
+ return !ref_count_.IsZero();
+}
+
+#if DCHECK_IS_ON()
+RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
+ DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
+ "calling Release()";
+}
+#endif
+
+// For security and correctness, we check the arithmetic on ref counts.
+//
+// In an attempt to avoid binary bloat (from inlining the `CHECK`), we define
+// these functions out-of-line. However, compilers are wily. Further testing may
+// show that `NOINLINE` helps or hurts.
+//
+#if defined(ARCH_CPU_64_BITS)
+void RefCountedBase::AddRefImpl() const {
+ // An attacker could induce use-after-free bugs, and potentially exploit them,
+ // by creating so many references to a ref-counted object that the reference
+ // count overflows. On 32-bit architectures, there is not enough address space
+ // to succeed. But on 64-bit architectures, it might indeed be possible.
+ // Therefore, we can elide the check for arithmetic overflow on 32-bit, but we
+ // must check on 64-bit.
+ //
+ // Make sure the addition didn't wrap back around to 0. This form of check
+ // works because we assert that `ref_count_` is an unsigned integer type.
+ CHECK(++ref_count_ != 0);
+}
+
+void RefCountedBase::ReleaseImpl() const {
+ // Make sure the subtraction didn't wrap back around from 0 to the max value.
+ // That could cause memory leaks, and may induce application-semantic
+ // correctness or safety bugs. (E.g. what if we really needed that object to
+ // be destroyed at the right time?)
+ //
+ // Note that unlike with overflow, underflow could also happen on 32-bit
+ // architectures. Arguably, we should do this check on32-bit machines too.
+ CHECK(--ref_count_ != std::numeric_limits<decltype(ref_count_)>::max());
+}
+#endif
+
+#if !defined(ARCH_CPU_X86_FAMILY)
+bool RefCountedThreadSafeBase::Release() const {
+ return ReleaseImpl();
+}
+void RefCountedThreadSafeBase::AddRef() const {
+ AddRefImpl();
+}
+void RefCountedThreadSafeBase::AddRefWithCheck() const {
+ AddRefWithCheckImpl();
+}
+#endif
+
+#if DCHECK_IS_ON()
+bool RefCountedBase::CalledOnValidSequence() const {
+#if defined(MOZ_SANDBOX)
+ return true;
+#else
+ return sequence_checker_.CalledOnValidSequence() ||
+ g_cross_thread_ref_count_access_allow_count.load() != 0;
+#endif
+}
+#endif
+
+} // namespace subtle
+
+#if DCHECK_IS_ON()
+ScopedAllowCrossThreadRefCountAccess::ScopedAllowCrossThreadRefCountAccess() {
+ ++g_cross_thread_ref_count_access_allow_count;
+}
+
+ScopedAllowCrossThreadRefCountAccess::~ScopedAllowCrossThreadRefCountAccess() {
+ --g_cross_thread_ref_count_access_allow_count;
+}
+#endif
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/memory/ref_counted.h b/security/sandbox/chromium/base/memory/ref_counted.h
new file mode 100644
index 0000000000..ac7183a49d
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/ref_counted.h
@@ -0,0 +1,463 @@
+// 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_MEMORY_REF_COUNTED_H_
+#define BASE_MEMORY_REF_COUNTED_H_
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/atomic_ref_count.h"
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequence_checker.h"
+#include "base/threading/thread_collision_warner.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace subtle {
+
+class BASE_EXPORT RefCountedBase {
+ public:
+ bool HasOneRef() const { return ref_count_ == 1; }
+ bool HasAtLeastOneRef() const { return ref_count_ >= 1; }
+
+ protected:
+ explicit RefCountedBase(StartRefCountFromZeroTag) {
+#if DCHECK_IS_ON()
+ sequence_checker_.DetachFromSequence();
+#endif
+ }
+
+ explicit RefCountedBase(StartRefCountFromOneTag) : ref_count_(1) {
+#if DCHECK_IS_ON()
+ needs_adopt_ref_ = true;
+ sequence_checker_.DetachFromSequence();
+#endif
+ }
+
+ ~RefCountedBase() {
+#if DCHECK_IS_ON()
+ DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+#endif
+ }
+
+ void AddRef() const {
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease"
+ // without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+#if DCHECK_IS_ON()
+ DCHECK(!in_dtor_);
+ DCHECK(!needs_adopt_ref_)
+ << "This RefCounted object is created with non-zero reference count."
+ << " The first reference to such a object has to be made by AdoptRef or"
+ << " MakeRefCounted.";
+ if (ref_count_ >= 1) {
+ DCHECK(CalledOnValidSequence());
+ }
+#endif
+
+ AddRefImpl();
+ }
+
+ // Returns true if the object should self-delete.
+ bool Release() const {
+ ReleaseImpl();
+
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease"
+ // without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+
+#if DCHECK_IS_ON()
+ DCHECK(!in_dtor_);
+ if (ref_count_ == 0)
+ in_dtor_ = true;
+
+ if (ref_count_ >= 1)
+ DCHECK(CalledOnValidSequence());
+ if (ref_count_ == 1)
+ sequence_checker_.DetachFromSequence();
+#endif
+
+ return ref_count_ == 0;
+ }
+
+ // Returns true if it is safe to read or write the object, from a thread
+ // safety standpoint. Should be DCHECK'd from the methods of RefCounted
+ // classes if there is a danger of objects being shared across threads.
+ //
+ // This produces fewer false positives than adding a separate SequenceChecker
+ // into the subclass, because it automatically detaches from the sequence when
+ // the reference count is 1 (and never fails if there is only one reference).
+ //
+ // This means unlike a separate SequenceChecker, it will permit a singly
+ // referenced object to be passed between threads (not holding a reference on
+ // the sending thread), but will trap if the sending thread holds onto a
+ // reference, or if the object is accessed from multiple threads
+ // simultaneously.
+ bool IsOnValidSequence() const {
+#if DCHECK_IS_ON()
+ return ref_count_ <= 1 || CalledOnValidSequence();
+#else
+ return true;
+#endif
+ }
+
+ private:
+ template <typename U>
+ friend scoped_refptr<U> base::AdoptRef(U*);
+
+ FRIEND_TEST_ALL_PREFIXES(RefCountedDeathTest, TestOverflowCheck);
+
+ void Adopted() const {
+#if DCHECK_IS_ON()
+ DCHECK(needs_adopt_ref_);
+ needs_adopt_ref_ = false;
+#endif
+ }
+
+#if defined(ARCH_CPU_64_BITS)
+ void AddRefImpl() const;
+ void ReleaseImpl() const;
+#else
+ void AddRefImpl() const { ++ref_count_; }
+ void ReleaseImpl() const { --ref_count_; }
+#endif
+
+#if DCHECK_IS_ON()
+ bool CalledOnValidSequence() const;
+#endif
+
+ mutable uint32_t ref_count_ = 0;
+ static_assert(std::is_unsigned<decltype(ref_count_)>::value,
+ "ref_count_ must be an unsigned type.");
+
+#if DCHECK_IS_ON()
+ mutable bool needs_adopt_ref_ = false;
+ mutable bool in_dtor_ = false;
+ mutable SequenceChecker sequence_checker_;
+#endif
+
+ DFAKE_MUTEX(add_release_);
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedBase);
+};
+
+class BASE_EXPORT RefCountedThreadSafeBase {
+ public:
+ bool HasOneRef() const;
+ bool HasAtLeastOneRef() const;
+
+ protected:
+ explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {}
+ explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag)
+ : ref_count_(1) {
+#if DCHECK_IS_ON()
+ needs_adopt_ref_ = true;
+#endif
+ }
+
+#if DCHECK_IS_ON()
+ ~RefCountedThreadSafeBase();
+#else
+ ~RefCountedThreadSafeBase() = default;
+#endif
+
+// Release and AddRef are suitable for inlining on X86 because they generate
+// very small code sequences. On other platforms (ARM), it causes a size
+// regression and is probably not worth it.
+#if defined(ARCH_CPU_X86_FAMILY)
+ // Returns true if the object should self-delete.
+ bool Release() const { return ReleaseImpl(); }
+ void AddRef() const { AddRefImpl(); }
+ void AddRefWithCheck() const { AddRefWithCheckImpl(); }
+#else
+ // Returns true if the object should self-delete.
+ bool Release() const;
+ void AddRef() const;
+ void AddRefWithCheck() const;
+#endif
+
+ private:
+ template <typename U>
+ friend scoped_refptr<U> base::AdoptRef(U*);
+
+ void Adopted() const {
+#if DCHECK_IS_ON()
+ DCHECK(needs_adopt_ref_);
+ needs_adopt_ref_ = false;
+#endif
+ }
+
+ ALWAYS_INLINE void AddRefImpl() const {
+#if DCHECK_IS_ON()
+ DCHECK(!in_dtor_);
+ DCHECK(!needs_adopt_ref_)
+ << "This RefCounted object is created with non-zero reference count."
+ << " The first reference to such a object has to be made by AdoptRef or"
+ << " MakeRefCounted.";
+#endif
+ ref_count_.Increment();
+ }
+
+ ALWAYS_INLINE void AddRefWithCheckImpl() const {
+#if DCHECK_IS_ON()
+ DCHECK(!in_dtor_);
+ DCHECK(!needs_adopt_ref_)
+ << "This RefCounted object is created with non-zero reference count."
+ << " The first reference to such a object has to be made by AdoptRef or"
+ << " MakeRefCounted.";
+#endif
+ CHECK(ref_count_.Increment() > 0);
+ }
+
+ ALWAYS_INLINE bool ReleaseImpl() const {
+#if DCHECK_IS_ON()
+ DCHECK(!in_dtor_);
+ DCHECK(!ref_count_.IsZero());
+#endif
+ if (!ref_count_.Decrement()) {
+#if DCHECK_IS_ON()
+ in_dtor_ = true;
+#endif
+ return true;
+ }
+ return false;
+ }
+
+ mutable AtomicRefCount ref_count_{0};
+#if DCHECK_IS_ON()
+ mutable bool needs_adopt_ref_ = false;
+ mutable bool in_dtor_ = false;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase);
+};
+
+} // namespace subtle
+
+// ScopedAllowCrossThreadRefCountAccess disables the check documented on
+// RefCounted below for rare pre-existing use cases where thread-safety was
+// guaranteed through other means (e.g. explicit sequencing of calls across
+// execution sequences when bouncing between threads in order). New callers
+// should refrain from using this (callsites handling thread-safety through
+// locks should use RefCountedThreadSafe per the overhead of its atomics being
+// negligible compared to locks anyways and callsites doing explicit sequencing
+// should properly std::move() the ref to avoid hitting this check).
+// TODO(tzik): Cleanup existing use cases and remove
+// ScopedAllowCrossThreadRefCountAccess.
+class BASE_EXPORT ScopedAllowCrossThreadRefCountAccess final {
+ public:
+#if DCHECK_IS_ON()
+ ScopedAllowCrossThreadRefCountAccess();
+ ~ScopedAllowCrossThreadRefCountAccess();
+#else
+ ScopedAllowCrossThreadRefCountAccess() {}
+ ~ScopedAllowCrossThreadRefCountAccess() {}
+#endif
+};
+
+//
+// A base class for reference counted classes. Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class. To use this, just extend your
+// class from it like so:
+//
+// class MyFoo : public base::RefCounted<MyFoo> {
+// ...
+// private:
+// friend class base::RefCounted<MyFoo>;
+// ~MyFoo();
+// };
+//
+// You should always make your destructor non-public, to avoid any code deleting
+// the object accidently while there are references to it.
+//
+//
+// The ref count manipulation to RefCounted is NOT thread safe and has DCHECKs
+// to trap unsafe cross thread usage. A subclass instance of RefCounted can be
+// passed to another execution sequence only when its ref count is 1. If the ref
+// count is more than 1, the RefCounted class verifies the ref updates are made
+// on the same execution sequence as the previous ones. The subclass can also
+// manually call IsOnValidSequence to trap other non-thread-safe accesses; see
+// the documentation for that method.
+//
+//
+// The reference count starts from zero by default, and we intended to migrate
+// to start-from-one ref count. Put REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() to
+// the ref counted class to opt-in.
+//
+// If an object has start-from-one ref count, the first scoped_refptr need to be
+// created by base::AdoptRef() or base::MakeRefCounted(). We can use
+// base::MakeRefCounted() to create create both type of ref counted object.
+//
+// The motivations to use start-from-one ref count are:
+// - Start-from-one ref count doesn't need the ref count increment for the
+// first reference.
+// - It can detect an invalid object acquisition for a being-deleted object
+// that has zero ref count. That tends to happen on custom deleter that
+// delays the deletion.
+// TODO(tzik): Implement invalid acquisition detection.
+// - Behavior parity to Blink's WTF::RefCounted, whose count starts from one.
+// And start-from-one ref count is a step to merge WTF::RefCounted into
+// base::RefCounted.
+//
+#define REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() \
+ static constexpr ::base::subtle::StartRefCountFromOneTag \
+ kRefCountPreference = ::base::subtle::kStartRefCountFromOneTag
+
+template <class T, typename Traits>
+class RefCounted;
+
+template <typename T>
+struct DefaultRefCountedTraits {
+ static void Destruct(const T* x) {
+ RefCounted<T, DefaultRefCountedTraits>::DeleteInternal(x);
+ }
+};
+
+template <class T, typename Traits = DefaultRefCountedTraits<T>>
+class RefCounted : public subtle::RefCountedBase {
+ public:
+ static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference =
+ subtle::kStartRefCountFromZeroTag;
+
+ RefCounted() : subtle::RefCountedBase(T::kRefCountPreference) {}
+
+ void AddRef() const {
+ subtle::RefCountedBase::AddRef();
+ }
+
+ void Release() const {
+ if (subtle::RefCountedBase::Release()) {
+ // Prune the code paths which the static analyzer may take to simulate
+ // object destruction. Use-after-free errors aren't possible given the
+ // lifetime guarantees of the refcounting system.
+ ANALYZER_SKIP_THIS_PATH();
+
+ Traits::Destruct(static_cast<const T*>(this));
+ }
+ }
+
+ protected:
+ ~RefCounted() = default;
+
+ private:
+ friend struct DefaultRefCountedTraits<T>;
+ template <typename U>
+ static void DeleteInternal(const U* x) {
+ delete x;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RefCounted);
+};
+
+// Forward declaration.
+template <class T, typename Traits> class RefCountedThreadSafe;
+
+// Default traits for RefCountedThreadSafe<T>. Deletes the object when its ref
+// count reaches 0. Overload to delete it on a different thread etc.
+template<typename T>
+struct DefaultRefCountedThreadSafeTraits {
+ static void Destruct(const T* x) {
+ // Delete through RefCountedThreadSafe to make child classes only need to be
+ // friend with RefCountedThreadSafe instead of this struct, which is an
+ // implementation detail.
+ RefCountedThreadSafe<T,
+ DefaultRefCountedThreadSafeTraits>::DeleteInternal(x);
+ }
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+// class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
+// ...
+// };
+//
+// If you're using the default trait, then you should add compile time
+// asserts that no one else is deleting your object. i.e.
+// private:
+// friend class base::RefCountedThreadSafe<MyFoo>;
+// ~MyFoo();
+//
+// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe
+// too. See the comment above the RefCounted definition for details.
+template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> >
+class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
+ public:
+ static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference =
+ subtle::kStartRefCountFromZeroTag;
+
+ explicit RefCountedThreadSafe()
+ : subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {}
+
+ void AddRef() const { AddRefImpl(T::kRefCountPreference); }
+
+ void Release() const {
+ if (subtle::RefCountedThreadSafeBase::Release()) {
+ ANALYZER_SKIP_THIS_PATH();
+ Traits::Destruct(static_cast<const T*>(this));
+ }
+ }
+
+ protected:
+ ~RefCountedThreadSafe() = default;
+
+ private:
+ friend struct DefaultRefCountedThreadSafeTraits<T>;
+ template <typename U>
+ static void DeleteInternal(const U* x) {
+ delete x;
+ }
+
+ void AddRefImpl(subtle::StartRefCountFromZeroTag) const {
+ subtle::RefCountedThreadSafeBase::AddRef();
+ }
+
+ void AddRefImpl(subtle::StartRefCountFromOneTag) const {
+ subtle::RefCountedThreadSafeBase::AddRefWithCheck();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe);
+};
+
+//
+// A thread-safe wrapper for some piece of data so we can place other
+// things in scoped_refptrs<>.
+//
+template<typename T>
+class RefCountedData
+ : public base::RefCountedThreadSafe< base::RefCountedData<T> > {
+ public:
+ RefCountedData() : data() {}
+ RefCountedData(const T& in_value) : data(in_value) {}
+ RefCountedData(T&& in_value) : data(std::move(in_value)) {}
+
+ T data;
+
+ private:
+ friend class base::RefCountedThreadSafe<base::RefCountedData<T> >;
+ ~RefCountedData() = default;
+};
+
+template <typename T>
+bool operator==(const RefCountedData<T>& lhs, const RefCountedData<T>& rhs) {
+ return lhs.data == rhs.data;
+}
+
+template <typename T>
+bool operator!=(const RefCountedData<T>& lhs, const RefCountedData<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace base
+
+#endif // BASE_MEMORY_REF_COUNTED_H_
diff --git a/security/sandbox/chromium/base/memory/scoped_refptr.h b/security/sandbox/chromium/base/memory/scoped_refptr.h
new file mode 100644
index 0000000000..238b61a736
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/scoped_refptr.h
@@ -0,0 +1,375 @@
+// Copyright 2017 The Chromium Authors. 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_MEMORY_SCOPED_REFPTR_H_
+#define BASE_MEMORY_SCOPED_REFPTR_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <type_traits>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+template <class T>
+class scoped_refptr;
+
+namespace base {
+
+template <class, typename>
+class RefCounted;
+template <class, typename>
+class RefCountedThreadSafe;
+class SequencedTaskRunner;
+class WrappedPromise;
+
+template <typename T>
+scoped_refptr<T> AdoptRef(T* t);
+
+namespace internal {
+
+class BasePromise;
+
+} // namespace internal
+
+namespace subtle {
+
+enum AdoptRefTag { kAdoptRefTag };
+enum StartRefCountFromZeroTag { kStartRefCountFromZeroTag };
+enum StartRefCountFromOneTag { kStartRefCountFromOneTag };
+
+template <typename T, typename U, typename V>
+constexpr bool IsRefCountPreferenceOverridden(const T*,
+ const RefCounted<U, V>*) {
+ return !std::is_same<std::decay_t<decltype(T::kRefCountPreference)>,
+ std::decay_t<decltype(U::kRefCountPreference)>>::value;
+}
+
+template <typename T, typename U, typename V>
+constexpr bool IsRefCountPreferenceOverridden(
+ const T*,
+ const RefCountedThreadSafe<U, V>*) {
+ return !std::is_same<std::decay_t<decltype(T::kRefCountPreference)>,
+ std::decay_t<decltype(U::kRefCountPreference)>>::value;
+}
+
+constexpr bool IsRefCountPreferenceOverridden(...) {
+ return false;
+}
+
+} // namespace subtle
+
+// Creates a scoped_refptr from a raw pointer without incrementing the reference
+// count. Use this only for a newly created object whose reference count starts
+// from 1 instead of 0.
+template <typename T>
+scoped_refptr<T> AdoptRef(T* obj) {
+ using Tag = std::decay_t<decltype(T::kRefCountPreference)>;
+ static_assert(std::is_same<subtle::StartRefCountFromOneTag, Tag>::value,
+ "Use AdoptRef only if the reference count starts from one.");
+
+ DCHECK(obj);
+ DCHECK(obj->HasOneRef());
+ obj->Adopted();
+ return scoped_refptr<T>(obj, subtle::kAdoptRefTag);
+}
+
+namespace subtle {
+
+template <typename T>
+scoped_refptr<T> AdoptRefIfNeeded(T* obj, StartRefCountFromZeroTag) {
+ return scoped_refptr<T>(obj);
+}
+
+template <typename T>
+scoped_refptr<T> AdoptRefIfNeeded(T* obj, StartRefCountFromOneTag) {
+ return AdoptRef(obj);
+}
+
+} // namespace subtle
+
+// Constructs an instance of T, which is a ref counted type, and wraps the
+// object into a scoped_refptr<T>.
+template <typename T, typename... Args>
+scoped_refptr<T> MakeRefCounted(Args&&... args) {
+ T* obj = new T(std::forward<Args>(args)...);
+ return subtle::AdoptRefIfNeeded(obj, T::kRefCountPreference);
+}
+
+// Takes an instance of T, which is a ref counted type, and wraps the object
+// into a scoped_refptr<T>.
+template <typename T>
+scoped_refptr<T> WrapRefCounted(T* t) {
+ return scoped_refptr<T>(t);
+}
+
+} // namespace base
+
+//
+// A smart pointer class for reference counted objects. Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference. Sample usage:
+//
+// class MyFoo : public RefCounted<MyFoo> {
+// ...
+// private:
+// friend class RefCounted<MyFoo>; // Allow destruction by RefCounted<>.
+// ~MyFoo(); // Destructor must be private/protected.
+// };
+//
+// void some_function() {
+// scoped_refptr<MyFoo> foo = MakeRefCounted<MyFoo>();
+// foo->Method(param);
+// // |foo| is released when this function returns
+// }
+//
+// void some_other_function() {
+// scoped_refptr<MyFoo> foo = MakeRefCounted<MyFoo>();
+// ...
+// foo.reset(); // explicitly releases |foo|
+// ...
+// if (foo)
+// foo->Method(param);
+// }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+// {
+// scoped_refptr<MyFoo> a = MakeRefCounted<MyFoo>();
+// scoped_refptr<MyFoo> b;
+//
+// b.swap(a);
+// // now, |b| references the MyFoo object, and |a| references nullptr.
+// }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+// {
+// scoped_refptr<MyFoo> a = MakeRefCounted<MyFoo>();
+// scoped_refptr<MyFoo> b;
+//
+// b = a;
+// // now, |a| and |b| each own a reference to the same MyFoo object.
+// }
+//
+// Also see Chromium's ownership and calling conventions:
+// https://chromium.googlesource.com/chromium/src/+/lkgr/styleguide/c++/c++.md#object-ownership-and-calling-conventions
+// Specifically:
+// If the function (at least sometimes) takes a ref on a refcounted object,
+// declare the param as scoped_refptr<T>. The caller can decide whether it
+// wishes to transfer ownership (by calling std::move(t) when passing t) or
+// retain its ref (by simply passing t directly).
+// In other words, use scoped_refptr like you would a std::unique_ptr except
+// in the odd case where it's required to hold on to a ref while handing one
+// to another component (if a component merely needs to use t on the stack
+// without keeping a ref: pass t as a raw T*).
+template <class T>
+class scoped_refptr {
+ public:
+ typedef T element_type;
+
+ constexpr scoped_refptr() = default;
+
+ // Allow implicit construction from nullptr.
+ constexpr scoped_refptr(std::nullptr_t) {}
+
+ // Constructs from a raw pointer. Note that this constructor allows implicit
+ // conversion from T* to scoped_refptr<T> which is strongly discouraged. If
+ // you are creating a new ref-counted object please use
+ // base::MakeRefCounted<T>() or base::WrapRefCounted<T>(). Otherwise you
+ // should move or copy construct from an existing scoped_refptr<T> to the
+ // ref-counted object.
+ scoped_refptr(T* p) : ptr_(p) {
+ if (ptr_)
+ AddRef(ptr_);
+ }
+
+ // Copy constructor. This is required in addition to the copy conversion
+ // constructor below.
+ scoped_refptr(const scoped_refptr& r) : scoped_refptr(r.ptr_) {}
+
+ // Copy conversion constructor.
+ template <typename U,
+ typename = typename std::enable_if<
+ std::is_convertible<U*, T*>::value>::type>
+ scoped_refptr(const scoped_refptr<U>& r) : scoped_refptr(r.ptr_) {}
+
+ // Move constructor. This is required in addition to the move conversion
+ // constructor below.
+ scoped_refptr(scoped_refptr&& r) noexcept : ptr_(r.ptr_) { r.ptr_ = nullptr; }
+
+ // Move conversion constructor.
+ template <typename U,
+ typename = typename std::enable_if<
+ std::is_convertible<U*, T*>::value>::type>
+ scoped_refptr(scoped_refptr<U>&& r) noexcept : ptr_(r.ptr_) {
+ r.ptr_ = nullptr;
+ }
+
+ ~scoped_refptr() {
+ static_assert(!base::subtle::IsRefCountPreferenceOverridden(
+ static_cast<T*>(nullptr), static_cast<T*>(nullptr)),
+ "It's unsafe to override the ref count preference."
+ " Please remove REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE"
+ " from subclasses.");
+ if (ptr_)
+ Release(ptr_);
+ }
+
+ T* get() const { return ptr_; }
+
+ T& operator*() const {
+ DCHECK(ptr_);
+ return *ptr_;
+ }
+
+ T* operator->() const {
+ DCHECK(ptr_);
+ return ptr_;
+ }
+
+ scoped_refptr& operator=(std::nullptr_t) {
+ reset();
+ return *this;
+ }
+
+ scoped_refptr& operator=(T* p) { return *this = scoped_refptr(p); }
+
+ // Unified assignment operator.
+ scoped_refptr& operator=(scoped_refptr r) noexcept {
+ swap(r);
+ return *this;
+ }
+
+ // Sets managed object to null and releases reference to the previous managed
+ // object, if it existed.
+ void reset() { scoped_refptr().swap(*this); }
+
+ void swap(scoped_refptr& r) noexcept { std::swap(ptr_, r.ptr_); }
+
+ explicit operator bool() const { return ptr_ != nullptr; }
+
+ template <typename U>
+ bool operator==(const scoped_refptr<U>& rhs) const {
+ return ptr_ == rhs.get();
+ }
+
+ template <typename U>
+ bool operator!=(const scoped_refptr<U>& rhs) const {
+ return !operator==(rhs);
+ }
+
+ template <typename U>
+ bool operator<(const scoped_refptr<U>& rhs) const {
+ return ptr_ < rhs.get();
+ }
+
+ protected:
+ T* ptr_ = nullptr;
+
+ private:
+ template <typename U>
+ friend scoped_refptr<U> base::AdoptRef(U*);
+ friend class ::base::SequencedTaskRunner;
+
+ // Friend access so these classes can use the constructor below as part of a
+ // binary size optimization.
+ friend class ::base::internal::BasePromise;
+ friend class ::base::WrappedPromise;
+
+ // Returns the owned pointer (if any), releasing ownership to the caller. The
+ // caller is responsible for managing the lifetime of the reference.
+ T* release();
+
+ scoped_refptr(T* p, base::subtle::AdoptRefTag) : ptr_(p) {}
+
+ // Friend required for move constructors that set r.ptr_ to null.
+ template <typename U>
+ friend class scoped_refptr;
+
+ // Non-inline helpers to allow:
+ // class Opaque;
+ // extern template class scoped_refptr<Opaque>;
+ // Otherwise the compiler will complain that Opaque is an incomplete type.
+ static void AddRef(T* ptr);
+ static void Release(T* ptr);
+};
+
+template <typename T>
+T* scoped_refptr<T>::release() {
+ T* ptr = ptr_;
+ ptr_ = nullptr;
+ return ptr;
+}
+
+// static
+template <typename T>
+void scoped_refptr<T>::AddRef(T* ptr) {
+ ptr->AddRef();
+}
+
+// static
+template <typename T>
+void scoped_refptr<T>::Release(T* ptr) {
+ ptr->Release();
+}
+
+template <typename T, typename U>
+bool operator==(const scoped_refptr<T>& lhs, const U* rhs) {
+ return lhs.get() == rhs;
+}
+
+template <typename T, typename U>
+bool operator==(const T* lhs, const scoped_refptr<U>& rhs) {
+ return lhs == rhs.get();
+}
+
+template <typename T>
+bool operator==(const scoped_refptr<T>& lhs, std::nullptr_t null) {
+ return !static_cast<bool>(lhs);
+}
+
+template <typename T>
+bool operator==(std::nullptr_t null, const scoped_refptr<T>& rhs) {
+ return !static_cast<bool>(rhs);
+}
+
+template <typename T, typename U>
+bool operator!=(const scoped_refptr<T>& lhs, const U* rhs) {
+ return !operator==(lhs, rhs);
+}
+
+template <typename T, typename U>
+bool operator!=(const T* lhs, const scoped_refptr<U>& rhs) {
+ return !operator==(lhs, rhs);
+}
+
+template <typename T>
+bool operator!=(const scoped_refptr<T>& lhs, std::nullptr_t null) {
+ return !operator==(lhs, null);
+}
+
+template <typename T>
+bool operator!=(std::nullptr_t null, const scoped_refptr<T>& rhs) {
+ return !operator==(null, rhs);
+}
+
+template <typename T>
+std::ostream& operator<<(std::ostream& out, const scoped_refptr<T>& p) {
+ return out << p.get();
+}
+
+template <typename T>
+void swap(scoped_refptr<T>& lhs, scoped_refptr<T>& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+#endif // BASE_MEMORY_SCOPED_REFPTR_H_
diff --git a/security/sandbox/chromium/base/memory/shared_memory_mapping.cc b/security/sandbox/chromium/base/memory/shared_memory_mapping.cc
new file mode 100644
index 0000000000..8426fa8c21
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/shared_memory_mapping.cc
@@ -0,0 +1,115 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/shared_memory_mapping.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/shared_memory_tracker.h"
+#include "base/unguessable_token.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include <sys/mman.h>
+#endif
+
+#if defined(OS_WIN)
+#include <aclapi.h>
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include <mach/mach_vm.h>
+#include "base/mac/mach_logging.h"
+#endif
+
+#if defined(OS_FUCHSIA)
+#include <lib/zx/vmar.h>
+#include "base/fuchsia/fuchsia_logging.h"
+#endif
+
+namespace base {
+
+SharedMemoryMapping::SharedMemoryMapping() = default;
+
+SharedMemoryMapping::SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept
+ : memory_(mapping.memory_),
+ size_(mapping.size_),
+ mapped_size_(mapping.mapped_size_),
+ guid_(mapping.guid_) {
+ mapping.memory_ = nullptr;
+}
+
+SharedMemoryMapping& SharedMemoryMapping::operator=(
+ SharedMemoryMapping&& mapping) noexcept {
+ Unmap();
+ memory_ = mapping.memory_;
+ size_ = mapping.size_;
+ mapped_size_ = mapping.mapped_size_;
+ guid_ = mapping.guid_;
+ mapping.memory_ = nullptr;
+ return *this;
+}
+
+SharedMemoryMapping::~SharedMemoryMapping() {
+ Unmap();
+}
+
+SharedMemoryMapping::SharedMemoryMapping(void* memory,
+ size_t size,
+ size_t mapped_size,
+ const UnguessableToken& guid)
+ : memory_(memory), size_(size), mapped_size_(mapped_size), guid_(guid) {
+ SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
+}
+
+void SharedMemoryMapping::Unmap() {
+ if (!IsValid())
+ return;
+
+ SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
+#if defined(OS_WIN)
+ if (!UnmapViewOfFile(memory_))
+ DPLOG(ERROR) << "UnmapViewOfFile";
+#elif defined(OS_FUCHSIA)
+ uintptr_t addr = reinterpret_cast<uintptr_t>(memory_);
+ zx_status_t status = zx::vmar::root_self()->unmap(addr, mapped_size_);
+ if (status != ZX_OK)
+ ZX_DLOG(ERROR, status) << "zx_vmar_unmap";
+#elif defined(OS_MACOSX) && !defined(OS_IOS)
+ kern_return_t kr = mach_vm_deallocate(
+ mach_task_self(), reinterpret_cast<mach_vm_address_t>(memory_),
+ mapped_size_);
+ MACH_DLOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_vm_deallocate";
+#else
+ if (munmap(memory_, mapped_size_) < 0)
+ DPLOG(ERROR) << "munmap";
+#endif
+}
+
+ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping() = default;
+ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
+ ReadOnlySharedMemoryMapping&&) noexcept = default;
+ReadOnlySharedMemoryMapping& ReadOnlySharedMemoryMapping::operator=(
+ ReadOnlySharedMemoryMapping&&) noexcept = default;
+ReadOnlySharedMemoryMapping::ReadOnlySharedMemoryMapping(
+ void* address,
+ size_t size,
+ size_t mapped_size,
+ const UnguessableToken& guid)
+ : SharedMemoryMapping(address, size, mapped_size, guid) {}
+
+WritableSharedMemoryMapping::WritableSharedMemoryMapping() = default;
+WritableSharedMemoryMapping::WritableSharedMemoryMapping(
+ WritableSharedMemoryMapping&&) noexcept = default;
+WritableSharedMemoryMapping& WritableSharedMemoryMapping::operator=(
+ WritableSharedMemoryMapping&&) noexcept = default;
+WritableSharedMemoryMapping::WritableSharedMemoryMapping(
+ void* address,
+ size_t size,
+ size_t mapped_size,
+ const UnguessableToken& guid)
+ : SharedMemoryMapping(address, size, mapped_size, guid) {}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/memory/shared_memory_mapping.h b/security/sandbox/chromium/base/memory/shared_memory_mapping.h
new file mode 100644
index 0000000000..2b8858e166
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/shared_memory_mapping.h
@@ -0,0 +1,252 @@
+// Copyright 2018 The Chromium Authors. 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_MEMORY_SHARED_MEMORY_MAPPING_H_
+#define BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
+
+#include <cstddef>
+#include <type_traits>
+
+#include "base/containers/buffer_iterator.h"
+#include "base/containers/span.h"
+#include "base/macros.h"
+#include "base/unguessable_token.h"
+
+namespace base {
+
+namespace subtle {
+class PlatformSharedMemoryRegion;
+} // namespace subtle
+
+// Base class for scoped handles to a shared memory mapping created from a
+// shared memory region. Created shared memory mappings remain valid even if the
+// creator region is transferred or destroyed.
+//
+// Each mapping has an UnguessableToken that identifies the shared memory region
+// it was created from. This is used for memory metrics, to avoid overcounting
+// shared memory.
+class BASE_EXPORT SharedMemoryMapping {
+ public:
+ // Default constructor initializes an invalid instance.
+ SharedMemoryMapping();
+
+ // Move operations are allowed.
+ SharedMemoryMapping(SharedMemoryMapping&& mapping) noexcept;
+ SharedMemoryMapping& operator=(SharedMemoryMapping&& mapping) noexcept;
+
+ // Unmaps the region if the mapping is valid.
+ virtual ~SharedMemoryMapping();
+
+ // Returns true iff the mapping is valid. False means there is no
+ // corresponding area of memory.
+ bool IsValid() const { return memory_ != nullptr; }
+
+ // Returns the logical size of the mapping in bytes. This is precisely the
+ // size requested by whoever created the mapping, and it is always less than
+ // or equal to |mapped_size()|. This is undefined for invalid instances.
+ size_t size() const {
+ DCHECK(IsValid());
+ return size_;
+ }
+
+ // Returns the actual size of the mapping in bytes. This is always at least
+ // as large as |size()| but may be larger due to platform mapping alignment
+ // constraints. This is undefined for invalid instances.
+ size_t mapped_size() const {
+ DCHECK(IsValid());
+ return mapped_size_;
+ }
+
+ // Returns 128-bit GUID of the region this mapping belongs to.
+ const UnguessableToken& guid() const {
+ DCHECK(IsValid());
+ return guid_;
+ }
+
+ protected:
+ SharedMemoryMapping(void* address,
+ size_t size,
+ size_t mapped_size,
+ const UnguessableToken& guid);
+ void* raw_memory_ptr() const { return memory_; }
+
+ private:
+ friend class SharedMemoryTracker;
+
+ void Unmap();
+
+ void* memory_ = nullptr;
+ size_t size_ = 0;
+ size_t mapped_size_ = 0;
+ UnguessableToken guid_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemoryMapping);
+};
+
+// Class modeling a read-only mapping of a shared memory region into the
+// current process' address space. This is created by ReadOnlySharedMemoryRegion
+// instances.
+class BASE_EXPORT ReadOnlySharedMemoryMapping : public SharedMemoryMapping {
+ public:
+ // Default constructor initializes an invalid instance.
+ ReadOnlySharedMemoryMapping();
+
+ // Move operations are allowed.
+ ReadOnlySharedMemoryMapping(ReadOnlySharedMemoryMapping&&) noexcept;
+ ReadOnlySharedMemoryMapping& operator=(
+ ReadOnlySharedMemoryMapping&&) noexcept;
+
+ // Returns the base address of the mapping. This is read-only memory. This is
+ // page-aligned. This is nullptr for invalid instances.
+ const void* memory() const { return raw_memory_ptr(); }
+
+ // Returns a pointer to a page-aligned const T if the mapping is valid and
+ // large enough to contain a T, or nullptr otherwise.
+ template <typename T>
+ const T* GetMemoryAs() const {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copying non-trivially-copyable object across memory spaces "
+ "is dangerous");
+ if (!IsValid())
+ return nullptr;
+ if (sizeof(T) > size())
+ return nullptr;
+ return static_cast<const T*>(raw_memory_ptr());
+ }
+
+ // Returns a span of const T. The number of elements is autodeduced from the
+ // size of the shared memory mapping. The number of elements may be
+ // autodeduced as zero, i.e. the mapping is invalid or the size of the mapping
+ // isn't large enough to contain even one T: in that case, an empty span
+ // will be returned. The first element, if any, is guaranteed to be
+ // page-aligned.
+ template <typename T>
+ span<const T> GetMemoryAsSpan() const {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copying non-trivially-copyable object across memory spaces "
+ "is dangerous");
+ if (!IsValid())
+ return span<const T>();
+ size_t count = size() / sizeof(T);
+ return GetMemoryAsSpan<T>(count);
+ }
+
+ // Returns a span of const T with |count| elements if the mapping is valid and
+ // large enough to contain |count| elements, or an empty span otherwise. The
+ // first element, if any, is guaranteed to be page-aligned.
+ template <typename T>
+ span<const T> GetMemoryAsSpan(size_t count) const {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copying non-trivially-copyable object across memory spaces "
+ "is dangerous");
+ if (!IsValid())
+ return span<const T>();
+ if (size() / sizeof(T) < count)
+ return span<const T>();
+ return span<const T>(static_cast<const T*>(raw_memory_ptr()), count);
+ }
+
+ // Returns a BufferIterator of const T.
+ template <typename T>
+ BufferIterator<const T> GetMemoryAsBufferIterator() const {
+ return BufferIterator<const T>(GetMemoryAsSpan<T>());
+ }
+
+ private:
+ friend class ReadOnlySharedMemoryRegion;
+ ReadOnlySharedMemoryMapping(void* address,
+ size_t size,
+ size_t mapped_size,
+ const UnguessableToken& guid);
+
+ DISALLOW_COPY_AND_ASSIGN(ReadOnlySharedMemoryMapping);
+};
+
+// Class modeling a writable mapping of a shared memory region into the
+// current process' address space. This is created by *SharedMemoryRegion
+// instances.
+class BASE_EXPORT WritableSharedMemoryMapping : public SharedMemoryMapping {
+ public:
+ // Default constructor initializes an invalid instance.
+ WritableSharedMemoryMapping();
+
+ // Move operations are allowed.
+ WritableSharedMemoryMapping(WritableSharedMemoryMapping&&) noexcept;
+ WritableSharedMemoryMapping& operator=(
+ WritableSharedMemoryMapping&&) noexcept;
+
+ // Returns the base address of the mapping. This is writable memory. This is
+ // page-aligned. This is nullptr for invalid instances.
+ void* memory() const { return raw_memory_ptr(); }
+
+ // Returns a pointer to a page-aligned T if the mapping is valid and large
+ // enough to contain a T, or nullptr otherwise.
+ template <typename T>
+ T* GetMemoryAs() const {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copying non-trivially-copyable object across memory spaces "
+ "is dangerous");
+ if (!IsValid())
+ return nullptr;
+ if (sizeof(T) > size())
+ return nullptr;
+ return static_cast<T*>(raw_memory_ptr());
+ }
+
+ // Returns a span of T. The number of elements is autodeduced from the size of
+ // the shared memory mapping. The number of elements may be autodeduced as
+ // zero, i.e. the mapping is invalid or the size of the mapping isn't large
+ // enough to contain even one T: in that case, an empty span will be returned.
+ // The first element, if any, is guaranteed to be page-aligned.
+ template <typename T>
+ span<T> GetMemoryAsSpan() const {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copying non-trivially-copyable object across memory spaces "
+ "is dangerous");
+ if (!IsValid())
+ return span<T>();
+ size_t count = size() / sizeof(T);
+ return GetMemoryAsSpan<T>(count);
+ }
+
+ // Returns a span of T with |count| elements if the mapping is valid and large
+ // enough to contain |count| elements, or an empty span otherwise. The first
+ // element, if any, is guaranteed to be page-aligned.
+ template <typename T>
+ span<T> GetMemoryAsSpan(size_t count) const {
+ static_assert(std::is_trivially_copyable<T>::value,
+ "Copying non-trivially-copyable object across memory spaces "
+ "is dangerous");
+ if (!IsValid())
+ return span<T>();
+ if (size() / sizeof(T) < count)
+ return span<T>();
+ return span<T>(static_cast<T*>(raw_memory_ptr()), count);
+ }
+
+ // Returns a BufferIterator of T.
+ template <typename T>
+ BufferIterator<T> GetMemoryAsBufferIterator() {
+ return BufferIterator<T>(GetMemoryAsSpan<T>());
+ }
+
+ private:
+ friend WritableSharedMemoryMapping MapAtForTesting(
+ subtle::PlatformSharedMemoryRegion* region,
+ off_t offset,
+ size_t size);
+ friend class ReadOnlySharedMemoryRegion;
+ friend class WritableSharedMemoryRegion;
+ friend class UnsafeSharedMemoryRegion;
+ WritableSharedMemoryMapping(void* address,
+ size_t size,
+ size_t mapped_size,
+ const UnguessableToken& guid);
+
+ DISALLOW_COPY_AND_ASSIGN(WritableSharedMemoryMapping);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_SHARED_MEMORY_MAPPING_H_
diff --git a/security/sandbox/chromium/base/memory/singleton.h b/security/sandbox/chromium/base/memory/singleton.h
new file mode 100644
index 0000000000..87b57919c0
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/singleton.h
@@ -0,0 +1,279 @@
+// 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.
+//
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// PLEASE READ: Do you really need a singleton? If possible, use a
+// function-local static of type base::NoDestructor<T> instead:
+//
+// Factory& Factory::GetInstance() {
+// static base::NoDestructor<Factory> instance;
+// return *instance;
+// }
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+//
+// Singletons make it hard to determine the lifetime of an object, which can
+// lead to buggy code and spurious crashes.
+//
+// Instead of adding another singleton into the mix, try to identify either:
+// a) An existing singleton that can manage your object's lifetime
+// b) Locations where you can deterministically create the object and pass
+// into other objects
+//
+// If you absolutely need a singleton, please keep them as trivial as possible
+// and ideally a leaf dependency. Singletons get problematic when they attempt
+// to do too much in their destructor or have circular dependencies.
+
+#ifndef BASE_MEMORY_SINGLETON_H_
+#define BASE_MEMORY_SINGLETON_H_
+
+#include "base/at_exit.h"
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/lazy_instance_helpers.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+// Default traits for Singleton<Type>. Calls operator new and operator delete on
+// the object. Registers automatic deletion at process exit.
+// Overload if you need arguments or another memory allocation function.
+template<typename Type>
+struct DefaultSingletonTraits {
+ // Allocates the object.
+ static Type* New() {
+ // The parenthesis is very important here; it forces POD type
+ // initialization.
+ return new Type();
+ }
+
+ // Destroys the object.
+ static void Delete(Type* x) {
+ delete x;
+ }
+
+ // Set to true to automatically register deletion of the object on process
+ // exit. See below for the required call that makes this happen.
+ static const bool kRegisterAtExit = true;
+
+#if DCHECK_IS_ON()
+ // Set to false to disallow access on a non-joinable thread. This is
+ // different from kRegisterAtExit because StaticMemorySingletonTraits allows
+ // access on non-joinable threads, and gracefully handles this.
+ static const bool kAllowedToAccessOnNonjoinableThread = false;
+#endif
+};
+
+
+// Alternate traits for use with the Singleton<Type>. Identical to
+// DefaultSingletonTraits except that the Singleton will not be cleaned up
+// at exit.
+template<typename Type>
+struct LeakySingletonTraits : public DefaultSingletonTraits<Type> {
+ static const bool kRegisterAtExit = false;
+#if DCHECK_IS_ON()
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+#endif
+};
+
+// Alternate traits for use with the Singleton<Type>. Allocates memory
+// for the singleton instance from a static buffer. The singleton will
+// be cleaned up at exit, but can't be revived after destruction unless
+// the ResurrectForTesting() method is called.
+//
+// This is useful for a certain category of things, notably logging and
+// tracing, where the singleton instance is of a type carefully constructed to
+// be safe to access post-destruction.
+// In logging and tracing you'll typically get stray calls at odd times, like
+// during static destruction, thread teardown and the like, and there's a
+// termination race on the heap-based singleton - e.g. if one thread calls
+// get(), but then another thread initiates AtExit processing, the first thread
+// may call into an object residing in unallocated memory. If the instance is
+// allocated from the data segment, then this is survivable.
+//
+// The destructor is to deallocate system resources, in this case to unregister
+// a callback the system will invoke when logging levels change. Note that
+// this is also used in e.g. Chrome Frame, where you have to allow for the
+// possibility of loading briefly into someone else's process space, and
+// so leaking is not an option, as that would sabotage the state of your host
+// process once you've unloaded.
+template <typename Type>
+struct StaticMemorySingletonTraits {
+ // WARNING: User has to support a New() which returns null.
+ static Type* New() {
+ // Only constructs once and returns pointer; otherwise returns null.
+ if (subtle::NoBarrier_AtomicExchange(&dead_, 1))
+ return nullptr;
+
+ return new (buffer_) Type();
+ }
+
+ static void Delete(Type* p) {
+ if (p)
+ p->Type::~Type();
+ }
+
+ static const bool kRegisterAtExit = true;
+
+#if DCHECK_IS_ON()
+ static const bool kAllowedToAccessOnNonjoinableThread = true;
+#endif
+
+ static void ResurrectForTesting() { subtle::NoBarrier_Store(&dead_, 0); }
+
+ private:
+ alignas(Type) static char buffer_[sizeof(Type)];
+ // Signal the object was already deleted, so it is not revived.
+ static subtle::Atomic32 dead_;
+};
+
+template <typename Type>
+alignas(Type) char StaticMemorySingletonTraits<Type>::buffer_[sizeof(Type)];
+template <typename Type>
+subtle::Atomic32 StaticMemorySingletonTraits<Type>::dead_ = 0;
+
+// The Singleton<Type, Traits, DifferentiatingType> class manages a single
+// instance of Type which will be created on first use and will be destroyed at
+// normal process exit). The Trait::Delete function will not be called on
+// abnormal process exit.
+//
+// DifferentiatingType is used as a key to differentiate two different
+// singletons having the same memory allocation functions but serving a
+// different purpose. This is mainly used for Locks serving different purposes.
+//
+// Example usage:
+//
+// In your header:
+// namespace base {
+// template <typename T>
+// struct DefaultSingletonTraits;
+// }
+// class FooClass {
+// public:
+// static FooClass* GetInstance(); <-- See comment below on this.
+// void Bar() { ... }
+// private:
+// FooClass() { ... }
+// friend struct base::DefaultSingletonTraits<FooClass>;
+//
+// DISALLOW_COPY_AND_ASSIGN(FooClass);
+// };
+//
+// In your source file:
+// #include "base/memory/singleton.h"
+// FooClass* FooClass::GetInstance() {
+// return base::Singleton<FooClass>::get();
+// }
+//
+// Or for leaky singletons:
+// #include "base/memory/singleton.h"
+// FooClass* FooClass::GetInstance() {
+// return base::Singleton<
+// FooClass, base::LeakySingletonTraits<FooClass>>::get();
+// }
+//
+// And to call methods on FooClass:
+// FooClass::GetInstance()->Bar();
+//
+// NOTE: The method accessing Singleton<T>::get() has to be named as GetInstance
+// and it is important that FooClass::GetInstance() is not inlined in the
+// header. This makes sure that when source files from multiple targets include
+// this header they don't end up with different copies of the inlined code
+// creating multiple copies of the singleton.
+//
+// Singleton<> has no non-static members and doesn't need to actually be
+// instantiated.
+//
+// This class is itself thread-safe. The underlying Type must of course be
+// thread-safe if you want to use it concurrently. Two parameters may be tuned
+// depending on the user's requirements.
+//
+// Glossary:
+// RAE = kRegisterAtExit
+//
+// On every platform, if Traits::RAE is true, the singleton will be destroyed at
+// 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.
+//
+// If Traits::RAE is false, the singleton will not be freed at process exit,
+// thus the singleton will be leaked if it is ever accessed. Traits::RAE
+// shouldn't be false unless absolutely necessary. Remember that the heap where
+// the object is allocated may be destroyed by the CRT anyway.
+//
+// Caveats:
+// (a) Every call to get(), operator->() and operator*() incurs some overhead
+// (16ns on my P4/2.8GHz) to check whether the object has already been
+// initialized. You may wish to cache the result of get(); it will not
+// change.
+//
+// (b) Your factory function must never throw an exception. This class is not
+// exception-safe.
+//
+
+template <typename Type,
+ typename Traits = DefaultSingletonTraits<Type>,
+ typename DifferentiatingType = Type>
+class Singleton {
+ private:
+ // A class T using the Singleton<T> pattern should declare a GetInstance()
+ // method and call Singleton::get() from within that. T may also declare a
+ // GetInstanceIfExists() method to invoke Singleton::GetIfExists().
+ friend Type;
+
+ // This class is safe to be constructed and copy-constructed since it has no
+ // member.
+
+ // Returns a pointer to the one true instance of the class.
+ static Type* get() {
+#if DCHECK_IS_ON()
+ if (!Traits::kAllowedToAccessOnNonjoinableThread)
+ ThreadRestrictions::AssertSingletonAllowed();
+#endif
+
+ return subtle::GetOrCreateLazyPointer(
+ &instance_, &CreatorFunc, nullptr,
+ Traits::kRegisterAtExit ? OnExit : nullptr, nullptr);
+ }
+
+ // Returns the same result as get() if the instance exists but doesn't
+ // construct it (and returns null) if it doesn't.
+ static Type* GetIfExists() {
+#if DCHECK_IS_ON()
+ if (!Traits::kAllowedToAccessOnNonjoinableThread)
+ ThreadRestrictions::AssertSingletonAllowed();
+#endif
+
+ if (!subtle::NoBarrier_Load(&instance_))
+ return nullptr;
+
+ // Need to invoke get() nonetheless as some Traits return null after
+ // destruction (even though |instance_| still holds garbage).
+ return get();
+ }
+
+ // Internal method used as an adaptor for GetOrCreateLazyPointer(). Do not use
+ // outside of that use case.
+ static Type* CreatorFunc(void* /* creator_arg*/) { return Traits::New(); }
+
+ // Adapter function for use with AtExit(). This should be called single
+ // threaded, so don't use atomic operations.
+ // Calling OnExit while singleton is in use by other threads is a mistake.
+ 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<Type*>(subtle::NoBarrier_Load(&instance_)));
+ instance_ = 0;
+ }
+ static subtle::AtomicWord instance_;
+};
+
+template <typename Type, typename Traits, typename DifferentiatingType>
+subtle::AtomicWord Singleton<Type, Traits, DifferentiatingType>::instance_ = 0;
+
+} // namespace base
+
+#endif // BASE_MEMORY_SINGLETON_H_
diff --git a/security/sandbox/chromium/base/memory/unsafe_shared_memory_region.cc b/security/sandbox/chromium/base/memory/unsafe_shared_memory_region.cc
new file mode 100644
index 0000000000..92385d3e78
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/unsafe_shared_memory_region.cc
@@ -0,0 +1,80 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/unsafe_shared_memory_region.h"
+
+#include <utility>
+
+namespace base {
+
+UnsafeSharedMemoryRegion::CreateFunction*
+ UnsafeSharedMemoryRegion::create_hook_ = nullptr;
+
+// static
+UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Create(size_t size) {
+ if (create_hook_)
+ return create_hook_(size);
+
+ subtle::PlatformSharedMemoryRegion handle =
+ subtle::PlatformSharedMemoryRegion::CreateUnsafe(size);
+
+ return UnsafeSharedMemoryRegion(std::move(handle));
+}
+
+// static
+UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Deserialize(
+ subtle::PlatformSharedMemoryRegion handle) {
+ return UnsafeSharedMemoryRegion(std::move(handle));
+}
+
+// static
+subtle::PlatformSharedMemoryRegion
+UnsafeSharedMemoryRegion::TakeHandleForSerialization(
+ UnsafeSharedMemoryRegion region) {
+ return std::move(region.handle_);
+}
+
+UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion() = default;
+UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion(
+ UnsafeSharedMemoryRegion&& region) = default;
+UnsafeSharedMemoryRegion& UnsafeSharedMemoryRegion::operator=(
+ UnsafeSharedMemoryRegion&& region) = default;
+UnsafeSharedMemoryRegion::~UnsafeSharedMemoryRegion() = default;
+
+UnsafeSharedMemoryRegion UnsafeSharedMemoryRegion::Duplicate() const {
+ return UnsafeSharedMemoryRegion(handle_.Duplicate());
+}
+
+WritableSharedMemoryMapping UnsafeSharedMemoryRegion::Map() const {
+ return MapAt(0, handle_.GetSize());
+}
+
+WritableSharedMemoryMapping UnsafeSharedMemoryRegion::MapAt(off_t offset,
+ size_t size) const {
+ if (!IsValid())
+ return {};
+
+ void* memory = nullptr;
+ size_t mapped_size = 0;
+ if (!handle_.MapAt(offset, size, &memory, &mapped_size))
+ return {};
+
+ return WritableSharedMemoryMapping(memory, size, mapped_size,
+ handle_.GetGUID());
+}
+
+bool UnsafeSharedMemoryRegion::IsValid() const {
+ return handle_.IsValid();
+}
+
+UnsafeSharedMemoryRegion::UnsafeSharedMemoryRegion(
+ subtle::PlatformSharedMemoryRegion handle)
+ : handle_(std::move(handle)) {
+ if (handle_.IsValid()) {
+ CHECK_EQ(handle_.GetMode(),
+ subtle::PlatformSharedMemoryRegion::Mode::kUnsafe);
+ }
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/memory/unsafe_shared_memory_region.h b/security/sandbox/chromium/base/memory/unsafe_shared_memory_region.h
new file mode 100644
index 0000000000..559d4c6830
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/unsafe_shared_memory_region.h
@@ -0,0 +1,127 @@
+// Copyright 2018 The Chromium Authors. 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_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_
+#define BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/shared_memory_mapping.h"
+
+namespace base {
+
+// Scoped move-only handle to a region of platform shared memory. The instance
+// owns the platform handle it wraps. Mappings created by this region are
+// writable. These mappings remain valid even after the region handle is moved
+// or destroyed.
+//
+// NOTE: UnsafeSharedMemoryRegion cannot be converted to a read-only region. Use
+// with caution as the region will be writable to any process with a handle to
+// the region.
+//
+// Use this if and only if the following is true:
+// - You do not need to share the region as read-only, and,
+// - You need to have several instances of the region simultaneously, possibly
+// in different processes, that can produce writable mappings.
+
+class BASE_EXPORT UnsafeSharedMemoryRegion {
+ public:
+ using MappingType = WritableSharedMemoryMapping;
+ // Creates a new UnsafeSharedMemoryRegion instance of a given size that can be
+ // used for mapping writable shared memory into the virtual address space.
+ //
+ // This call will fail if the process does not have sufficient permissions to
+ // create a shared memory region itself. See
+ // mojo::CreateUnsafeSharedMemoryRegion in
+ // mojo/public/cpp/base/shared_memory_utils.h for creating a shared memory
+ // region from a an unprivileged process where a broker must be used.
+ static UnsafeSharedMemoryRegion Create(size_t size);
+ using CreateFunction = decltype(Create);
+
+ // Returns an UnsafeSharedMemoryRegion built from a platform-specific handle
+ // that was taken from another UnsafeSharedMemoryRegion instance. Returns an
+ // invalid region iff the |handle| is invalid. CHECK-fails if the |handle|
+ // isn't unsafe.
+ // This should be used only by the code passing a handle across
+ // process boundaries.
+ static UnsafeSharedMemoryRegion Deserialize(
+ subtle::PlatformSharedMemoryRegion handle);
+
+ // Extracts a platform handle from the region. Ownership is transferred to the
+ // returned region object.
+ // This should be used only for sending the handle from the current
+ // process to another.
+ static subtle::PlatformSharedMemoryRegion TakeHandleForSerialization(
+ UnsafeSharedMemoryRegion region);
+
+ // Default constructor initializes an invalid instance.
+ UnsafeSharedMemoryRegion();
+
+ // Move operations are allowed.
+ UnsafeSharedMemoryRegion(UnsafeSharedMemoryRegion&&);
+ UnsafeSharedMemoryRegion& operator=(UnsafeSharedMemoryRegion&&);
+
+ // Destructor closes shared memory region if valid.
+ // All created mappings will remain valid.
+ ~UnsafeSharedMemoryRegion();
+
+ // Duplicates the underlying platform handle and creates a new
+ // UnsafeSharedMemoryRegion instance that owns the newly created handle.
+ // Returns a valid UnsafeSharedMemoryRegion on success, invalid otherwise.
+ // The current region instance remains valid in any case.
+ UnsafeSharedMemoryRegion Duplicate() const;
+
+ // Maps the shared memory region into the caller's address space with write
+ // access. The mapped address is guaranteed to have an alignment of
+ // at least |subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment|.
+ // Returns a valid WritableSharedMemoryMapping instance on success, invalid
+ // otherwise.
+ WritableSharedMemoryMapping Map() const;
+
+ // Same as above, but maps only |size| bytes of the shared memory region
+ // starting with the given |offset|. |offset| must be aligned to value of
+ // |SysInfo::VMAllocationGranularity()|. Returns an invalid mapping if
+ // requested bytes are out of the region limits.
+ WritableSharedMemoryMapping MapAt(off_t offset, size_t size) const;
+
+ // Whether the underlying platform handle is valid.
+ bool IsValid() const;
+
+ // Returns the maximum mapping size that can be created from this region.
+ size_t GetSize() const {
+ DCHECK(IsValid());
+ return handle_.GetSize();
+ }
+
+ // Returns 128-bit GUID of the region.
+ const UnguessableToken& GetGUID() const {
+ DCHECK(IsValid());
+ return handle_.GetGUID();
+ }
+
+ // Returns a platform shared memory handle. |this| remains the owner of the
+ // handle.
+ subtle::PlatformSharedMemoryRegion::PlatformHandle GetPlatformHandle() const {
+ DCHECK(IsValid());
+ return handle_.GetPlatformHandle();
+ }
+
+ private:
+ friend class SharedMemoryHooks;
+
+ explicit UnsafeSharedMemoryRegion(subtle::PlatformSharedMemoryRegion handle);
+
+ static void set_create_hook(CreateFunction* hook) { create_hook_ = hook; }
+
+ static CreateFunction* create_hook_;
+
+ subtle::PlatformSharedMemoryRegion handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnsafeSharedMemoryRegion);
+};
+
+} // namespace base
+
+#endif // BASE_MEMORY_UNSAFE_SHARED_MEMORY_REGION_H_
diff --git a/security/sandbox/chromium/base/memory/weak_ptr.h b/security/sandbox/chromium/base/memory/weak_ptr.h
new file mode 100644
index 0000000000..d274987168
--- /dev/null
+++ b/security/sandbox/chromium/base/memory/weak_ptr.h
@@ -0,0 +1,395 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Weak pointers are pointers to an object that do not affect its lifetime,
+// and which may be invalidated (i.e. reset to nullptr) by the object, or its
+// owner, at any time, most commonly when the object is about to be deleted.
+
+// Weak pointers are useful when an object needs to be accessed safely by one
+// or more objects other than its owner, and those callers can cope with the
+// object vanishing and e.g. tasks posted to it being silently dropped.
+// Reference-counting such an object would complicate the ownership graph and
+// make it harder to reason about the object's lifetime.
+
+// EXAMPLE:
+//
+// class Controller {
+// public:
+// void SpawnWorker() { Worker::StartNew(weak_factory_.GetWeakPtr()); }
+// void WorkComplete(const Result& result) { ... }
+// private:
+// // Member variables should appear before the WeakPtrFactory, to ensure
+// // that any WeakPtrs to Controller are invalidated before its members
+// // variable's destructors are executed, rendering them invalid.
+// WeakPtrFactory<Controller> weak_factory_{this};
+// };
+//
+// class Worker {
+// public:
+// static void StartNew(const WeakPtr<Controller>& controller) {
+// Worker* worker = new Worker(controller);
+// // Kick off asynchronous processing...
+// }
+// private:
+// Worker(const WeakPtr<Controller>& controller)
+// : controller_(controller) {}
+// void DidCompleteAsynchronousProcessing(const Result& result) {
+// if (controller_)
+// controller_->WorkComplete(result);
+// }
+// WeakPtr<Controller> controller_;
+// };
+//
+// With this implementation a caller may use SpawnWorker() to dispatch multiple
+// Workers and subsequently delete the Controller, without waiting for all
+// Workers to have completed.
+
+// ------------------------- IMPORTANT: Thread-safety -------------------------
+
+// Weak pointers may be passed safely between sequences, but must always be
+// dereferenced and invalidated on the same SequencedTaskRunner otherwise
+// checking the pointer would be racey.
+//
+// To ensure correct use, the first time a WeakPtr issued by a WeakPtrFactory
+// is dereferenced, the factory and its WeakPtrs become bound to the calling
+// sequence or current SequencedWorkerPool token, and cannot be dereferenced or
+// invalidated on any other task runner. Bound WeakPtrs can still be handed
+// off to other task runners, e.g. to use to post tasks back to object on the
+// bound sequence.
+//
+// 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 sequence to enforce that other WeakPtr objects will enforce they
+// are used on the desired sequence.
+
+#ifndef BASE_MEMORY_WEAK_PTR_H_
+#define BASE_MEMORY_WEAK_PTR_H_
+
+#include <cstddef>
+#include <type_traits>
+
+#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/synchronization/atomic_flag.h"
+
+namespace base {
+
+template <typename T> class SupportsWeakPtr;
+template <typename T> class WeakPtr;
+
+namespace internal {
+// These classes are part of the WeakPtr implementation.
+// DO NOT USE THESE CLASSES DIRECTLY YOURSELF.
+
+class BASE_EXPORT WeakReference {
+ public:
+ // Although Flag is bound to a specific SequencedTaskRunner, it may be
+ // deleted from another via base::WeakPtr::~WeakPtr().
+ class BASE_EXPORT Flag : public RefCountedThreadSafe<Flag> {
+ public:
+ Flag();
+
+ void Invalidate();
+ bool IsValid() const;
+
+ bool MaybeValid() const;
+
+ void DetachFromSequence();
+
+ private:
+ friend class base::RefCountedThreadSafe<Flag>;
+
+ ~Flag();
+
+ SEQUENCE_CHECKER(sequence_checker_);
+ AtomicFlag invalidated_;
+ };
+
+ WeakReference();
+ explicit WeakReference(const scoped_refptr<Flag>& flag);
+ ~WeakReference();
+
+ WeakReference(WeakReference&& other) noexcept;
+ WeakReference(const WeakReference& other);
+ WeakReference& operator=(WeakReference&& other) noexcept = default;
+ WeakReference& operator=(const WeakReference& other) = default;
+
+ bool IsValid() const;
+ bool MaybeValid() const;
+
+ private:
+ scoped_refptr<const Flag> flag_;
+};
+
+class BASE_EXPORT WeakReferenceOwner {
+ public:
+ WeakReferenceOwner();
+ ~WeakReferenceOwner();
+
+ WeakReference GetRef() const;
+
+ bool HasRefs() const { return !flag_->HasOneRef(); }
+
+ void Invalidate();
+
+ private:
+ scoped_refptr<WeakReference::Flag> flag_;
+};
+
+// This class simplifies the implementation of WeakPtr's type conversion
+// constructor by avoiding the need for a public accessor for ref_. A
+// WeakPtr<T> cannot access the private members of WeakPtr<U>, so this
+// base class gives us a way to access ref_ in a protected fashion.
+class BASE_EXPORT WeakPtrBase {
+ public:
+ WeakPtrBase();
+ ~WeakPtrBase();
+
+ WeakPtrBase(const WeakPtrBase& other) = default;
+ WeakPtrBase(WeakPtrBase&& other) noexcept = default;
+ WeakPtrBase& operator=(const WeakPtrBase& other) = default;
+ WeakPtrBase& operator=(WeakPtrBase&& other) noexcept = default;
+
+ void reset() {
+ ref_ = internal::WeakReference();
+ ptr_ = 0;
+ }
+
+ protected:
+ WeakPtrBase(const WeakReference& ref, uintptr_t ptr);
+
+ WeakReference ref_;
+
+ // This pointer is only valid when ref_.is_valid() is true. Otherwise, its
+ // value is undefined (as opposed to nullptr).
+ uintptr_t ptr_;
+};
+
+// This class provides a common implementation of common functions that would
+// otherwise get instantiated separately for each distinct instantiation of
+// SupportsWeakPtr<>.
+class SupportsWeakPtrBase {
+ public:
+ // A safe static downcast of a WeakPtr<Base> to WeakPtr<Derived>. This
+ // conversion will only compile if there is exists a Base which inherits
+ // from SupportsWeakPtr<Base>. See base::AsWeakPtr() below for a helper
+ // function that makes calling this easier.
+ //
+ // Precondition: t != nullptr
+ template<typename Derived>
+ static WeakPtr<Derived> StaticAsWeakPtr(Derived* t) {
+ static_assert(
+ std::is_base_of<internal::SupportsWeakPtrBase, Derived>::value,
+ "AsWeakPtr argument must inherit from SupportsWeakPtr");
+ return AsWeakPtrImpl<Derived>(t);
+ }
+
+ private:
+ // This template function uses type inference to find a Base of Derived
+ // which is an instance of SupportsWeakPtr<Base>. We can then safely
+ // static_cast the Base* to a Derived*.
+ template <typename Derived, typename Base>
+ static WeakPtr<Derived> AsWeakPtrImpl(SupportsWeakPtr<Base>* t) {
+ WeakPtr<Base> ptr = t->AsWeakPtr();
+ return WeakPtr<Derived>(
+ ptr.ref_, static_cast<Derived*>(reinterpret_cast<Base*>(ptr.ptr_)));
+ }
+};
+
+} // namespace internal
+
+template <typename T> class WeakPtrFactory;
+
+// The WeakPtr class holds a weak reference to |T*|.
+//
+// This class is designed to be used like a normal pointer. You should always
+// null-test an object of this class before using it or invoking a method that
+// may result in the underlying object being destroyed.
+//
+// EXAMPLE:
+//
+// class Foo { ... };
+// WeakPtr<Foo> foo;
+// if (foo)
+// foo->method();
+//
+template <typename T>
+class WeakPtr : public internal::WeakPtrBase {
+ public:
+ WeakPtr() = default;
+ WeakPtr(std::nullptr_t) {}
+
+ // Allow conversion from U to T provided U "is a" T. Note that this
+ // is separate from the (implicit) copy and move constructors.
+ template <typename U>
+ WeakPtr(const WeakPtr<U>& other) : WeakPtrBase(other) {
+ // Need to cast from U* to T* to do pointer adjustment in case of multiple
+ // inheritance. This also enforces the "U is a T" rule.
+ T* t = reinterpret_cast<U*>(other.ptr_);
+ ptr_ = reinterpret_cast<uintptr_t>(t);
+ }
+ template <typename U>
+ WeakPtr(WeakPtr<U>&& other) noexcept : WeakPtrBase(std::move(other)) {
+ // Need to cast from U* to T* to do pointer adjustment in case of multiple
+ // inheritance. This also enforces the "U is a T" rule.
+ T* t = reinterpret_cast<U*>(other.ptr_);
+ ptr_ = reinterpret_cast<uintptr_t>(t);
+ }
+
+ T* get() const {
+ return ref_.IsValid() ? reinterpret_cast<T*>(ptr_) : nullptr;
+ }
+
+ T& operator*() const {
+ DCHECK(get() != nullptr);
+ return *get();
+ }
+ T* operator->() const {
+ DCHECK(get() != nullptr);
+ return get();
+ }
+
+ // Allow conditionals to test validity, e.g. if (weak_ptr) {...};
+ explicit operator bool() const { return get() != nullptr; }
+
+ // Returns false if the WeakPtr is confirmed to be invalid. This call is safe
+ // to make from any thread, e.g. to optimize away unnecessary work, but
+ // operator bool() must always be called, on the correct sequence, before
+ // actually using the pointer.
+ //
+ // Warning: as with any object, this call is only thread-safe if the WeakPtr
+ // instance isn't being re-assigned or reset() racily with this call.
+ bool MaybeValid() const { return ref_.MaybeValid(); }
+
+ // Returns whether the object |this| points to has been invalidated. This can
+ // be used to distinguish a WeakPtr to a destroyed object from one that has
+ // been explicitly set to null.
+ bool WasInvalidated() const { return ptr_ && !ref_.IsValid(); }
+
+ private:
+ friend class internal::SupportsWeakPtrBase;
+ template <typename U> friend class WeakPtr;
+ friend class SupportsWeakPtr<T>;
+ friend class WeakPtrFactory<T>;
+
+ WeakPtr(const internal::WeakReference& ref, T* ptr)
+ : WeakPtrBase(ref, reinterpret_cast<uintptr_t>(ptr)) {}
+};
+
+// Allow callers to compare WeakPtrs against nullptr to test validity.
+template <class T>
+bool operator!=(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
+ return !(weak_ptr == nullptr);
+}
+template <class T>
+bool operator!=(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
+ return weak_ptr != nullptr;
+}
+template <class T>
+bool operator==(const WeakPtr<T>& weak_ptr, std::nullptr_t) {
+ return weak_ptr.get() == nullptr;
+}
+template <class T>
+bool operator==(std::nullptr_t, const WeakPtr<T>& weak_ptr) {
+ return weak_ptr == nullptr;
+}
+
+namespace internal {
+class BASE_EXPORT WeakPtrFactoryBase {
+ protected:
+ WeakPtrFactoryBase(uintptr_t ptr);
+ ~WeakPtrFactoryBase();
+ internal::WeakReferenceOwner weak_reference_owner_;
+ uintptr_t ptr_;
+};
+} // namespace internal
+
+// A class may be composed of a WeakPtrFactory and thereby
+// control how it exposes weak pointers to itself. This is helpful if you only
+// need weak pointers within the implementation of a class. This class is also
+// useful when working with primitive types. For example, you could have a
+// WeakPtrFactory<bool> that is used to pass around a weak reference to a bool.
+template <class T>
+class WeakPtrFactory : public internal::WeakPtrFactoryBase {
+ public:
+ explicit WeakPtrFactory(T* ptr)
+ : WeakPtrFactoryBase(reinterpret_cast<uintptr_t>(ptr)) {}
+
+ ~WeakPtrFactory() = default;
+
+ WeakPtr<T> GetWeakPtr() {
+ return WeakPtr<T>(weak_reference_owner_.GetRef(),
+ reinterpret_cast<T*>(ptr_));
+ }
+
+ // Call this method to invalidate all existing weak pointers.
+ void InvalidateWeakPtrs() {
+ DCHECK(ptr_);
+ weak_reference_owner_.Invalidate();
+ }
+
+ // Call this method to determine if any weak pointers exist.
+ bool HasWeakPtrs() const {
+ DCHECK(ptr_);
+ return weak_reference_owner_.HasRefs();
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WeakPtrFactory);
+};
+
+// A class may extend from SupportsWeakPtr to let others take weak pointers to
+// it. This avoids the class itself implementing boilerplate to dispense weak
+// pointers. However, since SupportsWeakPtr's destructor won't invalidate
+// weak pointers to the class until after the derived class' members have been
+// destroyed, its use can lead to subtle use-after-destroy issues.
+template <class T>
+class SupportsWeakPtr : public internal::SupportsWeakPtrBase {
+ public:
+ SupportsWeakPtr() = default;
+
+ WeakPtr<T> AsWeakPtr() {
+ return WeakPtr<T>(weak_reference_owner_.GetRef(), static_cast<T*>(this));
+ }
+
+ protected:
+ ~SupportsWeakPtr() = default;
+
+ private:
+ internal::WeakReferenceOwner weak_reference_owner_;
+ DISALLOW_COPY_AND_ASSIGN(SupportsWeakPtr);
+};
+
+// Helper function that uses type deduction to safely return a WeakPtr<Derived>
+// when Derived doesn't directly extend SupportsWeakPtr<Derived>, instead it
+// extends a Base that extends SupportsWeakPtr<Base>.
+//
+// EXAMPLE:
+// class Base : public base::SupportsWeakPtr<Producer> {};
+// class Derived : public Base {};
+//
+// Derived derived;
+// base::WeakPtr<Derived> ptr = base::AsWeakPtr(&derived);
+//
+// Note that the following doesn't work (invalid type conversion) since
+// Derived::AsWeakPtr() is WeakPtr<Base> SupportsWeakPtr<Base>::AsWeakPtr(),
+// and there's no way to safely cast WeakPtr<Base> to WeakPtr<Derived> at
+// the caller.
+//
+// base::WeakPtr<Derived> ptr = derived.AsWeakPtr(); // Fails.
+
+template <typename Derived>
+WeakPtr<Derived> AsWeakPtr(Derived* t) {
+ return internal::SupportsWeakPtrBase::StaticAsWeakPtr<Derived>(t);
+}
+
+} // namespace base
+
+#endif // BASE_MEMORY_WEAK_PTR_H_
diff --git a/security/sandbox/chromium/base/no_destructor.h b/security/sandbox/chromium/base/no_destructor.h
new file mode 100644
index 0000000000..21cfef8565
--- /dev/null
+++ b/security/sandbox/chromium/base/no_destructor.h
@@ -0,0 +1,98 @@
+// Copyright 2018 The Chromium Authors. 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_NO_DESTRUCTOR_H_
+#define BASE_NO_DESTRUCTOR_H_
+
+#include <new>
+#include <utility>
+
+namespace base {
+
+// A wrapper that makes it easy to create an object of type T with static
+// storage duration that:
+// - is only constructed on first access
+// - never invokes the destructor
+// in order to satisfy the styleguide ban on global constructors and
+// destructors.
+//
+// Runtime constant example:
+// const std::string& GetLineSeparator() {
+// // Forwards to std::string(size_t, char, const Allocator&) constructor.
+// static const base::NoDestructor<std::string> s(5, '-');
+// return *s;
+// }
+//
+// More complex initialization with a lambda:
+// const std::string& GetSessionNonce() {
+// static const base::NoDestructor<std::string> nonce([] {
+// std::string s(16);
+// crypto::RandString(s.data(), s.size());
+// return s;
+// }());
+// return *nonce;
+// }
+//
+// NoDestructor<T> stores the object inline, so it also avoids a pointer
+// indirection and a malloc. Also note that since C++11 static local variable
+// initialization is thread-safe and so is this pattern. Code should prefer to
+// use NoDestructor<T> over:
+// - A function scoped static T* or T& that is dynamically initialized.
+// - A global base::LazyInstance<T>.
+//
+// Note that since the destructor is never run, this *will* leak memory if used
+// as a stack or member variable. Furthermore, a NoDestructor<T> should never
+// have global scope as that may require a static initializer.
+template <typename T>
+class NoDestructor {
+ public:
+ // Not constexpr; just write static constexpr T x = ...; if the value should
+ // be a constexpr.
+ template <typename... Args>
+ explicit NoDestructor(Args&&... args) {
+ new (storage_) T(std::forward<Args>(args)...);
+ }
+
+ // Allows copy and move construction of the contained type, to allow
+ // construction from an initializer list, e.g. for std::vector.
+ explicit NoDestructor(const T& x) { new (storage_) T(x); }
+ explicit NoDestructor(T&& x) { new (storage_) T(std::move(x)); }
+
+ NoDestructor(const NoDestructor&) = delete;
+ NoDestructor& operator=(const NoDestructor&) = delete;
+
+ ~NoDestructor() = default;
+
+ const T& operator*() const { return *get(); }
+ T& operator*() { return *get(); }
+
+ const T* operator->() const { return get(); }
+ T* operator->() { return get(); }
+
+ const T* get() const { return reinterpret_cast<const T*>(storage_); }
+ T* get() { return reinterpret_cast<T*>(storage_); }
+
+ private:
+ alignas(T) char storage_[sizeof(T)];
+
+#if defined(LEAK_SANITIZER)
+ // TODO(https://crbug.com/812277): This is a hack to work around the fact
+ // that LSan doesn't seem to treat NoDestructor as a root for reachability
+ // analysis. This means that code like this:
+ // static base::NoDestructor<std::vector<int>> v({1, 2, 3});
+ // is considered a leak. Using the standard leak sanitizer annotations to
+ // suppress leaks doesn't work: std::vector is implicitly constructed before
+ // calling the base::NoDestructor constructor.
+ //
+ // Unfortunately, I haven't been able to demonstrate this issue in simpler
+ // reproductions: until that's resolved, hold an explicit pointer to the
+ // placement-new'd object in leak sanitizer mode to help LSan realize that
+ // objects allocated by the contained type are still reachable.
+ T* storage_ptr_ = reinterpret_cast<T*>(storage_);
+#endif // defined(LEAK_SANITIZER)
+};
+
+} // namespace base
+
+#endif // BASE_NO_DESTRUCTOR_H_
diff --git a/security/sandbox/chromium/base/numerics/checked_math.h b/security/sandbox/chromium/base/numerics/checked_math.h
new file mode 100644
index 0000000000..ede3344f82
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/checked_math.h
@@ -0,0 +1,393 @@
+// Copyright 2017 The Chromium Authors. 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_CHECKED_MATH_H_
+#define BASE_NUMERICS_CHECKED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/checked_math_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+class CheckedNumeric {
+ static_assert(std::is_arithmetic<T>::value,
+ "CheckedNumeric<T>: T must be a numeric type.");
+
+ public:
+ using type = T;
+
+ constexpr CheckedNumeric() = default;
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
+ : state_(rhs.state_.value(), rhs.IsValid()) {}
+
+ template <typename Src>
+ friend class CheckedNumeric;
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to CheckedNumerics to make them easier to use.
+ template <typename Src>
+ constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
+ : state_(value) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // This is not an explicit constructor because we want a seamless conversion
+ // from StrictNumeric types.
+ template <typename Src>
+ constexpr CheckedNumeric(
+ StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+ : state_(static_cast<Src>(value)) {}
+
+ // IsValid() - The public API to test if a CheckedNumeric is currently valid.
+ // A range checked destination type can be supplied using the Dst template
+ // parameter.
+ template <typename Dst = T>
+ constexpr bool IsValid() const {
+ return state_.is_valid() &&
+ IsValueInRangeForNumericType<Dst>(state_.value());
+ }
+
+ // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
+ // and is within the range supported by the destination type. Returns true if
+ // successful and false otherwise.
+ template <typename Dst>
+#if defined(__clang__) || defined(__GNUC__)
+ __attribute__((warn_unused_result))
+#elif defined(_MSC_VER)
+ _Check_return_
+#endif
+ constexpr bool
+ AssignIfValid(Dst* result) const {
+ return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+ ? ((*result = static_cast<Dst>(state_.value())), true)
+ : false;
+ }
+
+ // ValueOrDie() - The primary accessor for the underlying value. If the
+ // current state is not valid it will CHECK and crash.
+ // A range checked destination type can be supplied using the Dst template
+ // parameter, which will trigger a CHECK if the value is not in bounds for
+ // the destination.
+ // The CHECK behavior can be overridden by supplying a handler as a
+ // template parameter, for test code, etc. However, the handler cannot access
+ // the underlying value, and it is not available through other means.
+ template <typename Dst = T, class CheckHandler = CheckOnFailure>
+ constexpr StrictNumeric<Dst> ValueOrDie() const {
+ return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+ ? static_cast<Dst>(state_.value())
+ : CheckHandler::template HandleFailure<Dst>();
+ }
+
+ // 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.
+ // A range checked destination type can be supplied using the Dst template
+ // parameter. WARNING: This function may fail to compile or CHECK at runtime
+ // if the supplied default_value is not within range of the destination type.
+ template <typename Dst = T, typename Src>
+ constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
+ return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+ ? static_cast<Dst>(state_.value())
+ : checked_cast<Dst>(default_value);
+ }
+
+ // Returns a checked numeric of the specified type, cast from the current
+ // CheckedNumeric. If the current state is invalid or the destination cannot
+ // represent the result then the returned CheckedNumeric will be invalid.
+ template <typename Dst>
+ constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+ return *this;
+ }
+
+ // This friend method is available solely for providing more detailed logging
+ // in the the tests. Do not implement it in production code, because the
+ // underlying values may change at any time.
+ template <typename U>
+ friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
+
+ // Prototypes for the supported arithmetic operator overloads.
+ template <typename Src>
+ constexpr CheckedNumeric& operator+=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator-=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator*=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator/=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator%=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator<<=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator>>=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator&=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator|=(const Src rhs);
+ template <typename Src>
+ constexpr CheckedNumeric& operator^=(const Src rhs);
+
+ constexpr CheckedNumeric operator-() const {
+ // The negation of two's complement int min is int min, so we simply
+ // check for that in the constexpr case.
+ // We use an optimized code path for a known run-time variable.
+ return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
+ std::is_floating_point<T>::value
+ ? CheckedNumeric<T>(
+ NegateWrapper(state_.value()),
+ IsValid() && (!std::is_signed<T>::value ||
+ std::is_floating_point<T>::value ||
+ NegateWrapper(state_.value()) !=
+ std::numeric_limits<T>::lowest()))
+ : FastRuntimeNegate();
+ }
+
+ constexpr CheckedNumeric operator~() const {
+ return CheckedNumeric<decltype(InvertWrapper(T()))>(
+ InvertWrapper(state_.value()), IsValid());
+ }
+
+ constexpr CheckedNumeric Abs() const {
+ return !IsValueNegative(state_.value()) ? *this : -*this;
+ }
+
+ template <typename U>
+ constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
+ const U rhs) const {
+ using R = typename UnderlyingType<U>::type;
+ using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
+ // TODO(jschuh): This can be converted to the MathOp version and remain
+ // constexpr once we have C++14 support.
+ return CheckedNumeric<result_type>(
+ static_cast<result_type>(
+ IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
+ ? state_.value()
+ : Wrapper<U>::value(rhs)),
+ state_.is_valid() && Wrapper<U>::is_valid(rhs));
+ }
+
+ template <typename U>
+ constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
+ const U rhs) const {
+ using R = typename UnderlyingType<U>::type;
+ using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
+ // TODO(jschuh): This can be converted to the MathOp version and remain
+ // constexpr once we have C++14 support.
+ return CheckedNumeric<result_type>(
+ static_cast<result_type>(
+ IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
+ ? state_.value()
+ : Wrapper<U>::value(rhs)),
+ state_.is_valid() && Wrapper<U>::is_valid(rhs));
+ }
+
+ // 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.
+ constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
+ UnsignedAbs() const {
+ return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+ SafeUnsignedAbs(state_.value()), state_.is_valid());
+ }
+
+ constexpr CheckedNumeric& operator++() {
+ *this += 1;
+ return *this;
+ }
+
+ constexpr CheckedNumeric operator++(int) {
+ CheckedNumeric value = *this;
+ *this += 1;
+ return value;
+ }
+
+ constexpr CheckedNumeric& operator--() {
+ *this -= 1;
+ return *this;
+ }
+
+ constexpr CheckedNumeric operator--(int) {
+ CheckedNumeric value = *this;
+ *this -= 1;
+ return value;
+ }
+
+ // These perform the actual math operations on the CheckedNumerics.
+ // Binary arithmetic operations.
+ template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ T result = 0;
+ bool is_valid =
+ Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
+ Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
+ return CheckedNumeric<T>(result, is_valid);
+ }
+
+ // Assignment arithmetic operations.
+ template <template <typename, typename, typename> class M, typename R>
+ constexpr CheckedNumeric& MathOp(const R rhs) {
+ using Math = typename MathWrapper<M, T, R>::math;
+ T result = 0; // Using T as the destination saves a range check.
+ bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
+ Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
+ *this = CheckedNumeric<T>(result, is_valid);
+ return *this;
+ }
+
+ private:
+ CheckedNumericState<T> state_;
+
+ CheckedNumeric FastRuntimeNegate() const {
+ T result;
+ bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
+ return CheckedNumeric<T>(result, IsValid() && success);
+ }
+
+ template <typename Src>
+ constexpr CheckedNumeric(Src value, bool is_valid)
+ : state_(value, is_valid) {}
+
+ // These wrappers allow us to handle state the same way for both
+ // CheckedNumeric and POD arithmetic types.
+ template <typename Src>
+ struct Wrapper {
+ static constexpr bool is_valid(Src) { return true; }
+ static constexpr Src value(Src value) { return value; }
+ };
+
+ template <typename Src>
+ struct Wrapper<CheckedNumeric<Src>> {
+ static constexpr bool is_valid(const CheckedNumeric<Src> v) {
+ return v.IsValid();
+ }
+ static constexpr Src value(const CheckedNumeric<Src> v) {
+ return v.state_.value();
+ }
+ };
+
+ template <typename Src>
+ struct Wrapper<StrictNumeric<Src>> {
+ static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
+ static constexpr Src value(const StrictNumeric<Src> v) {
+ return static_cast<Src>(v);
+ }
+ };
+};
+
+// Convenience functions to avoid the ugly template disambiguator syntax.
+template <typename Dst, typename Src>
+constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
+ return value.template IsValid<Dst>();
+}
+
+template <typename Dst, typename Src>
+constexpr StrictNumeric<Dst> ValueOrDieForType(
+ const CheckedNumeric<Src> value) {
+ return value.template ValueOrDie<Dst>();
+}
+
+template <typename Dst, typename Src, typename Default>
+constexpr StrictNumeric<Dst> ValueOrDefaultForType(
+ const CheckedNumeric<Src> value,
+ const Default default_value) {
+ return value.template ValueOrDefault<Dst>(default_value);
+}
+
+// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
+// or CheckedNumericType.
+template <typename T>
+constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
+ const T value) {
+ return value;
+}
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
+ const L lhs,
+ const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+ rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
+CheckMathOp(const L lhs, const R rhs, const Args... args) {
+ return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
+
+// These are some extra StrictNumeric operators to support simple pointer
+// arithmetic with our result types. Since wrapping on a pointer is always
+// bad, we trigger the CHECK condition here.
+template <typename L, typename R>
+L* operator+(L* lhs, const StrictNumeric<R> rhs) {
+ uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
+ CheckMul(sizeof(L), static_cast<R>(rhs)))
+ .template ValueOrDie<uintptr_t>();
+ return reinterpret_cast<L*>(result);
+}
+
+template <typename L, typename R>
+L* operator-(L* lhs, const StrictNumeric<R> rhs) {
+ uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
+ CheckMul(sizeof(L), static_cast<R>(rhs)))
+ .template ValueOrDie<uintptr_t>();
+ return reinterpret_cast<L*>(result);
+}
+
+} // namespace internal
+
+using internal::CheckedNumeric;
+using internal::IsValidForType;
+using internal::ValueOrDieForType;
+using internal::ValueOrDefaultForType;
+using internal::MakeCheckedNum;
+using internal::CheckMax;
+using internal::CheckMin;
+using internal::CheckAdd;
+using internal::CheckSub;
+using internal::CheckMul;
+using internal::CheckDiv;
+using internal::CheckMod;
+using internal::CheckLsh;
+using internal::CheckRsh;
+using internal::CheckAnd;
+using internal::CheckOr;
+using internal::CheckXor;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_CHECKED_MATH_H_
diff --git a/security/sandbox/chromium/base/numerics/checked_math_impl.h b/security/sandbox/chromium/base/numerics/checked_math_impl.h
new file mode 100644
index 0000000000..e083389ebf
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/checked_math_impl.h
@@ -0,0 +1,567 @@
+// Copyright 2017 The Chromium Authors. 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_CHECKED_MATH_IMPL_H_
+#define BASE_NUMERICS_CHECKED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math_shared_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+constexpr bool CheckedAddImpl(T x, T y, T* result) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ // 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.
+ using UnsignedDst = typename std::make_unsigned<T>::type;
+ using SignedDst = typename std::make_signed<T>::type;
+ UnsignedDst ux = static_cast<UnsignedDst>(x);
+ UnsignedDst uy = static_cast<UnsignedDst>(y);
+ UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
+ *result = static_cast<T>(uresult);
+ // Addition is valid if the sign of (x + y) is equal to either that of x or
+ // that of y.
+ return (std::is_signed<T>::value)
+ ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
+ : uresult >= uy; // Unsigned is either valid or underflow.
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedAddOp {};
+
+template <typename T, typename U>
+struct CheckedAddOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (CheckedAddFastOp<T, U>::is_supported)
+ return CheckedAddFastOp<T, U>::Do(x, y, result);
+
+ // Double the underlying type up to a full machine word.
+ using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ using Promotion =
+ typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+ IntegerBitsPlusSign<intptr_t>::value),
+ typename BigEnoughPromotion<T, U>::type,
+ FastPromotion>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y))) {
+ return false;
+ }
+
+ Promotion presult = {};
+ bool is_valid = true;
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+ presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
+ } else {
+ is_valid = CheckedAddImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ }
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T>
+constexpr bool CheckedSubImpl(T x, T y, T* result) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ // 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.
+ using UnsignedDst = typename std::make_unsigned<T>::type;
+ using SignedDst = typename std::make_signed<T>::type;
+ UnsignedDst ux = static_cast<UnsignedDst>(x);
+ UnsignedDst uy = static_cast<UnsignedDst>(y);
+ UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
+ *result = static_cast<T>(uresult);
+ // Subtraction is valid if either x and y have same sign, or (x-y) and x have
+ // the same sign.
+ return (std::is_signed<T>::value)
+ ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
+ : x >= y;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedSubOp {};
+
+template <typename T, typename U>
+struct CheckedSubOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (CheckedSubFastOp<T, U>::is_supported)
+ return CheckedSubFastOp<T, U>::Do(x, y, result);
+
+ // Double the underlying type up to a full machine word.
+ using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ using Promotion =
+ typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+ IntegerBitsPlusSign<intptr_t>::value),
+ typename BigEnoughPromotion<T, U>::type,
+ FastPromotion>::type;
+ // Fail if either operand is out of range for the promoted type.
+ // TODO(jschuh): This could be made to work for a broader range of values.
+ if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y))) {
+ return false;
+ }
+
+ Promotion presult = {};
+ bool is_valid = true;
+ if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+ presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
+ } else {
+ is_valid = CheckedSubImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ }
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T>
+constexpr bool CheckedMulImpl(T x, T y, T* result) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ // Since the value of x*y is potentially undefined if we have a signed type,
+ // we compute it using the unsigned type of the same size.
+ using UnsignedDst = typename std::make_unsigned<T>::type;
+ using SignedDst = typename std::make_signed<T>::type;
+ const UnsignedDst ux = SafeUnsignedAbs(x);
+ const UnsignedDst uy = SafeUnsignedAbs(y);
+ UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
+ const bool is_negative =
+ std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
+ *result = is_negative ? 0 - uresult : uresult;
+ // We have a fast out for unsigned identity or zero on the second operand.
+ // After that it's an unsigned overflow check on the absolute value, with
+ // a +1 bound for a negative result.
+ return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
+ ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
+}
+
+template <typename T, typename U, class Enable = void>
+struct CheckedMulOp {};
+
+template <typename T, typename U>
+struct CheckedMulOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (CheckedMulFastOp<T, U>::is_supported)
+ return CheckedMulFastOp<T, U>::Do(x, y, result);
+
+ using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ // Verify the destination type can hold the result (always true for 0).
+ if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) &&
+ x && y)) {
+ return false;
+ }
+
+ Promotion presult = {};
+ bool is_valid = true;
+ if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
+ // The fast op may be available with the promoted type.
+ is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult);
+ } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+ presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
+ } else {
+ is_valid = CheckedMulImpl(static_cast<Promotion>(x),
+ static_cast<Promotion>(y), &presult);
+ }
+ *result = static_cast<V>(presult);
+ return is_valid && IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+// Division just requires a check for a zero denominator or an invalid negation
+// on signed min/-1.
+template <typename T, typename U, class Enable = void>
+struct CheckedDivOp {};
+
+template <typename T, typename U>
+struct CheckedDivOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ if (BASE_NUMERICS_UNLIKELY(!y))
+ return false;
+
+ // The overflow check can be compiled away if we don't have the exact
+ // combination of types needed to trigger this case.
+ using Promotion = typename BigEnoughPromotion<T, U>::type;
+ if (BASE_NUMERICS_UNLIKELY(
+ (std::is_signed<T>::value && std::is_signed<U>::value &&
+ IsTypeInRangeForNumericType<T, Promotion>::value &&
+ static_cast<Promotion>(x) ==
+ std::numeric_limits<Promotion>::lowest() &&
+ y == static_cast<U>(-1)))) {
+ return false;
+ }
+
+ // This branch always compiles away if the above branch wasn't removed.
+ if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+ !IsValueInRangeForNumericType<Promotion>(y)) &&
+ x)) {
+ return false;
+ }
+
+ Promotion presult = Promotion(x) / Promotion(y);
+ *result = static_cast<V>(presult);
+ return IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedModOp {};
+
+template <typename T, typename U>
+struct CheckedModOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ using Promotion = typename BigEnoughPromotion<T, U>::type;
+ if (BASE_NUMERICS_LIKELY(y)) {
+ Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y);
+ *result = static_cast<Promotion>(presult);
+ return IsValueInRangeForNumericType<V>(presult);
+ }
+ return false;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedLshOp {};
+
+// Left shift. Shifts less than 0 or greater than or equal to the number
+// of bits in the promoted type are undefined. Shifts of negative values
+// are undefined. Otherwise it is defined when the result fits.
+template <typename T, typename U>
+struct CheckedLshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V>
+ static constexpr bool Do(T x, U shift, V* result) {
+ // Disallow negative numbers and verify the shift is in bounds.
+ if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
+ as_unsigned(shift) <
+ as_unsigned(std::numeric_limits<T>::digits))) {
+ // Shift as unsigned to avoid undefined behavior.
+ *result = static_cast<V>(as_unsigned(x) << shift);
+ // If the shift can be reversed, we know it was valid.
+ return *result >> shift == x;
+ }
+
+ // Handle the legal corner-case of a full-width signed shift of zero.
+ return std::is_signed<T>::value && !x &&
+ as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedRshOp {};
+
+// Right shift. Shifts less than 0 or greater than or equal to the number
+// of bits in the promoted type are undefined. Otherwise, it is always defined,
+// but a right shift of a negative value is implementation-dependent.
+template <typename T, typename U>
+struct CheckedRshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V>
+ static bool Do(T x, U shift, V* result) {
+ // Use the type conversion push negative values out of range.
+ if (BASE_NUMERICS_LIKELY(as_unsigned(shift) <
+ IntegerBitsPlusSign<T>::value)) {
+ T tmp = x >> shift;
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+ return false;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedAndOp {};
+
+// For simplicity we support only unsigned integer results.
+template <typename T, typename U>
+struct CheckedAndOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedOrOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct CheckedOrOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct CheckedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct CheckedXorOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+// Max doesn't really need to be implemented this way because it can't fail,
+// but it makes the code much cleaner to use the MathOp wrappers.
+template <typename T, typename U, class Enable = void>
+struct CheckedMaxOp {};
+
+template <typename T, typename U>
+struct CheckedMaxOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
+ : static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+// Min doesn't really need to be implemented this way because it can't fail,
+// but it makes the code much cleaner to use the MathOp wrappers.
+template <typename T, typename U, class Enable = void>
+struct CheckedMinOp {};
+
+template <typename T, typename U>
+struct CheckedMinOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename LowestValuePromotion<T, U>::type;
+ template <typename V>
+ static constexpr bool Do(T x, U y, V* result) {
+ result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
+ : static_cast<result_type>(y);
+ *result = static_cast<V>(tmp);
+ return IsValueInRangeForNumericType<V>(tmp);
+ }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
+ template <typename T, typename U> \
+ struct Checked##NAME##Op< \
+ T, U, \
+ typename std::enable_if<std::is_floating_point<T>::value || \
+ std::is_floating_point<U>::value>::type> { \
+ using result_type = typename MaxExponentPromotion<T, U>::type; \
+ template <typename V> \
+ static constexpr bool Do(T x, U y, V* result) { \
+ using Promotion = typename MaxExponentPromotion<T, U>::type; \
+ Promotion presult = x OP y; \
+ *result = static_cast<V>(presult); \
+ return IsValueInRangeForNumericType<V>(presult); \
+ } \
+ };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+// 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 <typename NumericType>
+struct GetNumericRepresentation {
+ static const NumericRepresentation value =
+ std::is_integral<NumericType>::value
+ ? NUMERIC_INTEGER
+ : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING
+ : NUMERIC_UNKNOWN);
+};
+
+template <typename T,
+ NumericRepresentation type = GetNumericRepresentation<T>::value>
+class CheckedNumericState {};
+
+// Integrals require quite a bit of additional housekeeping to manage state.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_INTEGER> {
+ private:
+ // is_valid_ precedes value_ because member intializers in the constructors
+ // are evaluated in field order, and is_valid_ must be read when initializing
+ // value_.
+ bool is_valid_;
+ T value_;
+
+ // Ensures that a type conversion does not trigger undefined behavior.
+ template <typename Src>
+ static constexpr T WellDefinedConversionOrZero(const Src value,
+ const bool is_valid) {
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return (std::is_integral<SrcType>::value || is_valid)
+ ? static_cast<T>(value)
+ : static_cast<T>(0);
+ }
+
+ public:
+ template <typename Src, NumericRepresentation type>
+ friend class CheckedNumericState;
+
+ constexpr CheckedNumericState() : is_valid_(true), value_(0) {}
+
+ template <typename Src>
+ constexpr CheckedNumericState(Src value, bool is_valid)
+ : is_valid_(is_valid && IsValueInRangeForNumericType<T>(value)),
+ value_(WellDefinedConversionOrZero(value, is_valid_)) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : is_valid_(rhs.IsValid()),
+ value_(WellDefinedConversionOrZero(rhs.value(), is_valid_)) {}
+
+ template <typename Src>
+ constexpr explicit CheckedNumericState(Src value)
+ : is_valid_(IsValueInRangeForNumericType<T>(value)),
+ value_(WellDefinedConversionOrZero(value, is_valid_)) {}
+
+ constexpr bool is_valid() const { return is_valid_; }
+ constexpr T value() const { return value_; }
+};
+
+// Floating points maintain their own validity, but need translation wrappers.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_FLOATING> {
+ private:
+ T value_;
+
+ // Ensures that a type conversion does not trigger undefined behavior.
+ template <typename Src>
+ static constexpr T WellDefinedConversionOrNaN(const Src value,
+ const bool is_valid) {
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return (StaticDstRangeRelationToSrcRange<T, SrcType>::value ==
+ NUMERIC_RANGE_CONTAINED ||
+ is_valid)
+ ? static_cast<T>(value)
+ : std::numeric_limits<T>::quiet_NaN();
+ }
+
+ public:
+ template <typename Src, NumericRepresentation type>
+ friend class CheckedNumericState;
+
+ constexpr CheckedNumericState() : value_(0.0) {}
+
+ template <typename Src>
+ constexpr CheckedNumericState(Src value, bool is_valid)
+ : value_(WellDefinedConversionOrNaN(value, is_valid)) {}
+
+ template <typename Src>
+ constexpr explicit CheckedNumericState(Src value)
+ : value_(WellDefinedConversionOrNaN(
+ value,
+ IsValueInRangeForNumericType<T>(value))) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : value_(WellDefinedConversionOrNaN(
+ rhs.value(),
+ rhs.is_valid() && IsValueInRangeForNumericType<T>(rhs.value()))) {}
+
+ constexpr bool is_valid() const {
+ // Written this way because std::isfinite is not reliably constexpr.
+ return MustTreatAsConstexpr(value_)
+ ? value_ <= std::numeric_limits<T>::max() &&
+ value_ >= std::numeric_limits<T>::lowest()
+ : std::isfinite(value_);
+ }
+ constexpr T value() const { return value_; }
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_
diff --git a/security/sandbox/chromium/base/numerics/clamped_math.h b/security/sandbox/chromium/base/numerics/clamped_math.h
new file mode 100644
index 0000000000..37a4cfd22a
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/clamped_math.h
@@ -0,0 +1,264 @@
+// Copyright 2017 The Chromium Authors. 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_CLAMPED_MATH_H_
+#define BASE_NUMERICS_CLAMPED_MATH_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/clamped_math_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T>
+class ClampedNumeric {
+ static_assert(std::is_arithmetic<T>::value,
+ "ClampedNumeric<T>: T must be a numeric type.");
+
+ public:
+ using type = T;
+
+ constexpr ClampedNumeric() : value_(0) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr ClampedNumeric(const ClampedNumeric<Src>& rhs)
+ : value_(saturated_cast<T>(rhs.value_)) {}
+
+ template <typename Src>
+ friend class ClampedNumeric;
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to ClampedNumerics to make them easier to use.
+ template <typename Src>
+ constexpr ClampedNumeric(Src value) // NOLINT(runtime/explicit)
+ : value_(saturated_cast<T>(value)) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ }
+
+ // This is not an explicit constructor because we want a seamless conversion
+ // from StrictNumeric types.
+ template <typename Src>
+ constexpr ClampedNumeric(
+ StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+ : value_(saturated_cast<T>(static_cast<Src>(value))) {}
+
+ // Returns a ClampedNumeric of the specified type, cast from the current
+ // ClampedNumeric, and saturated to the destination type.
+ template <typename Dst>
+ constexpr ClampedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+ return *this;
+ }
+
+ // Prototypes for the supported arithmetic operator overloads.
+ template <typename Src>
+ constexpr ClampedNumeric& operator+=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator-=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator*=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator/=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator%=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator<<=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator>>=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator&=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator|=(const Src rhs);
+ template <typename Src>
+ constexpr ClampedNumeric& operator^=(const Src rhs);
+
+ constexpr ClampedNumeric operator-() const {
+ // The negation of two's complement int min is int min, so that's the
+ // only overflow case where we will saturate.
+ return ClampedNumeric<T>(SaturatedNegWrapper(value_));
+ }
+
+ constexpr ClampedNumeric operator~() const {
+ return ClampedNumeric<decltype(InvertWrapper(T()))>(InvertWrapper(value_));
+ }
+
+ constexpr ClampedNumeric Abs() const {
+ // The negation of two's complement int min is int min, so that's the
+ // only overflow case where we will saturate.
+ return ClampedNumeric<T>(SaturatedAbsWrapper(value_));
+ }
+
+ template <typename U>
+ constexpr ClampedNumeric<typename MathWrapper<ClampedMaxOp, T, U>::type> Max(
+ const U rhs) const {
+ using result_type = typename MathWrapper<ClampedMaxOp, T, U>::type;
+ return ClampedNumeric<result_type>(
+ ClampedMaxOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
+ }
+
+ template <typename U>
+ constexpr ClampedNumeric<typename MathWrapper<ClampedMinOp, T, U>::type> Min(
+ const U rhs) const {
+ using result_type = typename MathWrapper<ClampedMinOp, T, U>::type;
+ return ClampedNumeric<result_type>(
+ ClampedMinOp<T, U>::Do(value_, Wrapper<U>::value(rhs)));
+ }
+
+ // 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.
+ constexpr ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>
+ UnsignedAbs() const {
+ return ClampedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+ SafeUnsignedAbs(value_));
+ }
+
+ constexpr ClampedNumeric& operator++() {
+ *this += 1;
+ return *this;
+ }
+
+ constexpr ClampedNumeric operator++(int) {
+ ClampedNumeric value = *this;
+ *this += 1;
+ return value;
+ }
+
+ constexpr ClampedNumeric& operator--() {
+ *this -= 1;
+ return *this;
+ }
+
+ constexpr ClampedNumeric operator--(int) {
+ ClampedNumeric value = *this;
+ *this -= 1;
+ return value;
+ }
+
+ // These perform the actual math operations on the ClampedNumerics.
+ // Binary arithmetic operations.
+ template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+ static constexpr ClampedNumeric MathOp(const L lhs, const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return ClampedNumeric<T>(
+ Math::template Do<T>(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs)));
+ }
+
+ // Assignment arithmetic operations.
+ template <template <typename, typename, typename> class M, typename R>
+ constexpr ClampedNumeric& MathOp(const R rhs) {
+ using Math = typename MathWrapper<M, T, R>::math;
+ *this =
+ ClampedNumeric<T>(Math::template Do<T>(value_, Wrapper<R>::value(rhs)));
+ return *this;
+ }
+
+ template <typename Dst>
+ constexpr operator Dst() const {
+ return saturated_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(
+ value_);
+ }
+
+ // This method extracts the raw integer value without saturating it to the
+ // destination type as the conversion operator does. This is useful when
+ // e.g. assigning to an auto type or passing as a deduced template parameter.
+ constexpr T RawValue() const { return value_; }
+
+ private:
+ T value_;
+
+ // These wrappers allow us to handle state the same way for both
+ // ClampedNumeric and POD arithmetic types.
+ template <typename Src>
+ struct Wrapper {
+ static constexpr Src value(Src value) {
+ return static_cast<typename UnderlyingType<Src>::type>(value);
+ }
+ };
+};
+
+// Convience wrapper to return a new ClampedNumeric from the provided arithmetic
+// or ClampedNumericType.
+template <typename T>
+constexpr ClampedNumeric<typename UnderlyingType<T>::type> MakeClampedNum(
+ const T value) {
+ return value;
+}
+
+#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
+// Overload the ostream output operator to make logging work nicely.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const ClampedNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+#endif
+
+// These implement the variadic wrapper for the math operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+constexpr ClampedNumeric<typename MathWrapper<M, L, R>::type> ClampMathOp(
+ const L lhs,
+ const R rhs) {
+ using Math = typename MathWrapper<M, L, R>::math;
+ return ClampedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+ rhs);
+}
+
+// General purpose wrapper template for arithmetic operations.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+constexpr ClampedNumeric<typename ResultType<M, L, R, Args...>::type>
+ClampMathOp(const L lhs, const R rhs, const Args... args) {
+ return ClampMathOp<M>(ClampMathOp<M>(lhs, rhs), args...);
+}
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Add, +, +=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Sub, -, -=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mul, *, *=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Div, /, /=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Mod, %, %=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Lsh, <<, <<=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Rsh, >>, >>=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, And, &, &=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Or, |, |=)
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Clamped, Clamp, Xor, ^, ^=)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Max)
+BASE_NUMERIC_ARITHMETIC_VARIADIC(Clamped, Clamp, Min)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLess, <)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsLessOrEqual, <=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreater, >)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsGreaterOrEqual, >=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsEqual, ==)
+BASE_NUMERIC_COMPARISON_OPERATORS(Clamped, IsNotEqual, !=)
+
+} // namespace internal
+
+using internal::ClampedNumeric;
+using internal::MakeClampedNum;
+using internal::ClampMax;
+using internal::ClampMin;
+using internal::ClampAdd;
+using internal::ClampSub;
+using internal::ClampMul;
+using internal::ClampDiv;
+using internal::ClampMod;
+using internal::ClampLsh;
+using internal::ClampRsh;
+using internal::ClampAnd;
+using internal::ClampOr;
+using internal::ClampXor;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_CLAMPED_MATH_H_
diff --git a/security/sandbox/chromium/base/numerics/clamped_math_impl.h b/security/sandbox/chromium/base/numerics/clamped_math_impl.h
new file mode 100644
index 0000000000..303a7e945a
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/clamped_math_impl.h
@@ -0,0 +1,341 @@
+// Copyright 2017 The Chromium Authors. 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_CLAMPED_MATH_IMPL_H_
+#define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/checked_math.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math_shared_impl.h"
+
+namespace base {
+namespace internal {
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_signed<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+ return MustTreatAsConstexpr(value) || !ClampedNegFastOp<T>::is_supported
+ ? (NegateWrapper(value) != std::numeric_limits<T>::lowest()
+ ? NegateWrapper(value)
+ : std::numeric_limits<T>::max())
+ : ClampedNegFastOp<T>::Do(value);
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value &&
+ !std::is_signed<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+ return T(0);
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedNegWrapper(T value) {
+ return -value;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+ // The calculation below is a static identity for unsigned types, but for
+ // signed integer types it provides a non-branching, saturated absolute value.
+ // This works because SafeUnsignedAbs() returns an unsigned type, which can
+ // represent the absolute value of all negative numbers of an equal-width
+ // integer type. The call to IsValueNegative() then detects overflow in the
+ // special case of numeric_limits<T>::min(), by evaluating the bit pattern as
+ // a signed integer value. If it is the overflow case, we end up subtracting
+ // one from the unsigned result, thus saturating to numeric_limits<T>::max().
+ return static_cast<T>(SafeUnsignedAbs(value) -
+ IsValueNegative<T>(SafeUnsignedAbs(value)));
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T SaturatedAbsWrapper(T value) {
+ return value < 0 ? -value : value;
+}
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAddOp {};
+
+template <typename T, typename U>
+struct ClampedAddOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ if (ClampedAddFastOp<T, U>::is_supported)
+ return ClampedAddFastOp<T, U>::template Do<V>(x, y);
+
+ static_assert(std::is_same<V, result_type>::value ||
+ IsTypeInRangeForNumericType<U, V>::value,
+ "The saturation result cannot be determined from the "
+ "provided types.");
+ const V saturated = CommonMaxOrMin<V>(IsValueNegative(y));
+ V result = {};
+ return BASE_NUMERICS_LIKELY((CheckedAddOp<T, U>::Do(x, y, &result)))
+ ? result
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedSubOp {};
+
+template <typename T, typename U>
+struct ClampedSubOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (ClampedSubFastOp<T, U>::is_supported)
+ return ClampedSubFastOp<T, U>::template Do<V>(x, y);
+
+ static_assert(std::is_same<V, result_type>::value ||
+ IsTypeInRangeForNumericType<U, V>::value,
+ "The saturation result cannot be determined from the "
+ "provided types.");
+ const V saturated = CommonMaxOrMin<V>(!IsValueNegative(y));
+ V result = {};
+ return BASE_NUMERICS_LIKELY((CheckedSubOp<T, U>::Do(x, y, &result)))
+ ? result
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMulOp {};
+
+template <typename T, typename U>
+struct ClampedMulOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ // TODO(jschuh) Make this "constexpr if" once we're C++17.
+ if (ClampedMulFastOp<T, U>::is_supported)
+ return ClampedMulFastOp<T, U>::template Do<V>(x, y);
+
+ V result = {};
+ const V saturated =
+ CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+ return BASE_NUMERICS_LIKELY((CheckedMulOp<T, U>::Do(x, y, &result)))
+ ? result
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedDivOp {};
+
+template <typename T, typename U>
+struct ClampedDivOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ V result = {};
+ if (BASE_NUMERICS_LIKELY((CheckedDivOp<T, U>::Do(x, y, &result))))
+ return result;
+ // Saturation goes to max, min, or NaN (if x is zero).
+ return x ? CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y))
+ : SaturationDefaultLimits<V>::NaN();
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedModOp {};
+
+template <typename T, typename U>
+struct ClampedModOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ V result = {};
+ return BASE_NUMERICS_LIKELY((CheckedModOp<T, U>::Do(x, y, &result)))
+ ? result
+ : x;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedLshOp {};
+
+// Left shift. Non-zero values saturate in the direction of the sign. A zero
+// shifted by any value always results in zero.
+template <typename T, typename U>
+struct ClampedLshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U shift) {
+ static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+ if (BASE_NUMERICS_LIKELY(shift < std::numeric_limits<T>::digits)) {
+ // Shift as unsigned to avoid undefined behavior.
+ V result = static_cast<V>(as_unsigned(x) << shift);
+ // If the shift can be reversed, we know it was valid.
+ if (BASE_NUMERICS_LIKELY(result >> shift == x))
+ return result;
+ }
+ return x ? CommonMaxOrMin<V>(IsValueNegative(x)) : 0;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedRshOp {};
+
+// Right shift. Negative values saturate to -1. Positive or 0 saturates to 0.
+template <typename T, typename U>
+struct ClampedRshOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = T;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U shift) {
+ static_assert(!std::is_signed<U>::value, "Shift value must be unsigned.");
+ // Signed right shift is odd, because it saturates to -1 or 0.
+ const V saturated = as_unsigned(V(0)) - IsValueNegative(x);
+ return BASE_NUMERICS_LIKELY(shift < IntegerBitsPlusSign<T>::value)
+ ? saturated_cast<V>(x >> shift)
+ : saturated;
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedAndOp {};
+
+template <typename T, typename U>
+struct ClampedAndOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) & static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedOrOp {};
+
+// For simplicity we promote to unsigned integers.
+template <typename T, typename U>
+struct ClampedOrOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) | static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedXorOp {};
+
+// For simplicity we support only unsigned integers.
+template <typename T, typename U>
+struct ClampedXorOp<T,
+ U,
+ typename std::enable_if<std::is_integral<T>::value &&
+ std::is_integral<U>::value>::type> {
+ using result_type = typename std::make_unsigned<
+ typename MaxExponentPromotion<T, U>::type>::type;
+ template <typename V>
+ static constexpr V Do(T x, U y) {
+ return static_cast<result_type>(x) ^ static_cast<result_type>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMaxOp {};
+
+template <typename T, typename U>
+struct ClampedMaxOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename MaxExponentPromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x)
+ : saturated_cast<V>(y);
+ }
+};
+
+template <typename T, typename U, class Enable = void>
+struct ClampedMinOp {};
+
+template <typename T, typename U>
+struct ClampedMinOp<
+ T,
+ U,
+ typename std::enable_if<std::is_arithmetic<T>::value &&
+ std::is_arithmetic<U>::value>::type> {
+ using result_type = typename LowestValuePromotion<T, U>::type;
+ template <typename V = result_type>
+ static constexpr V Do(T x, U y) {
+ return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x)
+ : saturated_cast<V>(y);
+ }
+};
+
+// This is just boilerplate that wraps the standard floating point arithmetic.
+// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
+ template <typename T, typename U> \
+ struct Clamped##NAME##Op< \
+ T, U, \
+ typename std::enable_if<std::is_floating_point<T>::value || \
+ std::is_floating_point<U>::value>::type> { \
+ using result_type = typename MaxExponentPromotion<T, U>::type; \
+ template <typename V = result_type> \
+ static constexpr V Do(T x, U y) { \
+ return saturated_cast<V>(x OP y); \
+ } \
+ };
+
+BASE_FLOAT_ARITHMETIC_OPS(Add, +)
+BASE_FLOAT_ARITHMETIC_OPS(Sub, -)
+BASE_FLOAT_ARITHMETIC_OPS(Mul, *)
+BASE_FLOAT_ARITHMETIC_OPS(Div, /)
+
+#undef BASE_FLOAT_ARITHMETIC_OPS
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_
diff --git a/security/sandbox/chromium/base/numerics/safe_conversions.h b/security/sandbox/chromium/base/numerics/safe_conversions.h
new file mode 100644
index 0000000000..b9636fec42
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_conversions.h
@@ -0,0 +1,358 @@
+// 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_CONVERSIONS_H_
+#define BASE_NUMERICS_SAFE_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions_impl.h"
+
+#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
+#include "base/numerics/safe_conversions_arm_impl.h"
+#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (1)
+#else
+#define BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS (0)
+#endif
+
+#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
+#include <ostream>
+#endif
+
+namespace base {
+namespace internal {
+
+#if !BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+template <typename Dst, typename Src>
+struct SaturateFastAsmOp {
+ static const bool is_supported = false;
+ static constexpr Dst Do(Src) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<Dst>();
+ }
+};
+#endif // BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+#undef BASE_HAS_OPTIMIZED_SAFE_CONVERSIONS
+
+// The following special case a few specific integer conversions where we can
+// eke out better performance than range checking.
+template <typename Dst, typename Src, typename Enable = void>
+struct IsValueInRangeFastOp {
+ static const bool is_supported = false;
+ static constexpr bool Do(Src value) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+// Signed to signed range comparison.
+template <typename Dst, typename Src>
+struct IsValueInRangeFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<
+ std::is_integral<Dst>::value && std::is_integral<Src>::value &&
+ std::is_signed<Dst>::value && std::is_signed<Src>::value &&
+ !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+ static const bool is_supported = true;
+
+ static constexpr bool Do(Src value) {
+ // Just downcast to the smaller type, sign extend it back to the original
+ // type, and then see if it matches the original value.
+ return value == static_cast<Dst>(value);
+ }
+};
+
+// Signed to unsigned range comparison.
+template <typename Dst, typename Src>
+struct IsValueInRangeFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<
+ std::is_integral<Dst>::value && std::is_integral<Src>::value &&
+ !std::is_signed<Dst>::value && std::is_signed<Src>::value &&
+ !IsTypeInRangeForNumericType<Dst, Src>::value>::type> {
+ static const bool is_supported = true;
+
+ static constexpr bool Do(Src value) {
+ // We cast a signed as unsigned to overflow negative values to the top,
+ // then compare against whichever maximum is smaller, as our upper bound.
+ return as_unsigned(value) <= as_unsigned(CommonMax<Src, Dst>());
+ }
+};
+
+// Convenience function that returns true if the supplied value is in range
+// for the destination type.
+template <typename Dst, typename Src>
+constexpr bool IsValueInRangeForNumericType(Src value) {
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return internal::IsValueInRangeFastOp<Dst, SrcType>::is_supported
+ ? internal::IsValueInRangeFastOp<Dst, SrcType>::Do(
+ static_cast<SrcType>(value))
+ : internal::DstRangeRelationToSrcRange<Dst>(
+ static_cast<SrcType>(value))
+ .IsValid();
+}
+
+// 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.
+template <typename Dst,
+ class CheckHandler = internal::CheckOnFailure,
+ typename Src>
+constexpr Dst checked_cast(Src value) {
+ // This throws a compile-time error on evaluating the constexpr if it can be
+ // determined at compile-time as failing, otherwise it will CHECK at runtime.
+ using SrcType = typename internal::UnderlyingType<Src>::type;
+ return BASE_NUMERICS_LIKELY((IsValueInRangeForNumericType<Dst>(value)))
+ ? static_cast<Dst>(static_cast<SrcType>(value))
+ : CheckHandler::template HandleFailure<Dst>();
+}
+
+// Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN.
+// You may provide your own limits (e.g. to saturated_cast) so long as you
+// implement all of the static constexpr member functions in the class below.
+template <typename T>
+struct SaturationDefaultLimits : public std::numeric_limits<T> {
+ static constexpr T NaN() {
+ return std::numeric_limits<T>::has_quiet_NaN
+ ? std::numeric_limits<T>::quiet_NaN()
+ : T();
+ }
+ using std::numeric_limits<T>::max;
+ static constexpr T Overflow() {
+ return std::numeric_limits<T>::has_infinity
+ ? std::numeric_limits<T>::infinity()
+ : std::numeric_limits<T>::max();
+ }
+ using std::numeric_limits<T>::lowest;
+ static constexpr T Underflow() {
+ return std::numeric_limits<T>::has_infinity
+ ? std::numeric_limits<T>::infinity() * -1
+ : std::numeric_limits<T>::lowest();
+ }
+};
+
+template <typename Dst, template <typename> class S, typename Src>
+constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) {
+ // For some reason clang generates much better code when the branch is
+ // structured exactly this way, rather than a sequence of checks.
+ return !constraint.IsOverflowFlagSet()
+ ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value)
+ : S<Dst>::Underflow())
+ // Skip this check for integral Src, which cannot be NaN.
+ : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet()
+ ? S<Dst>::Overflow()
+ : S<Dst>::NaN());
+}
+
+// We can reduce the number of conditions and get slightly better performance
+// for normal signed and unsigned integer ranges. And in the specific case of
+// Arm, we can use the optimized saturation instructions.
+template <typename Dst, typename Src, typename Enable = void>
+struct SaturateFastOp {
+ static const bool is_supported = false;
+ static constexpr Dst Do(Src value) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<Dst>();
+ }
+};
+
+template <typename Dst, typename Src>
+struct SaturateFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<std::is_integral<Src>::value &&
+ std::is_integral<Dst>::value &&
+ SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
+ static const bool is_supported = true;
+ static Dst Do(Src value) { return SaturateFastAsmOp<Dst, Src>::Do(value); }
+};
+
+template <typename Dst, typename Src>
+struct SaturateFastOp<
+ Dst,
+ Src,
+ typename std::enable_if<std::is_integral<Src>::value &&
+ std::is_integral<Dst>::value &&
+ !SaturateFastAsmOp<Dst, Src>::is_supported>::type> {
+ static const bool is_supported = true;
+ static Dst Do(Src value) {
+ // The exact order of the following is structured to hit the correct
+ // optimization heuristics across compilers. Do not change without
+ // checking the emitted code.
+ Dst saturated = CommonMaxOrMin<Dst, Src>(
+ IsMaxInRangeForNumericType<Dst, Src>() ||
+ (!IsMinInRangeForNumericType<Dst, Src>() && IsValueNegative(value)));
+ return BASE_NUMERICS_LIKELY(IsValueInRangeForNumericType<Dst>(value))
+ ? static_cast<Dst>(value)
+ : saturated;
+ }
+};
+
+// saturated_cast<> is analogous to static_cast<> for numeric types, except
+// that the specified numeric conversion will saturate by default rather than
+// overflow or underflow, and NaN assignment to an integral will return 0.
+// All boundary condition behaviors can be overriden with a custom handler.
+template <typename Dst,
+ template <typename> class SaturationHandler = SaturationDefaultLimits,
+ typename Src>
+constexpr Dst saturated_cast(Src value) {
+ using SrcType = typename UnderlyingType<Src>::type;
+ return !IsCompileTimeConstant(value) &&
+ SaturateFastOp<Dst, SrcType>::is_supported &&
+ std::is_same<SaturationHandler<Dst>,
+ SaturationDefaultLimits<Dst>>::value
+ ? SaturateFastOp<Dst, SrcType>::Do(static_cast<SrcType>(value))
+ : saturated_cast_impl<Dst, SaturationHandler, SrcType>(
+ static_cast<SrcType>(value),
+ DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(
+ static_cast<SrcType>(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 <typename Dst, typename Src>
+constexpr Dst strict_cast(Src value) {
+ using SrcType = typename UnderlyingType<Src>::type;
+ static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric.");
+ static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
+
+ // If you got here from a compiler error, it's because you tried to assign
+ // from a source type to a destination type that has insufficient range.
+ // The solution may be to change the destination type you're assigning to,
+ // and use one large enough to represent the source.
+ // Alternatively, you may be better served with the checked_cast<> or
+ // saturated_cast<> template functions for your particular use case.
+ static_assert(StaticDstRangeRelationToSrcRange<Dst, SrcType>::value ==
+ NUMERIC_RANGE_CONTAINED,
+ "The source type is out of range for the destination type. "
+ "Please see strict_cast<> comments for more information.");
+
+ return static_cast<Dst>(static_cast<SrcType>(value));
+}
+
+// Some wrappers to statically check that a type is in range.
+template <typename Dst, typename Src, class Enable = void>
+struct IsNumericRangeContained {
+ static const bool value = false;
+};
+
+template <typename Dst, typename Src>
+struct IsNumericRangeContained<
+ Dst,
+ Src,
+ typename std::enable_if<ArithmeticOrUnderlyingEnum<Dst>::value &&
+ ArithmeticOrUnderlyingEnum<Src>::value>::type> {
+ static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ NUMERIC_RANGE_CONTAINED;
+};
+
+// 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 the actual value being assigned.
+template <typename T>
+class StrictNumeric {
+ public:
+ using type = T;
+
+ constexpr StrictNumeric() : value_(0) {}
+
+ // Copy constructor.
+ template <typename Src>
+ constexpr StrictNumeric(const StrictNumeric<Src>& rhs)
+ : value_(strict_cast<T>(rhs.value_)) {}
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to StrictNumerics to make them easier to use.
+ template <typename Src>
+ constexpr StrictNumeric(Src value) // NOLINT(runtime/explicit)
+ : value_(strict_cast<T>(value)) {}
+
+ // If you got here from a compiler error, it's because you tried to assign
+ // from a source type to a destination type that has insufficient range.
+ // The solution may be to change the destination type you're assigning to,
+ // and use one large enough to represent the source.
+ // If you're assigning from a CheckedNumeric<> class, you may be able to use
+ // the AssignIfValid() member function, specify a narrower destination type to
+ // the member value functions (e.g. val.template ValueOrDie<Dst>()), use one
+ // of the value helper functions (e.g. ValueOrDieForType<Dst>(val)).
+ // If you've encountered an _ambiguous overload_ you can use a static_cast<>
+ // to explicitly cast the result to the destination type.
+ // If none of that works, you may be better served with the checked_cast<> or
+ // saturated_cast<> template functions for your particular use case.
+ template <typename Dst,
+ typename std::enable_if<
+ IsNumericRangeContained<Dst, T>::value>::type* = nullptr>
+ constexpr operator Dst() const {
+ return static_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_);
+ }
+
+ private:
+ const T value_;
+};
+
+// Convience wrapper returns a StrictNumeric from the provided arithmetic type.
+template <typename T>
+constexpr StrictNumeric<typename UnderlyingType<T>::type> MakeStrictNum(
+ const T value) {
+ return value;
+}
+
+#if !BASE_NUMERICS_DISABLE_OSTREAM_OPERATORS
+// Overload the ostream output operator to make logging work nicely.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) {
+ os << static_cast<T>(value);
+ return os;
+}
+#endif
+
+#define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \
+ template <typename L, typename R, \
+ typename std::enable_if< \
+ internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \
+ constexpr bool operator OP(const L lhs, const R rhs) { \
+ return SafeCompare<NAME, typename UnderlyingType<L>::type, \
+ typename UnderlyingType<R>::type>(lhs, rhs); \
+ }
+
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==)
+BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=)
+
+} // namespace internal
+
+using internal::as_signed;
+using internal::as_unsigned;
+using internal::checked_cast;
+using internal::strict_cast;
+using internal::saturated_cast;
+using internal::SafeUnsignedAbs;
+using internal::StrictNumeric;
+using internal::MakeStrictNum;
+using internal::IsValueInRangeForNumericType;
+using internal::IsTypeInRangeForNumericType;
+using internal::IsValueNegative;
+
+// Explicitly make a shorter size_t alias for convenience.
+using SizeT = StrictNumeric<size_t>;
+
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_
diff --git a/security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h b/security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h
new file mode 100644
index 0000000000..cf31072b9b
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_conversions_arm_impl.h
@@ -0,0 +1,51 @@
+// Copyright 2017 The Chromium Authors. 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_CONVERSIONS_ARM_IMPL_H_
+#define BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions_impl.h"
+
+namespace base {
+namespace internal {
+
+// Fast saturation to a destination type.
+template <typename Dst, typename Src>
+struct SaturateFastAsmOp {
+ static constexpr bool is_supported =
+ std::is_signed<Src>::value && std::is_integral<Dst>::value &&
+ std::is_integral<Src>::value &&
+ IntegerBitsPlusSign<Src>::value <= IntegerBitsPlusSign<int32_t>::value &&
+ IntegerBitsPlusSign<Dst>::value <= IntegerBitsPlusSign<int32_t>::value &&
+ !IsTypeInRangeForNumericType<Dst, Src>::value;
+
+ __attribute__((always_inline)) static Dst Do(Src value) {
+ int32_t src = value;
+ typename std::conditional<std::is_signed<Dst>::value, int32_t,
+ uint32_t>::type result;
+ if (std::is_signed<Dst>::value) {
+ asm("ssat %[dst], %[shift], %[src]"
+ : [dst] "=r"(result)
+ : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value <= 32
+ ? IntegerBitsPlusSign<Dst>::value
+ : 32));
+ } else {
+ asm("usat %[dst], %[shift], %[src]"
+ : [dst] "=r"(result)
+ : [src] "r"(src), [shift] "n"(IntegerBitsPlusSign<Dst>::value < 32
+ ? IntegerBitsPlusSign<Dst>::value
+ : 31));
+ }
+ return static_cast<Dst>(result);
+ }
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_CONVERSIONS_ARM_IMPL_H_
diff --git a/security/sandbox/chromium/base/numerics/safe_conversions_impl.h b/security/sandbox/chromium/base/numerics/safe_conversions_impl.h
new file mode 100644
index 0000000000..7c5ca68c3b
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_conversions_impl.h
@@ -0,0 +1,851 @@
+// 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_CONVERSIONS_IMPL_H_
+#define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_
+
+#include <stdint.h>
+
+#include <limits>
+#include <type_traits>
+
+#if defined(__GNUC__) || defined(__clang__)
+#define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1)
+#define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define BASE_NUMERICS_LIKELY(x) (x)
+#define BASE_NUMERICS_UNLIKELY(x) (x)
+#endif
+
+namespace base {
+namespace internal {
+
+// The std library doesn't provide a binary max_exponent for integers, however
+// we can compute an analog using std::numeric_limits<>::digits.
+template <typename NumericType>
+struct MaxExponent {
+ static const int value = std::is_floating_point<NumericType>::value
+ ? std::numeric_limits<NumericType>::max_exponent
+ : std::numeric_limits<NumericType>::digits + 1;
+};
+
+// The number of bits (including the sign) in an integer. Eliminates sizeof
+// hacks.
+template <typename NumericType>
+struct IntegerBitsPlusSign {
+ static const int value = std::numeric_limits<NumericType>::digits +
+ std::is_signed<NumericType>::value;
+};
+
+// Helper templates for integer manipulations.
+
+template <typename Integer>
+struct PositionOfSignBit {
+ static const size_t value = IntegerBitsPlusSign<Integer>::value - 1;
+};
+
+// Determines if a numeric value is negative without throwing compiler
+// warnings on: unsigned(value) < 0.
+template <typename T,
+ typename std::enable_if<std::is_signed<T>::value>::type* = nullptr>
+constexpr bool IsValueNegative(T value) {
+ static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
+ return value < 0;
+}
+
+template <typename T,
+ typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr>
+constexpr bool IsValueNegative(T) {
+ static_assert(std::is_arithmetic<T>::value, "Argument must be numeric.");
+ return false;
+}
+
+// This performs a fast negation, returning a signed value. It works on unsigned
+// arguments, but probably doesn't do what you want for any unsigned value
+// larger than max / 2 + 1 (i.e. signed min cast to unsigned).
+template <typename T>
+constexpr typename std::make_signed<T>::type ConditionalNegate(
+ T x,
+ bool is_negative) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ using SignedT = typename std::make_signed<T>::type;
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ return static_cast<SignedT>(
+ (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative);
+}
+
+// This performs a safe, absolute value via unsigned overflow.
+template <typename T>
+constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) {
+ static_assert(std::is_integral<T>::value, "Type must be integral");
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ return IsValueNegative(value)
+ ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value))
+ : static_cast<UnsignedT>(value);
+}
+
+// This allows us to switch paths on known compile-time constants.
+#if defined(__clang__) || defined(__GNUC__)
+constexpr bool CanDetectCompileTimeConstant() {
+ return true;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T v) {
+ return __builtin_constant_p(v);
+}
+#else
+constexpr bool CanDetectCompileTimeConstant() {
+ return false;
+}
+template <typename T>
+constexpr bool IsCompileTimeConstant(const T) {
+ return false;
+}
+#endif
+template <typename T>
+constexpr bool MustTreatAsConstexpr(const T v) {
+ // Either we can't detect a compile-time constant, and must always use the
+ // constexpr path, or we know we have a compile-time constant.
+ return !CanDetectCompileTimeConstant() || IsCompileTimeConstant(v);
+}
+
+// Forces a crash, like a CHECK(false). Used for numeric boundary errors.
+// Also used in a constexpr template to trigger a compilation failure on
+// an error condition.
+struct CheckOnFailure {
+ template <typename T>
+ static T HandleFailure() {
+#if defined(_MSC_VER)
+ __debugbreak();
+#elif defined(__GNUC__) || defined(__clang__)
+ __builtin_trap();
+#else
+ ((void)(*(volatile char*)0 = 0));
+#endif
+ return T();
+ }
+};
+
+enum IntegerRepresentation {
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED
+};
+
+// A range for a given nunmeric Src type is contained for a given numeric Dst
+// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
+// numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true.
+// We implement this as template specializations rather than simple static
+// comparisons to ensure type correctness in our comparisons.
+enum NumericRangeRepresentation {
+ NUMERIC_RANGE_NOT_CONTAINED,
+ NUMERIC_RANGE_CONTAINED
+};
+
+// Helper templates to statically determine if our destination type can contain
+// maximum and minimum values represented by the source type.
+
+template <typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign = std::is_signed<Dst>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign = std::is_signed<Src>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED>
+struct StaticDstRangeRelationToSrcRange;
+
+// Same sign: Dst is guaranteed to contain Src only if its range is equal or
+// larger.
+template <typename Dst, typename Src, IntegerRepresentation Sign>
+struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
+ static const NumericRangeRepresentation value =
+ MaxExponent<Dst>::value >= MaxExponent<Src>::value
+ ? NUMERIC_RANGE_CONTAINED
+ : NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// Unsigned to signed: Dst is guaranteed to contain source only if its range is
+// larger.
+template <typename Dst, typename Src>
+struct StaticDstRangeRelationToSrcRange<Dst,
+ Src,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED> {
+ static const NumericRangeRepresentation value =
+ MaxExponent<Dst>::value > MaxExponent<Src>::value
+ ? NUMERIC_RANGE_CONTAINED
+ : NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// Signed to unsigned: Dst cannot be statically determined to contain Src.
+template <typename Dst, typename Src>
+struct StaticDstRangeRelationToSrcRange<Dst,
+ Src,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED> {
+ static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// This class wraps the range constraints as separate booleans so the compiler
+// can identify constants and eliminate unused code paths.
+class RangeCheck {
+ public:
+ constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound)
+ : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {}
+ constexpr RangeCheck() : is_underflow_(0), is_overflow_(0) {}
+ constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; }
+ constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; }
+ constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; }
+ constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; }
+ constexpr bool IsOverflowFlagSet() const { return is_overflow_; }
+ constexpr bool IsUnderflowFlagSet() const { return is_underflow_; }
+ constexpr bool operator==(const RangeCheck rhs) const {
+ return is_underflow_ == rhs.is_underflow_ &&
+ is_overflow_ == rhs.is_overflow_;
+ }
+ constexpr bool operator!=(const RangeCheck rhs) const {
+ return !(*this == rhs);
+ }
+
+ private:
+ // Do not change the order of these member variables. The integral conversion
+ // optimization depends on this exact order.
+ const bool is_underflow_;
+ const bool is_overflow_;
+};
+
+// 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 <typename Dst, typename Src, template <typename> class Bounds>
+struct NarrowingRange {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = typename std::numeric_limits<Dst>;
+
+ // Computes the mask required to make an accurate comparison between types.
+ static const int kShift =
+ (MaxExponent<Src>::value > MaxExponent<Dst>::value &&
+ SrcLimits::digits < DstLimits::digits)
+ ? (DstLimits::digits - SrcLimits::digits)
+ : 0;
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+
+ // Masks out the integer bits that are beyond the precision of the
+ // intermediate type used for comparison.
+ static constexpr T Adjust(T value) {
+ static_assert(std::is_same<T, Dst>::value, "");
+ static_assert(kShift < DstLimits::digits, "");
+ return static_cast<T>(
+ ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)),
+ IsValueNegative(value)));
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static constexpr T Adjust(T value) {
+ static_assert(std::is_same<T, Dst>::value, "");
+ static_assert(kShift == 0, "");
+ return value;
+ }
+
+ static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); }
+ static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); }
+};
+
+template <typename Dst,
+ typename Src,
+ template <typename> class Bounds,
+ IntegerRepresentation DstSign = std::is_signed<Dst>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign = std::is_signed<Src>::value
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ NumericRangeRepresentation DstRange =
+ StaticDstRangeRelationToSrcRange<Dst, Src>::value>
+struct DstRangeRelationToSrcRangeImpl;
+
+// The following templates are for ranges that must be verified at runtime. We
+// split it into checks based on signedness to avoid confusing casts and
+// compiler warnings on signed an unsigned comparisons.
+
+// Same sign narrowing: The range is contained for normal limits.
+template <typename Dst,
+ typename Src,
+ template <typename> class Bounds,
+ IntegerRepresentation DstSign,
+ IntegerRepresentation SrcSign>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ DstSign,
+ SrcSign,
+ NUMERIC_RANGE_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeCheck(
+ static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() ||
+ static_cast<Dst>(value) >= DstLimits::lowest(),
+ static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() ||
+ static_cast<Dst>(value) <= DstLimits::max());
+ }
+};
+
+// Signed to signed narrowing: Both the upper and lower boundaries may be
+// exceeded for standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_SIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max());
+ }
+};
+
+// Unsigned to unsigned narrowing: Only the upper bound can be exceeded for
+// standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ return RangeCheck(
+ DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(),
+ value <= DstLimits::max());
+ }
+};
+
+// Unsigned to signed: Only the upper bound can be exceeded for standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ using Promotion = decltype(Src() + Dst());
+ return RangeCheck(DstLimits::lowest() <= Dst(0) ||
+ static_cast<Promotion>(value) >=
+ static_cast<Promotion>(DstLimits::lowest()),
+ static_cast<Promotion>(value) <=
+ static_cast<Promotion>(DstLimits::max()));
+ }
+};
+
+// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
+// and any negative value exceeds the lower boundary for standard limits.
+template <typename Dst, typename Src, template <typename> class Bounds>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ Bounds,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static constexpr RangeCheck Check(Src value) {
+ using SrcLimits = std::numeric_limits<Src>;
+ using DstLimits = NarrowingRange<Dst, Src, Bounds>;
+ using Promotion = decltype(Src() + Dst());
+ return RangeCheck(
+ value >= Src(0) && (DstLimits::lowest() == 0 ||
+ static_cast<Dst>(value) >= DstLimits::lowest()),
+ static_cast<Promotion>(SrcLimits::max()) <=
+ static_cast<Promotion>(DstLimits::max()) ||
+ static_cast<Promotion>(value) <=
+ static_cast<Promotion>(DstLimits::max()));
+ }
+};
+
+// Simple wrapper for statically checking if a type's range is contained.
+template <typename Dst, typename Src>
+struct IsTypeInRangeForNumericType {
+ static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value ==
+ NUMERIC_RANGE_CONTAINED;
+};
+
+template <typename Dst,
+ template <typename> class Bounds = std::numeric_limits,
+ typename Src>
+constexpr RangeCheck DstRangeRelationToSrcRange(Src value) {
+ static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+ static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric.");
+ static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), "");
+ return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value);
+}
+
+// Integer promotion templates used by the portable checked integer arithmetic.
+template <size_t Size, bool IsSigned>
+struct IntegerForDigitsAndSign;
+
+#define INTEGER_FOR_DIGITS_AND_SIGN(I) \
+ template <> \
+ struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \
+ std::is_signed<I>::value> { \
+ using type = I; \
+ }
+
+INTEGER_FOR_DIGITS_AND_SIGN(int8_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint8_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int16_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint16_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int32_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint32_t);
+INTEGER_FOR_DIGITS_AND_SIGN(int64_t);
+INTEGER_FOR_DIGITS_AND_SIGN(uint64_t);
+#undef INTEGER_FOR_DIGITS_AND_SIGN
+
+// 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).
+static_assert(IntegerBitsPlusSign<intmax_t>::value == 64,
+ "Max integer size not supported for this toolchain.");
+
+template <typename Integer, bool IsSigned = std::is_signed<Integer>::value>
+struct TwiceWiderInteger {
+ using type =
+ typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2,
+ IsSigned>::type;
+};
+
+enum ArithmeticPromotionCategory {
+ LEFT_PROMOTION, // Use the type of the left-hand argument.
+ RIGHT_PROMOTION // Use the type of the right-hand argument.
+};
+
+// Determines the type that can represent the largest positive value.
+template <typename Lhs,
+ typename Rhs,
+ ArithmeticPromotionCategory Promotion =
+ (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION>
+struct MaxExponentPromotion;
+
+template <typename Lhs, typename Rhs>
+struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ using type = Lhs;
+};
+
+template <typename Lhs, typename Rhs>
+struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ using type = Rhs;
+};
+
+// Determines the type that can represent the lowest arithmetic value.
+template <typename Lhs,
+ typename Rhs,
+ ArithmeticPromotionCategory Promotion =
+ std::is_signed<Lhs>::value
+ ? (std::is_signed<Rhs>::value
+ ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION)
+ : LEFT_PROMOTION)
+ : (std::is_signed<Rhs>::value
+ ? RIGHT_PROMOTION
+ : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value
+ ? LEFT_PROMOTION
+ : RIGHT_PROMOTION))>
+struct LowestValuePromotion;
+
+template <typename Lhs, typename Rhs>
+struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ using type = Lhs;
+};
+
+template <typename Lhs, typename Rhs>
+struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ using type = Rhs;
+};
+
+// Determines the type that is best able to represent an arithmetic result.
+template <
+ typename Lhs,
+ typename Rhs = Lhs,
+ bool is_intmax_type =
+ std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&&
+ IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>::
+ value == IntegerBitsPlusSign<intmax_t>::value,
+ bool is_max_exponent =
+ StaticDstRangeRelationToSrcRange<
+ typename MaxExponentPromotion<Lhs, Rhs>::type,
+ Lhs>::value ==
+ NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange<
+ typename MaxExponentPromotion<Lhs, Rhs>::type,
+ Rhs>::value == NUMERIC_RANGE_CONTAINED>
+struct BigEnoughPromotion;
+
+// The side with the max exponent is big enough.
+template <typename Lhs, typename Rhs, bool is_intmax_type>
+struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> {
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
+ static const bool is_contained = true;
+};
+
+// We can use a twice wider type to fit.
+template <typename Lhs, typename Rhs>
+struct BigEnoughPromotion<Lhs, Rhs, false, false> {
+ using type =
+ typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
+ std::is_signed<Lhs>::value ||
+ std::is_signed<Rhs>::value>::type;
+ static const bool is_contained = true;
+};
+
+// No type is large enough.
+template <typename Lhs, typename Rhs>
+struct BigEnoughPromotion<Lhs, Rhs, true, false> {
+ using type = typename MaxExponentPromotion<Lhs, Rhs>::type;
+ static const bool is_contained = false;
+};
+
+// 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 <typename T, typename Lhs, typename Rhs = Lhs>
+struct IsIntegerArithmeticSafe {
+ static const bool value =
+ !std::is_floating_point<T>::value &&
+ !std::is_floating_point<Lhs>::value &&
+ !std::is_floating_point<Rhs>::value &&
+ std::is_signed<T>::value >= std::is_signed<Lhs>::value &&
+ IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) &&
+ std::is_signed<T>::value >= std::is_signed<Rhs>::value &&
+ IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value);
+};
+
+// Promotes to a type that can represent any possible result of a binary
+// arithmetic operation with the source types.
+template <typename Lhs,
+ typename Rhs,
+ bool is_promotion_possible = IsIntegerArithmeticSafe<
+ typename std::conditional<std::is_signed<Lhs>::value ||
+ std::is_signed<Rhs>::value,
+ intmax_t,
+ uintmax_t>::type,
+ typename MaxExponentPromotion<Lhs, Rhs>::type>::value>
+struct FastIntegerArithmeticPromotion;
+
+template <typename Lhs, typename Rhs>
+struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> {
+ using type =
+ typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type,
+ std::is_signed<Lhs>::value ||
+ std::is_signed<Rhs>::value>::type;
+ static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, "");
+ static const bool is_contained = true;
+};
+
+template <typename Lhs, typename Rhs>
+struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> {
+ using type = typename BigEnoughPromotion<Lhs, Rhs>::type;
+ static const bool is_contained = false;
+};
+
+// Extracts the underlying type from an enum.
+template <typename T, bool is_enum = std::is_enum<T>::value>
+struct ArithmeticOrUnderlyingEnum;
+
+template <typename T>
+struct ArithmeticOrUnderlyingEnum<T, true> {
+ using type = typename std::underlying_type<T>::type;
+ static const bool value = std::is_arithmetic<type>::value;
+};
+
+template <typename T>
+struct ArithmeticOrUnderlyingEnum<T, false> {
+ using type = T;
+ static const bool value = std::is_arithmetic<type>::value;
+};
+
+// The following are helper templates used in the CheckedNumeric class.
+template <typename T>
+class CheckedNumeric;
+
+template <typename T>
+class ClampedNumeric;
+
+template <typename T>
+class StrictNumeric;
+
+// Used to treat CheckedNumeric and arithmetic underlying types the same.
+template <typename T>
+struct UnderlyingType {
+ using type = typename ArithmeticOrUnderlyingEnum<T>::type;
+ static const bool is_numeric = std::is_arithmetic<type>::value;
+ static const bool is_checked = false;
+ static const bool is_clamped = false;
+ static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<CheckedNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = true;
+ static const bool is_clamped = false;
+ static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<ClampedNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = false;
+ static const bool is_clamped = true;
+ static const bool is_strict = false;
+};
+
+template <typename T>
+struct UnderlyingType<StrictNumeric<T>> {
+ using type = T;
+ static const bool is_numeric = true;
+ static const bool is_checked = false;
+ static const bool is_clamped = false;
+ static const bool is_strict = true;
+};
+
+template <typename L, typename R>
+struct IsCheckedOp {
+ static const bool value =
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+ (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
+};
+
+template <typename L, typename R>
+struct IsClampedOp {
+ static const bool value =
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+ (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) &&
+ !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked);
+};
+
+template <typename L, typename R>
+struct IsStrictOp {
+ static const bool value =
+ UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric &&
+ (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) &&
+ !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) &&
+ !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped);
+};
+
+// as_signed<> returns the supplied integral value (or integral castable
+// Numeric template) cast as a signed integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_signed<
+ typename base::internal::UnderlyingType<Src>::type>::type
+as_signed(const Src value) {
+ static_assert(std::is_integral<decltype(as_signed(value))>::value,
+ "Argument must be a signed or unsigned integer type.");
+ return static_cast<decltype(as_signed(value))>(value);
+}
+
+// as_unsigned<> returns the supplied integral value (or integral castable
+// Numeric template) cast as an unsigned integral of equivalent precision.
+// I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t)
+template <typename Src>
+constexpr typename std::make_unsigned<
+ typename base::internal::UnderlyingType<Src>::type>::type
+as_unsigned(const Src value) {
+ static_assert(std::is_integral<decltype(as_unsigned(value))>::value,
+ "Argument must be a signed or unsigned integer type.");
+ return static_cast<decltype(as_unsigned(value))>(value);
+}
+
+template <typename L, typename R>
+constexpr bool IsLessImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsUnderflow() || r_range.IsOverflow() ||
+ (l_range == r_range &&
+ static_cast<decltype(lhs + rhs)>(lhs) <
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsLess {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+constexpr bool IsLessOrEqualImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsUnderflow() || r_range.IsOverflow() ||
+ (l_range == r_range &&
+ static_cast<decltype(lhs + rhs)>(lhs) <=
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsLessOrEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+constexpr bool IsGreaterImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsOverflow() || r_range.IsUnderflow() ||
+ (l_range == r_range &&
+ static_cast<decltype(lhs + rhs)>(lhs) >
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsGreater {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+constexpr bool IsGreaterOrEqualImpl(const L lhs,
+ const R rhs,
+ const RangeCheck l_range,
+ const RangeCheck r_range) {
+ return l_range.IsOverflow() || r_range.IsUnderflow() ||
+ (l_range == r_range &&
+ static_cast<decltype(lhs + rhs)>(lhs) >=
+ static_cast<decltype(lhs + rhs)>(rhs));
+}
+
+template <typename L, typename R>
+struct IsGreaterOrEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs),
+ DstRangeRelationToSrcRange<L>(rhs));
+ }
+};
+
+template <typename L, typename R>
+struct IsEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return DstRangeRelationToSrcRange<R>(lhs) ==
+ DstRangeRelationToSrcRange<L>(rhs) &&
+ static_cast<decltype(lhs + rhs)>(lhs) ==
+ static_cast<decltype(lhs + rhs)>(rhs);
+ }
+};
+
+template <typename L, typename R>
+struct IsNotEqual {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ static constexpr bool Test(const L lhs, const R rhs) {
+ return DstRangeRelationToSrcRange<R>(lhs) !=
+ DstRangeRelationToSrcRange<L>(rhs) ||
+ static_cast<decltype(lhs + rhs)>(lhs) !=
+ static_cast<decltype(lhs + rhs)>(rhs);
+ }
+};
+
+// These perform the actual math operations on the CheckedNumerics.
+// Binary arithmetic operations.
+template <template <typename, typename> class C, typename L, typename R>
+constexpr bool SafeCompare(const L lhs, const R rhs) {
+ static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value,
+ "Types must be numeric.");
+ using Promotion = BigEnoughPromotion<L, R>;
+ using BigType = typename Promotion::type;
+ return Promotion::is_contained
+ // Force to a larger type for speed if both are contained.
+ ? C<BigType, BigType>::Test(
+ static_cast<BigType>(static_cast<L>(lhs)),
+ static_cast<BigType>(static_cast<R>(rhs)))
+ // Let the template functions figure it out for mixed types.
+ : C<L, R>::Test(lhs, rhs);
+}
+
+template <typename Dst, typename Src>
+constexpr bool IsMaxInRangeForNumericType() {
+ return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(),
+ std::numeric_limits<Src>::max());
+}
+
+template <typename Dst, typename Src>
+constexpr bool IsMinInRangeForNumericType() {
+ return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(),
+ std::numeric_limits<Src>::lowest());
+}
+
+template <typename Dst, typename Src>
+constexpr Dst CommonMax() {
+ return !IsMaxInRangeForNumericType<Dst, Src>()
+ ? Dst(std::numeric_limits<Dst>::max())
+ : Dst(std::numeric_limits<Src>::max());
+}
+
+template <typename Dst, typename Src>
+constexpr Dst CommonMin() {
+ return !IsMinInRangeForNumericType<Dst, Src>()
+ ? Dst(std::numeric_limits<Dst>::lowest())
+ : Dst(std::numeric_limits<Src>::lowest());
+}
+
+// This is a wrapper to generate return the max or min for a supplied type.
+// If the argument is false, the returned value is the maximum. If true the
+// returned value is the minimum.
+template <typename Dst, typename Src = Dst>
+constexpr Dst CommonMaxOrMin(bool is_min) {
+ return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>();
+}
+
+} // namespace internal
+} // namespace base
+
+#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 0000000000..e30be901f9
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_math.h
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. 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 "base/numerics/checked_math.h"
+#include "base/numerics/clamped_math.h"
+#include "base/numerics/safe_conversions.h"
+
+#endif // BASE_NUMERICS_SAFE_MATH_H_
diff --git a/security/sandbox/chromium/base/numerics/safe_math_arm_impl.h b/security/sandbox/chromium/base/numerics/safe_math_arm_impl.h
new file mode 100644
index 0000000000..ff86bd0b73
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_math_arm_impl.h
@@ -0,0 +1,122 @@
+// Copyright 2017 The Chromium Authors. 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_ARM_IMPL_H_
+#define BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+
+namespace base {
+namespace internal {
+
+template <typename T, typename U>
+struct CheckedMulFastAsmOp {
+ static const bool is_supported =
+ FastIntegerArithmeticPromotion<T, U>::is_contained;
+
+ // The following is much more efficient than the Clang and GCC builtins for
+ // performing overflow-checked multiplication when a twice wider type is
+ // available. The below compiles down to 2-3 instructions, depending on the
+ // width of the types in use.
+ // As an example, an int32_t multiply compiles to:
+ // smull r0, r1, r0, r1
+ // cmp r1, r1, asr #31
+ // And an int16_t multiply compiles to:
+ // smulbb r1, r1, r0
+ // asr r2, r1, #16
+ // cmp r2, r1, asr #15
+ template <typename V>
+ __attribute__((always_inline)) static bool Do(T x, U y, V* result) {
+ using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ Promotion presult;
+
+ presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
+ *result = static_cast<V>(presult);
+ return IsValueInRangeForNumericType<V>(presult);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastAsmOp {
+ static const bool is_supported =
+ BigEnoughPromotion<T, U>::is_contained &&
+ IsTypeInRangeForNumericType<
+ int32_t,
+ typename BigEnoughPromotion<T, U>::type>::value;
+
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ // This will get promoted to an int, so let the compiler do whatever is
+ // clever and rely on the saturated cast to bounds check.
+ if (IsIntegerArithmeticSafe<int, T, U>::value)
+ return saturated_cast<V>(x + y);
+
+ int32_t result;
+ int32_t x_i32 = checked_cast<int32_t>(x);
+ int32_t y_i32 = checked_cast<int32_t>(y);
+
+ asm("qadd %[result], %[first], %[second]"
+ : [result] "=r"(result)
+ : [first] "r"(x_i32), [second] "r"(y_i32));
+ return saturated_cast<V>(result);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastAsmOp {
+ static const bool is_supported =
+ BigEnoughPromotion<T, U>::is_contained &&
+ IsTypeInRangeForNumericType<
+ int32_t,
+ typename BigEnoughPromotion<T, U>::type>::value;
+
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ // This will get promoted to an int, so let the compiler do whatever is
+ // clever and rely on the saturated cast to bounds check.
+ if (IsIntegerArithmeticSafe<int, T, U>::value)
+ return saturated_cast<V>(x - y);
+
+ int32_t result;
+ int32_t x_i32 = checked_cast<int32_t>(x);
+ int32_t y_i32 = checked_cast<int32_t>(y);
+
+ asm("qsub %[result], %[first], %[second]"
+ : [result] "=r"(result)
+ : [first] "r"(x_i32), [second] "r"(y_i32));
+ return saturated_cast<V>(result);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastAsmOp {
+ static const bool is_supported = CheckedMulFastAsmOp<T, U>::is_supported;
+
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ // Use the CheckedMulFastAsmOp for full-width 32-bit values, because
+ // it's fewer instructions than promoting and then saturating.
+ if (!IsIntegerArithmeticSafe<int32_t, T, U>::value &&
+ !IsIntegerArithmeticSafe<uint32_t, T, U>::value) {
+ V result;
+ if (CheckedMulFastAsmOp<T, U>::Do(x, y, &result))
+ return result;
+ return CommonMaxOrMin<V>(IsValueNegative(x) ^ IsValueNegative(y));
+ }
+
+ assert((FastIntegerArithmeticPromotion<T, U>::is_contained));
+ using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+ return saturated_cast<V>(static_cast<Promotion>(x) *
+ static_cast<Promotion>(y));
+ }
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_MATH_ARM_IMPL_H_
diff --git a/security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h b/security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h
new file mode 100644
index 0000000000..1760338b08
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_math_clang_gcc_impl.h
@@ -0,0 +1,157 @@
+// Copyright 2017 The Chromium Authors. 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_CLANG_GCC_IMPL_H_
+#define BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
+
+#include <cassert>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+
+#if !defined(__native_client__) && (defined(__ARMEL__) || defined(__arch64__))
+#include "base/numerics/safe_math_arm_impl.h"
+#define BASE_HAS_ASSEMBLER_SAFE_MATH (1)
+#else
+#define BASE_HAS_ASSEMBLER_SAFE_MATH (0)
+#endif
+
+namespace base {
+namespace internal {
+
+// These are the non-functioning boilerplate implementations of the optimized
+// safe math routines.
+#if !BASE_HAS_ASSEMBLER_SAFE_MATH
+template <typename T, typename U>
+struct CheckedMulFastAsmOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastAsmOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastAsmOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastAsmOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+#endif // BASE_HAS_ASSEMBLER_SAFE_MATH
+#undef BASE_HAS_ASSEMBLER_SAFE_MATH
+
+template <typename T, typename U>
+struct CheckedAddFastOp {
+ static const bool is_supported = true;
+ template <typename V>
+ __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+ return !__builtin_add_overflow(x, y, result);
+ }
+};
+
+template <typename T, typename U>
+struct CheckedSubFastOp {
+ static const bool is_supported = true;
+ template <typename V>
+ __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+ return !__builtin_sub_overflow(x, y, result);
+ }
+};
+
+template <typename T, typename U>
+struct CheckedMulFastOp {
+#if defined(__clang__)
+ // TODO(jschuh): Get the Clang runtime library issues sorted out so we can
+ // support full-width, mixed-sign multiply builtins.
+ // https://crbug.com/613003
+ // We can support intptr_t, uintptr_t, or a smaller common type.
+ static const bool is_supported =
+ (IsTypeInRangeForNumericType<intptr_t, T>::value &&
+ IsTypeInRangeForNumericType<intptr_t, U>::value) ||
+ (IsTypeInRangeForNumericType<uintptr_t, T>::value &&
+ IsTypeInRangeForNumericType<uintptr_t, U>::value);
+#else
+ static const bool is_supported = true;
+#endif
+ template <typename V>
+ __attribute__((always_inline)) static constexpr bool Do(T x, U y, V* result) {
+ return CheckedMulFastAsmOp<T, U>::is_supported
+ ? CheckedMulFastAsmOp<T, U>::Do(x, y, result)
+ : !__builtin_mul_overflow(x, y, result);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastOp {
+ static const bool is_supported = ClampedAddFastAsmOp<T, U>::is_supported;
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ return ClampedAddFastAsmOp<T, U>::template Do<V>(x, y);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastOp {
+ static const bool is_supported = ClampedSubFastAsmOp<T, U>::is_supported;
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ return ClampedSubFastAsmOp<T, U>::template Do<V>(x, y);
+ }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastOp {
+ static const bool is_supported = ClampedMulFastAsmOp<T, U>::is_supported;
+ template <typename V>
+ __attribute__((always_inline)) static V Do(T x, U y) {
+ return ClampedMulFastAsmOp<T, U>::template Do<V>(x, y);
+ }
+};
+
+template <typename T>
+struct ClampedNegFastOp {
+ static const bool is_supported = std::is_signed<T>::value;
+ __attribute__((always_inline)) static T Do(T value) {
+ // Use this when there is no assembler path available.
+ if (!ClampedSubFastAsmOp<T, T>::is_supported) {
+ T result;
+ return !__builtin_sub_overflow(T(0), value, &result)
+ ? result
+ : std::numeric_limits<T>::max();
+ }
+
+ // Fallback to the normal subtraction path.
+ return ClampedSubFastOp<T, T>::template Do<T>(T(0), value);
+ }
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_MATH_CLANG_GCC_IMPL_H_
diff --git a/security/sandbox/chromium/base/numerics/safe_math_shared_impl.h b/security/sandbox/chromium/base/numerics/safe_math_shared_impl.h
new file mode 100644
index 0000000000..3556b1ea81
--- /dev/null
+++ b/security/sandbox/chromium/base/numerics/safe_math_shared_impl.h
@@ -0,0 +1,240 @@
+// Copyright 2017 The Chromium Authors. 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_SHARED_IMPL_H_
+#define BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+#include <type_traits>
+
+#include "base/numerics/safe_conversions.h"
+
+#ifdef __asmjs__
+// Optimized safe math instructions are incompatible with asmjs.
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
+// Where available use builtin math overflow support on Clang and GCC.
+#elif !defined(__native_client__) && \
+ ((defined(__clang__) && \
+ ((__clang_major__ > 3) || \
+ (__clang_major__ == 3 && __clang_minor__ >= 4))) || \
+ (defined(__GNUC__) && __GNUC__ >= 5))
+#include "base/numerics/safe_math_clang_gcc_impl.h"
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (1)
+#else
+#define BASE_HAS_OPTIMIZED_SAFE_MATH (0)
+#endif
+
+namespace base {
+namespace internal {
+
+// These are the non-functioning boilerplate implementations of the optimized
+// safe math routines.
+#if !BASE_HAS_OPTIMIZED_SAFE_MATH
+template <typename T, typename U>
+struct CheckedAddFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct CheckedSubFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct CheckedMulFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr bool Do(T, U, V*) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<bool>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedAddFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedSubFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T, typename U>
+struct ClampedMulFastOp {
+ static const bool is_supported = false;
+ template <typename V>
+ static constexpr V Do(T, U) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<V>();
+ }
+};
+
+template <typename T>
+struct ClampedNegFastOp {
+ static const bool is_supported = false;
+ static constexpr T Do(T) {
+ // Force a compile failure if instantiated.
+ return CheckOnFailure::template HandleFailure<T>();
+ }
+};
+#endif // BASE_HAS_OPTIMIZED_SAFE_MATH
+#undef BASE_HAS_OPTIMIZED_SAFE_MATH
+
+// 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. SafeUnsignedAbs,
+// so the float versions will not compile.
+template <typename Numeric,
+ bool IsInteger = std::is_integral<Numeric>::value,
+ bool IsFloat = std::is_floating_point<Numeric>::value>
+struct UnsignedOrFloatForSize;
+
+template <typename Numeric>
+struct UnsignedOrFloatForSize<Numeric, true, false> {
+ using type = typename std::make_unsigned<Numeric>::type;
+};
+
+template <typename Numeric>
+struct UnsignedOrFloatForSize<Numeric, false, true> {
+ using type = Numeric;
+};
+
+// Wrap the unary operations to allow SFINAE when instantiating integrals versus
+// floating points. These don't perform any overflow checking. Rather, they
+// exhibit well-defined overflow semantics and rely on the caller to detect
+// if an overflow occured.
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+ using UnsignedT = typename std::make_unsigned<T>::type;
+ // This will compile to a NEG on Intel, and is normal negation on ARM.
+ return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value));
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T NegateWrapper(T value) {
+ return -value;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) {
+ return ~value;
+}
+
+template <typename T,
+ typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+ return static_cast<T>(SafeUnsignedAbs(value));
+}
+
+template <
+ typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
+constexpr T AbsWrapper(T value) {
+ return value < 0 ? -value : value;
+}
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+struct MathWrapper {
+ using math = M<typename UnderlyingType<L>::type,
+ typename UnderlyingType<R>::type,
+ void>;
+ using type = typename math::result_type;
+};
+
+// These variadic templates work out the return types.
+// TODO(jschuh): Rip all this out once we have C++14 non-trailing auto support.
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+struct ResultType;
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R>
+struct ResultType<M, L, R> {
+ using type = typename MathWrapper<M, L, R>::type;
+};
+
+template <template <typename, typename, typename> class M,
+ typename L,
+ typename R,
+ typename... Args>
+struct ResultType {
+ using type =
+ typename ResultType<M, typename ResultType<M, L, R>::type, Args...>::type;
+};
+
+// The following macros are just boilerplate for the standard arithmetic
+// operator overloads and variadic function templates. A macro isn't the nicest
+// solution, but it beats rewriting these over and over again.
+#define BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME) \
+ template <typename L, typename R, typename... Args> \
+ constexpr CLASS##Numeric< \
+ typename ResultType<CLASS##OP_NAME##Op, L, R, Args...>::type> \
+ CL_ABBR##OP_NAME(const L lhs, const R rhs, const Args... args) { \
+ return CL_ABBR##MathOp<CLASS##OP_NAME##Op, L, R, Args...>(lhs, rhs, \
+ args...); \
+ }
+
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(CLASS, CL_ABBR, OP_NAME, OP, CMP_OP) \
+ /* Binary arithmetic operator for all CLASS##Numeric operations. */ \
+ template <typename L, typename R, \
+ typename std::enable_if<Is##CLASS##Op<L, R>::value>::type* = \
+ nullptr> \
+ constexpr CLASS##Numeric< \
+ typename MathWrapper<CLASS##OP_NAME##Op, L, R>::type> \
+ operator OP(const L lhs, const R rhs) { \
+ return decltype(lhs OP rhs)::template MathOp<CLASS##OP_NAME##Op>(lhs, \
+ rhs); \
+ } \
+ /* Assignment arithmetic operator implementation from CLASS##Numeric. */ \
+ template <typename L> \
+ template <typename R> \
+ constexpr CLASS##Numeric<L>& CLASS##Numeric<L>::operator CMP_OP( \
+ const R rhs) { \
+ return MathOp<CLASS##OP_NAME##Op>(rhs); \
+ } \
+ /* Variadic arithmetic functions that return CLASS##Numeric. */ \
+ BASE_NUMERIC_ARITHMETIC_VARIADIC(CLASS, CL_ABBR, OP_NAME)
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_NUMERICS_SAFE_MATH_SHARED_IMPL_H_
diff --git a/security/sandbox/chromium/base/optional.h b/security/sandbox/chromium/base/optional.h
new file mode 100644
index 0000000000..36ae36fc98
--- /dev/null
+++ b/security/sandbox/chromium/base/optional.h
@@ -0,0 +1,937 @@
+// 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_OPTIONAL_H_
+#define BASE_OPTIONAL_H_
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/template_util.h"
+
+namespace base {
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
+struct nullopt_t {
+ constexpr explicit nullopt_t(int) {}
+};
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt
+constexpr nullopt_t nullopt(0);
+
+// Forward declaration, which is refered by following helpers.
+template <typename T>
+class Optional;
+
+namespace internal {
+
+template <typename T, bool = std::is_trivially_destructible<T>::value>
+struct OptionalStorageBase {
+ // Initializing |empty_| here instead of using default member initializing
+ // to avoid errors in g++ 4.8.
+ constexpr OptionalStorageBase() : empty_('\0') {}
+
+ template <class... Args>
+ constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
+ : is_populated_(true), value_(std::forward<Args>(args)...) {}
+
+ // When T is not trivially destructible we must call its
+ // destructor before deallocating its memory.
+ // Note that this hides the (implicitly declared) move constructor, which
+ // would be used for constexpr move constructor in OptionalStorage<T>.
+ // It is needed iff T is trivially move constructible. However, the current
+ // is_trivially_{copy,move}_constructible implementation requires
+ // is_trivially_destructible (which looks a bug, cf:
+ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51452 and
+ // http://cplusplus.github.io/LWG/lwg-active.html#2116), so it is not
+ // necessary for this case at the moment. Please see also the destructor
+ // comment in "is_trivially_destructible = true" specialization below.
+ ~OptionalStorageBase() {
+ if (is_populated_)
+ value_.~T();
+ }
+
+ template <class... Args>
+ void Init(Args&&... args) {
+ DCHECK(!is_populated_);
+ ::new (&value_) T(std::forward<Args>(args)...);
+ is_populated_ = true;
+ }
+
+ bool is_populated_ = false;
+ union {
+ // |empty_| exists so that the union will always be initialized, even when
+ // it doesn't contain a value. Union members must be initialized for the
+ // constructor to be 'constexpr'.
+ char empty_;
+ T value_;
+ };
+};
+
+template <typename T>
+struct OptionalStorageBase<T, true /* trivially destructible */> {
+ // Initializing |empty_| here instead of using default member initializing
+ // to avoid errors in g++ 4.8.
+ constexpr OptionalStorageBase() : empty_('\0') {}
+
+ template <class... Args>
+ constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
+ : is_populated_(true), value_(std::forward<Args>(args)...) {}
+
+ // When T is trivially destructible (i.e. its destructor does nothing) there
+ // is no need to call it. Implicitly defined destructor is trivial, because
+ // both members (bool and union containing only variants which are trivially
+ // destructible) are trivially destructible.
+ // Explicitly-defaulted destructor is also trivial, but do not use it here,
+ // because it hides the implicit move constructor. It is needed to implement
+ // constexpr move constructor in OptionalStorage iff T is trivially move
+ // constructible. Note that, if T is trivially move constructible, the move
+ // constructor of OptionalStorageBase<T> is also implicitly defined and it is
+ // trivially move constructor. If T is not trivially move constructible,
+ // "not declaring move constructor without destructor declaration" here means
+ // "delete move constructor", which works because any move constructor of
+ // OptionalStorage will not refer to it in that case.
+
+ template <class... Args>
+ void Init(Args&&... args) {
+ DCHECK(!is_populated_);
+ ::new (&value_) T(std::forward<Args>(args)...);
+ is_populated_ = true;
+ }
+
+ bool is_populated_ = false;
+ union {
+ // |empty_| exists so that the union will always be initialized, even when
+ // it doesn't contain a value. Union members must be initialized for the
+ // constructor to be 'constexpr'.
+ char empty_;
+ T value_;
+ };
+};
+
+// Implement conditional constexpr copy and move constructors. These are
+// constexpr if is_trivially_{copy,move}_constructible<T>::value is true
+// respectively. If each is true, the corresponding constructor is defined as
+// "= default;", which generates a constexpr constructor (In this case,
+// the condition of constexpr-ness is satisfied because the base class also has
+// compiler generated constexpr {copy,move} constructors). Note that
+// placement-new is prohibited in constexpr.
+template <typename T,
+ bool = is_trivially_copy_constructible<T>::value,
+ bool = std::is_trivially_move_constructible<T>::value>
+struct OptionalStorage : OptionalStorageBase<T> {
+ // This is no trivially {copy,move} constructible case. Other cases are
+ // defined below as specializations.
+
+ // Accessing the members of template base class requires explicit
+ // declaration.
+ using OptionalStorageBase<T>::is_populated_;
+ using OptionalStorageBase<T>::value_;
+ using OptionalStorageBase<T>::Init;
+
+ // Inherit constructors (specifically, the in_place constructor).
+ using OptionalStorageBase<T>::OptionalStorageBase;
+
+ // User defined constructor deletes the default constructor.
+ // Define it explicitly.
+ OptionalStorage() = default;
+
+ OptionalStorage(const OptionalStorage& other) {
+ if (other.is_populated_)
+ Init(other.value_);
+ }
+
+ OptionalStorage(OptionalStorage&& other) noexcept(
+ std::is_nothrow_move_constructible<T>::value) {
+ if (other.is_populated_)
+ Init(std::move(other.value_));
+ }
+};
+
+template <typename T>
+struct OptionalStorage<T,
+ true /* trivially copy constructible */,
+ false /* trivially move constructible */>
+ : OptionalStorageBase<T> {
+ using OptionalStorageBase<T>::is_populated_;
+ using OptionalStorageBase<T>::value_;
+ using OptionalStorageBase<T>::Init;
+ using OptionalStorageBase<T>::OptionalStorageBase;
+
+ OptionalStorage() = default;
+ OptionalStorage(const OptionalStorage& other) = default;
+
+ OptionalStorage(OptionalStorage&& other) noexcept(
+ std::is_nothrow_move_constructible<T>::value) {
+ if (other.is_populated_)
+ Init(std::move(other.value_));
+ }
+};
+
+template <typename T>
+struct OptionalStorage<T,
+ false /* trivially copy constructible */,
+ true /* trivially move constructible */>
+ : OptionalStorageBase<T> {
+ using OptionalStorageBase<T>::is_populated_;
+ using OptionalStorageBase<T>::value_;
+ using OptionalStorageBase<T>::Init;
+ using OptionalStorageBase<T>::OptionalStorageBase;
+
+ OptionalStorage() = default;
+ OptionalStorage(OptionalStorage&& other) = default;
+
+ OptionalStorage(const OptionalStorage& other) {
+ if (other.is_populated_)
+ Init(other.value_);
+ }
+};
+
+template <typename T>
+struct OptionalStorage<T,
+ true /* trivially copy constructible */,
+ true /* trivially move constructible */>
+ : OptionalStorageBase<T> {
+ // If both trivially {copy,move} constructible are true, it is not necessary
+ // to use user-defined constructors. So, just inheriting constructors
+ // from the base class works.
+ using OptionalStorageBase<T>::OptionalStorageBase;
+};
+
+// Base class to support conditionally usable copy-/move- constructors
+// and assign operators.
+template <typename T>
+class OptionalBase {
+ // This class provides implementation rather than public API, so everything
+ // should be hidden. Often we use composition, but we cannot in this case
+ // because of C++ language restriction.
+ protected:
+ constexpr OptionalBase() = default;
+ constexpr OptionalBase(const OptionalBase& other) = default;
+ constexpr OptionalBase(OptionalBase&& other) = default;
+
+ template <class... Args>
+ constexpr explicit OptionalBase(in_place_t, Args&&... args)
+ : storage_(in_place, std::forward<Args>(args)...) {}
+
+ // Implementation of converting constructors.
+ template <typename U>
+ explicit OptionalBase(const OptionalBase<U>& other) {
+ if (other.storage_.is_populated_)
+ storage_.Init(other.storage_.value_);
+ }
+
+ template <typename U>
+ explicit OptionalBase(OptionalBase<U>&& other) {
+ if (other.storage_.is_populated_)
+ storage_.Init(std::move(other.storage_.value_));
+ }
+
+ ~OptionalBase() = default;
+
+ OptionalBase& operator=(const OptionalBase& other) {
+ CopyAssign(other);
+ return *this;
+ }
+
+ OptionalBase& operator=(OptionalBase&& other) noexcept(
+ std::is_nothrow_move_assignable<T>::value&&
+ std::is_nothrow_move_constructible<T>::value) {
+ MoveAssign(std::move(other));
+ return *this;
+ }
+
+ template <typename U>
+ void CopyAssign(const OptionalBase<U>& other) {
+ if (other.storage_.is_populated_)
+ InitOrAssign(other.storage_.value_);
+ else
+ FreeIfNeeded();
+ }
+
+ template <typename U>
+ void MoveAssign(OptionalBase<U>&& other) {
+ if (other.storage_.is_populated_)
+ InitOrAssign(std::move(other.storage_.value_));
+ else
+ FreeIfNeeded();
+ }
+
+ template <typename U>
+ void InitOrAssign(U&& value) {
+ if (storage_.is_populated_)
+ storage_.value_ = std::forward<U>(value);
+ else
+ storage_.Init(std::forward<U>(value));
+ }
+
+ void FreeIfNeeded() {
+ if (!storage_.is_populated_)
+ return;
+ storage_.value_.~T();
+ storage_.is_populated_ = false;
+ }
+
+ // For implementing conversion, allow access to other typed OptionalBase
+ // class.
+ template <typename U>
+ friend class OptionalBase;
+
+ OptionalStorage<T> storage_;
+};
+
+// The following {Copy,Move}{Constructible,Assignable} structs are helpers to
+// implement constructor/assign-operator overloading. Specifically, if T is
+// is not movable but copyable, Optional<T>'s move constructor should not
+// participate in overload resolution. This inheritance trick implements that.
+template <bool is_copy_constructible>
+struct CopyConstructible {};
+
+template <>
+struct CopyConstructible<false> {
+ constexpr CopyConstructible() = default;
+ constexpr CopyConstructible(const CopyConstructible&) = delete;
+ constexpr CopyConstructible(CopyConstructible&&) = default;
+ CopyConstructible& operator=(const CopyConstructible&) = default;
+ CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <bool is_move_constructible>
+struct MoveConstructible {};
+
+template <>
+struct MoveConstructible<false> {
+ constexpr MoveConstructible() = default;
+ constexpr MoveConstructible(const MoveConstructible&) = default;
+ constexpr MoveConstructible(MoveConstructible&&) = delete;
+ MoveConstructible& operator=(const MoveConstructible&) = default;
+ MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <bool is_copy_assignable>
+struct CopyAssignable {};
+
+template <>
+struct CopyAssignable<false> {
+ constexpr CopyAssignable() = default;
+ constexpr CopyAssignable(const CopyAssignable&) = default;
+ constexpr CopyAssignable(CopyAssignable&&) = default;
+ CopyAssignable& operator=(const CopyAssignable&) = delete;
+ CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <bool is_move_assignable>
+struct MoveAssignable {};
+
+template <>
+struct MoveAssignable<false> {
+ constexpr MoveAssignable() = default;
+ constexpr MoveAssignable(const MoveAssignable&) = default;
+ constexpr MoveAssignable(MoveAssignable&&) = default;
+ MoveAssignable& operator=(const MoveAssignable&) = default;
+ MoveAssignable& operator=(MoveAssignable&&) = delete;
+};
+
+// Helper to conditionally enable converting constructors and assign operators.
+template <typename T, typename U>
+struct IsConvertibleFromOptional
+ : std::integral_constant<
+ bool,
+ std::is_constructible<T, Optional<U>&>::value ||
+ std::is_constructible<T, const Optional<U>&>::value ||
+ std::is_constructible<T, Optional<U>&&>::value ||
+ std::is_constructible<T, const Optional<U>&&>::value ||
+ std::is_convertible<Optional<U>&, T>::value ||
+ std::is_convertible<const Optional<U>&, T>::value ||
+ std::is_convertible<Optional<U>&&, T>::value ||
+ std::is_convertible<const Optional<U>&&, T>::value> {};
+
+template <typename T, typename U>
+struct IsAssignableFromOptional
+ : std::integral_constant<
+ bool,
+ IsConvertibleFromOptional<T, U>::value ||
+ std::is_assignable<T&, Optional<U>&>::value ||
+ std::is_assignable<T&, const Optional<U>&>::value ||
+ std::is_assignable<T&, Optional<U>&&>::value ||
+ std::is_assignable<T&, const Optional<U>&&>::value> {};
+
+// Forward compatibility for C++17.
+// Introduce one more deeper nested namespace to avoid leaking using std::swap.
+namespace swappable_impl {
+using std::swap;
+
+struct IsSwappableImpl {
+ // Tests if swap can be called. Check<T&>(0) returns true_type iff swap
+ // is available for T. Otherwise, Check's overload resolution falls back
+ // to Check(...) declared below thanks to SFINAE, so returns false_type.
+ template <typename T>
+ static auto Check(int)
+ -> decltype(swap(std::declval<T>(), std::declval<T>()), std::true_type());
+
+ template <typename T>
+ static std::false_type Check(...);
+};
+} // namespace swappable_impl
+
+template <typename T>
+struct IsSwappable : decltype(swappable_impl::IsSwappableImpl::Check<T&>(0)) {};
+
+// Forward compatibility for C++20.
+template <typename T>
+using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>;
+
+} // namespace internal
+
+// On Windows, by default, empty-base class optimization does not work,
+// which means even if the base class is empty struct, it still consumes one
+// byte for its body. __declspec(empty_bases) enables the optimization.
+// cf)
+// https://blogs.msdn.microsoft.com/vcblog/2016/03/30/optimizing-the-layout-of-empty-base-classes-in-vs2015-update-2-3/
+#ifdef OS_WIN
+#define OPTIONAL_DECLSPEC_EMPTY_BASES __declspec(empty_bases)
+#else
+#define OPTIONAL_DECLSPEC_EMPTY_BASES
+#endif
+
+// base::Optional is a Chromium version of the C++17 optional class:
+// std::optional documentation:
+// http://en.cppreference.com/w/cpp/utility/optional
+// Chromium documentation:
+// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
+//
+// These are the differences between the specification and the implementation:
+// - Constructors do not use 'constexpr' as it is a C++14 extension.
+// - 'constexpr' might be missing in some places for reasons specified locally.
+// - No exceptions are thrown, because they are banned from Chromium.
+// Marked noexcept for only move constructor and move assign operators.
+// - All the non-members are in the 'base' namespace instead of 'std'.
+//
+// Note that T cannot have a constructor T(Optional<T>) etc. Optional<T> checks
+// T's constructor (specifically via IsConvertibleFromOptional), and in the
+// check whether T can be constructible from Optional<T>, which is recursive
+// so it does not work. As of Feb 2018, std::optional C++17 implementation in
+// both clang and gcc has same limitation. MSVC SFINAE looks to have different
+// behavior, but anyway it reports an error, too.
+template <typename T>
+class OPTIONAL_DECLSPEC_EMPTY_BASES Optional
+ : public internal::OptionalBase<T>,
+ public internal::CopyConstructible<std::is_copy_constructible<T>::value>,
+ public internal::MoveConstructible<std::is_move_constructible<T>::value>,
+ public internal::CopyAssignable<std::is_copy_constructible<T>::value &&
+ std::is_copy_assignable<T>::value>,
+ public internal::MoveAssignable<std::is_move_constructible<T>::value &&
+ std::is_move_assignable<T>::value> {
+ private:
+ // Disable some versions of T that are ill-formed.
+ // See: https://timsong-cpp.github.io/cppwp/n4659/optional#syn-1
+ static_assert(
+ !std::is_same<internal::RemoveCvRefT<T>, in_place_t>::value,
+ "instantiation of base::Optional with in_place_t is ill-formed");
+ static_assert(!std::is_same<internal::RemoveCvRefT<T>, nullopt_t>::value,
+ "instantiation of base::Optional with nullopt_t is ill-formed");
+ static_assert(
+ !std::is_reference<T>::value,
+ "instantiation of base::Optional with a reference type is ill-formed");
+ // See: https://timsong-cpp.github.io/cppwp/n4659/optional#optional-3
+ static_assert(std::is_destructible<T>::value,
+ "instantiation of base::Optional with a non-destructible type "
+ "is ill-formed");
+ // Arrays are explicitly disallowed because for arrays of known bound
+ // is_destructible is of undefined value.
+ // See: https://en.cppreference.com/w/cpp/types/is_destructible
+ static_assert(
+ !std::is_array<T>::value,
+ "instantiation of base::Optional with an array type is ill-formed");
+
+ public:
+#undef OPTIONAL_DECLSPEC_EMPTY_BASES
+ using value_type = T;
+
+ // Defer default/copy/move constructor implementation to OptionalBase.
+ constexpr Optional() = default;
+ constexpr Optional(const Optional& other) = default;
+ constexpr Optional(Optional&& other) noexcept(
+ std::is_nothrow_move_constructible<T>::value) = default;
+
+ constexpr Optional(nullopt_t) {} // NOLINT(runtime/explicit)
+
+ // Converting copy constructor. "explicit" only if
+ // std::is_convertible<const U&, T>::value is false. It is implemented by
+ // declaring two almost same constructors, but that condition in enable_if_t
+ // is different, so that either one is chosen, thanks to SFINAE.
+ template <
+ typename U,
+ std::enable_if_t<std::is_constructible<T, const U&>::value &&
+ !internal::IsConvertibleFromOptional<T, U>::value &&
+ std::is_convertible<const U&, T>::value,
+ bool> = false>
+ Optional(const Optional<U>& other) : internal::OptionalBase<T>(other) {}
+
+ template <
+ typename U,
+ std::enable_if_t<std::is_constructible<T, const U&>::value &&
+ !internal::IsConvertibleFromOptional<T, U>::value &&
+ !std::is_convertible<const U&, T>::value,
+ bool> = false>
+ explicit Optional(const Optional<U>& other)
+ : internal::OptionalBase<T>(other) {}
+
+ // Converting move constructor. Similar to converting copy constructor,
+ // declaring two (explicit and non-explicit) constructors.
+ template <
+ typename U,
+ std::enable_if_t<std::is_constructible<T, U&&>::value &&
+ !internal::IsConvertibleFromOptional<T, U>::value &&
+ std::is_convertible<U&&, T>::value,
+ bool> = false>
+ Optional(Optional<U>&& other) : internal::OptionalBase<T>(std::move(other)) {}
+
+ template <
+ typename U,
+ std::enable_if_t<std::is_constructible<T, U&&>::value &&
+ !internal::IsConvertibleFromOptional<T, U>::value &&
+ !std::is_convertible<U&&, T>::value,
+ bool> = false>
+ explicit Optional(Optional<U>&& other)
+ : internal::OptionalBase<T>(std::move(other)) {}
+
+ template <class... Args>
+ constexpr explicit Optional(in_place_t, Args&&... args)
+ : internal::OptionalBase<T>(in_place, std::forward<Args>(args)...) {}
+
+ template <
+ class U,
+ class... Args,
+ class = std::enable_if_t<std::is_constructible<value_type,
+ std::initializer_list<U>&,
+ Args...>::value>>
+ constexpr explicit Optional(in_place_t,
+ std::initializer_list<U> il,
+ Args&&... args)
+ : internal::OptionalBase<T>(in_place, il, std::forward<Args>(args)...) {}
+
+ // Forward value constructor. Similar to converting constructors,
+ // conditionally explicit.
+ template <
+ typename U = value_type,
+ std::enable_if_t<
+ std::is_constructible<T, U&&>::value &&
+ !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
+ !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+ std::is_convertible<U&&, T>::value,
+ bool> = false>
+ constexpr Optional(U&& value)
+ : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
+
+ template <
+ typename U = value_type,
+ std::enable_if_t<
+ std::is_constructible<T, U&&>::value &&
+ !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
+ !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+ !std::is_convertible<U&&, T>::value,
+ bool> = false>
+ constexpr explicit Optional(U&& value)
+ : internal::OptionalBase<T>(in_place, std::forward<U>(value)) {}
+
+ ~Optional() = default;
+
+ // Defer copy-/move- assign operator implementation to OptionalBase.
+ Optional& operator=(const Optional& other) = default;
+ Optional& operator=(Optional&& other) noexcept(
+ std::is_nothrow_move_assignable<T>::value&&
+ std::is_nothrow_move_constructible<T>::value) = default;
+
+ Optional& operator=(nullopt_t) {
+ FreeIfNeeded();
+ return *this;
+ }
+
+ // Perfect-forwarded assignment.
+ template <typename U>
+ std::enable_if_t<
+ !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<T&, U>::value &&
+ (!std::is_scalar<T>::value ||
+ !std::is_same<std::decay_t<U>, T>::value),
+ Optional&>
+ operator=(U&& value) {
+ InitOrAssign(std::forward<U>(value));
+ return *this;
+ }
+
+ // Copy assign the state of other.
+ template <typename U>
+ std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
+ std::is_constructible<T, const U&>::value &&
+ std::is_assignable<T&, const U&>::value,
+ Optional&>
+ operator=(const Optional<U>& other) {
+ CopyAssign(other);
+ return *this;
+ }
+
+ // Move assign the state of other.
+ template <typename U>
+ std::enable_if_t<!internal::IsAssignableFromOptional<T, U>::value &&
+ std::is_constructible<T, U>::value &&
+ std::is_assignable<T&, U>::value,
+ Optional&>
+ operator=(Optional<U>&& other) {
+ MoveAssign(std::move(other));
+ return *this;
+ }
+
+ constexpr const T* operator->() const {
+ CHECK(storage_.is_populated_);
+ return &storage_.value_;
+ }
+
+ constexpr T* operator->() {
+ CHECK(storage_.is_populated_);
+ return &storage_.value_;
+ }
+
+ constexpr const T& operator*() const & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+ constexpr T& operator*() & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+ constexpr const T&& operator*() const && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+
+ constexpr T&& operator*() && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+
+ constexpr explicit operator bool() const { return storage_.is_populated_; }
+
+ constexpr bool has_value() const { return storage_.is_populated_; }
+
+ constexpr T& value() & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+ constexpr const T& value() const & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+ constexpr T&& value() && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+
+ constexpr const T&& value() const && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+
+ template <class U>
+ constexpr T value_or(U&& default_value) const& {
+ // TODO(mlamouri): add the following assert when possible:
+ // static_assert(std::is_copy_constructible<T>::value,
+ // "T must be copy constructible");
+ static_assert(std::is_convertible<U, T>::value,
+ "U must be convertible to T");
+ return storage_.is_populated_
+ ? storage_.value_
+ : static_cast<T>(std::forward<U>(default_value));
+ }
+
+ template <class U>
+ constexpr T value_or(U&& default_value) && {
+ // TODO(mlamouri): add the following assert when possible:
+ // static_assert(std::is_move_constructible<T>::value,
+ // "T must be move constructible");
+ static_assert(std::is_convertible<U, T>::value,
+ "U must be convertible to T");
+ return storage_.is_populated_
+ ? std::move(storage_.value_)
+ : static_cast<T>(std::forward<U>(default_value));
+ }
+
+ void swap(Optional& other) {
+ if (!storage_.is_populated_ && !other.storage_.is_populated_)
+ return;
+
+ if (storage_.is_populated_ != other.storage_.is_populated_) {
+ if (storage_.is_populated_) {
+ other.storage_.Init(std::move(storage_.value_));
+ FreeIfNeeded();
+ } else {
+ storage_.Init(std::move(other.storage_.value_));
+ other.FreeIfNeeded();
+ }
+ return;
+ }
+
+ DCHECK(storage_.is_populated_ && other.storage_.is_populated_);
+ using std::swap;
+ swap(**this, *other);
+ }
+
+ void reset() { FreeIfNeeded(); }
+
+ template <class... Args>
+ T& emplace(Args&&... args) {
+ FreeIfNeeded();
+ storage_.Init(std::forward<Args>(args)...);
+ return storage_.value_;
+ }
+
+ template <class U, class... Args>
+ std::enable_if_t<
+ std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
+ T&>
+ emplace(std::initializer_list<U> il, Args&&... args) {
+ FreeIfNeeded();
+ storage_.Init(il, std::forward<Args>(args)...);
+ return storage_.value_;
+ }
+
+ private:
+ // Accessing template base class's protected member needs explicit
+ // declaration to do so.
+ using internal::OptionalBase<T>::CopyAssign;
+ using internal::OptionalBase<T>::FreeIfNeeded;
+ using internal::OptionalBase<T>::InitOrAssign;
+ using internal::OptionalBase<T>::MoveAssign;
+ using internal::OptionalBase<T>::storage_;
+};
+
+// Here after defines comparation operators. The definition follows
+// http://en.cppreference.com/w/cpp/utility/optional/operator_cmp
+// while bool() casting is replaced by has_value() to meet the chromium
+// style guide.
+template <class T, class U>
+constexpr bool operator==(const Optional<T>& lhs, const Optional<U>& rhs) {
+ if (lhs.has_value() != rhs.has_value())
+ return false;
+ if (!lhs.has_value())
+ return true;
+ return *lhs == *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator!=(const Optional<T>& lhs, const Optional<U>& rhs) {
+ if (lhs.has_value() != rhs.has_value())
+ return true;
+ if (!lhs.has_value())
+ return false;
+ return *lhs != *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator<(const Optional<T>& lhs, const Optional<U>& rhs) {
+ if (!rhs.has_value())
+ return false;
+ if (!lhs.has_value())
+ return true;
+ return *lhs < *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator<=(const Optional<T>& lhs, const Optional<U>& rhs) {
+ if (!lhs.has_value())
+ return true;
+ if (!rhs.has_value())
+ return false;
+ return *lhs <= *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator>(const Optional<T>& lhs, const Optional<U>& rhs) {
+ if (!lhs.has_value())
+ return false;
+ if (!rhs.has_value())
+ return true;
+ return *lhs > *rhs;
+}
+
+template <class T, class U>
+constexpr bool operator>=(const Optional<T>& lhs, const Optional<U>& rhs) {
+ if (!rhs.has_value())
+ return true;
+ if (!lhs.has_value())
+ return false;
+ return *lhs >= *rhs;
+}
+
+template <class T>
+constexpr bool operator==(const Optional<T>& opt, nullopt_t) {
+ return !opt;
+}
+
+template <class T>
+constexpr bool operator==(nullopt_t, const Optional<T>& opt) {
+ return !opt;
+}
+
+template <class T>
+constexpr bool operator!=(const Optional<T>& opt, nullopt_t) {
+ return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator!=(nullopt_t, const Optional<T>& opt) {
+ return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator<(const Optional<T>& opt, nullopt_t) {
+ return false;
+}
+
+template <class T>
+constexpr bool operator<(nullopt_t, const Optional<T>& opt) {
+ return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator<=(const Optional<T>& opt, nullopt_t) {
+ return !opt;
+}
+
+template <class T>
+constexpr bool operator<=(nullopt_t, const Optional<T>& opt) {
+ return true;
+}
+
+template <class T>
+constexpr bool operator>(const Optional<T>& opt, nullopt_t) {
+ return opt.has_value();
+}
+
+template <class T>
+constexpr bool operator>(nullopt_t, const Optional<T>& opt) {
+ return false;
+}
+
+template <class T>
+constexpr bool operator>=(const Optional<T>& opt, nullopt_t) {
+ return true;
+}
+
+template <class T>
+constexpr bool operator>=(nullopt_t, const Optional<T>& opt) {
+ return !opt;
+}
+
+template <class T, class U>
+constexpr bool operator==(const Optional<T>& opt, const U& value) {
+ return opt.has_value() ? *opt == value : false;
+}
+
+template <class T, class U>
+constexpr bool operator==(const U& value, const Optional<T>& opt) {
+ return opt.has_value() ? value == *opt : false;
+}
+
+template <class T, class U>
+constexpr bool operator!=(const Optional<T>& opt, const U& value) {
+ return opt.has_value() ? *opt != value : true;
+}
+
+template <class T, class U>
+constexpr bool operator!=(const U& value, const Optional<T>& opt) {
+ return opt.has_value() ? value != *opt : true;
+}
+
+template <class T, class U>
+constexpr bool operator<(const Optional<T>& opt, const U& value) {
+ return opt.has_value() ? *opt < value : true;
+}
+
+template <class T, class U>
+constexpr bool operator<(const U& value, const Optional<T>& opt) {
+ return opt.has_value() ? value < *opt : false;
+}
+
+template <class T, class U>
+constexpr bool operator<=(const Optional<T>& opt, const U& value) {
+ return opt.has_value() ? *opt <= value : true;
+}
+
+template <class T, class U>
+constexpr bool operator<=(const U& value, const Optional<T>& opt) {
+ return opt.has_value() ? value <= *opt : false;
+}
+
+template <class T, class U>
+constexpr bool operator>(const Optional<T>& opt, const U& value) {
+ return opt.has_value() ? *opt > value : false;
+}
+
+template <class T, class U>
+constexpr bool operator>(const U& value, const Optional<T>& opt) {
+ return opt.has_value() ? value > *opt : true;
+}
+
+template <class T, class U>
+constexpr bool operator>=(const Optional<T>& opt, const U& value) {
+ return opt.has_value() ? *opt >= value : false;
+}
+
+template <class T, class U>
+constexpr bool operator>=(const U& value, const Optional<T>& opt) {
+ return opt.has_value() ? value >= *opt : true;
+}
+
+template <class T>
+constexpr Optional<std::decay_t<T>> make_optional(T&& value) {
+ return Optional<std::decay_t<T>>(std::forward<T>(value));
+}
+
+template <class T, class... Args>
+constexpr Optional<T> make_optional(Args&&... args) {
+ return Optional<T>(in_place, std::forward<Args>(args)...);
+}
+
+template <class T, class U, class... Args>
+constexpr Optional<T> make_optional(std::initializer_list<U> il,
+ Args&&... args) {
+ return Optional<T>(in_place, il, std::forward<Args>(args)...);
+}
+
+// Partial specialization for a function template is not allowed. Also, it is
+// not allowed to add overload function to std namespace, while it is allowed
+// to specialize the template in std. Thus, swap() (kind of) overloading is
+// defined in base namespace, instead.
+template <class T>
+std::enable_if_t<std::is_move_constructible<T>::value &&
+ internal::IsSwappable<T>::value>
+swap(Optional<T>& lhs, Optional<T>& rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace base
+
+namespace std {
+
+template <class T>
+struct hash<base::Optional<T>> {
+ size_t operator()(const base::Optional<T>& opt) const {
+ return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
+ }
+};
+
+} // namespace std
+
+#endif // BASE_OPTIONAL_H_
diff --git a/security/sandbox/chromium/base/os_compat_android.h b/security/sandbox/chromium/base/os_compat_android.h
new file mode 100644
index 0000000000..e33b1f7ac3
--- /dev/null
+++ b/security/sandbox/chromium/base/os_compat_android.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_OS_COMPAT_ANDROID_H_
+#define BASE_OS_COMPAT_ANDROID_H_
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+
+// Not implemented in Bionic.
+extern "C" int futimes(int fd, const struct timeval tv[2]);
+
+// Not exposed or implemented in Bionic.
+extern "C" char* mkdtemp(char* path);
+
+// Android has no timegm().
+extern "C" time_t timegm(struct tm* const t);
+
+#endif // BASE_OS_COMPAT_ANDROID_H_
diff --git a/security/sandbox/chromium/base/path_service.h b/security/sandbox/chromium/base/path_service.h
new file mode 100644
index 0000000000..9b4715f073
--- /dev/null
+++ b/security/sandbox/chromium/base/path_service.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PATH_SERVICE_H_
+#define BASE_PATH_SERVICE_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/base_paths.h"
+#include "base/gtest_prod_util.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class FilePath;
+class ScopedPathOverride;
+
+// The path service is a global table mapping keys to file system paths. It is
+// OK to use this service from multiple threads.
+//
+class BASE_EXPORT PathService {
+ public:
+ // Retrieves a path to a special directory or file and places it into the
+ // string pointed to by 'path'. If you ask for a directory it is guaranteed
+ // to NOT have a path separator at the end. For example, "c:\windows\temp"
+ // Directories are also guaranteed to exist when this function succeeds.
+ //
+ // Returns true if the directory or file was successfully retrieved. On
+ // failure, 'path' will not be changed.
+ 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
+ // path specifies a directory that does not exist, the directory will be
+ // created by this method. This method returns true if successful.
+ //
+ // If the given path is relative, then it will be resolved against
+ // DIR_CURRENT.
+ //
+ // WARNING: Consumers of PathService::Get may expect paths to be constant
+ // over the lifetime of the app, so this method should be used with caution.
+ //
+ // Unit tests generally should use ScopedPathOverride instead. Overrides from
+ // one test should not carry over to another.
+ static bool Override(int key, const FilePath& path);
+
+ // This function does the same as PathService::Override but it takes extra
+ // parameters:
+ // - |is_absolute| indicates that |path| has already been expanded into an
+ // absolute path, otherwise MakeAbsoluteFilePath() will be used. This is
+ // useful to override paths that may not exist yet, since MakeAbsoluteFilePath
+ // fails for those. Note that MakeAbsoluteFilePath also expands symbolic
+ // links, even if path.IsAbsolute() is already true.
+ // - |create| guides whether the directory to be overriden must
+ // be created in case it doesn't exist already.
+ static bool OverrideAndCreateIfNeeded(int key,
+ const FilePath& path,
+ bool is_absolute,
+ bool create);
+
+ // To extend the set of supported keys, you can register a path provider,
+ // which is just a function mirroring PathService::Get. The ProviderFunc
+ // returns false if it cannot provide a non-empty path for the given key.
+ // Otherwise, true is returned.
+ //
+ // 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, FilePath*);
+
+ // Call to register a path provider. You must specify the range "[key_start,
+ // key_end)" of supported path keys.
+ static void RegisterProvider(ProviderFunc provider,
+ int key_start,
+ int key_end);
+
+ // Disable internal cache.
+ static void DisableCache();
+
+ private:
+ friend class ScopedPathOverride;
+ FRIEND_TEST_ALL_PREFIXES(PathServiceTest, RemoveOverride);
+
+ // Removes an override for a special directory or file. Returns true if there
+ // was an override to remove or false if none was present.
+ // NOTE: This function is intended to be used by tests only!
+ static bool RemoveOverride(int key);
+};
+
+} // namespace base
+
+#endif // BASE_PATH_SERVICE_H_
diff --git a/security/sandbox/chromium/base/posix/can_lower_nice_to.cc b/security/sandbox/chromium/base/posix/can_lower_nice_to.cc
new file mode 100644
index 0000000000..b1686dcae1
--- /dev/null
+++ b/security/sandbox/chromium/base/posix/can_lower_nice_to.cc
@@ -0,0 +1,60 @@
+// Copyright 2018 The Chromium Authors. 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/posix/can_lower_nice_to.h"
+
+#include <limits.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "build/build_config.h"
+
+// Not defined on AIX by default.
+#if defined(OS_AIX)
+#if defined(RLIMIT_NICE)
+#error Assumption about OS_AIX is incorrect
+#endif
+#define RLIMIT_NICE 20
+#endif
+
+namespace base {
+namespace internal {
+
+bool CanLowerNiceTo(int nice_value) {
+ // On a POSIX system, the nice value of a thread can be lowered 1. by the root
+ // user, 2. by a user with the CAP_SYS_NICE permission or 3. by any user if
+ // the target value is within the range allowed by RLIMIT_NICE.
+
+ // 1. Check for root user.
+ if (geteuid() == 0)
+ return true;
+
+ // 2. Skip checking the CAP_SYS_NICE permission because it would require
+ // libcap.so.
+
+ // 3. Check whether the target value is within the range allowed by
+ // RLIMIT_NICE.
+ //
+ // NZERO should be defined in <limits.h> per POSIX, and should be at least 20.
+ // (NZERO-1) is the highest possible niceness value (i.e. lowest priority).
+ // Most platforms use NZERO=20.
+ //
+ // RLIMIT_NICE tells us how much we can reduce niceness (increase priority) if
+ // we start at NZERO. For example, if NZERO is 20 and the rlimit is 30, we can
+ // lower niceness anywhere within the [-10, 19] range (20 - 30 = -10).
+ //
+ // So, we are allowed to reduce niceness to a minimum of NZERO - rlimit:
+ struct rlimit rlim;
+ if (getrlimit(RLIMIT_NICE, &rlim) != 0)
+ return false;
+ const int lowest_nice_allowed = NZERO - static_cast<int>(rlim.rlim_cur);
+
+ // And lowering niceness to |nice_value| is allowed if it is greater than or
+ // equal to the limit:
+ return nice_value >= lowest_nice_allowed;
+}
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/posix/can_lower_nice_to.h b/security/sandbox/chromium/base/posix/can_lower_nice_to.h
new file mode 100644
index 0000000000..aa8f02e9f7
--- /dev/null
+++ b/security/sandbox/chromium/base/posix/can_lower_nice_to.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. 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_POSIX_CAN_LOWER_NICE_TO_H_
+#define BASE_POSIX_CAN_LOWER_NICE_TO_H_
+
+namespace base {
+namespace internal {
+
+// Returns true if lowering the nice value of a process or thread to
+// |nice_value| using setpriority() or nice() should succeed. Note: A lower nice
+// value means a higher priority.
+bool CanLowerNiceTo(int nice_value);
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_POSIX_CAN_LOWER_NICE_TO_H_
diff --git a/security/sandbox/chromium/base/posix/eintr_wrapper.h b/security/sandbox/chromium/base/posix/eintr_wrapper.h
new file mode 100644
index 0000000000..0e6e437953
--- /dev/null
+++ b/security/sandbox/chromium/base/posix/eintr_wrapper.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This provides a wrapper around system calls which may be interrupted by a
+// signal and return EINTR. See man 7 signal.
+// To prevent long-lasting loops (which would likely be a bug, such as a signal
+// that should be masked) to go unnoticed, there is a limit after which the
+// caller will nonetheless see an EINTR in Debug builds.
+//
+// On Windows and Fuchsia, this wrapper macro does nothing because there are no
+// signals.
+//
+// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return
+// value of close is significant. See http://crbug.com/269623.
+
+#ifndef BASE_POSIX_EINTR_WRAPPER_H_
+#define BASE_POSIX_EINTR_WRAPPER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+
+#include <errno.h>
+
+#if defined(NDEBUG)
+
+#define HANDLE_EINTR(x) ({ \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR); \
+ eintr_wrapper_result; \
+})
+
+#else
+
+#define HANDLE_EINTR(x) ({ \
+ int eintr_wrapper_counter = 0; \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR && \
+ eintr_wrapper_counter++ < 100); \
+ eintr_wrapper_result; \
+})
+
+#endif // NDEBUG
+
+#define IGNORE_EINTR(x) ({ \
+ decltype(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ if (eintr_wrapper_result == -1 && errno == EINTR) { \
+ eintr_wrapper_result = 0; \
+ } \
+ } while (0); \
+ eintr_wrapper_result; \
+})
+
+#else // !OS_POSIX
+
+#define HANDLE_EINTR(x) (x)
+#define IGNORE_EINTR(x) (x)
+
+#endif // !OS_POSIX
+
+#endif // BASE_POSIX_EINTR_WRAPPER_H_
diff --git a/security/sandbox/chromium/base/posix/safe_strerror.cc b/security/sandbox/chromium/base/posix/safe_strerror.cc
new file mode 100644
index 0000000000..aef5742d33
--- /dev/null
+++ b/security/sandbox/chromium/base/posix/safe_strerror.cc
@@ -0,0 +1,128 @@
+// 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 "base/posix/safe_strerror.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "build/build_config.h"
+
+namespace base {
+
+#if defined(__GLIBC__) || defined(OS_NACL)
+#define USE_HISTORICAL_STRERRO_R 1
+#else
+#define USE_HISTORICAL_STRERRO_R 0
+#endif
+
+#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 == nullptr || 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);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/posix/safe_strerror.h b/security/sandbox/chromium/base/posix/safe_strerror.h
new file mode 100644
index 0000000000..2945312910
--- /dev/null
+++ b/security/sandbox/chromium/base/posix/safe_strerror.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_POSIX_SAFE_STRERROR_H_
+#define BASE_POSIX_SAFE_STRERROR_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/base_export.h"
+
+namespace base {
+
+// 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);
+
+} // namespace base
+
+#endif // BASE_POSIX_SAFE_STRERROR_H_
diff --git a/security/sandbox/chromium/base/process/environment_internal.cc b/security/sandbox/chromium/base/process/environment_internal.cc
new file mode 100644
index 0000000000..357140fa6f
--- /dev/null
+++ b/security/sandbox/chromium/base/process/environment_internal.cc
@@ -0,0 +1,128 @@
+// Copyright 2019 The Chromium Authors. 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/process/environment_internal.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+namespace base {
+namespace internal {
+
+namespace {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA) || defined(OS_WIN)
+// Parses a null-terminated input string of an environment block. The key is
+// placed into the given string, and the total length of the line, including
+// the terminating null, is returned.
+size_t ParseEnvLine(const NativeEnvironmentString::value_type* input,
+ NativeEnvironmentString* key) {
+ // Skip to the equals or end of the string, this is the key.
+ size_t cur = 0;
+ while (input[cur] && input[cur] != '=')
+ cur++;
+ *key = NativeEnvironmentString(&input[0], cur);
+
+ // Now just skip to the end of the string.
+ while (input[cur])
+ cur++;
+ return cur + 1;
+}
+#endif
+
+} // namespace
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+std::unique_ptr<char* []> AlterEnvironment(const char* const* const env,
+ const EnvironmentMap& changes) {
+ std::string value_storage; // Holds concatenated null-terminated strings.
+ std::vector<size_t> result_indices; // Line indices into value_storage.
+
+ // First build up all of the unchanged environment strings. These are
+ // null-terminated of the form "key=value".
+ std::string key;
+ for (size_t i = 0; env[i]; i++) {
+ size_t line_length = ParseEnvLine(env[i], &key);
+
+ // Keep only values not specified in the change vector.
+ auto found_change = changes.find(key);
+ if (found_change == changes.end()) {
+ result_indices.push_back(value_storage.size());
+ value_storage.append(env[i], line_length);
+ }
+ }
+
+ // Now append all modified and new values.
+ for (const auto& i : changes) {
+ if (!i.second.empty()) {
+ result_indices.push_back(value_storage.size());
+ value_storage.append(i.first);
+ value_storage.push_back('=');
+ value_storage.append(i.second);
+ value_storage.push_back(0);
+ }
+ }
+
+ size_t pointer_count_required =
+ result_indices.size() + 1 + // Null-terminated array of pointers.
+ (value_storage.size() + sizeof(char*) - 1) / sizeof(char*); // Buffer.
+ std::unique_ptr<char*[]> result(new char*[pointer_count_required]);
+
+ // The string storage goes after the array of pointers.
+ char* storage_data =
+ reinterpret_cast<char*>(&result.get()[result_indices.size() + 1]);
+ if (!value_storage.empty())
+ memcpy(storage_data, value_storage.data(), value_storage.size());
+
+ // Fill array of pointers at the beginning of the result.
+ for (size_t i = 0; i < result_indices.size(); i++)
+ result[i] = &storage_data[result_indices[i]];
+ result[result_indices.size()] = 0; // Null terminator.
+
+ return result;
+}
+
+#elif defined(OS_WIN)
+
+NativeEnvironmentString AlterEnvironment(const wchar_t* env,
+ const EnvironmentMap& changes) {
+ NativeEnvironmentString result;
+
+ // First build up all of the unchanged environment strings.
+ const wchar_t* ptr = env;
+ while (*ptr) {
+ std::wstring key;
+ size_t line_length = ParseEnvLine(ptr, &key);
+
+ // Keep only values not specified in the change vector.
+ if (changes.find(key) == changes.end()) {
+ result.append(ptr, line_length);
+ }
+ ptr += line_length;
+ }
+
+ // Now append all modified and new values.
+ for (const auto& i : changes) {
+ // Windows environment blocks cannot handle keys or values with NULs.
+ CHECK_EQ(std::wstring::npos, i.first.find(L'\0'));
+ CHECK_EQ(std::wstring::npos, i.second.find(L'\0'));
+ if (!i.second.empty()) {
+ result += i.first;
+ result.push_back('=');
+ result += i.second;
+ result.push_back('\0');
+ }
+ }
+
+ // Add the terminating NUL.
+ result.push_back('\0');
+ return result;
+}
+
+#endif // OS_POSIX || OS_FUCHSIA
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/process/environment_internal.h b/security/sandbox/chromium/base/process/environment_internal.h
new file mode 100644
index 0000000000..31338f1320
--- /dev/null
+++ b/security/sandbox/chromium/base/process/environment_internal.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium 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 internal routines that are called by other files in
+// base/process/.
+
+#ifndef BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
+#define BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
+
+#include <memory>
+
+#include "base/environment.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace internal {
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// Returns a modified environment vector constructed from the given environment
+// and the list of changes given in |changes|. Each key in the environment is
+// matched against the first element of the pairs. In the event of a match, the
+// value is replaced by the second of the pair, unless the second is empty, in
+// which case the key-value is removed.
+//
+// This POSIX version takes and returns a POSIX-style environment block, which
+// is a null-terminated list of pointers to null-terminated strings. The
+// returned array will have appended to it the storage for the array itself so
+// there is only one pointer to manage, but this means that you can't copy the
+// array without keeping the original around.
+BASE_EXPORT std::unique_ptr<char*[]> AlterEnvironment(
+ const char* const* env,
+ const EnvironmentMap& changes);
+#elif defined(OS_WIN)
+// Returns a modified environment vector constructed from the given environment
+// and the list of changes given in |changes|. Each key in the environment is
+// matched against the first element of the pairs. In the event of a match, the
+// value is replaced by the second of the pair, unless the second is empty, in
+// which case the key-value is removed.
+//
+// This Windows version takes and returns a Windows-style environment block,
+// which is a string containing several null-terminated strings followed by an
+// extra terminating null character. So, e.g., the environment A=1 B=2 is
+// represented as L"A=1\0B=2\0\0".
+BASE_EXPORT NativeEnvironmentString
+AlterEnvironment(const wchar_t* env, const EnvironmentMap& changes);
+#endif // OS_*
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_PROCESS_ENVIRONMENT_INTERNAL_H_
diff --git a/security/sandbox/chromium/base/process/kill.h b/security/sandbox/chromium/base/process/kill.h
new file mode 100644
index 0000000000..70a04d97e5
--- /dev/null
+++ b/security/sandbox/chromium/base/process/kill.h
@@ -0,0 +1,162 @@
+// 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.
+
+// This file contains routines to kill processes and get the exit code and
+// termination status.
+
+#ifndef BASE_PROCESS_KILL_H_
+#define BASE_PROCESS_KILL_H_
+
+#include "base/files/file_path.h"
+#include "base/process/process.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+
+namespace base {
+
+class ProcessFilter;
+
+#if defined(OS_WIN)
+namespace win {
+
+// See definition in sandbox/win/src/sandbox_types.h
+const DWORD kSandboxFatalMemoryExceeded = 7012;
+
+// Exit codes with special meanings on Windows.
+const DWORD kNormalTerminationExitCode = 0;
+const DWORD kDebuggerInactiveExitCode = 0xC0000354;
+const DWORD kKeyboardInterruptExitCode = 0xC000013A;
+const DWORD kDebuggerTerminatedExitCode = 0x40010004;
+const DWORD kStatusInvalidImageHashExitCode = 0xC0000428;
+
+// This exit code is used by the Windows task manager when it kills a
+// process. It's value is obviously not that unique, and it's
+// surprising to me that the task manager uses this value, but it
+// seems to be common practice on Windows to test for it as an
+// indication that the task manager has killed something if the
+// process goes away.
+const DWORD kProcessKilledExitCode = 1;
+
+} // namespace win
+
+#endif // OS_WIN
+
+// Return status values from GetTerminationStatus. Don't use these as
+// exit code arguments to KillProcess*(), use platform/application
+// specific values instead.
+enum TerminationStatus {
+ // clang-format off
+ TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status
+ TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status
+ TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
+ TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
+ TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
+#if defined(OS_CHROMEOS)
+ // Used for the case when oom-killer kills a process on ChromeOS.
+ TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM,
+#endif
+#if defined(OS_ANDROID)
+ // On Android processes are spawned from the system Zygote and we do not get
+ // the termination status. We can't know if the termination was a crash or an
+ // oom kill for sure, but we can use status of the strong process bindings as
+ // a hint.
+ TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill
+#endif
+ TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
+ TERMINATION_STATUS_OOM, // Process died due to oom
+#if defined(OS_WIN)
+ // On Windows, the OS terminated process due to code integrity failure.
+ TERMINATION_STATUS_INTEGRITY_FAILURE,
+#endif
+ TERMINATION_STATUS_MAX_ENUM
+ // clang-format on
+};
+
+// Attempts to kill all the processes on the current machine that were launched
+// from the given executable name, ending them with the given exit code. If
+// filter is non-null, then only processes selected by the filter are killed.
+// Returns true if all processes were able to be killed off, false if at least
+// one couldn't be killed.
+BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name,
+ int exit_code,
+ const ProcessFilter* filter);
+
+#if defined(OS_POSIX)
+// Attempts to kill the process group identified by |process_group_id|. Returns
+// true on success.
+BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id);
+#endif // defined(OS_POSIX)
+
+// Get the termination status of the process by interpreting the
+// circumstances of the child process' death. |exit_code| is set to
+// the status returned by waitpid() on POSIX, and from GetExitCodeProcess() on
+// Windows, and may not be null. Note that on Linux, this function
+// will only return a useful result the first time it is called after
+// the child exits (because it will reap the child and the information
+// will no longer be available).
+BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle,
+ int* exit_code);
+
+#if defined(OS_POSIX)
+// Send a kill signal to the process and then wait for the process to exit
+// and get the termination status.
+//
+// This is used in situations where it is believed that the process is dead
+// or dying (because communication with the child process has been cut).
+// In order to avoid erroneously returning that the process is still running
+// because the kernel is still cleaning it up, this will wait for the process
+// to terminate. In order to avoid the risk of hanging while waiting for the
+// process to terminate, send a SIGKILL to the process before waiting for the
+// termination status.
+//
+// Note that it is not an option to call WaitForExitCode and then
+// GetTerminationStatus as the child will be reaped when WaitForExitCode
+// returns, and this information will be lost.
+//
+BASE_EXPORT TerminationStatus GetKnownDeadTerminationStatus(
+ ProcessHandle handle, int* exit_code);
+
+#if defined(OS_LINUX)
+// Spawns a thread to wait asynchronously for the child |process| to exit
+// and then reaps it.
+BASE_EXPORT void EnsureProcessGetsReaped(Process process);
+#endif // defined(OS_LINUX)
+#endif // defined(OS_POSIX)
+
+// Registers |process| to be asynchronously monitored for termination, forcibly
+// terminated if necessary, and reaped on exit. The caller should have signalled
+// |process| to exit before calling this API. The API will allow a couple of
+// seconds grace period before forcibly terminating |process|.
+// TODO(https://crbug.com/806451): The Mac implementation currently blocks the
+// calling thread for up to two seconds.
+BASE_EXPORT void EnsureProcessTerminated(Process process);
+
+// These are only sparingly used, and not needed on Fuchsia. They could be
+// implemented if necessary.
+#if !defined(OS_FUCHSIA)
+// Wait for all the processes based on the named executable to exit. If filter
+// is non-null, then only processes selected by the filter are waited on.
+// Returns after all processes have exited or wait_milliseconds have expired.
+// Returns true if all the processes exited, false otherwise.
+BASE_EXPORT bool WaitForProcessesToExit(
+ const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ const ProcessFilter* filter);
+
+// Waits a certain amount of time (can be 0) for all the processes with a given
+// executable name to exit, then kills off any of them that are still around.
+// If filter is non-null, then only processes selected by the filter are waited
+// on. Killed processes are ended with the given exit code. Returns false if
+// any processes needed to be killed, true if they all exited cleanly within
+// the wait_milliseconds delay.
+BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name,
+ base::TimeDelta wait,
+ int exit_code,
+ const ProcessFilter* filter);
+#endif // !defined(OS_FUCHSIA)
+
+} // namespace base
+
+#endif // BASE_PROCESS_KILL_H_
diff --git a/security/sandbox/chromium/base/process/memory.h b/security/sandbox/chromium/base/process/memory.h
new file mode 100644
index 0000000000..ddbb9d957c
--- /dev/null
+++ b/security/sandbox/chromium/base/process/memory.h
@@ -0,0 +1,89 @@
+// 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 BASE_PROCESS_MEMORY_H_
+#define BASE_PROCESS_MEMORY_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/process/process_handle.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// Enables 'terminate on heap corruption' flag. Helps protect against heap
+// overflow. Has no effect if the OS doesn't provide the necessary facility.
+BASE_EXPORT void EnableTerminationOnHeapCorruption();
+
+// Turns on process termination if memory runs out.
+BASE_EXPORT void EnableTerminationOnOutOfMemory();
+
+// Terminates process. Should be called only for out of memory errors.
+// Crash reporting classifies such crashes as OOM.
+BASE_EXPORT void TerminateBecauseOutOfMemory(size_t size);
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX)
+BASE_EXPORT extern size_t g_oom_size;
+
+// The maximum allowed value for the OOM score.
+const int kMaxOomScore = 1000;
+
+// This adjusts /proc/<pid>/oom_score_adj so the Linux OOM killer will
+// prefer to kill certain process types over others. The range for the
+// adjustment is [-1000, 1000], with [0, 1000] being user accessible.
+// If the Linux system doesn't support the newer oom_score_adj range
+// of [0, 1000], then we revert to using the older oom_adj, and
+// translate the given value into [0, 15]. Some aliasing of values
+// may occur in that case, of course.
+BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
+#endif
+
+namespace internal {
+// Returns true if address-space was released. Some configurations reserve part
+// of the process address-space for special allocations (e.g. WASM).
+bool ReleaseAddressSpaceReservation();
+} // namespace internal
+
+#if defined(OS_WIN)
+namespace win {
+
+// Custom Windows exception code chosen to indicate an out of memory error.
+// See https://msdn.microsoft.com/en-us/library/het71c37.aspx.
+// "To make sure that you do not define a code that conflicts with an existing
+// exception code" ... "The resulting error code should therefore have the
+// highest four bits set to hexadecimal E."
+// 0xe0000008 was chosen arbitrarily, as 0x00000008 is ERROR_NOT_ENOUGH_MEMORY.
+const DWORD kOomExceptionCode = 0xe0000008;
+
+} // namespace win
+#endif
+
+namespace internal {
+
+// Handles out of memory, with the failed allocation |size|, or 0 when it is not
+// known.
+BASE_EXPORT void OnNoMemoryInternal(size_t size);
+
+} // namespace internal
+
+// Special allocator functions for callers that want to check for OOM.
+// These will not abort if the allocation fails even if
+// EnableTerminationOnOutOfMemory has been called.
+// This can be useful for huge and/or unpredictable size memory allocations.
+// Please only use this if you really handle the case when the allocation
+// fails. Doing otherwise would risk security.
+// These functions may still crash on OOM when running under memory tools,
+// specifically ASan and other sanitizers.
+// Return value tells whether the allocation succeeded. If it fails |result| is
+// set to NULL, otherwise it holds the memory address.
+BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
+ void** result);
+BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
+ size_t size,
+ void** result);
+
+} // namespace base
+
+#endif // BASE_PROCESS_MEMORY_H_
diff --git a/security/sandbox/chromium/base/process/process.h b/security/sandbox/chromium/base/process/process.h
new file mode 100644
index 0000000000..d6f8d83e36
--- /dev/null
+++ b/security/sandbox/chromium/base/process/process.h
@@ -0,0 +1,223 @@
+// Copyright 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_PROCESS_PROCESS_H_
+#define BASE_PROCESS_PROCESS_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/process/process_handle.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
+#if defined(OS_FUCHSIA)
+#include <lib/zx/process.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include "base/feature_list.h"
+#include "base/process/port_provider_mac.h"
+#endif
+
+namespace base {
+
+#if defined(OS_MACOSX)
+extern const Feature kMacAllowBackgroundingProcesses;
+#endif
+
+// Provides a move-only encapsulation of a process.
+//
+// This object is not tied to the lifetime of the underlying process: the
+// process may be killed and this object may still around, and it will still
+// claim to be valid. The actual behavior in that case is OS dependent like so:
+//
+// Windows: The underlying ProcessHandle will be valid after the process dies
+// and can be used to gather some information about that process, but most
+// methods will obviously fail.
+//
+// POSIX: The underlying ProcessHandle is not guaranteed to remain valid after
+// the process dies, and it may be reused by the system, which means that it may
+// end up pointing to the wrong process.
+class BASE_EXPORT Process {
+ public:
+ // On Windows, this takes ownership of |handle|. On POSIX, this does not take
+ // ownership of |handle|.
+ explicit Process(ProcessHandle handle = kNullProcessHandle);
+
+ Process(Process&& other);
+
+ // The destructor does not terminate the process.
+ ~Process();
+
+ Process& operator=(Process&& other);
+
+ // Returns an object for the current process.
+ static Process Current();
+
+ // Returns a Process for the given |pid|.
+ static Process Open(ProcessId pid);
+
+ // Returns a Process for the given |pid|. On Windows the handle is opened
+ // with more access rights and must only be used by trusted code (can read the
+ // address space and duplicate handles).
+ static Process OpenWithExtraPrivileges(ProcessId pid);
+
+#if defined(OS_WIN)
+ // Returns a Process for the given |pid|, using some |desired_access|.
+ // See ::OpenProcess documentation for valid |desired_access|.
+ static Process OpenWithAccess(ProcessId pid, DWORD desired_access);
+#endif
+
+ // Creates an object from a |handle| owned by someone else.
+ // Don't use this for new code. It is only intended to ease the migration to
+ // a strict ownership model.
+ // TODO(rvargas) crbug.com/417532: Remove this code.
+ static Process DeprecatedGetProcessFromHandle(ProcessHandle handle);
+
+ // Returns true if processes can be backgrounded.
+ static bool CanBackgroundProcesses();
+
+ // Terminates the current process immediately with |exit_code|.
+ [[noreturn]] static void TerminateCurrentProcessImmediately(int exit_code);
+
+ // Returns true if this objects represents a valid process.
+ bool IsValid() const;
+
+ // Returns a handle for this process. There is no guarantee about when that
+ // handle becomes invalid because this object retains ownership.
+ ProcessHandle Handle() const;
+
+ // Returns a second object that represents this process.
+ Process Duplicate() const;
+
+ // Get the PID for this process.
+ ProcessId Pid() const;
+
+#if !defined(OS_ANDROID)
+ // Get the creation time for this process. Since the Pid can be reused after a
+ // process dies, it is useful to use both the Pid and the creation time to
+ // uniquely identify a process.
+ //
+ // Not available on Android because /proc/stat/ cannot be accessed on O+.
+ // https://issuetracker.google.com/issues/37140047
+ Time CreationTime() const;
+#endif // !defined(OS_ANDROID)
+
+ // Returns true if this process is the current process.
+ bool is_current() const;
+
+ // Close the process handle. This will not terminate the process.
+ void Close();
+
+ // Returns true if this process is still running. This is only safe on Windows
+ // (and maybe Fuchsia?), because the ProcessHandle will keep the zombie
+ // process information available until itself has been released. But on Posix,
+ // the OS may reuse the ProcessId.
+#if defined(OS_WIN)
+ bool IsRunning() const {
+ return !WaitForExitWithTimeout(base::TimeDelta(), nullptr);
+ }
+#endif
+
+ // Terminates the process with extreme prejudice. The given |exit_code| will
+ // be the exit code of the process. If |wait| is true, this method will wait
+ // for up to one minute for the process to actually terminate.
+ // Returns true if the process terminates within the allowed time.
+ // NOTE: On POSIX |exit_code| is ignored.
+ bool Terminate(int exit_code, bool wait) const;
+
+ // Waits for the process to exit. Returns true on success.
+ // On POSIX, if the process has been signaled then |exit_code| is set to -1.
+ // On Linux this must be a child process, however on Mac and Windows it can be
+ // any process.
+ // NOTE: |exit_code| is optional, nullptr can be passed if the exit code is
+ // not required.
+ bool WaitForExit(int* exit_code) const;
+
+ // Same as WaitForExit() but only waits for up to |timeout|.
+ // NOTE: |exit_code| is optional, nullptr can be passed if the exit code
+ // is not required.
+ bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const;
+
+ // Indicates that the process has exited with the specified |exit_code|.
+ // This should be called if process exit is observed outside of this class.
+ // (i.e. Not because Terminate or WaitForExit, above, was called.)
+ // Note that nothing prevents this being called multiple times for a dead
+ // process though that should be avoided.
+ void Exited(int exit_code) const;
+
+#if defined(OS_MACOSX)
+ // The Mac needs a Mach port in order to manipulate a process's priority,
+ // and there's no good way to get that from base given the pid. These Mac
+ // variants of the IsProcessBackgrounded and SetProcessBackgrounded API take
+ // a port provider for this reason. See crbug.com/460102
+ //
+ // A process is backgrounded when its task priority is
+ // |TASK_BACKGROUND_APPLICATION|.
+ //
+ // Returns true if the port_provider can locate a task port for the process
+ // and it is backgrounded. If port_provider is null, returns false.
+ bool IsProcessBackgrounded(PortProvider* port_provider) const;
+
+ // Set the process as backgrounded. If value is
+ // true, the priority of the associated task will be set to
+ // TASK_BACKGROUND_APPLICATION. If value is false, the
+ // priority of the process will be set to TASK_FOREGROUND_APPLICATION.
+ //
+ // Returns true if the priority was changed, false otherwise. If
+ // |port_provider| is null, this is a no-op and it returns false.
+ bool SetProcessBackgrounded(PortProvider* port_provider, bool value);
+#else
+ // A process is backgrounded when it's priority is lower than normal.
+ // Return true if this process is backgrounded, false otherwise.
+ bool IsProcessBackgrounded() const;
+
+ // Set a process as backgrounded. If value is true, the priority of the
+ // process will be lowered. If value is false, the priority of the process
+ // will be made "normal" - equivalent to default process priority.
+ // Returns true if the priority was changed, false otherwise.
+ bool SetProcessBackgrounded(bool value);
+#endif // defined(OS_MACOSX)
+ // Returns an integer representing the priority of a process. The meaning
+ // of this value is OS dependent.
+ int GetPriority() const;
+
+#if defined(OS_CHROMEOS)
+ // Get the PID in its PID namespace.
+ // If the process is not in a PID namespace or /proc/<pid>/status does not
+ // report NSpid, kNullProcessId is returned.
+ ProcessId GetPidInNamespace() const;
+#endif
+
+ private:
+#if defined(OS_WIN)
+ win::ScopedHandle process_;
+#elif defined(OS_FUCHSIA)
+ zx::process process_;
+#else
+ ProcessHandle process_;
+#endif
+
+#if defined(OS_WIN) || defined(OS_FUCHSIA)
+ bool is_current_process_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(Process);
+};
+
+#if defined(OS_CHROMEOS)
+// Exposed for testing.
+// Given the contents of the /proc/<pid>/cgroup file, determine whether the
+// process is backgrounded or not.
+BASE_EXPORT bool IsProcessBackgroundedCGroup(
+ const StringPiece& cgroup_contents);
+#endif // defined(OS_CHROMEOS)
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_H_
diff --git a/security/sandbox/chromium/base/process/process_handle.h b/security/sandbox/chromium/base/process/process_handle.h
new file mode 100644
index 0000000000..94f7006119
--- /dev/null
+++ b/security/sandbox/chromium/base/process/process_handle.h
@@ -0,0 +1,142 @@
+// 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 BASE_PROCESS_PROCESS_HANDLE_H_
+#define BASE_PROCESS_PROCESS_HANDLE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "base/base_export.h"
+#include "base/files/file_path.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_types.h"
+#endif
+
+#if defined(OS_FUCHSIA)
+#include <zircon/types.h>
+#endif
+
+namespace base {
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+// ProcessId is a number which identifies the process in the OS.
+#if defined(OS_WIN)
+typedef HANDLE ProcessHandle;
+typedef DWORD ProcessId;
+typedef HANDLE UserTokenHandle;
+const ProcessHandle kNullProcessHandle = NULL;
+const ProcessId kNullProcessId = 0;
+#elif defined(OS_FUCHSIA)
+typedef zx_handle_t ProcessHandle;
+typedef zx_koid_t ProcessId;
+const ProcessHandle kNullProcessHandle = ZX_HANDLE_INVALID;
+const ProcessId kNullProcessId = ZX_KOID_INVALID;
+#elif defined(OS_POSIX)
+// On POSIX, our ProcessHandle will just be the PID.
+typedef pid_t ProcessHandle;
+typedef pid_t ProcessId;
+const ProcessHandle kNullProcessHandle = 0;
+const ProcessId kNullProcessId = 0;
+#endif // defined(OS_WIN)
+
+// To print ProcessIds portably use CrPRIdPid (based on PRIuS and friends from
+// C99 and format_macros.h) like this:
+// base::StringPrintf("PID is %" CrPRIdPid ".\n", pid);
+#if defined(OS_WIN) || defined(OS_FUCHSIA)
+#define CrPRIdPid "ld"
+#else
+#define CrPRIdPid "d"
+#endif
+
+class UniqueProcId {
+ public:
+ explicit UniqueProcId(ProcessId value) : value_(value) {}
+ UniqueProcId(const UniqueProcId& other) = default;
+ UniqueProcId& operator=(const UniqueProcId& other) = default;
+
+ // Returns the process PID. WARNING: On some platforms, the pid may not be
+ // valid within the current process sandbox.
+ ProcessId GetUnsafeValue() const { return value_; }
+
+ bool operator==(const UniqueProcId& other) const {
+ return value_ == other.value_;
+ }
+
+ bool operator!=(const UniqueProcId& other) const {
+ return value_ != other.value_;
+ }
+
+ bool operator<(const UniqueProcId& other) const {
+ return value_ < other.value_;
+ }
+
+ bool operator<=(const UniqueProcId& other) const {
+ return value_ <= other.value_;
+ }
+
+ bool operator>(const UniqueProcId& other) const {
+ return value_ > other.value_;
+ }
+
+ bool operator>=(const UniqueProcId& other) const {
+ return value_ >= other.value_;
+ }
+
+ private:
+ ProcessId value_;
+};
+
+std::ostream& operator<<(std::ostream& os, const UniqueProcId& obj);
+
+// 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.
+BASE_EXPORT UniqueProcId 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();
+
+// 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_FUCHSIA)
+// Returns the ID for the parent of the given process. Not available on Fuchsia.
+// Returning a negative value indicates an error, such as if the |process| does
+// not exist. Returns 0 when |process| has no parent process.
+BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process);
+#endif // !defined(OS_FUCHSIA)
+
+#if defined(OS_POSIX)
+// Returns the path to the executable of the given process.
+BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
+#endif
+
+} // namespace base
+
+#endif // BASE_PROCESS_PROCESS_HANDLE_H_
diff --git a/security/sandbox/chromium/base/process/process_handle_win.cc b/security/sandbox/chromium/base/process/process_handle_win.cc
new file mode 100644
index 0000000000..ccc759039d
--- /dev/null
+++ b/security/sandbox/chromium/base/process/process_handle_win.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/process/process_handle.h"
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+
+namespace base {
+
+ProcessId GetCurrentProcId() {
+ return ::GetCurrentProcessId();
+}
+
+ProcessHandle GetCurrentProcessHandle() {
+ return ::GetCurrentProcess();
+}
+
+ProcessId GetProcId(ProcessHandle process) {
+ if (process == base::kNullProcessHandle)
+ return 0;
+ // This returns 0 if we have insufficient rights to query the process handle.
+ // Invalid handles or non-process handles will cause a hard failure.
+ ProcessId result = GetProcessId(process);
+ CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE)
+ << "process handle = " << process;
+ return result;
+}
+
+ProcessId GetParentProcessId(ProcessHandle process) {
+ ProcessId child_pid = GetProcId(process);
+ PROCESSENTRY32 process_entry;
+ process_entry.dwSize = sizeof(PROCESSENTRY32);
+
+ win::ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+ if (snapshot.IsValid() && Process32First(snapshot.Get(), &process_entry)) {
+ do {
+ if (process_entry.th32ProcessID == child_pid)
+ return process_entry.th32ParentProcessID;
+ } while (Process32Next(snapshot.Get(), &process_entry));
+ }
+
+ // TODO(zijiehe): To match other platforms, -1 (UINT32_MAX) should be returned
+ // if |child_id| cannot be found in the |snapshot|.
+ return 0u;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/rand_util.h b/security/sandbox/chromium/base/rand_util.h
new file mode 100644
index 0000000000..45e4283223
--- /dev/null
+++ b/security/sandbox/chromium/base/rand_util.h
@@ -0,0 +1,78 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_RAND_UTIL_H_
+#define BASE_RAND_UTIL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// 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);
+
+// Returns a random number in range [0, range). Thread-safe.
+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_t bits);
+
+// Fills |output_length| bytes of |output| with random data. Thread-safe.
+//
+// Although implementations are required to use a cryptographically secure
+// random number source, code outside of base/ that relies on this should use
+// crypto::RandBytes instead to ensure the requirement is easily discoverable.
+BASE_EXPORT void RandBytes(void* output, size_t output_length);
+
+// Fills a string of length |length| with random data and returns it.
+// |length| should be nonzero. Thread-safe.
+//
+// Note that this is a variation of |RandBytes| with a different return type.
+// The returned string is likely not ASCII/UTF-8. Use with care.
+//
+// Although implementations are required to use a cryptographically secure
+// random number source, code outside of base/ that relies on this should use
+// crypto::RandBytes instead to ensure the requirement is easily discoverable.
+BASE_EXPORT std::string RandBytesAsString(size_t length);
+
+// An STL UniformRandomBitGenerator backed by RandUint64.
+// TODO(tzik): Consider replacing this with a faster implementation.
+class RandomBitGenerator {
+ public:
+ using result_type = uint64_t;
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return UINT64_MAX; }
+ result_type operator()() const { return RandUint64(); }
+
+ RandomBitGenerator() = default;
+ ~RandomBitGenerator() = default;
+};
+
+// Shuffles [first, last) randomly. Thread-safe.
+template <typename Itr>
+void RandomShuffle(Itr first, Itr last) {
+ std::shuffle(first, last, RandomBitGenerator());
+}
+
+#if defined(OS_POSIX)
+BASE_EXPORT int GetUrandomFD();
+#endif
+
+} // namespace base
+
+#endif // BASE_RAND_UTIL_H_
diff --git a/security/sandbox/chromium/base/rand_util_win.cc b/security/sandbox/chromium/base/rand_util_win.cc
new file mode 100644
index 0000000000..193a3f63a3
--- /dev/null
+++ b/security/sandbox/chromium/base/rand_util_win.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/rand_util.h"
+
+#include <windows.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// #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 <ntsecapi.h>
+#undef SystemFunction036
+
+#include <algorithm>
+#include <limits>
+
+#include "base/logging.h"
+
+namespace base {
+
+void RandBytes(void* output, size_t output_length) {
+ char* output_ptr = static_cast<char*>(output);
+ while (output_length > 0) {
+ const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min(
+ output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max())));
+ const bool success =
+ RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE;
+ CHECK(success);
+ output_length -= output_bytes_this_pass;
+ output_ptr += output_bytes_this_pass;
+ }
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/scoped_clear_last_error.h b/security/sandbox/chromium/base/scoped_clear_last_error.h
new file mode 100644
index 0000000000..b19f0436ae
--- /dev/null
+++ b/security/sandbox/chromium/base/scoped_clear_last_error.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2018 The Chromium Authors. 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_SCOPED_CLEAR_LAST_ERROR_H_
+#define BASE_SCOPED_CLEAR_LAST_ERROR_H_
+
+#include <errno.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace internal {
+
+// ScopedClearLastError stores and resets the value of thread local error codes
+// (errno, GetLastError()), and restores them in the destructor. This is useful
+// to avoid side effects on these values in instrumentation functions that
+// interact with the OS.
+
+// Common implementation of ScopedClearLastError for all platforms. Use
+// ScopedClearLastError instead.
+class BASE_EXPORT ScopedClearLastErrorBase {
+ public:
+ ScopedClearLastErrorBase() : last_errno_(errno) { errno = 0; }
+ ~ScopedClearLastErrorBase() { errno = last_errno_; }
+
+ private:
+ const int last_errno_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedClearLastErrorBase);
+};
+
+#if defined(OS_WIN)
+
+// Windows specific implementation of ScopedClearLastError.
+class BASE_EXPORT ScopedClearLastError : public ScopedClearLastErrorBase {
+ public:
+ ScopedClearLastError();
+ ~ScopedClearLastError();
+
+ private:
+ unsigned int last_system_error_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedClearLastError);
+};
+
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+
+using ScopedClearLastError = ScopedClearLastErrorBase;
+
+#endif // defined(OS_WIN)
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_SCOPED_CLEAR_LAST_ERROR_H_
diff --git a/security/sandbox/chromium/base/scoped_clear_last_error_win.cc b/security/sandbox/chromium/base/scoped_clear_last_error_win.cc
new file mode 100644
index 0000000000..cdf996359e
--- /dev/null
+++ b/security/sandbox/chromium/base/scoped_clear_last_error_win.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2018 The Chromium Authors. 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/scoped_clear_last_error.h"
+
+#include <windows.h>
+
+namespace base {
+namespace internal {
+
+ScopedClearLastError::ScopedClearLastError()
+ : last_system_error_(::GetLastError()) {
+ ::SetLastError(0);
+}
+
+ScopedClearLastError::~ScopedClearLastError() {
+ ::SetLastError(last_system_error_);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/sequence_checker.h b/security/sandbox/chromium/base/sequence_checker.h
new file mode 100644
index 0000000000..60ffd75a4f
--- /dev/null
+++ b/security/sandbox/chromium/base/sequence_checker.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SEQUENCE_CHECKER_H_
+#define BASE_SEQUENCE_CHECKER_H_
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/sequence_checker_impl.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+// SequenceChecker is a helper class used to help verify that some methods of a
+// class are called sequentially (for thread-safety). It supports thread safety
+// annotations (see base/thread_annotations.h).
+//
+// Use the macros below instead of the SequenceChecker directly so that the
+// unused member doesn't result in an extra byte (four when padded) per
+// instance in production.
+//
+// This class is much prefered to ThreadChecker for thread-safety checks.
+// ThreadChecker should only be used for classes that are truly thread-affine
+// (use thread-local-storage or a third-party API that does).
+//
+// Usage:
+// class MyClass {
+// public:
+// MyClass() {
+// // It's sometimes useful to detach on construction for objects that are
+// // constructed in one place and forever after used from another
+// // sequence.
+// DETACH_FROM_SEQUENCE(my_sequence_checker_);
+// }
+//
+// ~MyClass() {
+// // SequenceChecker doesn't automatically check it's destroyed on origin
+// // sequence for the same reason it's sometimes detached in the
+// // constructor. It's okay to destroy off sequence if the owner
+// // otherwise knows usage on the associated sequence is done. If you're
+// // not detaching in the constructor, you probably want to explicitly
+// // check in the destructor.
+// DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+// }
+// void MyMethod() {
+// DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
+// ... (do stuff) ...
+// MyOtherMethod();
+// }
+//
+// void MyOtherMethod()
+// VALID_CONTEXT_REQUIRED(my_sequence_checker_) {
+// foo_ = 42;
+// }
+//
+// private:
+// // GUARDED_BY_CONTEXT() enforces that this member is only
+// // accessed from a scope that invokes DCHECK_CALLED_ON_VALID_SEQUENCE()
+// // or from a function annotated with VALID_CONTEXT_REQUIRED(). A
+// // DCHECK build will not compile if the member is accessed and these
+// // conditions are not met.
+// int foo_ GUARDED_BY_CONTEXT(my_sequence_checker_);
+//
+// SEQUENCE_CHECKER(my_sequence_checker_);
+// }
+
+#define SEQUENCE_CHECKER_INTERNAL_CONCAT2(a, b) a##b
+#define SEQUENCE_CHECKER_INTERNAL_CONCAT(a, b) \
+ SEQUENCE_CHECKER_INTERNAL_CONCAT2(a, b)
+#define SEQUENCE_CHECKER_INTERNAL_UID(prefix) \
+ SEQUENCE_CHECKER_INTERNAL_CONCAT(prefix, __LINE__)
+
+#if DCHECK_IS_ON()
+#define SEQUENCE_CHECKER(name) base::SequenceChecker name
+#define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) \
+ base::ScopedValidateSequenceChecker SEQUENCE_CHECKER_INTERNAL_UID( \
+ scoped_validate_sequence_checker_)(name, ##__VA_ARGS__);
+#define DETACH_FROM_SEQUENCE(name) (name).DetachFromSequence()
+#else // DCHECK_IS_ON()
+#if __OBJC__ && defined(OS_IOS) && !HAS_FEATURE(objc_cxx_static_assert)
+// TODO(thakis): Remove this branch once Xcode's clang has clang r356148.
+#define SEQUENCE_CHECKER(name)
+#else
+#define SEQUENCE_CHECKER(name) static_assert(true, "")
+#endif
+#define DCHECK_CALLED_ON_VALID_SEQUENCE(name, ...) EAT_STREAM_PARAMETERS
+#define DETACH_FROM_SEQUENCE(name)
+#endif // DCHECK_IS_ON()
+
+namespace base {
+
+// Do nothing implementation, for use in release mode.
+//
+// Note: You should almost always use the SequenceChecker class (through the
+// above macros) to get the right version for your build configuration.
+// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
+// order to support thread_annotations.h.
+class LOCKABLE SequenceCheckerDoNothing {
+ public:
+ SequenceCheckerDoNothing() = default;
+
+ // Moving between matching sequences is allowed to help classes with
+ // SequenceCheckers that want a default move-construct/assign.
+ SequenceCheckerDoNothing(SequenceCheckerDoNothing&& other) = default;
+ SequenceCheckerDoNothing& operator=(SequenceCheckerDoNothing&& other) =
+ default;
+
+ bool CalledOnValidSequence() const WARN_UNUSED_RESULT { return true; }
+ void DetachFromSequence() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SequenceCheckerDoNothing);
+};
+
+#if DCHECK_IS_ON()
+class SequenceChecker : public SequenceCheckerImpl {
+};
+#else
+class SequenceChecker : public SequenceCheckerDoNothing {
+};
+#endif // DCHECK_IS_ON()
+
+class SCOPED_LOCKABLE ScopedValidateSequenceChecker {
+ public:
+ explicit ScopedValidateSequenceChecker(const SequenceChecker& checker)
+ EXCLUSIVE_LOCK_FUNCTION(checker) {
+ DCHECK(checker.CalledOnValidSequence());
+ }
+
+ explicit ScopedValidateSequenceChecker(const SequenceChecker& checker,
+ const StringPiece& msg)
+ EXCLUSIVE_LOCK_FUNCTION(checker) {
+ DCHECK(checker.CalledOnValidSequence()) << msg;
+ }
+
+ ~ScopedValidateSequenceChecker() UNLOCK_FUNCTION() {}
+
+ private:
+};
+
+} // namespace base
+
+#endif // BASE_SEQUENCE_CHECKER_H_
diff --git a/security/sandbox/chromium/base/sequence_checker_impl.h b/security/sandbox/chromium/base/sequence_checker_impl.h
new file mode 100644
index 0000000000..ea0fbb5bfa
--- /dev/null
+++ b/security/sandbox/chromium/base/sequence_checker_impl.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SEQUENCE_CHECKER_IMPL_H_
+#define BASE_SEQUENCE_CHECKER_IMPL_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+
+namespace base {
+
+// Real implementation of SequenceChecker for use in debug mode or for temporary
+// use in release mode (e.g. to CHECK on a threading issue seen only in the
+// wild).
+//
+// Note: You should almost always use the SequenceChecker class to get the right
+// version for your build configuration.
+// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
+// order to support thread_annotations.h.
+class LOCKABLE BASE_EXPORT SequenceCheckerImpl {
+ public:
+ SequenceCheckerImpl();
+ ~SequenceCheckerImpl();
+
+ // Allow move construct/assign. This must be called on |other|'s associated
+ // sequence and assignment can only be made into a SequenceCheckerImpl which
+ // is detached or already associated with the current sequence. This isn't
+ // thread-safe (|this| and |other| shouldn't be in use while this move is
+ // performed). If the assignment was legal, the resulting SequenceCheckerImpl
+ // will be bound to the current sequence and |other| will be detached.
+ SequenceCheckerImpl(SequenceCheckerImpl&& other);
+ SequenceCheckerImpl& operator=(SequenceCheckerImpl&& other);
+
+ // Returns true if called in sequence with previous calls to this method and
+ // the constructor.
+ bool CalledOnValidSequence() const WARN_UNUSED_RESULT;
+
+ // Unbinds the checker from the currently associated sequence. The checker
+ // will be re-bound on the next call to CalledOnValidSequence().
+ void DetachFromSequence();
+
+ private:
+ class Core;
+
+ // Calls straight to ThreadLocalStorage::HasBeenDestroyed(). Exposed purely
+ // for 'friend' to work.
+ static bool HasThreadLocalStorageBeenDestroyed();
+
+ mutable Lock lock_;
+ mutable std::unique_ptr<Core> core_ GUARDED_BY(lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(SequenceCheckerImpl);
+};
+
+} // namespace base
+
+#endif // BASE_SEQUENCE_CHECKER_IMPL_H_
diff --git a/security/sandbox/chromium/base/sequence_token.h b/security/sandbox/chromium/base/sequence_token.h
new file mode 100644
index 0000000000..6e7d191ae8
--- /dev/null
+++ b/security/sandbox/chromium/base/sequence_token.h
@@ -0,0 +1,115 @@
+// 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_SEQUENCE_TOKEN_H_
+#define BASE_SEQUENCE_TOKEN_H_
+
+#include "base/base_export.h"
+#include "base/macros.h"
+
+namespace base {
+
+// A token that identifies a series of sequenced tasks (i.e. tasks that run one
+// at a time in posting order).
+class BASE_EXPORT SequenceToken {
+ public:
+ // Instantiates an invalid SequenceToken.
+ SequenceToken() = default;
+
+ // Explicitly allow copy.
+ SequenceToken(const SequenceToken& other) = default;
+ SequenceToken& operator=(const SequenceToken& other) = default;
+
+ // An invalid SequenceToken is not equal to any other SequenceToken, including
+ // other invalid SequenceTokens.
+ bool operator==(const SequenceToken& other) const;
+ bool operator!=(const SequenceToken& other) const;
+
+ // Returns true if this is a valid SequenceToken.
+ bool IsValid() const;
+
+ // Returns the integer uniquely representing this SequenceToken. This method
+ // should only be used for tracing and debugging.
+ int ToInternalValue() const;
+
+ // Returns a valid SequenceToken which isn't equal to any previously returned
+ // SequenceToken.
+ static SequenceToken Create();
+
+ // Returns the SequenceToken associated with the task running on the current
+ // thread, as determined by the active ScopedSetSequenceTokenForCurrentThread
+ // if any.
+ static SequenceToken GetForCurrentThread();
+
+ private:
+ explicit SequenceToken(int token) : token_(token) {}
+
+ static constexpr int kInvalidSequenceToken = -1;
+ int token_ = kInvalidSequenceToken;
+};
+
+// A token that identifies a task.
+//
+// This is used by ThreadCheckerImpl to determine whether calls to
+// CalledOnValidThread() come from the same task and hence are deterministically
+// single-threaded (vs. calls coming from different sequenced or parallel tasks,
+// which may or may not run on the same thread).
+class BASE_EXPORT TaskToken {
+ public:
+ // Instantiates an invalid TaskToken.
+ TaskToken() = default;
+
+ // Explicitly allow copy.
+ TaskToken(const TaskToken& other) = default;
+ TaskToken& operator=(const TaskToken& other) = default;
+
+ // An invalid TaskToken is not equal to any other TaskToken, including
+ // other invalid TaskTokens.
+ bool operator==(const TaskToken& other) const;
+ bool operator!=(const TaskToken& other) const;
+
+ // Returns true if this is a valid TaskToken.
+ bool IsValid() const;
+
+ // In the scope of a ScopedSetSequenceTokenForCurrentThread, returns a valid
+ // TaskToken which isn't equal to any TaskToken returned in the scope of a
+ // different ScopedSetSequenceTokenForCurrentThread. Otherwise, returns an
+ // invalid TaskToken.
+ static TaskToken GetForCurrentThread();
+
+ private:
+ friend class ScopedSetSequenceTokenForCurrentThread;
+
+ explicit TaskToken(int token) : token_(token) {}
+
+ // Returns a valid TaskToken which isn't equal to any previously returned
+ // TaskToken. This is private as it only meant to be instantiated by
+ // ScopedSetSequenceTokenForCurrentThread.
+ static TaskToken Create();
+
+ static constexpr int kInvalidTaskToken = -1;
+ int token_ = kInvalidTaskToken;
+};
+
+// Instantiate this in the scope where a single task runs.
+class BASE_EXPORT ScopedSetSequenceTokenForCurrentThread {
+ public:
+ // Throughout the lifetime of the constructed object,
+ // SequenceToken::GetForCurrentThread() will return |sequence_token| and
+ // TaskToken::GetForCurrentThread() will return a TaskToken which is not equal
+ // to any TaskToken returned in the scope of another
+ // ScopedSetSequenceTokenForCurrentThread.
+ ScopedSetSequenceTokenForCurrentThread(const SequenceToken& sequence_token);
+ ~ScopedSetSequenceTokenForCurrentThread();
+
+ private:
+ const SequenceToken sequence_token_;
+ const TaskToken task_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSetSequenceTokenForCurrentThread);
+};
+
+} // namespace base
+
+#endif // BASE_SEQUENCE_TOKEN_H_
diff --git a/security/sandbox/chromium/base/sequenced_task_runner.h b/security/sandbox/chromium/base/sequenced_task_runner.h
new file mode 100644
index 0000000000..976f87ff2a
--- /dev/null
+++ b/security/sandbox/chromium/base/sequenced_task_runner.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SEQUENCED_TASK_RUNNER_H_
+#define BASE_SEQUENCED_TASK_RUNNER_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/sequenced_task_runner_helpers.h"
+#include "base/task_runner.h"
+
+namespace base {
+
+// A SequencedTaskRunner is a subclass of TaskRunner that provides
+// additional guarantees on the order that tasks are started, as well
+// as guarantees on when tasks are in sequence, i.e. one task finishes
+// before the other one starts.
+//
+// Summary
+// -------
+// Non-nested tasks with the same delay will run one by one in FIFO
+// order.
+//
+// Detailed guarantees
+// -------------------
+//
+// SequencedTaskRunner also adds additional methods for posting
+// non-nestable tasks. In general, an implementation of TaskRunner
+// may expose task-running methods which are themselves callable from
+// within tasks. A non-nestable task is one that is guaranteed to not
+// be run from within an already-running task. Conversely, a nestable
+// task (the default) is a task that can be run from within an
+// already-running task.
+//
+// The guarantees of SequencedTaskRunner are as follows:
+//
+// - Given two tasks T2 and T1, T2 will start after T1 starts if:
+//
+// * T2 is posted after T1; and
+// * T2 has equal or higher delay than T1; and
+// * T2 is non-nestable or T1 is nestable.
+//
+// - If T2 will start after T1 starts by the above guarantee, then
+// T2 will start after T1 finishes and is destroyed if:
+//
+// * T2 is non-nestable, or
+// * T1 doesn't call any task-running methods.
+//
+// - If T2 will start after T1 finishes by the above guarantee, then
+// all memory changes in T1 and T1's destruction will be visible
+// to T2.
+//
+// - If T2 runs nested within T1 via a call to the task-running
+// method M, then all memory changes in T1 up to the call to M
+// will be visible to T2, and all memory changes in T2 will be
+// visible to T1 from the return from M.
+//
+// Note that SequencedTaskRunner does not guarantee that tasks are run
+// on a single dedicated thread, although the above guarantees provide
+// most (but not all) of the same guarantees. If you do need to
+// guarantee that tasks are run on a single dedicated thread, see
+// SingleThreadTaskRunner (in single_thread_task_runner.h).
+//
+// Some corollaries to the above guarantees, assuming the tasks in
+// question don't call any task-running methods:
+//
+// - Tasks posted via PostTask are run in FIFO order.
+//
+// - Tasks posted via PostNonNestableTask are run in FIFO order.
+//
+// - Tasks posted with the same delay and the same nestable state
+// are run in FIFO order.
+//
+// - A list of tasks with the same nestable state posted in order of
+// non-decreasing delay is run in FIFO order.
+//
+// - A list of tasks posted in order of non-decreasing delay with at
+// most a single change in nestable state from nestable to
+// non-nestable is run in FIFO order. (This is equivalent to the
+// statement of the first guarantee above.)
+//
+// Some theoretical implementations of SequencedTaskRunner:
+//
+// - A SequencedTaskRunner that wraps a regular TaskRunner but makes
+// sure that only one task at a time is posted to the TaskRunner,
+// with appropriate memory barriers in between tasks.
+//
+// - A SequencedTaskRunner that, for each task, spawns a joinable
+// thread to run that task and immediately quit, and then
+// immediately joins that thread.
+//
+// - A SequencedTaskRunner that stores the list of posted tasks and
+// has a method Run() that runs each runnable task in FIFO order
+// that can be called from any thread, but only if another
+// (non-nested) Run() call isn't already happening.
+class BASE_EXPORT SequencedTaskRunner : public TaskRunner {
+ public:
+ // The two PostNonNestable*Task methods below are like their
+ // nestable equivalents in TaskRunner, but they guarantee that the
+ // posted task will not run nested within an already-running task.
+ //
+ // A simple corollary is that posting a task as non-nestable can
+ // only delay when the task gets run. That is, posting a task as
+ // non-nestable may not affect when the task gets run, or it could
+ // make it run later than it normally would, but it won't make it
+ // run earlier than it normally would.
+
+ // TODO(akalin): Get rid of the boolean return value for the methods
+ // below.
+
+ bool PostNonNestableTask(const Location& from_here, OnceClosure task);
+
+ virtual bool PostNonNestableDelayedTask(const Location& from_here,
+ OnceClosure task,
+ base::TimeDelta delay) = 0;
+
+ // Submits a non-nestable task to delete the given object. Returns
+ // true if the object may be deleted at some point in the future,
+ // and false if the object definitely will not be deleted.
+ template <class T>
+ bool DeleteSoon(const Location& from_here, const T* object) {
+ return DeleteOrReleaseSoonInternal(from_here, &DeleteHelper<T>::DoDelete,
+ object);
+ }
+
+ template <class T>
+ bool DeleteSoon(const Location& from_here, std::unique_ptr<T> object) {
+ return DeleteSoon(from_here, object.release());
+ }
+
+ // Submits a non-nestable task to release the given object.
+ //
+ // ReleaseSoon makes sure that the object it the scoped_refptr points to gets
+ // properly released on the correct thread.
+ // We apply ReleaseSoon to the rvalue as the side-effects can be unclear to
+ // the caller if an lvalue is used. That being so, the scoped_refptr should
+ // always be std::move'd.
+ // Example use:
+ //
+ // scoped_refptr<T> foo_scoped_refptr;
+ // ...
+ // task_runner->ReleaseSoon(std::move(foo_scoped_refptr));
+ template <class T>
+ void ReleaseSoon(const Location& from_here, scoped_refptr<T>&& object) {
+ if (!object)
+ return;
+
+ DeleteOrReleaseSoonInternal(from_here, &ReleaseHelper<T>::DoRelease,
+ object.release());
+ }
+
+ // Returns true iff tasks posted to this TaskRunner are sequenced
+ // with this call.
+ //
+ // In particular:
+ // - Returns true if this is a SequencedTaskRunner to which the
+ // current task was posted.
+ // - Returns true if this is a SequencedTaskRunner bound to the
+ // same sequence as the SequencedTaskRunner to which the current
+ // task was posted.
+ // - Returns true if this is a SingleThreadTaskRunner bound to
+ // the current thread.
+ virtual bool RunsTasksInCurrentSequence() const = 0;
+
+ protected:
+ ~SequencedTaskRunner() override = default;
+
+ private:
+ bool DeleteOrReleaseSoonInternal(const Location& from_here,
+ void (*deleter)(const void*),
+ const void* object);
+};
+
+// Sample usage with std::unique_ptr :
+// std::unique_ptr<Foo, base::OnTaskRunnerDeleter> ptr(
+// new Foo, base::OnTaskRunnerDeleter(my_task_runner));
+//
+// For RefCounted see base::RefCountedDeleteOnSequence.
+struct BASE_EXPORT OnTaskRunnerDeleter {
+ explicit OnTaskRunnerDeleter(scoped_refptr<SequencedTaskRunner> task_runner);
+ ~OnTaskRunnerDeleter();
+
+ OnTaskRunnerDeleter(OnTaskRunnerDeleter&&);
+ OnTaskRunnerDeleter& operator=(OnTaskRunnerDeleter&&);
+
+ // For compatibility with std:: deleters.
+ template <typename T>
+ void operator()(const T* ptr) {
+ if (ptr)
+ task_runner_->DeleteSoon(FROM_HERE, ptr);
+ }
+
+ scoped_refptr<SequencedTaskRunner> task_runner_;
+};
+
+} // namespace base
+
+#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
new file mode 100644
index 0000000000..18ec0e26f5
--- /dev/null
+++ b/security/sandbox/chromium/base/sequenced_task_runner_helpers.h
@@ -0,0 +1,42 @@
+// 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_SEQUENCED_TASK_RUNNER_HELPERS_H_
+#define BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
+
+namespace base {
+
+class SequencedTaskRunner;
+
+// Template helpers which use function indirection to erase T from the
+// function signature while still remembering it so we can call the
+// correct destructor/release function.
+//
+// We use this trick so we don't need to include bind.h in a header
+// file like sequenced_task_runner.h. We also wrap the helpers in a
+// templated class to make it easier for users of DeleteSoon to
+// declare the helper as a friend.
+template <class T>
+class DeleteHelper {
+ private:
+ static void DoDelete(const void* object) {
+ delete static_cast<const T*>(object);
+ }
+
+ friend class SequencedTaskRunner;
+};
+
+template <class T>
+class ReleaseHelper {
+ private:
+ static void DoRelease(const void* object) {
+ static_cast<const T*>(object)->Release();
+ }
+
+ friend class SequencedTaskRunner;
+};
+
+} // namespace base
+
+#endif // BASE_SEQUENCED_TASK_RUNNER_HELPERS_H_
diff --git a/security/sandbox/chromium/base/single_thread_task_runner.h b/security/sandbox/chromium/base/single_thread_task_runner.h
new file mode 100644
index 0000000000..4d6938ed6c
--- /dev/null
+++ b/security/sandbox/chromium/base/single_thread_task_runner.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SINGLE_THREAD_TASK_RUNNER_H_
+#define BASE_SINGLE_THREAD_TASK_RUNNER_H_
+
+#include "base/base_export.h"
+#include "base/sequenced_task_runner.h"
+
+namespace base {
+
+// A SingleThreadTaskRunner is a SequencedTaskRunner with one more
+// guarantee; namely, that all tasks are run on a single dedicated
+// thread. Most use cases require only a SequencedTaskRunner, unless
+// 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
+// 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
+// running other kinds of message loop, e.g. Jingle threads.
+class BASE_EXPORT SingleThreadTaskRunner : public SequencedTaskRunner {
+ public:
+ // A more explicit alias to RunsTasksInCurrentSequence().
+ bool BelongsToCurrentThread() const { return RunsTasksInCurrentSequence(); }
+
+ protected:
+ ~SingleThreadTaskRunner() override = default;
+};
+
+} // namespace base
+
+#endif // BASE_SINGLE_THREAD_TASK_RUNNER_H_
diff --git a/security/sandbox/chromium/base/stl_util.h b/security/sandbox/chromium/base/stl_util.h
new file mode 100644
index 0000000000..83d86ad90d
--- /dev/null
+++ b/security/sandbox/chromium/base/stl_util.h
@@ -0,0 +1,681 @@
+// 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.
+
+// Derived from google3/util/gtl/stl_util.h
+
+#ifndef BASE_STL_UTIL_H_
+#define BASE_STL_UTIL_H_
+
+#include <algorithm>
+#include <deque>
+#include <forward_list>
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/template_util.h"
+
+namespace base {
+
+namespace internal {
+
+// Calls erase on iterators of matching elements.
+template <typename Container, typename Predicate>
+void IterateAndEraseIf(Container& container, Predicate pred) {
+ for (auto it = container.begin(); it != container.end();) {
+ if (pred(*it))
+ it = container.erase(it);
+ else
+ ++it;
+ }
+}
+
+template <typename Iter>
+constexpr bool IsRandomAccessIter =
+ std::is_same<typename std::iterator_traits<Iter>::iterator_category,
+ std::random_access_iterator_tag>::value;
+
+// Utility type traits used for specializing base::Contains() below.
+template <typename Container, typename Element, typename = void>
+struct HasFindWithNpos : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasFindWithNpos<
+ Container,
+ Element,
+ void_t<decltype(std::declval<const Container&>().find(
+ std::declval<const Element&>()) != Container::npos)>>
+ : std::true_type {};
+
+template <typename Container, typename Element, typename = void>
+struct HasFindWithEnd : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasFindWithEnd<Container,
+ Element,
+ void_t<decltype(std::declval<const Container&>().find(
+ std::declval<const Element&>()) !=
+ std::declval<const Container&>().end())>>
+ : std::true_type {};
+
+template <typename Container, typename Element, typename = void>
+struct HasContains : std::false_type {};
+
+template <typename Container, typename Element>
+struct HasContains<Container,
+ Element,
+ void_t<decltype(std::declval<const Container&>().contains(
+ std::declval<const Element&>()))>> : std::true_type {};
+
+} // namespace internal
+
+// C++14 implementation of C++17's std::size():
+// http://en.cppreference.com/w/cpp/iterator/size
+template <typename Container>
+constexpr auto size(const Container& c) -> decltype(c.size()) {
+ return c.size();
+}
+
+template <typename T, size_t N>
+constexpr size_t size(const T (&array)[N]) noexcept {
+ return N;
+}
+
+// C++14 implementation of C++17's std::empty():
+// http://en.cppreference.com/w/cpp/iterator/empty
+template <typename Container>
+constexpr auto empty(const Container& c) -> decltype(c.empty()) {
+ return c.empty();
+}
+
+template <typename T, size_t N>
+constexpr bool empty(const T (&array)[N]) noexcept {
+ return false;
+}
+
+template <typename T>
+constexpr bool empty(std::initializer_list<T> il) noexcept {
+ return il.size() == 0;
+}
+
+// C++14 implementation of C++17's std::data():
+// http://en.cppreference.com/w/cpp/iterator/data
+template <typename Container>
+constexpr auto data(Container& c) -> decltype(c.data()) {
+ return c.data();
+}
+
+// std::basic_string::data() had no mutable overload prior to C++17 [1].
+// Hence this overload is provided.
+// Note: str[0] is safe even for empty strings, as they are guaranteed to be
+// null-terminated [2].
+//
+// [1] http://en.cppreference.com/w/cpp/string/basic_string/data
+// [2] http://en.cppreference.com/w/cpp/string/basic_string/operator_at
+template <typename CharT, typename Traits, typename Allocator>
+CharT* data(std::basic_string<CharT, Traits, Allocator>& str) {
+ return std::addressof(str[0]);
+}
+
+template <typename Container>
+constexpr auto data(const Container& c) -> decltype(c.data()) {
+ return c.data();
+}
+
+template <typename T, size_t N>
+constexpr T* data(T (&array)[N]) noexcept {
+ return array;
+}
+
+template <typename T>
+constexpr const T* data(std::initializer_list<T> il) noexcept {
+ return il.begin();
+}
+
+// std::array::data() was not constexpr prior to C++17 [1].
+// Hence these overloads are provided.
+//
+// [1] https://en.cppreference.com/w/cpp/container/array/data
+template <typename T, size_t N>
+constexpr T* data(std::array<T, N>& array) noexcept {
+ return !array.empty() ? &array[0] : nullptr;
+}
+
+template <typename T, size_t N>
+constexpr const T* data(const std::array<T, N>& array) noexcept {
+ return !array.empty() ? &array[0] : nullptr;
+}
+
+// C++14 implementation of C++17's std::as_const():
+// https://en.cppreference.com/w/cpp/utility/as_const
+template <typename T>
+constexpr std::add_const_t<T>& as_const(T& t) noexcept {
+ return t;
+}
+
+template <typename T>
+void as_const(const T&& t) = delete;
+
+// Returns a const reference to the underlying container of a container adapter.
+// Works for std::priority_queue, std::queue, and std::stack.
+template <class A>
+const typename A::container_type& GetUnderlyingContainer(const A& adapter) {
+ struct ExposedAdapter : A {
+ using A::c;
+ };
+ return adapter.*&ExposedAdapter::c;
+}
+
+// Clears internal memory of an STL object.
+// STL clear()/reserve(0) does not always free internal memory allocated
+// This function uses swap/destructor to ensure the internal memory is freed.
+template<class T>
+void STLClearObject(T* obj) {
+ T tmp;
+ tmp.swap(*obj);
+ // Sometimes "T tmp" allocates objects with memory (arena implementation?).
+ // Hence using additional reserve(0) even if it doesn't always work.
+ obj->reserve(0);
+}
+
+// Counts the number of instances of val in a container.
+template <typename Container, typename T>
+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);
+}
+
+// General purpose implementation to check if |container| contains |value|.
+template <typename Container,
+ typename Value,
+ std::enable_if_t<
+ !internal::HasFindWithNpos<Container, Value>::value &&
+ !internal::HasFindWithEnd<Container, Value>::value &&
+ !internal::HasContains<Container, Value>::value>* = nullptr>
+bool Contains(const Container& container, const Value& value) {
+ using std::begin;
+ using std::end;
+ return std::find(begin(container), end(container), value) != end(container);
+}
+
+// Specialized Contains() implementation for when |container| has a find()
+// member function and a static npos member, but no contains() member function.
+template <typename Container,
+ typename Value,
+ std::enable_if_t<internal::HasFindWithNpos<Container, Value>::value &&
+ !internal::HasContains<Container, Value>::value>* =
+ nullptr>
+bool Contains(const Container& container, const Value& value) {
+ return container.find(value) != Container::npos;
+}
+
+// Specialized Contains() implementation for when |container| has a find()
+// and end() member function, but no contains() member function.
+template <typename Container,
+ typename Value,
+ std::enable_if_t<internal::HasFindWithEnd<Container, Value>::value &&
+ !internal::HasContains<Container, Value>::value>* =
+ nullptr>
+bool Contains(const Container& container, const Value& value) {
+ return container.find(value) != container.end();
+}
+
+// Specialized Contains() implementation for when |container| has a contains()
+// member function.
+template <
+ typename Container,
+ typename Value,
+ std::enable_if_t<internal::HasContains<Container, Value>::value>* = nullptr>
+bool Contains(const Container& container, const Value& value) {
+ return container.contains(value);
+}
+
+// O(1) implementation of const casting an iterator for any sequence,
+// associative or unordered associative container in the STL.
+//
+// Reference: https://stackoverflow.com/a/10669041
+template <typename Container,
+ typename ConstIter,
+ std::enable_if_t<!internal::IsRandomAccessIter<ConstIter>>* = nullptr>
+constexpr auto ConstCastIterator(Container& c, ConstIter it) {
+ return c.erase(it, it);
+}
+
+// Explicit overload for std::forward_list where erase() is named erase_after().
+template <typename T, typename Allocator>
+constexpr auto ConstCastIterator(
+ std::forward_list<T, Allocator>& c,
+ typename std::forward_list<T, Allocator>::const_iterator it) {
+// The erase_after(it, it) trick used below does not work for libstdc++ [1],
+// thus we need a different way.
+// TODO(crbug.com/972541): Remove this workaround once libstdc++ is fixed on all
+// platforms.
+//
+// [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90857
+#if defined(__GLIBCXX__)
+ return c.insert_after(it, {});
+#else
+ return c.erase_after(it, it);
+#endif
+}
+
+// Specialized O(1) const casting for random access iterators. This is
+// necessary, because erase() is either not available (e.g. array-like
+// containers), or has O(n) complexity (e.g. std::deque or std::vector).
+template <typename Container,
+ typename ConstIter,
+ std::enable_if_t<internal::IsRandomAccessIter<ConstIter>>* = nullptr>
+constexpr auto ConstCastIterator(Container& c, ConstIter it) {
+ using std::begin;
+ using std::cbegin;
+ return begin(c) + (it - cbegin(c));
+}
+
+namespace internal {
+
+template <typename Map, typename Key, typename Value>
+std::pair<typename Map::iterator, bool> InsertOrAssignImpl(Map& map,
+ Key&& key,
+ Value&& value) {
+ auto lower = map.lower_bound(key);
+ if (lower != map.end() && !map.key_comp()(key, lower->first)) {
+ // key already exists, perform assignment.
+ lower->second = std::forward<Value>(value);
+ return {lower, false};
+ }
+
+ // key did not yet exist, insert it.
+ return {map.emplace_hint(lower, std::forward<Key>(key),
+ std::forward<Value>(value)),
+ true};
+}
+
+template <typename Map, typename Key, typename Value>
+typename Map::iterator InsertOrAssignImpl(Map& map,
+ typename Map::const_iterator hint,
+ Key&& key,
+ Value&& value) {
+ auto&& key_comp = map.key_comp();
+ if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
+ if (hint == map.end() || key_comp(key, hint->first)) {
+ // *(hint - 1) < key < *hint => key did not exist and hint is correct.
+ return map.emplace_hint(hint, std::forward<Key>(key),
+ std::forward<Value>(value));
+ }
+
+ if (!key_comp(hint->first, key)) {
+ // key == *hint => key already exists and hint is correct.
+ auto mutable_hint = ConstCastIterator(map, hint);
+ mutable_hint->second = std::forward<Value>(value);
+ return mutable_hint;
+ }
+ }
+
+ // hint was not helpful, dispatch to hintless version.
+ return InsertOrAssignImpl(map, std::forward<Key>(key),
+ std::forward<Value>(value))
+ .first;
+}
+
+template <typename Map, typename Key, typename... Args>
+std::pair<typename Map::iterator, bool> TryEmplaceImpl(Map& map,
+ Key&& key,
+ Args&&... args) {
+ auto lower = map.lower_bound(key);
+ if (lower != map.end() && !map.key_comp()(key, lower->first)) {
+ // key already exists, do nothing.
+ return {lower, false};
+ }
+
+ // key did not yet exist, insert it.
+ return {map.emplace_hint(lower, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<Key>(key)),
+ std::forward_as_tuple(std::forward<Args>(args)...)),
+ true};
+}
+
+template <typename Map, typename Key, typename... Args>
+typename Map::iterator TryEmplaceImpl(Map& map,
+ typename Map::const_iterator hint,
+ Key&& key,
+ Args&&... args) {
+ auto&& key_comp = map.key_comp();
+ if ((hint == map.begin() || key_comp(std::prev(hint)->first, key))) {
+ if (hint == map.end() || key_comp(key, hint->first)) {
+ // *(hint - 1) < key < *hint => key did not exist and hint is correct.
+ return map.emplace_hint(
+ hint, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<Key>(key)),
+ std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+
+ if (!key_comp(hint->first, key)) {
+ // key == *hint => no-op, return correct hint.
+ return ConstCastIterator(map, hint);
+ }
+ }
+
+ // hint was not helpful, dispatch to hintless version.
+ return TryEmplaceImpl(map, std::forward<Key>(key),
+ std::forward<Args>(args)...)
+ .first;
+}
+
+} // namespace internal
+
+// Implementation of C++17's std::map::insert_or_assign as a free function.
+template <typename Map, typename Value>
+std::pair<typename Map::iterator, bool>
+InsertOrAssign(Map& map, const typename Map::key_type& key, Value&& value) {
+ return internal::InsertOrAssignImpl(map, key, std::forward<Value>(value));
+}
+
+template <typename Map, typename Value>
+std::pair<typename Map::iterator, bool>
+InsertOrAssign(Map& map, typename Map::key_type&& key, Value&& value) {
+ return internal::InsertOrAssignImpl(map, std::move(key),
+ std::forward<Value>(value));
+}
+
+// Implementation of C++17's std::map::insert_or_assign with hint as a free
+// function.
+template <typename Map, typename Value>
+typename Map::iterator InsertOrAssign(Map& map,
+ typename Map::const_iterator hint,
+ const typename Map::key_type& key,
+ Value&& value) {
+ return internal::InsertOrAssignImpl(map, hint, key,
+ std::forward<Value>(value));
+}
+
+template <typename Map, typename Value>
+typename Map::iterator InsertOrAssign(Map& map,
+ typename Map::const_iterator hint,
+ typename Map::key_type&& key,
+ Value&& value) {
+ return internal::InsertOrAssignImpl(map, hint, std::move(key),
+ std::forward<Value>(value));
+}
+
+// Implementation of C++17's std::map::try_emplace as a free function.
+template <typename Map, typename... Args>
+std::pair<typename Map::iterator, bool>
+TryEmplace(Map& map, const typename Map::key_type& key, Args&&... args) {
+ return internal::TryEmplaceImpl(map, key, std::forward<Args>(args)...);
+}
+
+template <typename Map, typename... Args>
+std::pair<typename Map::iterator, bool> TryEmplace(Map& map,
+ typename Map::key_type&& key,
+ Args&&... args) {
+ return internal::TryEmplaceImpl(map, std::move(key),
+ std::forward<Args>(args)...);
+}
+
+// Implementation of C++17's std::map::try_emplace with hint as a free
+// function.
+template <typename Map, typename... Args>
+typename Map::iterator TryEmplace(Map& map,
+ typename Map::const_iterator hint,
+ const typename Map::key_type& key,
+ Args&&... args) {
+ return internal::TryEmplaceImpl(map, hint, key, std::forward<Args>(args)...);
+}
+
+template <typename Map, typename... Args>
+typename Map::iterator TryEmplace(Map& map,
+ typename Map::const_iterator hint,
+ typename Map::key_type&& key,
+ Args&&... args) {
+ return internal::TryEmplaceImpl(map, hint, std::move(key),
+ std::forward<Args>(args)...);
+}
+
+// Returns true if the container is sorted.
+template <typename Container>
+bool STLIsSorted(const Container& cont) {
+ return std::is_sorted(std::begin(cont), std::end(cont));
+}
+
+// Returns a new ResultType containing the difference of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType difference;
+ std::set_difference(a1.begin(), a1.end(),
+ a2.begin(), a2.end(),
+ std::inserter(difference, difference.end()));
+ return difference;
+}
+
+// Returns a new ResultType containing the union of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType result;
+ std::set_union(a1.begin(), a1.end(),
+ a2.begin(), a2.end(),
+ std::inserter(result, result.end()));
+ return result;
+}
+
+// Returns a new ResultType containing the intersection of two sorted
+// containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType result;
+ std::set_intersection(a1.begin(), a1.end(),
+ a2.begin(), a2.end(),
+ std::inserter(result, result.end()));
+ return result;
+}
+
+// Returns true if the sorted container |a1| contains all elements of the sorted
+// container |a2|.
+template <typename Arg1, typename Arg2>
+bool STLIncludes(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ return std::includes(a1.begin(), a1.end(),
+ a2.begin(), a2.end());
+}
+
+// Erase/EraseIf are based on library fundamentals ts v2 erase/erase_if
+// http://en.cppreference.com/w/cpp/experimental/lib_extensions_2
+// They provide a generic way to erase elements from a container.
+// The functions here implement these for the standard containers until those
+// functions are available in the C++ standard.
+// For Chromium containers overloads should be defined in their own headers
+// (like standard containers).
+// Note: there is no std::erase for standard associative containers so we don't
+// have it either.
+
+template <typename CharT, typename Traits, typename Allocator, typename Value>
+void Erase(std::basic_string<CharT, Traits, Allocator>& container,
+ const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <typename CharT, typename Traits, typename Allocator, class Predicate>
+void EraseIf(std::basic_string<CharT, Traits, Allocator>& container,
+ Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::deque<T, Allocator>& container, const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::deque<T, Allocator>& container, Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::vector<T, Allocator>& container, const Value& value) {
+ container.erase(std::remove(container.begin(), container.end(), value),
+ container.end());
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::vector<T, Allocator>& container, Predicate pred) {
+ container.erase(std::remove_if(container.begin(), container.end(), pred),
+ container.end());
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::forward_list<T, Allocator>& container, const Value& value) {
+ // Unlike std::forward_list::remove, this function template accepts
+ // heterogeneous types and does not force a conversion to the container's
+ // value type before invoking the == operator.
+ container.remove_if([&](const T& cur) { return cur == value; });
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::forward_list<T, Allocator>& container, Predicate pred) {
+ container.remove_if(pred);
+}
+
+template <class T, class Allocator, class Value>
+void Erase(std::list<T, Allocator>& container, const Value& value) {
+ // Unlike std::list::remove, this function template accepts heterogeneous
+ // types and does not force a conversion to the container's value type before
+ // invoking the == operator.
+ container.remove_if([&](const T& cur) { return cur == value; });
+}
+
+template <class T, class Allocator, class Predicate>
+void EraseIf(std::list<T, Allocator>& container, Predicate pred) {
+ container.remove_if(pred);
+}
+
+template <class Key, class T, class Compare, class Allocator, class Predicate>
+void EraseIf(std::map<Key, T, Compare, Allocator>& container, Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class T, class Compare, class Allocator, class Predicate>
+void EraseIf(std::multimap<Key, T, Compare, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class Compare, class Allocator, class Predicate>
+void EraseIf(std::set<Key, Compare, Allocator>& container, Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key, class Compare, class Allocator, class Predicate>
+void EraseIf(std::multiset<Key, Compare, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class T,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class T,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(
+ std::unordered_multimap<Key, T, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(std::unordered_set<Key, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+template <class Key,
+ class Hash,
+ class KeyEqual,
+ class Allocator,
+ class Predicate>
+void EraseIf(std::unordered_multiset<Key, Hash, KeyEqual, Allocator>& container,
+ Predicate pred) {
+ internal::IterateAndEraseIf(container, pred);
+}
+
+// A helper class to be used as the predicate with |EraseIf| to implement
+// in-place set intersection. Helps implement the algorithm of going through
+// each container an element at a time, erasing elements from the first
+// container if they aren't in the second container. Requires each container be
+// sorted. Note that the logic below appears inverted since it is returning
+// whether an element should be erased.
+template <class Collection>
+class IsNotIn {
+ public:
+ explicit IsNotIn(const Collection& collection)
+ : i_(collection.begin()), end_(collection.end()) {}
+
+ bool operator()(const typename Collection::value_type& x) {
+ while (i_ != end_ && *i_ < x)
+ ++i_;
+ if (i_ == end_)
+ return true;
+ if (*i_ == x) {
+ ++i_;
+ return false;
+ }
+ return true;
+ }
+
+ private:
+ typename Collection::const_iterator i_;
+ const typename Collection::const_iterator end_;
+};
+
+// Helper for returning the optional value's address, or nullptr.
+template <class T>
+T* OptionalOrNullptr(base::Optional<T>& optional) {
+ return optional.has_value() ? &optional.value() : nullptr;
+}
+
+template <class T>
+const T* OptionalOrNullptr(const base::Optional<T>& optional) {
+ return optional.has_value() ? &optional.value() : nullptr;
+}
+
+} // namespace base
+
+#endif // BASE_STL_UTIL_H_
diff --git a/security/sandbox/chromium/base/strings/char_traits.h b/security/sandbox/chromium/base/strings/char_traits.h
new file mode 100644
index 0000000000..b193e216cc
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/char_traits.h
@@ -0,0 +1,92 @@
+// Copyright 2018 The Chromium Authors. 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_STRINGS_CHAR_TRAITS_H_
+#define BASE_STRINGS_CHAR_TRAITS_H_
+
+#include <stddef.h>
+
+#include "base/compiler_specific.h"
+
+namespace base {
+
+// constexpr version of http://en.cppreference.com/w/cpp/string/char_traits.
+// This currently just implements the bits needed to support a (mostly)
+// constexpr StringPiece.
+//
+// TODO(dcheng): Once we switch to C++17, most methods will become constexpr and
+// we can switch over to using the one in the standard library.
+template <typename T>
+struct CharTraits {
+ // Performs a lexographical comparison of the first N characters of |s1| and
+ // |s2|. Returns 0 if equal, -1 if |s1| is less than |s2|, and 1 if |s1| is
+ // greater than |s2|.
+ static constexpr int compare(const T* s1, const T* s2, size_t n) noexcept;
+
+ // Returns the length of |s|, assuming null termination (and not including the
+ // terminating null).
+ static constexpr size_t length(const T* s) noexcept;
+};
+
+template <typename T>
+constexpr int CharTraits<T>::compare(const T* s1,
+ const T* s2,
+ size_t n) noexcept {
+ for (; n; --n, ++s1, ++s2) {
+ if (*s1 < *s2)
+ return -1;
+ if (*s1 > *s2)
+ return 1;
+ }
+ return 0;
+}
+
+template <typename T>
+constexpr size_t CharTraits<T>::length(const T* s) noexcept {
+ size_t i = 0;
+ for (; *s; ++s)
+ ++i;
+ return i;
+}
+
+// char specialization of CharTraits that can use clang's constexpr instrinsics,
+// where available.
+template <>
+struct CharTraits<char> {
+ static constexpr int compare(const char* s1,
+ const char* s2,
+ size_t n) noexcept;
+ static constexpr size_t length(const char* s) noexcept;
+};
+
+constexpr int CharTraits<char>::compare(const char* s1,
+ const char* s2,
+ size_t n) noexcept {
+#if HAS_FEATURE(cxx_constexpr_string_builtins)
+ return __builtin_memcmp(s1, s2, n);
+#else
+ for (; n; --n, ++s1, ++s2) {
+ if (*s1 < *s2)
+ return -1;
+ if (*s1 > *s2)
+ return 1;
+ }
+ return 0;
+#endif
+}
+
+constexpr size_t CharTraits<char>::length(const char* s) noexcept {
+#if defined(__clang__)
+ return __builtin_strlen(s);
+#else
+ size_t i = 0;
+ for (; *s; ++s)
+ ++i;
+ return i;
+#endif
+}
+
+} // namespace base
+
+#endif // BASE_STRINGS_CHAR_TRAITS_H_
diff --git a/security/sandbox/chromium/base/strings/nullable_string16.cc b/security/sandbox/chromium/base/strings/nullable_string16.cc
new file mode 100644
index 0000000000..076b282eb1
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/nullable_string16.cc
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/nullable_string16.h"
+
+#include <ostream>
+#include <utility>
+
+namespace base {
+NullableString16::NullableString16() = default;
+NullableString16::NullableString16(const NullableString16& other) = default;
+NullableString16::NullableString16(NullableString16&& other) = default;
+
+NullableString16::NullableString16(const string16& string, bool is_null) {
+ if (!is_null)
+ string_.emplace(string);
+}
+
+NullableString16::NullableString16(Optional<string16> optional_string16)
+ : string_(std::move(optional_string16)) {}
+
+NullableString16::~NullableString16() = default;
+NullableString16& NullableString16::operator=(const NullableString16& other) =
+ default;
+NullableString16& NullableString16::operator=(NullableString16&& other) =
+ default;
+
+std::ostream& operator<<(std::ostream& out, const NullableString16& value) {
+ return value.is_null() ? out << "(null)" : out << value.string();
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/nullable_string16.h b/security/sandbox/chromium/base/strings/nullable_string16.h
new file mode 100644
index 0000000000..abddee0f74
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/nullable_string16.h
@@ -0,0 +1,55 @@
+// 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 BASE_STRINGS_NULLABLE_STRING16_H_
+#define BASE_STRINGS_NULLABLE_STRING16_H_
+
+#include <iosfwd>
+
+#include "base/base_export.h"
+#include "base/optional.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+
+// This class is a simple wrapper for string16 which also contains a null
+// state. This should be used only where the difference between null and
+// empty is meaningful.
+class BASE_EXPORT NullableString16 {
+ public:
+ NullableString16();
+ NullableString16(const NullableString16& other);
+ NullableString16(NullableString16&& other);
+ NullableString16(const string16& string, bool is_null);
+ explicit NullableString16(Optional<string16> optional_string16);
+ ~NullableString16();
+
+ NullableString16& operator=(const NullableString16& other);
+ NullableString16& operator=(NullableString16&& other);
+
+ const string16& string() const {
+ return string_ ? *string_ : EmptyString16();
+ }
+ bool is_null() const { return !string_; }
+ const Optional<string16>& as_optional_string16() const { return string_; }
+
+ private:
+ Optional<string16> string_;
+};
+
+inline bool operator==(const NullableString16& a, const NullableString16& b) {
+ return a.as_optional_string16() == b.as_optional_string16();
+}
+
+inline bool operator!=(const NullableString16& a, const NullableString16& b) {
+ return !(a == b);
+}
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+ const NullableString16& value);
+
+} // 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
new file mode 100644
index 0000000000..89049abd79
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/safe_sprintf.cc
@@ -0,0 +1,682 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/safe_sprintf.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+
+#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.
+// As our contract promises that SafeSPrintf() can be called from any
+// restricted run-time context, it is not actually safe to call logging
+// functions from it; and we only ever do so for debug builds and hope for the
+// best. We should _never_ call any logging function other than RAW_CHECK(),
+// and we should _never_ include any logging code that is active in production
+// builds. Most notably, we should not include these logging functions in
+// unofficial release builds, even though those builds would otherwise have
+// DCHECKS() enabled.
+// In other words; please do not remove the #ifdef around this #include.
+// Instead, in production builds we opt for returning a degraded result,
+// whenever an error is encountered.
+// E.g. The broken function call
+// SafeSPrintf("errno = %d (%x)", errno, strerror(errno))
+// will print something like
+// errno = 13, (%x)
+// instead of
+// errno = 13 (Access denied)
+// In most of the anticipated use cases, that's probably the preferred
+// behavior.
+#include "base/logging.h"
+#define DEBUG_CHECK RAW_CHECK
+#else
+#define DEBUG_CHECK(x) do { if (x) { } } while (0)
+#endif
+
+namespace base {
+namespace strings {
+
+// The code in this file is extremely careful to be async-signal-safe.
+//
+// Most obviously, we avoid calling any code that could dynamically allocate
+// memory. Doing so would almost certainly result in bugs and dead-locks.
+// We also avoid calling any other STL functions that could have unintended
+// side-effects involving memory allocation or access to other shared
+// resources.
+//
+// But on top of that, we also avoid calling other library functions, as many
+// of them have the side-effect of calling getenv() (in order to deal with
+// localization) or accessing errno. The latter sounds benign, but there are
+// several execution contexts where it isn't even possible to safely read let
+// alone write errno.
+//
+// The stated design goal of the SafeSPrintf() function is that it can be
+// called from any context that can safely call C or C++ code (i.e. anything
+// that doesn't require assembly code).
+//
+// For a brief overview of some but not all of the issues with async-signal-
+// safety, refer to:
+// http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+
+namespace {
+const size_t kSSizeMaxConst = ((size_t)(ssize_t)-1) >> 1;
+
+const char kUpCaseHexDigits[] = "0123456789ABCDEF";
+const char kDownCaseHexDigits[] = "0123456789abcdef";
+}
+
+#if defined(NDEBUG)
+// We would like to define kSSizeMax as std::numeric_limits<ssize_t>::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 static_assert to
+// verify that this worked correctly.
+namespace {
+const size_t kSSizeMax = kSSizeMaxConst;
+}
+#else // defined(NDEBUG)
+// For efficiency, we really need kSSizeMax to be a constant. But for unit
+// tests, it should be adjustable. This allows us to verify edge cases without
+// having to fill the entire available address space. As a compromise, we make
+// kSSizeMax adjustable in debug builds, and then only compile that particular
+// part of the unit test in debug builds.
+namespace {
+static size_t kSSizeMax = kSSizeMaxConst;
+}
+
+namespace internal {
+void SetSafeSPrintfSSizeMaxForTest(size_t max) {
+ kSSizeMax = max;
+}
+
+size_t GetSafeSPrintfSSizeMaxForTest() {
+ return kSSizeMax;
+}
+}
+#endif // defined(NDEBUG)
+
+namespace {
+class Buffer {
+ public:
+ // |buffer| is caller-allocated storage that SafeSPrintf() writes to. It
+ // has |size| bytes of writable storage. It is the caller's responsibility
+ // to ensure that the buffer is at least one byte in size, so that it fits
+ // the trailing NUL that will be added by the destructor. The buffer also
+ // must be smaller or equal to kSSizeMax in size.
+ Buffer(char* buffer, size_t size)
+ : buffer_(buffer),
+ size_(size - 1), // Account for trailing NUL byte
+ count_(0) {
+// 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<size_t>(std::numeric_limits<ssize_t>::max()),
+ "kSSizeMaxConst should be the max value of an ssize_t");
+#endif
+ DEBUG_CHECK(size > 0);
+ DEBUG_CHECK(size <= kSSizeMax);
+ }
+
+ ~Buffer() {
+ // The code calling the constructor guaranteed that there was enough space
+ // to store a trailing NUL -- and in debug builds, we are actually
+ // verifying this with DEBUG_CHECK()s in the constructor. So, we can
+ // always unconditionally write the NUL byte in the destructor. We do not
+ // need to adjust the count_, as SafeSPrintf() copies snprintf() in not
+ // including the NUL byte in its return code.
+ *GetInsertionPoint() = '\000';
+ }
+
+ // Returns true, iff the buffer is filled all the way to |kSSizeMax-1|. The
+ // caller can now stop adding more data, as GetCount() has reached its
+ // maximum possible value.
+ inline bool OutOfAddressableSpace() const {
+ return count_ == static_cast<size_t>(kSSizeMax - 1);
+ }
+
+ // Returns the number of bytes that would have been emitted to |buffer_|
+ // if it was sized sufficiently large. This number can be larger than
+ // |size_|, if the caller provided an insufficiently large output buffer.
+ // But it will never be bigger than |kSSizeMax-1|.
+ inline ssize_t GetCount() const {
+ DEBUG_CHECK(count_ < kSSizeMax);
+ return static_cast<ssize_t>(count_);
+ }
+
+ // Emits one |ch| character into the |buffer_| and updates the |count_| of
+ // characters that are currently supposed to be in the buffer.
+ // Returns "false", iff the buffer was already full.
+ // N.B. |count_| increases even if no characters have been written. This is
+ // needed so that GetCount() can return the number of bytes that should
+ // have been allocated for the |buffer_|.
+ inline bool Out(char ch) {
+ if (size_ >= 1 && count_ < size_) {
+ buffer_[count_] = ch;
+ return IncrementCountByOne();
+ }
+ // |count_| still needs to be updated, even if the buffer has been
+ // filled completely. This allows SafeSPrintf() to return the number of
+ // bytes that should have been emitted.
+ IncrementCountByOne();
+ return false;
+ }
+
+ // Inserts |padding|-|len| bytes worth of padding into the |buffer_|.
+ // |count_| will also be incremented by the number of bytes that were meant
+ // to be emitted. The |pad| character is typically either a ' ' space
+ // or a '0' zero, but other non-NUL values are legal.
+ // Returns "false", iff the the |buffer_| filled up (i.e. |count_|
+ // overflowed |size_|) at any time during padding.
+ inline bool Pad(char pad, size_t padding, size_t len) {
+ DEBUG_CHECK(pad);
+ DEBUG_CHECK(padding <= kSSizeMax);
+ for (; padding > len; --padding) {
+ if (!Out(pad)) {
+ if (--padding) {
+ IncrementCount(padding-len);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // POSIX doesn't define any async-signal-safe function for converting
+ // an integer to ASCII. Define our own version.
+ //
+ // This also gives us the ability to make the function a little more
+ // powerful and have it deal with |padding|, with truncation, and with
+ // predicting the length of the untruncated output.
+ //
+ // IToASCII() converts an integer |i| to ASCII.
+ //
+ // Unlike similar functions in the standard C library, it never appends a
+ // NUL character. This is left for the caller to do.
+ //
+ // While the function signature takes a signed int64_t, the code decides at
+ // run-time whether to treat the argument as signed (int64_t) or as unsigned
+ // (uint64_t) based on the value of |sign|.
+ //
+ // It supports |base|s 2 through 16. Only a |base| of 10 is allowed to have
+ // a |sign|. Otherwise, |i| is treated as unsigned.
+ //
+ // For bases larger than 10, |upcase| decides whether lower-case or upper-
+ // case letters should be used to designate digits greater than 10.
+ //
+ // Padding can be done with either '0' zeros or ' ' spaces. Padding has to
+ // be positive and will always be applied to the left of the output.
+ //
+ // Prepends a |prefix| to the number (e.g. "0x"). This prefix goes to
+ // the left of |padding|, if |pad| is '0'; and to the right of |padding|
+ // if |pad| is ' '.
+ //
+ // Returns "false", if the |buffer_| overflowed at any time.
+ bool IToASCII(bool sign, bool upcase, int64_t i, int base,
+ char pad, size_t padding, const char* prefix);
+
+ private:
+ // Increments |count_| by |inc| unless this would cause |count_| to
+ // overflow |kSSizeMax-1|. Returns "false", iff an overflow was detected;
+ // it then clamps |count_| to |kSSizeMax-1|.
+ inline bool IncrementCount(size_t inc) {
+ // "inc" is either 1 or a "padding" value. Padding is clamped at
+ // run-time to at most kSSizeMax-1. So, we know that "inc" is always in
+ // the range 1..kSSizeMax-1.
+ // This allows us to compute "kSSizeMax - 1 - inc" without incurring any
+ // integer overflows.
+ DEBUG_CHECK(inc <= kSSizeMax - 1);
+ if (count_ > kSSizeMax - 1 - inc) {
+ count_ = kSSizeMax - 1;
+ return false;
+ }
+ count_ += inc;
+ return true;
+ }
+
+ // Convenience method for the common case of incrementing |count_| by one.
+ inline bool IncrementCountByOne() {
+ return IncrementCount(1);
+ }
+
+ // Return the current insertion point into the buffer. This is typically
+ // at |buffer_| + |count_|, but could be before that if truncation
+ // happened. It always points to one byte past the last byte that was
+ // successfully placed into the |buffer_|.
+ inline char* GetInsertionPoint() const {
+ size_t idx = count_;
+ if (idx > size_) {
+ idx = size_;
+ }
+ return buffer_ + idx;
+ }
+
+ // User-provided buffer that will receive the fully formatted output string.
+ char* buffer_;
+
+ // Number of bytes that are available in the buffer excluding the trailing
+ // NUL byte that will be added by the destructor.
+ const size_t size_;
+
+ // Number of bytes that would have been emitted to the buffer, if the buffer
+ // was sufficiently big. This number always excludes the trailing NUL byte
+ // and it is guaranteed to never grow bigger than kSSizeMax-1.
+ size_t count_;
+
+ DISALLOW_COPY_AND_ASSIGN(Buffer);
+};
+
+
+bool Buffer::IToASCII(bool sign, bool upcase, int64_t i, int base,
+ char pad, size_t padding, const char* prefix) {
+ // Sanity check for parameters. None of these should ever fail, but see
+ // above for the rationale why we can't call CHECK().
+ DEBUG_CHECK(base >= 2);
+ DEBUG_CHECK(base <= 16);
+ DEBUG_CHECK(!sign || base == 10);
+ DEBUG_CHECK(pad == '0' || pad == ' ');
+ DEBUG_CHECK(padding <= kSSizeMax);
+ DEBUG_CHECK(!(sign && prefix && *prefix));
+
+ // Handle negative numbers, if the caller indicated that |i| should be
+ // treated as a signed number; otherwise treat |i| as unsigned (even if the
+ // MSB is set!)
+ // Details are tricky, because of limited data-types, but equivalent pseudo-
+ // code would look like:
+ // if (sign && i < 0)
+ // prefix = "-";
+ // num = abs(i);
+ int minint = 0;
+ uint64_t num;
+ if (sign && i < 0) {
+ prefix = "-";
+
+ // Turn our number positive.
+ if (i == std::numeric_limits<int64_t>::min()) {
+ // The most negative integer needs special treatment.
+ minint = 1;
+ num = static_cast<uint64_t>(-(i + 1));
+ } else {
+ // "Normal" negative numbers are easy.
+ num = static_cast<uint64_t>(-i);
+ }
+ } else {
+ num = static_cast<uint64_t>(i);
+ }
+
+ // If padding with '0' zero, emit the prefix or '-' character now. Otherwise,
+ // make the prefix accessible in reverse order, so that we can later output
+ // it right between padding and the number.
+ // We cannot choose the easier approach of just reversing the number, as that
+ // fails in situations where we need to truncate numbers that have padding
+ // and/or prefixes.
+ const char* reverse_prefix = nullptr;
+ if (prefix && *prefix) {
+ if (pad == '0') {
+ while (*prefix) {
+ if (padding) {
+ --padding;
+ }
+ Out(*prefix++);
+ }
+ prefix = nullptr;
+ } else {
+ for (reverse_prefix = prefix; *reverse_prefix; ++reverse_prefix) {
+ }
+ }
+ } else
+ prefix = nullptr;
+ const size_t prefix_length = reverse_prefix - prefix;
+
+ // Loop until we have converted the entire number. Output at least one
+ // character (i.e. '0').
+ size_t start = count_;
+ size_t discarded = 0;
+ bool started = false;
+ do {
+ // Make sure there is still enough space left in our output buffer.
+ if (count_ >= size_) {
+ if (start < size_) {
+ // It is rare that we need to output a partial number. But if asked
+ // to do so, we will still make sure we output the correct number of
+ // leading digits.
+ // Since we are generating the digits in reverse order, we actually
+ // have to discard digits in the order that we have already emitted
+ // them. This is essentially equivalent to:
+ // memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1)
+ for (char* move = buffer_ + start, *end = buffer_ + size_ - 1;
+ move < end;
+ ++move) {
+ *move = move[1];
+ }
+ ++discarded;
+ --count_;
+ } else if (count_ - size_ > 1) {
+ // Need to increment either |count_| or |discarded| to make progress.
+ // The latter is more efficient, as it eventually triggers fast
+ // handling of padding. But we have to ensure we don't accidentally
+ // change the overall state (i.e. switch the state-machine from
+ // discarding to non-discarding). |count_| needs to always stay
+ // bigger than |size_|.
+ --count_;
+ ++discarded;
+ }
+ }
+
+ // Output the next digit and (if necessary) compensate for the most
+ // negative integer needing special treatment. This works because,
+ // no matter the bit width of the integer, the lowest-most decimal
+ // integer always ends in 2, 4, 6, or 8.
+ if (!num && started) {
+ if (reverse_prefix > prefix) {
+ Out(*--reverse_prefix);
+ } else {
+ Out(pad);
+ }
+ } else {
+ started = true;
+ Out((upcase ? kUpCaseHexDigits : kDownCaseHexDigits)[num%base + minint]);
+ }
+
+ minint = 0;
+ num /= base;
+
+ // Add padding, if requested.
+ if (padding > 0) {
+ --padding;
+
+ // Performance optimization for when we are asked to output excessive
+ // padding, but our output buffer is limited in size. Even if we output
+ // a 64bit number in binary, we would never write more than 64 plus
+ // prefix non-padding characters. So, once this limit has been passed,
+ // any further state change can be computed arithmetically; we know that
+ // by this time, our entire final output consists of padding characters
+ // that have all already been output.
+ if (discarded > 8*sizeof(num) + prefix_length) {
+ IncrementCount(padding);
+ padding = 0;
+ }
+ }
+ } while (num || padding || (reverse_prefix > prefix));
+
+ // Conversion to ASCII actually resulted in the digits being in reverse
+ // order. We can't easily generate them in forward order, as we can't tell
+ // the number of characters needed until we are done converting.
+ // So, now, we reverse the string (except for the possible '-' sign).
+ char* front = buffer_ + start;
+ char* back = GetInsertionPoint();
+ while (--back > front) {
+ char ch = *back;
+ *back = *front;
+ *front++ = ch;
+ }
+
+ IncrementCount(discarded);
+ return !discarded;
+}
+
+} // anonymous namespace
+
+namespace internal {
+
+ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt, const Arg* args,
+ const size_t max_args) {
+ // Make sure that at least one NUL byte can be written, and that the buffer
+ // never overflows kSSizeMax. Not only does that use up most or all of the
+ // address space, it also would result in a return code that cannot be
+ // represented.
+ if (static_cast<ssize_t>(sz) < 1)
+ return -1;
+ sz = std::min(sz, kSSizeMax);
+
+ // Iterate over format string and interpret '%' arguments as they are
+ // encountered.
+ Buffer buffer(buf, sz);
+ size_t padding;
+ char pad;
+ for (unsigned int cur_arg = 0; *fmt && !buffer.OutOfAddressableSpace(); ) {
+ if (*fmt++ == '%') {
+ padding = 0;
+ pad = ' ';
+ char ch = *fmt++;
+ format_character_found:
+ switch (ch) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ // Found a width parameter. Convert to an integer value and store in
+ // "padding". If the leading digit is a zero, change the padding
+ // character from a space ' ' to a zero '0'.
+ pad = ch == '0' ? '0' : ' ';
+ for (;;) {
+ // The maximum allowed padding fills all the available address
+ // space and leaves just enough space to insert the trailing NUL.
+ const size_t max_padding = kSSizeMax - 1;
+ if (padding > max_padding/10 ||
+ 10*padding > max_padding - (ch - '0')) {
+ DEBUG_CHECK(padding <= max_padding/10 &&
+ 10*padding <= max_padding - (ch - '0'));
+ // Integer overflow detected. Skip the rest of the width until
+ // we find the format character, then do the normal error handling.
+ padding_overflow:
+ padding = max_padding;
+ while ((ch = *fmt++) >= '0' && ch <= '9') {
+ }
+ if (cur_arg < max_args) {
+ ++cur_arg;
+ }
+ goto fail_to_expand;
+ }
+ padding = 10*padding + ch - '0';
+ if (padding > max_padding) {
+ // This doesn't happen for "sane" values of kSSizeMax. But once
+ // kSSizeMax gets smaller than about 10, our earlier range checks
+ // are incomplete. Unittests do trigger this artificial corner
+ // case.
+ DEBUG_CHECK(padding <= max_padding);
+ goto padding_overflow;
+ }
+ ch = *fmt++;
+ if (ch < '0' || ch > '9') {
+ // Reached the end of the width parameter. This is where the format
+ // character is found.
+ goto format_character_found;
+ }
+ }
+ break;
+ case 'c': { // Output an ASCII character.
+ // Check that there are arguments left to be inserted.
+ if (cur_arg >= max_args) {
+ DEBUG_CHECK(cur_arg < max_args);
+ goto fail_to_expand;
+ }
+
+ // Check that the argument has the expected type.
+ const Arg& arg = args[cur_arg++];
+ if (arg.type != Arg::INT && arg.type != Arg::UINT) {
+ DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT);
+ goto fail_to_expand;
+ }
+
+ // Apply padding, if needed.
+ buffer.Pad(' ', padding, 1);
+
+ // Convert the argument to an ASCII character and output it.
+ char as_char = static_cast<char>(arg.integer.i);
+ if (!as_char) {
+ goto end_of_output_buffer;
+ }
+ buffer.Out(as_char);
+ break; }
+ case 'd': // Output a possibly signed decimal value.
+ case 'o': // Output an unsigned octal value.
+ case 'x': // Output an unsigned hexadecimal value.
+ case 'X':
+ case 'p': { // Output a pointer value.
+ // Check that there are arguments left to be inserted.
+ if (cur_arg >= max_args) {
+ DEBUG_CHECK(cur_arg < max_args);
+ goto fail_to_expand;
+ }
+
+ const Arg& arg = args[cur_arg++];
+ int64_t i;
+ const char* prefix = nullptr;
+ if (ch != 'p') {
+ // Check that the argument has the expected type.
+ if (arg.type != Arg::INT && arg.type != Arg::UINT) {
+ DEBUG_CHECK(arg.type == Arg::INT || arg.type == Arg::UINT);
+ goto fail_to_expand;
+ }
+ i = arg.integer.i;
+
+ if (ch != 'd') {
+ // The Arg() constructor automatically performed sign expansion on
+ // signed parameters. This is great when outputting a %d decimal
+ // number, but can result in unexpected leading 0xFF bytes when
+ // outputting a %x hexadecimal number. Mask bits, if necessary.
+ // We have to do this here, instead of in the Arg() constructor, as
+ // the Arg() constructor cannot tell whether we will output a %d
+ // or a %x. Only the latter should experience masking.
+ if (arg.integer.width < sizeof(int64_t)) {
+ i &= (1LL << (8*arg.integer.width)) - 1;
+ }
+ }
+ } else {
+ // Pointer values require an actual pointer or a string.
+ if (arg.type == Arg::POINTER) {
+ i = reinterpret_cast<uintptr_t>(arg.ptr);
+ } else if (arg.type == Arg::STRING) {
+ i = reinterpret_cast<uintptr_t>(arg.str);
+ } else if (arg.type == Arg::INT &&
+ arg.integer.width == sizeof(NULL) &&
+ arg.integer.i == 0) { // Allow C++'s version of NULL
+ i = 0;
+ } else {
+ DEBUG_CHECK(arg.type == Arg::POINTER || arg.type == Arg::STRING);
+ goto fail_to_expand;
+ }
+
+ // Pointers always include the "0x" prefix.
+ prefix = "0x";
+ }
+
+ // Use IToASCII() to convert to ASCII representation. For decimal
+ // numbers, optionally print a sign. For hexadecimal numbers,
+ // distinguish between upper and lower case. %p addresses are always
+ // printed as upcase. Supports base 8, 10, and 16. Prints padding
+ // and/or prefixes, if so requested.
+ buffer.IToASCII(ch == 'd' && arg.type == Arg::INT,
+ ch != 'x', i,
+ ch == 'o' ? 8 : ch == 'd' ? 10 : 16,
+ pad, padding, prefix);
+ break; }
+ case 's': {
+ // Check that there are arguments left to be inserted.
+ if (cur_arg >= max_args) {
+ DEBUG_CHECK(cur_arg < max_args);
+ goto fail_to_expand;
+ }
+
+ // Check that the argument has the expected type.
+ const Arg& arg = args[cur_arg++];
+ const char *s;
+ if (arg.type == Arg::STRING) {
+ s = arg.str ? arg.str : "<NULL>";
+ } else if (arg.type == Arg::INT && arg.integer.width == sizeof(NULL) &&
+ arg.integer.i == 0) { // Allow C++'s version of NULL
+ s = "<NULL>";
+ } else {
+ DEBUG_CHECK(arg.type == Arg::STRING);
+ goto fail_to_expand;
+ }
+
+ // Apply padding, if needed. This requires us to first check the
+ // length of the string that we are outputting.
+ if (padding) {
+ size_t len = 0;
+ for (const char* src = s; *src++; ) {
+ ++len;
+ }
+ buffer.Pad(' ', padding, len);
+ }
+
+ // Printing a string involves nothing more than copying it into the
+ // output buffer and making sure we don't output more bytes than
+ // available space; Out() takes care of doing that.
+ for (const char* src = s; *src; ) {
+ buffer.Out(*src++);
+ }
+ break; }
+ case '%':
+ // Quoted percent '%' character.
+ goto copy_verbatim;
+ fail_to_expand:
+ // C++ gives us tools to do type checking -- something that snprintf()
+ // could never really do. So, whenever we see arguments that don't
+ // match up with the format string, we refuse to output them. But
+ // since we have to be extremely conservative about being async-
+ // signal-safe, we are limited in the type of error handling that we
+ // can do in production builds (in debug builds we can use
+ // DEBUG_CHECK() and hope for the best). So, all we do is pass the
+ // format string unchanged. That should eventually get the user's
+ // attention; and in the meantime, it hopefully doesn't lose too much
+ // data.
+ default:
+ // Unknown or unsupported format character. Just copy verbatim to
+ // output.
+ buffer.Out('%');
+ DEBUG_CHECK(ch);
+ if (!ch) {
+ goto end_of_format_string;
+ }
+ buffer.Out(ch);
+ break;
+ }
+ } else {
+ copy_verbatim:
+ buffer.Out(fmt[-1]);
+ }
+ }
+ end_of_format_string:
+ end_of_output_buffer:
+ return buffer.GetCount();
+}
+
+} // namespace internal
+
+ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt) {
+ // Make sure that at least one NUL byte can be written, and that the buffer
+ // never overflows kSSizeMax. Not only does that use up most or all of the
+ // address space, it also would result in a return code that cannot be
+ // represented.
+ if (static_cast<ssize_t>(sz) < 1)
+ return -1;
+ sz = std::min(sz, kSSizeMax);
+
+ Buffer buffer(buf, sz);
+
+ // In the slow-path, we deal with errors by copying the contents of
+ // "fmt" unexpanded. This means, if there are no arguments passed, the
+ // SafeSPrintf() function always degenerates to a version of strncpy() that
+ // de-duplicates '%' characters.
+ const char* src = fmt;
+ for (; *src; ++src) {
+ buffer.Out(*src);
+ DEBUG_CHECK(src[0] != '%' || src[1] == '%');
+ if (src[0] == '%' && src[1] == '%') {
+ ++src;
+ }
+ }
+ return buffer.GetCount();
+}
+
+} // namespace strings
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/safe_sprintf.h b/security/sandbox/chromium/base/strings/safe_sprintf.h
new file mode 100644
index 0000000000..01d649d07a
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/safe_sprintf.h
@@ -0,0 +1,246 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_SAFE_SPRINTF_H_
+#define BASE_STRINGS_SAFE_SPRINTF_H_
+
+#include "build/build_config.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+// For ssize_t
+#include <unistd.h>
+#endif
+
+#include "base/base_export.h"
+
+namespace base {
+namespace strings {
+
+#if defined(COMPILER_MSVC)
+// Define ssize_t inside of our namespace.
+#if defined(_WIN64)
+typedef __int64 ssize_t;
+#else
+typedef long ssize_t;
+#endif
+#endif
+
+// SafeSPrintf() is a type-safe and completely self-contained version of
+// snprintf().
+//
+// SafeSNPrintf() is an alternative function signature that can be used when
+// not dealing with fixed-sized buffers. When possible, SafeSPrintf() should
+// always be used instead of SafeSNPrintf()
+//
+// These functions allow for formatting complicated messages from contexts that
+// require strict async-signal-safety. In fact, it is safe to call them from
+// any low-level execution context, as they are guaranteed to make no library
+// or system calls. It deliberately never touches "errno", either.
+//
+// The only exception to this rule is that in debug builds the code calls
+// RAW_CHECK() to help diagnose problems when the format string does not
+// match the rest of the arguments. In release builds, no CHECK()s are used,
+// and SafeSPrintf() instead returns an output string that expands only
+// those arguments that match their format characters. Mismatched arguments
+// are ignored.
+//
+// The code currently only supports a subset of format characters:
+// %c, %o, %d, %x, %X, %p, and %s.
+//
+// SafeSPrintf() aims to be as liberal as reasonably possible. Integer-like
+// values of arbitrary width can be passed to all of the format characters
+// that expect integers. Thus, it is explicitly legal to pass an "int" to
+// "%c", and output will automatically look at the LSB only. It is also
+// explicitly legal to pass either signed or unsigned values, and the format
+// characters will automatically interpret the arguments accordingly.
+//
+// It is still not legal to mix-and-match integer-like values with pointer
+// values. For instance, you cannot pass a pointer to %x, nor can you pass an
+// integer to %p.
+//
+// The one exception is "0" zero being accepted by "%p". This works-around
+// the problem of C++ defining NULL as an integer-like value.
+//
+// All format characters take an optional width parameter. This must be a
+// positive integer. For %d, %o, %x, %X and %p, if the width starts with
+// a leading '0', padding is done with '0' instead of ' ' characters.
+//
+// There are a few features of snprintf()-style format strings, that
+// SafeSPrintf() does not support at this time.
+//
+// If an actual user showed up, there is no particularly strong reason they
+// couldn't be added. But that assumes that the trade-offs between complexity
+// and utility are favorable.
+//
+// For example, adding support for negative padding widths, and for %n are all
+// likely to be viewed positively. They are all clearly useful, low-risk, easy
+// to test, don't jeopardize the async-signal-safety of the code, and overall
+// have little impact on other parts of SafeSPrintf() function.
+//
+// On the other hands, adding support for alternate forms, positional
+// arguments, grouping, wide characters, localization or floating point numbers
+// are all unlikely to ever be added.
+//
+// SafeSPrintf() and SafeSNPrintf() mimic the behavior of snprintf() and they
+// return the number of bytes needed to store the untruncated output. This
+// does *not* include the terminating NUL byte.
+//
+// They return -1, iff a fatal error happened. This typically can only happen,
+// if the buffer size is a) negative, or b) zero (i.e. not even the NUL byte
+// can be written). The return value can never be larger than SSIZE_MAX-1.
+// This ensures that the caller can always add one to the signed return code
+// in order to determine the amount of storage that needs to be allocated.
+//
+// While the code supports type checking and while it is generally very careful
+// to avoid printing incorrect values, it tends to be conservative in printing
+// as much as possible, even when given incorrect parameters. Typically, in
+// case of an error, the format string will not be expanded. (i.e. something
+// like SafeSPrintf(buf, "%p %d", 1, 2) results in "%p 2"). See above for
+// the use of RAW_CHECK() in debug builds, though.
+//
+// Basic example:
+// char buf[20];
+// base::strings::SafeSPrintf(buf, "The answer: %2d", 42);
+//
+// Example with dynamically sized buffer (async-signal-safe). This code won't
+// work on Visual studio, as it requires dynamically allocating arrays on the
+// stack. Consider picking a smaller value for |kMaxSize| if stack size is
+// limited and known. On the other hand, if the parameters to SafeSNPrintf()
+// are trusted and not controllable by the user, you can consider eliminating
+// the check for |kMaxSize| altogether. The current value of SSIZE_MAX is
+// essentially a no-op that just illustrates how to implement an upper bound:
+// const size_t kInitialSize = 128;
+// const size_t kMaxSize = std::numeric_limits<ssize_t>::max();
+// size_t size = kInitialSize;
+// for (;;) {
+// char buf[size];
+// size = SafeSNPrintf(buf, size, "Error message \"%s\"\n", err) + 1;
+// if (sizeof(buf) < kMaxSize && size > kMaxSize) {
+// size = kMaxSize;
+// continue;
+// } else if (size > sizeof(buf))
+// continue;
+// write(2, buf, size-1);
+// break;
+// }
+
+namespace internal {
+// Helpers that use C++ overloading, templates, and specializations to deduce
+// and record type information from function arguments. This allows us to
+// later write a type-safe version of snprintf().
+
+struct Arg {
+ enum Type { INT, UINT, STRING, POINTER };
+
+ // Any integer-like value.
+ Arg(signed char c) : type(INT) {
+ integer.i = c;
+ integer.width = sizeof(char);
+ }
+ Arg(unsigned char c) : type(UINT) {
+ integer.i = c;
+ integer.width = sizeof(char);
+ }
+ Arg(signed short j) : type(INT) {
+ integer.i = j;
+ integer.width = sizeof(short);
+ }
+ Arg(unsigned short j) : type(UINT) {
+ integer.i = j;
+ integer.width = sizeof(short);
+ }
+ Arg(signed int j) : type(INT) {
+ integer.i = j;
+ integer.width = sizeof(int);
+ }
+ Arg(unsigned int j) : type(UINT) {
+ integer.i = j;
+ integer.width = sizeof(int);
+ }
+ Arg(signed long j) : type(INT) {
+ integer.i = j;
+ integer.width = sizeof(long);
+ }
+ Arg(unsigned long j) : type(UINT) {
+ integer.i = j;
+ integer.width = sizeof(long);
+ }
+ Arg(signed long long j) : type(INT) {
+ integer.i = j;
+ integer.width = sizeof(long long);
+ }
+ Arg(unsigned long long j) : type(UINT) {
+ integer.i = j;
+ integer.width = sizeof(long long);
+ }
+
+ // A C-style text string.
+ Arg(const char* s) : str(s), type(STRING) { }
+ Arg(char* s) : str(s), type(STRING) { }
+
+ // Any pointer value that can be cast to a "void*".
+ template<class T> Arg(T* p) : ptr((void*)p), type(POINTER) { }
+
+ union {
+ // An integer-like value.
+ struct {
+ int64_t i;
+ unsigned char width;
+ } integer;
+
+ // A C-style text string.
+ const char* str;
+
+ // A pointer to an arbitrary object.
+ const void* ptr;
+ };
+ const enum Type type;
+};
+
+// This is the internal function that performs the actual formatting of
+// an snprintf()-style format string.
+BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt,
+ const Arg* args, size_t max_args);
+
+#if !defined(NDEBUG)
+// In debug builds, allow unit tests to artificially lower the kSSizeMax
+// constant that is used as a hard upper-bound for all buffers. In normal
+// use, this constant should always be std::numeric_limits<ssize_t>::max().
+BASE_EXPORT void SetSafeSPrintfSSizeMaxForTest(size_t max);
+BASE_EXPORT size_t GetSafeSPrintfSSizeMaxForTest();
+#endif
+
+} // namespace internal
+
+template<typename... Args>
+ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args... args) {
+ // Use Arg() object to record type information and then copy arguments to an
+ // array to make it easier to iterate over them.
+ const internal::Arg arg_array[] = { args... };
+ return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args));
+}
+
+template<size_t N, typename... Args>
+ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, Args... args) {
+ // Use Arg() object to record type information and then copy arguments to an
+ // array to make it easier to iterate over them.
+ const internal::Arg arg_array[] = { args... };
+ return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args));
+}
+
+// Fast-path when we don't actually need to substitute any arguments.
+BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt);
+template<size_t N>
+inline ssize_t SafeSPrintf(char (&buf)[N], const char* fmt) {
+ return SafeSNPrintf(buf, N, fmt);
+}
+
+} // namespace strings
+} // namespace base
+
+#endif // BASE_STRINGS_SAFE_SPRINTF_H_
diff --git a/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc b/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc
new file mode 100644
index 0000000000..bb9908f928
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/safe_sprintf_unittest.cc
@@ -0,0 +1,765 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/safe_sprintf.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <limits>
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.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
+// tests, as they just make it hard to spot real problems.
+// TODO(markus): See if the restrictions on Android can eventually be lifted.
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(OS_ANDROID)
+#define ALLOW_DEATH_TEST
+#endif
+
+namespace base {
+namespace strings {
+
+TEST(SafeSPrintfTest, Empty) {
+ char buf[2] = { 'X', 'X' };
+
+ // Negative buffer size should always result in an error.
+ EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast<size_t>(-1), ""));
+ EXPECT_EQ('X', buf[0]);
+ EXPECT_EQ('X', buf[1]);
+
+ // Zero buffer size should always result in an error.
+ EXPECT_EQ(-1, SafeSNPrintf(buf, 0, ""));
+ EXPECT_EQ('X', buf[0]);
+ EXPECT_EQ('X', buf[1]);
+
+ // A one-byte buffer should always print a single NUL byte.
+ EXPECT_EQ(0, SafeSNPrintf(buf, 1, ""));
+ EXPECT_EQ(0, buf[0]);
+ EXPECT_EQ('X', buf[1]);
+ buf[0] = 'X';
+
+ // A larger buffer should leave the trailing bytes unchanged.
+ EXPECT_EQ(0, SafeSNPrintf(buf, 2, ""));
+ EXPECT_EQ(0, buf[0]);
+ EXPECT_EQ('X', buf[1]);
+ buf[0] = 'X';
+
+ // The same test using SafeSPrintf() instead of SafeSNPrintf().
+ EXPECT_EQ(0, SafeSPrintf(buf, ""));
+ EXPECT_EQ(0, buf[0]);
+ EXPECT_EQ('X', buf[1]);
+ buf[0] = 'X';
+}
+
+TEST(SafeSPrintfTest, NoArguments) {
+ // Output a text message that doesn't require any substitutions. This
+ // is roughly equivalent to calling strncpy() (but unlike strncpy(), it does
+ // always add a trailing NUL; it always deduplicates '%' characters).
+ static const char text[] = "hello world";
+ char ref[20], buf[20];
+ memset(ref, 'X', sizeof(ref));
+ memcpy(buf, ref, sizeof(buf));
+
+ // A negative buffer size should always result in an error.
+ EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast<size_t>(-1), text));
+ EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+ // Zero buffer size should always result in an error.
+ EXPECT_EQ(-1, SafeSNPrintf(buf, 0, text));
+ EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+ // A one-byte buffer should always print a single NUL byte.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSNPrintf(buf, 1, text));
+ EXPECT_EQ(0, buf[0]);
+ EXPECT_TRUE(!memcmp(buf+1, ref+1, sizeof(buf)-1));
+ memcpy(buf, ref, sizeof(buf));
+
+ // A larger (but limited) buffer should always leave the trailing bytes
+ // unchanged.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSNPrintf(buf, 2, text));
+ EXPECT_EQ(text[0], buf[0]);
+ EXPECT_EQ(0, buf[1]);
+ EXPECT_TRUE(!memcmp(buf+2, ref+2, sizeof(buf)-2));
+ memcpy(buf, ref, sizeof(buf));
+
+ // A unrestricted buffer length should always leave the trailing bytes
+ // unchanged.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+ SafeSNPrintf(buf, sizeof(buf), text));
+ EXPECT_EQ(std::string(text), std::string(buf));
+ EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+ sizeof(buf) - sizeof(text)));
+ memcpy(buf, ref, sizeof(buf));
+
+ // The same test using SafeSPrintf() instead of SafeSNPrintf().
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSPrintf(buf, text));
+ EXPECT_EQ(std::string(text), std::string(buf));
+ EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+ sizeof(buf) - sizeof(text)));
+ memcpy(buf, ref, sizeof(buf));
+
+ // Check for deduplication of '%' percent characters.
+ EXPECT_EQ(1, SafeSPrintf(buf, "%%"));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%%%"));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%X"));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%%%%X"));
+#if defined(NDEBUG)
+ EXPECT_EQ(1, SafeSPrintf(buf, "%"));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%%"));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%X"));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%%%X"));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, "%"), "src.1. == '%'");
+ EXPECT_DEATH(SafeSPrintf(buf, "%%%"), "src.1. == '%'");
+ EXPECT_DEATH(SafeSPrintf(buf, "%X"), "src.1. == '%'");
+ EXPECT_DEATH(SafeSPrintf(buf, "%%%X"), "src.1. == '%'");
+#endif
+}
+
+TEST(SafeSPrintfTest, OneArgument) {
+ // Test basic single-argument single-character substitution.
+ const char text[] = "hello world";
+ const char fmt[] = "hello%cworld";
+ char ref[20], buf[20];
+ memset(ref, 'X', sizeof(buf));
+ memcpy(buf, ref, sizeof(buf));
+
+ // A negative buffer size should always result in an error.
+ EXPECT_EQ(-1, SafeSNPrintf(buf, static_cast<size_t>(-1), fmt, ' '));
+ EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+ // Zero buffer size should always result in an error.
+ EXPECT_EQ(-1, SafeSNPrintf(buf, 0, fmt, ' '));
+ EXPECT_TRUE(!memcmp(buf, ref, sizeof(buf)));
+
+ // A one-byte buffer should always print a single NUL byte.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+ SafeSNPrintf(buf, 1, fmt, ' '));
+ EXPECT_EQ(0, buf[0]);
+ EXPECT_TRUE(!memcmp(buf+1, ref+1, sizeof(buf)-1));
+ memcpy(buf, ref, sizeof(buf));
+
+ // A larger (but limited) buffer should always leave the trailing bytes
+ // unchanged.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+ SafeSNPrintf(buf, 2, fmt, ' '));
+ EXPECT_EQ(text[0], buf[0]);
+ EXPECT_EQ(0, buf[1]);
+ EXPECT_TRUE(!memcmp(buf+2, ref+2, sizeof(buf)-2));
+ memcpy(buf, ref, sizeof(buf));
+
+ // A unrestricted buffer length should always leave the trailing bytes
+ // unchanged.
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1,
+ SafeSNPrintf(buf, sizeof(buf), fmt, ' '));
+ EXPECT_EQ(std::string(text), std::string(buf));
+ EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+ sizeof(buf) - sizeof(text)));
+ memcpy(buf, ref, sizeof(buf));
+
+ // The same test using SafeSPrintf() instead of SafeSNPrintf().
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(text))-1, SafeSPrintf(buf, fmt, ' '));
+ EXPECT_EQ(std::string(text), std::string(buf));
+ EXPECT_TRUE(!memcmp(buf + sizeof(text), ref + sizeof(text),
+ sizeof(buf) - sizeof(text)));
+ memcpy(buf, ref, sizeof(buf));
+
+ // Check for deduplication of '%' percent characters.
+ EXPECT_EQ(1, SafeSPrintf(buf, "%%", 0));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%%%", 0));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%Y", 0));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%Y", 0));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%%%Y", 0));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%%%%Y", 0));
+#if defined(NDEBUG)
+ EXPECT_EQ(1, SafeSPrintf(buf, "%", 0));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%%", 0));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, "%", 0), "ch");
+ EXPECT_DEATH(SafeSPrintf(buf, "%%%", 0), "ch");
+#endif
+}
+
+TEST(SafeSPrintfTest, MissingArg) {
+#if defined(NDEBUG)
+ char buf[20];
+ EXPECT_EQ(3, SafeSPrintf(buf, "%c%c", 'A'));
+ EXPECT_EQ("A%c", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ char buf[20];
+ EXPECT_DEATH(SafeSPrintf(buf, "%c%c", 'A'), "cur_arg < max_args");
+#endif
+}
+
+TEST(SafeSPrintfTest, ASANFriendlyBufferTest) {
+ // Print into a buffer that is sized exactly to size. ASAN can verify that
+ // nobody attempts to write past the end of the buffer.
+ // There is a more complicated test in PrintLongString() that covers a lot
+ // more edge case, but it is also harder to debug in case of a failure.
+ const char kTestString[] = "This is a test";
+ std::unique_ptr<char[]> buf(new char[sizeof(kTestString)]);
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kTestString) - 1),
+ SafeSNPrintf(buf.get(), sizeof(kTestString), kTestString));
+ EXPECT_EQ(std::string(kTestString), std::string(buf.get()));
+ EXPECT_EQ(static_cast<ssize_t>(sizeof(kTestString) - 1),
+ SafeSNPrintf(buf.get(), sizeof(kTestString), "%s", kTestString));
+ EXPECT_EQ(std::string(kTestString), std::string(buf.get()));
+}
+
+TEST(SafeSPrintfTest, NArgs) {
+ // Pre-C++11 compilers have a different code path, that can only print
+ // up to ten distinct arguments.
+ // We test both SafeSPrintf() and SafeSNPrintf(). This makes sure we don't
+ // have typos in the copy-n-pasted code that is needed to deal with various
+ // numbers of arguments.
+ char buf[12];
+ EXPECT_EQ(1, SafeSPrintf(buf, "%c", 1));
+ EXPECT_EQ("\1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%c%c", 1, 2));
+ EXPECT_EQ("\1\2", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%c%c%c", 1, 2, 3));
+ EXPECT_EQ("\1\2\3", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%c%c%c%c", 1, 2, 3, 4));
+ EXPECT_EQ("\1\2\3\4", std::string(buf));
+ EXPECT_EQ(5, SafeSPrintf(buf, "%c%c%c%c%c", 1, 2, 3, 4, 5));
+ EXPECT_EQ("\1\2\3\4\5", std::string(buf));
+ EXPECT_EQ(6, SafeSPrintf(buf, "%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6));
+ EXPECT_EQ("\1\2\3\4\5\6", std::string(buf));
+ EXPECT_EQ(7, SafeSPrintf(buf, "%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7));
+ EXPECT_EQ("\1\2\3\4\5\6\7", std::string(buf));
+ EXPECT_EQ(8, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7, 8));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10", std::string(buf));
+ EXPECT_EQ(9, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10\11", std::string(buf));
+ EXPECT_EQ(10, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+
+ // Repeat all the tests with SafeSNPrintf() instead of SafeSPrintf().
+ EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf));
+ EXPECT_EQ(1, SafeSNPrintf(buf, 11, "%c", 1));
+ EXPECT_EQ("\1", std::string(buf));
+ EXPECT_EQ(2, SafeSNPrintf(buf, 11, "%c%c", 1, 2));
+ EXPECT_EQ("\1\2", std::string(buf));
+ EXPECT_EQ(3, SafeSNPrintf(buf, 11, "%c%c%c", 1, 2, 3));
+ EXPECT_EQ("\1\2\3", std::string(buf));
+ EXPECT_EQ(4, SafeSNPrintf(buf, 11, "%c%c%c%c", 1, 2, 3, 4));
+ EXPECT_EQ("\1\2\3\4", std::string(buf));
+ EXPECT_EQ(5, SafeSNPrintf(buf, 11, "%c%c%c%c%c", 1, 2, 3, 4, 5));
+ EXPECT_EQ("\1\2\3\4\5", std::string(buf));
+ EXPECT_EQ(6, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6));
+ EXPECT_EQ("\1\2\3\4\5\6", std::string(buf));
+ EXPECT_EQ(7, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c", 1, 2, 3, 4, 5, 6, 7));
+ EXPECT_EQ("\1\2\3\4\5\6\7", std::string(buf));
+ EXPECT_EQ(8, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10", std::string(buf));
+ EXPECT_EQ(9, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10\11", std::string(buf));
+ EXPECT_EQ(10, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf));
+
+ EXPECT_EQ(11, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf));
+ EXPECT_EQ(11, SafeSNPrintf(buf, 12, "%c%c%c%c%c%c%c%c%c%c%c",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
+ EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf));
+}
+
+TEST(SafeSPrintfTest, DataTypes) {
+ char buf[40];
+
+ // Bytes
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint8_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%d", (uint8_t)-1));
+ EXPECT_EQ("255", std::string(buf));
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int8_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int8_t)-1));
+ EXPECT_EQ("-1", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%d", (int8_t)-128));
+ EXPECT_EQ("-128", std::string(buf));
+
+ // Half-words
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint16_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(5, SafeSPrintf(buf, "%d", (uint16_t)-1));
+ EXPECT_EQ("65535", std::string(buf));
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int16_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int16_t)-1));
+ EXPECT_EQ("-1", std::string(buf));
+ EXPECT_EQ(6, SafeSPrintf(buf, "%d", (int16_t)-32768));
+ EXPECT_EQ("-32768", std::string(buf));
+
+ // Words
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint32_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(10, SafeSPrintf(buf, "%d", (uint32_t)-1));
+ EXPECT_EQ("4294967295", std::string(buf));
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int32_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int32_t)-1));
+ EXPECT_EQ("-1", std::string(buf));
+ // Work-around for an limitation of C90
+ EXPECT_EQ(11, SafeSPrintf(buf, "%d", (int32_t)-2147483647-1));
+ EXPECT_EQ("-2147483648", std::string(buf));
+
+ // Quads
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (uint64_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(20, SafeSPrintf(buf, "%d", (uint64_t)-1));
+ EXPECT_EQ("18446744073709551615", std::string(buf));
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", (int64_t)1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%d", (int64_t)-1));
+ EXPECT_EQ("-1", std::string(buf));
+ // Work-around for an limitation of C90
+ EXPECT_EQ(20, SafeSPrintf(buf, "%d", (int64_t)-9223372036854775807LL-1));
+ EXPECT_EQ("-9223372036854775808", std::string(buf));
+
+ // Strings (both const and mutable).
+ EXPECT_EQ(4, SafeSPrintf(buf, "test"));
+ EXPECT_EQ("test", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, buf));
+ EXPECT_EQ("test", std::string(buf));
+
+ // Pointer
+ char addr[20];
+ sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)buf);
+ SafeSPrintf(buf, "%p", buf);
+ EXPECT_EQ(std::string(addr), std::string(buf));
+ SafeSPrintf(buf, "%p", (const char *)buf);
+ EXPECT_EQ(std::string(addr), std::string(buf));
+ sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)sprintf);
+ SafeSPrintf(buf, "%p", sprintf);
+ EXPECT_EQ(std::string(addr), std::string(buf));
+
+ // Padding for pointers is a little more complicated because of the "0x"
+ // prefix. Padding with '0' zeros is relatively straight-forward, but
+ // padding with ' ' spaces requires more effort.
+ sprintf(addr, "0x%017llX", (unsigned long long)(uintptr_t)buf);
+ SafeSPrintf(buf, "%019p", buf);
+ EXPECT_EQ(std::string(addr), std::string(buf));
+ sprintf(addr, "0x%llX", (unsigned long long)(uintptr_t)buf);
+ memset(addr, ' ',
+ (char*)memmove(addr + sizeof(addr) - strlen(addr) - 1,
+ addr, strlen(addr)+1) - addr);
+ SafeSPrintf(buf, "%19p", buf);
+ EXPECT_EQ(std::string(addr), std::string(buf));
+}
+
+namespace {
+void PrintLongString(char* buf, size_t sz) {
+ // Output a reasonably complex expression into a limited-size buffer.
+ // At least one byte is available for writing the NUL character.
+ CHECK_GT(sz, static_cast<size_t>(0));
+
+ // Allocate slightly more space, so that we can verify that SafeSPrintf()
+ // never writes past the end of the buffer.
+ std::unique_ptr<char[]> tmp(new char[sz + 2]);
+ memset(tmp.get(), 'X', sz+2);
+
+ // Use SafeSPrintf() to output a complex list of arguments:
+ // - test padding and truncating %c single characters.
+ // - test truncating %s simple strings.
+ // - test mismatching arguments and truncating (for %d != %s).
+ // - test zero-padding and truncating %x hexadecimal numbers.
+ // - test outputting and truncating %d MININT.
+ // - test outputting and truncating %p arbitrary pointer values.
+ // - test outputting, padding and truncating NULL-pointer %s strings.
+ char* out = tmp.get();
+ size_t out_sz = sz;
+ size_t len;
+ for (std::unique_ptr<char[]> perfect_buf;;) {
+ size_t needed =
+ SafeSNPrintf(out, out_sz,
+#if defined(NDEBUG)
+ "A%2cong %s: %d %010X %d %p%7s", 'l', "string", "",
+#else
+ "A%2cong %s: %%d %010X %d %p%7s", 'l', "string",
+#endif
+ 0xDEADBEEF, std::numeric_limits<intptr_t>::min(),
+ PrintLongString, static_cast<char*>(nullptr)) +
+ 1;
+
+ // Various sanity checks:
+ // The numbered of characters needed to print the full string should always
+ // be bigger or equal to the bytes that have actually been output.
+ len = strlen(tmp.get());
+ CHECK_GE(needed, len+1);
+
+ // The number of characters output should always fit into the buffer that
+ // was passed into SafeSPrintf().
+ CHECK_LT(len, out_sz);
+
+ // The output is always terminated with a NUL byte (actually, this test is
+ // always going to pass, as strlen() already verified this)
+ EXPECT_FALSE(tmp[len]);
+
+ // ASAN can check that we are not overwriting buffers, iff we make sure the
+ // buffer is exactly the size that we are expecting to be written. After
+ // running SafeSNPrintf() the first time, it is possible to compute the
+ // correct buffer size for this test. So, allocate a second buffer and run
+ // the exact same SafeSNPrintf() command again.
+ if (!perfect_buf.get()) {
+ out_sz = std::min(needed, sz);
+ out = new char[out_sz];
+ perfect_buf.reset(out);
+ } else {
+ break;
+ }
+ }
+
+ // All trailing bytes are unchanged.
+ for (size_t i = len+1; i < sz+2; ++i)
+ EXPECT_EQ('X', tmp[i]);
+
+ // The text that was generated by SafeSPrintf() should always match the
+ // equivalent text generated by sprintf(). Please note that the format
+ // string for sprintf() is not complicated, as it does not have the
+ // benefit of getting type information from the C++ compiler.
+ //
+ // N.B.: It would be so much cleaner to use snprintf(). But unfortunately,
+ // Visual Studio doesn't support this function, and the work-arounds
+ // are all really awkward.
+ char ref[256];
+ CHECK_LE(sz, sizeof(ref));
+ sprintf(ref, "A long string: %%d 00DEADBEEF %lld 0x%llX <NULL>",
+ static_cast<long long>(std::numeric_limits<intptr_t>::min()),
+ static_cast<unsigned long long>(
+ reinterpret_cast<uintptr_t>(PrintLongString)));
+ ref[sz-1] = '\000';
+
+#if defined(NDEBUG)
+ const size_t kSSizeMax = std::numeric_limits<ssize_t>::max();
+#else
+ const size_t kSSizeMax = internal::GetSafeSPrintfSSizeMaxForTest();
+#endif
+
+ // Compare the output from SafeSPrintf() to the one from sprintf().
+ EXPECT_EQ(std::string(ref).substr(0, kSSizeMax-1), std::string(tmp.get()));
+
+ // We allocated a slightly larger buffer, so that we could perform some
+ // extra sanity checks. Now that the tests have all passed, we copy the
+ // data to the output buffer that the caller provided.
+ memcpy(buf, tmp.get(), len+1);
+}
+
+#if !defined(NDEBUG)
+class ScopedSafeSPrintfSSizeMaxSetter {
+ public:
+ ScopedSafeSPrintfSSizeMaxSetter(size_t sz) {
+ old_ssize_max_ = internal::GetSafeSPrintfSSizeMaxForTest();
+ internal::SetSafeSPrintfSSizeMaxForTest(sz);
+ }
+
+ ~ScopedSafeSPrintfSSizeMaxSetter() {
+ internal::SetSafeSPrintfSSizeMaxForTest(old_ssize_max_);
+ }
+
+ private:
+ size_t old_ssize_max_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSafeSPrintfSSizeMaxSetter);
+};
+#endif
+
+} // anonymous namespace
+
+TEST(SafeSPrintfTest, Truncation) {
+ // We use PrintLongString() to print a complex long string and then
+ // truncate to all possible lengths. This ends up exercising a lot of
+ // different code paths in SafeSPrintf() and IToASCII(), as truncation can
+ // happen in a lot of different states.
+ char ref[256];
+ PrintLongString(ref, sizeof(ref));
+ for (size_t i = strlen(ref)+1; i; --i) {
+ char buf[sizeof(ref)];
+ PrintLongString(buf, i);
+ EXPECT_EQ(std::string(ref, i - 1), std::string(buf));
+ }
+
+ // When compiling in debug mode, we have the ability to fake a small
+ // upper limit for the maximum value that can be stored in an ssize_t.
+ // SafeSPrintf() uses this upper limit to determine how many bytes it will
+ // write to the buffer, even if the caller claimed a bigger buffer size.
+ // Repeat the truncation test and verify that this other code path in
+ // SafeSPrintf() works correctly, too.
+#if !defined(NDEBUG)
+ for (size_t i = strlen(ref)+1; i > 1; --i) {
+ ScopedSafeSPrintfSSizeMaxSetter ssize_max_setter(i);
+ char buf[sizeof(ref)];
+ PrintLongString(buf, sizeof(buf));
+ EXPECT_EQ(std::string(ref, i - 1), std::string(buf));
+ }
+
+ // kSSizeMax is also used to constrain the maximum amount of padding, before
+ // SafeSPrintf() detects an error in the format string.
+ ScopedSafeSPrintfSSizeMaxSetter ssize_max_setter(100);
+ char buf[256];
+ EXPECT_EQ(99, SafeSPrintf(buf, "%99c", ' '));
+ EXPECT_EQ(std::string(99, ' '), std::string(buf));
+ *buf = '\000';
+#if defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, "%100c", ' '), "padding <= max_padding");
+#endif
+ EXPECT_EQ(0, *buf);
+#endif
+}
+
+TEST(SafeSPrintfTest, Padding) {
+ char buf[40], fmt[40];
+
+ // Chars %c
+ EXPECT_EQ(1, SafeSPrintf(buf, "%c", 'A'));
+ EXPECT_EQ("A", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%2c", 'A'));
+ EXPECT_EQ(" A", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%02c", 'A'));
+ EXPECT_EQ(" A", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%-2c", 'A'));
+ EXPECT_EQ("%-2c", std::string(buf));
+ SafeSPrintf(fmt, "%%%dc", std::numeric_limits<ssize_t>::max() - 1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1, SafeSPrintf(buf, fmt, 'A'));
+ SafeSPrintf(fmt, "%%%dc",
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, fmt, 'A'));
+ EXPECT_EQ("%c", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, fmt, 'A'), "padding <= max_padding");
+#endif
+
+ // Octal %o
+ EXPECT_EQ(1, SafeSPrintf(buf, "%o", 1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%2o", 1));
+ EXPECT_EQ(" 1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%02o", 1));
+ EXPECT_EQ("01", std::string(buf));
+ EXPECT_EQ(12, SafeSPrintf(buf, "%12o", -1));
+ EXPECT_EQ(" 37777777777", std::string(buf));
+ EXPECT_EQ(12, SafeSPrintf(buf, "%012o", -1));
+ EXPECT_EQ("037777777777", std::string(buf));
+ EXPECT_EQ(23, SafeSPrintf(buf, "%23o", -1LL));
+ EXPECT_EQ(" 1777777777777777777777", std::string(buf));
+ EXPECT_EQ(23, SafeSPrintf(buf, "%023o", -1LL));
+ EXPECT_EQ("01777777777777777777777", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%2o", 0111));
+ EXPECT_EQ("111", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%-2o", 1));
+ EXPECT_EQ("%-2o", std::string(buf));
+ SafeSPrintf(fmt, "%%%do", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, 1));
+ EXPECT_EQ(" ", std::string(buf));
+ SafeSPrintf(fmt, "%%0%do", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, 1));
+ EXPECT_EQ("000", std::string(buf));
+ SafeSPrintf(fmt, "%%%do",
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+ EXPECT_EQ("%o", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+ // Decimals %d
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", 1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%2d", 1));
+ EXPECT_EQ(" 1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%02d", 1));
+ EXPECT_EQ("01", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%3d", -1));
+ EXPECT_EQ(" -1", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%03d", -1));
+ EXPECT_EQ("-01", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%2d", 111));
+ EXPECT_EQ("111", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%2d", -111));
+ EXPECT_EQ("-111", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%-2d", 1));
+ EXPECT_EQ("%-2d", std::string(buf));
+ SafeSPrintf(fmt, "%%%dd", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, 1));
+ EXPECT_EQ(" ", std::string(buf));
+ SafeSPrintf(fmt, "%%0%dd", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, 1));
+ EXPECT_EQ("000", std::string(buf));
+ SafeSPrintf(fmt, "%%%dd",
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+ EXPECT_EQ("%d", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+ // Hex %X
+ EXPECT_EQ(1, SafeSPrintf(buf, "%X", 1));
+ EXPECT_EQ("1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%2X", 1));
+ EXPECT_EQ(" 1", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%02X", 1));
+ EXPECT_EQ("01", std::string(buf));
+ EXPECT_EQ(9, SafeSPrintf(buf, "%9X", -1));
+ EXPECT_EQ(" FFFFFFFF", std::string(buf));
+ EXPECT_EQ(9, SafeSPrintf(buf, "%09X", -1));
+ EXPECT_EQ("0FFFFFFFF", std::string(buf));
+ EXPECT_EQ(17, SafeSPrintf(buf, "%17X", -1LL));
+ EXPECT_EQ(" FFFFFFFFFFFFFFFF", std::string(buf));
+ EXPECT_EQ(17, SafeSPrintf(buf, "%017X", -1LL));
+ EXPECT_EQ("0FFFFFFFFFFFFFFFF", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%2X", 0x111));
+ EXPECT_EQ("111", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%-2X", 1));
+ EXPECT_EQ("%-2X", std::string(buf));
+ SafeSPrintf(fmt, "%%%dX", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, 1));
+ EXPECT_EQ(" ", std::string(buf));
+ SafeSPrintf(fmt, "%%0%dX", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, 1));
+ EXPECT_EQ("000", std::string(buf));
+ SafeSPrintf(fmt, "%%%dX",
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+ EXPECT_EQ("%X", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+ // Pointer %p
+ EXPECT_EQ(3, SafeSPrintf(buf, "%p", (void*)1));
+ EXPECT_EQ("0x1", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%4p", (void*)1));
+ EXPECT_EQ(" 0x1", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%04p", (void*)1));
+ EXPECT_EQ("0x01", std::string(buf));
+ EXPECT_EQ(5, SafeSPrintf(buf, "%4p", (void*)0x111));
+ EXPECT_EQ("0x111", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%-2p", (void*)1));
+ EXPECT_EQ("%-2p", std::string(buf));
+ SafeSPrintf(fmt, "%%%dp", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, (void*)1));
+ EXPECT_EQ(" ", std::string(buf));
+ SafeSPrintf(fmt, "%%0%dp", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, (void*)1));
+ EXPECT_EQ("0x0", std::string(buf));
+ SafeSPrintf(fmt, "%%%dp",
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, fmt, 1));
+ EXPECT_EQ("%p", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, fmt, 1), "padding <= max_padding");
+#endif
+
+ // String
+ EXPECT_EQ(1, SafeSPrintf(buf, "%s", "A"));
+ EXPECT_EQ("A", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%2s", "A"));
+ EXPECT_EQ(" A", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%02s", "A"));
+ EXPECT_EQ(" A", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%2s", "AAA"));
+ EXPECT_EQ("AAA", std::string(buf));
+ EXPECT_EQ(4, SafeSPrintf(buf, "%-2s", "A"));
+ EXPECT_EQ("%-2s", std::string(buf));
+ SafeSPrintf(fmt, "%%%ds", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, "A"));
+ EXPECT_EQ(" ", std::string(buf));
+ SafeSPrintf(fmt, "%%0%ds", std::numeric_limits<ssize_t>::max()-1);
+ EXPECT_EQ(std::numeric_limits<ssize_t>::max()-1,
+ SafeSNPrintf(buf, 4, fmt, "A"));
+ EXPECT_EQ(" ", std::string(buf));
+ SafeSPrintf(fmt, "%%%ds",
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, fmt, "A"));
+ EXPECT_EQ("%s", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, fmt, "A"), "padding <= max_padding");
+#endif
+}
+
+TEST(SafeSPrintfTest, EmbeddedNul) {
+ char buf[] = { 'X', 'X', 'X', 'X' };
+ EXPECT_EQ(2, SafeSPrintf(buf, "%3c", 0));
+ EXPECT_EQ(' ', buf[0]);
+ EXPECT_EQ(' ', buf[1]);
+ EXPECT_EQ(0, buf[2]);
+ EXPECT_EQ('X', buf[3]);
+
+ // Check handling of a NUL format character. N.B. this takes two different
+ // code paths depending on whether we are actually passing arguments. If
+ // we don't have any arguments, we are running in the fast-path code, that
+ // looks (almost) like a strncpy().
+#if defined(NDEBUG)
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%%"));
+ EXPECT_EQ("%%", std::string(buf));
+ EXPECT_EQ(2, SafeSPrintf(buf, "%%%", 0));
+ EXPECT_EQ("%%", std::string(buf));
+#elif defined(ALLOW_DEATH_TEST)
+ EXPECT_DEATH(SafeSPrintf(buf, "%%%"), "src.1. == '%'");
+ EXPECT_DEATH(SafeSPrintf(buf, "%%%", 0), "ch");
+#endif
+}
+
+TEST(SafeSPrintfTest, EmitNULL) {
+ char buf[40];
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wconversion-null"
+#endif
+ EXPECT_EQ(1, SafeSPrintf(buf, "%d", NULL));
+ EXPECT_EQ("0", std::string(buf));
+ EXPECT_EQ(3, SafeSPrintf(buf, "%p", NULL));
+ EXPECT_EQ("0x0", std::string(buf));
+ EXPECT_EQ(6, SafeSPrintf(buf, "%s", NULL));
+ EXPECT_EQ("<NULL>", std::string(buf));
+#if defined(__GCC__)
+#pragma GCC diagnostic pop
+#endif
+}
+
+TEST(SafeSPrintfTest, PointerSize) {
+ // The internal data representation is a 64bit value, independent of the
+ // native word size. We want to perform sign-extension for signed integers,
+ // but we want to avoid doing so for pointer types. This could be a
+ // problem on systems, where pointers are only 32bit. This tests verifies
+ // that there is no such problem.
+ char *str = reinterpret_cast<char *>(0x80000000u);
+ void *ptr = str;
+ char buf[40];
+ EXPECT_EQ(10, SafeSPrintf(buf, "%p", str));
+ EXPECT_EQ("0x80000000", std::string(buf));
+ EXPECT_EQ(10, SafeSPrintf(buf, "%p", ptr));
+ EXPECT_EQ("0x80000000", std::string(buf));
+}
+
+} // namespace strings
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/string16.cc b/security/sandbox/chromium/base/strings/string16.cc
new file mode 100644
index 0000000000..84962e6710
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string16.cc
@@ -0,0 +1,87 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string16.h"
+
+#if defined(WCHAR_T_IS_UTF16) && !defined(_AIX)
+
+#error This file should not be used on 2-byte wchar_t systems
+// If this winds up being needed on 2-byte wchar_t systems, either the
+// definitions below can be used, or the host system's wide character
+// functions like wmemcmp can be wrapped.
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <ostream>
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+int c16memcmp(const char16* s1, const char16* s2, size_t n) {
+ // We cannot call memcmp because that changes the semantics.
+ while (n-- > 0) {
+ if (*s1 != *s2) {
+ // We cannot use (*s1 - *s2) because char16 is unsigned.
+ return ((*s1 < *s2) ? -1 : 1);
+ }
+ ++s1;
+ ++s2;
+ }
+ return 0;
+}
+
+size_t c16len(const char16* s) {
+ const char16 *s_orig = s;
+ while (*s) {
+ ++s;
+ }
+ return s - s_orig;
+}
+
+const char16* c16memchr(const char16* s, char16 c, size_t n) {
+ while (n-- > 0) {
+ if (*s == c) {
+ return s;
+ }
+ ++s;
+ }
+ return nullptr;
+}
+
+char16* c16memmove(char16* s1, const char16* s2, size_t n) {
+ return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memcpy(char16* s1, const char16* s2, size_t n) {
+ return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16)));
+}
+
+char16* c16memset(char16* s, char16 c, size_t n) {
+ char16 *s_orig = s;
+ while (n-- > 0) {
+ *s = c;
+ ++s;
+ }
+ return s_orig;
+}
+
+namespace string16_internals {
+
+std::ostream& operator<<(std::ostream& out, const string16& str) {
+ return out << base::StringPiece16(str);
+}
+
+void PrintTo(const string16& str, std::ostream* out) {
+ *out << str;
+}
+
+} // namespace string16_internals
+
+} // namespace base
+
+template class std::
+ basic_string<base::char16, base::string16_internals::string16_char_traits>;
+
+#endif // WCHAR_T_IS_UTF32
diff --git a/security/sandbox/chromium/base/strings/string16.h b/security/sandbox/chromium/base/strings/string16.h
new file mode 100644
index 0000000000..3cb6c7c495
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string16.h
@@ -0,0 +1,229 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_STRING16_H_
+#define BASE_STRINGS_STRING16_H_
+
+// WHAT:
+// A version of std::basic_string that provides 2-byte characters even when
+// wchar_t is not implemented as a 2-byte type. You can access this class as
+// string16. We also define char16, which string16 is based upon.
+//
+// WHY:
+// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2
+// data. Plenty of existing code operates on strings encoded as UTF-16.
+//
+// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make
+// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails
+// at run time, because it calls some functions (like wcslen) that come from
+// the system's native C library -- which was built with a 4-byte wchar_t!
+// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's
+// entirely improper on those systems where the encoding of wchar_t is defined
+// as UTF-32.
+//
+// Here, we define string16, which is similar to std::wstring but replaces all
+// libc functions with custom, 2-byte-char compatible routines. It is capable
+// of carrying UTF-16-encoded data.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <functional>
+#include <string>
+
+#include "base/base_export.h"
+#include "build/build_config.h"
+
+#if defined(WCHAR_T_IS_UTF16)
+
+// Define a macro for wrapping construction of char16 arrays and string16s from
+// a literal string. This indirection allows for an easier migration of
+// base::char16 to char16_t on platforms where WCHAR_T_IS_UTF16, as only a one
+// character change to the macro will be necessary.
+// This macro does not exist when WCHAR_T_IS_UTF32, as it is currently not
+// possible to create a char array form a literal in this case.
+// TODO(https://crbug.com/911896): Remove this macro once base::char16 is
+// char16_t on all platforms.
+#define STRING16_LITERAL(x) L##x
+
+namespace base {
+
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+} // namespace base
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+#include <wchar.h> // for mbstate_t
+
+namespace base {
+
+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"
+// instead of "c16").
+BASE_EXPORT int c16memcmp(const char16* s1, const char16* s2, size_t n);
+BASE_EXPORT size_t c16len(const char16* s);
+BASE_EXPORT const char16* c16memchr(const char16* s, char16 c, size_t n);
+BASE_EXPORT char16* c16memmove(char16* s1, const char16* s2, size_t n);
+BASE_EXPORT char16* c16memcpy(char16* s1, const char16* s2, size_t n);
+BASE_EXPORT char16* c16memset(char16* s, char16 c, size_t n);
+
+// This namespace contains the implementation of base::string16 along with
+// things that need to be found via argument-dependent lookup from a
+// base::string16.
+namespace string16_internals {
+
+struct string16_char_traits {
+ typedef char16 char_type;
+ typedef int int_type;
+
+ // int_type needs to be able to hold each possible value of char_type, and in
+ // addition, the distinct value of eof().
+ 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;
+ typedef std::fpos<state_type> pos_type;
+
+ static void assign(char_type& c1, const char_type& c2) {
+ c1 = c2;
+ }
+
+ static bool eq(const char_type& c1, const char_type& c2) {
+ return c1 == c2;
+ }
+ static bool lt(const char_type& c1, const char_type& c2) {
+ return c1 < c2;
+ }
+
+ static int compare(const char_type* s1, const char_type* s2, size_t n) {
+ return c16memcmp(s1, s2, n);
+ }
+
+ static size_t length(const char_type* s) {
+ return c16len(s);
+ }
+
+ static const char_type* find(const char_type* s, size_t n,
+ const char_type& a) {
+ return c16memchr(s, a, n);
+ }
+
+ static char_type* move(char_type* s1, const char_type* s2, size_t n) {
+ return c16memmove(s1, s2, n);
+ }
+
+ static char_type* copy(char_type* s1, const char_type* s2, size_t n) {
+ return c16memcpy(s1, s2, n);
+ }
+
+ static char_type* assign(char_type* s, size_t n, char_type a) {
+ return c16memset(s, a, n);
+ }
+
+ static int_type not_eof(const int_type& c) {
+ return eq_int_type(c, eof()) ? 0 : c;
+ }
+
+ static char_type to_char_type(const int_type& c) {
+ return char_type(c);
+ }
+
+ static int_type to_int_type(const char_type& c) {
+ return int_type(c);
+ }
+
+ static bool eq_int_type(const int_type& c1, const int_type& c2) {
+ return c1 == c2;
+ }
+
+ static int_type eof() {
+ return static_cast<int_type>(EOF);
+ }
+};
+
+} // namespace string16_internals
+
+typedef std::basic_string<char16,
+ base::string16_internals::string16_char_traits>
+ string16;
+
+namespace string16_internals {
+
+BASE_EXPORT extern std::ostream& operator<<(std::ostream& out,
+ const string16& str);
+
+// This is required by googletest to print a readable output on test failures.
+BASE_EXPORT extern void PrintTo(const string16& str, std::ostream* out);
+
+} // namespace string16_internals
+
+} // namespace base
+
+// The string class will be explicitly instantiated only once, in string16.cc.
+//
+// std::basic_string<> in GNU libstdc++ contains a static data member,
+// _S_empty_rep_storage, to represent empty strings. When an operation such
+// as assignment or destruction is performed on a string, causing its existing
+// data member to be invalidated, it must not be freed if this static data
+// member is being used. Otherwise, it counts as an attempt to free static
+// (and not allocated) data, which is a memory error.
+//
+// Generally, due to C++ template magic, _S_empty_rep_storage will be marked
+// as a coalesced symbol, meaning that the linker will combine multiple
+// instances into a single one when generating output.
+//
+// If a string class is used by multiple shared libraries, a problem occurs.
+// Each library will get its own copy of _S_empty_rep_storage. When strings
+// are passed across a library boundary for alteration or destruction, memory
+// errors will result. GNU libstdc++ contains a configuration option,
+// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which
+// disables the static data member optimization, but it's a good optimization
+// and non-STL code is generally at the mercy of the system's STL
+// configuration. Fully-dynamic strings are not the default for GNU libstdc++
+// libstdc++ itself or for the libstdc++ installations on the systems we care
+// about, such as Mac OS X and relevant flavors of Linux.
+//
+// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 .
+//
+// To avoid problems, string classes need to be explicitly instantiated only
+// once, in exactly one library. All other string users see it via an "extern"
+// declaration. This is precisely how GNU libstdc++ handles
+// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring).
+//
+// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2),
+// in which the linker does not fully coalesce symbols when dead code
+// stripping is enabled. This bug causes the memory errors described above
+// to occur even when a std::basic_string<> does not cross shared library
+// boundaries, such as in statically-linked executables.
+//
+// TODO(mark): File this bug with Apple and update this note with a bug number.
+
+extern template class BASE_EXPORT
+ std::basic_string<base::char16,
+ base::string16_internals::string16_char_traits>;
+
+// Specialize std::hash for base::string16. Although the style guide forbids
+// this in general, it is necessary for consistency with WCHAR_T_IS_UTF16
+// platforms, where base::string16 is a type alias for std::wstring.
+namespace std {
+template <>
+struct hash<base::string16> {
+ std::size_t operator()(const base::string16& s) const {
+ std::size_t result = 0;
+ for (base::char16 c : s)
+ result = (result * 131) + c;
+ return result;
+ }
+};
+} // namespace std
+
+#endif // WCHAR_T_IS_UTF32
+
+#endif // BASE_STRINGS_STRING16_H_
diff --git a/security/sandbox/chromium/base/strings/string_number_conversions.cc b/security/sandbox/chromium/base/strings/string_number_conversions.cc
new file mode 100644
index 0000000000..8b71b0ae11
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_number_conversions.cc
@@ -0,0 +1,545 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_number_conversions.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <wctype.h>
+
+#include <limits>
+#include <type_traits>
+
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/numerics/safe_math.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/double_conversion/double-conversion/double-conversion.h"
+
+namespace base {
+
+namespace {
+
+template <typename STR, typename INT>
+struct IntToStringT {
+ 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 size_t kOutputBufSize =
+ 3 * sizeof(INT) + std::numeric_limits<INT>::is_signed;
+
+ // Create the string in a temporary buffer, write it back to front, and
+ // then return the substr of what we ended up using.
+ using CHR = typename STR::value_type;
+ CHR outbuf[kOutputBufSize];
+
+ // The ValueOrDie call below can never fail, because UnsignedAbs is valid
+ // for all valid inputs.
+ typename std::make_unsigned<INT>::type res =
+ CheckedNumeric<INT>(value).UnsignedAbs().ValueOrDie();
+
+ CHR* end = outbuf + kOutputBufSize;
+ CHR* i = end;
+ do {
+ --i;
+ DCHECK(i != outbuf);
+ *i = static_cast<CHR>((res % 10) + '0');
+ res /= 10;
+ } while (res != 0);
+ if (IsValueNegative(value)) {
+ --i;
+ DCHECK(i != outbuf);
+ *i = static_cast<CHR>('-');
+ }
+ return STR(i, end);
+ }
+};
+
+// Utility to convert a character to a digit in a given base
+template<typename CHAR, int BASE, bool BASE_LTE_10> class BaseCharToDigit {
+};
+
+// Faster specialization for bases <= 10
+template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, true> {
+ public:
+ static bool Convert(CHAR c, uint8_t* digit) {
+ if (c >= '0' && c < '0' + BASE) {
+ *digit = static_cast<uint8_t>(c - '0');
+ return true;
+ }
+ return false;
+ }
+};
+
+// Specialization for bases where 10 < base <= 36
+template<typename CHAR, int BASE> class BaseCharToDigit<CHAR, BASE, false> {
+ public:
+ static bool Convert(CHAR c, uint8_t* digit) {
+ if (c >= '0' && c <= '9') {
+ *digit = c - '0';
+ } else if (c >= 'a' && c < 'a' + BASE - 10) {
+ *digit = c - 'a' + 10;
+ } else if (c >= 'A' && c < 'A' + BASE - 10) {
+ *digit = c - 'A' + 10;
+ } else {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <int BASE, typename CHAR>
+bool CharToDigit(CHAR c, uint8_t* digit) {
+ return BaseCharToDigit<CHAR, BASE, BASE <= 10>::Convert(c, digit);
+}
+
+// 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<typename CHAR> class WhitespaceHelper {
+};
+
+template<> class WhitespaceHelper<char> {
+ public:
+ static bool Invoke(char c) {
+ return 0 != isspace(static_cast<unsigned char>(c));
+ }
+};
+
+template<> class WhitespaceHelper<char16> {
+ public:
+ static bool Invoke(char16 c) {
+ return 0 != iswspace(c);
+ }
+};
+
+template<typename CHAR> bool LocalIsWhitespace(CHAR c) {
+ return WhitespaceHelper<CHAR>::Invoke(c);
+}
+
+// IteratorRangeToNumberTraits should provide:
+// - a typedef for iterator_type, the iterator type used as input.
+// - a typedef for value_type, the target numeric type.
+// - static functions min, max (returning the minimum and maximum permitted
+// values)
+// - constant kBase, the base in which to interpret the input
+template<typename IteratorRangeToNumberTraits>
+class IteratorRangeToNumber {
+ public:
+ typedef IteratorRangeToNumberTraits traits;
+ typedef typename traits::iterator_type const_iterator;
+ typedef typename traits::value_type value_type;
+
+ // Generalized iterator-range-to-number conversion.
+ //
+ static bool Invoke(const_iterator begin,
+ const_iterator end,
+ value_type* output) {
+ bool valid = true;
+
+ while (begin != end && LocalIsWhitespace(*begin)) {
+ valid = false;
+ ++begin;
+ }
+
+ if (begin != end && *begin == '-') {
+ if (!std::numeric_limits<value_type>::is_signed) {
+ *output = 0;
+ valid = false;
+ } else if (!Negative::Invoke(begin + 1, end, output)) {
+ valid = false;
+ }
+ } else {
+ if (begin != end && *begin == '+') {
+ ++begin;
+ }
+ if (!Positive::Invoke(begin, end, output)) {
+ valid = false;
+ }
+ }
+
+ return valid;
+ }
+
+ private:
+ // Sign provides:
+ // - a static function, CheckBounds, that determines whether the next digit
+ // causes an overflow/underflow
+ // - a static function, Increment, that appends the next digit appropriately
+ // according to the sign of the number being parsed.
+ template<typename Sign>
+ class Base {
+ public:
+ static bool Invoke(const_iterator begin, const_iterator end,
+ typename traits::value_type* output) {
+ *output = 0;
+
+ if (begin == end) {
+ return false;
+ }
+
+ // Note: no performance difference was found when using template
+ // specialization to remove this check in bases other than 16
+ if (traits::kBase == 16 && end - begin > 2 && *begin == '0' &&
+ (*(begin + 1) == 'x' || *(begin + 1) == 'X')) {
+ begin += 2;
+ }
+
+ for (const_iterator current = begin; current != end; ++current) {
+ uint8_t new_digit = 0;
+
+ if (!CharToDigit<traits::kBase>(*current, &new_digit)) {
+ return false;
+ }
+
+ if (current != begin) {
+ if (!Sign::CheckBounds(output, new_digit)) {
+ return false;
+ }
+ *output *= traits::kBase;
+ }
+
+ Sign::Increment(new_digit, output);
+ }
+ return true;
+ }
+ };
+
+ class Positive : public Base<Positive> {
+ public:
+ static bool CheckBounds(value_type* output, uint8_t new_digit) {
+ if (*output > static_cast<value_type>(traits::max() / traits::kBase) ||
+ (*output == static_cast<value_type>(traits::max() / traits::kBase) &&
+ new_digit > traits::max() % traits::kBase)) {
+ *output = traits::max();
+ return false;
+ }
+ return true;
+ }
+ static void Increment(uint8_t increment, value_type* output) {
+ *output += increment;
+ }
+ };
+
+ class Negative : public Base<Negative> {
+ public:
+ 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)) {
+ *output = traits::min();
+ return false;
+ }
+ return true;
+ }
+ static void Increment(uint8_t increment, value_type* output) {
+ *output -= increment;
+ }
+ };
+};
+
+template<typename ITERATOR, typename VALUE, int BASE>
+class BaseIteratorRangeToNumberTraits {
+ public:
+ typedef ITERATOR iterator_type;
+ typedef VALUE value_type;
+ static value_type min() {
+ return std::numeric_limits<value_type>::min();
+ }
+ static value_type max() {
+ return std::numeric_limits<value_type>::max();
+ }
+ static const int kBase = BASE;
+};
+
+template<typename ITERATOR>
+class BaseHexIteratorRangeToIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, int, 16> {
+};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, uint32_t, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToInt64Traits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, int64_t, 16> {};
+
+template <typename ITERATOR>
+class BaseHexIteratorRangeToUInt64Traits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, uint64_t, 16> {};
+
+typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
+ HexIteratorRangeToIntTraits;
+
+typedef BaseHexIteratorRangeToUIntTraits<StringPiece::const_iterator>
+ HexIteratorRangeToUIntTraits;
+
+typedef BaseHexIteratorRangeToInt64Traits<StringPiece::const_iterator>
+ HexIteratorRangeToInt64Traits;
+
+typedef BaseHexIteratorRangeToUInt64Traits<StringPiece::const_iterator>
+ HexIteratorRangeToUInt64Traits;
+
+template <typename VALUE, int BASE>
+class StringPieceToNumberTraits
+ : public BaseIteratorRangeToNumberTraits<StringPiece::const_iterator,
+ VALUE,
+ BASE> {
+};
+
+template <typename VALUE>
+bool StringToIntImpl(StringPiece input, VALUE* output) {
+ return IteratorRangeToNumber<StringPieceToNumberTraits<VALUE, 10> >::Invoke(
+ input.begin(), input.end(), output);
+}
+
+template <typename VALUE, int BASE>
+class StringPiece16ToNumberTraits
+ : public BaseIteratorRangeToNumberTraits<StringPiece16::const_iterator,
+ VALUE,
+ BASE> {
+};
+
+template <typename VALUE>
+bool String16ToIntImpl(StringPiece16 input, VALUE* output) {
+ return IteratorRangeToNumber<StringPiece16ToNumberTraits<VALUE, 10> >::Invoke(
+ input.begin(), input.end(), output);
+}
+
+} // namespace
+
+std::string NumberToString(int value) {
+ return IntToStringT<std::string, int>::IntToString(value);
+}
+
+string16 NumberToString16(int value) {
+ return IntToStringT<string16, int>::IntToString(value);
+}
+
+std::string NumberToString(unsigned value) {
+ return IntToStringT<std::string, unsigned>::IntToString(value);
+}
+
+string16 NumberToString16(unsigned value) {
+ return IntToStringT<string16, unsigned>::IntToString(value);
+}
+
+std::string NumberToString(long value) {
+ return IntToStringT<std::string, long>::IntToString(value);
+}
+
+string16 NumberToString16(long value) {
+ return IntToStringT<string16, long>::IntToString(value);
+}
+
+std::string NumberToString(unsigned long value) {
+ return IntToStringT<std::string, unsigned long>::IntToString(value);
+}
+
+string16 NumberToString16(unsigned long value) {
+ return IntToStringT<string16, unsigned long>::IntToString(value);
+}
+
+std::string NumberToString(long long value) {
+ return IntToStringT<std::string, long long>::IntToString(value);
+}
+
+string16 NumberToString16(long long value) {
+ return IntToStringT<string16, long long>::IntToString(value);
+}
+
+std::string NumberToString(unsigned long long value) {
+ return IntToStringT<std::string, unsigned long long>::IntToString(value);
+}
+
+string16 NumberToString16(unsigned long long value) {
+ return IntToStringT<string16, unsigned long long>::IntToString(value);
+}
+
+static const double_conversion::DoubleToStringConverter*
+GetDoubleToStringConverter() {
+ static NoDestructor<double_conversion::DoubleToStringConverter> converter(
+ double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN,
+ nullptr, nullptr, 'e', -6, 12, 0, 0);
+ return converter.get();
+}
+
+std::string NumberToString(double value) {
+ char buffer[32];
+ double_conversion::StringBuilder builder(buffer, sizeof(buffer));
+ GetDoubleToStringConverter()->ToShortest(value, &builder);
+ return std::string(buffer, builder.position());
+}
+
+base::string16 NumberToString16(double value) {
+ char buffer[32];
+ double_conversion::StringBuilder builder(buffer, sizeof(buffer));
+ GetDoubleToStringConverter()->ToShortest(value, &builder);
+
+ // The number will be ASCII. This creates the string using the "input
+ // iterator" variant which promotes from 8-bit to 16-bit via "=".
+ return base::string16(&buffer[0], &buffer[builder.position()]);
+}
+
+bool StringToInt(StringPiece input, int* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToInt(StringPiece16 input, int* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToUint(StringPiece input, unsigned* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToUint(StringPiece16 input, unsigned* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToInt64(StringPiece input, int64_t* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToInt64(StringPiece16 input, int64_t* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToUint64(StringPiece input, uint64_t* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToUint64(StringPiece16 input, uint64_t* output) {
+ return String16ToIntImpl(input, output);
+}
+
+bool StringToSizeT(StringPiece input, size_t* output) {
+ return StringToIntImpl(input, output);
+}
+
+bool StringToSizeT(StringPiece16 input, size_t* output) {
+ return String16ToIntImpl(input, output);
+}
+
+template <typename STRING, typename CHAR>
+bool StringToDoubleImpl(STRING input, const CHAR* data, double* output) {
+ static NoDestructor<double_conversion::StringToDoubleConverter> converter(
+ double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
+ double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
+ 0.0, 0, nullptr, nullptr);
+
+ int processed_characters_count;
+ *output = converter->StringToDouble(data, input.size(),
+ &processed_characters_count);
+
+ // Cases to return false:
+ // - If the input string is empty, there was nothing to parse.
+ // - If the value saturated to HUGE_VAL.
+ // - If the entire string was not processed, there are either characters
+ // remaining in the string after a parsed number, or the string does not
+ // begin with a parseable number.
+ // - If the first character is a space, there was leading whitespace
+ return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL &&
+ static_cast<size_t>(processed_characters_count) == input.size() &&
+ !IsUnicodeWhitespace(input[0]);
+}
+
+bool StringToDouble(StringPiece input, double* output) {
+ return StringToDoubleImpl(input, input.data(), output);
+}
+
+bool StringToDouble(StringPiece16 input, double* output) {
+ return StringToDoubleImpl(
+ input, reinterpret_cast<const uint16_t*>(input.data()), output);
+}
+
+std::string HexEncode(const void* bytes, size_t size) {
+ static const char kHexChars[] = "0123456789ABCDEF";
+
+ // Each input byte creates two output hex characters.
+ std::string ret(size * 2, '\0');
+
+ for (size_t i = 0; i < size; ++i) {
+ char b = reinterpret_cast<const char*>(bytes)[i];
+ ret[(i * 2)] = kHexChars[(b >> 4) & 0xf];
+ ret[(i * 2) + 1] = kHexChars[b & 0xf];
+ }
+ return ret;
+}
+
+std::string HexEncode(base::span<const uint8_t> bytes) {
+ return HexEncode(bytes.data(), bytes.size());
+}
+
+bool HexStringToInt(StringPiece input, int* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToUInt(StringPiece input, uint32_t* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToInt64(StringPiece input, int64_t* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToInt64Traits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+bool HexStringToUInt64(StringPiece input, uint64_t* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToUInt64Traits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
+template <typename Container>
+static bool HexStringToByteContainer(StringPiece input, Container* 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_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;
+ }
+ output->push_back((msb << 4) | lsb);
+ }
+ return true;
+}
+
+bool HexStringToBytes(StringPiece input, std::vector<uint8_t>* output) {
+ return HexStringToByteContainer(input, output);
+}
+
+bool HexStringToString(StringPiece input, std::string* output) {
+ return HexStringToByteContainer(input, output);
+}
+
+bool HexStringToSpan(StringPiece input, base::span<uint8_t> output) {
+ size_t count = input.size();
+ if (count == 0 || (count % 2) != 0)
+ return false;
+
+ if (count / 2 != output.size())
+ return false;
+
+ for (uintptr_t i = 0; i < count / 2; ++i) {
+ 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;
+ }
+ output[i] = (msb << 4) | lsb;
+ }
+ return true;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/string_number_conversions.h b/security/sandbox/chromium/base/strings/string_number_conversions.h
new file mode 100644
index 0000000000..87df24e21c
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_number_conversions.h
@@ -0,0 +1,157 @@
+// 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_STRINGS_STRING_NUMBER_CONVERSIONS_H_
+#define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/containers/span.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+// ----------------------------------------------------------------------------
+// IMPORTANT MESSAGE FROM YOUR SPONSOR
+//
+// This file contains no "wstring" variants. New code should use string16. If
+// you need to make old code work, use the UTF8 version and convert. Please do
+// not add wstring variants.
+//
+// Please do not add "convenience" functions for converting strings to integers
+// that return the value and ignore success/failure. That encourages people to
+// write code that doesn't properly handle the error conditions.
+//
+// DO NOT use these functions in any UI unless it's NOT localized on purpose.
+// Instead, use base::MessageFormatter for a complex message with numbers
+// (integer, float, double) embedded or base::Format{Number,Double,Percent} to
+// just format a single number/percent. Note that some languages use native
+// digits instead of ASCII digits while others use a group separator or decimal
+// point different from ',' and '.'. Using these functions in the UI would lead
+// numbers to be formatted in a non-native way.
+// ----------------------------------------------------------------------------
+
+namespace base {
+
+// Number -> string conversions ------------------------------------------------
+
+// Ignores locale! see warning above.
+BASE_EXPORT std::string NumberToString(int value);
+BASE_EXPORT string16 NumberToString16(int value);
+BASE_EXPORT std::string NumberToString(unsigned int value);
+BASE_EXPORT string16 NumberToString16(unsigned int value);
+BASE_EXPORT std::string NumberToString(long value);
+BASE_EXPORT string16 NumberToString16(long value);
+BASE_EXPORT std::string NumberToString(unsigned long value);
+BASE_EXPORT string16 NumberToString16(unsigned long value);
+BASE_EXPORT std::string NumberToString(long long value);
+BASE_EXPORT string16 NumberToString16(long long value);
+BASE_EXPORT std::string NumberToString(unsigned long long value);
+BASE_EXPORT string16 NumberToString16(unsigned long long value);
+BASE_EXPORT std::string NumberToString(double value);
+BASE_EXPORT string16 NumberToString16(double value);
+
+// String -> number conversions ------------------------------------------------
+
+// Perform a best-effort conversion of the input string to a numeric type,
+// setting |*output| to the result of the conversion. Returns true for
+// "perfect" conversions; returns false in the following cases:
+// - Overflow. |*output| will be set to the maximum value supported
+// by the data type.
+// - Underflow. |*output| will be set to the minimum value supported
+// by the data type.
+// - Trailing characters in the string after parsing the number. |*output|
+// will be set to the value of the number that was parsed.
+// - Leading whitespace in the string before parsing the number. |*output| will
+// be set to the value of the number that was parsed.
+// - 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(StringPiece input, int* output);
+BASE_EXPORT bool StringToInt(StringPiece16 input, int* output);
+
+BASE_EXPORT bool StringToUint(StringPiece input, unsigned* output);
+BASE_EXPORT bool StringToUint(StringPiece16 input, unsigned* output);
+
+BASE_EXPORT bool StringToInt64(StringPiece input, int64_t* output);
+BASE_EXPORT bool StringToInt64(StringPiece16 input, int64_t* output);
+
+BASE_EXPORT bool StringToUint64(StringPiece input, uint64_t* output);
+BASE_EXPORT bool StringToUint64(StringPiece16 input, uint64_t* output);
+
+BASE_EXPORT bool StringToSizeT(StringPiece input, size_t* output);
+BASE_EXPORT bool StringToSizeT(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-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(StringPiece input, double* output);
+BASE_EXPORT bool StringToDouble(StringPiece16 input, double* output);
+
+// Hex encoding ----------------------------------------------------------------
+
+// Returns a hex string representation of a binary buffer. The returned hex
+// string will be in upper case. This function does not check if |size| is
+// within reasonable limits since it's written with trusted data in mind. If
+// you suspect that the data you want to format might be large, the absolute
+// max size for |size| should be is
+// std::numeric_limits<size_t>::max() / 2
+BASE_EXPORT std::string HexEncode(const void* bytes, size_t size);
+BASE_EXPORT std::string HexEncode(base::span<const uint8_t> bytes);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
+// -0x80000000 < |input| < 0x7FFFFFFF.
+BASE_EXPORT bool HexStringToInt(StringPiece input, int* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// 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(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(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(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(StringPiece input,
+ std::vector<uint8_t>* output);
+
+// Same as HexStringToBytes, but for an std::string.
+BASE_EXPORT bool HexStringToString(StringPiece input, std::string* output);
+
+// Decodes the hex string |input| into a presized |output|. The output buffer
+// must be sized exactly to |input.size() / 2| or decoding will fail and no
+// bytes will be written to |output|. Decoding an empty input is also
+// considered a failure. When decoding fails due to encountering invalid input
+// characters, |output| will have been filled with the decoded bytes up until
+// the failure.
+BASE_EXPORT bool HexStringToSpan(StringPiece input, base::span<uint8_t> output);
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_NUMBER_CONVERSIONS_H_
diff --git a/security/sandbox/chromium/base/strings/string_piece.cc b/security/sandbox/chromium/base/strings/string_piece.cc
new file mode 100644
index 0000000000..d743144a4e
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_piece.cc
@@ -0,0 +1,426 @@
+// 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.
+// Copied from strings/stringpiece.cc with modifications
+
+#include "base/strings/string_piece.h"
+
+#include <limits.h>
+
+#include <algorithm>
+#include <ostream>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+
+namespace base {
+namespace {
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table. This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char. Thus it should be be declared
+// as follows:
+// bool table[UCHAR_MAX + 1]
+inline void BuildLookupTable(const StringPiece& characters_wanted,
+ bool* table) {
+ const size_t length = characters_wanted.length();
+ const char* const data = characters_wanted.data();
+ for (size_t i = 0; i < length; ++i) {
+ table[static_cast<unsigned char>(data[i])] = true;
+ }
+}
+
+} // namespace
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+template class BasicStringPiece<std::string>;
+template class BasicStringPiece<string16>;
+#endif
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
+ o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
+ return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const StringPiece16& piece) {
+ return o << UTF16ToUTF8(piece);
+}
+
+namespace internal {
+
+template<typename STR>
+void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
+ if (!self.empty())
+ target->append(self.data(), self.size());
+}
+
+void AppendToString(const StringPiece& self, std::string* target) {
+ AppendToStringT(self, target);
+}
+
+void AppendToString(const StringPiece16& self, string16* target) {
+ AppendToStringT(self, target);
+}
+
+template<typename STR>
+size_t copyT(const BasicStringPiece<STR>& self,
+ typename STR::value_type* buf,
+ size_t n,
+ size_t pos) {
+ size_t ret = std::min(self.size() - pos, n);
+ memcpy(buf, self.data() + pos, ret * sizeof(typename STR::value_type));
+ return ret;
+}
+
+size_t copy(const StringPiece& self, char* buf, size_t n, size_t pos) {
+ return copyT(self, buf, n, pos);
+}
+
+size_t copy(const StringPiece16& self, char16* buf, size_t n, size_t pos) {
+ return copyT(self, buf, n, pos);
+}
+
+template<typename STR>
+size_t findT(const BasicStringPiece<STR>& self,
+ const BasicStringPiece<STR>& s,
+ size_t pos) {
+ if (pos > self.size())
+ return BasicStringPiece<STR>::npos;
+
+ typename BasicStringPiece<STR>::const_iterator result =
+ std::search(self.begin() + pos, self.end(), s.begin(), s.end());
+ const size_t xpos =
+ static_cast<size_t>(result - self.begin());
+ return xpos + s.size() <= self.size() ? xpos : BasicStringPiece<STR>::npos;
+}
+
+size_t find(const StringPiece& self, const StringPiece& s, size_t pos) {
+ return findT(self, s, pos);
+}
+
+size_t find(const StringPiece16& self, const StringPiece16& s, size_t pos) {
+ return findT(self, s, pos);
+}
+
+template<typename STR>
+size_t findT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
+ if (pos >= self.size())
+ return BasicStringPiece<STR>::npos;
+
+ typename BasicStringPiece<STR>::const_iterator result =
+ std::find(self.begin() + pos, self.end(), c);
+ return result != self.end() ?
+ static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
+}
+
+size_t find(const StringPiece& self, char c, size_t pos) {
+ return findT(self, c, pos);
+}
+
+size_t find(const StringPiece16& self, char16 c, size_t pos) {
+ return findT(self, c, pos);
+}
+
+template<typename STR>
+size_t rfindT(const BasicStringPiece<STR>& self,
+ const BasicStringPiece<STR>& s,
+ size_t pos) {
+ if (self.size() < s.size())
+ return BasicStringPiece<STR>::npos;
+
+ if (s.empty())
+ return std::min(self.size(), pos);
+
+ typename BasicStringPiece<STR>::const_iterator last =
+ self.begin() + std::min(self.size() - s.size(), pos) + s.size();
+ typename BasicStringPiece<STR>::const_iterator result =
+ std::find_end(self.begin(), last, s.begin(), s.end());
+ return result != last ?
+ static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
+}
+
+size_t rfind(const StringPiece& self, const StringPiece& s, size_t pos) {
+ return rfindT(self, s, pos);
+}
+
+size_t rfind(const StringPiece16& self, const StringPiece16& s, size_t pos) {
+ return rfindT(self, s, pos);
+}
+
+template<typename STR>
+size_t rfindT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
+ if (self.size() == 0)
+ return BasicStringPiece<STR>::npos;
+
+ for (size_t i = std::min(pos, self.size() - 1); ;
+ --i) {
+ if (self.data()[i] == c)
+ return i;
+ if (i == 0)
+ break;
+ }
+ return BasicStringPiece<STR>::npos;
+}
+
+size_t rfind(const StringPiece& self, char c, size_t pos) {
+ return rfindT(self, c, pos);
+}
+
+size_t rfind(const StringPiece16& self, char16 c, size_t pos) {
+ return rfindT(self, c, pos);
+}
+
+// 8-bit version using lookup table.
+size_t find_first_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos) {
+ if (self.size() == 0 || s.size() == 0)
+ return StringPiece::npos;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return find(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (size_t i = pos; i < self.size(); ++i) {
+ if (lookup[static_cast<unsigned char>(self.data()[i])]) {
+ return i;
+ }
+ }
+ return StringPiece::npos;
+}
+
+// 16-bit brute force version.
+size_t find_first_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
+ // Use the faster std::find() if searching for a single character.
+ StringPiece16::const_iterator found =
+ s.size() == 1 ? std::find(self.begin() + pos, self.end(), s[0])
+ : std::find_first_of(self.begin() + pos, self.end(),
+ s.begin(), s.end());
+ if (found == self.end())
+ return StringPiece16::npos;
+ return found - self.begin();
+}
+
+// 8-bit version using lookup table.
+size_t find_first_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ if (s.size() == 0)
+ return 0;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return find_first_not_of(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (size_t i = pos; i < self.size(); ++i) {
+ if (!lookup[static_cast<unsigned char>(self.data()[i])]) {
+ return i;
+ }
+ }
+ return StringPiece::npos;
+}
+
+// 16-bit brute-force version.
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
+ if (self.size() == 0)
+ return StringPiece16::npos;
+
+ for (size_t self_i = pos; self_i < self.size(); ++self_i) {
+ bool found = false;
+ for (auto c : s) {
+ if (self[self_i] == c) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return self_i;
+ }
+ return StringPiece16::npos;
+}
+
+template<typename STR>
+size_t find_first_not_ofT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
+ if (self.size() == 0)
+ return BasicStringPiece<STR>::npos;
+
+ for (; pos < self.size(); ++pos) {
+ if (self.data()[pos] != c) {
+ return pos;
+ }
+ }
+ return BasicStringPiece<STR>::npos;
+}
+
+size_t find_first_not_of(const StringPiece& self,
+ char c,
+ size_t pos) {
+ return find_first_not_ofT(self, c, pos);
+}
+
+size_t find_first_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos) {
+ return find_first_not_ofT(self, c, pos);
+}
+
+// 8-bit version using lookup table.
+size_t find_last_of(const StringPiece& self, const StringPiece& s, size_t pos) {
+ if (self.size() == 0 || s.size() == 0)
+ return StringPiece::npos;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return rfind(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (size_t i = std::min(pos, self.size() - 1); ; --i) {
+ if (lookup[static_cast<unsigned char>(self.data()[i])])
+ return i;
+ if (i == 0)
+ break;
+ }
+ return StringPiece::npos;
+}
+
+// 16-bit brute-force version.
+size_t find_last_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
+ if (self.size() == 0)
+ return StringPiece16::npos;
+
+ for (size_t self_i = std::min(pos, self.size() - 1); ;
+ --self_i) {
+ for (auto c : s) {
+ if (self.data()[self_i] == c)
+ return self_i;
+ }
+ if (self_i == 0)
+ break;
+ }
+ return StringPiece16::npos;
+}
+
+// 8-bit version using lookup table.
+size_t find_last_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ size_t i = std::min(pos, self.size() - 1);
+ if (s.size() == 0)
+ return i;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.size() == 1)
+ return find_last_not_of(self, s.data()[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (; ; --i) {
+ if (!lookup[static_cast<unsigned char>(self.data()[i])])
+ return i;
+ if (i == 0)
+ break;
+ }
+ return StringPiece::npos;
+}
+
+// 16-bit brute-force version.
+size_t find_last_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
+ if (self.size() == 0)
+ return StringPiece::npos;
+
+ for (size_t self_i = std::min(pos, self.size() - 1); ; --self_i) {
+ bool found = false;
+ for (auto c : s) {
+ if (self.data()[self_i] == c) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return self_i;
+ if (self_i == 0)
+ break;
+ }
+ return StringPiece16::npos;
+}
+
+template<typename STR>
+size_t find_last_not_ofT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
+ if (self.size() == 0)
+ return BasicStringPiece<STR>::npos;
+
+ for (size_t i = std::min(pos, self.size() - 1); ; --i) {
+ if (self.data()[i] != c)
+ return i;
+ if (i == 0)
+ break;
+ }
+ return BasicStringPiece<STR>::npos;
+}
+
+size_t find_last_not_of(const StringPiece& self,
+ char c,
+ size_t pos) {
+ return find_last_not_ofT(self, c, pos);
+}
+
+size_t find_last_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos) {
+ return find_last_not_ofT(self, c, pos);
+}
+
+template<typename STR>
+BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self,
+ size_t pos,
+ size_t n) {
+ if (pos > self.size()) pos = self.size();
+ if (n > self.size() - pos) n = self.size() - pos;
+ return BasicStringPiece<STR>(self.data() + pos, n);
+}
+
+StringPiece substr(const StringPiece& self,
+ size_t pos,
+ size_t n) {
+ return substrT(self, pos, n);
+}
+
+StringPiece16 substr(const StringPiece16& self,
+ size_t pos,
+ size_t n) {
+ return substrT(self, pos, n);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/string_piece.h b/security/sandbox/chromium/base/strings/string_piece.h
new file mode 100644
index 0000000000..536bf3f023
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_piece.h
@@ -0,0 +1,513 @@
+// 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.
+// Copied from strings/stringpiece.h with modifications
+//
+// A string-like object that points to a sized piece of memory.
+//
+// You can use StringPiece as a function or method parameter. A StringPiece
+// parameter can receive a double-quoted string literal argument, a "const
+// char*" argument, a string argument, or a StringPiece argument with no data
+// copying. Systematic use of StringPiece for arguments reduces data
+// copies and strlen() calls.
+//
+// Prefer passing StringPieces by value:
+// void MyFunction(StringPiece arg);
+// If circumstances require, you may also pass by const reference:
+// void MyFunction(const StringPiece& arg); // not preferred
+// 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.
+
+#ifndef BASE_STRINGS_STRING_PIECE_H_
+#define BASE_STRINGS_STRING_PIECE_H_
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+#include <type_traits>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/strings/char_traits.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece_forward.h"
+
+namespace base {
+
+// internal --------------------------------------------------------------------
+
+// Many of the StringPiece functions use different implementations for the
+// 8-bit and 16-bit versions, and we don't want lots of template expansions in
+// this (very common) header that will slow down compilation.
+//
+// So here we define overloaded functions called by the StringPiece template.
+// For those that share an implementation, the two versions will expand to a
+// template internal to the .cc file.
+namespace internal {
+
+BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
+BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target);
+
+BASE_EXPORT size_t copy(const StringPiece& self,
+ char* buf,
+ size_t n,
+ size_t pos);
+BASE_EXPORT size_t copy(const StringPiece16& self,
+ char16* buf,
+ size_t n,
+ size_t pos);
+
+BASE_EXPORT size_t find(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t find(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t rfind(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t find_first_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_first_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+
+BASE_EXPORT size_t find_first_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t find_last_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t find_last_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece& self,
+ char c,
+ size_t pos);
+
+BASE_EXPORT StringPiece substr(const StringPiece& self,
+ size_t pos,
+ size_t n);
+BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
+ size_t pos,
+ size_t n);
+
+} // namespace internal
+
+// BasicStringPiece ------------------------------------------------------------
+
+// Defines the types, methods, operators, and data members common to both
+// StringPiece and StringPiece16.
+//
+// This is templatized by string class type rather than character type, so
+// BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
+template <typename STRING_TYPE> class BasicStringPiece {
+ public:
+ // Standard STL container boilerplate.
+ typedef size_t size_type;
+ typedef typename STRING_TYPE::value_type value_type;
+ typedef const value_type* pointer;
+ typedef const value_type& reference;
+ typedef const value_type& const_reference;
+ typedef ptrdiff_t difference_type;
+ typedef const value_type* const_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ static const size_type npos;
+
+ public:
+ // We provide non-explicit singleton constructors so users can pass
+ // in a "const char*" or a "string" wherever a "StringPiece" is
+ // expected (likewise for char16, string16, StringPiece16).
+ constexpr BasicStringPiece() : ptr_(NULL), length_(0) {}
+ // TODO(dcheng): Construction from nullptr is not allowed for
+ // std::basic_string_view, so remove the special handling for it.
+ // Note: This doesn't just use STRING_TYPE::traits_type::length(), since that
+ // isn't constexpr until C++17.
+ constexpr BasicStringPiece(const value_type* str)
+ : ptr_(str), length_(!str ? 0 : CharTraits<value_type>::length(str)) {}
+ BasicStringPiece(const STRING_TYPE& str)
+ : ptr_(str.data()), length_(str.size()) {}
+ constexpr 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) {
+ DCHECK(begin <= end) << "StringPiece iterators swapped or invalid.";
+ length_ = static_cast<size_t>(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
+ // typically a mistake to pass data() to a routine that expects a NUL
+ // terminated string.
+ constexpr const value_type* data() const { return ptr_; }
+ constexpr size_type size() const noexcept { return length_; }
+ constexpr size_type length() const noexcept { return length_; }
+ bool empty() const { return length_ == 0; }
+
+ constexpr value_type operator[](size_type i) const {
+ CHECK(i < length_);
+ return ptr_[i];
+ }
+
+ value_type front() const {
+ CHECK_NE(0UL, length_);
+ return ptr_[0];
+ }
+
+ value_type back() const {
+ CHECK_NE(0UL, length_);
+ return ptr_[length_ - 1];
+ }
+
+ constexpr void remove_prefix(size_type n) {
+ CHECK(n <= length_);
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ constexpr void remove_suffix(size_type n) {
+ CHECK(n <= length_);
+ length_ -= n;
+ }
+
+ constexpr int compare(BasicStringPiece x) const noexcept {
+ int r = CharTraits<value_type>::compare(
+ ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_));
+ if (r == 0) {
+ if (length_ < x.length_) r = -1;
+ else if (length_ > x.length_) r = +1;
+ }
+ return r;
+ }
+
+ // This is the style of conversion preferred by std::string_view in C++17.
+ explicit operator STRING_TYPE() const { return as_string(); }
+
+ STRING_TYPE as_string() const {
+ // std::string doesn't like to take a NULL pointer even with a 0 size.
+ return empty() ? STRING_TYPE() : STRING_TYPE(data(), size());
+ }
+
+ const_iterator begin() const { return ptr_; }
+ const_iterator end() const { return ptr_ + length_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(ptr_ + length_);
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ptr_);
+ }
+
+ size_type max_size() const { return length_; }
+ size_type capacity() const { return length_; }
+
+ void AppendToString(STRING_TYPE* target) const {
+ internal::AppendToString(*this, target);
+ }
+
+ size_type copy(value_type* buf, size_type n, size_type pos = 0) const {
+ return internal::copy(*this, buf, n, pos);
+ }
+
+ // Does "this" start with "x"
+ constexpr bool starts_with(BasicStringPiece x) const noexcept {
+ return (
+ (this->length_ >= x.length_) &&
+ (CharTraits<value_type>::compare(this->ptr_, x.ptr_, x.length_) == 0));
+ }
+
+ // Does "this" end with "x"
+ constexpr bool ends_with(BasicStringPiece x) const noexcept {
+ return ((this->length_ >= x.length_) &&
+ (CharTraits<value_type>::compare(
+ this->ptr_ + (this->length_ - x.length_), x.ptr_, x.length_) ==
+ 0));
+ }
+
+ // find: Search for a character or substring at a given offset.
+ size_type find(const BasicStringPiece<STRING_TYPE>& s,
+ size_type pos = 0) const {
+ return internal::find(*this, s, pos);
+ }
+ size_type find(value_type c, size_type pos = 0) const {
+ return internal::find(*this, c, pos);
+ }
+
+ // rfind: Reverse find.
+ size_type rfind(const BasicStringPiece& s,
+ size_type pos = BasicStringPiece::npos) const {
+ return internal::rfind(*this, s, pos);
+ }
+ size_type rfind(value_type c, size_type pos = BasicStringPiece::npos) const {
+ return internal::rfind(*this, c, pos);
+ }
+
+ // find_first_of: Find the first occurence of one of a set of characters.
+ size_type find_first_of(const BasicStringPiece& s,
+ size_type pos = 0) const {
+ return internal::find_first_of(*this, s, pos);
+ }
+ size_type find_first_of(value_type c, size_type pos = 0) const {
+ return find(c, pos);
+ }
+
+ // find_first_not_of: Find the first occurence not of a set of characters.
+ size_type find_first_not_of(const BasicStringPiece& s,
+ size_type pos = 0) const {
+ return internal::find_first_not_of(*this, s, pos);
+ }
+ size_type find_first_not_of(value_type c, size_type pos = 0) const {
+ return internal::find_first_not_of(*this, c, pos);
+ }
+
+ // find_last_of: Find the last occurence of one of a set of characters.
+ size_type find_last_of(const BasicStringPiece& s,
+ size_type pos = BasicStringPiece::npos) const {
+ return internal::find_last_of(*this, s, pos);
+ }
+ size_type find_last_of(value_type c,
+ size_type pos = BasicStringPiece::npos) const {
+ return rfind(c, pos);
+ }
+
+ // find_last_not_of: Find the last occurence not of a set of characters.
+ size_type find_last_not_of(const BasicStringPiece& s,
+ size_type pos = BasicStringPiece::npos) const {
+ return internal::find_last_not_of(*this, s, pos);
+ }
+ size_type find_last_not_of(value_type c,
+ size_type pos = BasicStringPiece::npos) const {
+ return internal::find_last_not_of(*this, c, pos);
+ }
+
+ // substr.
+ BasicStringPiece substr(size_type pos,
+ size_type n = BasicStringPiece::npos) const {
+ return internal::substr(*this, pos, n);
+ }
+
+ protected:
+ const value_type* ptr_;
+ size_type length_;
+};
+
+template <typename STRING_TYPE>
+const typename BasicStringPiece<STRING_TYPE>::size_type
+BasicStringPiece<STRING_TYPE>::npos =
+ typename BasicStringPiece<STRING_TYPE>::size_type(-1);
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+extern template class BASE_EXPORT BasicStringPiece<std::string>;
+extern template class BASE_EXPORT BasicStringPiece<string16>;
+#endif
+
+// Comparison operators --------------------------------------------------------
+// operator ==
+template <typename StringT>
+constexpr bool operator==(BasicStringPiece<StringT> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+// Here and below we make use of std::common_type_t to emulate an identity type
+// transformation. This creates a non-deduced context, so that we can compare
+// StringPieces with types that implicitly convert to StringPieces. See
+// https://wg21.link/n3766 for details.
+// Furthermore, we require dummy template parameters for these overloads to work
+// around a name mangling issue on Windows.
+template <typename StringT, int = 1>
+constexpr bool operator==(
+ BasicStringPiece<StringT> lhs,
+ std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+ return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator==(std::common_type_t<BasicStringPiece<StringT>> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return lhs.size() == rhs.size() && lhs.compare(rhs) == 0;
+}
+
+// operator !=
+template <typename StringT>
+constexpr bool operator!=(BasicStringPiece<StringT> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator!=(
+ BasicStringPiece<StringT> lhs,
+ std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator!=(std::common_type_t<BasicStringPiece<StringT>> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+// operator <
+template <typename StringT>
+constexpr bool operator<(BasicStringPiece<StringT> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return lhs.compare(rhs) < 0;
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator<(
+ BasicStringPiece<StringT> lhs,
+ std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+ return lhs.compare(rhs) < 0;
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator<(std::common_type_t<BasicStringPiece<StringT>> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return lhs.compare(rhs) < 0;
+}
+
+// operator >
+template <typename StringT>
+constexpr bool operator>(BasicStringPiece<StringT> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return rhs < lhs;
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator>(
+ BasicStringPiece<StringT> lhs,
+ std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+ return rhs < lhs;
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator>(std::common_type_t<BasicStringPiece<StringT>> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return rhs < lhs;
+}
+
+// operator <=
+template <typename StringT>
+constexpr bool operator<=(BasicStringPiece<StringT> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return !(rhs < lhs);
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator<=(
+ BasicStringPiece<StringT> lhs,
+ std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+ return !(rhs < lhs);
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator<=(std::common_type_t<BasicStringPiece<StringT>> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return !(rhs < lhs);
+}
+
+// operator >=
+template <typename StringT>
+constexpr bool operator>=(BasicStringPiece<StringT> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+template <typename StringT, int = 1>
+constexpr bool operator>=(
+ BasicStringPiece<StringT> lhs,
+ std::common_type_t<BasicStringPiece<StringT>> rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+template <typename StringT, int = 2>
+constexpr bool operator>=(std::common_type_t<BasicStringPiece<StringT>> lhs,
+ BasicStringPiece<StringT> rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& o,
+ const StringPiece& piece);
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& o,
+ const StringPiece16& piece);
+
+// Hashing ---------------------------------------------------------------------
+
+// We provide appropriate hash functions so StringPiece and StringPiece16 can
+// be used as keys in hash sets and maps.
+
+// This hash function is copied from base/strings/string16.h. We don't use the
+// ones already defined for string and string16 directly because it would
+// require the string constructors to be called, which we don't want.
+
+template <typename StringPieceType>
+struct StringPieceHashImpl {
+ std::size_t operator()(StringPieceType sp) const {
+ std::size_t result = 0;
+ for (auto c : sp)
+ result = (result * 131) + c;
+ return result;
+ }
+};
+
+using StringPieceHash = StringPieceHashImpl<StringPiece>;
+using StringPiece16Hash = StringPieceHashImpl<StringPiece16>;
+using WStringPieceHash = StringPieceHashImpl<WStringPiece>;
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_PIECE_H_
diff --git a/security/sandbox/chromium/base/strings/string_piece_forward.h b/security/sandbox/chromium/base/strings/string_piece_forward.h
new file mode 100644
index 0000000000..b50b9806c9
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_piece_forward.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Forward declaration of StringPiece types from base/strings/string_piece.h
+
+#ifndef BASE_STRINGS_STRING_PIECE_FORWARD_H_
+#define BASE_STRINGS_STRING_PIECE_FORWARD_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+
+namespace base {
+
+template <typename STRING_TYPE>
+class BasicStringPiece;
+typedef BasicStringPiece<std::string> StringPiece;
+typedef BasicStringPiece<string16> StringPiece16;
+typedef BasicStringPiece<std::wstring> WStringPiece;
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_PIECE_FORWARD_H_
diff --git a/security/sandbox/chromium/base/strings/string_split.cc b/security/sandbox/chromium/base/strings/string_split.cc
new file mode 100644
index 0000000000..42bc5e5b60
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_split.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_split.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base {
+
+namespace {
+
+// Returns either the ASCII or UTF-16 whitespace.
+template<typename Str> BasicStringPiece<Str> WhitespaceForType();
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+template <>
+WStringPiece WhitespaceForType<std::wstring>() {
+ return kWhitespaceWide;
+}
+#endif
+
+template<> StringPiece16 WhitespaceForType<string16>() {
+ return kWhitespaceUTF16;
+}
+template<> StringPiece WhitespaceForType<std::string>() {
+ return kWhitespaceASCII;
+}
+
+// General string splitter template. Can take 8- or 16-bit input, can produce
+// the corresponding string or StringPiece output.
+template <typename OutputStringType, typename Str>
+static std::vector<OutputStringType> SplitStringT(
+ BasicStringPiece<Str> str,
+ BasicStringPiece<Str> delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<OutputStringType> result;
+ if (str.empty())
+ return result;
+
+ size_t start = 0;
+ while (start != Str::npos) {
+ size_t end = str.find_first_of(delimiter, start);
+
+ BasicStringPiece<Str> 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<Str>(), TRIM_ALL);
+
+ if (result_type == SPLIT_WANT_ALL || !piece.empty())
+ result.emplace_back(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 = input.find_first_of(delimiter);
+ if (end_key_pos == std::string::npos) {
+ DVLOG(1) << "cannot find delimiter in: " << input;
+ return false; // No delimiter.
+ }
+ result_pair.first = std::string(input.substr(0, end_key_pos));
+
+ // Find the value string.
+ 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.
+ }
+
+ result_pair.second = std::string(
+ remains.substr(begin_value_pos, remains.size() - begin_value_pos));
+
+ return true;
+}
+
+template <typename OutputStringType, typename Str>
+std::vector<OutputStringType> SplitStringUsingSubstrT(
+ BasicStringPiece<Str> input,
+ BasicStringPiece<Str> delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ using Piece = BasicStringPiece<Str>;
+ using size_type = typename Piece::size_type;
+
+ std::vector<OutputStringType> result;
+ 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);
+
+ if (whitespace == TRIM_WHITESPACE)
+ term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
+
+ if (result_type == SPLIT_WANT_ALL || !term.empty())
+ result.emplace_back(term);
+ }
+
+ return result;
+}
+
+} // namespace
+
+std::vector<std::string> SplitString(StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<std::string>(input, separators, whitespace, result_type);
+}
+
+std::vector<string16> SplitString(StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<string16>(input, separators, whitespace, result_type);
+}
+
+std::vector<StringPiece> SplitStringPiece(StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<StringPiece>(input, separators, whitespace, result_type);
+}
+
+std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<StringPiece16>(input, separators, whitespace,
+ result_type);
+}
+
+bool SplitStringIntoKeyValuePairs(StringPiece input,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs) {
+ return SplitStringIntoKeyValuePairsUsingSubstr(
+ input, key_value_delimiter, StringPiece(&key_value_pair_delimiter, 1),
+ key_value_pairs);
+}
+
+bool SplitStringIntoKeyValuePairsUsingSubstr(
+ StringPiece input,
+ char key_value_delimiter,
+ StringPiece key_value_pair_delimiter,
+ StringPairs* key_value_pairs) {
+ key_value_pairs->clear();
+
+ std::vector<StringPiece> pairs = SplitStringPieceUsingSubstr(
+ input, key_value_pair_delimiter, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ key_value_pairs->reserve(pairs.size());
+
+ bool success = true;
+ 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;
+ }
+ }
+ return success;
+}
+
+std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
+ StringPiece16 delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<string16>(input, delimiter, whitespace,
+ result_type);
+}
+
+std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
+ StringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<std::string>(input, delimiter, whitespace,
+ result_type);
+}
+
+std::vector<StringPiece16> SplitStringPieceUsingSubstr(
+ StringPiece16 input,
+ StringPiece16 delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ std::vector<StringPiece16> result;
+ return SplitStringUsingSubstrT<StringPiece16>(input, delimiter, whitespace,
+ result_type);
+}
+
+std::vector<StringPiece> SplitStringPieceUsingSubstr(
+ StringPiece input,
+ StringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<StringPiece>(input, delimiter, whitespace,
+ result_type);
+}
+
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+std::vector<std::wstring> SplitString(WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<std::wstring>(input, separators, whitespace, result_type);
+}
+
+std::vector<WStringPiece> SplitStringPiece(WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<WStringPiece>(input, separators, whitespace, result_type);
+}
+
+std::vector<std::wstring> SplitStringUsingSubstr(WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<std::wstring>(input, delimiter, whitespace,
+ result_type);
+}
+
+std::vector<WStringPiece> SplitStringPieceUsingSubstr(
+ WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<WStringPiece>(input, delimiter, whitespace,
+ result_type);
+}
+#endif
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/string_split.h b/security/sandbox/chromium/base/strings/string_split.h
new file mode 100644
index 0000000000..efa8b199fe
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_split.h
@@ -0,0 +1,169 @@
+// 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_STRINGS_STRING_SPLIT_H_
+#define BASE_STRINGS_STRING_SPLIT_H_
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+#include "build/build_config.h"
+
+namespace base {
+
+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.
+//
+// Note this is inverse of JoinString() defined in string_util.h.
+//
+// To split on either commas or semicolons, keeping all whitespace:
+//
+// std::vector<std::string> tokens = base::SplitString(
+// input, ", WARN_UNUSED_RESULT;", base::KEEP_WHITESPACE,
+// base::SPLIT_WANT_ALL) WARN_UNUSED_RESULT;
+BASE_EXPORT std::vector<std::string> SplitString(StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type)
+ WARN_UNUSED_RESULT;
+BASE_EXPORT std::vector<string16> SplitString(StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type)
+ WARN_UNUSED_RESULT;
+
+// 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.
+//
+// Note this is inverse of JoinString() defined in string_util.h.
+//
+// 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<StringPiece> SplitStringPiece(
+ StringPiece input,
+ StringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+BASE_EXPORT std::vector<StringPiece16> SplitStringPiece(
+ StringPiece16 input,
+ StringPiece16 separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+
+using StringPairs = std::vector<std::pair<std::string, std::string>>;
+
+// 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(StringPiece input,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs);
+
+// Similar to SplitStringIntoKeyValuePairs, but use a substring
+// |key_value_pair_delimiter| instead of a single char.
+BASE_EXPORT bool SplitStringIntoKeyValuePairsUsingSubstr(
+ StringPiece input,
+ char key_value_delimiter,
+ StringPiece key_value_pair_delimiter,
+ StringPairs* key_value_pairs);
+
+// Similar to SplitString, but use a substring delimiter instead of a list of
+// characters that are all possible delimiters.
+BASE_EXPORT std::vector<string16> SplitStringUsingSubstr(
+ StringPiece16 input,
+ StringPiece16 delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+BASE_EXPORT std::vector<std::string> SplitStringUsingSubstr(
+ StringPiece input,
+ StringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_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<StringPiece16> SplitStringPieceUsingSubstr(
+ StringPiece16 input,
+ StringPiece16 delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
+ StringPiece input,
+ StringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+BASE_EXPORT std::vector<std::wstring> SplitString(WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type)
+ WARN_UNUSED_RESULT;
+
+BASE_EXPORT std::vector<WStringPiece> SplitStringPiece(
+ WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+
+BASE_EXPORT std::vector<std::wstring> SplitStringUsingSubstr(
+ WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+
+BASE_EXPORT std::vector<WStringPiece> SplitStringPieceUsingSubstr(
+ WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) WARN_UNUSED_RESULT;
+#endif
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_SPLIT_H_
diff --git a/security/sandbox/chromium/base/strings/string_util.cc b/security/sandbox/chromium/base/strings/string_util.cc
new file mode 100644
index 0000000000..7e140fae48
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_util.cc
@@ -0,0 +1,1157 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_util.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.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"
+
+namespace base {
+
+namespace {
+
+// Used by ReplaceStringPlaceholders to track the position in the string of
+// replaced parameters.
+struct ReplacementOffset {
+ ReplacementOffset(uintptr_t parameter, size_t offset)
+ : parameter(parameter),
+ offset(offset) {}
+
+ // Index of the parameter.
+ uintptr_t parameter;
+
+ // Starting position in the string.
+ size_t offset;
+};
+
+static bool CompareParameter(const ReplacementOffset& elem1,
+ const ReplacementOffset& elem2) {
+ return elem1.parameter < elem2.parameter;
+}
+
+// Overloaded function to append one string onto the end of another. Having a
+// separate overload for |source| as both string and StringPiece allows for more
+// efficient usage from functions templated to work with either type (avoiding a
+// redundant call to the BasicStringPiece constructor in both cases).
+template <typename string_type>
+inline void AppendToString(string_type* target, const string_type& source) {
+ target->append(source);
+}
+
+template <typename string_type>
+inline void AppendToString(string_type* target,
+ const BasicStringPiece<string_type>& source) {
+ source.AppendToString(target);
+}
+
+// Assuming that a pointer is the size of a "machine word", then
+// uintptr_t is an integer type that is also a machine word.
+using MachineWord = uintptr_t;
+
+inline bool IsMachineWordAligned(const void* pointer) {
+ return !(reinterpret_cast<MachineWord>(pointer) & (sizeof(MachineWord) - 1));
+}
+
+template <typename CharacterType>
+struct NonASCIIMask;
+template <>
+struct NonASCIIMask<char> {
+ static constexpr MachineWord value() {
+ return static_cast<MachineWord>(0x8080808080808080ULL);
+ }
+};
+template <>
+struct NonASCIIMask<char16> {
+ static constexpr MachineWord value() {
+ return static_cast<MachineWord>(0xFF80FF80FF80FF80ULL);
+ }
+};
+#if defined(WCHAR_T_IS_UTF32)
+template <>
+struct NonASCIIMask<wchar_t> {
+ static constexpr MachineWord value() {
+ return static_cast<MachineWord>(0xFFFFFF80FFFFFF80ULL);
+ }
+};
+#endif // WCHAR_T_IS_UTF32
+
+} // namespace
+
+bool IsWprintfFormatPortable(const wchar_t* format) {
+ for (const wchar_t* position = format; *position != '\0'; ++position) {
+ if (*position == '%') {
+ bool in_specification = true;
+ bool modifier_l = false;
+ while (in_specification) {
+ // Eat up characters until reaching a known specifier.
+ if (*++position == '\0') {
+ // The format string ended in the middle of a specification. Call
+ // it portable because no unportable specifications were found. The
+ // string is equally broken on all platforms.
+ return true;
+ }
+
+ if (*position == 'l') {
+ // 'l' is the only thing that can save the 's' and 'c' specifiers.
+ modifier_l = true;
+ } else if (((*position == 's' || *position == 'c') && !modifier_l) ||
+ *position == 'S' || *position == 'C' || *position == 'F' ||
+ *position == 'D' || *position == 'O' || *position == 'U') {
+ // Not portable.
+ return false;
+ }
+
+ if (wcschr(L"diouxXeEfgGaAcspn%", *position)) {
+ // Portable, keep scanning the rest of the format string.
+ in_specification = false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+namespace {
+
+template<typename StringType>
+StringType ToLowerASCIIImpl(BasicStringPiece<StringType> 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<typename StringType>
+StringType ToUpperASCIIImpl(BasicStringPiece<StringType> 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<std::string>(str);
+}
+
+string16 ToLowerASCII(StringPiece16 str) {
+ return ToLowerASCIIImpl<string16>(str);
+}
+
+std::string ToUpperASCII(StringPiece str) {
+ return ToUpperASCIIImpl<std::string>(str);
+}
+
+string16 ToUpperASCII(StringPiece16 str) {
+ return ToUpperASCIIImpl<string16>(str);
+}
+
+template<class StringType>
+int CompareCaseInsensitiveASCIIT(BasicStringPiece<StringType> a,
+ BasicStringPiece<StringType> 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<std::string>(a, b);
+}
+
+int CompareCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
+ return CompareCaseInsensitiveASCIIT<string16>(a, b);
+}
+
+bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b) {
+ if (a.length() != b.length())
+ return false;
+ return CompareCaseInsensitiveASCIIT<std::string>(a, b) == 0;
+}
+
+bool EqualsCaseInsensitiveASCII(StringPiece16 a, StringPiece16 b) {
+ if (a.length() != b.length())
+ return false;
+ return CompareCaseInsensitiveASCIIT<string16>(a, b) == 0;
+}
+
+const std::string& EmptyString() {
+ static const base::NoDestructor<std::string> s;
+ return *s;
+}
+
+const string16& EmptyString16() {
+ static const base::NoDestructor<string16> s16;
+ return *s16;
+}
+
+template <class StringType>
+bool ReplaceCharsT(const StringType& input,
+ BasicStringPiece<StringType> find_any_of_these,
+ BasicStringPiece<StringType> replace_with,
+ StringType* output);
+
+bool ReplaceChars(const string16& input,
+ StringPiece16 replace_chars,
+ StringPiece16 replace_with,
+ string16* output) {
+ return ReplaceCharsT(input, replace_chars, replace_with, output);
+}
+
+bool ReplaceChars(const std::string& input,
+ StringPiece replace_chars,
+ StringPiece replace_with,
+ std::string* output) {
+ return ReplaceCharsT(input, replace_chars, replace_with, output);
+}
+
+bool RemoveChars(const string16& input,
+ StringPiece16 remove_chars,
+ string16* output) {
+ return ReplaceCharsT(input, remove_chars, StringPiece16(), output);
+}
+
+bool RemoveChars(const std::string& input,
+ StringPiece remove_chars,
+ std::string* output) {
+ return ReplaceCharsT(input, remove_chars, StringPiece(), output);
+}
+
+template <typename Str>
+TrimPositions TrimStringT(BasicStringPiece<Str> input,
+ BasicStringPiece<Str> trim_chars,
+ TrimPositions positions,
+ 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).
+ 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;
+ const size_t last_good_char = (positions & TRIM_TRAILING)
+ ? input.find_last_not_of(trim_chars)
+ : last_char;
+
+ // 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) {
+ bool input_was_empty = input.empty(); // in case output == &input
+ output->clear();
+ return input_was_empty ? TRIM_NONE : positions;
+ }
+
+ // Trim.
+ output->assign(input.data() + first_good_char,
+ last_good_char - first_good_char + 1);
+
+ // Return where we trimmed from.
+ return static_cast<TrimPositions>(
+ (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) |
+ (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING));
+}
+
+bool TrimString(StringPiece16 input,
+ StringPiece16 trim_chars,
+ string16* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+bool TrimString(StringPiece input,
+ StringPiece trim_chars,
+ std::string* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+template<typename Str>
+BasicStringPiece<Str> TrimStringPieceT(BasicStringPiece<Str> input,
+ BasicStringPiece<Str> 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,
+ StringPiece16 trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+StringPiece TrimString(StringPiece input,
+ StringPiece trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+void TruncateUTF8ToByteSize(const std::string& input,
+ const size_t byte_size,
+ std::string* output) {
+ DCHECK(output);
+ if (byte_size > input.length()) {
+ *output = input;
+ return;
+ }
+ DCHECK_LE(byte_size,
+ static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
+ // Note: This cast is necessary because CBU8_NEXT uses int32_ts.
+ int32_t truncation_length = static_cast<int32_t>(byte_size);
+ int32_t char_index = truncation_length - 1;
+ const char* data = input.data();
+
+ // Using CBU8, we will move backwards from the truncation point
+ // to the beginning of the string looking for a valid UTF8
+ // character. Once a full UTF8 character is found, we will
+ // truncate the string to the end of that character.
+ while (char_index >= 0) {
+ int32_t prev = char_index;
+ base_icu::UChar32 code_point = 0;
+ CBU8_NEXT(data, char_index, truncation_length, code_point);
+ if (!IsValidCharacter(code_point) ||
+ !IsValidCodepoint(code_point)) {
+ char_index = prev - 1;
+ } else {
+ break;
+ }
+ }
+
+ if (char_index >= 0 )
+ *output = input.substr(0, char_index);
+ else
+ output->clear();
+}
+
+TrimPositions TrimWhitespace(StringPiece16 input,
+ TrimPositions positions,
+ string16* output) {
+ return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
+}
+
+StringPiece16 TrimWhitespace(StringPiece16 input,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
+}
+
+TrimPositions TrimWhitespaceASCII(StringPiece input,
+ TrimPositions positions,
+ std::string* output) {
+ return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
+}
+
+StringPiece TrimWhitespaceASCII(StringPiece input, TrimPositions positions) {
+ return TrimStringPieceT(input, StringPiece(kWhitespaceASCII), positions);
+}
+
+template<typename STR>
+STR CollapseWhitespaceT(const STR& text,
+ bool trim_sequences_with_line_breaks) {
+ STR result;
+ result.resize(text.size());
+
+ // Set flags to pretend we're already in a trimmed whitespace sequence, so we
+ // will trim any leading whitespace.
+ bool in_whitespace = true;
+ bool already_trimmed = true;
+
+ int chars_written = 0;
+ for (typename STR::const_iterator i(text.begin()); i != text.end(); ++i) {
+ if (IsUnicodeWhitespace(*i)) {
+ if (!in_whitespace) {
+ // Reduce all whitespace sequences to a single space.
+ in_whitespace = true;
+ result[chars_written++] = L' ';
+ }
+ if (trim_sequences_with_line_breaks && !already_trimmed &&
+ ((*i == '\n') || (*i == '\r'))) {
+ // Whitespace sequences containing CR or LF are eliminated entirely.
+ already_trimmed = true;
+ --chars_written;
+ }
+ } else {
+ // Non-whitespace chracters are copied straight across.
+ in_whitespace = false;
+ already_trimmed = false;
+ result[chars_written++] = *i;
+ }
+ }
+
+ if (in_whitespace && !already_trimmed) {
+ // Any trailing whitespace is eliminated.
+ --chars_written;
+ }
+
+ result.resize(chars_written);
+ return result;
+}
+
+string16 CollapseWhitespace(const string16& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+std::string CollapseWhitespaceASCII(const std::string& text,
+ bool trim_sequences_with_line_breaks) {
+ return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
+}
+
+bool ContainsOnlyChars(StringPiece input, StringPiece characters) {
+ return input.find_first_not_of(characters) == StringPiece::npos;
+}
+
+bool ContainsOnlyChars(StringPiece16 input, StringPiece16 characters) {
+ return input.find_first_not_of(characters) == StringPiece16::npos;
+}
+
+template <class Char>
+inline bool DoIsStringASCII(const Char* characters, size_t length) {
+ if (!length)
+ return true;
+ constexpr MachineWord non_ascii_bit_mask = NonASCIIMask<Char>::value();
+ MachineWord all_char_bits = 0;
+ const Char* end = characters + length;
+
+ // Prologue: align the input.
+ while (!IsMachineWordAligned(characters) && characters < end)
+ all_char_bits |= *characters++;
+ if (all_char_bits & non_ascii_bit_mask)
+ return false;
+
+ // Compare the values of CPU word size.
+ constexpr size_t chars_per_word = sizeof(MachineWord) / sizeof(Char);
+ constexpr int batch_count = 16;
+ while (characters <= end - batch_count * chars_per_word) {
+ all_char_bits = 0;
+ for (int i = 0; i < batch_count; ++i) {
+ all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
+ characters += chars_per_word;
+ }
+ if (all_char_bits & non_ascii_bit_mask)
+ return false;
+ }
+
+ // Process the remaining words.
+ all_char_bits = 0;
+ while (characters <= end - chars_per_word) {
+ all_char_bits |= *(reinterpret_cast<const MachineWord*>(characters));
+ characters += chars_per_word;
+ }
+
+ // Process the remaining bytes.
+ while (characters < end)
+ all_char_bits |= *characters++;
+
+ return !(all_char_bits & non_ascii_bit_mask);
+}
+
+bool IsStringASCII(StringPiece str) {
+ return DoIsStringASCII(str.data(), str.length());
+}
+
+bool IsStringASCII(StringPiece16 str) {
+ return DoIsStringASCII(str.data(), str.length());
+}
+
+#if defined(WCHAR_T_IS_UTF32)
+bool IsStringASCII(WStringPiece str) {
+ return DoIsStringASCII(str.data(), str.length());
+}
+#endif
+
+template <bool (*Validator)(uint32_t)>
+inline static bool DoIsStringUTF8(StringPiece str) {
+ const char* src = str.data();
+ int32_t src_len = static_cast<int32_t>(str.length());
+ int32_t char_index = 0;
+
+ while (char_index < src_len) {
+ int32_t code_point;
+ CBU8_NEXT(src, char_index, src_len, code_point);
+ if (!Validator(code_point))
+ return false;
+ }
+ return true;
+}
+
+bool IsStringUTF8(StringPiece str) {
+ return DoIsStringUTF8<IsValidCharacter>(str);
+}
+
+bool IsStringUTF8AllowingNoncharacters(StringPiece str) {
+ return DoIsStringUTF8<IsValidCodepoint>(str);
+}
+
+// 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<typename Str>
+static inline bool DoLowerCaseEqualsASCII(BasicStringPiece<Str> 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 true;
+}
+
+bool LowerCaseEqualsASCII(StringPiece str, StringPiece lowercase_ascii) {
+ return DoLowerCaseEqualsASCII<std::string>(str, lowercase_ascii);
+}
+
+bool LowerCaseEqualsASCII(StringPiece16 str, StringPiece lowercase_ascii) {
+ return DoLowerCaseEqualsASCII<string16>(str, lowercase_ascii);
+}
+
+bool EqualsASCII(StringPiece16 str, StringPiece ascii) {
+ if (str.length() != ascii.length())
+ return false;
+ return std::equal(ascii.begin(), ascii.end(), str.begin());
+}
+
+template<typename Str>
+bool StartsWithT(BasicStringPiece<Str> str,
+ BasicStringPiece<Str> search_for,
+ CompareCase case_sensitivity) {
+ if (search_for.size() > str.size())
+ return false;
+
+ BasicStringPiece<Str> 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<typename Str::value_type>());
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool StartsWith(StringPiece str,
+ StringPiece search_for,
+ CompareCase case_sensitivity) {
+ return StartsWithT<std::string>(str, search_for, case_sensitivity);
+}
+
+bool StartsWith(StringPiece16 str,
+ StringPiece16 search_for,
+ CompareCase case_sensitivity) {
+ return StartsWithT<string16>(str, search_for, case_sensitivity);
+}
+
+template <typename Str>
+bool EndsWithT(BasicStringPiece<Str> str,
+ BasicStringPiece<Str> search_for,
+ CompareCase case_sensitivity) {
+ if (search_for.size() > str.size())
+ return false;
+
+ BasicStringPiece<Str> 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<typename Str::value_type>());
+
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool EndsWith(StringPiece str,
+ StringPiece search_for,
+ CompareCase case_sensitivity) {
+ return EndsWithT<std::string>(str, search_for, case_sensitivity);
+}
+
+bool EndsWith(StringPiece16 str,
+ StringPiece16 search_for,
+ CompareCase case_sensitivity) {
+ return EndsWithT<string16>(str, search_for, case_sensitivity);
+}
+
+char HexDigitToInt(wchar_t c) {
+ DCHECK(IsHexDigit(c));
+ if (c >= '0' && c <= '9')
+ return static_cast<char>(c - '0');
+ if (c >= 'A' && c <= 'F')
+ return static_cast<char>(c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return static_cast<char>(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[] = {
+ " B",
+ " kB",
+ " MB",
+ " GB",
+ " TB",
+ " PB"
+};
+
+string16 FormatBytesUnlocalized(int64_t bytes) {
+ double unit_amount = static_cast<double>(bytes);
+ size_t dimension = 0;
+ const int kKilo = 1024;
+ while (unit_amount >= kKilo &&
+ dimension < base::size(kByteStringsUnlocalized) - 1) {
+ unit_amount /= kKilo;
+ dimension++;
+ }
+
+ char buf[64];
+ if (bytes != 0 && dimension > 0 && unit_amount < 100) {
+ base::snprintf(buf, base::size(buf), "%.1lf%s", unit_amount,
+ kByteStringsUnlocalized[dimension]);
+ } else {
+ base::snprintf(buf, base::size(buf), "%.0lf%s", unit_amount,
+ kByteStringsUnlocalized[dimension]);
+ }
+
+ return ASCIIToUTF16(buf);
+}
+
+// A Matcher for DoReplaceMatchesAfterOffset() that matches substrings.
+template <class StringType>
+struct SubstringMatcher {
+ BasicStringPiece<StringType> find_this;
+
+ size_t Find(const StringType& input, size_t pos) {
+ return input.find(find_this.data(), pos, find_this.length());
+ }
+ size_t MatchSize() { return find_this.length(); }
+};
+
+// A Matcher for DoReplaceMatchesAfterOffset() that matches single characters.
+template <class StringType>
+struct CharacterMatcher {
+ BasicStringPiece<StringType> find_any_of_these;
+
+ size_t Find(const StringType& input, size_t pos) {
+ return input.find_first_of(find_any_of_these.data(), pos,
+ find_any_of_these.length());
+ }
+ constexpr size_t MatchSize() { return 1; }
+};
+
+enum class ReplaceType { REPLACE_ALL, REPLACE_FIRST };
+
+// Runs in O(n) time in the length of |str|, and transforms the string without
+// reallocating when possible. Returns |true| if any matches were found.
+//
+// This is parameterized on a |Matcher| traits type, so that it can be the
+// implementation for both ReplaceChars() and ReplaceSubstringsAfterOffset().
+template <class StringType, class Matcher>
+bool DoReplaceMatchesAfterOffset(StringType* str,
+ size_t initial_offset,
+ Matcher matcher,
+ BasicStringPiece<StringType> replace_with,
+ ReplaceType replace_type) {
+ using CharTraits = typename StringType::traits_type;
+
+ const size_t find_length = matcher.MatchSize();
+ if (!find_length)
+ return false;
+
+ // If the find string doesn't appear, there's nothing to do.
+ size_t first_match = matcher.Find(*str, initial_offset);
+ if (first_match == StringType::npos)
+ return false;
+
+ // If we're only replacing one instance, there's no need to do anything
+ // complicated.
+ const size_t replace_length = replace_with.length();
+ if (replace_type == ReplaceType::REPLACE_FIRST) {
+ str->replace(first_match, find_length, replace_with.data(), replace_length);
+ return true;
+ }
+
+ // 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.
+ if (find_length == replace_length) {
+ auto* buffer = &((*str)[0]);
+ for (size_t offset = first_match; offset != StringType::npos;
+ offset = matcher.Find(*str, offset + replace_length)) {
+ CharTraits::copy(buffer + offset, replace_with.data(), replace_length);
+ }
+ return true;
+ }
+
+ // 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).
+ //
+ // When the string is being shortened, it's possible to just shift the matches
+ // down in one pass while finding, and truncate the length at the end of the
+ // search.
+ //
+ // If the string is being lengthened, more work is required. The strategy used
+ // here is to make two find() passes through the string. The first pass counts
+ // the number of matches to determine the new size. The second pass will
+ // either construct the new string into a new buffer (if the existing buffer
+ // lacked capacity), or else -- if there is room -- create a region of scratch
+ // space after |first_match| by shifting the tail of the string to a higher
+ // index, and doing in-place moves from the tail to lower indices thereafter.
+ size_t str_length = str->length();
+ size_t expansion = 0;
+ if (replace_length > find_length) {
+ // This operation lengthens the string; determine the new length by counting
+ // matches.
+ const size_t expansion_per_match = (replace_length - find_length);
+ size_t num_matches = 0;
+ for (size_t match = first_match; match != StringType::npos;
+ match = matcher.Find(*str, match + find_length)) {
+ expansion += expansion_per_match;
+ ++num_matches;
+ }
+ const size_t final_length = str_length + expansion;
+
+ if (str->capacity() < final_length) {
+ // If we'd have to allocate a new buffer to grow the string, build the
+ // result directly into the new allocation via append().
+ StringType src(str->get_allocator());
+ str->swap(src);
+ str->reserve(final_length);
+
+ size_t pos = 0;
+ for (size_t match = first_match;; match = matcher.Find(src, pos)) {
+ str->append(src, pos, match - pos);
+ str->append(replace_with.data(), replace_length);
+ pos = match + find_length;
+
+ // A mid-loop test/break enables skipping the final Find() call; the
+ // number of matches is known, so don't search past the last one.
+ if (!--num_matches)
+ break;
+ }
+
+ // Handle substring after the final match.
+ str->append(src, pos, str_length - pos);
+ return true;
+ }
+
+ // Prepare for the copy/move loop below -- expand the string to its final
+ // size by shifting the data after the first match to the end of the resized
+ // string.
+ size_t shift_src = first_match + find_length;
+ size_t shift_dst = shift_src + expansion;
+
+ // Big |expansion| factors (relative to |str_length|) require padding up to
+ // |shift_dst|.
+ if (shift_dst > str_length)
+ str->resize(shift_dst);
+
+ str->replace(shift_dst, str_length - shift_src, *str, shift_src,
+ str_length - shift_src);
+ str_length = final_length;
+ }
+
+ // We can alternate replacement and move operations. This won't overwrite the
+ // unsearched region of the string so long as |write_offset| <= |read_offset|;
+ // that condition is always satisfied because:
+ //
+ // (a) If the string is being shortened, |expansion| is zero and
+ // |write_offset| grows slower than |read_offset|.
+ //
+ // (b) If the string is being lengthened, |write_offset| grows faster than
+ // |read_offset|, but |expansion| is big enough so that |write_offset|
+ // will only catch up to |read_offset| at the point of the last match.
+ auto* buffer = &((*str)[0]);
+ size_t write_offset = first_match;
+ size_t read_offset = first_match + expansion;
+ do {
+ if (replace_length) {
+ CharTraits::copy(buffer + write_offset, replace_with.data(),
+ replace_length);
+ write_offset += replace_length;
+ }
+ read_offset += find_length;
+
+ // min() clamps StringType::npos (the largest unsigned value) to str_length.
+ size_t match = std::min(matcher.Find(*str, read_offset), str_length);
+
+ size_t length = match - read_offset;
+ if (length) {
+ CharTraits::move(buffer + write_offset, buffer + read_offset, length);
+ write_offset += length;
+ read_offset += length;
+ }
+ } while (read_offset < str_length);
+
+ // If we're shortening the string, truncate it now.
+ str->resize(write_offset);
+ return true;
+}
+
+template <class StringType>
+bool ReplaceCharsT(const StringType& input,
+ BasicStringPiece<StringType> find_any_of_these,
+ BasicStringPiece<StringType> replace_with,
+ StringType* output) {
+ // Commonly, this is called with output and input being the same string; in
+ // that case, this assignment is inexpensive.
+ *output = input;
+
+ return DoReplaceMatchesAfterOffset(
+ output, 0, CharacterMatcher<StringType>{find_any_of_these}, replace_with,
+ ReplaceType::REPLACE_ALL);
+}
+
+void ReplaceFirstSubstringAfterOffset(string16* str,
+ size_t start_offset,
+ StringPiece16 find_this,
+ StringPiece16 replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<string16>{find_this},
+ replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceFirstSubstringAfterOffset(std::string* str,
+ size_t start_offset,
+ StringPiece find_this,
+ StringPiece replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<std::string>{find_this},
+ replace_with, ReplaceType::REPLACE_FIRST);
+}
+
+void ReplaceSubstringsAfterOffset(string16* str,
+ size_t start_offset,
+ StringPiece16 find_this,
+ StringPiece16 replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<string16>{find_this},
+ replace_with, ReplaceType::REPLACE_ALL);
+}
+
+void ReplaceSubstringsAfterOffset(std::string* str,
+ size_t start_offset,
+ StringPiece find_this,
+ StringPiece replace_with) {
+ DoReplaceMatchesAfterOffset(str, start_offset,
+ SubstringMatcher<std::string>{find_this},
+ replace_with, ReplaceType::REPLACE_ALL);
+}
+
+template <class string_type>
+inline typename string_type::value_type* WriteIntoT(string_type* str,
+ size_t length_with_null) {
+ DCHECK_GE(length_with_null, 1u);
+ str->reserve(length_with_null);
+ str->resize(length_with_null - 1);
+ return &((*str)[0]);
+}
+
+char* WriteInto(std::string* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
+}
+
+char16* WriteInto(string16* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
+}
+
+#if defined(_MSC_VER) && !defined(__clang__)
+// Work around VC++ code-gen bug. https://crbug.com/804884
+#pragma optimize("", off)
+#endif
+
+// Generic version for all JoinString overloads. |list_type| must be a sequence
+// (std::vector or std::initializer_list) of strings/StringPieces (std::string,
+// string16, StringPiece or StringPiece16). |string_type| is either std::string
+// or string16.
+template <typename list_type, typename string_type>
+static string_type JoinStringT(const list_type& parts,
+ BasicStringPiece<string_type> sep) {
+ if (parts.size() == 0)
+ return string_type();
+
+ // Pre-allocate the eventual size of the string. Start with the size of all of
+ // the separators (note that this *assumes* parts.size() > 0).
+ size_t total_size = (parts.size() - 1) * sep.size();
+ for (const auto& part : parts)
+ total_size += part.size();
+ string_type result;
+ result.reserve(total_size);
+
+ auto iter = parts.begin();
+ DCHECK(iter != parts.end());
+ AppendToString(&result, *iter);
+ ++iter;
+
+ for (; iter != parts.end(); ++iter) {
+ sep.AppendToString(&result);
+ // Using the overloaded AppendToString allows this template function to work
+ // on both strings and StringPieces without creating an intermediate
+ // StringPiece object.
+ AppendToString(&result, *iter);
+ }
+
+ // Sanity-check that we pre-allocated correctly.
+ DCHECK_EQ(total_size, result.size());
+
+ return result;
+}
+
+std::string JoinString(const std::vector<std::string>& parts,
+ StringPiece separator) {
+ return JoinStringT(parts, separator);
+}
+
+string16 JoinString(const std::vector<string16>& parts,
+ StringPiece16 separator) {
+ return JoinStringT(parts, separator);
+}
+
+#if defined(_MSC_VER) && !defined(__clang__)
+// Work around VC++ code-gen bug. https://crbug.com/804884
+#pragma optimize("", on)
+#endif
+
+std::string JoinString(const std::vector<StringPiece>& parts,
+ StringPiece separator) {
+ return JoinStringT(parts, separator);
+}
+
+string16 JoinString(const std::vector<StringPiece16>& parts,
+ StringPiece16 separator) {
+ return JoinStringT(parts, separator);
+}
+
+std::string JoinString(std::initializer_list<StringPiece> parts,
+ StringPiece separator) {
+ return JoinStringT(parts, separator);
+}
+
+string16 JoinString(std::initializer_list<StringPiece16> parts,
+ StringPiece16 separator) {
+ return JoinStringT(parts, separator);
+}
+
+template<class FormatStringType, class OutStringType>
+OutStringType DoReplaceStringPlaceholders(
+ const FormatStringType& format_string,
+ const std::vector<OutStringType>& subst,
+ std::vector<size_t>* offsets) {
+ size_t substitutions = subst.size();
+ DCHECK_LT(substitutions, 10U);
+
+ size_t sub_length = 0;
+ for (const auto& cur : subst)
+ sub_length += cur.length();
+
+ OutStringType formatted;
+ formatted.reserve(format_string.length() + sub_length);
+
+ std::vector<ReplacementOffset> r_offsets;
+ for (auto i = format_string.begin(); i != format_string.end(); ++i) {
+ if ('$' == *i) {
+ if (i + 1 != format_string.end()) {
+ ++i;
+ if ('$' == *i) {
+ while (i != format_string.end() && '$' == *i) {
+ formatted.push_back('$');
+ ++i;
+ }
+ --i;
+ } else {
+ if (*i < '1' || *i > '9') {
+ DLOG(ERROR) << "Invalid placeholder: $" << *i;
+ continue;
+ }
+ uintptr_t index = *i - '1';
+ if (offsets) {
+ ReplacementOffset r_offset(index,
+ static_cast<int>(formatted.size()));
+ r_offsets.insert(
+ std::upper_bound(r_offsets.begin(), r_offsets.end(), r_offset,
+ &CompareParameter),
+ r_offset);
+ }
+ if (index < substitutions)
+ formatted.append(subst.at(index));
+ }
+ }
+ } else {
+ formatted.push_back(*i);
+ }
+ }
+ if (offsets) {
+ for (const auto& cur : r_offsets)
+ offsets->push_back(cur.offset);
+ }
+ return formatted;
+}
+
+string16 ReplaceStringPlaceholders(const string16& format_string,
+ const std::vector<string16>& subst,
+ std::vector<size_t>* offsets) {
+ return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+std::string ReplaceStringPlaceholders(StringPiece format_string,
+ const std::vector<std::string>& subst,
+ std::vector<size_t>* offsets) {
+ return DoReplaceStringPlaceholders(format_string, subst, offsets);
+}
+
+string16 ReplaceStringPlaceholders(const string16& format_string,
+ const string16& a,
+ size_t* offset) {
+ std::vector<size_t> offsets;
+ std::vector<string16> subst;
+ subst.push_back(a);
+ string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
+
+ DCHECK_EQ(1U, offsets.size());
+ if (offset)
+ *offset = offsets[0];
+ return result;
+}
+
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+
+TrimPositions TrimWhitespace(WStringPiece input,
+ TrimPositions positions,
+ std::wstring* output) {
+ return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output);
+}
+
+WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) {
+ return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions);
+}
+
+bool TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ std::wstring* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+WStringPiece TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+wchar_t* WriteInto(std::wstring* str, size_t length_with_null) {
+ return WriteIntoT(str, length_with_null);
+}
+
+#endif
+
+// 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
+
+namespace {
+
+template <typename CHAR>
+size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) {
+ for (size_t i = 0; i < dst_size; ++i) {
+ if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL.
+ return i;
+ }
+
+ // We were left off at dst_size. We over copied 1 byte. Null terminate.
+ if (dst_size != 0)
+ dst[dst_size - 1] = 0;
+
+ // Count the rest of the |src|, and return it's length in characters.
+ while (src[dst_size]) ++dst_size;
+ return dst_size;
+}
+
+} // namespace
+
+size_t strlcpy(char* dst, const char* src, size_t dst_size) {
+ return lcpyT<char>(dst, src, dst_size);
+}
+size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
+ return lcpyT<wchar_t>(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
new file mode 100644
index 0000000000..f9f5e10ade
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_util.h
@@ -0,0 +1,568 @@
+// 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 defines utility functions for working with strings.
+
+#ifndef BASE_STRINGS_STRING_UTIL_H_
+#define BASE_STRINGS_STRING_UTIL_H_
+
+#include <ctype.h>
+#include <stdarg.h> // va_list
+#include <stddef.h>
+#include <stdint.h>
+
+#include <initializer_list>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/stl_util.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 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
+// string, even when truncation occurs.
+int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments)
+ PRINTF_FORMAT(3, 0);
+
+// Some of these implementations need to be inlined.
+
+// 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, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ int result = vsnprintf(buffer, size, format, arguments);
+ va_end(arguments);
+ return result;
+}
+
+// BSD-style safe and consistent string copy functions.
+// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
+// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
+// long as |dst_size| is not 0. Returns the length of |src| in characters.
+// If the return value is >= dst_size, then the output was truncated.
+// NOTE: All sizes are in number of characters, NOT in bytes.
+BASE_EXPORT size_t strlcpy(char* dst, const char* src, size_t dst_size);
+BASE_EXPORT size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size);
+
+// Scan a wprintf format string to determine whether it's portable across a
+// variety of systems. This function only checks that the conversion
+// specifiers used by the format string are supported and have the same meaning
+// on a variety of systems. It doesn't check for other errors that might occur
+// within a format string.
+//
+// Nonportable conversion specifiers for wprintf are:
+// - 's' and 'c' without an 'l' length modifier. %s and %c operate on char
+// data on all systems except Windows, which treat them as wchar_t data.
+// Use %ls and %lc for wchar_t data instead.
+// - 'S' and 'C', which operate on wchar_t data on all systems except Windows,
+// which treat them as char data. Use %ls and %lc for wchar_t data
+// instead.
+// - 'F', which is not identified by Windows wprintf documentation.
+// - 'D', 'O', and 'U', which are deprecated and not available on all systems.
+// Use %ld, %lo, and %lu instead.
+//
+// Note that there is no portable conversion specifier for char data when
+// working with wprintf.
+//
+// This function is intended to be called from base::vswprintf.
+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.
+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.
+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;
+}
+
+// Converts the given string to it's ASCII-lowercase equivalent.
+BASE_EXPORT std::string ToLowerASCII(StringPiece str);
+BASE_EXPORT string16 ToLowerASCII(StringPiece16 str);
+
+// 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<typename Char> struct CaseInsensitiveCompareASCII {
+ public:
+ bool operator()(Char x, Char y) const {
+ return ToLowerASCII(x) == ToLowerASCII(y);
+ }
+};
+
+// 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.
+//
+// It is likely faster to construct a new empty string object (just a few
+// instructions to set the length to 0) than to get the empty string instance
+// returned by these functions (which requires threadsafe static access).
+//
+// Therefore, DO NOT USE THESE AS A GENERAL-PURPOSE SUBSTITUTE FOR DEFAULT
+// CONSTRUCTORS. There is only one case where you should use these: functions
+// which need to return a string by reference (e.g. as a class member
+// accessor), and don't have an empty string to use (e.g. in an error case).
+// These should not be used as initializers, function arguments, or return
+// values for functions which return by value or outparam.
+BASE_EXPORT const std::string& EmptyString();
+BASE_EXPORT const string16& EmptyString16();
+
+// Contains the set of characters representing whitespace in the corresponding
+// 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 char16 kWhitespaceNoCrLfUTF16[]; // Unicode w/o CR/LF.
+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[];
+
+// Removes characters in |remove_chars| from anywhere in |input|. Returns true
+// 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,
+ StringPiece16 remove_chars,
+ string16* output);
+BASE_EXPORT bool RemoveChars(const std::string& input,
+ StringPiece remove_chars,
+ std::string* output);
+
+// Replaces characters in |replace_chars| from anywhere in |input| with
+// |replace_with|. Each character in |replace_chars| will be replaced with
+// the |replace_with| string. Returns true if any characters were replaced.
+// |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,
+ StringPiece16 replace_chars,
+ StringPiece16 replace_with,
+ string16* output);
+BASE_EXPORT bool ReplaceChars(const std::string& input,
+ StringPiece replace_chars,
+ StringPiece 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|.
+// The 8-bit version only works on 8-bit characters, not UTF-8. Returns true if
+// any characters were removed.
+//
+// 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(StringPiece16 input,
+ StringPiece16 trim_chars,
+ string16* output);
+BASE_EXPORT bool TrimString(StringPiece input,
+ 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,
+ StringPiece16 trim_chars,
+ TrimPositions positions);
+BASE_EXPORT StringPiece TrimString(StringPiece input,
+ 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);
+
+#if defined(WCHAR_T_IS_UTF16)
+// Utility functions to access the underlying string buffer as a wide char
+// pointer.
+//
+// Note: These functions violate strict aliasing when char16 and wchar_t are
+// unrelated types. We thus pass -fno-strict-aliasing to the compiler on
+// non-Windows platforms [1], and rely on it being off in Clang's CL mode [2].
+//
+// [1] https://crrev.com/b9a0976622/build/config/compiler/BUILD.gn#244
+// [2]
+// https://github.com/llvm/llvm-project/blob/1e28a66/clang/lib/Driver/ToolChains/Clang.cpp#L3949
+inline wchar_t* as_writable_wcstr(char16* str) {
+ return reinterpret_cast<wchar_t*>(str);
+}
+
+inline wchar_t* as_writable_wcstr(string16& str) {
+ return reinterpret_cast<wchar_t*>(data(str));
+}
+
+inline const wchar_t* as_wcstr(const char16* str) {
+ return reinterpret_cast<const wchar_t*>(str);
+}
+
+inline const wchar_t* as_wcstr(StringPiece16 str) {
+ return reinterpret_cast<const wchar_t*>(str.data());
+}
+
+// Utility functions to access the underlying string buffer as a char16 pointer.
+inline char16* as_writable_u16cstr(wchar_t* str) {
+ return reinterpret_cast<char16*>(str);
+}
+
+inline char16* as_writable_u16cstr(std::wstring& str) {
+ return reinterpret_cast<char16*>(data(str));
+}
+
+inline const char16* as_u16cstr(const wchar_t* str) {
+ return reinterpret_cast<const char16*>(str);
+}
+
+inline const char16* as_u16cstr(WStringPiece str) {
+ return reinterpret_cast<const char16*>(str.data());
+}
+
+// Utility functions to convert between base::WStringPiece and
+// base::StringPiece16.
+inline WStringPiece AsWStringPiece(StringPiece16 str) {
+ return WStringPiece(as_wcstr(str.data()), str.size());
+}
+
+inline StringPiece16 AsStringPiece16(WStringPiece str) {
+ return StringPiece16(as_u16cstr(str.data()), str.size());
+}
+
+inline std::wstring AsWString(StringPiece16 str) {
+ return std::wstring(as_wcstr(str.data()), str.size());
+}
+
+inline string16 AsString16(WStringPiece str) {
+ return string16(as_u16cstr(str.data()), str.size());
+}
+#endif // defined(WCHAR_T_IS_UTF16)
+
+// 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.
+BASE_EXPORT TrimPositions TrimWhitespace(StringPiece16 input,
+ TrimPositions positions,
+ string16* output);
+BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
+ TrimPositions positions);
+BASE_EXPORT TrimPositions TrimWhitespaceASCII(StringPiece 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
+// copied from terminals.
+// Returns |text|, with the following three transformations:
+// (1) Leading and trailing whitespace is trimmed.
+// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
+// sequences containing a CR or LF are trimmed.
+// (3) All other whitespace sequences are converted to single spaces.
+BASE_EXPORT string16 CollapseWhitespace(
+ const string16& text,
+ bool trim_sequences_with_line_breaks);
+BASE_EXPORT std::string CollapseWhitespaceASCII(
+ const std::string& text,
+ bool trim_sequences_with_line_breaks);
+
+// Returns true if |input| is empty or contains only characters found in
+// |characters|.
+BASE_EXPORT bool ContainsOnlyChars(StringPiece input, StringPiece characters);
+BASE_EXPORT bool ContainsOnlyChars(StringPiece16 input,
+ StringPiece16 characters);
+
+// Returns true if |str| is structurally valid UTF-8 and also doesn't
+// contain any non-character code point (e.g. U+10FFFE). Prohibiting
+// non-characters increases the likelihood of detecting non-UTF-8 in
+// real-world text, for callers which do not need to accept
+// non-characters in strings.
+BASE_EXPORT bool IsStringUTF8(StringPiece str);
+
+// Returns true if |str| contains valid UTF-8, allowing non-character
+// code points.
+BASE_EXPORT bool IsStringUTF8AllowingNoncharacters(StringPiece str);
+
+// Returns true if |str| contains only valid ASCII character values.
+// Note 1: IsStringASCII executes in time determined solely by the
+// length of the string, not by its contents, so it is robust against
+// timing attacks for all strings of equal length.
+// Note 2: IsStringASCII assumes the input is likely all ASCII, and
+// does not leave early if it is not the case.
+BASE_EXPORT bool IsStringASCII(StringPiece str);
+BASE_EXPORT bool IsStringASCII(StringPiece16 str);
+#if defined(WCHAR_T_IS_UTF32)
+BASE_EXPORT bool IsStringASCII(WStringPiece str);
+#endif
+
+// 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);
+
+// 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);
+
+// 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).
+template <typename Char>
+inline bool IsAsciiWhitespace(Char c) {
+ return c == ' ' || c == '\r' || c == '\n' || c == '\t' || c == '\f';
+}
+template <typename Char>
+inline bool IsAsciiAlpha(Char c) {
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+template <typename Char>
+inline bool IsAsciiUpper(Char c) {
+ return c >= 'A' && c <= 'Z';
+}
+template <typename Char>
+inline bool IsAsciiLower(Char c) {
+ return c >= 'a' && c <= 'z';
+}
+template <typename Char>
+inline bool IsAsciiDigit(Char c) {
+ return c >= '0' && c <= '9';
+}
+template <typename Char>
+inline bool IsAsciiPrintable(Char c) {
+ return c >= ' ' && c <= '~';
+}
+
+template <typename Char>
+inline bool IsHexDigit(Char c) {
+ return (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f');
+}
+
+// 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 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 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,
+ StringPiece16 find_this,
+ StringPiece16 replace_with);
+BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
+ std::string* str,
+ size_t start_offset,
+ 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|.
+//
+// This does entire substrings; use std::replace in <algorithm> for single
+// characters, for example:
+// std::replace(str.begin(), str.end(), 'a', 'b');
+BASE_EXPORT void ReplaceSubstringsAfterOffset(
+ string16* str,
+ size_t start_offset,
+ 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
+// pointer to the underlying contiguous array of characters. This is typically
+// used when calling a function that writes results into a character array, but
+// the caller wants the data to be managed by a string-like object. It is
+// convenient in that is can be used inline in the call, and fast in that it
+// avoids copying the results of the call from a char* into a string.
+//
+// Internally, this takes linear time because the resize() call 0-fills the
+// underlying array for potentially all
+// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
+// could avoid this aspect of the resize() call, as we expect the caller to
+// immediately write over this memory, but there is no other way to set the size
+// 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).
+BASE_EXPORT char* WriteInto(std::string* str, size_t length_with_null);
+BASE_EXPORT char16* WriteInto(string16* str, size_t length_with_null);
+
+// Joins a vector or list of strings into a single string, inserting |separator|
+// (which may be empty) in between all elements.
+//
+// Note this is inverse of SplitString()/SplitStringPiece() defined in
+// string_split.h.
+//
+// If possible, callers should build a vector of StringPieces and use the
+// StringPiece variant, so that they do not create unnecessary copies of
+// strings. For example, instead of using SplitString, modifying the vector,
+// then using JoinString, use SplitStringPiece followed by JoinString so that no
+// copies of those strings are created until the final join operation.
+//
+// Use StrCat (in base/strings/strcat.h) if you don't need a separator.
+BASE_EXPORT std::string JoinString(const std::vector<std::string>& parts,
+ StringPiece separator);
+BASE_EXPORT string16 JoinString(const std::vector<string16>& parts,
+ StringPiece16 separator);
+BASE_EXPORT std::string JoinString(const std::vector<StringPiece>& parts,
+ StringPiece separator);
+BASE_EXPORT string16 JoinString(const std::vector<StringPiece16>& parts,
+ StringPiece16 separator);
+// Explicit initializer_list overloads are required to break ambiguity when used
+// with a literal initializer list (otherwise the compiler would not be able to
+// decide between the string and StringPiece overloads).
+BASE_EXPORT std::string JoinString(std::initializer_list<StringPiece> parts,
+ StringPiece separator);
+BASE_EXPORT string16 JoinString(std::initializer_list<StringPiece16> parts,
+ StringPiece16 separator);
+
+// Replace $1-$2-$3..$9 in the format string with values from |subst|.
+// 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 string16 ReplaceStringPlaceholders(
+ const string16& format_string,
+ const std::vector<string16>& subst,
+ std::vector<size_t>* offsets);
+
+BASE_EXPORT std::string ReplaceStringPlaceholders(
+ StringPiece format_string,
+ const std::vector<std::string>& subst,
+ std::vector<size_t>* offsets);
+
+// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
+BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
+ const string16& a,
+ size_t* offset);
+
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+BASE_EXPORT TrimPositions TrimWhitespace(WStringPiece input,
+ TrimPositions positions,
+ std::wstring* output);
+
+BASE_EXPORT WStringPiece TrimWhitespace(WStringPiece input,
+ TrimPositions positions);
+
+BASE_EXPORT bool TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ std::wstring* output);
+
+BASE_EXPORT WStringPiece TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ TrimPositions positions);
+
+BASE_EXPORT wchar_t* WriteInto(std::wstring* str, size_t length_with_null);
+#endif
+
+} // namespace base
+
+#if defined(OS_WIN)
+#include "base/strings/string_util_win.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include "base/strings/string_util_posix.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
+
+#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
new file mode 100644
index 0000000000..212c5ab082
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_util_constants.cc
@@ -0,0 +1,54 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/string_util.h"
+
+namespace base {
+
+#define WHITESPACE_ASCII_NO_CR_LF \
+ 0x09, /* CHARACTER TABULATION */ \
+ 0x0B, /* LINE TABULATION */ \
+ 0x0C, /* FORM FEED (FF) */ \
+ 0x20 /* SPACE */
+
+#define WHITESPACE_ASCII \
+ WHITESPACE_ASCII_NO_CR_LF, /* Comment to make clang-format linebreak */ \
+ 0x0A, /* LINE FEED (LF) */ \
+ 0x0D /* CARRIAGE RETURN (CR) */
+
+#define WHITESPACE_UNICODE_NON_ASCII \
+ 0x0085, /* NEXT LINE (NEL) */ \
+ 0x00A0, /* NO-BREAK SPACE */ \
+ 0x1680, /* OGHAM SPACE MARK */ \
+ 0x2000, /* EN QUAD */ \
+ 0x2001, /* EM QUAD */ \
+ 0x2002, /* EN SPACE */ \
+ 0x2003, /* EM SPACE */ \
+ 0x2004, /* THREE-PER-EM SPACE */ \
+ 0x2005, /* FOUR-PER-EM SPACE */ \
+ 0x2006, /* SIX-PER-EM SPACE */ \
+ 0x2007, /* FIGURE SPACE */ \
+ 0x2008, /* PUNCTUATION SPACE */ \
+ 0x2009, /* THIN SPACE */ \
+ 0x200A, /* HAIR SPACE */ \
+ 0x2028, /* LINE SEPARATOR */ \
+ 0x2029, /* PARAGRAPH SEPARATOR */ \
+ 0x202F, /* NARROW NO-BREAK SPACE */ \
+ 0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
+ 0x3000 /* IDEOGRAPHIC SPACE */
+
+#define WHITESPACE_UNICODE_NO_CR_LF \
+ WHITESPACE_ASCII_NO_CR_LF, WHITESPACE_UNICODE_NON_ASCII
+
+#define WHITESPACE_UNICODE WHITESPACE_ASCII, WHITESPACE_UNICODE_NON_ASCII
+
+const wchar_t kWhitespaceWide[] = {WHITESPACE_UNICODE, 0};
+const char16 kWhitespaceUTF16[] = {WHITESPACE_UNICODE, 0};
+const char16 kWhitespaceNoCrLfUTF16[] = {WHITESPACE_UNICODE_NO_CR_LF, 0};
+const char kWhitespaceASCII[] = {WHITESPACE_ASCII, 0};
+const char16 kWhitespaceASCIIAs16[] = {WHITESPACE_ASCII, 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
new file mode 100644
index 0000000000..8299118e10
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_util_posix.h
@@ -0,0 +1,37 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_STRING_UTIL_POSIX_H_
+#define BASE_STRINGS_STRING_UTIL_POSIX_H_
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+// Chromium code style is to not use malloc'd strings; this is only for use
+// for interaction with APIs that require it.
+inline char* strdup(const char* str) {
+ return ::strdup(str);
+}
+
+inline int vsnprintf(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ return ::vsnprintf(buffer, size, format, arguments);
+}
+
+inline int vswprintf(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ DCHECK(IsWprintfFormatPortable(format));
+ return ::vswprintf(buffer, size, format, arguments);
+}
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_UTIL_POSIX_H_
diff --git a/security/sandbox/chromium/base/strings/string_util_win.h b/security/sandbox/chromium/base/strings/string_util_win.h
new file mode 100644
index 0000000000..7f260bfc8b
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/string_util_win.h
@@ -0,0 +1,44 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_STRING_UTIL_WIN_H_
+#define BASE_STRINGS_STRING_UTIL_WIN_H_
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+// Chromium code style is to not use malloc'd strings; this is only for use
+// for interaction with APIs that require it.
+inline char* strdup(const char* str) {
+ return _strdup(str);
+}
+
+inline int vsnprintf(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ int length = vsnprintf_s(buffer, size, size - 1, format, arguments);
+ if (length < 0)
+ return _vscprintf(format, arguments);
+ return length;
+}
+
+inline int vswprintf(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ DCHECK(IsWprintfFormatPortable(format));
+
+ int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments);
+ if (length < 0)
+ return _vscwprintf(format, arguments);
+ return length;
+}
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRING_UTIL_WIN_H_
diff --git a/security/sandbox/chromium/base/strings/stringprintf.cc b/security/sandbox/chromium/base/strings/stringprintf.cc
new file mode 100644
index 0000000000..738cc63bbe
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/stringprintf.cc
@@ -0,0 +1,225 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/stringprintf.h"
+
+#include <errno.h>
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/scoped_clear_last_error.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace {
+
+// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
+// is the size of the buffer. These return the number of characters in the
+// formatted string excluding the NUL terminator. If the buffer is not
+// large enough to accommodate the formatted string without truncation, they
+// return the number of characters that would be in the fully-formatted string
+// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms).
+inline int vsnprintfT(char* buffer,
+ size_t buf_size,
+ const char* format,
+ va_list argptr) {
+ return base::vsnprintf(buffer, buf_size, format, argptr);
+}
+
+#if defined(OS_WIN)
+inline int vsnprintfT(wchar_t* buffer,
+ size_t buf_size,
+ const wchar_t* format,
+ va_list argptr) {
+ return base::vswprintf(buffer, buf_size, format, argptr);
+}
+inline int vsnprintfT(char16_t* buffer,
+ size_t buf_size,
+ const char16_t* format,
+ va_list argptr) {
+ return base::vswprintf(reinterpret_cast<wchar_t*>(buffer), buf_size,
+ reinterpret_cast<const wchar_t*>(format), argptr);
+}
+#endif
+
+// Templatized backend for StringPrintF/StringAppendF. This does not finalize
+// the va_list, the caller is expected to do that.
+template <class CharT>
+static void StringAppendVT(std::basic_string<CharT>* dst,
+ const CharT* format,
+ va_list ap) {
+ // First try with a small fixed size buffer.
+ // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
+ // and StringUtilTest.StringPrintfBounds.
+ CharT stack_buf[1024];
+
+ va_list ap_copy;
+ va_copy(ap_copy, ap);
+
+ base::internal::ScopedClearLastError last_error;
+ int result = vsnprintfT(stack_buf, base::size(stack_buf), format, ap_copy);
+ va_end(ap_copy);
+
+ if (result >= 0 && result < static_cast<int>(base::size(stack_buf))) {
+ // It fit.
+ dst->append(stack_buf, result);
+ return;
+ }
+
+ // Repeatedly increase buffer size until it fits.
+ int mem_length = base::size(stack_buf);
+ while (true) {
+ if (result < 0) {
+#if defined(OS_WIN)
+ // On Windows, vsnprintfT always returns the number of characters in a
+ // fully-formatted string, so if we reach this point, something else is
+ // wrong and no amount of buffer-doubling is going to fix it.
+ return;
+#else
+ if (errno != 0 && errno != EOVERFLOW)
+ return;
+ // Try doubling the buffer size.
+ mem_length *= 2;
+#endif
+ } else {
+ // We need exactly "result + 1" characters.
+ mem_length = result + 1;
+ }
+
+ if (mem_length > 32 * 1024 * 1024) {
+ // That should be plenty, don't try anything larger. This protects
+ // against huge allocations when using vsnprintfT implementations that
+ // return -1 for reasons other than overflow without setting errno.
+ DLOG(WARNING) << "Unable to printf the requested string due to size.";
+ return;
+ }
+
+ std::vector<CharT> mem_buf(mem_length);
+
+ // 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.
+ va_copy(ap_copy, ap);
+ result = vsnprintfT(&mem_buf[0], mem_length, format, ap_copy);
+ va_end(ap_copy);
+
+ if ((result >= 0) && (result < mem_length)) {
+ // It fit.
+ dst->append(&mem_buf[0], result);
+ return;
+ }
+ }
+}
+
+} // namespace
+
+std::string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+#if defined(OS_WIN)
+std::wstring StringPrintf(const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::wstring result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+std::u16string StringPrintf(const char16_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::u16string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+#endif
+
+std::string StringPrintV(const char* format, va_list ap) {
+ std::string result;
+ StringAppendV(&result, format, ap);
+ return result;
+}
+
+const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+#if defined(OS_WIN)
+const std::wstring& SStringPrintf(std::wstring* dst,
+ const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+const std::u16string& SStringPrintf(std::u16string* dst,
+ const char16_t* format,
+ ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+#endif
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+#if defined(OS_WIN)
+void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+void StringAppendF(std::u16string* dst, const char16_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+#endif
+
+void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
+
+#if defined(OS_WIN)
+void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
+
+void StringAppendV(std::u16string* dst, const char16_t* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
+#endif
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/stringprintf.h b/security/sandbox/chromium/base/strings/stringprintf.h
new file mode 100644
index 0000000000..a8d5bc84e9
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/stringprintf.h
@@ -0,0 +1,74 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_STRINGPRINTF_H_
+#define BASE_STRINGS_STRINGPRINTF_H_
+
+#include <stdarg.h> // va_list
+
+#include <string>
+
+#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, ...)
+ PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
+#if defined(OS_WIN)
+// Note: Unfortunately compile time checking of the format string for UTF-16
+// strings is not supported by any compiler, thus these functions should be used
+// carefully and sparingly. Also applies to SStringPrintf and StringAppendV
+// below.
+BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
+ WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
+BASE_EXPORT std::u16string StringPrintf(const char16_t* format, ...)
+ WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
+#endif
+
+// Return a C++ string given vprintf-like input.
+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_WIN)
+BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst,
+ const wchar_t* format,
+ ...) WPRINTF_FORMAT(2, 3);
+BASE_EXPORT const std::u16string& SStringPrintf(std::u16string* dst,
+ const char16_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_WIN)
+BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
+ WPRINTF_FORMAT(2, 3);
+BASE_EXPORT void StringAppendF(std::u16string* dst, const char16_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_WIN)
+BASE_EXPORT void StringAppendV(std::wstring* dst,
+ const wchar_t* format,
+ va_list ap) WPRINTF_FORMAT(2, 0);
+BASE_EXPORT void StringAppendV(std::u16string* dst,
+ const char16_t* format,
+ va_list ap) WPRINTF_FORMAT(2, 0);
+#endif
+
+} // namespace base
+
+#endif // BASE_STRINGS_STRINGPRINTF_H_
diff --git a/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc
new file mode 100644
index 0000000000..f7682c1be9
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversion_utils.h"
+
+#include "base/third_party/icu/icu_utf.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// ReadUnicodeCharacter --------------------------------------------------------
+
+bool ReadUnicodeCharacter(const char* src,
+ 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_t code_point;
+ CBU8_NEXT(src, *char_index, src_len, code_point);
+ *code_point_out = static_cast<uint32_t>(code_point);
+
+ // The ICU macro above moves to the next char, we want to point to the last
+ // char consumed.
+ (*char_index)--;
+
+ // Validate the decoded value.
+ return IsValidCodepoint(code_point);
+}
+
+bool ReadUnicodeCharacter(const char16* src,
+ 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 ||
+ !CBU16_IS_TRAIL(src[*char_index + 1])) {
+ // Invalid surrogate pair.
+ return false;
+ }
+
+ // Valid surrogate pair.
+ *code_point = CBU16_GET_SUPPLEMENTARY(src[*char_index],
+ src[*char_index + 1]);
+ (*char_index)++;
+ } else {
+ // Not a surrogate, just one 16-bit word.
+ *code_point = src[*char_index];
+ }
+
+ return IsValidCodepoint(*code_point);
+}
+
+#if defined(WCHAR_T_IS_UTF32)
+bool ReadUnicodeCharacter(const wchar_t* src,
+ 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];
+
+ // Validate the value.
+ return IsValidCodepoint(*code_point);
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// WriteUnicodeCharacter -------------------------------------------------------
+
+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<char>(code_point));
+ return 1;
+ }
+
+
+ // CBU8_APPEND_UNSAFE can append up to 4 bytes.
+ size_t char_offset = output->length();
+ size_t original_char_offset = char_offset;
+ output->resize(char_offset + CBU8_MAX_LENGTH);
+
+ CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+
+ // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so
+ // it will represent the new length of the string.
+ output->resize(char_offset);
+ return char_offset - original_char_offset;
+}
+
+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<char16>(code_point));
+ return 1;
+ }
+ // Non-BMP characters use a double-character encoding.
+ size_t char_offset = output->length();
+ output->resize(char_offset + CBU16_MAX_LENGTH);
+ CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+ return CBU16_MAX_LENGTH;
+}
+
+// Generalized Unicode converter -----------------------------------------------
+
+template<typename CHAR>
+void PrepareForUTF8Output(const CHAR* src,
+ size_t src_len,
+ std::string* output) {
+ output->clear();
+ if (src_len == 0)
+ return;
+ if (src[0] < 0x80) {
+ // Assume that the entire input will be ASCII.
+ output->reserve(src_len);
+ } else {
+ // Assume that the entire input is non-ASCII and will have 3 bytes per char.
+ output->reserve(src_len * 3);
+ }
+}
+
+// Instantiate versions we know callers will need.
+#if !defined(OS_WIN)
+// wchar_t and char16 are the same thing on Windows.
+template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*);
+#endif
+template void PrepareForUTF8Output(const char16*, size_t, std::string*);
+
+template<typename STRING>
+void PrepareForUTF16Or32Output(const char* src,
+ size_t src_len,
+ STRING* output) {
+ output->clear();
+ if (src_len == 0)
+ return;
+ if (static_cast<unsigned char>(src[0]) < 0x80) {
+ // Assume the input is all ASCII, which means 1:1 correspondence.
+ output->reserve(src_len);
+ } else {
+ // Otherwise assume that the UTF-8 sequences will have 2 bytes for each
+ // character.
+ output->reserve(src_len / 2);
+ }
+}
+
+// Instantiate versions we know callers will need.
+#if !defined(OS_WIN)
+// std::wstring and string16 are the same thing on Windows.
+template void PrepareForUTF16Or32Output(const char*, size_t, std::wstring*);
+#endif
+template void PrepareForUTF16Or32Output(const char*, size_t, string16*);
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h
new file mode 100644
index 0000000000..01d24c3e2e
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/utf_string_conversion_utils.h
@@ -0,0 +1,103 @@
+// 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_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
+#define BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
+
+// Low-level UTF handling functions. Most code will want to use the functions
+// in utf_string_conversions.h
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+
+namespace base {
+
+inline bool IsValidCodepoint(uint32_t code_point) {
+ // Excludes code points that are not Unicode scalar values, i.e.
+ // surrogate code points ([0xD800, 0xDFFF]). Additionally, excludes
+ // code points larger than 0x10FFFF (the highest codepoint allowed).
+ // Non-characters and unassigned code points are allowed.
+ // https://unicode.org/glossary/#unicode_scalar_value
+ return code_point < 0xD800u ||
+ (code_point >= 0xE000u && code_point <= 0x10FFFFu);
+}
+
+inline bool IsValidCharacter(uint32_t code_point) {
+ // Excludes non-characters (U+FDD0..U+FDEF, and all code points
+ // ending in 0xFFFE or 0xFFFF) from the set of valid code points.
+ // https://unicode.org/faq/private_use.html#nonchar1
+ return code_point < 0xD800u || (code_point >= 0xE000u &&
+ code_point < 0xFDD0u) || (code_point > 0xFDEFu &&
+ code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu);
+}
+
+// ReadUnicodeCharacter --------------------------------------------------------
+
+// Reads a UTF-8 stream, placing the next code point into the given output
+// |*code_point|. |src| represents the entire string to read, and |*char_index|
+// is the character offset within the string to start reading at. |*char_index|
+// will be updated to index the last character read, such that incrementing it
+// (as in a for loop) will take the reader to the next character.
+//
+// Returns true on success. On false, |*code_point| will be invalid.
+BASE_EXPORT bool ReadUnicodeCharacter(const char* src,
+ 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_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_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.
+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_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_t code_point, std::wstring* output) {
+ // This is the easy case, just append the character.
+ output->push_back(code_point);
+ return 1;
+}
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// Generalized Unicode converter -----------------------------------------------
+
+// Guesses the length of the output in UTF-8 in bytes, clears that output
+// string, and reserves that amount of space. We assume that the input
+// character types are unsigned, which will be true for UTF-16 and -32 on our
+// systems.
+template<typename CHAR>
+void PrepareForUTF8Output(const CHAR* src, size_t src_len, std::string* output);
+
+// Prepares an output buffer (containing either UTF-16 or -32 data) given some
+// UTF-8 input that will be converted to it. See PrepareForUTF8Output().
+template<typename STRING>
+void PrepareForUTF16Or32Output(const char* src, size_t src_len, STRING* output);
+
+} // namespace base
+
+#endif // BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
diff --git a/security/sandbox/chromium/base/strings/utf_string_conversions.cc b/security/sandbox/chromium/base/strings/utf_string_conversions.cc
new file mode 100644
index 0000000000..9a79889159
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/utf_string_conversions.cc
@@ -0,0 +1,342 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+
+#include <limits.h>
+#include <stdint.h>
+
+#include <type_traits>
+
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/third_party/icu/icu_utf.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace {
+
+constexpr int32_t kErrorCodePoint = 0xFFFD;
+
+// Size coefficient ----------------------------------------------------------
+// The maximum number of codeunits in the destination encoding corresponding to
+// one codeunit in the source encoding.
+
+template <typename SrcChar, typename DestChar>
+struct SizeCoefficient {
+ static_assert(sizeof(SrcChar) < sizeof(DestChar),
+ "Default case: from a smaller encoding to the bigger one");
+
+ // ASCII symbols are encoded by one codeunit in all encodings.
+ static constexpr int value = 1;
+};
+
+template <>
+struct SizeCoefficient<char16, char> {
+ // One UTF-16 codeunit corresponds to at most 3 codeunits in UTF-8.
+ static constexpr int value = 3;
+};
+
+#if defined(WCHAR_T_IS_UTF32)
+template <>
+struct SizeCoefficient<wchar_t, char> {
+ // UTF-8 uses at most 4 codeunits per character.
+ static constexpr int value = 4;
+};
+
+template <>
+struct SizeCoefficient<wchar_t, char16> {
+ // UTF-16 uses at most 2 codeunits per character.
+ static constexpr int value = 2;
+};
+#endif // defined(WCHAR_T_IS_UTF32)
+
+template <typename SrcChar, typename DestChar>
+constexpr int size_coefficient_v =
+ SizeCoefficient<std::decay_t<SrcChar>, std::decay_t<DestChar>>::value;
+
+// UnicodeAppendUnsafe --------------------------------------------------------
+// Function overloads that write code_point to the output string. Output string
+// has to have enough space for the codepoint.
+
+// Convenience typedef that checks whether the passed in type is integral (i.e.
+// bool, char, int or their extended versions) and is of the correct size.
+template <typename Char, size_t N>
+using EnableIfBitsAre = std::enable_if_t<std::is_integral<Char>::value &&
+ CHAR_BIT * sizeof(Char) == N,
+ bool>;
+
+template <typename Char, EnableIfBitsAre<Char, 8> = true>
+void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) {
+ CBU8_APPEND_UNSAFE(out, *size, code_point);
+}
+
+template <typename Char, EnableIfBitsAre<Char, 16> = true>
+void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) {
+ CBU16_APPEND_UNSAFE(out, *size, code_point);
+}
+
+template <typename Char, EnableIfBitsAre<Char, 32> = true>
+void UnicodeAppendUnsafe(Char* out, int32_t* size, uint32_t code_point) {
+ out[(*size)++] = code_point;
+}
+
+// DoUTFConversion ------------------------------------------------------------
+// Main driver of UTFConversion specialized for different Src encodings.
+// dest has to have enough room for the converted text.
+
+template <typename DestChar>
+bool DoUTFConversion(const char* src,
+ int32_t src_len,
+ DestChar* dest,
+ int32_t* dest_len) {
+ bool success = true;
+
+ for (int32_t i = 0; i < src_len;) {
+ int32_t code_point;
+ CBU8_NEXT(src, i, src_len, code_point);
+
+ if (!IsValidCodepoint(code_point)) {
+ success = false;
+ code_point = kErrorCodePoint;
+ }
+
+ UnicodeAppendUnsafe(dest, dest_len, code_point);
+ }
+
+ return success;
+}
+
+template <typename DestChar>
+bool DoUTFConversion(const char16* src,
+ int32_t src_len,
+ DestChar* dest,
+ int32_t* dest_len) {
+ bool success = true;
+
+ auto ConvertSingleChar = [&success](char16 in) -> int32_t {
+ if (!CBU16_IS_SINGLE(in) || !IsValidCodepoint(in)) {
+ success = false;
+ return kErrorCodePoint;
+ }
+ return in;
+ };
+
+ int32_t i = 0;
+
+ // Always have another symbol in order to avoid checking boundaries in the
+ // middle of the surrogate pair.
+ while (i < src_len - 1) {
+ int32_t code_point;
+
+ if (CBU16_IS_LEAD(src[i]) && CBU16_IS_TRAIL(src[i + 1])) {
+ code_point = CBU16_GET_SUPPLEMENTARY(src[i], src[i + 1]);
+ if (!IsValidCodepoint(code_point)) {
+ code_point = kErrorCodePoint;
+ success = false;
+ }
+ i += 2;
+ } else {
+ code_point = ConvertSingleChar(src[i]);
+ ++i;
+ }
+
+ UnicodeAppendUnsafe(dest, dest_len, code_point);
+ }
+
+ if (i < src_len)
+ UnicodeAppendUnsafe(dest, dest_len, ConvertSingleChar(src[i]));
+
+ return success;
+}
+
+#if defined(WCHAR_T_IS_UTF32)
+
+template <typename DestChar>
+bool DoUTFConversion(const wchar_t* src,
+ int32_t src_len,
+ DestChar* dest,
+ int32_t* dest_len) {
+ bool success = true;
+
+ for (int32_t i = 0; i < src_len; ++i) {
+ int32_t code_point = src[i];
+
+ if (!IsValidCodepoint(code_point)) {
+ success = false;
+ code_point = kErrorCodePoint;
+ }
+
+ UnicodeAppendUnsafe(dest, dest_len, code_point);
+ }
+
+ return success;
+}
+
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// UTFConversion --------------------------------------------------------------
+// Function template for generating all UTF conversions.
+
+template <typename InputString, typename DestString>
+bool UTFConversion(const InputString& src_str, DestString* dest_str) {
+ if (IsStringASCII(src_str)) {
+ dest_str->assign(src_str.begin(), src_str.end());
+ return true;
+ }
+
+ dest_str->resize(src_str.length() *
+ size_coefficient_v<typename InputString::value_type,
+ typename DestString::value_type>);
+
+ // Empty string is ASCII => it OK to call operator[].
+ auto* dest = &(*dest_str)[0];
+
+ // ICU requires 32 bit numbers.
+ int32_t src_len32 = static_cast<int32_t>(src_str.length());
+ int32_t dest_len32 = 0;
+
+ bool res = DoUTFConversion(src_str.data(), src_len32, dest, &dest_len32);
+
+ dest_str->resize(dest_len32);
+ dest_str->shrink_to_fit();
+
+ return res;
+}
+
+} // namespace
+
+// UTF16 <-> UTF8 --------------------------------------------------------------
+
+bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) {
+ return UTFConversion(StringPiece(src, src_len), output);
+}
+
+string16 UTF8ToUTF16(StringPiece utf8) {
+ string16 ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF8ToUTF16(utf8.data(), utf8.size(), &ret);
+ return ret;
+}
+
+bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) {
+ return UTFConversion(StringPiece16(src, src_len), output);
+}
+
+std::string UTF16ToUTF8(StringPiece16 utf16) {
+ std::string ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF16ToUTF8(utf16.data(), utf16.length(), &ret);
+ return ret;
+}
+
+// UTF-16 <-> Wide -------------------------------------------------------------
+
+#if defined(WCHAR_T_IS_UTF16)
+// When wide == UTF-16 the conversions are a NOP.
+
+bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) {
+ output->assign(src, src + src_len);
+ return true;
+}
+
+string16 WideToUTF16(WStringPiece wide) {
+ return string16(wide.begin(), wide.end());
+}
+
+bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) {
+ output->assign(src, src + src_len);
+ return true;
+}
+
+std::wstring UTF16ToWide(StringPiece16 utf16) {
+ return std::wstring(utf16.begin(), utf16.end());
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) {
+ return UTFConversion(base::WStringPiece(src, src_len), output);
+}
+
+string16 WideToUTF16(WStringPiece wide) {
+ string16 ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ WideToUTF16(wide.data(), wide.length(), &ret);
+ return ret;
+}
+
+bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) {
+ return UTFConversion(StringPiece16(src, src_len), output);
+}
+
+std::wstring UTF16ToWide(StringPiece16 utf16) {
+ std::wstring ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF16ToWide(utf16.data(), utf16.length(), &ret);
+ return ret;
+}
+
+#endif // defined(WCHAR_T_IS_UTF32)
+
+// UTF-8 <-> Wide --------------------------------------------------------------
+
+// UTF8ToWide is the same code, regardless of whether wide is 16 or 32 bits
+
+bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output) {
+ return UTFConversion(StringPiece(src, src_len), output);
+}
+
+std::wstring UTF8ToWide(StringPiece utf8) {
+ std::wstring ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ UTF8ToWide(utf8.data(), utf8.length(), &ret);
+ return ret;
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// Easy case since we can use the "utf" versions we already wrote above.
+
+bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) {
+ return UTF16ToUTF8(as_u16cstr(src), src_len, output);
+}
+
+std::string WideToUTF8(WStringPiece wide) {
+ return UTF16ToUTF8(StringPiece16(as_u16cstr(wide), wide.size()));
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+
+bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) {
+ return UTFConversion(WStringPiece(src, src_len), output);
+}
+
+std::string WideToUTF8(WStringPiece wide) {
+ std::string ret;
+ // Ignore the success flag of this call, it will do the best it can for
+ // invalid input, which is what we want here.
+ WideToUTF8(wide.data(), wide.length(), &ret);
+ return ret;
+}
+
+#endif // defined(WCHAR_T_IS_UTF32)
+
+string16 ASCIIToUTF16(StringPiece ascii) {
+ DCHECK(IsStringASCII(ascii)) << ascii;
+ return string16(ascii.begin(), ascii.end());
+}
+
+std::string UTF16ToASCII(StringPiece16 utf16) {
+ DCHECK(IsStringASCII(utf16)) << UTF16ToUTF8(utf16);
+ return std::string(utf16.begin(), utf16.end());
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/strings/utf_string_conversions.h b/security/sandbox/chromium/base/strings/utf_string_conversions.h
new file mode 100644
index 0000000000..f780fb4f4f
--- /dev/null
+++ b/security/sandbox/chromium/base/strings/utf_string_conversions.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
+#define BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// These convert between UTF-8, -16, and -32 strings. They are potentially slow,
+// so avoid unnecessary conversions. The low-level versions return a boolean
+// indicating whether the conversion was 100% valid. In this case, it will still
+// do the best it can and put the result in the output buffer. The versions that
+// return strings ignore this error and just return the best conversion
+// possible.
+BASE_EXPORT bool WideToUTF8(const wchar_t* src, size_t src_len,
+ std::string* output);
+BASE_EXPORT std::string WideToUTF8(WStringPiece wide) WARN_UNUSED_RESULT;
+BASE_EXPORT bool UTF8ToWide(const char* src, size_t src_len,
+ std::wstring* output);
+BASE_EXPORT std::wstring UTF8ToWide(StringPiece utf8) WARN_UNUSED_RESULT;
+
+BASE_EXPORT bool WideToUTF16(const wchar_t* src, size_t src_len,
+ string16* output);
+BASE_EXPORT string16 WideToUTF16(WStringPiece wide) WARN_UNUSED_RESULT;
+BASE_EXPORT bool UTF16ToWide(const char16* src, size_t src_len,
+ std::wstring* output);
+BASE_EXPORT std::wstring UTF16ToWide(StringPiece16 utf16) WARN_UNUSED_RESULT;
+
+BASE_EXPORT bool UTF8ToUTF16(const char* src, size_t src_len, string16* output);
+BASE_EXPORT string16 UTF8ToUTF16(StringPiece utf8) WARN_UNUSED_RESULT;
+BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
+ std::string* output);
+BASE_EXPORT std::string UTF16ToUTF8(StringPiece16 utf16) WARN_UNUSED_RESULT;
+
+// This converts an ASCII string, typically a hardcoded constant, to a UTF16
+// string.
+BASE_EXPORT string16 ASCIIToUTF16(StringPiece ascii) WARN_UNUSED_RESULT;
+
+// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
+// beforehand.
+BASE_EXPORT std::string UTF16ToASCII(StringPiece16 utf16) WARN_UNUSED_RESULT;
+
+} // namespace base
+
+#endif // BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
diff --git a/security/sandbox/chromium/base/synchronization/atomic_flag.h b/security/sandbox/chromium/base/synchronization/atomic_flag.h
new file mode 100644
index 0000000000..f386a16cbd
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/atomic_flag.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
+#define BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
+
+#include <stdint.h>
+
+#include <atomic>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/sequence_checker.h"
+
+namespace base {
+
+// A flag that can safely be set from one thread and read from other threads.
+//
+// This class IS NOT intended for synchronization between threads.
+class BASE_EXPORT AtomicFlag {
+ public:
+ AtomicFlag();
+ ~AtomicFlag();
+
+ // Set the flag. Must always be called from the same sequence.
+ void Set();
+
+ // Returns true iff the flag was set. If this returns true, the current thread
+ // is guaranteed to be synchronized with all memory operations on the sequence
+ // which invoked Set() up until at least the first call to Set() on it.
+ bool IsSet() const {
+ // Inline here: this has a measurable performance impact on base::WeakPtr.
+ return flag_.load(std::memory_order_acquire) != 0;
+ }
+
+ // Resets the flag. Be careful when using this: callers might not expect
+ // IsSet() to return false after returning true once.
+ void UnsafeResetForTesting();
+
+ private:
+ std::atomic<uint_fast8_t> flag_{0};
+ SEQUENCE_CHECKER(set_sequence_checker_);
+
+ DISALLOW_COPY_AND_ASSIGN(AtomicFlag);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_ATOMIC_FLAG_H_
diff --git a/security/sandbox/chromium/base/synchronization/condition_variable.h b/security/sandbox/chromium/base/synchronization/condition_variable.h
new file mode 100644
index 0000000000..d92b738081
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/condition_variable.h
@@ -0,0 +1,135 @@
+// 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.
+
+// ConditionVariable wraps pthreads condition variable synchronization or, on
+// Windows, simulates it. This functionality is very helpful for having
+// several threads wait for an event, as is common with a thread pool managed
+// by a master. The meaning of such an event in the (worker) thread pool
+// scenario is that additional tasks are now available for processing. It is
+// used in Chrome in the DNS prefetching system to notify worker threads that
+// a queue now has items (tasks) which need to be tended to. A related use
+// would have a pool manager waiting on a ConditionVariable, waiting for a
+// thread in the pool to announce (signal) that there is now more room in a
+// (bounded size) communications queue for the manager to deposit tasks, or,
+// as a second example, that the queue of tasks is completely empty and all
+// workers are waiting.
+//
+// USAGE NOTE 1: spurious signal events are possible with this and
+// most implementations of condition variables. As a result, be
+// *sure* to retest your condition before proceeding. The following
+// is a good example of doing this correctly:
+//
+// while (!work_to_be_done()) Wait(...);
+//
+// In contrast do NOT do the following:
+//
+// if (!work_to_be_done()) Wait(...); // Don't do this.
+//
+// Especially avoid the above if you are relying on some other thread only
+// issuing a signal up *if* there is work-to-do. There can/will
+// be spurious signals. Recheck state on waiting thread before
+// assuming the signal was intentional. Caveat caller ;-).
+//
+// USAGE NOTE 2: Broadcast() frees up all waiting threads at once,
+// which leads to contention for the locks they all held when they
+// called Wait(). This results in POOR performance. A much better
+// approach to getting a lot of threads out of Wait() is to have each
+// thread (upon exiting Wait()) call Signal() to free up another
+// Wait'ing thread. Look at condition_variable_unittest.cc for
+// both examples.
+//
+// Broadcast() can be used nicely during teardown, as it gets the job
+// done, and leaves no sleeping threads... and performance is less
+// critical at that point.
+//
+// The semantics of Broadcast() are carefully crafted so that *all*
+// threads that were waiting when the request was made will indeed
+// get signaled. Some implementations mess up, and don't signal them
+// all, while others allow the wait to be effectively turned off (for
+// a while while waiting threads come around). This implementation
+// appears correct, as it will not "lose" any signals, and will guarantee
+// that all threads get signaled by Broadcast().
+//
+// This implementation offers support for "performance" in its selection of
+// which thread to revive. Performance, in direct contrast with "fairness,"
+// assures that the thread that most recently began to Wait() is selected by
+// Signal to revive. Fairness would (if publicly supported) assure that the
+// thread that has Wait()ed the longest is selected. The default policy
+// may improve performance, as the selected thread may have a greater chance of
+// having some of its stack data in various CPU caches.
+
+#ifndef BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
+#define BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <pthread.h>
+#endif
+
+#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_WIN)
+#include "base/win/windows_types.h"
+#endif
+
+namespace base {
+
+class TimeDelta;
+
+class BASE_EXPORT ConditionVariable {
+ public:
+ // Construct a cv for use with ONLY one user lock.
+ explicit ConditionVariable(Lock* user_lock);
+
+ ~ConditionVariable();
+
+ // Wait() releases the caller's critical section atomically as it starts to
+ // sleep, and the reacquires it when it is signaled. The wait functions are
+ // susceptible to spurious wakeups. (See usage note 1 for more details.)
+ void Wait();
+ void TimedWait(const TimeDelta& max_time);
+
+ // Broadcast() revives all waiting threads. (See usage note 2 for more
+ // details.)
+ void Broadcast();
+ // Signal() revives one waiting thread.
+ void Signal();
+
+ // Declares that this ConditionVariable will only ever be used by a thread
+ // that is idle at the bottom of its stack and waiting for work (in
+ // particular, it is not synchronously waiting on this ConditionVariable
+ // before resuming ongoing work). This is useful to avoid telling
+ // base-internals that this thread is "blocked" when it's merely idle and
+ // ready to do work. As such, this is only expected to be used by thread and
+ // thread pool impls.
+ void declare_only_used_while_idle() { waiting_is_blocking_ = false; }
+
+ private:
+
+#if defined(OS_WIN)
+ CHROME_CONDITION_VARIABLE cv_;
+ CHROME_SRWLOCK* const srwlock_;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ pthread_cond_t condition_;
+ pthread_mutex_t* user_mutex_;
+#endif
+
+#if DCHECK_IS_ON()
+ base::Lock* const user_lock_; // Needed to adjust shadow lock state on wait.
+#endif
+
+ // Whether a thread invoking Wait() on this ConditionalVariable should be
+ // considered blocked as opposed to idle (and potentially replaced if part of
+ // a pool).
+ bool waiting_is_blocking_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_CONDITION_VARIABLE_H_
diff --git a/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc b/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc
new file mode 100644
index 0000000000..189eb360d2
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/condition_variable_posix.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/condition_variable.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#include "base/optional.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/scoped_blocking_call.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 DCHECK_IS_ON()
+ , user_lock_(user_lock)
+#endif
+{
+ int rv = 0;
+ // http://crbug.com/293736
+ // NaCl doesn't support monotonic clock based absolute deadlines.
+ // On older Android platform versions, it's supported through the
+ // non-standard pthread_cond_timedwait_monotonic_np. Newer platform
+ // versions have pthread_condattr_setclock.
+ // Mac can use relative time deadlines.
+#if !defined(OS_MACOSX) && !defined(OS_NACL) && \
+ !(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
+ pthread_condattr_t attrs;
+ rv = pthread_condattr_init(&attrs);
+ DCHECK_EQ(0, rv);
+ pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ rv = pthread_cond_init(&condition_, &attrs);
+ pthread_condattr_destroy(&attrs);
+#else
+ rv = pthread_cond_init(&condition_, NULL);
+#endif
+ DCHECK_EQ(0, rv);
+}
+
+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() {
+ Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
+ scoped_blocking_call;
+ if (waiting_is_blocking_)
+ scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
+
+#if DCHECK_IS_ON()
+ user_lock_->CheckHeldAndUnmark();
+#endif
+ int rv = pthread_cond_wait(&condition_, user_mutex_);
+ DCHECK_EQ(0, rv);
+#if DCHECK_IS_ON()
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
+ scoped_blocking_call;
+ if (waiting_is_blocking_)
+ scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
+
+ 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 DCHECK_IS_ON()
+ user_lock_->CheckHeldAndUnmark();
+#endif
+
+#if defined(OS_MACOSX)
+ int rv = pthread_cond_timedwait_relative_np(
+ &condition_, user_mutex_, &relative_time);
+#else
+ // The timeout argument to pthread_cond_timedwait is in absolute time.
+ struct timespec absolute_time;
+#if defined(OS_NACL)
+ // See comment in constructor for why this is different in NaCl.
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ absolute_time.tv_sec = now.tv_sec;
+ absolute_time.tv_nsec = now.tv_usec * Time::kNanosecondsPerMicrosecond;
+#else
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ absolute_time.tv_sec = now.tv_sec;
+ absolute_time.tv_nsec = now.tv_nsec;
+#endif
+
+ absolute_time.tv_sec += relative_time.tv_sec;
+ absolute_time.tv_nsec += relative_time.tv_nsec;
+ absolute_time.tv_sec += absolute_time.tv_nsec / Time::kNanosecondsPerSecond;
+ absolute_time.tv_nsec %= Time::kNanosecondsPerSecond;
+ DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia
+
+#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
+ int rv = pthread_cond_timedwait_monotonic_np(
+ &condition_, user_mutex_, &absolute_time);
+#else
+ int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time);
+#endif // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
+#endif // OS_MACOSX
+
+ // On failure, we only expect the CV to timeout. Any other error value means
+ // that we've unexpectedly woken up.
+ DCHECK(rv == 0 || rv == ETIMEDOUT);
+#if DCHECK_IS_ON()
+ user_lock_->CheckUnheldAndMark();
+#endif
+}
+
+void ConditionVariable::Broadcast() {
+ int rv = pthread_cond_broadcast(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+void ConditionVariable::Signal() {
+ int rv = pthread_cond_signal(&condition_);
+ DCHECK_EQ(0, rv);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/synchronization/lock.cc b/security/sandbox/chromium/base/synchronization/lock.cc
new file mode 100644
index 0000000000..03297ada52
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/lock.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is used for debugging assertion support. The Lock class
+// is functionally a wrapper around the LockImpl class, so the only
+// real intelligence in the class is in the debugging logic.
+
+#include "base/synchronization/lock.h"
+
+#if DCHECK_IS_ON()
+
+namespace base {
+
+Lock::Lock() : lock_() {
+}
+
+Lock::~Lock() {
+ DCHECK(owning_thread_ref_.is_null());
+}
+
+void Lock::AssertAcquired() const {
+ DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef());
+}
+
+void Lock::CheckHeldAndUnmark() {
+ DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef());
+ owning_thread_ref_ = PlatformThreadRef();
+}
+
+void Lock::CheckUnheldAndMark() {
+ DCHECK(owning_thread_ref_.is_null());
+ owning_thread_ref_ = PlatformThread::CurrentRef();
+}
+
+} // namespace base
+
+#endif // DCHECK_IS_ON()
diff --git a/security/sandbox/chromium/base/synchronization/lock.h b/security/sandbox/chromium/base/synchronization/lock.h
new file mode 100644
index 0000000000..00095ab3af
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/lock.h
@@ -0,0 +1,133 @@
+// 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_SYNCHRONIZATION_LOCK_H_
+#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/thread_annotations.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// A convenient wrapper for an OS specific critical section. The only real
+// intelligence in this class is in debug mode for the support for the
+// AssertAcquired() method.
+class LOCKABLE BASE_EXPORT Lock {
+ public:
+#if !DCHECK_IS_ON()
+ // Optimized wrapper implementation
+ Lock() : lock_() {}
+ ~Lock() {}
+
+ // TODO(lukasza): https://crbug.com/831825: Add EXCLUSIVE_LOCK_FUNCTION
+ // annotation to Acquire method and similar annotations to Release and Try
+ // methods (here and in the #else branch).
+ void Acquire() { lock_.Lock(); }
+ void Release() { lock_.Unlock(); }
+
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by another thread, immediately return false. This must not be called
+ // by a thread already holding the lock (what happens is undefined and an
+ // assertion may fail).
+ bool Try() { return lock_.Try(); }
+
+ // Null implementation if not debug.
+ void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK() {}
+#else
+ Lock();
+ ~Lock();
+
+ // NOTE: We do not permit recursive locks and will commonly fire a DCHECK() if
+ // a thread attempts to acquire the lock a second time (while already holding
+ // it).
+ void Acquire() {
+ lock_.Lock();
+ CheckUnheldAndMark();
+ }
+ void Release() {
+ CheckHeldAndUnmark();
+ lock_.Unlock();
+ }
+
+ bool Try() {
+ bool rv = lock_.Try();
+ if (rv) {
+ CheckUnheldAndMark();
+ }
+ return rv;
+ }
+
+ void AssertAcquired() const ASSERT_EXCLUSIVE_LOCK();
+#endif // DCHECK_IS_ON()
+
+ // Whether Lock mitigates priority inversion when used from different thread
+ // priorities.
+ static bool HandlesMultipleThreadPriorities() {
+#if defined(OS_WIN)
+ // Windows mitigates priority inversion by randomly boosting the priority of
+ // ready threads.
+ // https://msdn.microsoft.com/library/windows/desktop/ms684831.aspx
+ return true;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // POSIX mitigates priority inversion by setting the priority of a thread
+ // holding a Lock to the maximum priority of any other thread waiting on it.
+ return internal::LockImpl::PriorityInheritanceAvailable();
+#else
+#error Unsupported platform
+#endif
+ }
+
+ // Both Windows and POSIX implementations of ConditionVariable need to be
+ // able to see our lock and tweak our debugging counters, as they release and
+ // acquire locks inside of their condition variable APIs.
+ friend class ConditionVariable;
+
+ private:
+#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
+ // on windows but not on Posix, so we're doing unneeded checks on Posix.
+ // It's worth it to share the code.
+ void CheckHeldAndUnmark();
+ void CheckUnheldAndMark();
+
+ // All private data is implicitly protected by lock_.
+ // Be VERY careful to only access members under that lock.
+ base::PlatformThreadRef owning_thread_ref_;
+#endif // DCHECK_IS_ON()
+
+ // Platform specific underlying lock implementation.
+ internal::LockImpl lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(Lock);
+};
+
+// A helper class that acquires the given Lock while the AutoLock is in scope.
+using AutoLock = internal::BasicAutoLock<Lock>;
+
+// AutoUnlock is a helper that will Release() the |lock| argument in the
+// constructor, and re-Acquire() it in the destructor.
+using AutoUnlock = internal::BasicAutoUnlock<Lock>;
+
+// Like AutoLock but is a no-op when the provided Lock* is null. Inspired from
+// absl::MutexLockMaybe. Use this instead of base::Optional<base::AutoLock> to
+// get around -Wthread-safety-analysis warnings for conditional locking.
+using AutoLockMaybe = internal::BasicAutoLockMaybe<Lock>;
+
+// Like AutoLock but permits Release() of its mutex before destruction.
+// Release() may be called at most once. Inspired from
+// absl::ReleasableMutexLock. Use this instead of base::Optional<base::AutoLock>
+// to get around -Wthread-safety-analysis warnings for AutoLocks that are
+// explicitly released early (prefer proper scoping to this).
+using ReleasableAutoLock = internal::BasicReleasableAutoLock<Lock>;
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_LOCK_H_
diff --git a/security/sandbox/chromium/base/synchronization/lock_impl.h b/security/sandbox/chromium/base/synchronization/lock_impl.h
new file mode 100644
index 0000000000..830b878e8e
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/lock_impl.h
@@ -0,0 +1,175 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SYNCHRONIZATION_LOCK_IMPL_H_
+#define BASE_SYNCHRONIZATION_LOCK_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/thread_annotations.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_types.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <errno.h>
+#include <pthread.h>
+#endif
+
+namespace base {
+namespace internal {
+
+// This class implements the underlying platform-specific spin-lock mechanism
+// used for the Lock class. Most users should not use LockImpl directly, but
+// should instead use Lock.
+class BASE_EXPORT LockImpl {
+ public:
+#if defined(OS_WIN)
+ using NativeHandle = CHROME_SRWLOCK;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ using NativeHandle = pthread_mutex_t;
+#endif
+
+ LockImpl();
+ ~LockImpl();
+
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by something else, immediately return false.
+ bool Try();
+
+ // Take the lock, blocking until it is available if necessary.
+ void Lock();
+
+ // Release the lock. This must only be called by the lock's holder: after
+ // a successful call to Try, or a call to Lock.
+ inline void Unlock();
+
+ // Return the native underlying lock.
+ // TODO(awalker): refactor lock and condition variables so that this is
+ // unnecessary.
+ NativeHandle* native_handle() { return &native_handle_; }
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // Whether this lock will attempt to use priority inheritance.
+ static bool PriorityInheritanceAvailable();
+#endif
+
+ private:
+ NativeHandle native_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(LockImpl);
+};
+
+#if defined(OS_WIN)
+void LockImpl::Unlock() {
+ ::ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&native_handle_));
+}
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+void LockImpl::Unlock() {
+ int rv = pthread_mutex_unlock(&native_handle_);
+ DCHECK_EQ(rv, 0) << ". " << strerror(rv);
+}
+#endif
+
+// This is an implementation used for AutoLock templated on the lock type.
+template <class LockType>
+class SCOPED_LOCKABLE BasicAutoLock {
+ public:
+ struct AlreadyAcquired {};
+
+ explicit BasicAutoLock(LockType& lock) EXCLUSIVE_LOCK_FUNCTION(lock)
+ : lock_(lock) {
+ lock_.Acquire();
+ }
+
+ BasicAutoLock(LockType& lock, const AlreadyAcquired&)
+ EXCLUSIVE_LOCKS_REQUIRED(lock)
+ : lock_(lock) {
+ lock_.AssertAcquired();
+ }
+
+ ~BasicAutoLock() UNLOCK_FUNCTION() {
+ lock_.AssertAcquired();
+ lock_.Release();
+ }
+
+ private:
+ LockType& lock_;
+ DISALLOW_COPY_AND_ASSIGN(BasicAutoLock);
+};
+
+// This is an implementation used for AutoUnlock templated on the lock type.
+template <class LockType>
+class BasicAutoUnlock {
+ public:
+ explicit BasicAutoUnlock(LockType& lock) : lock_(lock) {
+ // We require our caller to have the lock.
+ lock_.AssertAcquired();
+ lock_.Release();
+ }
+
+ ~BasicAutoUnlock() { lock_.Acquire(); }
+
+ private:
+ LockType& lock_;
+ DISALLOW_COPY_AND_ASSIGN(BasicAutoUnlock);
+};
+
+// This is an implementation used for AutoLockMaybe templated on the lock type.
+template <class LockType>
+class SCOPED_LOCKABLE BasicAutoLockMaybe {
+ public:
+ explicit BasicAutoLockMaybe(LockType* lock) EXCLUSIVE_LOCK_FUNCTION(lock)
+ : lock_(lock) {
+ if (lock_)
+ lock_->Acquire();
+ }
+
+ ~BasicAutoLockMaybe() UNLOCK_FUNCTION() {
+ if (lock_) {
+ lock_->AssertAcquired();
+ lock_->Release();
+ }
+ }
+
+ private:
+ LockType* const lock_;
+ DISALLOW_COPY_AND_ASSIGN(BasicAutoLockMaybe);
+};
+
+// This is an implementation used for ReleasableAutoLock templated on the lock
+// type.
+template <class LockType>
+class SCOPED_LOCKABLE BasicReleasableAutoLock {
+ public:
+ explicit BasicReleasableAutoLock(LockType* lock) EXCLUSIVE_LOCK_FUNCTION(lock)
+ : lock_(lock) {
+ DCHECK(lock_);
+ lock_->Acquire();
+ }
+
+ ~BasicReleasableAutoLock() UNLOCK_FUNCTION() {
+ if (lock_) {
+ lock_->AssertAcquired();
+ lock_->Release();
+ }
+ }
+
+ void Release() UNLOCK_FUNCTION() {
+ DCHECK(lock_);
+ lock_->AssertAcquired();
+ lock_->Release();
+ lock_ = nullptr;
+ }
+
+ private:
+ LockType* lock_;
+ DISALLOW_COPY_AND_ASSIGN(BasicReleasableAutoLock);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_LOCK_IMPL_H_
diff --git a/security/sandbox/chromium/base/synchronization/lock_impl_posix.cc b/security/sandbox/chromium/base/synchronization/lock_impl_posix.cc
new file mode 100644
index 0000000000..7571f68a9a
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/lock_impl_posix.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/lock_impl.h"
+
+#include <string>
+
+#include "base/debug/activity_tracker.h"
+#include "base/logging.h"
+#include "base/posix/safe_strerror.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/synchronization_buildflags.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace internal {
+
+namespace {
+
+#if DCHECK_IS_ON()
+const char* AdditionalHintForSystemErrorCode(int error_code) {
+ switch (error_code) {
+ case EINVAL:
+ return "Hint: This is often related to a use-after-free.";
+ default:
+ return "";
+ }
+}
+#endif // DCHECK_IS_ON()
+
+std::string SystemErrorCodeToString(int error_code) {
+#if DCHECK_IS_ON()
+ return base::safe_strerror(error_code) + ". " +
+ AdditionalHintForSystemErrorCode(error_code);
+#else // DCHECK_IS_ON()
+ return std::string();
+#endif // DCHECK_IS_ON()
+}
+
+} // namespace
+
+// Determines which platforms can consider using priority inheritance locks. Use
+// this define for platform code that may not compile if priority inheritance
+// locks aren't available. For this platform code,
+// PRIORITY_INHERITANCE_LOCKS_POSSIBLE() is a necessary but insufficient check.
+// Lock::PriorityInheritanceAvailable still must be checked as the code may
+// compile but the underlying platform still may not correctly support priority
+// inheritance locks.
+#if defined(OS_NACL) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+#define PRIORITY_INHERITANCE_LOCKS_POSSIBLE() 0
+#else
+#define PRIORITY_INHERITANCE_LOCKS_POSSIBLE() 1
+#endif
+
+LockImpl::LockImpl() {
+ pthread_mutexattr_t mta;
+ int rv = pthread_mutexattr_init(&mta);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+#if PRIORITY_INHERITANCE_LOCKS_POSSIBLE()
+ if (PriorityInheritanceAvailable()) {
+ rv = pthread_mutexattr_setprotocol(&mta, PTHREAD_PRIO_INHERIT);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+ }
+#endif
+#ifndef NDEBUG
+ // In debug, setup attributes for lock error checking.
+ rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+#endif
+ rv = pthread_mutex_init(&native_handle_, &mta);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+ rv = pthread_mutexattr_destroy(&mta);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+}
+
+LockImpl::~LockImpl() {
+ int rv = pthread_mutex_destroy(&native_handle_);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+}
+
+bool LockImpl::Try() {
+ int rv = pthread_mutex_trylock(&native_handle_);
+ DCHECK(rv == 0 || rv == EBUSY) << ". " << SystemErrorCodeToString(rv);
+ return rv == 0;
+}
+
+void LockImpl::Lock() {
+ // The ScopedLockAcquireActivity below is relatively expensive and so its
+ // actions can become significant due to the very large number of locks
+ // that tend to be used throughout the build. To avoid this cost in the
+ // vast majority of the calls, simply "try" the lock first and only do the
+ // (tracked) blocking call if that fails. Since "try" itself is a system
+ // call, and thus also somewhat expensive, don't bother with it unless
+ // tracking is actually enabled.
+ if (base::debug::GlobalActivityTracker::IsEnabled())
+ if (Try())
+ return;
+
+ base::debug::ScopedLockAcquireActivity lock_activity(this);
+ int rv = pthread_mutex_lock(&native_handle_);
+ DCHECK_EQ(rv, 0) << ". " << SystemErrorCodeToString(rv);
+}
+
+// static
+bool LockImpl::PriorityInheritanceAvailable() {
+#if BUILDFLAG(ENABLE_MUTEX_PRIORITY_INHERITANCE)
+ return true;
+#elif PRIORITY_INHERITANCE_LOCKS_POSSIBLE() && defined(OS_MACOSX)
+ return true;
+#else
+ // Security concerns prevent the use of priority inheritance mutexes on Linux.
+ // * CVE-2010-0622 - Linux < 2.6.33-rc7, wake_futex_pi possible DoS.
+ // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0622
+ // * CVE-2012-6647 - Linux < 3.5.1, futex_wait_requeue_pi possible DoS.
+ // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6647
+ // * CVE-2014-3153 - Linux <= 3.14.5, futex_requeue, privilege escalation.
+ // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3153
+ //
+ // If the above were all addressed, we still need a runtime check to deal with
+ // the bug below.
+ // * glibc Bug 14652: https://sourceware.org/bugzilla/show_bug.cgi?id=14652
+ // Fixed in glibc 2.17.
+ // Priority inheritance mutexes may deadlock with condition variables
+ // during reacquisition of the mutex after the condition variable is
+ // signalled.
+ return false;
+#endif
+}
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/synchronization/lock_impl_win.cc b/security/sandbox/chromium/base/synchronization/lock_impl_win.cc
new file mode 100644
index 0000000000..e0c4e9d7fc
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/lock_impl_win.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/synchronization/lock_impl.h"
+
+#include "base/debug/activity_tracker.h"
+
+#include <windows.h>
+
+namespace base {
+namespace internal {
+
+LockImpl::LockImpl() : native_handle_(SRWLOCK_INIT) {}
+
+LockImpl::~LockImpl() = default;
+
+bool LockImpl::Try() {
+ return !!::TryAcquireSRWLockExclusive(
+ reinterpret_cast<PSRWLOCK>(&native_handle_));
+}
+
+void LockImpl::Lock() {
+ // The ScopedLockAcquireActivity below is relatively expensive and so its
+ // actions can become significant due to the very large number of locks
+ // that tend to be used throughout the build. To avoid this cost in the
+ // vast majority of the calls, simply "try" the lock first and only do the
+ // (tracked) blocking call if that fails. Since "try" itself is a system
+ // call, and thus also somewhat expensive, don't bother with it unless
+ // tracking is actually enabled.
+ if (base::debug::GlobalActivityTracker::IsEnabled())
+ if (Try())
+ return;
+
+ base::debug::ScopedLockAcquireActivity lock_activity(this);
+ ::AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(&native_handle_));
+}
+
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/synchronization/waitable_event.h b/security/sandbox/chromium/base/synchronization/waitable_event.h
new file mode 100644
index 0000000000..8f78084e0d
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/waitable_event.h
@@ -0,0 +1,291 @@
+// 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_SYNCHRONIZATION_WAITABLE_EVENT_H_
+#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#elif defined(OS_MACOSX)
+#include <mach/mach.h>
+
+#include <list>
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <list>
+#include <utility>
+
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#endif
+
+namespace base {
+
+class TimeDelta;
+
+// A WaitableEvent can be a useful thread synchronization tool when you want to
+// allow one thread to wait for another thread to finish some work. For
+// non-Windows systems, this can only be used from within a single address
+// space.
+//
+// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to
+// protect a simple boolean value. However, if you find yourself using a
+// WaitableEvent in conjunction with a Lock to wait for a more complex state
+// change (e.g., for an item to be added to a queue), then you should probably
+// be using a ConditionVariable instead of a WaitableEvent.
+//
+// NOTE: On Windows, this class provides a subset of the functionality afforded
+// by a Windows event object. This is intentional. If you are writing Windows
+// specific code and you need other features of a Windows event, then you might
+// be better off just using an Windows event directly.
+class BASE_EXPORT WaitableEvent {
+ public:
+ // Indicates whether a WaitableEvent should automatically reset the event
+ // state after a single waiting thread has been released or remain signaled
+ // until Reset() is manually invoked.
+ enum class ResetPolicy { MANUAL, AUTOMATIC };
+
+ // Indicates whether a new WaitableEvent should start in a signaled state or
+ // not.
+ enum class InitialState { SIGNALED, NOT_SIGNALED };
+
+ // Constructs a WaitableEvent with policy and initial state as detailed in
+ // the above enums.
+ WaitableEvent(ResetPolicy reset_policy = ResetPolicy::MANUAL,
+ InitialState initial_state = InitialState::NOT_SIGNALED);
+
+#if defined(OS_WIN)
+ // 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.
+ explicit WaitableEvent(win::ScopedHandle event_handle);
+#endif
+
+ ~WaitableEvent();
+
+ // Put the event in the un-signaled state.
+ void Reset();
+
+ // Put the event in the signaled state. Causing any thread blocked on Wait
+ // to be woken up.
+ void Signal();
+
+ // Returns true if the event is in the signaled state, else false. If this
+ // is not a manual reset event, then this test will cause a reset.
+ bool IsSignaled();
+
+ // Wait indefinitely for the event to be signaled. Wait's return "happens
+ // after" |Signal| has completed. This means that it's safe for a
+ // WaitableEvent to synchronise its own destruction, like this:
+ //
+ // WaitableEvent *e = new WaitableEvent;
+ // SendToOtherThread(e);
+ // e->Wait();
+ // delete e;
+ void Wait();
+
+ // Wait up until wait_delta has passed for the event to be signaled
+ // (real-time; ignores time overrides). Returns true if the event was
+ // signaled. Handles spurious wakeups and guarantees that |wait_delta| will
+ // have elapsed if this returns false.
+ //
+ // TimedWait can synchronise its own destruction like |Wait|.
+ bool TimedWait(const TimeDelta& wait_delta);
+
+#if defined(OS_WIN)
+ HANDLE handle() const { return handle_.Get(); }
+#endif
+
+ // Declares that this WaitableEvent will only ever be used by a thread that is
+ // idle at the bottom of its stack and waiting for work (in particular, it is
+ // not synchronously waiting on this event before resuming ongoing work). This
+ // is useful to avoid telling base-internals that this thread is "blocked"
+ // when it's merely idle and ready to do work. As such, this is only expected
+ // to be used by thread and thread pool impls.
+ void declare_only_used_while_idle() { waiting_is_blocking_ = false; }
+
+ // Wait, synchronously, on multiple events.
+ // waitables: an array of WaitableEvent pointers
+ // count: the number of elements in @waitables
+ //
+ // returns: the index of a WaitableEvent which has been signaled.
+ //
+ // You MUST NOT delete any of the WaitableEvent objects while this wait is
+ // happening, however WaitMany's return "happens after" the |Signal| call
+ // that caused it has completed, like |Wait|.
+ //
+ // If more than one WaitableEvent is signaled to unblock WaitMany, the lowest
+ // index among them is returned.
+ static size_t WaitMany(WaitableEvent** waitables, size_t count);
+
+ // For asynchronous waiting, see WaitableEventWatcher
+
+ // This is a private helper class. It's here because it's used by friends of
+ // this class (such as WaitableEventWatcher) to be able to enqueue elements
+ // of the wait-list
+ class Waiter {
+ public:
+ // Signal the waiter to wake up.
+ //
+ // Consider the case of a Waiter which is in multiple WaitableEvent's
+ // wait-lists. Each WaitableEvent is automatic-reset and two of them are
+ // signaled at the same time. Now, each will wake only the first waiter in
+ // the wake-list before resetting. However, if those two waiters happen to
+ // be the same object (as can happen if another thread didn't have a chance
+ // to dequeue the waiter from the other wait-list in time), two auto-resets
+ // will have happened, but only one waiter has been signaled!
+ //
+ // Because of this, a Waiter may "reject" a wake by returning false. In
+ // this case, the auto-reset WaitableEvent shouldn't act as if anything has
+ // been notified.
+ virtual bool Fire(WaitableEvent* signaling_event) = 0;
+
+ // Waiters may implement this in order to provide an extra condition for
+ // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the
+ // pointers match then this function is called as a final check. See the
+ // comments in ~Handle for why.
+ virtual bool Compare(void* tag) = 0;
+
+ protected:
+ virtual ~Waiter() = default;
+ };
+
+ private:
+ friend class WaitableEventWatcher;
+
+#if defined(OS_WIN)
+ win::ScopedHandle handle_;
+#elif defined(OS_MACOSX)
+ // Prior to macOS 10.12, a TYPE_MACH_RECV dispatch source may not be invoked
+ // immediately. If a WaitableEventWatcher is used on a manual-reset event,
+ // and another thread that is Wait()ing on the event calls Reset()
+ // immediately after waking up, the watcher may not receive the callback.
+ // On macOS 10.12 and higher, dispatch delivery is reliable. But for OSes
+ // prior, a lock-protected list of callbacks is used for manual-reset event
+ // watchers. Automatic-reset events are not prone to this issue, since the
+ // first thread to wake will claim the event.
+ static bool UseSlowWatchList(ResetPolicy policy);
+
+ // Peeks the message queue named by |port| and returns true if a message
+ // is present and false if not. If |dequeue| is true, the messsage will be
+ // drained from the queue. If |dequeue| is false, the queue will only be
+ // peeked. |port| must be a receive right.
+ static bool PeekPort(mach_port_t port, bool dequeue);
+
+ // The Mach receive right is waited on by both WaitableEvent and
+ // WaitableEventWatcher. It is valid to signal and then delete an event, and
+ // a watcher should still be notified. If the right were to be destroyed
+ // immediately, the watcher would not receive the signal. Because Mach
+ // receive rights cannot have a user refcount greater than one, the right
+ // must be reference-counted manually.
+ class ReceiveRight : public RefCountedThreadSafe<ReceiveRight> {
+ public:
+ ReceiveRight(mach_port_t name, bool create_slow_watch_list);
+
+ mach_port_t Name() const { return right_.get(); }
+
+ // This structure is used iff UseSlowWatchList() is true. See the comment
+ // in Signal() for details.
+ struct WatchList {
+ WatchList();
+ ~WatchList();
+
+ // The lock protects a list of closures to be run when the event is
+ // Signal()ed. The closures are invoked on the signaling thread, so they
+ // must be safe to be called from any thread.
+ Lock lock;
+ std::list<OnceClosure> list;
+ };
+
+ WatchList* SlowWatchList() const { return slow_watch_list_.get(); }
+
+ private:
+ friend class RefCountedThreadSafe<ReceiveRight>;
+ ~ReceiveRight();
+
+ mac::ScopedMachReceiveRight right_;
+
+ // This is allocated iff UseSlowWatchList() is true. It is created on the
+ // heap to avoid performing initialization when not using the slow path.
+ std::unique_ptr<WatchList> slow_watch_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReceiveRight);
+ };
+
+ const ResetPolicy policy_;
+
+ // The receive right for the event.
+ scoped_refptr<ReceiveRight> receive_right_;
+
+ // The send right used to signal the event. This can be disposed of with
+ // the event, unlike the receive right, since a deleted event cannot be
+ // signaled.
+ mac::ScopedMachSendRight send_right_;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // On Windows, you must not close a HANDLE which is currently being waited on.
+ // The MSDN documentation says that the resulting behaviour is 'undefined'.
+ // To solve that issue each WaitableEventWatcher duplicates the given event
+ // handle.
+
+ // However, if we were to include the following members
+ // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an
+ // event which gets deleted. This mismatch has bitten us several times now,
+ // so we have a kernel of the WaitableEvent, which is reference counted.
+ // WaitableEventWatchers may then take a reference and thus match the Windows
+ // behaviour.
+ struct WaitableEventKernel :
+ public RefCountedThreadSafe<WaitableEventKernel> {
+ public:
+ WaitableEventKernel(ResetPolicy reset_policy, InitialState initial_state);
+
+ bool Dequeue(Waiter* waiter, void* tag);
+
+ base::Lock lock_;
+ const bool manual_reset_;
+ bool signaled_;
+ std::list<Waiter*> waiters_;
+
+ private:
+ friend class RefCountedThreadSafe<WaitableEventKernel>;
+ ~WaitableEventKernel();
+ };
+
+ typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex;
+
+ // When dealing with arrays of WaitableEvent*, we want to sort by the address
+ // of the WaitableEvent in order to have a globally consistent locking order.
+ // In that case we keep them, in sorted order, in an array of pairs where the
+ // second element is the index of the WaitableEvent in the original,
+ // unsorted, array.
+ static size_t EnqueueMany(WaiterAndIndex* waitables,
+ size_t count, Waiter* waiter);
+
+ bool SignalAll();
+ bool SignalOne();
+ void Enqueue(Waiter* waiter);
+
+ scoped_refptr<WaitableEventKernel> kernel_;
+#endif
+
+ // Whether a thread invoking Wait() on this WaitableEvent should be considered
+ // blocked as opposed to idle (and potentially replaced if part of a pool).
+ bool waiting_is_blocking_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(WaitableEvent);
+};
+
+} // namespace base
+
+#endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_
diff --git a/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc b/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc
new file mode 100644
index 0000000000..effa899191
--- /dev/null
+++ b/security/sandbox/chromium/base/synchronization/waitable_event_posix.cc
@@ -0,0 +1,445 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "base/debug/activity_tracker.h"
+#include "base/logging.h"
+#include "base/optional.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time.h"
+#include "base/time/time_override.h"
+
+// -----------------------------------------------------------------------------
+// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't
+// support cross-process events (where one process can signal an event which
+// others are waiting on). Because of this, we can avoid having one thread per
+// listener in several cases.
+//
+// The WaitableEvent maintains a list of waiters, protected by a lock. Each
+// waiter is either an async wait, in which case we have a Task and the
+// MessageLoop to run it on, or a blocking wait, in which case we have the
+// condition variable to signal.
+//
+// Waiting involves grabbing the lock and adding oneself to the wait list. Async
+// waits can be canceled, which means grabbing the lock and removing oneself
+// from the list.
+//
+// Waiting on multiple events is handled by adding a single, synchronous wait to
+// the wait-list of many events. An event passes a pointer to itself when
+// firing a waiter and so we can store that pointer to find out which event
+// triggered.
+// -----------------------------------------------------------------------------
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// This is just an abstract base class for waking the two types of waiters
+// -----------------------------------------------------------------------------
+WaitableEvent::WaitableEvent(ResetPolicy reset_policy,
+ InitialState initial_state)
+ : kernel_(new WaitableEventKernel(reset_policy, initial_state)) {}
+
+WaitableEvent::~WaitableEvent() = default;
+
+void WaitableEvent::Reset() {
+ base::AutoLock locked(kernel_->lock_);
+ kernel_->signaled_ = false;
+}
+
+void WaitableEvent::Signal() {
+ base::AutoLock locked(kernel_->lock_);
+
+ if (kernel_->signaled_)
+ return;
+
+ if (kernel_->manual_reset_) {
+ SignalAll();
+ kernel_->signaled_ = true;
+ } else {
+ // In the case of auto reset, if no waiters were woken, we remain
+ // signaled.
+ if (!SignalOne())
+ kernel_->signaled_ = true;
+ }
+}
+
+bool WaitableEvent::IsSignaled() {
+ base::AutoLock locked(kernel_->lock_);
+
+ const bool result = kernel_->signaled_;
+ if (result && !kernel_->manual_reset_)
+ kernel_->signaled_ = false;
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+// Synchronous waits
+
+// -----------------------------------------------------------------------------
+// This is a synchronous waiter. The thread is waiting on the given condition
+// variable and the fired flag in this object.
+// -----------------------------------------------------------------------------
+class SyncWaiter : public WaitableEvent::Waiter {
+ public:
+ SyncWaiter()
+ : fired_(false), signaling_event_(nullptr), lock_(), cv_(&lock_) {}
+
+ bool Fire(WaitableEvent* signaling_event) override {
+ base::AutoLock locked(lock_);
+
+ if (fired_)
+ return false;
+
+ fired_ = true;
+ signaling_event_ = signaling_event;
+
+ cv_.Broadcast();
+
+ // Unlike AsyncWaiter objects, SyncWaiter objects are stack-allocated on
+ // the blocking thread's stack. There is no |delete this;| in Fire. The
+ // SyncWaiter object is destroyed when it goes out of scope.
+
+ return true;
+ }
+
+ WaitableEvent* signaling_event() const {
+ return signaling_event_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // These waiters are always stack allocated and don't delete themselves. Thus
+ // there's no problem and the ABA tag is the same as the object pointer.
+ // ---------------------------------------------------------------------------
+ bool Compare(void* tag) override { return this == tag; }
+
+ // ---------------------------------------------------------------------------
+ // Called with lock held.
+ // ---------------------------------------------------------------------------
+ bool fired() const {
+ return fired_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // During a TimedWait, we need a way to make sure that an auto-reset
+ // WaitableEvent doesn't think that this event has been signaled between
+ // unlocking it and removing it from the wait-list. Called with lock held.
+ // ---------------------------------------------------------------------------
+ void Disable() {
+ fired_ = true;
+ }
+
+ base::Lock* lock() {
+ return &lock_;
+ }
+
+ base::ConditionVariable* cv() {
+ return &cv_;
+ }
+
+ private:
+ bool fired_;
+ WaitableEvent* signaling_event_; // The WaitableEvent which woke us
+ base::Lock lock_;
+ base::ConditionVariable cv_;
+};
+
+void WaitableEvent::Wait() {
+ bool result = TimedWait(TimeDelta::Max());
+ DCHECK(result) << "TimedWait() should never fail with infinite timeout";
+}
+
+bool WaitableEvent::TimedWait(const TimeDelta& wait_delta) {
+ if (wait_delta <= TimeDelta())
+ return IsSignaled();
+
+ // Record the event that this thread is blocking upon (for hang diagnosis) and
+ // consider it blocked for scheduling purposes. Ignore this for non-blocking
+ // WaitableEvents.
+ Optional<debug::ScopedEventWaitActivity> event_activity;
+ Optional<internal::ScopedBlockingCallWithBaseSyncPrimitives>
+ scoped_blocking_call;
+ if (waiting_is_blocking_) {
+ event_activity.emplace(this);
+ scoped_blocking_call.emplace(FROM_HERE, BlockingType::MAY_BLOCK);
+ }
+
+ kernel_->lock_.Acquire();
+ if (kernel_->signaled_) {
+ if (!kernel_->manual_reset_) {
+ // In this case we were signaled when we had no waiters. Now that
+ // someone has waited upon us, we can automatically reset.
+ kernel_->signaled_ = false;
+ }
+
+ kernel_->lock_.Release();
+ return true;
+ }
+
+ SyncWaiter sw;
+ if (!waiting_is_blocking_)
+ sw.cv()->declare_only_used_while_idle();
+ sw.lock()->Acquire();
+
+ Enqueue(&sw);
+ kernel_->lock_.Release();
+ // We are violating locking order here by holding the SyncWaiter lock but not
+ // the WaitableEvent lock. However, this is safe because we don't lock |lock_|
+ // again before unlocking it.
+
+ // TimeTicks takes care of overflow but we special case is_max() nonetheless
+ // to avoid invoking TimeTicksNowIgnoringOverride() unnecessarily (same for
+ // the increment step of the for loop if the condition variable returns
+ // early). Ref: https://crbug.com/910524#c7
+ const TimeTicks end_time =
+ wait_delta.is_max() ? TimeTicks::Max()
+ : subtle::TimeTicksNowIgnoringOverride() + wait_delta;
+ for (TimeDelta remaining = wait_delta; remaining > TimeDelta() && !sw.fired();
+ remaining = end_time.is_max()
+ ? TimeDelta::Max()
+ : end_time - subtle::TimeTicksNowIgnoringOverride()) {
+ if (end_time.is_max())
+ sw.cv()->Wait();
+ else
+ sw.cv()->TimedWait(remaining);
+ }
+
+ // Get the SyncWaiter signaled state before releasing the lock.
+ const bool return_value = sw.fired();
+
+ // We can't acquire |lock_| before releasing the SyncWaiter lock (because of
+ // locking order), however, in between the two a signal could be fired and
+ // |sw| would accept it, however we will still return false, so the signal
+ // would be lost on an auto-reset WaitableEvent. Thus we call Disable which
+ // makes sw::Fire return false.
+ sw.Disable();
+ sw.lock()->Release();
+
+ // This is a bug that has been enshrined in the interface of WaitableEvent
+ // now: |Dequeue| is called even when |sw.fired()| is true, even though it'll
+ // always return false in that case. However, taking the lock ensures that
+ // |Signal| has completed before we return and means that a WaitableEvent can
+ // synchronise its own destruction.
+ kernel_->lock_.Acquire();
+ kernel_->Dequeue(&sw, &sw);
+ kernel_->lock_.Release();
+
+ return return_value;
+}
+
+// -----------------------------------------------------------------------------
+// Synchronous waiting on multiple objects.
+
+static bool // StrictWeakOrdering
+cmp_fst_addr(const std::pair<WaitableEvent*, unsigned> &a,
+ const std::pair<WaitableEvent*, unsigned> &b) {
+ return a.first < b.first;
+}
+
+// static
+size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables,
+ size_t count) {
+ DCHECK(count) << "Cannot wait on no events";
+ internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
+ FROM_HERE, BlockingType::MAY_BLOCK);
+ // Record an event (the first) that this thread is blocking upon.
+ debug::ScopedEventWaitActivity event_activity(raw_waitables[0]);
+
+ // We need to acquire the locks in a globally consistent order. Thus we sort
+ // the array of waitables by address. We actually sort a pairs so that we can
+ // map back to the original index values later.
+ std::vector<std::pair<WaitableEvent*, size_t> > waitables;
+ waitables.reserve(count);
+ for (size_t i = 0; i < count; ++i)
+ waitables.push_back(std::make_pair(raw_waitables[i], i));
+
+ DCHECK_EQ(count, waitables.size());
+
+ sort(waitables.begin(), waitables.end(), cmp_fst_addr);
+
+ // The set of waitables must be distinct. Since we have just sorted by
+ // address, we can check this cheaply by comparing pairs of consecutive
+ // elements.
+ for (size_t i = 0; i < waitables.size() - 1; ++i) {
+ DCHECK(waitables[i].first != waitables[i+1].first);
+ }
+
+ SyncWaiter sw;
+
+ const size_t r = EnqueueMany(&waitables[0], count, &sw);
+ if (r < count) {
+ // One of the events is already signaled. The SyncWaiter has not been
+ // enqueued anywhere.
+ return waitables[r].second;
+ }
+
+ // At this point, we hold the locks on all the WaitableEvents and we have
+ // enqueued our waiter in them all.
+ sw.lock()->Acquire();
+ // Release the WaitableEvent locks in the reverse order
+ for (size_t i = 0; i < count; ++i) {
+ waitables[count - (1 + i)].first->kernel_->lock_.Release();
+ }
+
+ for (;;) {
+ if (sw.fired())
+ break;
+
+ sw.cv()->Wait();
+ }
+ sw.lock()->Release();
+
+ // The address of the WaitableEvent which fired is stored in the SyncWaiter.
+ WaitableEvent *const signaled_event = sw.signaling_event();
+ // This will store the index of the raw_waitables which fired.
+ size_t signaled_index = 0;
+
+ // Take the locks of each WaitableEvent in turn (except the signaled one) and
+ // remove our SyncWaiter from the wait-list
+ for (size_t i = 0; i < count; ++i) {
+ if (raw_waitables[i] != signaled_event) {
+ raw_waitables[i]->kernel_->lock_.Acquire();
+ // There's no possible ABA issue with the address of the SyncWaiter here
+ // because it lives on the stack. Thus the tag value is just the pointer
+ // value again.
+ raw_waitables[i]->kernel_->Dequeue(&sw, &sw);
+ raw_waitables[i]->kernel_->lock_.Release();
+ } else {
+ // By taking this lock here we ensure that |Signal| has completed by the
+ // time we return, because |Signal| holds this lock. This matches the
+ // behaviour of |Wait| and |TimedWait|.
+ raw_waitables[i]->kernel_->lock_.Acquire();
+ raw_waitables[i]->kernel_->lock_.Release();
+ signaled_index = i;
+ }
+ }
+
+ return signaled_index;
+}
+
+// -----------------------------------------------------------------------------
+// If return value == count:
+// The locks of the WaitableEvents have been taken in order and the Waiter has
+// been enqueued in the wait-list of each. None of the WaitableEvents are
+// currently signaled
+// else:
+// None of the WaitableEvent locks are held. The Waiter has not been enqueued
+// in any of them and the return value is the index of the WaitableEvent which
+// was signaled with the lowest input index from the original WaitMany call.
+// -----------------------------------------------------------------------------
+// static
+size_t WaitableEvent::EnqueueMany(std::pair<WaitableEvent*, size_t>* waitables,
+ size_t count,
+ Waiter* waiter) {
+ size_t winner = count;
+ size_t winner_index = count;
+ for (size_t i = 0; i < count; ++i) {
+ auto& kernel = waitables[i].first->kernel_;
+ kernel->lock_.Acquire();
+ if (kernel->signaled_ && waitables[i].second < winner) {
+ winner = waitables[i].second;
+ winner_index = i;
+ }
+ }
+
+ // No events signaled. All locks acquired. Enqueue the Waiter on all of them
+ // and return.
+ if (winner == count) {
+ for (size_t i = 0; i < count; ++i)
+ waitables[i].first->Enqueue(waiter);
+ return count;
+ }
+
+ // Unlock in reverse order and possibly clear the chosen winner's signal
+ // before returning its index.
+ for (auto* w = waitables + count - 1; w >= waitables; --w) {
+ auto& kernel = w->first->kernel_;
+ if (w->second == winner) {
+ if (!kernel->manual_reset_)
+ kernel->signaled_ = false;
+ }
+ kernel->lock_.Release();
+ }
+
+ return winner_index;
+}
+
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// Private functions...
+
+WaitableEvent::WaitableEventKernel::WaitableEventKernel(
+ ResetPolicy reset_policy,
+ InitialState initial_state)
+ : manual_reset_(reset_policy == ResetPolicy::MANUAL),
+ signaled_(initial_state == InitialState::SIGNALED) {}
+
+WaitableEvent::WaitableEventKernel::~WaitableEventKernel() = default;
+
+// -----------------------------------------------------------------------------
+// Wake all waiting waiters. Called with lock held.
+// -----------------------------------------------------------------------------
+bool WaitableEvent::SignalAll() {
+ bool signaled_at_least_one = false;
+
+ for (auto* i : kernel_->waiters_) {
+ if (i->Fire(this))
+ signaled_at_least_one = true;
+ }
+
+ kernel_->waiters_.clear();
+ return signaled_at_least_one;
+}
+
+// ---------------------------------------------------------------------------
+// Try to wake a single waiter. Return true if one was woken. Called with lock
+// held.
+// ---------------------------------------------------------------------------
+bool WaitableEvent::SignalOne() {
+ for (;;) {
+ if (kernel_->waiters_.empty())
+ return false;
+
+ const bool r = (*kernel_->waiters_.begin())->Fire(this);
+ kernel_->waiters_.pop_front();
+ if (r)
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Add a waiter to the list of those waiting. Called with lock held.
+// -----------------------------------------------------------------------------
+void WaitableEvent::Enqueue(Waiter* waiter) {
+ kernel_->waiters_.push_back(waiter);
+}
+
+// -----------------------------------------------------------------------------
+// Remove a waiter from the list of those waiting. Return true if the waiter was
+// actually removed. Called with lock held.
+// -----------------------------------------------------------------------------
+bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) {
+ for (auto i = waiters_.begin(); i != waiters_.end(); ++i) {
+ if (*i == waiter && (*i)->Compare(tag)) {
+ waiters_.erase(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/task_runner.h b/security/sandbox/chromium/base/task_runner.h
new file mode 100644
index 0000000000..8abf5ef4ba
--- /dev/null
+++ b/security/sandbox/chromium/base/task_runner.h
@@ -0,0 +1,136 @@
+// 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_TASK_RUNNER_H_
+#define BASE_TASK_RUNNER_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+
+namespace base {
+
+struct TaskRunnerTraits;
+
+// A TaskRunner is an object that runs posted tasks (in the form of
+// OnceClosure objects). The TaskRunner interface provides a way of
+// decoupling task posting from the mechanics of how each task will be
+// run. TaskRunner provides very weak guarantees as to how posted
+// tasks are run (or if they're run at all). In particular, it only
+// guarantees:
+//
+// - Posting a task will not run it synchronously. That is, no
+// Post*Task method will call task.Run() directly.
+//
+// - Increasing the delay can only delay when the task gets run.
+// That is, increasing the delay may not affect when the task gets
+// run, or it could make it run later than it normally would, but
+// it won't make it run earlier than it normally would.
+//
+// TaskRunner does not guarantee the order in which posted tasks are
+// run, whether tasks overlap, or whether they're run on a particular
+// thread. Also it does not guarantee a memory model for shared data
+// between tasks. (In other words, you should use your own
+// synchronization/locking primitives if you need to share data
+// between tasks.)
+//
+// Implementations of TaskRunner should be thread-safe in that all
+// methods must be safe to call on any thread. Ownership semantics
+// for TaskRunners are in general not clear, which is why the
+// interface itself is RefCountedThreadSafe.
+//
+// Some theoretical implementations of TaskRunner:
+//
+// - A TaskRunner that uses a thread pool to run posted tasks.
+//
+// - A TaskRunner that, for each task, spawns a non-joinable thread
+// to run that task and immediately quit.
+//
+// - A TaskRunner that stores the list of posted tasks and has a
+// method Run() that runs each runnable task in random order.
+class BASE_EXPORT TaskRunner
+ : public RefCountedThreadSafe<TaskRunner, TaskRunnerTraits> {
+ public:
+ // Posts the given task to be run. Returns true if the task may be
+ // run at some point in the future, and false if the task definitely
+ // will not be run.
+ //
+ // Equivalent to PostDelayedTask(from_here, task, 0).
+ bool PostTask(const Location& from_here, OnceClosure task);
+
+ // Like PostTask, but tries to run the posted task only after |delay_ms|
+ // has passed. Implementations should use a tick clock, rather than wall-
+ // clock time, to implement |delay|.
+ virtual bool PostDelayedTask(const Location& from_here,
+ OnceClosure task,
+ base::TimeDelta delay) = 0;
+
+ // Posts |task| on the current TaskRunner. On completion, |reply|
+ // is posted to the thread that called PostTaskAndReply(). Both
+ // |task| and |reply| are guaranteed to be deleted on the thread
+ // from which PostTaskAndReply() is invoked. This allows objects
+ // that must be deleted on the originating thread to be bound into
+ // the |task| and |reply| OnceClosures. In particular, it can be useful
+ // to use WeakPtr<> in the |reply| OnceClosure so that the reply
+ // operation can be canceled. See the following pseudo-code:
+ //
+ // class DataBuffer : public RefCountedThreadSafe<DataBuffer> {
+ // public:
+ // // Called to add data into a buffer.
+ // void AddData(void* buf, size_t length);
+ // ...
+ // };
+ //
+ //
+ // class DataLoader : public SupportsWeakPtr<DataLoader> {
+ // public:
+ // void GetData() {
+ // scoped_refptr<DataBuffer> buffer = new DataBuffer();
+ // target_thread_.task_runner()->PostTaskAndReply(
+ // FROM_HERE,
+ // base::BindOnce(&DataBuffer::AddData, buffer),
+ // base::BindOnce(&DataLoader::OnDataReceived, AsWeakPtr(), buffer));
+ // }
+ //
+ // private:
+ // void OnDataReceived(scoped_refptr<DataBuffer> buffer) {
+ // // Do something with buffer.
+ // }
+ // };
+ //
+ //
+ // Things to notice:
+ // * Results of |task| are shared with |reply| by binding a shared argument
+ // (a DataBuffer instance).
+ // * The DataLoader object has no special thread safety.
+ // * The DataLoader object can be deleted while |task| is still running,
+ // and the reply will cancel itself safely because it is bound to a
+ // WeakPtr<>.
+ bool PostTaskAndReply(const Location& from_here,
+ OnceClosure task,
+ OnceClosure reply);
+
+ protected:
+ friend struct TaskRunnerTraits;
+
+ TaskRunner();
+ virtual ~TaskRunner();
+
+ // Called when this object should be destroyed. By default simply
+ // deletes |this|, but can be overridden to do something else, like
+ // delete on a certain thread.
+ virtual void OnDestruct() const;
+};
+
+struct BASE_EXPORT TaskRunnerTraits {
+ static void Destruct(const TaskRunner* task_runner);
+};
+
+} // namespace base
+
+#endif // BASE_TASK_RUNNER_H_
diff --git a/security/sandbox/chromium/base/template_util.h b/security/sandbox/chromium/base/template_util.h
new file mode 100644
index 0000000000..51bd085279
--- /dev/null
+++ b/security/sandbox/chromium/base/template_util.h
@@ -0,0 +1,188 @@
+// 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_TEMPLATE_UTIL_H_
+#define BASE_TEMPLATE_UTIL_H_
+
+#include <stddef.h>
+#include <iosfwd>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "build/build_config.h"
+
+// Some versions of libstdc++ have partial support for type_traits, but misses
+// a smaller subset while removing some of the older non-standard stuff. Assume
+// that all versions below 5.0 fall in this category, along with one 5.0
+// experimental release. Test for this by consulting compiler major version,
+// the only reliable option available, so theoretically this could fail should
+// you attempt to mix an earlier version of libstdc++ with >= GCC5. But
+// that's unlikely to work out, especially as GCC5 changed ABI.
+#define CR_GLIBCXX_5_0_0 20150123
+#if (defined(__GNUC__) && __GNUC__ < 5) || \
+ (defined(__GLIBCXX__) && __GLIBCXX__ == CR_GLIBCXX_5_0_0)
+#define CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
+#endif
+
+// This hacks around using gcc with libc++ which has some incompatibilies.
+// - is_trivially_* doesn't work: https://llvm.org/bugs/show_bug.cgi?id=27538
+// TODO(danakj): Remove this when android builders are all using a newer version
+// of gcc, or the android ndk is updated to a newer libc++ that works with older
+// gcc versions.
+#if !defined(__clang__) && defined(_LIBCPP_VERSION)
+#define CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
+#endif
+
+namespace base {
+
+template <class T> struct is_non_const_reference : std::false_type {};
+template <class T> struct is_non_const_reference<T&> : std::true_type {};
+template <class T> struct is_non_const_reference<const T&> : std::false_type {};
+
+namespace internal {
+
+// Implementation detail of base::void_t below.
+template <typename...>
+struct make_void {
+ using type = void;
+};
+
+} // namespace internal
+
+// base::void_t is an implementation of std::void_t from C++17.
+//
+// We use |base::internal::make_void| as a helper struct to avoid a C++14
+// defect:
+// http://en.cppreference.com/w/cpp/types/void_t
+// http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558
+template <typename... Ts>
+using void_t = typename ::base::internal::make_void<Ts...>::type;
+
+namespace internal {
+
+// Uses expression SFINAE to detect whether using operator<< would work.
+template <typename T, typename = void>
+struct SupportsOstreamOperator : std::false_type {};
+template <typename T>
+struct SupportsOstreamOperator<T,
+ decltype(void(std::declval<std::ostream&>()
+ << std::declval<T>()))>
+ : std::true_type {};
+
+template <typename T, typename = void>
+struct SupportsToString : std::false_type {};
+template <typename T>
+struct SupportsToString<T, decltype(void(std::declval<T>().ToString()))>
+ : std::true_type {};
+
+// Used to detech whether the given type is an iterator. This is normally used
+// with std::enable_if to provide disambiguation for functions that take
+// templatzed iterators as input.
+template <typename T, typename = void>
+struct is_iterator : std::false_type {};
+
+template <typename T>
+struct is_iterator<T,
+ void_t<typename std::iterator_traits<T>::iterator_category>>
+ : std::true_type {};
+
+} // namespace internal
+
+// is_trivially_copyable is especially hard to get right.
+// - Older versions of libstdc++ will fail to have it like they do for other
+// type traits. This has become a subset of the second point, but used to be
+// handled independently.
+// - An experimental release of gcc includes most of type_traits but misses
+// is_trivially_copyable, so we still have to avoid using libstdc++ in this
+// case, which is covered by CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX.
+// - When compiling libc++ from before r239653, with a gcc compiler, the
+// std::is_trivially_copyable can fail. So we need to work around that by not
+// using the one in libc++ in this case. This is covered by the
+// CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX define, and is discussed in
+// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 where they point out that
+// in libc++'s commit r239653 this is fixed by libc++ checking for gcc 5.1.
+// - In both of the above cases we are using the gcc compiler. When defining
+// this ourselves on compiler intrinsics, the __is_trivially_copyable()
+// intrinsic is not available on gcc before version 5.1 (see the discussion in
+// https://llvm.org/bugs/show_bug.cgi?id=27538#c1 again), so we must check for
+// that version.
+// - When __is_trivially_copyable() is not available because we are on gcc older
+// than 5.1, we need to fall back to something, so we use __has_trivial_copy()
+// instead based on what was done one-off in bit_cast() previously.
+
+// TODO(crbug.com/554293): Remove this when all platforms have this in the std
+// namespace and it works with gcc as needed.
+#if defined(CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX) || \
+ defined(CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX)
+template <typename T>
+struct is_trivially_copyable {
+// TODO(danakj): Remove this when android builders are all using a newer version
+// of gcc, or the android ndk is updated to a newer libc++ that does this for
+// us.
+#if _GNUC_VER >= 501
+ static constexpr bool value = __is_trivially_copyable(T);
+#else
+ static constexpr bool value =
+ __has_trivial_copy(T) && __has_trivial_destructor(T);
+#endif
+};
+#else
+template <class T>
+using is_trivially_copyable = std::is_trivially_copyable<T>;
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 7
+// Workaround for g++7 and earlier family.
+// Due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80654, without this
+// Optional<std::vector<T>> where T is non-copyable causes a compile error.
+// As we know it is not trivially copy constructible, explicitly declare so.
+template <typename T>
+struct is_trivially_copy_constructible
+ : std::is_trivially_copy_constructible<T> {};
+
+template <typename... T>
+struct is_trivially_copy_constructible<std::vector<T...>> : std::false_type {};
+#else
+// Otherwise use std::is_trivially_copy_constructible as is.
+template <typename T>
+using is_trivially_copy_constructible = std::is_trivially_copy_constructible<T>;
+#endif
+
+// base::in_place_t is an implementation of std::in_place_t from
+// C++17. A tag type used to request in-place construction in template vararg
+// constructors.
+
+// Specification:
+// https://en.cppreference.com/w/cpp/utility/in_place
+struct in_place_t {};
+constexpr in_place_t in_place = {};
+
+// base::in_place_type_t is an implementation of std::in_place_type_t from
+// C++17. A tag type used for in-place construction when the type to construct
+// needs to be specified, such as with base::unique_any, designed to be a
+// drop-in replacement.
+
+// Specification:
+// http://en.cppreference.com/w/cpp/utility/in_place
+template <typename T>
+struct in_place_type_t {};
+
+template <typename T>
+struct is_in_place_type_t {
+ static constexpr bool value = false;
+};
+
+template <typename... Ts>
+struct is_in_place_type_t<in_place_type_t<Ts...>> {
+ static constexpr bool value = true;
+};
+
+} // namespace base
+
+#undef CR_USE_FALLBACKS_FOR_GCC_WITH_LIBCXX
+#undef CR_USE_FALLBACKS_FOR_OLD_EXPERIMENTAL_GLIBCXX
+
+#endif // BASE_TEMPLATE_UTIL_H_
diff --git a/security/sandbox/chromium/base/third_party/cityhash/COPYING b/security/sandbox/chromium/base/third_party/cityhash/COPYING
new file mode 100644
index 0000000000..bf15194dd5
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/cityhash/COPYING
@@ -0,0 +1,19 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
diff --git a/security/sandbox/chromium/base/third_party/cityhash/city.cc b/security/sandbox/chromium/base/third_party/cityhash/city.cc
new file mode 100644
index 0000000000..b0d2294aab
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/cityhash/city.cc
@@ -0,0 +1,532 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// CityHash, by Geoff Pike and Jyrki Alakuijala
+//
+// This file provides CityHash64() and related functions.
+//
+// It's probably possible to create even faster hash functions by
+// writing a program that systematically explores some of the space of
+// possible hash functions, by using SIMD instructions, or by
+// compromising on hash quality.
+
+#include "city.h"
+
+#include <string.h> // for memcpy and memset
+#include <algorithm>
+
+using std::make_pair;
+using std::pair;
+
+#ifdef _MSC_VER
+
+#include <stdlib.h>
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+
+// Mac OS X / Darwin features
+#include <libkern/OSByteOrder.h>
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif defined(__sun) || defined(sun)
+
+#include <sys/byteorder.h>
+#define bswap_32(x) BSWAP_32(x)
+#define bswap_64(x) BSWAP_64(x)
+
+#elif defined(__FreeBSD__)
+
+#include <sys/endian.h>
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+
+#elif defined(__OpenBSD__)
+
+#include <sys/types.h>
+#define bswap_32(x) swap32(x)
+#define bswap_64(x) swap64(x)
+
+#elif defined(__NetBSD__)
+
+#include <machine/bswap.h>
+#include <sys/types.h>
+#if defined(__BSWAP_RENAME) && !defined(__bswap_32)
+#define bswap_32(x) bswap32(x)
+#define bswap_64(x) bswap64(x)
+#endif
+
+#else
+
+// XXX(cavalcanti): building 'native_client' fails with this header.
+//#include <byteswap.h>
+
+// Falling back to compiler builtins instead.
+#define bswap_32(x) __builtin_bswap32(x)
+#define bswap_64(x) __builtin_bswap64(x)
+
+#endif
+
+namespace base {
+namespace internal {
+namespace cityhash_v111 {
+
+#ifdef WORDS_BIGENDIAN
+#define uint32_in_expected_order(x) (bswap_32(x))
+#define uint64_in_expected_order(x) (bswap_64(x))
+#else
+#define uint32_in_expected_order(x) (x)
+#define uint64_in_expected_order(x) (x)
+#endif
+
+#if !defined(LIKELY)
+#if HAVE_BUILTIN_EXPECT
+#define LIKELY(x) (__builtin_expect(!!(x), 1))
+#else
+#define LIKELY(x) (x)
+#endif
+#endif
+
+static uint64 UNALIGNED_LOAD64(const char* p) {
+ uint64 result;
+ memcpy(&result, p, sizeof(result));
+ return result;
+}
+
+static uint32 UNALIGNED_LOAD32(const char* p) {
+ uint32 result;
+ memcpy(&result, p, sizeof(result));
+ return result;
+}
+
+static uint64 Fetch64(const char* p) {
+ return uint64_in_expected_order(UNALIGNED_LOAD64(p));
+}
+
+static uint32 Fetch32(const char* p) {
+ return uint32_in_expected_order(UNALIGNED_LOAD32(p));
+}
+
+// Some primes between 2^63 and 2^64 for various uses.
+static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
+static const uint64 k1 = 0xb492b66fbe98f273ULL;
+static const uint64 k2 = 0x9ae16a3b2f90404fULL;
+
+// Magic numbers for 32-bit hashing. Copied from Murmur3.
+static const uint32 c1 = 0xcc9e2d51;
+static const uint32 c2 = 0x1b873593;
+
+// A 32-bit to 32-bit integer hash copied from Murmur3.
+static uint32 fmix(uint32 h) {
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+ return h;
+}
+
+static uint32 Rotate32(uint32 val, int shift) {
+ // Avoid shifting by 32: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
+}
+
+#undef PERMUTE3
+#define PERMUTE3(a, b, c) \
+ do { \
+ std::swap(a, b); \
+ std::swap(a, c); \
+ } while (0)
+
+static uint32 Mur(uint32 a, uint32 h) {
+ // Helper from Murmur3 for combining two 32-bit values.
+ a *= c1;
+ a = Rotate32(a, 17);
+ a *= c2;
+ h ^= a;
+ h = Rotate32(h, 19);
+ return h * 5 + 0xe6546b64;
+}
+
+static uint32 Hash32Len13to24(const char* s, size_t len) {
+ uint32 a = Fetch32(s - 4 + (len >> 1));
+ uint32 b = Fetch32(s + 4);
+ uint32 c = Fetch32(s + len - 8);
+ uint32 d = Fetch32(s + (len >> 1));
+ uint32 e = Fetch32(s);
+ uint32 f = Fetch32(s + len - 4);
+ uint32 h = len;
+
+ return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
+}
+
+static uint32 Hash32Len0to4(const char* s, size_t len) {
+ uint32 b = 0;
+ uint32 c = 9;
+ for (size_t i = 0; i < len; i++) {
+ signed char v = s[i];
+ b = b * c1 + v;
+ c ^= b;
+ }
+ return fmix(Mur(b, Mur(len, c)));
+}
+
+static uint32 Hash32Len5to12(const char* s, size_t len) {
+ uint32 a = len, b = len * 5, c = 9, d = b;
+ a += Fetch32(s);
+ b += Fetch32(s + len - 4);
+ c += Fetch32(s + ((len >> 1) & 4));
+ return fmix(Mur(c, Mur(b, Mur(a, d))));
+}
+
+uint32 CityHash32(const char* s, size_t len) {
+ if (len <= 24) {
+ return len <= 12
+ ? (len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len))
+ : Hash32Len13to24(s, len);
+ }
+
+ // len > 24
+ uint32 h = len, g = c1 * len, f = g;
+ uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
+ uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
+ uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
+ uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
+ uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
+ h ^= a0;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ h ^= a2;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a1;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ g ^= a3;
+ g = Rotate32(g, 19);
+ g = g * 5 + 0xe6546b64;
+ f += a4;
+ f = Rotate32(f, 19);
+ f = f * 5 + 0xe6546b64;
+ size_t iters = (len - 1) / 20;
+ do {
+ a0 = Rotate32(Fetch32(s) * c1, 17) * c2;
+ a1 = Fetch32(s + 4);
+ a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
+ a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
+ a4 = Fetch32(s + 16);
+ h ^= a0;
+ h = Rotate32(h, 18);
+ h = h * 5 + 0xe6546b64;
+ f += a1;
+ f = Rotate32(f, 19);
+ f = f * c1;
+ g += a2;
+ g = Rotate32(g, 18);
+ g = g * 5 + 0xe6546b64;
+ h ^= a3 + a1;
+ h = Rotate32(h, 19);
+ h = h * 5 + 0xe6546b64;
+ g ^= a4;
+ g = bswap_32(g) * 5;
+ h += a4 * 5;
+ h = bswap_32(h);
+ f += a0;
+ PERMUTE3(f, h, g);
+ s += 20;
+ } while (--iters != 0);
+ g = Rotate32(g, 11) * c1;
+ g = Rotate32(g, 17) * c1;
+ f = Rotate32(f, 11) * c1;
+ f = Rotate32(f, 17) * c1;
+ h = Rotate32(h + g, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ h = Rotate32(h + f, 19);
+ h = h * 5 + 0xe6546b64;
+ h = Rotate32(h, 17) * c1;
+ return h;
+}
+
+// Bitwise right rotate. Normally this will compile to a single
+// instruction, especially if the shift is a manifest constant.
+static uint64 Rotate(uint64 val, int shift) {
+ // Avoid shifting by 64: doing so yields an undefined result.
+ return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
+}
+
+static uint64 ShiftMix(uint64 val) {
+ return val ^ (val >> 47);
+}
+
+static uint64 HashLen16(uint64 u, uint64 v) {
+ return Hash128to64(uint128(u, v));
+}
+
+static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
+ // Murmur-inspired hashing.
+ uint64 a = (u ^ v) * mul;
+ a ^= (a >> 47);
+ uint64 b = (v ^ a) * mul;
+ b ^= (b >> 47);
+ b *= mul;
+ return b;
+}
+
+static uint64 HashLen0to16(const char* s, size_t len) {
+ if (len >= 8) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch64(s) + k2;
+ uint64 b = Fetch64(s + len - 8);
+ uint64 c = Rotate(b, 37) * mul + a;
+ uint64 d = (Rotate(a, 25) + b) * mul;
+ return HashLen16(c, d, mul);
+ }
+ if (len >= 4) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch32(s);
+ return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
+ }
+ if (len > 0) {
+ uint8 a = s[0];
+ uint8 b = s[len >> 1];
+ uint8 c = s[len - 1];
+ uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
+ uint32 z = len + (static_cast<uint32>(c) << 2);
+ return ShiftMix(y * k2 ^ z * k0) * k2;
+ }
+ return k2;
+}
+
+// This probably works well for 16-byte strings as well, but it may be overkill
+// in that case.
+static uint64 HashLen17to32(const char* s, size_t len) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch64(s) * k1;
+ uint64 b = Fetch64(s + 8);
+ uint64 c = Fetch64(s + len - 8) * mul;
+ uint64 d = Fetch64(s + len - 16) * k2;
+ return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,
+ a + Rotate(b + k2, 18) + c, mul);
+}
+
+// Return a 16-byte hash for 48 bytes. Quick and dirty.
+// Callers do best to use "random-looking" values for a and b.
+static pair<uint64, uint64> WeakHashLen32WithSeeds(uint64 w,
+ uint64 x,
+ uint64 y,
+ uint64 z,
+ uint64 a,
+ uint64 b) {
+ a += w;
+ b = Rotate(b + a + z, 21);
+ uint64 c = a;
+ a += x;
+ a += y;
+ b += Rotate(a, 44);
+ return make_pair(a + z, b + c);
+}
+
+// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
+static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s,
+ uint64 a,
+ uint64 b) {
+ return WeakHashLen32WithSeeds(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16),
+ Fetch64(s + 24), a, b);
+}
+
+// Return an 8-byte hash for 33 to 64 bytes.
+static uint64 HashLen33to64(const char* s, size_t len) {
+ uint64 mul = k2 + len * 2;
+ uint64 a = Fetch64(s) * k2;
+ uint64 b = Fetch64(s + 8);
+ uint64 c = Fetch64(s + len - 24);
+ uint64 d = Fetch64(s + len - 32);
+ uint64 e = Fetch64(s + 16) * k2;
+ uint64 f = Fetch64(s + 24) * 9;
+ uint64 g = Fetch64(s + len - 8);
+ uint64 h = Fetch64(s + len - 16) * mul;
+ uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
+ uint64 v = ((a + g) ^ d) + f + 1;
+ uint64 w = bswap_64((u + v) * mul) + h;
+ uint64 x = Rotate(e + f, 42) + c;
+ uint64 y = (bswap_64((v + w) * mul) + g) * mul;
+ uint64 z = e + f + c;
+ a = bswap_64((x + z) * mul + y) + b;
+ b = ShiftMix((z + a) * mul + d + h) * mul;
+ return b + x;
+}
+
+uint64 CityHash64(const char* s, size_t len) {
+ if (len <= 32) {
+ if (len <= 16) {
+ return HashLen0to16(s, len);
+ } else {
+ return HashLen17to32(s, len);
+ }
+ } else if (len <= 64) {
+ return HashLen33to64(s, len);
+ }
+
+ // For strings over 64 bytes we hash the end first, and then as we
+ // loop we keep 56 bytes of state: v, w, x, y, and z.
+ uint64 x = Fetch64(s + len - 40);
+ uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
+ uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
+ pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
+ pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
+ x = x * k1 + Fetch64(s);
+
+ // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
+ len = (len - 1) & ~static_cast<size_t>(63);
+ do {
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ len -= 64;
+ } while (len != 0);
+ return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
+ HashLen16(v.second, w.second) + x);
+}
+
+uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) {
+ return CityHash64WithSeeds(s, len, k2, seed);
+}
+
+uint64 CityHash64WithSeeds(const char* s,
+ size_t len,
+ uint64 seed0,
+ uint64 seed1) {
+ return HashLen16(CityHash64(s, len) - seed0, seed1);
+}
+
+// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
+// of any length representable in signed long. Based on City and Murmur.
+static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
+ uint64 a = Uint128Low64(seed);
+ uint64 b = Uint128High64(seed);
+ uint64 c = 0;
+ uint64 d = 0;
+ signed long l = len - 16;
+ if (l <= 0) { // len <= 16
+ a = ShiftMix(a * k1) * k1;
+ c = b * k1 + HashLen0to16(s, len);
+ d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));
+ } else { // len > 16
+ c = HashLen16(Fetch64(s + len - 8) + k1, a);
+ d = HashLen16(b + len, c + Fetch64(s + len - 16));
+ a += d;
+ do {
+ a ^= ShiftMix(Fetch64(s) * k1) * k1;
+ a *= k1;
+ b ^= a;
+ c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;
+ c *= k1;
+ d ^= c;
+ s += 16;
+ l -= 16;
+ } while (l > 0);
+ }
+ a = HashLen16(a, c);
+ b = HashLen16(d, b);
+ return uint128(a ^ b, HashLen16(b, a));
+}
+
+uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
+ if (len < 128) {
+ return CityMurmur(s, len, seed);
+ }
+
+ // We expect len >= 128 to be the common case. Keep 56 bytes of state:
+ // v, w, x, y, and z.
+ pair<uint64, uint64> v, w;
+ uint64 x = Uint128Low64(seed);
+ uint64 y = Uint128High64(seed);
+ uint64 z = len * k1;
+ v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
+ v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
+ w.first = Rotate(y + z, 35) * k1 + x;
+ w.second = Rotate(x + Fetch64(s + 88), 53) * k1;
+
+ // This is the same inner loop as CityHash64(), manually unrolled.
+ do {
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
+ y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
+ x ^= w.second;
+ y += v.first + Fetch64(s + 40);
+ z = Rotate(z + w.first, 33) * k1;
+ v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
+ w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
+ std::swap(z, x);
+ s += 64;
+ len -= 128;
+ } while (LIKELY(len >= 128));
+ x += Rotate(v.first + z, 49) * k0;
+ y = y * k0 + Rotate(w.second, 37);
+ z = z * k0 + Rotate(w.first, 27);
+ w.first *= 9;
+ v.first *= k0;
+ // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
+ for (size_t tail_done = 0; tail_done < len;) {
+ tail_done += 32;
+ y = Rotate(x + y, 42) * k0 + v.second;
+ w.first += Fetch64(s + len - tail_done + 16);
+ x = x * k0 + w.first;
+ z += w.second + Fetch64(s + len - tail_done);
+ w.second += v.first;
+ v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);
+ v.first *= k0;
+ }
+ // At this point our 56 bytes of state should contain more than
+ // enough information for a strong 128-bit hash. We use two
+ // different 56-byte-to-8-byte hashes to get a 16-byte final result.
+ x = HashLen16(x, v.first);
+ y = HashLen16(y + z, w.first);
+ return uint128(HashLen16(x + v.second, w.second) + y,
+ HashLen16(x + w.second, y + v.second));
+}
+
+uint128 CityHash128(const char* s, size_t len) {
+ return len >= 16
+ ? CityHash128WithSeed(s + 16, len - 16,
+ uint128(Fetch64(s), Fetch64(s + 8) + k0))
+ : CityHash128WithSeed(s, len, uint128(k0, k1));
+}
+
+} // namespace cityhash_v111
+} // namespace internal
+} // namespace base
diff --git a/security/sandbox/chromium/base/third_party/cityhash/city.h b/security/sandbox/chromium/base/third_party/cityhash/city.h
new file mode 100644
index 0000000000..3e3dcaaaa9
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/cityhash/city.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2011 Google, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// CityHash, by Geoff Pike and Jyrki Alakuijala
+//
+// http://code.google.com/p/cityhash/
+//
+// This file provides a few functions for hashing strings. All of them are
+// high-quality functions in the sense that they pass standard tests such
+// as Austin Appleby's SMHasher. They are also fast.
+//
+// For 64-bit x86 code, on short strings, we don't know of anything faster than
+// CityHash64 that is of comparable quality. We believe our nearest competitor
+// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash
+// tables and most other hashing (excluding cryptography).
+//
+// For 64-bit x86 code, on long strings, the picture is more complicated.
+// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc.,
+// CityHashCrc128 appears to be faster than all competitors of comparable
+// quality. CityHash128 is also good but not quite as fast. We believe our
+// nearest competitor is Bob Jenkins' Spooky. We don't have great data for
+// other 64-bit CPUs, but for long strings we know that Spooky is slightly
+// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example.
+// Note that CityHashCrc128 is declared in citycrc.h.
+//
+// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
+// is of comparable quality. We believe our nearest competitor is Murmur3A.
+// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
+//
+// Functions in the CityHash family are not suitable for cryptography.
+//
+// Please see CityHash's README file for more details on our performance
+// measurements and so on.
+//
+// WARNING: This code has been only lightly tested on big-endian platforms!
+// It is known to work well on little-endian platforms that have a small penalty
+// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
+// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
+// bug reports are welcome.
+//
+// By the way, for some hash functions, given strings a and b, the hash
+// of a+b is easily derived from the hashes of a and b. This property
+// doesn't hold for any hash functions in this file.
+
+#ifndef BASE_THIRD_PARTY_CITYHASH_CITY_H_
+#define BASE_THIRD_PARTY_CITYHASH_CITY_H_
+
+#include <stdint.h>
+#include <stdlib.h> // for size_t.
+#include <utility>
+
+// XXX(cavalcantii): Declaring it inside of the 'base' namespace allows to
+// handle linker symbol clash error with deprecated CityHash from
+// third_party/smhasher in a few unit tests.
+namespace base {
+namespace internal {
+namespace cityhash_v111 {
+
+typedef uint8_t uint8;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+typedef std::pair<uint64, uint64> uint128;
+
+inline uint64 Uint128Low64(const uint128& x) {
+ return x.first;
+}
+inline uint64 Uint128High64(const uint128& x) {
+ return x.second;
+}
+
+// Hash function for a byte array.
+uint64 CityHash64(const char* buf, size_t len);
+
+// Hash function for a byte array. For convenience, a 64-bit seed is also
+// hashed into the result.
+uint64 CityHash64WithSeed(const char* buf, size_t len, uint64 seed);
+
+// Hash function for a byte array. For convenience, two seeds are also
+// hashed into the result.
+uint64 CityHash64WithSeeds(const char* buf,
+ size_t len,
+ uint64 seed0,
+ uint64 seed1);
+
+// Hash function for a byte array.
+uint128 CityHash128(const char* s, size_t len);
+
+// Hash function for a byte array. For convenience, a 128-bit seed is also
+// hashed into the result.
+uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed);
+
+// Hash function for a byte array. Most useful in 32-bit binaries.
+uint32 CityHash32(const char* buf, size_t len);
+
+// Hash 128 input bits down to 64 bits of output.
+// This is intended to be a reasonably good hash function.
+inline uint64 Hash128to64(const uint128& x) {
+ // Murmur-inspired hashing.
+ const uint64 kMul = 0x9ddfea08eb382d69ULL;
+ uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
+ a ^= (a >> 47);
+ uint64 b = (Uint128High64(x) ^ a) * kMul;
+ b ^= (b >> 47);
+ b *= kMul;
+ return b;
+}
+
+} // namespace cityhash_v111
+} // namespace internal
+} // namespace base
+
+#endif // CITY_HASH_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/LICENSE b/security/sandbox/chromium/base/third_party/double_conversion/LICENSE
new file mode 100644
index 0000000000..933718a9ef
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2006-2011, the V8 project authors. All rights reserved.
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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 COPYRIGHT
+OWNER OR CONTRIBUTORS 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/double_conversion/double-conversion/bignum-dtoa.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.cc
new file mode 100644
index 0000000000..abdd71452b
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.cc
@@ -0,0 +1,641 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <cmath>
+
+#include "bignum-dtoa.h"
+
+#include "bignum.h"
+#include "ieee.h"
+
+namespace double_conversion {
+
+static int NormalizedExponent(uint64_t significand, int exponent) {
+ DOUBLE_CONVERSION_ASSERT(significand != 0);
+ while ((significand & Double::kHiddenBit) == 0) {
+ significand = significand << 1;
+ exponent = exponent - 1;
+ }
+ return exponent;
+}
+
+
+// Forward declarations:
+// Returns an estimation of k such that 10^(k-1) <= v < 10^k.
+static int EstimatePower(int exponent);
+// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+// and denominator.
+static void InitialScaledStartValues(uint64_t significand,
+ int exponent,
+ bool lower_boundary_is_closer,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus);
+// Multiplies numerator/denominator so that its values lies in the range 1-10.
+// Returns decimal_point s.t.
+// v = numerator'/denominator' * 10^(decimal_point-1)
+// where numerator' and denominator' are the values of numerator and
+// denominator after the call to this function.
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus);
+// Generates digits from the left to the right and stops when the generated
+// digits yield the shortest decimal representation of v.
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length);
+// Generates 'requested_digits' after the decimal point.
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char> buffer, int* length);
+// Generates 'count' digits of numerator/denominator.
+// Once 'count' digits have been produced rounds the result depending on the
+// remainder (remainders of exactly .5 round upwards). Might update the
+// decimal_point when rounding up (for example for 0.9999).
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char> buffer, int* length);
+
+
+void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* length, int* decimal_point) {
+ DOUBLE_CONVERSION_ASSERT(v > 0);
+ DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
+ uint64_t significand;
+ int exponent;
+ bool lower_boundary_is_closer;
+ if (mode == BIGNUM_DTOA_SHORTEST_SINGLE) {
+ float f = static_cast<float>(v);
+ DOUBLE_CONVERSION_ASSERT(f == v);
+ significand = Single(f).Significand();
+ exponent = Single(f).Exponent();
+ lower_boundary_is_closer = Single(f).LowerBoundaryIsCloser();
+ } else {
+ significand = Double(v).Significand();
+ exponent = Double(v).Exponent();
+ lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser();
+ }
+ bool need_boundary_deltas =
+ (mode == BIGNUM_DTOA_SHORTEST || mode == BIGNUM_DTOA_SHORTEST_SINGLE);
+
+ bool is_even = (significand & 1) == 0;
+ int normalized_exponent = NormalizedExponent(significand, exponent);
+ // estimated_power might be too low by 1.
+ int estimated_power = EstimatePower(normalized_exponent);
+
+ // Shortcut for Fixed.
+ // The requested digits correspond to the digits after the point. If the
+ // number is much too small, then there is no need in trying to get any
+ // digits.
+ if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
+ buffer[0] = '\0';
+ *length = 0;
+ // Set decimal-point to -requested_digits. This is what Gay does.
+ // Note that it should not have any effect anyways since the string is
+ // empty.
+ *decimal_point = -requested_digits;
+ return;
+ }
+
+ Bignum numerator;
+ Bignum denominator;
+ Bignum delta_minus;
+ Bignum delta_plus;
+ // Make sure the bignum can grow large enough. The smallest double equals
+ // 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
+ // The maximum double is 1.7976931348623157e308 which needs fewer than
+ // 308*4 binary digits.
+ DOUBLE_CONVERSION_ASSERT(Bignum::kMaxSignificantBits >= 324*4);
+ InitialScaledStartValues(significand, exponent, lower_boundary_is_closer,
+ estimated_power, need_boundary_deltas,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+ // We now have v = (numerator / denominator) * 10^estimated_power.
+ FixupMultiply10(estimated_power, is_even, decimal_point,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+ // We now have v = (numerator / denominator) * 10^(decimal_point-1), and
+ // 1 <= (numerator + delta_plus) / denominator < 10
+ switch (mode) {
+ case BIGNUM_DTOA_SHORTEST:
+ case BIGNUM_DTOA_SHORTEST_SINGLE:
+ GenerateShortestDigits(&numerator, &denominator,
+ &delta_minus, &delta_plus,
+ is_even, buffer, length);
+ break;
+ case BIGNUM_DTOA_FIXED:
+ BignumToFixed(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ case BIGNUM_DTOA_PRECISION:
+ GenerateCountedDigits(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ default:
+ DOUBLE_CONVERSION_UNREACHABLE();
+ }
+ buffer[*length] = '\0';
+}
+
+
+// The procedure starts generating digits from the left to the right and stops
+// when the generated digits yield the shortest decimal representation of v. A
+// decimal representation of v is a number lying closer to v than to any other
+// double, so it converts to v when read.
+//
+// This is true if d, the decimal representation, is between m- and m+, the
+// upper and lower boundaries. d must be strictly between them if !is_even.
+// m- := (numerator - delta_minus) / denominator
+// m+ := (numerator + delta_plus) / denominator
+//
+// Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
+// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
+// will be produced. This should be the standard precondition.
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length) {
+ // Small optimization: if delta_minus and delta_plus are the same just reuse
+ // one of the two bignums.
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_plus = delta_minus;
+ }
+ *length = 0;
+ for (;;) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
+ // digit = numerator / denominator (integer division).
+ // numerator = numerator % denominator.
+ buffer[(*length)++] = static_cast<char>(digit + '0');
+
+ // Can we stop already?
+ // If the remainder of the division is less than the distance to the lower
+ // boundary we can stop. In this case we simply round down (discarding the
+ // remainder).
+ // Similarly we test if we can round up (using the upper boundary).
+ bool in_delta_room_minus;
+ bool in_delta_room_plus;
+ if (is_even) {
+ in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
+ } else {
+ in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
+ }
+ if (is_even) {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (!in_delta_room_minus && !in_delta_room_plus) {
+ // Prepare for next iteration.
+ numerator->Times10();
+ delta_minus->Times10();
+ // We optimized delta_plus to be equal to delta_minus (if they share the
+ // same value). So don't multiply delta_plus if they point to the same
+ // object.
+ if (delta_minus != delta_plus) {
+ delta_plus->Times10();
+ }
+ } else if (in_delta_room_minus && in_delta_room_plus) {
+ // Let's see if 2*numerator < denominator.
+ // If yes, then the next digit would be < 5 and we can round down.
+ int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
+ if (compare < 0) {
+ // Remaining digits are less than .5. -> Round down (== do nothing).
+ } else if (compare > 0) {
+ // Remaining digits are more than .5 of denominator. -> Round up.
+ // Note that the last digit could not be a '9' as otherwise the whole
+ // loop would have stopped earlier.
+ // We still have an assert here in case the preconditions were not
+ // satisfied.
+ DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ } else {
+ // Halfway case.
+ // TODO(floitsch): need a way to solve half-way cases.
+ // For now let's round towards even (since this is what Gay seems to
+ // do).
+
+ if ((buffer[(*length) - 1] - '0') % 2 == 0) {
+ // Round down => Do nothing.
+ } else {
+ DOUBLE_CONVERSION_ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ }
+ }
+ return;
+ } else if (in_delta_room_minus) {
+ // Round down (== do nothing).
+ return;
+ } else { // in_delta_room_plus
+ // Round up.
+ // Note again that the last digit could not be '9' since this would have
+ // stopped the loop earlier.
+ // We still have an DOUBLE_CONVERSION_ASSERT here, in case the preconditions were not
+ // satisfied.
+ DOUBLE_CONVERSION_ASSERT(buffer[(*length) -1] != '9');
+ buffer[(*length) - 1]++;
+ return;
+ }
+ }
+}
+
+
+// Let v = numerator / denominator < 10.
+// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
+// from left to right. Once 'count' digits have been produced we decide wether
+// to round up or down. Remainders of exactly .5 round upwards. Numbers such
+// as 9.999999 propagate a carry all the way, and change the
+// exponent (decimal_point), when rounding upwards.
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char> buffer, int* length) {
+ DOUBLE_CONVERSION_ASSERT(count >= 0);
+ for (int i = 0; i < count - 1; ++i) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ DOUBLE_CONVERSION_ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive.
+ // digit = numerator / denominator (integer division).
+ // numerator = numerator % denominator.
+ buffer[i] = static_cast<char>(digit + '0');
+ // Prepare for next iteration.
+ numerator->Times10();
+ }
+ // Generate the last digit.
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ digit++;
+ }
+ DOUBLE_CONVERSION_ASSERT(digit <= 10);
+ buffer[count - 1] = static_cast<char>(digit + '0');
+ // Correct bad digits (in case we had a sequence of '9's). Propagate the
+ // carry until we hat a non-'9' or til we reach the first digit.
+ for (int i = count - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ // Propagate a carry past the top place.
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+ *length = count;
+}
+
+
+// Generates 'requested_digits' after the decimal point. It might omit
+// trailing '0's. If the input number is too small then no digits at all are
+// generated (ex.: 2 fixed digits for 0.00001).
+//
+// Input verifies: 1 <= (numerator + delta) / denominator < 10.
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char> buffer, int* length) {
+ // Note that we have to look at more than just the requested_digits, since
+ // a number could be rounded up. Example: v=0.5 with requested_digits=0.
+ // Even though the power of v equals 0 we can't just stop here.
+ if (-(*decimal_point) > requested_digits) {
+ // The number is definitively too small.
+ // Ex: 0.001 with requested_digits == 1.
+ // Set decimal-point to -requested_digits. This is what Gay does.
+ // Note that it should not have any effect anyways since the string is
+ // empty.
+ *decimal_point = -requested_digits;
+ *length = 0;
+ return;
+ } else if (-(*decimal_point) == requested_digits) {
+ // We only need to verify if the number rounds down or up.
+ // Ex: 0.04 and 0.06 with requested_digits == 1.
+ DOUBLE_CONVERSION_ASSERT(*decimal_point == -requested_digits);
+ // Initially the fraction lies in range (1, 10]. Multiply the denominator
+ // by 10 so that we can compare more easily.
+ denominator->Times10();
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ // If the fraction is >= 0.5 then we have to include the rounded
+ // digit.
+ buffer[0] = '1';
+ *length = 1;
+ (*decimal_point)++;
+ } else {
+ // Note that we caught most of similar cases earlier.
+ *length = 0;
+ }
+ return;
+ } else {
+ // The requested digits correspond to the digits after the point.
+ // The variable 'needed_digits' includes the digits before the point.
+ int needed_digits = (*decimal_point) + requested_digits;
+ GenerateCountedDigits(needed_digits, decimal_point,
+ numerator, denominator,
+ buffer, length);
+ }
+}
+
+
+// Returns an estimation of k such that 10^(k-1) <= v < 10^k where
+// v = f * 2^exponent and 2^52 <= f < 2^53.
+// v is hence a normalized double with the given exponent. The output is an
+// approximation for the exponent of the decimal approimation .digits * 10^k.
+//
+// The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
+// Note: this property holds for v's upper boundary m+ too.
+// 10^k <= m+ < 10^k+1.
+// (see explanation below).
+//
+// Examples:
+// EstimatePower(0) => 16
+// EstimatePower(-52) => 0
+//
+// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
+static int EstimatePower(int exponent) {
+ // This function estimates log10 of v where v = f*2^e (with e == exponent).
+ // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
+ // Note that f is bounded by its container size. Let p = 53 (the double's
+ // significand size). Then 2^(p-1) <= f < 2^p.
+ //
+ // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
+ // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
+ // The computed number undershoots by less than 0.631 (when we compute log3
+ // and not log10).
+ //
+ // Optimization: since we only need an approximated result this computation
+ // can be performed on 64 bit integers. On x86/x64 architecture the speedup is
+ // not really measurable, though.
+ //
+ // Since we want to avoid overshooting we decrement by 1e10 so that
+ // floating-point imprecisions don't affect us.
+ //
+ // Explanation for v's boundary m+: the computation takes advantage of
+ // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
+ // (even for denormals where the delta can be much more important).
+
+ const double k1Log10 = 0.30102999566398114; // 1/lg(10)
+
+ // For doubles len(f) == 53 (don't forget the hidden bit).
+ const int kSignificandSize = Double::kSignificandSize;
+ double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
+ return static_cast<int>(estimate);
+}
+
+
+// See comments for InitialScaledStartValues.
+static void InitialScaledStartValuesPositiveExponent(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ // A positive exponent implies a positive power.
+ DOUBLE_CONVERSION_ASSERT(estimated_power >= 0);
+ // Since the estimated_power is positive we simply multiply the denominator
+ // by 10^estimated_power.
+
+ // numerator = v.
+ numerator->AssignUInt64(significand);
+ numerator->ShiftLeft(exponent);
+ // denominator = 10^estimated_power.
+ denominator->AssignPowerUInt16(10, estimated_power);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+ // denominator (of 2) delta_plus equals 2^e.
+ delta_plus->AssignUInt16(1);
+ delta_plus->ShiftLeft(exponent);
+ // Same for delta_minus. The adjustments if f == 2^p-1 are done later.
+ delta_minus->AssignUInt16(1);
+ delta_minus->ShiftLeft(exponent);
+ }
+}
+
+
+// See comments for InitialScaledStartValues
+static void InitialScaledStartValuesNegativeExponentPositivePower(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ // v = f * 2^e with e < 0, and with estimated_power >= 0.
+ // This means that e is close to 0 (have a look at how estimated_power is
+ // computed).
+
+ // numerator = significand
+ // since v = significand * 2^exponent this is equivalent to
+ // numerator = v * / 2^-exponent
+ numerator->AssignUInt64(significand);
+ // denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
+ denominator->AssignPowerUInt16(10, estimated_power);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
+ // denominator (of 2) delta_plus equals 2^e.
+ // Given that the denominator already includes v's exponent the distance
+ // to the boundaries is simply 1.
+ delta_plus->AssignUInt16(1);
+ // Same for delta_minus. The adjustments if f == 2^p-1 are done later.
+ delta_minus->AssignUInt16(1);
+ }
+}
+
+
+// See comments for InitialScaledStartValues
+static void InitialScaledStartValuesNegativeExponentNegativePower(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ // Instead of multiplying the denominator with 10^estimated_power we
+ // multiply all values (numerator and deltas) by 10^-estimated_power.
+
+ // Use numerator as temporary container for power_ten.
+ Bignum* power_ten = numerator;
+ power_ten->AssignPowerUInt16(10, -estimated_power);
+
+ if (need_boundary_deltas) {
+ // Since power_ten == numerator we must make a copy of 10^estimated_power
+ // before we complete the computation of the numerator.
+ // delta_plus = delta_minus = 10^estimated_power
+ delta_plus->AssignBignum(*power_ten);
+ delta_minus->AssignBignum(*power_ten);
+ }
+
+ // numerator = significand * 2 * 10^-estimated_power
+ // since v = significand * 2^exponent this is equivalent to
+ // numerator = v * 10^-estimated_power * 2 * 2^-exponent.
+ // Remember: numerator has been abused as power_ten. So no need to assign it
+ // to itself.
+ DOUBLE_CONVERSION_ASSERT(numerator == power_ten);
+ numerator->MultiplyByUInt64(significand);
+
+ // denominator = 2 * 2^-exponent with exponent < 0.
+ denominator->AssignUInt16(1);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ // Introduce a common denominator so that the deltas to the boundaries are
+ // integers.
+ numerator->ShiftLeft(1);
+ denominator->ShiftLeft(1);
+ // With this shift the boundaries have their correct value, since
+ // delta_plus = 10^-estimated_power, and
+ // delta_minus = 10^-estimated_power.
+ // These assignments have been done earlier.
+ // The adjustments if f == 2^p-1 (lower boundary is closer) are done later.
+ }
+}
+
+
+// Let v = significand * 2^exponent.
+// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
+// and denominator. The functions GenerateShortestDigits and
+// GenerateCountedDigits will then convert this ratio to its decimal
+// representation d, with the required accuracy.
+// Then d * 10^estimated_power is the representation of v.
+// (Note: the fraction and the estimated_power might get adjusted before
+// generating the decimal representation.)
+//
+// The initial start values consist of:
+// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
+// - a scaled (common) denominator.
+// optionally (used by GenerateShortestDigits to decide if it has the shortest
+// decimal converting back to v):
+// - v - m-: the distance to the lower boundary.
+// - m+ - v: the distance to the upper boundary.
+//
+// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
+//
+// Let ep == estimated_power, then the returned values will satisfy:
+// v / 10^ep = numerator / denominator.
+// v's boundarys m- and m+:
+// m- / 10^ep == v / 10^ep - delta_minus / denominator
+// m+ / 10^ep == v / 10^ep + delta_plus / denominator
+// Or in other words:
+// m- == v - delta_minus * 10^ep / denominator;
+// m+ == v + delta_plus * 10^ep / denominator;
+//
+// Since 10^(k-1) <= v < 10^k (with k == estimated_power)
+// or 10^k <= v < 10^(k+1)
+// we then have 0.1 <= numerator/denominator < 1
+// or 1 <= numerator/denominator < 10
+//
+// It is then easy to kickstart the digit-generation routine.
+//
+// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST
+// or BIGNUM_DTOA_SHORTEST_SINGLE.
+
+static void InitialScaledStartValues(uint64_t significand,
+ int exponent,
+ bool lower_boundary_is_closer,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus) {
+ if (exponent >= 0) {
+ InitialScaledStartValuesPositiveExponent(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else if (estimated_power >= 0) {
+ InitialScaledStartValuesNegativeExponentPositivePower(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else {
+ InitialScaledStartValuesNegativeExponentNegativePower(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ }
+
+ if (need_boundary_deltas && lower_boundary_is_closer) {
+ // The lower boundary is closer at half the distance of "normal" numbers.
+ // Increase the common denominator and adapt all but the delta_minus.
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+}
+
+
+// This routine multiplies numerator/denominator so that its values lies in the
+// range 1-10. That is after a call to this function we have:
+// 1 <= (numerator + delta_plus) /denominator < 10.
+// Let numerator the input before modification and numerator' the argument
+// after modification, then the output-parameter decimal_point is such that
+// numerator / denominator * 10^estimated_power ==
+// numerator' / denominator' * 10^(decimal_point - 1)
+// In some cases estimated_power was too low, and this is already the case. We
+// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
+// estimated_power) but do not touch the numerator or denominator.
+// Otherwise the routine multiplies the numerator and the deltas by 10.
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ bool in_range;
+ if (is_even) {
+ // For IEEE doubles half-way cases (in decimal system numbers ending with 5)
+ // are rounded to the closest floating-point number with even significand.
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (in_range) {
+ // Since numerator + delta_plus >= denominator we already have
+ // 1 <= numerator/denominator < 10. Simply update the estimated_power.
+ *decimal_point = estimated_power + 1;
+ } else {
+ *decimal_point = estimated_power;
+ numerator->Times10();
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_minus->Times10();
+ delta_plus->AssignBignum(*delta_minus);
+ } else {
+ delta_minus->Times10();
+ delta_plus->Times10();
+ }
+ }
+}
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.h
new file mode 100644
index 0000000000..34b961992d
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.h
@@ -0,0 +1,84 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_BIGNUM_DTOA_H_
+#define DOUBLE_CONVERSION_BIGNUM_DTOA_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+enum BignumDtoaMode {
+ // Return the shortest correct representation.
+ // For example the output of 0.299999999999999988897 is (the less accurate but
+ // correct) 0.3.
+ BIGNUM_DTOA_SHORTEST,
+ // Same as BIGNUM_DTOA_SHORTEST but for single-precision floats.
+ BIGNUM_DTOA_SHORTEST_SINGLE,
+ // Return a fixed number of digits after the decimal point.
+ // For instance fixed(0.1, 4) becomes 0.1000
+ // If the input number is big, the output will be big.
+ BIGNUM_DTOA_FIXED,
+ // Return a fixed number of digits, no matter what the exponent is.
+ BIGNUM_DTOA_PRECISION
+};
+
+// Converts the given double 'v' to ascii.
+// The result should be interpreted as buffer * 10^(point-length).
+// The buffer will be null-terminated.
+//
+// The input v must be > 0 and different from NaN, and Infinity.
+//
+// The output depends on the given mode:
+// - SHORTEST: produce the least amount of digits for which the internal
+// identity requirement is still satisfied. If the digits are printed
+// (together with the correct exponent) then reading this number will give
+// 'v' again. The buffer will choose the representation that is closest to
+// 'v'. If there are two at the same distance, than the number is round up.
+// In this mode the 'requested_digits' parameter is ignored.
+// - FIXED: produces digits necessary to print a given number with
+// 'requested_digits' digits after the decimal point. The produced digits
+// might be too short in which case the caller has to fill the gaps with '0's.
+// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
+// buffer="2", point=0.
+// Note: the length of the returned buffer has no meaning wrt the significance
+// of its digits. That is, just because it contains '0's does not mean that
+// any other digit would not satisfy the internal identity requirement.
+// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+// Even though the length of produced digits usually equals
+// 'requested_digits', the function is allowed to return fewer digits, in
+// which case the caller has to fill the missing digits with '0's.
+// Halfway cases are again rounded up.
+// 'BignumDtoa' expects the given buffer to be big enough to hold all digits
+// and a terminating null-character.
+void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* length, int* point);
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_BIGNUM_DTOA_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.cc
new file mode 100644
index 0000000000..d858c16ca0
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.cc
@@ -0,0 +1,796 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <algorithm>
+#include <cstring>
+
+#include "bignum.h"
+#include "utils.h"
+
+namespace double_conversion {
+
+Bignum::Chunk& Bignum::RawBigit(const int index) {
+ DOUBLE_CONVERSION_ASSERT(static_cast<unsigned>(index) < kBigitCapacity);
+ return bigits_buffer_[index];
+}
+
+
+const Bignum::Chunk& Bignum::RawBigit(const int index) const {
+ DOUBLE_CONVERSION_ASSERT(static_cast<unsigned>(index) < kBigitCapacity);
+ return bigits_buffer_[index];
+}
+
+
+template<typename S>
+static int BitSize(const S value) {
+ (void) value; // Mark variable as used.
+ return 8 * sizeof(value);
+}
+
+// Guaranteed to lie in one Bigit.
+void Bignum::AssignUInt16(const uint16_t value) {
+ DOUBLE_CONVERSION_ASSERT(kBigitSize >= BitSize(value));
+ Zero();
+ if (value > 0) {
+ RawBigit(0) = value;
+ used_bigits_ = 1;
+ }
+}
+
+
+void Bignum::AssignUInt64(uint64_t value) {
+ Zero();
+ for(int i = 0; value > 0; ++i) {
+ RawBigit(i) = value & kBigitMask;
+ value >>= kBigitSize;
+ ++used_bigits_;
+ }
+}
+
+
+void Bignum::AssignBignum(const Bignum& other) {
+ exponent_ = other.exponent_;
+ for (int i = 0; i < other.used_bigits_; ++i) {
+ RawBigit(i) = other.RawBigit(i);
+ }
+ used_bigits_ = other.used_bigits_;
+}
+
+
+static uint64_t ReadUInt64(const Vector<const char> buffer,
+ const int from,
+ const int digits_to_read) {
+ uint64_t result = 0;
+ for (int i = from; i < from + digits_to_read; ++i) {
+ const int digit = buffer[i] - '0';
+ DOUBLE_CONVERSION_ASSERT(0 <= digit && digit <= 9);
+ result = result * 10 + digit;
+ }
+ return result;
+}
+
+
+void Bignum::AssignDecimalString(const Vector<const char> value) {
+ // 2^64 = 18446744073709551616 > 10^19
+ static const int kMaxUint64DecimalDigits = 19;
+ Zero();
+ int length = value.length();
+ unsigned pos = 0;
+ // Let's just say that each digit needs 4 bits.
+ while (length >= kMaxUint64DecimalDigits) {
+ const uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits);
+ pos += kMaxUint64DecimalDigits;
+ length -= kMaxUint64DecimalDigits;
+ MultiplyByPowerOfTen(kMaxUint64DecimalDigits);
+ AddUInt64(digits);
+ }
+ const uint64_t digits = ReadUInt64(value, pos, length);
+ MultiplyByPowerOfTen(length);
+ AddUInt64(digits);
+ Clamp();
+}
+
+
+static uint64_t HexCharValue(const int c) {
+ if ('0' <= c && c <= '9') {
+ return c - '0';
+ }
+ if ('a' <= c && c <= 'f') {
+ return 10 + c - 'a';
+ }
+ DOUBLE_CONVERSION_ASSERT('A' <= c && c <= 'F');
+ return 10 + c - 'A';
+}
+
+
+// Unlike AssignDecimalString(), this function is "only" used
+// for unit-tests and therefore not performance critical.
+void Bignum::AssignHexString(Vector<const char> value) {
+ Zero();
+ // Required capacity could be reduced by ignoring leading zeros.
+ EnsureCapacity(((value.length() * 4) + kBigitSize - 1) / kBigitSize);
+ DOUBLE_CONVERSION_ASSERT(sizeof(uint64_t) * 8 >= kBigitSize + 4); // TODO: static_assert
+ // Accumulates converted hex digits until at least kBigitSize bits.
+ // Works with non-factor-of-four kBigitSizes.
+ uint64_t tmp = 0; // Accumulates converted hex digits until at least
+ for (int cnt = 0; !value.is_empty(); value.pop_back()) {
+ tmp |= (HexCharValue(value.last()) << cnt);
+ if ((cnt += 4) >= kBigitSize) {
+ RawBigit(used_bigits_++) = (tmp & kBigitMask);
+ cnt -= kBigitSize;
+ tmp >>= kBigitSize;
+ }
+ }
+ if (tmp > 0) {
+ RawBigit(used_bigits_++) = tmp;
+ }
+ Clamp();
+}
+
+
+void Bignum::AddUInt64(const uint64_t operand) {
+ if (operand == 0) {
+ return;
+ }
+ Bignum other;
+ other.AssignUInt64(operand);
+ AddBignum(other);
+}
+
+
+void Bignum::AddBignum(const Bignum& other) {
+ DOUBLE_CONVERSION_ASSERT(IsClamped());
+ DOUBLE_CONVERSION_ASSERT(other.IsClamped());
+
+ // If this has a greater exponent than other append zero-bigits to this.
+ // After this call exponent_ <= other.exponent_.
+ Align(other);
+
+ // There are two possibilities:
+ // aaaaaaaaaaa 0000 (where the 0s represent a's exponent)
+ // bbbbb 00000000
+ // ----------------
+ // ccccccccccc 0000
+ // or
+ // aaaaaaaaaa 0000
+ // bbbbbbbbb 0000000
+ // -----------------
+ // cccccccccccc 0000
+ // In both cases we might need a carry bigit.
+
+ EnsureCapacity(1 + (std::max)(BigitLength(), other.BigitLength()) - exponent_);
+ Chunk carry = 0;
+ int bigit_pos = other.exponent_ - exponent_;
+ DOUBLE_CONVERSION_ASSERT(bigit_pos >= 0);
+ for (int i = used_bigits_; i < bigit_pos; ++i) {
+ RawBigit(i) = 0;
+ }
+ for (int i = 0; i < other.used_bigits_; ++i) {
+ const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0;
+ const Chunk sum = my + other.RawBigit(i) + carry;
+ RawBigit(bigit_pos) = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ ++bigit_pos;
+ }
+ while (carry != 0) {
+ const Chunk my = (bigit_pos < used_bigits_) ? RawBigit(bigit_pos) : 0;
+ const Chunk sum = my + carry;
+ RawBigit(bigit_pos) = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ ++bigit_pos;
+ }
+ used_bigits_ = (std::max)(bigit_pos, static_cast<int>(used_bigits_));
+ DOUBLE_CONVERSION_ASSERT(IsClamped());
+}
+
+
+void Bignum::SubtractBignum(const Bignum& other) {
+ DOUBLE_CONVERSION_ASSERT(IsClamped());
+ DOUBLE_CONVERSION_ASSERT(other.IsClamped());
+ // We require this to be bigger than other.
+ DOUBLE_CONVERSION_ASSERT(LessEqual(other, *this));
+
+ Align(other);
+
+ const int offset = other.exponent_ - exponent_;
+ Chunk borrow = 0;
+ int i;
+ for (i = 0; i < other.used_bigits_; ++i) {
+ DOUBLE_CONVERSION_ASSERT((borrow == 0) || (borrow == 1));
+ const Chunk difference = RawBigit(i + offset) - other.RawBigit(i) - borrow;
+ RawBigit(i + offset) = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ while (borrow != 0) {
+ const Chunk difference = RawBigit(i + offset) - borrow;
+ RawBigit(i + offset) = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ ++i;
+ }
+ Clamp();
+}
+
+
+void Bignum::ShiftLeft(const int shift_amount) {
+ if (used_bigits_ == 0) {
+ return;
+ }
+ exponent_ += (shift_amount / kBigitSize);
+ const int local_shift = shift_amount % kBigitSize;
+ EnsureCapacity(used_bigits_ + 1);
+ BigitsShiftLeft(local_shift);
+}
+
+
+void Bignum::MultiplyByUInt32(const uint32_t factor) {
+ if (factor == 1) {
+ return;
+ }
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ if (used_bigits_ == 0) {
+ return;
+ }
+ // The product of a bigit with the factor is of size kBigitSize + 32.
+ // Assert that this number + 1 (for the carry) fits into double chunk.
+ DOUBLE_CONVERSION_ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1);
+ DoubleChunk carry = 0;
+ for (int i = 0; i < used_bigits_; ++i) {
+ const DoubleChunk product = static_cast<DoubleChunk>(factor) * RawBigit(i) + carry;
+ RawBigit(i) = static_cast<Chunk>(product & kBigitMask);
+ carry = (product >> kBigitSize);
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_bigits_ + 1);
+ RawBigit(used_bigits_) = carry & kBigitMask;
+ used_bigits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByUInt64(const uint64_t factor) {
+ if (factor == 1) {
+ return;
+ }
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ if (used_bigits_ == 0) {
+ return;
+ }
+ DOUBLE_CONVERSION_ASSERT(kBigitSize < 32);
+ uint64_t carry = 0;
+ const uint64_t low = factor & 0xFFFFFFFF;
+ const uint64_t high = factor >> 32;
+ for (int i = 0; i < used_bigits_; ++i) {
+ const uint64_t product_low = low * RawBigit(i);
+ const uint64_t product_high = high * RawBigit(i);
+ const uint64_t tmp = (carry & kBigitMask) + product_low;
+ RawBigit(i) = tmp & kBigitMask;
+ carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
+ (product_high << (32 - kBigitSize));
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_bigits_ + 1);
+ RawBigit(used_bigits_) = carry & kBigitMask;
+ used_bigits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByPowerOfTen(const int exponent) {
+ static const uint64_t kFive27 = DOUBLE_CONVERSION_UINT64_2PART_C(0x6765c793, fa10079d);
+ static const uint16_t kFive1 = 5;
+ static const uint16_t kFive2 = kFive1 * 5;
+ static const uint16_t kFive3 = kFive2 * 5;
+ static const uint16_t kFive4 = kFive3 * 5;
+ static const uint16_t kFive5 = kFive4 * 5;
+ static const uint16_t kFive6 = kFive5 * 5;
+ static const uint32_t kFive7 = kFive6 * 5;
+ static const uint32_t kFive8 = kFive7 * 5;
+ static const uint32_t kFive9 = kFive8 * 5;
+ static const uint32_t kFive10 = kFive9 * 5;
+ static const uint32_t kFive11 = kFive10 * 5;
+ static const uint32_t kFive12 = kFive11 * 5;
+ static const uint32_t kFive13 = kFive12 * 5;
+ static const uint32_t kFive1_to_12[] =
+ { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
+ kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
+
+ DOUBLE_CONVERSION_ASSERT(exponent >= 0);
+
+ if (exponent == 0) {
+ return;
+ }
+ if (used_bigits_ == 0) {
+ return;
+ }
+ // We shift by exponent at the end just before returning.
+ int remaining_exponent = exponent;
+ while (remaining_exponent >= 27) {
+ MultiplyByUInt64(kFive27);
+ remaining_exponent -= 27;
+ }
+ while (remaining_exponent >= 13) {
+ MultiplyByUInt32(kFive13);
+ remaining_exponent -= 13;
+ }
+ if (remaining_exponent > 0) {
+ MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
+ }
+ ShiftLeft(exponent);
+}
+
+
+void Bignum::Square() {
+ DOUBLE_CONVERSION_ASSERT(IsClamped());
+ const int product_length = 2 * used_bigits_;
+ EnsureCapacity(product_length);
+
+ // Comba multiplication: compute each column separately.
+ // Example: r = a2a1a0 * b2b1b0.
+ // r = 1 * a0b0 +
+ // 10 * (a1b0 + a0b1) +
+ // 100 * (a2b0 + a1b1 + a0b2) +
+ // 1000 * (a2b1 + a1b2) +
+ // 10000 * a2b2
+ //
+ // In the worst case we have to accumulate nb-digits products of digit*digit.
+ //
+ // Assert that the additional number of bits in a DoubleChunk are enough to
+ // sum up used_digits of Bigit*Bigit.
+ if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_bigits_) {
+ DOUBLE_CONVERSION_UNIMPLEMENTED();
+ }
+ DoubleChunk accumulator = 0;
+ // First shift the digits so we don't overwrite them.
+ const int copy_offset = used_bigits_;
+ for (int i = 0; i < used_bigits_; ++i) {
+ RawBigit(copy_offset + i) = RawBigit(i);
+ }
+ // We have two loops to avoid some 'if's in the loop.
+ for (int i = 0; i < used_bigits_; ++i) {
+ // Process temporary digit i with power i.
+ // The sum of the two indices must be equal to i.
+ int bigit_index1 = i;
+ int bigit_index2 = 0;
+ // Sum all of the sub-products.
+ while (bigit_index1 >= 0) {
+ const Chunk chunk1 = RawBigit(copy_offset + bigit_index1);
+ const Chunk chunk2 = RawBigit(copy_offset + bigit_index2);
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ RawBigit(i) = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ for (int i = used_bigits_; i < product_length; ++i) {
+ int bigit_index1 = used_bigits_ - 1;
+ int bigit_index2 = i - bigit_index1;
+ // Invariant: sum of both indices is again equal to i.
+ // Inner loop runs 0 times on last iteration, emptying accumulator.
+ while (bigit_index2 < used_bigits_) {
+ const Chunk chunk1 = RawBigit(copy_offset + bigit_index1);
+ const Chunk chunk2 = RawBigit(copy_offset + bigit_index2);
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ // The overwritten RawBigit(i) will never be read in further loop iterations,
+ // because bigit_index1 and bigit_index2 are always greater
+ // than i - used_bigits_.
+ RawBigit(i) = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ // Since the result was guaranteed to lie inside the number the
+ // accumulator must be 0 now.
+ DOUBLE_CONVERSION_ASSERT(accumulator == 0);
+
+ // Don't forget to update the used_digits and the exponent.
+ used_bigits_ = product_length;
+ exponent_ *= 2;
+ Clamp();
+}
+
+
+void Bignum::AssignPowerUInt16(uint16_t base, const int power_exponent) {
+ DOUBLE_CONVERSION_ASSERT(base != 0);
+ DOUBLE_CONVERSION_ASSERT(power_exponent >= 0);
+ if (power_exponent == 0) {
+ AssignUInt16(1);
+ return;
+ }
+ Zero();
+ int shifts = 0;
+ // We expect base to be in range 2-32, and most often to be 10.
+ // It does not make much sense to implement different algorithms for counting
+ // the bits.
+ while ((base & 1) == 0) {
+ base >>= 1;
+ shifts++;
+ }
+ int bit_size = 0;
+ int tmp_base = base;
+ while (tmp_base != 0) {
+ tmp_base >>= 1;
+ bit_size++;
+ }
+ const int final_size = bit_size * power_exponent;
+ // 1 extra bigit for the shifting, and one for rounded final_size.
+ EnsureCapacity(final_size / kBigitSize + 2);
+
+ // Left to Right exponentiation.
+ int mask = 1;
+ while (power_exponent >= mask) mask <<= 1;
+
+ // The mask is now pointing to the bit above the most significant 1-bit of
+ // power_exponent.
+ // Get rid of first 1-bit;
+ mask >>= 2;
+ uint64_t this_value = base;
+
+ bool delayed_multiplication = false;
+ const uint64_t max_32bits = 0xFFFFFFFF;
+ while (mask != 0 && this_value <= max_32bits) {
+ this_value = this_value * this_value;
+ // Verify that there is enough space in this_value to perform the
+ // multiplication. The first bit_size bits must be 0.
+ if ((power_exponent & mask) != 0) {
+ DOUBLE_CONVERSION_ASSERT(bit_size > 0);
+ const uint64_t base_bits_mask =
+ ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1);
+ const bool high_bits_zero = (this_value & base_bits_mask) == 0;
+ if (high_bits_zero) {
+ this_value *= base;
+ } else {
+ delayed_multiplication = true;
+ }
+ }
+ mask >>= 1;
+ }
+ AssignUInt64(this_value);
+ if (delayed_multiplication) {
+ MultiplyByUInt32(base);
+ }
+
+ // Now do the same thing as a bignum.
+ while (mask != 0) {
+ Square();
+ if ((power_exponent & mask) != 0) {
+ MultiplyByUInt32(base);
+ }
+ mask >>= 1;
+ }
+
+ // And finally add the saved shifts.
+ ShiftLeft(shifts * power_exponent);
+}
+
+
+// Precondition: this/other < 16bit.
+uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) {
+ DOUBLE_CONVERSION_ASSERT(IsClamped());
+ DOUBLE_CONVERSION_ASSERT(other.IsClamped());
+ DOUBLE_CONVERSION_ASSERT(other.used_bigits_ > 0);
+
+ // Easy case: if we have less digits than the divisor than the result is 0.
+ // Note: this handles the case where this == 0, too.
+ if (BigitLength() < other.BigitLength()) {
+ return 0;
+ }
+
+ Align(other);
+
+ uint16_t result = 0;
+
+ // Start by removing multiples of 'other' until both numbers have the same
+ // number of digits.
+ while (BigitLength() > other.BigitLength()) {
+ // This naive approach is extremely inefficient if `this` divided by other
+ // is big. This function is implemented for doubleToString where
+ // the result should be small (less than 10).
+ DOUBLE_CONVERSION_ASSERT(other.RawBigit(other.used_bigits_ - 1) >= ((1 << kBigitSize) / 16));
+ DOUBLE_CONVERSION_ASSERT(RawBigit(used_bigits_ - 1) < 0x10000);
+ // Remove the multiples of the first digit.
+ // Example this = 23 and other equals 9. -> Remove 2 multiples.
+ result += static_cast<uint16_t>(RawBigit(used_bigits_ - 1));
+ SubtractTimes(other, RawBigit(used_bigits_ - 1));
+ }
+
+ DOUBLE_CONVERSION_ASSERT(BigitLength() == other.BigitLength());
+
+ // Both bignums are at the same length now.
+ // Since other has more than 0 digits we know that the access to
+ // RawBigit(used_bigits_ - 1) is safe.
+ const Chunk this_bigit = RawBigit(used_bigits_ - 1);
+ const Chunk other_bigit = other.RawBigit(other.used_bigits_ - 1);
+
+ if (other.used_bigits_ == 1) {
+ // Shortcut for easy (and common) case.
+ int quotient = this_bigit / other_bigit;
+ RawBigit(used_bigits_ - 1) = this_bigit - other_bigit * quotient;
+ DOUBLE_CONVERSION_ASSERT(quotient < 0x10000);
+ result += static_cast<uint16_t>(quotient);
+ Clamp();
+ return result;
+ }
+
+ const int division_estimate = this_bigit / (other_bigit + 1);
+ DOUBLE_CONVERSION_ASSERT(division_estimate < 0x10000);
+ result += static_cast<uint16_t>(division_estimate);
+ SubtractTimes(other, division_estimate);
+
+ if (other_bigit * (division_estimate + 1) > this_bigit) {
+ // No need to even try to subtract. Even if other's remaining digits were 0
+ // another subtraction would be too much.
+ return result;
+ }
+
+ while (LessEqual(other, *this)) {
+ SubtractBignum(other);
+ result++;
+ }
+ return result;
+}
+
+
+template<typename S>
+static int SizeInHexChars(S number) {
+ DOUBLE_CONVERSION_ASSERT(number > 0);
+ int result = 0;
+ while (number != 0) {
+ number >>= 4;
+ result++;
+ }
+ return result;
+}
+
+
+static char HexCharOfValue(const int value) {
+ DOUBLE_CONVERSION_ASSERT(0 <= value && value <= 16);
+ if (value < 10) {
+ return static_cast<char>(value + '0');
+ }
+ return static_cast<char>(value - 10 + 'A');
+}
+
+
+bool Bignum::ToHexString(char* buffer, const int buffer_size) const {
+ DOUBLE_CONVERSION_ASSERT(IsClamped());
+ // Each bigit must be printable as separate hex-character.
+ DOUBLE_CONVERSION_ASSERT(kBigitSize % 4 == 0);
+ static const int kHexCharsPerBigit = kBigitSize / 4;
+
+ if (used_bigits_ == 0) {
+ if (buffer_size < 2) {
+ return false;
+ }
+ buffer[0] = '0';
+ buffer[1] = '\0';
+ return true;
+ }
+ // We add 1 for the terminating '\0' character.
+ const int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit +
+ SizeInHexChars(RawBigit(used_bigits_ - 1)) + 1;
+ if (needed_chars > buffer_size) {
+ return false;
+ }
+ int string_index = needed_chars - 1;
+ buffer[string_index--] = '\0';
+ for (int i = 0; i < exponent_; ++i) {
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = '0';
+ }
+ }
+ for (int i = 0; i < used_bigits_ - 1; ++i) {
+ Chunk current_bigit = RawBigit(i);
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = HexCharOfValue(current_bigit & 0xF);
+ current_bigit >>= 4;
+ }
+ }
+ // And finally the last bigit.
+ Chunk most_significant_bigit = RawBigit(used_bigits_ - 1);
+ while (most_significant_bigit != 0) {
+ buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF);
+ most_significant_bigit >>= 4;
+ }
+ return true;
+}
+
+
+Bignum::Chunk Bignum::BigitOrZero(const int index) const {
+ if (index >= BigitLength()) {
+ return 0;
+ }
+ if (index < exponent_) {
+ return 0;
+ }
+ return RawBigit(index - exponent_);
+}
+
+
+int Bignum::Compare(const Bignum& a, const Bignum& b) {
+ DOUBLE_CONVERSION_ASSERT(a.IsClamped());
+ DOUBLE_CONVERSION_ASSERT(b.IsClamped());
+ const int bigit_length_a = a.BigitLength();
+ const int bigit_length_b = b.BigitLength();
+ if (bigit_length_a < bigit_length_b) {
+ return -1;
+ }
+ if (bigit_length_a > bigit_length_b) {
+ return +1;
+ }
+ for (int i = bigit_length_a - 1; i >= (std::min)(a.exponent_, b.exponent_); --i) {
+ const Chunk bigit_a = a.BigitOrZero(i);
+ const Chunk bigit_b = b.BigitOrZero(i);
+ if (bigit_a < bigit_b) {
+ return -1;
+ }
+ if (bigit_a > bigit_b) {
+ return +1;
+ }
+ // Otherwise they are equal up to this digit. Try the next digit.
+ }
+ return 0;
+}
+
+
+int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) {
+ DOUBLE_CONVERSION_ASSERT(a.IsClamped());
+ DOUBLE_CONVERSION_ASSERT(b.IsClamped());
+ DOUBLE_CONVERSION_ASSERT(c.IsClamped());
+ if (a.BigitLength() < b.BigitLength()) {
+ return PlusCompare(b, a, c);
+ }
+ if (a.BigitLength() + 1 < c.BigitLength()) {
+ return -1;
+ }
+ if (a.BigitLength() > c.BigitLength()) {
+ return +1;
+ }
+ // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than
+ // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one
+ // of 'a'.
+ if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) {
+ return -1;
+ }
+
+ Chunk borrow = 0;
+ // Starting at min_exponent all digits are == 0. So no need to compare them.
+ const int min_exponent = (std::min)((std::min)(a.exponent_, b.exponent_), c.exponent_);
+ for (int i = c.BigitLength() - 1; i >= min_exponent; --i) {
+ const Chunk chunk_a = a.BigitOrZero(i);
+ const Chunk chunk_b = b.BigitOrZero(i);
+ const Chunk chunk_c = c.BigitOrZero(i);
+ const Chunk sum = chunk_a + chunk_b;
+ if (sum > chunk_c + borrow) {
+ return +1;
+ } else {
+ borrow = chunk_c + borrow - sum;
+ if (borrow > 1) {
+ return -1;
+ }
+ borrow <<= kBigitSize;
+ }
+ }
+ if (borrow == 0) {
+ return 0;
+ }
+ return -1;
+}
+
+
+void Bignum::Clamp() {
+ while (used_bigits_ > 0 && RawBigit(used_bigits_ - 1) == 0) {
+ used_bigits_--;
+ }
+ if (used_bigits_ == 0) {
+ // Zero.
+ exponent_ = 0;
+ }
+}
+
+
+void Bignum::Align(const Bignum& other) {
+ if (exponent_ > other.exponent_) {
+ // If "X" represents a "hidden" bigit (by the exponent) then we are in the
+ // following case (a == this, b == other):
+ // a: aaaaaaXXXX or a: aaaaaXXX
+ // b: bbbbbbX b: bbbbbbbbXX
+ // We replace some of the hidden digits (X) of a with 0 digits.
+ // a: aaaaaa000X or a: aaaaa0XX
+ const int zero_bigits = exponent_ - other.exponent_;
+ EnsureCapacity(used_bigits_ + zero_bigits);
+ for (int i = used_bigits_ - 1; i >= 0; --i) {
+ RawBigit(i + zero_bigits) = RawBigit(i);
+ }
+ for (int i = 0; i < zero_bigits; ++i) {
+ RawBigit(i) = 0;
+ }
+ used_bigits_ += zero_bigits;
+ exponent_ -= zero_bigits;
+
+ DOUBLE_CONVERSION_ASSERT(used_bigits_ >= 0);
+ DOUBLE_CONVERSION_ASSERT(exponent_ >= 0);
+ }
+}
+
+
+void Bignum::BigitsShiftLeft(const int shift_amount) {
+ DOUBLE_CONVERSION_ASSERT(shift_amount < kBigitSize);
+ DOUBLE_CONVERSION_ASSERT(shift_amount >= 0);
+ Chunk carry = 0;
+ for (int i = 0; i < used_bigits_; ++i) {
+ const Chunk new_carry = RawBigit(i) >> (kBigitSize - shift_amount);
+ RawBigit(i) = ((RawBigit(i) << shift_amount) + carry) & kBigitMask;
+ carry = new_carry;
+ }
+ if (carry != 0) {
+ RawBigit(used_bigits_) = carry;
+ used_bigits_++;
+ }
+}
+
+
+void Bignum::SubtractTimes(const Bignum& other, const int factor) {
+ DOUBLE_CONVERSION_ASSERT(exponent_ <= other.exponent_);
+ if (factor < 3) {
+ for (int i = 0; i < factor; ++i) {
+ SubtractBignum(other);
+ }
+ return;
+ }
+ Chunk borrow = 0;
+ const int exponent_diff = other.exponent_ - exponent_;
+ for (int i = 0; i < other.used_bigits_; ++i) {
+ const DoubleChunk product = static_cast<DoubleChunk>(factor) * other.RawBigit(i);
+ const DoubleChunk remove = borrow + product;
+ const Chunk difference = RawBigit(i + exponent_diff) - (remove & kBigitMask);
+ RawBigit(i + exponent_diff) = difference & kBigitMask;
+ borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) +
+ (remove >> kBigitSize));
+ }
+ for (int i = other.used_bigits_ + exponent_diff; i < used_bigits_; ++i) {
+ if (borrow == 0) {
+ return;
+ }
+ const Chunk difference = RawBigit(i) - borrow;
+ RawBigit(i) = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ Clamp();
+}
+
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.h
new file mode 100644
index 0000000000..14d1ca86fc
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/bignum.h
@@ -0,0 +1,152 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_BIGNUM_H_
+#define DOUBLE_CONVERSION_BIGNUM_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+class Bignum {
+ public:
+ // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
+ // This bignum can encode much bigger numbers, since it contains an
+ // exponent.
+ static const int kMaxSignificantBits = 3584;
+
+ Bignum() : used_bigits_(0), exponent_(0) {}
+
+ void AssignUInt16(const uint16_t value);
+ void AssignUInt64(uint64_t value);
+ void AssignBignum(const Bignum& other);
+
+ void AssignDecimalString(const Vector<const char> value);
+ void AssignHexString(const Vector<const char> value);
+
+ void AssignPowerUInt16(uint16_t base, const int exponent);
+
+ void AddUInt64(const uint64_t operand);
+ void AddBignum(const Bignum& other);
+ // Precondition: this >= other.
+ void SubtractBignum(const Bignum& other);
+
+ void Square();
+ void ShiftLeft(const int shift_amount);
+ void MultiplyByUInt32(const uint32_t factor);
+ void MultiplyByUInt64(const uint64_t factor);
+ void MultiplyByPowerOfTen(const int exponent);
+ void Times10() { return MultiplyByUInt32(10); }
+ // Pseudocode:
+ // int result = this / other;
+ // this = this % other;
+ // In the worst case this function is in O(this/other).
+ uint16_t DivideModuloIntBignum(const Bignum& other);
+
+ bool ToHexString(char* buffer, const int buffer_size) const;
+
+ // Returns
+ // -1 if a < b,
+ // 0 if a == b, and
+ // +1 if a > b.
+ static int Compare(const Bignum& a, const Bignum& b);
+ static bool Equal(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) == 0;
+ }
+ static bool LessEqual(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) <= 0;
+ }
+ static bool Less(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) < 0;
+ }
+ // Returns Compare(a + b, c);
+ static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
+ // Returns a + b == c
+ static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) == 0;
+ }
+ // Returns a + b <= c
+ static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) <= 0;
+ }
+ // Returns a + b < c
+ static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) < 0;
+ }
+ private:
+ typedef uint32_t Chunk;
+ typedef uint64_t DoubleChunk;
+
+ static const int kChunkSize = sizeof(Chunk) * 8;
+ static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
+ // With bigit size of 28 we loose some bits, but a double still fits easily
+ // into two chunks, and more importantly we can use the Comba multiplication.
+ static const int kBigitSize = 28;
+ static const Chunk kBigitMask = (1 << kBigitSize) - 1;
+ // Every instance allocates kBigitLength chunks on the stack. Bignums cannot
+ // grow. There are no checks if the stack-allocated space is sufficient.
+ static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
+
+ static void EnsureCapacity(const int size) {
+ if (size > kBigitCapacity) {
+ DOUBLE_CONVERSION_UNREACHABLE();
+ }
+ }
+ void Align(const Bignum& other);
+ void Clamp();
+ bool IsClamped() const {
+ return used_bigits_ == 0 || RawBigit(used_bigits_ - 1) != 0;
+ }
+ void Zero() {
+ used_bigits_ = 0;
+ exponent_ = 0;
+ }
+ // Requires this to have enough capacity (no tests done).
+ // Updates used_bigits_ if necessary.
+ // shift_amount must be < kBigitSize.
+ void BigitsShiftLeft(const int shift_amount);
+ // BigitLength includes the "hidden" bigits encoded in the exponent.
+ int BigitLength() const { return used_bigits_ + exponent_; }
+ Chunk& RawBigit(const int index);
+ const Chunk& RawBigit(const int index) const;
+ Chunk BigitOrZero(const int index) const;
+ void SubtractTimes(const Bignum& other, const int factor);
+
+ // The Bignum's value is value(bigits_buffer_) * 2^(exponent_ * kBigitSize),
+ // where the value of the buffer consists of the lower kBigitSize bits of
+ // the first used_bigits_ Chunks in bigits_buffer_, first chunk has lowest
+ // significant bits.
+ int16_t used_bigits_;
+ int16_t exponent_;
+ Chunk bigits_buffer_[kBigitCapacity];
+
+ DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Bignum);
+};
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_BIGNUM_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.cc
new file mode 100644
index 0000000000..56bdfc9d63
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.cc
@@ -0,0 +1,175 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+
+#include "utils.h"
+
+#include "cached-powers.h"
+
+namespace double_conversion {
+
+namespace PowersOfTenCache {
+
+struct CachedPower {
+ uint64_t significand;
+ int16_t binary_exponent;
+ int16_t decimal_exponent;
+};
+
+static const CachedPower kCachedPowers[] = {
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xef340a98, 172aace5), -715, -196},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xaa242499, 697392d3), -183, -36},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xd1b71758, e219652c), -77, -4},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50, 4},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x813f3978, f8940984), 30, 28},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x924d692c, a61be758), 269, 100},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xa59bc234, db398c25), 508, 172},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xd01fef10, a657842c), 800, 260},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332},
+ {DOUBLE_CONVERSION_UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340},
+};
+
+static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
+static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
+
+void GetCachedPowerForBinaryExponentRange(
+ int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent) {
+ int kQ = DiyFp::kSignificandSize;
+ double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
+ int foo = kCachedPowersOffset;
+ int index =
+ (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
+ DOUBLE_CONVERSION_ASSERT(0 <= index && index < static_cast<int>(DOUBLE_CONVERSION_ARRAY_SIZE(kCachedPowers)));
+ CachedPower cached_power = kCachedPowers[index];
+ DOUBLE_CONVERSION_ASSERT(min_exponent <= cached_power.binary_exponent);
+ (void) max_exponent; // Mark variable as used.
+ DOUBLE_CONVERSION_ASSERT(cached_power.binary_exponent <= max_exponent);
+ *decimal_exponent = cached_power.decimal_exponent;
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+}
+
+
+void GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent) {
+ DOUBLE_CONVERSION_ASSERT(kMinDecimalExponent <= requested_exponent);
+ DOUBLE_CONVERSION_ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
+ int index =
+ (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
+ CachedPower cached_power = kCachedPowers[index];
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+ *found_exponent = cached_power.decimal_exponent;
+ DOUBLE_CONVERSION_ASSERT(*found_exponent <= requested_exponent);
+ DOUBLE_CONVERSION_ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
+}
+
+} // namespace PowersOfTenCache
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.h
new file mode 100644
index 0000000000..f38c26d201
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/cached-powers.h
@@ -0,0 +1,64 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_CACHED_POWERS_H_
+#define DOUBLE_CONVERSION_CACHED_POWERS_H_
+
+#include "diy-fp.h"
+
+namespace double_conversion {
+
+namespace PowersOfTenCache {
+
+ // Not all powers of ten are cached. The decimal exponent of two neighboring
+ // cached numbers will differ by kDecimalExponentDistance.
+ static const int kDecimalExponentDistance = 8;
+
+ static const int kMinDecimalExponent = -348;
+ static const int kMaxDecimalExponent = 340;
+
+ // Returns a cached power-of-ten with a binary exponent in the range
+ // [min_exponent; max_exponent] (boundaries included).
+ void GetCachedPowerForBinaryExponentRange(int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent);
+
+ // Returns a cached power of ten x ~= 10^k such that
+ // k <= decimal_exponent < k + kCachedPowersDecimalDistance.
+ // The given decimal_exponent must satisfy
+ // kMinDecimalExponent <= requested_exponent, and
+ // requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance.
+ void GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent);
+
+} // namespace PowersOfTenCache
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_CACHED_POWERS_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/diy-fp.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/diy-fp.h
new file mode 100644
index 0000000000..a2200c4ded
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/diy-fp.h
@@ -0,0 +1,137 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_DIY_FP_H_
+#define DOUBLE_CONVERSION_DIY_FP_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+// This "Do It Yourself Floating Point" class implements a floating-point number
+// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
+// have the most significant bit of the significand set.
+// Multiplication and Subtraction do not normalize their results.
+// DiyFp store only non-negative numbers and are not designed to contain special
+// doubles (NaN and Infinity).
+class DiyFp {
+ public:
+ static const int kSignificandSize = 64;
+
+ DiyFp() : f_(0), e_(0) {}
+ DiyFp(const uint64_t significand, const int32_t exponent) : f_(significand), e_(exponent) {}
+
+ // this -= other.
+ // The exponents of both numbers must be the same and the significand of this
+ // must be greater or equal than the significand of other.
+ // The result will not be normalized.
+ void Subtract(const DiyFp& other) {
+ DOUBLE_CONVERSION_ASSERT(e_ == other.e_);
+ DOUBLE_CONVERSION_ASSERT(f_ >= other.f_);
+ f_ -= other.f_;
+ }
+
+ // Returns a - b.
+ // The exponents of both numbers must be the same and a must be greater
+ // or equal than b. The result will not be normalized.
+ static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Subtract(b);
+ return result;
+ }
+
+ // this *= other.
+ void Multiply(const DiyFp& other) {
+ // Simply "emulates" a 128 bit multiplication.
+ // However: the resulting number only contains 64 bits. The least
+ // significant 64 bits are only used for rounding the most significant 64
+ // bits.
+ const uint64_t kM32 = 0xFFFFFFFFU;
+ const uint64_t a = f_ >> 32;
+ const uint64_t b = f_ & kM32;
+ const uint64_t c = other.f_ >> 32;
+ const uint64_t d = other.f_ & kM32;
+ const uint64_t ac = a * c;
+ const uint64_t bc = b * c;
+ const uint64_t ad = a * d;
+ const uint64_t bd = b * d;
+ // By adding 1U << 31 to tmp we round the final result.
+ // Halfway cases will be rounded up.
+ const uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32) + (1U << 31);
+ e_ += other.e_ + 64;
+ f_ = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ }
+
+ // returns a * b;
+ static DiyFp Times(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Multiply(b);
+ return result;
+ }
+
+ void Normalize() {
+ DOUBLE_CONVERSION_ASSERT(f_ != 0);
+ uint64_t significand = f_;
+ int32_t exponent = e_;
+
+ // This method is mainly called for normalizing boundaries. In general,
+ // boundaries need to be shifted by 10 bits, and we optimize for this case.
+ const uint64_t k10MSBits = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFC00000, 00000000);
+ while ((significand & k10MSBits) == 0) {
+ significand <<= 10;
+ exponent -= 10;
+ }
+ while ((significand & kUint64MSB) == 0) {
+ significand <<= 1;
+ exponent--;
+ }
+ f_ = significand;
+ e_ = exponent;
+ }
+
+ static DiyFp Normalize(const DiyFp& a) {
+ DiyFp result = a;
+ result.Normalize();
+ return result;
+ }
+
+ uint64_t f() const { return f_; }
+ int32_t e() const { return e_; }
+
+ void set_f(uint64_t new_value) { f_ = new_value; }
+ void set_e(int32_t new_value) { e_ = new_value; }
+
+ private:
+ static const uint64_t kUint64MSB = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000);
+
+ uint64_t f_;
+ int32_t e_;
+};
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_DIY_FP_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-conversion.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-conversion.h
new file mode 100644
index 0000000000..6e8884d84c
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-conversion.h
@@ -0,0 +1,34 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_
+#define DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_
+
+#include "string-to-double.h"
+#include "double-to-string.h"
+
+#endif // DOUBLE_CONVERSION_DOUBLE_CONVERSION_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.cc
new file mode 100644
index 0000000000..4562f99f49
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.cc
@@ -0,0 +1,428 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <algorithm>
+#include <climits>
+#include <cmath>
+
+#include "double-to-string.h"
+
+#include "bignum-dtoa.h"
+#include "fast-dtoa.h"
+#include "fixed-dtoa.h"
+#include "ieee.h"
+#include "utils.h"
+
+namespace double_conversion {
+
+const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() {
+ int flags = UNIQUE_ZERO | EMIT_POSITIVE_EXPONENT_SIGN;
+ static DoubleToStringConverter converter(flags,
+ "Infinity",
+ "NaN",
+ 'e',
+ -6, 21,
+ 6, 0);
+ return converter;
+}
+
+
+bool DoubleToStringConverter::HandleSpecialValues(
+ double value,
+ StringBuilder* result_builder) const {
+ Double double_inspect(value);
+ if (double_inspect.IsInfinite()) {
+ if (infinity_symbol_ == NULL) return false;
+ if (value < 0) {
+ result_builder->AddCharacter('-');
+ }
+ result_builder->AddString(infinity_symbol_);
+ return true;
+ }
+ if (double_inspect.IsNan()) {
+ if (nan_symbol_ == NULL) return false;
+ result_builder->AddString(nan_symbol_);
+ return true;
+ }
+ return false;
+}
+
+
+void DoubleToStringConverter::CreateExponentialRepresentation(
+ const char* decimal_digits,
+ int length,
+ int exponent,
+ StringBuilder* result_builder) const {
+ DOUBLE_CONVERSION_ASSERT(length != 0);
+ result_builder->AddCharacter(decimal_digits[0]);
+ if (length != 1) {
+ result_builder->AddCharacter('.');
+ result_builder->AddSubstring(&decimal_digits[1], length-1);
+ }
+ result_builder->AddCharacter(exponent_character_);
+ if (exponent < 0) {
+ result_builder->AddCharacter('-');
+ exponent = -exponent;
+ } else {
+ if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
+ result_builder->AddCharacter('+');
+ }
+ }
+ if (exponent == 0) {
+ result_builder->AddCharacter('0');
+ return;
+ }
+ DOUBLE_CONVERSION_ASSERT(exponent < 1e4);
+ // Changing this constant requires updating the comment of DoubleToStringConverter constructor
+ const int kMaxExponentLength = 5;
+ char buffer[kMaxExponentLength + 1];
+ buffer[kMaxExponentLength] = '\0';
+ int first_char_pos = kMaxExponentLength;
+ while (exponent > 0) {
+ buffer[--first_char_pos] = '0' + (exponent % 10);
+ exponent /= 10;
+ }
+ // Add prefix '0' to make exponent width >= min(min_exponent_with_, kMaxExponentLength)
+ // For example: convert 1e+9 -> 1e+09, if min_exponent_with_ is set to 2
+ while(kMaxExponentLength - first_char_pos < std::min(min_exponent_width_, kMaxExponentLength)) {
+ buffer[--first_char_pos] = '0';
+ }
+ result_builder->AddSubstring(&buffer[first_char_pos],
+ kMaxExponentLength - first_char_pos);
+}
+
+
+void DoubleToStringConverter::CreateDecimalRepresentation(
+ const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ StringBuilder* result_builder) const {
+ // Create a representation that is padded with zeros if needed.
+ if (decimal_point <= 0) {
+ // "0.00000decimal_rep" or "0.000decimal_rep00".
+ result_builder->AddCharacter('0');
+ if (digits_after_point > 0) {
+ result_builder->AddCharacter('.');
+ result_builder->AddPadding('0', -decimal_point);
+ DOUBLE_CONVERSION_ASSERT(length <= digits_after_point - (-decimal_point));
+ result_builder->AddSubstring(decimal_digits, length);
+ int remaining_digits = digits_after_point - (-decimal_point) - length;
+ result_builder->AddPadding('0', remaining_digits);
+ }
+ } else if (decimal_point >= length) {
+ // "decimal_rep0000.00000" or "decimal_rep.0000".
+ result_builder->AddSubstring(decimal_digits, length);
+ result_builder->AddPadding('0', decimal_point - length);
+ if (digits_after_point > 0) {
+ result_builder->AddCharacter('.');
+ result_builder->AddPadding('0', digits_after_point);
+ }
+ } else {
+ // "decima.l_rep000".
+ DOUBLE_CONVERSION_ASSERT(digits_after_point > 0);
+ result_builder->AddSubstring(decimal_digits, decimal_point);
+ result_builder->AddCharacter('.');
+ DOUBLE_CONVERSION_ASSERT(length - decimal_point <= digits_after_point);
+ result_builder->AddSubstring(&decimal_digits[decimal_point],
+ length - decimal_point);
+ int remaining_digits = digits_after_point - (length - decimal_point);
+ result_builder->AddPadding('0', remaining_digits);
+ }
+ if (digits_after_point == 0) {
+ if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
+ result_builder->AddCharacter('.');
+ }
+ if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
+ result_builder->AddCharacter('0');
+ }
+ }
+}
+
+
+bool DoubleToStringConverter::ToShortestIeeeNumber(
+ double value,
+ StringBuilder* result_builder,
+ DoubleToStringConverter::DtoaMode mode) const {
+ DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE);
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ int decimal_point;
+ bool sign;
+ const int kDecimalRepCapacity = kBase10MaximalLength + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ int exponent = decimal_point - 1;
+ if ((decimal_in_shortest_low_ <= exponent) &&
+ (exponent < decimal_in_shortest_high_)) {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length,
+ decimal_point,
+ (std::max)(0, decimal_rep_length - decimal_point),
+ result_builder);
+ } else {
+ CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent,
+ result_builder);
+ }
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToFixed(double value,
+ int requested_digits,
+ StringBuilder* result_builder) const {
+ DOUBLE_CONVERSION_ASSERT(kMaxFixedDigitsBeforePoint == 60);
+ const double kFirstNonFixed = 1e60;
+
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
+ if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false;
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add space for the '\0' byte.
+ const int kDecimalRepCapacity =
+ kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+ DoubleToAscii(value, FIXED, requested_digits,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ requested_digits, result_builder);
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToExponential(
+ double value,
+ int requested_digits,
+ StringBuilder* result_builder) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ if (requested_digits < -1) return false;
+ if (requested_digits > kMaxExponentialDigits) return false;
+
+ int decimal_point;
+ bool sign;
+ // Add space for digit before the decimal point and the '\0' character.
+ const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
+ DOUBLE_CONVERSION_ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
+ char decimal_rep[kDecimalRepCapacity];
+#ifndef NDEBUG
+ // Problem: there is an assert in StringBuilder::AddSubstring() that
+ // will pass this buffer to strlen(), and this buffer is not generally
+ // null-terminated.
+ memset(decimal_rep, 0, sizeof(decimal_rep));
+#endif
+ int decimal_rep_length;
+
+ if (requested_digits == -1) {
+ DoubleToAscii(value, SHORTEST, 0,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ } else {
+ DoubleToAscii(value, PRECISION, requested_digits + 1,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= requested_digits + 1);
+
+ for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
+ decimal_rep[i] = '0';
+ }
+ decimal_rep_length = requested_digits + 1;
+ }
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ int exponent = decimal_point - 1;
+ CreateExponentialRepresentation(decimal_rep,
+ decimal_rep_length,
+ exponent,
+ result_builder);
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToPrecision(double value,
+ int precision,
+ StringBuilder* result_builder) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result_builder);
+ }
+
+ if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
+ return false;
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add one for the terminating null character.
+ const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, PRECISION, precision,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ DOUBLE_CONVERSION_ASSERT(decimal_rep_length <= precision);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result_builder->AddCharacter('-');
+ }
+
+ // The exponent if we print the number as x.xxeyyy. That is with the
+ // decimal point after the first digit.
+ int exponent = decimal_point - 1;
+
+ int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
+ if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
+ (decimal_point - precision + extra_zero >
+ max_trailing_padding_zeroes_in_precision_mode_)) {
+ // Fill buffer to contain 'precision' digits.
+ // Usually the buffer is already at the correct length, but 'DoubleToAscii'
+ // is allowed to return less characters.
+ for (int i = decimal_rep_length; i < precision; ++i) {
+ decimal_rep[i] = '0';
+ }
+
+ CreateExponentialRepresentation(decimal_rep,
+ precision,
+ exponent,
+ result_builder);
+ } else {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ (std::max)(0, precision - decimal_point),
+ result_builder);
+ }
+ return true;
+}
+
+
+static BignumDtoaMode DtoaToBignumDtoaMode(
+ DoubleToStringConverter::DtoaMode dtoa_mode) {
+ switch (dtoa_mode) {
+ case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
+ case DoubleToStringConverter::SHORTEST_SINGLE:
+ return BIGNUM_DTOA_SHORTEST_SINGLE;
+ case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
+ case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
+ default:
+ DOUBLE_CONVERSION_UNREACHABLE();
+ }
+}
+
+
+void DoubleToStringConverter::DoubleToAscii(double v,
+ DtoaMode mode,
+ int requested_digits,
+ char* buffer,
+ int buffer_length,
+ bool* sign,
+ int* length,
+ int* point) {
+ Vector<char> vector(buffer, buffer_length);
+ DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
+ DOUBLE_CONVERSION_ASSERT(mode == SHORTEST || mode == SHORTEST_SINGLE || requested_digits >= 0);
+
+ if (Double(v).Sign() < 0) {
+ *sign = true;
+ v = -v;
+ } else {
+ *sign = false;
+ }
+
+ if (mode == PRECISION && requested_digits == 0) {
+ vector[0] = '\0';
+ *length = 0;
+ return;
+ }
+
+ if (v == 0) {
+ vector[0] = '0';
+ vector[1] = '\0';
+ *length = 1;
+ *point = 1;
+ return;
+ }
+
+ bool fast_worked;
+ switch (mode) {
+ case SHORTEST:
+ fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
+ break;
+ case SHORTEST_SINGLE:
+ fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST_SINGLE, 0,
+ vector, length, point);
+ break;
+ case FIXED:
+ fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
+ break;
+ case PRECISION:
+ fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
+ vector, length, point);
+ break;
+ default:
+ fast_worked = false;
+ DOUBLE_CONVERSION_UNREACHABLE();
+ }
+ if (fast_worked) return;
+
+ // If the fast dtoa didn't succeed use the slower bignum version.
+ BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
+ BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
+ vector[*length] = '\0';
+}
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.h
new file mode 100644
index 0000000000..a44fa3c7e9
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/double-to-string.h
@@ -0,0 +1,396 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_
+#define DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+class DoubleToStringConverter {
+ public:
+ // When calling ToFixed with a double > 10^kMaxFixedDigitsBeforePoint
+ // or a requested_digits parameter > kMaxFixedDigitsAfterPoint then the
+ // function returns false.
+ static const int kMaxFixedDigitsBeforePoint = 60;
+ static const int kMaxFixedDigitsAfterPoint = 60;
+
+ // When calling ToExponential with a requested_digits
+ // parameter > kMaxExponentialDigits then the function returns false.
+ static const int kMaxExponentialDigits = 120;
+
+ // When calling ToPrecision with a requested_digits
+ // parameter < kMinPrecisionDigits or requested_digits > kMaxPrecisionDigits
+ // then the function returns false.
+ static const int kMinPrecisionDigits = 1;
+ static const int kMaxPrecisionDigits = 120;
+
+ enum Flags {
+ NO_FLAGS = 0,
+ EMIT_POSITIVE_EXPONENT_SIGN = 1,
+ EMIT_TRAILING_DECIMAL_POINT = 2,
+ EMIT_TRAILING_ZERO_AFTER_POINT = 4,
+ UNIQUE_ZERO = 8
+ };
+
+ // Flags should be a bit-or combination of the possible Flags-enum.
+ // - NO_FLAGS: no special flags.
+ // - EMIT_POSITIVE_EXPONENT_SIGN: when the number is converted into exponent
+ // form, emits a '+' for positive exponents. Example: 1.2e+2.
+ // - EMIT_TRAILING_DECIMAL_POINT: when the input number is an integer and is
+ // converted into decimal format then a trailing decimal point is appended.
+ // Example: 2345.0 is converted to "2345.".
+ // - EMIT_TRAILING_ZERO_AFTER_POINT: in addition to a trailing decimal point
+ // emits a trailing '0'-character. This flag requires the
+ // EXMIT_TRAILING_DECIMAL_POINT flag.
+ // Example: 2345.0 is converted to "2345.0".
+ // - UNIQUE_ZERO: "-0.0" is converted to "0.0".
+ //
+ // Infinity symbol and nan_symbol provide the string representation for these
+ // special values. If the string is NULL and the special value is encountered
+ // then the conversion functions return false.
+ //
+ // The exponent_character is used in exponential representations. It is
+ // usually 'e' or 'E'.
+ //
+ // When converting to the shortest representation the converter will
+ // represent input numbers in decimal format if they are in the interval
+ // [10^decimal_in_shortest_low; 10^decimal_in_shortest_high[
+ // (lower boundary included, greater boundary excluded).
+ // Example: with decimal_in_shortest_low = -6 and
+ // decimal_in_shortest_high = 21:
+ // ToShortest(0.000001) -> "0.000001"
+ // ToShortest(0.0000001) -> "1e-7"
+ // ToShortest(111111111111111111111.0) -> "111111111111111110000"
+ // ToShortest(100000000000000000000.0) -> "100000000000000000000"
+ // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21"
+ //
+ // When converting to precision mode the converter may add
+ // max_leading_padding_zeroes before returning the number in exponential
+ // format.
+ // Example with max_leading_padding_zeroes_in_precision_mode = 6.
+ // ToPrecision(0.0000012345, 2) -> "0.0000012"
+ // ToPrecision(0.00000012345, 2) -> "1.2e-7"
+ // Similarily the converter may add up to
+ // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid
+ // returning an exponential representation. A zero added by the
+ // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit.
+ // Examples for max_trailing_padding_zeroes_in_precision_mode = 1:
+ // ToPrecision(230.0, 2) -> "230"
+ // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT.
+ // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
+ //
+ // The min_exponent_width is used for exponential representations.
+ // The converter adds leading '0's to the exponent until the exponent
+ // is at least min_exponent_width digits long.
+ // The min_exponent_width is clamped to 5.
+ // As such, the exponent may never have more than 5 digits in total.
+ DoubleToStringConverter(int flags,
+ const char* infinity_symbol,
+ const char* nan_symbol,
+ char exponent_character,
+ int decimal_in_shortest_low,
+ int decimal_in_shortest_high,
+ int max_leading_padding_zeroes_in_precision_mode,
+ int max_trailing_padding_zeroes_in_precision_mode,
+ int min_exponent_width = 0)
+ : flags_(flags),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol),
+ exponent_character_(exponent_character),
+ decimal_in_shortest_low_(decimal_in_shortest_low),
+ decimal_in_shortest_high_(decimal_in_shortest_high),
+ max_leading_padding_zeroes_in_precision_mode_(
+ max_leading_padding_zeroes_in_precision_mode),
+ max_trailing_padding_zeroes_in_precision_mode_(
+ max_trailing_padding_zeroes_in_precision_mode),
+ min_exponent_width_(min_exponent_width) {
+ // When 'trailing zero after the point' is set, then 'trailing point'
+ // must be set too.
+ DOUBLE_CONVERSION_ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) ||
+ !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0));
+ }
+
+ // Returns a converter following the EcmaScript specification.
+ static const DoubleToStringConverter& EcmaScriptConverter();
+
+ // Computes the shortest string of digits that correctly represent the input
+ // number. Depending on decimal_in_shortest_low and decimal_in_shortest_high
+ // (see constructor) it then either returns a decimal representation, or an
+ // exponential representation.
+ // Example with decimal_in_shortest_low = -6,
+ // decimal_in_shortest_high = 21,
+ // EMIT_POSITIVE_EXPONENT_SIGN activated, and
+ // EMIT_TRAILING_DECIMAL_POINT deactived:
+ // ToShortest(0.000001) -> "0.000001"
+ // ToShortest(0.0000001) -> "1e-7"
+ // ToShortest(111111111111111111111.0) -> "111111111111111110000"
+ // ToShortest(100000000000000000000.0) -> "100000000000000000000"
+ // ToShortest(1111111111111111111111.0) -> "1.1111111111111111e+21"
+ //
+ // Note: the conversion may round the output if the returned string
+ // is accurate enough to uniquely identify the input-number.
+ // For example the most precise representation of the double 9e59 equals
+ // "899999999999999918767229449717619953810131273674690656206848", but
+ // the converter will return the shorter (but still correct) "9e59".
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except when the input value is special and no infinity_symbol or
+ // nan_symbol has been given to the constructor.
+ bool ToShortest(double value, StringBuilder* result_builder) const {
+ return ToShortestIeeeNumber(value, result_builder, SHORTEST);
+ }
+
+ // Same as ToShortest, but for single-precision floats.
+ bool ToShortestSingle(float value, StringBuilder* result_builder) const {
+ return ToShortestIeeeNumber(value, result_builder, SHORTEST_SINGLE);
+ }
+
+
+ // Computes a decimal representation with a fixed number of digits after the
+ // decimal point. The last emitted digit is rounded.
+ //
+ // Examples:
+ // ToFixed(3.12, 1) -> "3.1"
+ // ToFixed(3.1415, 3) -> "3.142"
+ // ToFixed(1234.56789, 4) -> "1234.5679"
+ // ToFixed(1.23, 5) -> "1.23000"
+ // ToFixed(0.1, 4) -> "0.1000"
+ // ToFixed(1e30, 2) -> "1000000000000000019884624838656.00"
+ // ToFixed(0.1, 30) -> "0.100000000000000005551115123126"
+ // ToFixed(0.1, 17) -> "0.10000000000000001"
+ //
+ // If requested_digits equals 0, then the tail of the result depends on
+ // the EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT.
+ // Examples, for requested_digits == 0,
+ // let EMIT_TRAILING_DECIMAL_POINT and EMIT_TRAILING_ZERO_AFTER_POINT be
+ // - false and false: then 123.45 -> 123
+ // 0.678 -> 1
+ // - true and false: then 123.45 -> 123.
+ // 0.678 -> 1.
+ // - true and true: then 123.45 -> 123.0
+ // 0.678 -> 1.0
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except for the following cases:
+ // - the input value is special and no infinity_symbol or nan_symbol has
+ // been provided to the constructor,
+ // - 'value' > 10^kMaxFixedDigitsBeforePoint, or
+ // - 'requested_digits' > kMaxFixedDigitsAfterPoint.
+ // The last two conditions imply that the result will never contain more than
+ // 1 + kMaxFixedDigitsBeforePoint + 1 + kMaxFixedDigitsAfterPoint characters
+ // (one additional character for the sign, and one for the decimal point).
+ bool ToFixed(double value,
+ int requested_digits,
+ StringBuilder* result_builder) const;
+
+ // Computes a representation in exponential format with requested_digits
+ // after the decimal point. The last emitted digit is rounded.
+ // If requested_digits equals -1, then the shortest exponential representation
+ // is computed.
+ //
+ // Examples with EMIT_POSITIVE_EXPONENT_SIGN deactivated, and
+ // exponent_character set to 'e'.
+ // ToExponential(3.12, 1) -> "3.1e0"
+ // ToExponential(5.0, 3) -> "5.000e0"
+ // ToExponential(0.001, 2) -> "1.00e-3"
+ // ToExponential(3.1415, -1) -> "3.1415e0"
+ // ToExponential(3.1415, 4) -> "3.1415e0"
+ // ToExponential(3.1415, 3) -> "3.142e0"
+ // ToExponential(123456789000000, 3) -> "1.235e14"
+ // ToExponential(1000000000000000019884624838656.0, -1) -> "1e30"
+ // ToExponential(1000000000000000019884624838656.0, 32) ->
+ // "1.00000000000000001988462483865600e30"
+ // ToExponential(1234, 0) -> "1e3"
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except for the following cases:
+ // - the input value is special and no infinity_symbol or nan_symbol has
+ // been provided to the constructor,
+ // - 'requested_digits' > kMaxExponentialDigits.
+ // The last condition implies that the result will never contain more than
+ // kMaxExponentialDigits + 8 characters (the sign, the digit before the
+ // decimal point, the decimal point, the exponent character, the
+ // exponent's sign, and at most 3 exponent digits).
+ bool ToExponential(double value,
+ int requested_digits,
+ StringBuilder* result_builder) const;
+
+ // Computes 'precision' leading digits of the given 'value' and returns them
+ // either in exponential or decimal format, depending on
+ // max_{leading|trailing}_padding_zeroes_in_precision_mode (given to the
+ // constructor).
+ // The last computed digit is rounded.
+ //
+ // Example with max_leading_padding_zeroes_in_precision_mode = 6.
+ // ToPrecision(0.0000012345, 2) -> "0.0000012"
+ // ToPrecision(0.00000012345, 2) -> "1.2e-7"
+ // Similarily the converter may add up to
+ // max_trailing_padding_zeroes_in_precision_mode in precision mode to avoid
+ // returning an exponential representation. A zero added by the
+ // EMIT_TRAILING_ZERO_AFTER_POINT flag is counted for this limit.
+ // Examples for max_trailing_padding_zeroes_in_precision_mode = 1:
+ // ToPrecision(230.0, 2) -> "230"
+ // ToPrecision(230.0, 2) -> "230." with EMIT_TRAILING_DECIMAL_POINT.
+ // ToPrecision(230.0, 2) -> "2.3e2" with EMIT_TRAILING_ZERO_AFTER_POINT.
+ // Examples for max_trailing_padding_zeroes_in_precision_mode = 3, and no
+ // EMIT_TRAILING_ZERO_AFTER_POINT:
+ // ToPrecision(123450.0, 6) -> "123450"
+ // ToPrecision(123450.0, 5) -> "123450"
+ // ToPrecision(123450.0, 4) -> "123500"
+ // ToPrecision(123450.0, 3) -> "123000"
+ // ToPrecision(123450.0, 2) -> "1.2e5"
+ //
+ // Returns true if the conversion succeeds. The conversion always succeeds
+ // except for the following cases:
+ // - the input value is special and no infinity_symbol or nan_symbol has
+ // been provided to the constructor,
+ // - precision < kMinPericisionDigits
+ // - precision > kMaxPrecisionDigits
+ // The last condition implies that the result will never contain more than
+ // kMaxPrecisionDigits + 7 characters (the sign, the decimal point, the
+ // exponent character, the exponent's sign, and at most 3 exponent digits).
+ bool ToPrecision(double value,
+ int precision,
+ StringBuilder* result_builder) const;
+
+ enum DtoaMode {
+ // Produce the shortest correct representation.
+ // For example the output of 0.299999999999999988897 is (the less accurate
+ // but correct) 0.3.
+ SHORTEST,
+ // Same as SHORTEST, but for single-precision floats.
+ SHORTEST_SINGLE,
+ // Produce a fixed number of digits after the decimal point.
+ // For instance fixed(0.1, 4) becomes 0.1000
+ // If the input number is big, the output will be big.
+ FIXED,
+ // Fixed number of digits (independent of the decimal point).
+ PRECISION
+ };
+
+ // The maximal number of digits that are needed to emit a double in base 10.
+ // A higher precision can be achieved by using more digits, but the shortest
+ // accurate representation of any double will never use more digits than
+ // kBase10MaximalLength.
+ // Note that DoubleToAscii null-terminates its input. So the given buffer
+ // should be at least kBase10MaximalLength + 1 characters long.
+ static const int kBase10MaximalLength = 17;
+
+ // Converts the given double 'v' to digit characters. 'v' must not be NaN,
+ // +Infinity, or -Infinity. In SHORTEST_SINGLE-mode this restriction also
+ // applies to 'v' after it has been casted to a single-precision float. That
+ // is, in this mode static_cast<float>(v) must not be NaN, +Infinity or
+ // -Infinity.
+ //
+ // The result should be interpreted as buffer * 10^(point-length).
+ //
+ // The digits are written to the buffer in the platform's charset, which is
+ // often UTF-8 (with ASCII-range digits) but may be another charset, such
+ // as EBCDIC.
+ //
+ // The output depends on the given mode:
+ // - SHORTEST: produce the least amount of digits for which the internal
+ // identity requirement is still satisfied. If the digits are printed
+ // (together with the correct exponent) then reading this number will give
+ // 'v' again. The buffer will choose the representation that is closest to
+ // 'v'. If there are two at the same distance, than the one farther away
+ // from 0 is chosen (halfway cases - ending with 5 - are rounded up).
+ // In this mode the 'requested_digits' parameter is ignored.
+ // - SHORTEST_SINGLE: same as SHORTEST but with single-precision.
+ // - FIXED: produces digits necessary to print a given number with
+ // 'requested_digits' digits after the decimal point. The produced digits
+ // might be too short in which case the caller has to fill the remainder
+ // with '0's.
+ // Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
+ // Halfway cases are rounded towards +/-Infinity (away from 0). The call
+ // toFixed(0.15, 2) thus returns buffer="2", point=0.
+ // The returned buffer may contain digits that would be truncated from the
+ // shortest representation of the input.
+ // - PRECISION: produces 'requested_digits' where the first digit is not '0'.
+ // Even though the length of produced digits usually equals
+ // 'requested_digits', the function is allowed to return fewer digits, in
+ // which case the caller has to fill the missing digits with '0's.
+ // Halfway cases are again rounded away from 0.
+ // DoubleToAscii expects the given buffer to be big enough to hold all
+ // digits and a terminating null-character. In SHORTEST-mode it expects a
+ // buffer of at least kBase10MaximalLength + 1. In all other modes the
+ // requested_digits parameter and the padding-zeroes limit the size of the
+ // output. Don't forget the decimal point, the exponent character and the
+ // terminating null-character when computing the maximal output size.
+ // The given length is only used in debug mode to ensure the buffer is big
+ // enough.
+ static void DoubleToAscii(double v,
+ DtoaMode mode,
+ int requested_digits,
+ char* buffer,
+ int buffer_length,
+ bool* sign,
+ int* length,
+ int* point);
+
+ private:
+ // Implementation for ToShortest and ToShortestSingle.
+ bool ToShortestIeeeNumber(double value,
+ StringBuilder* result_builder,
+ DtoaMode mode) const;
+
+ // If the value is a special value (NaN or Infinity) constructs the
+ // corresponding string using the configured infinity/nan-symbol.
+ // If either of them is NULL or the value is not special then the
+ // function returns false.
+ bool HandleSpecialValues(double value, StringBuilder* result_builder) const;
+ // Constructs an exponential representation (i.e. 1.234e56).
+ // The given exponent assumes a decimal point after the first decimal digit.
+ void CreateExponentialRepresentation(const char* decimal_digits,
+ int length,
+ int exponent,
+ StringBuilder* result_builder) const;
+ // Creates a decimal representation (i.e 1234.5678).
+ void CreateDecimalRepresentation(const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ StringBuilder* result_builder) const;
+
+ const int flags_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+ const char exponent_character_;
+ const int decimal_in_shortest_low_;
+ const int decimal_in_shortest_high_;
+ const int max_leading_padding_zeroes_in_precision_mode_;
+ const int max_trailing_padding_zeroes_in_precision_mode_;
+ const int min_exponent_width_;
+
+ DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
+};
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_DOUBLE_TO_STRING_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.cc
new file mode 100644
index 0000000000..f470286437
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.cc
@@ -0,0 +1,665 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include "fast-dtoa.h"
+
+#include "cached-powers.h"
+#include "diy-fp.h"
+#include "ieee.h"
+
+namespace double_conversion {
+
+// The minimal and maximal target exponent define the range of w's binary
+// exponent, where 'w' is the result of multiplying the input by a cached power
+// of ten.
+//
+// A different range might be chosen on a different platform, to optimize digit
+// generation, but a smaller range requires more powers of ten to be cached.
+static const int kMinimalTargetExponent = -60;
+static const int kMaximalTargetExponent = -32;
+
+
+// Adjusts the last digit of the generated number, and screens out generated
+// solutions that may be inaccurate. A solution may be inaccurate if it is
+// outside the safe interval, or if we cannot prove that it is closer to the
+// input than a neighboring representation of the same length.
+//
+// Input: * buffer containing the digits of too_high / 10^kappa
+// * the buffer's length
+// * distance_too_high_w == (too_high - w).f() * unit
+// * unsafe_interval == (too_high - too_low).f() * unit
+// * rest = (too_high - buffer * 10^kappa).f() * unit
+// * ten_kappa = 10^kappa * unit
+// * unit = the common multiplier
+// Output: returns true if the buffer is guaranteed to contain the closest
+// representable number to the input.
+// Modifies the generated digits in the buffer to approach (round towards) w.
+static bool RoundWeed(Vector<char> buffer,
+ int length,
+ uint64_t distance_too_high_w,
+ uint64_t unsafe_interval,
+ uint64_t rest,
+ uint64_t ten_kappa,
+ uint64_t unit) {
+ uint64_t small_distance = distance_too_high_w - unit;
+ uint64_t big_distance = distance_too_high_w + unit;
+ // Let w_low = too_high - big_distance, and
+ // w_high = too_high - small_distance.
+ // Note: w_low < w < w_high
+ //
+ // The real w (* unit) must lie somewhere inside the interval
+ // ]w_low; w_high[ (often written as "(w_low; w_high)")
+
+ // Basically the buffer currently contains a number in the unsafe interval
+ // ]too_low; too_high[ with too_low < w < too_high
+ //
+ // too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ // ^v 1 unit ^ ^ ^ ^
+ // boundary_high --------------------- . . . .
+ // ^v 1 unit . . . .
+ // - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . .
+ // . . ^ . .
+ // . big_distance . . .
+ // . . . . rest
+ // small_distance . . . .
+ // v . . . .
+ // w_high - - - - - - - - - - - - - - - - - - . . . .
+ // ^v 1 unit . . . .
+ // w ---------------------------------------- . . . .
+ // ^v 1 unit v . . .
+ // w_low - - - - - - - - - - - - - - - - - - - - - . . .
+ // . . v
+ // buffer --------------------------------------------------+-------+--------
+ // . .
+ // safe_interval .
+ // v .
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
+ // ^v 1 unit .
+ // boundary_low ------------------------- unsafe_interval
+ // ^v 1 unit v
+ // too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ //
+ //
+ // Note that the value of buffer could lie anywhere inside the range too_low
+ // to too_high.
+ //
+ // boundary_low, boundary_high and w are approximations of the real boundaries
+ // and v (the input number). They are guaranteed to be precise up to one unit.
+ // In fact the error is guaranteed to be strictly less than one unit.
+ //
+ // Anything that lies outside the unsafe interval is guaranteed not to round
+ // to v when read again.
+ // Anything that lies inside the safe interval is guaranteed to round to v
+ // when read again.
+ // If the number inside the buffer lies inside the unsafe interval but not
+ // inside the safe interval then we simply do not know and bail out (returning
+ // false).
+ //
+ // Similarly we have to take into account the imprecision of 'w' when finding
+ // the closest representation of 'w'. If we have two potential
+ // representations, and one is closer to both w_low and w_high, then we know
+ // it is closer to the actual value v.
+ //
+ // By generating the digits of too_high we got the largest (closest to
+ // too_high) buffer that is still in the unsafe interval. In the case where
+ // w_high < buffer < too_high we try to decrement the buffer.
+ // This way the buffer approaches (rounds towards) w.
+ // There are 3 conditions that stop the decrementation process:
+ // 1) the buffer is already below w_high
+ // 2) decrementing the buffer would make it leave the unsafe interval
+ // 3) decrementing the buffer would yield a number below w_high and farther
+ // away than the current number. In other words:
+ // (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
+ // Instead of using the buffer directly we use its distance to too_high.
+ // Conceptually rest ~= too_high - buffer
+ // We need to do the following tests in this order to avoid over- and
+ // underflows.
+ DOUBLE_CONVERSION_ASSERT(rest <= unsafe_interval);
+ while (rest < small_distance && // Negated condition 1
+ unsafe_interval - rest >= ten_kappa && // Negated condition 2
+ (rest + ten_kappa < small_distance || // buffer{-1} > w_high
+ small_distance - rest >= rest + ten_kappa - small_distance)) {
+ buffer[length - 1]--;
+ rest += ten_kappa;
+ }
+
+ // We have approached w+ as much as possible. We now test if approaching w-
+ // would require changing the buffer. If yes, then we have two possible
+ // representations close to w, but we cannot decide which one is closer.
+ if (rest < big_distance &&
+ unsafe_interval - rest >= ten_kappa &&
+ (rest + ten_kappa < big_distance ||
+ big_distance - rest > rest + ten_kappa - big_distance)) {
+ return false;
+ }
+
+ // Weeding test.
+ // The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
+ // Since too_low = too_high - unsafe_interval this is equivalent to
+ // [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
+ // Conceptually we have: rest ~= too_high - buffer
+ return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit);
+}
+
+
+// Rounds the buffer upwards if the result is closer to v by possibly adding
+// 1 to the buffer. If the precision of the calculation is not sufficient to
+// round correctly, return false.
+// The rounding might shift the whole buffer in which case the kappa is
+// adjusted. For example "99", kappa = 3 might become "10", kappa = 4.
+//
+// If 2*rest > ten_kappa then the buffer needs to be round up.
+// rest can have an error of +/- 1 unit. This function accounts for the
+// imprecision and returns false, if the rounding direction cannot be
+// unambiguously determined.
+//
+// Precondition: rest < ten_kappa.
+static bool RoundWeedCounted(Vector<char> buffer,
+ int length,
+ uint64_t rest,
+ uint64_t ten_kappa,
+ uint64_t unit,
+ int* kappa) {
+ DOUBLE_CONVERSION_ASSERT(rest < ten_kappa);
+ // The following tests are done in a specific order to avoid overflows. They
+ // will work correctly with any uint64 values of rest < ten_kappa and unit.
+ //
+ // If the unit is too big, then we don't know which way to round. For example
+ // a unit of 50 means that the real number lies within rest +/- 50. If
+ // 10^kappa == 40 then there is no way to tell which way to round.
+ if (unit >= ten_kappa) return false;
+ // Even if unit is just half the size of 10^kappa we are already completely
+ // lost. (And after the previous test we know that the expression will not
+ // over/underflow.)
+ if (ten_kappa - unit <= unit) return false;
+ // If 2 * (rest + unit) <= 10^kappa we can safely round down.
+ if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
+ return true;
+ }
+ // If 2 * (rest - unit) >= 10^kappa, then we can safely round up.
+ if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
+ // Increment the last digit recursively until we find a non '9' digit.
+ buffer[length - 1]++;
+ for (int i = length - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ // If the first digit is now '0'+ 10 we had a buffer with all '9's. With the
+ // exception of the first digit all digits are now '0'. Simply switch the
+ // first digit to '1' and adjust the kappa. Example: "99" becomes "10" and
+ // the power (the kappa) is increased.
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*kappa) += 1;
+ }
+ return true;
+ }
+ return false;
+}
+
+// Returns the biggest power of ten that is less than or equal to the given
+// number. We furthermore receive the maximum number of bits 'number' has.
+//
+// Returns power == 10^(exponent_plus_one-1) such that
+// power <= number < power * 10.
+// If number_bits == 0 then 0^(0-1) is returned.
+// The number of bits must be <= 32.
+// Precondition: number < (1 << (number_bits + 1)).
+
+// Inspired by the method for finding an integer log base 10 from here:
+// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+static unsigned int const kSmallPowersOfTen[] =
+ {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+ 1000000000};
+
+static void BiggestPowerTen(uint32_t number,
+ int number_bits,
+ uint32_t* power,
+ int* exponent_plus_one) {
+ DOUBLE_CONVERSION_ASSERT(number < (1u << (number_bits + 1)));
+ // 1233/4096 is approximately 1/lg(10).
+ int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12);
+ // We increment to skip over the first entry in the kPowersOf10 table.
+ // Note: kPowersOf10[i] == 10^(i-1).
+ exponent_plus_one_guess++;
+ // We don't have any guarantees that 2^number_bits <= number.
+ if (number < kSmallPowersOfTen[exponent_plus_one_guess]) {
+ exponent_plus_one_guess--;
+ }
+ *power = kSmallPowersOfTen[exponent_plus_one_guess];
+ *exponent_plus_one = exponent_plus_one_guess;
+}
+
+// Generates the digits of input number w.
+// w is a floating-point number (DiyFp), consisting of a significand and an
+// exponent. Its exponent is bounded by kMinimalTargetExponent and
+// kMaximalTargetExponent.
+// Hence -60 <= w.e() <= -32.
+//
+// Returns false if it fails, in which case the generated digits in the buffer
+// should not be used.
+// Preconditions:
+// * low, w and high are correct up to 1 ulp (unit in the last place). That
+// is, their error must be less than a unit of their last digits.
+// * low.e() == w.e() == high.e()
+// * low < w < high, and taking into account their error: low~ <= high~
+// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+// Postconditions: returns false if procedure fails.
+// otherwise:
+// * buffer is not null-terminated, but len contains the number of digits.
+// * buffer contains the shortest possible decimal digit-sequence
+// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
+// correct values of low and high (without their error).
+// * if more than one decimal representation gives the minimal number of
+// decimal digits then the one closest to W (where W is the correct value
+// of w) is chosen.
+// Remark: this procedure takes into account the imprecision of its input
+// numbers. If the precision is not enough to guarantee all the postconditions
+// then false is returned. This usually happens rarely (~0.5%).
+//
+// Say, for the sake of example, that
+// w.e() == -48, and w.f() == 0x1234567890abcdef
+// w's value can be computed by w.f() * 2^w.e()
+// We can obtain w's integral digits by simply shifting w.f() by -w.e().
+// -> w's integral part is 0x1234
+// w's fractional part is therefore 0x567890abcdef.
+// Printing w's integral part is easy (simply print 0x1234 in decimal).
+// In order to print its fraction we repeatedly multiply the fraction by 10 and
+// get each digit. Example the first digit after the point would be computed by
+// (0x567890abcdef * 10) >> 48. -> 3
+// The whole thing becomes slightly more complicated because we want to stop
+// once we have enough digits. That is, once the digits inside the buffer
+// represent 'w' we can stop. Everything inside the interval low - high
+// represents w. However we have to pay attention to low, high and w's
+// imprecision.
+static bool DigitGen(DiyFp low,
+ DiyFp w,
+ DiyFp high,
+ Vector<char> buffer,
+ int* length,
+ int* kappa) {
+ DOUBLE_CONVERSION_ASSERT(low.e() == w.e() && w.e() == high.e());
+ DOUBLE_CONVERSION_ASSERT(low.f() + 1 <= high.f() - 1);
+ DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+ // low, w and high are imprecise, but by less than one ulp (unit in the last
+ // place).
+ // If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
+ // the new numbers are outside of the interval we want the final
+ // representation to lie in.
+ // Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
+ // numbers that are certain to lie in the interval. We will use this fact
+ // later on.
+ // We will now start by generating the digits within the uncertain
+ // interval. Later we will weed out representations that lie outside the safe
+ // interval and thus _might_ lie outside the correct interval.
+ uint64_t unit = 1;
+ DiyFp too_low = DiyFp(low.f() - unit, low.e());
+ DiyFp too_high = DiyFp(high.f() + unit, high.e());
+ // too_low and too_high are guaranteed to lie outside the interval we want the
+ // generated number in.
+ DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low);
+ // We now cut the input number into two parts: the integral digits and the
+ // fractionals. We will not write any decimal separator though, but adapt
+ // kappa instead.
+ // Reminder: we are currently computing the digits (stored inside the buffer)
+ // such that: too_low < buffer * 10^kappa < too_high
+ // We use too_high for the digit_generation and stop as soon as possible.
+ // If we stop early we effectively round down.
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+ // Division by one is a shift.
+ uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e());
+ // Modulo by one is an and.
+ uint64_t fractionals = too_high.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent_plus_one;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent_plus_one);
+ *kappa = divisor_exponent_plus_one;
+ *length = 0;
+ // Loop invariant: buffer = too_high / 10^kappa (integer division)
+ // The invariant holds for the first iteration: kappa has been initialized
+ // with the divisor exponent + 1. And the divisor is the biggest power of ten
+ // that is smaller than integrals.
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ DOUBLE_CONVERSION_ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ integrals %= divisor;
+ (*kappa)--;
+ // Note that kappa now equals the exponent of the divisor and that the
+ // invariant thus holds again.
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+ // Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
+ // Reminder: unsafe_interval.e() == one.e()
+ if (rest < unsafe_interval.f()) {
+ // Rounding down (by not emitting the remaining digits) yields a number
+ // that lies within the unsafe interval.
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(),
+ unsafe_interval.f(), rest,
+ static_cast<uint64_t>(divisor) << -one.e(), unit);
+ }
+ divisor /= 10;
+ }
+
+ // The integrals have been generated. We are at the point of the decimal
+ // separator. In the following loop we simply multiply the remaining digits by
+ // 10 and divide by one. We just need to pay attention to multiply associated
+ // data (like the interval or 'unit'), too.
+ // Note that the multiplication by 10 does not overflow, because w.e >= -60
+ // and thus one.e >= -60.
+ DOUBLE_CONVERSION_ASSERT(one.e() >= -60);
+ DOUBLE_CONVERSION_ASSERT(fractionals < one.f());
+ DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+ for (;;) {
+ fractionals *= 10;
+ unit *= 10;
+ unsafe_interval.set_f(unsafe_interval.f() * 10);
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ DOUBLE_CONVERSION_ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ if (fractionals < unsafe_interval.f()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit,
+ unsafe_interval.f(), fractionals, one.f(), unit);
+ }
+ }
+}
+
+
+
+// Generates (at most) requested_digits digits of input number w.
+// w is a floating-point number (DiyFp), consisting of a significand and an
+// exponent. Its exponent is bounded by kMinimalTargetExponent and
+// kMaximalTargetExponent.
+// Hence -60 <= w.e() <= -32.
+//
+// Returns false if it fails, in which case the generated digits in the buffer
+// should not be used.
+// Preconditions:
+// * w is correct up to 1 ulp (unit in the last place). That
+// is, its error must be strictly less than a unit of its last digit.
+// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
+//
+// Postconditions: returns false if procedure fails.
+// otherwise:
+// * buffer is not null-terminated, but length contains the number of
+// digits.
+// * the representation in buffer is the most precise representation of
+// requested_digits digits.
+// * buffer contains at most requested_digits digits of w. If there are less
+// than requested_digits digits then some trailing '0's have been removed.
+// * kappa is such that
+// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2.
+//
+// Remark: This procedure takes into account the imprecision of its input
+// numbers. If the precision is not enough to guarantee all the postconditions
+// then false is returned. This usually happens rarely, but the failure-rate
+// increases with higher requested_digits.
+static bool DigitGenCounted(DiyFp w,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* kappa) {
+ DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+ DOUBLE_CONVERSION_ASSERT(kMinimalTargetExponent >= -60);
+ DOUBLE_CONVERSION_ASSERT(kMaximalTargetExponent <= -32);
+ // w is assumed to have an error less than 1 unit. Whenever w is scaled we
+ // also scale its error.
+ uint64_t w_error = 1;
+ // We cut the input number into two parts: the integral digits and the
+ // fractional digits. We don't emit any decimal separator, but adapt kappa
+ // instead. Example: instead of writing "1.2" we put "12" into the buffer and
+ // increase kappa by 1.
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+ // Division by one is a shift.
+ uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e());
+ // Modulo by one is an and.
+ uint64_t fractionals = w.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent_plus_one;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent_plus_one);
+ *kappa = divisor_exponent_plus_one;
+ *length = 0;
+
+ // Loop invariant: buffer = w / 10^kappa (integer division)
+ // The invariant holds for the first iteration: kappa has been initialized
+ // with the divisor exponent + 1. And the divisor is the biggest power of ten
+ // that is smaller than 'integrals'.
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ DOUBLE_CONVERSION_ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ requested_digits--;
+ integrals %= divisor;
+ (*kappa)--;
+ // Note that kappa now equals the exponent of the divisor and that the
+ // invariant thus holds again.
+ if (requested_digits == 0) break;
+ divisor /= 10;
+ }
+
+ if (requested_digits == 0) {
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+ return RoundWeedCounted(buffer, *length, rest,
+ static_cast<uint64_t>(divisor) << -one.e(), w_error,
+ kappa);
+ }
+
+ // The integrals have been generated. We are at the point of the decimal
+ // separator. In the following loop we simply multiply the remaining digits by
+ // 10 and divide by one. We just need to pay attention to multiply associated
+ // data (the 'unit'), too.
+ // Note that the multiplication by 10 does not overflow, because w.e >= -60
+ // and thus one.e >= -60.
+ DOUBLE_CONVERSION_ASSERT(one.e() >= -60);
+ DOUBLE_CONVERSION_ASSERT(fractionals < one.f());
+ DOUBLE_CONVERSION_ASSERT(DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+ while (requested_digits > 0 && fractionals > w_error) {
+ fractionals *= 10;
+ w_error *= 10;
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ DOUBLE_CONVERSION_ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ requested_digits--;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ }
+ if (requested_digits != 0) return false;
+ return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error,
+ kappa);
+}
+
+
+// Provides a decimal representation of v.
+// Returns true if it succeeds, otherwise the result cannot be trusted.
+// There will be *length digits inside the buffer (not null-terminated).
+// If the function returns true then
+// v == (double) (buffer * 10^decimal_exponent).
+// The digits in the buffer are the shortest representation possible: no
+// 0.09999999999999999 instead of 0.1. The shorter representation will even be
+// chosen even if the longer one would be closer to v.
+// The last digit will be closest to the actual v. That is, even if several
+// digits might correctly yield 'v' when read again, the closest will be
+// computed.
+static bool Grisu3(double v,
+ FastDtoaMode mode,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_exponent) {
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ // boundary_minus and boundary_plus are the boundaries between v and its
+ // closest floating-point neighbors. Any number strictly between
+ // boundary_minus and boundary_plus will round to v when convert to a double.
+ // Grisu3 will never output representations that lie exactly on a boundary.
+ DiyFp boundary_minus, boundary_plus;
+ if (mode == FAST_DTOA_SHORTEST) {
+ Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
+ } else {
+ DOUBLE_CONVERSION_ASSERT(mode == FAST_DTOA_SHORTEST_SINGLE);
+ float single_v = static_cast<float>(v);
+ Single(single_v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
+ }
+ DOUBLE_CONVERSION_ASSERT(boundary_plus.e() == w.e());
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+ DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+ // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+ // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+ // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+ // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+ // off by a small amount.
+ // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+ // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+ // (f-1) * 2^e < w*10^k < (f+1) * 2^e
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+ DOUBLE_CONVERSION_ASSERT(scaled_w.e() ==
+ boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize);
+ // In theory it would be possible to avoid some recomputations by computing
+ // the difference between w and boundary_minus/plus (a power of 2) and to
+ // compute scaled_boundary_minus/plus by subtracting/adding from
+ // scaled_w. However the code becomes much less readable and the speed
+ // enhancements are not terriffic.
+ DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk);
+ DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk);
+
+ // DigitGen will generate the digits of scaled_w. Therefore we have
+ // v == (double) (scaled_w * 10^-mk).
+ // Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
+ // integer than it will be updated. For instance if scaled_w == 1.23 then
+ // the buffer will be filled with "123" und the decimal_exponent will be
+ // decreased by 2.
+ int kappa;
+ bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+
+// The "counted" version of grisu3 (see above) only generates requested_digits
+// number of digits. This version does not generate the shortest representation,
+// and with enough requested digits 0.1 will at some point print as 0.9999999...
+// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and
+// therefore the rounding strategy for halfway cases is irrelevant.
+static bool Grisu3Counted(double v,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_exponent) {
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+ DOUBLE_CONVERSION_ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+ // Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
+ // 64 bit significand and ten_mk is thus only precise up to 64 bits.
+
+ // The DiyFp::Times procedure rounds its result, and ten_mk is approximated
+ // too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
+ // off by a small amount.
+ // In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
+ // In other words: let f = scaled_w.f() and e = scaled_w.e(), then
+ // (f-1) * 2^e < w*10^k < (f+1) * 2^e
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+
+ // We now have (double) (scaled_w * 10^-mk).
+ // DigitGen will generate the first requested_digits digits of scaled_w and
+ // return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
+ // will not always be exactly the same since DigitGenCounted only produces a
+ // limited number of digits.)
+ int kappa;
+ bool result = DigitGenCounted(scaled_w, requested_digits,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+
+bool FastDtoa(double v,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ DOUBLE_CONVERSION_ASSERT(v > 0);
+ DOUBLE_CONVERSION_ASSERT(!Double(v).IsSpecial());
+
+ bool result = false;
+ int decimal_exponent = 0;
+ switch (mode) {
+ case FAST_DTOA_SHORTEST:
+ case FAST_DTOA_SHORTEST_SINGLE:
+ result = Grisu3(v, mode, buffer, length, &decimal_exponent);
+ break;
+ case FAST_DTOA_PRECISION:
+ result = Grisu3Counted(v, requested_digits,
+ buffer, length, &decimal_exponent);
+ break;
+ default:
+ DOUBLE_CONVERSION_UNREACHABLE();
+ }
+ if (result) {
+ *decimal_point = *length + decimal_exponent;
+ buffer[*length] = '\0';
+ }
+ return result;
+}
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.h
new file mode 100644
index 0000000000..5f1e8eee5e
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.h
@@ -0,0 +1,88 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_FAST_DTOA_H_
+#define DOUBLE_CONVERSION_FAST_DTOA_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+enum FastDtoaMode {
+ // Computes the shortest representation of the given input. The returned
+ // result will be the most accurate number of this length. Longer
+ // representations might be more accurate.
+ FAST_DTOA_SHORTEST,
+ // Same as FAST_DTOA_SHORTEST but for single-precision floats.
+ FAST_DTOA_SHORTEST_SINGLE,
+ // Computes a representation where the precision (number of digits) is
+ // given as input. The precision is independent of the decimal point.
+ FAST_DTOA_PRECISION
+};
+
+// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
+// include the terminating '\0' character.
+static const int kFastDtoaMaximalLength = 17;
+// Same for single-precision numbers.
+static const int kFastDtoaMaximalSingleLength = 9;
+
+// Provides a decimal representation of v.
+// The result should be interpreted as buffer * 10^(point - length).
+//
+// Precondition:
+// * v must be a strictly positive finite double.
+//
+// Returns true if it succeeds, otherwise the result can not be trusted.
+// There will be *length digits inside the buffer followed by a null terminator.
+// If the function returns true and mode equals
+// - FAST_DTOA_SHORTEST, then
+// the parameter requested_digits is ignored.
+// The result satisfies
+// v == (double) (buffer * 10^(point - length)).
+// The digits in the buffer are the shortest representation possible. E.g.
+// if 0.099999999999 and 0.1 represent the same double then "1" is returned
+// with point = 0.
+// The last digit will be closest to the actual v. That is, even if several
+// digits might correctly yield 'v' when read again, the buffer will contain
+// the one closest to v.
+// - FAST_DTOA_PRECISION, then
+// the buffer contains requested_digits digits.
+// the difference v - (buffer * 10^(point-length)) is closest to zero for
+// all possible representations of requested_digits digits.
+// If there are two values that are equally close, then FastDtoa returns
+// false.
+// For both modes the buffer must be large enough to hold the result.
+bool FastDtoa(double d,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point);
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_FAST_DTOA_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.cc
new file mode 100644
index 0000000000..ab6ef10eba
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.cc
@@ -0,0 +1,405 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <cmath>
+
+#include "fixed-dtoa.h"
+#include "ieee.h"
+
+namespace double_conversion {
+
+// Represents a 128bit type. This class should be replaced by a native type on
+// platforms that support 128bit integers.
+class UInt128 {
+ public:
+ UInt128() : high_bits_(0), low_bits_(0) { }
+ UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { }
+
+ void Multiply(uint32_t multiplicand) {
+ uint64_t accumulator;
+
+ accumulator = (low_bits_ & kMask32) * multiplicand;
+ uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
+ low_bits_ = (accumulator << 32) + part;
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
+ part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
+ high_bits_ = (accumulator << 32) + part;
+ DOUBLE_CONVERSION_ASSERT((accumulator >> 32) == 0);
+ }
+
+ void Shift(int shift_amount) {
+ DOUBLE_CONVERSION_ASSERT(-64 <= shift_amount && shift_amount <= 64);
+ if (shift_amount == 0) {
+ return;
+ } else if (shift_amount == -64) {
+ high_bits_ = low_bits_;
+ low_bits_ = 0;
+ } else if (shift_amount == 64) {
+ low_bits_ = high_bits_;
+ high_bits_ = 0;
+ } else if (shift_amount <= 0) {
+ high_bits_ <<= -shift_amount;
+ high_bits_ += low_bits_ >> (64 + shift_amount);
+ low_bits_ <<= -shift_amount;
+ } else {
+ low_bits_ >>= shift_amount;
+ low_bits_ += high_bits_ << (64 - shift_amount);
+ high_bits_ >>= shift_amount;
+ }
+ }
+
+ // Modifies *this to *this MOD (2^power).
+ // Returns *this DIV (2^power).
+ int DivModPowerOf2(int power) {
+ if (power >= 64) {
+ int result = static_cast<int>(high_bits_ >> (power - 64));
+ high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
+ return result;
+ } else {
+ uint64_t part_low = low_bits_ >> power;
+ uint64_t part_high = high_bits_ << (64 - power);
+ int result = static_cast<int>(part_low + part_high);
+ high_bits_ = 0;
+ low_bits_ -= part_low << power;
+ return result;
+ }
+ }
+
+ bool IsZero() const {
+ return high_bits_ == 0 && low_bits_ == 0;
+ }
+
+ int BitAt(int position) const {
+ if (position >= 64) {
+ return static_cast<int>(high_bits_ >> (position - 64)) & 1;
+ } else {
+ return static_cast<int>(low_bits_ >> position) & 1;
+ }
+ }
+
+ private:
+ static const uint64_t kMask32 = 0xFFFFFFFF;
+ // Value == (high_bits_ << 64) + low_bits_
+ uint64_t high_bits_;
+ uint64_t low_bits_;
+};
+
+
+static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
+
+
+static void FillDigits32FixedLength(uint32_t number, int requested_length,
+ Vector<char> buffer, int* length) {
+ for (int i = requested_length - 1; i >= 0; --i) {
+ buffer[(*length) + i] = '0' + number % 10;
+ number /= 10;
+ }
+ *length += requested_length;
+}
+
+
+static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
+ int number_length = 0;
+ // We fill the digits in reverse order and exchange them afterwards.
+ while (number != 0) {
+ int digit = number % 10;
+ number /= 10;
+ buffer[(*length) + number_length] = static_cast<char>('0' + digit);
+ number_length++;
+ }
+ // Exchange the digits.
+ int i = *length;
+ int j = *length + number_length - 1;
+ while (i < j) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ i++;
+ j--;
+ }
+ *length += number_length;
+}
+
+
+static void FillDigits64FixedLength(uint64_t number,
+ Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ FillDigits32FixedLength(part0, 3, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+}
+
+
+static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ if (part0 != 0) {
+ FillDigits32(part0, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else if (part1 != 0) {
+ FillDigits32(part1, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else {
+ FillDigits32(part2, buffer, length);
+ }
+}
+
+
+static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
+ // An empty buffer represents 0.
+ if (*length == 0) {
+ buffer[0] = '1';
+ *decimal_point = 1;
+ *length = 1;
+ return;
+ }
+ // Round the last digit until we either have a digit that was not '9' or until
+ // we reached the first digit.
+ buffer[(*length) - 1]++;
+ for (int i = (*length) - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) {
+ return;
+ }
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ // If the first digit is now '0' + 10, we would need to set it to '0' and add
+ // a '1' in front. However we reach the first digit only if all following
+ // digits had been '9' before rounding up. Now all trailing digits are '0' and
+ // we simply switch the first digit to '1' and update the decimal-point
+ // (indicating that the point is now one digit to the right).
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+}
+
+
+// The given fractionals number represents a fixed-point number with binary
+// point at bit (-exponent).
+// Preconditions:
+// -128 <= exponent <= 0.
+// 0 <= fractionals * 2^exponent < 1
+// The buffer holds the result.
+// The function will round its result. During the rounding-process digits not
+// generated by this function might be updated, and the decimal-point variable
+// might be updated. If this function generates the digits 99 and the buffer
+// already contained "199" (thus yielding a buffer of "19999") then a
+// rounding-up will change the contents of the buffer to "20000".
+static void FillFractionals(uint64_t fractionals, int exponent,
+ int fractional_count, Vector<char> buffer,
+ int* length, int* decimal_point) {
+ DOUBLE_CONVERSION_ASSERT(-128 <= exponent && exponent <= 0);
+ // 'fractionals' is a fixed-point number, with binary point at bit
+ // (-exponent). Inside the function the non-converted remainder of fractionals
+ // is a fixed-point number, with binary point at bit 'point'.
+ if (-exponent <= 64) {
+ // One 64 bit number is sufficient.
+ DOUBLE_CONVERSION_ASSERT(fractionals >> 56 == 0);
+ int point = -exponent;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals == 0) break;
+ // Instead of multiplying by 10 we multiply by 5 and adjust the point
+ // location. This way the fractionals variable will not overflow.
+ // Invariant at the beginning of the loop: fractionals < 2^point.
+ // Initially we have: point <= 64 and fractionals < 2^56
+ // After each iteration the point is decremented by one.
+ // Note that 5^3 = 125 < 128 = 2^7.
+ // Therefore three iterations of this loop will not overflow fractionals
+ // (even without the subtraction at the end of the loop body). At this
+ // time point will satisfy point <= 61 and therefore fractionals < 2^point
+ // and any further multiplication of fractionals by 5 will not overflow.
+ fractionals *= 5;
+ point--;
+ int digit = static_cast<int>(fractionals >> point);
+ DOUBLE_CONVERSION_ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ fractionals -= static_cast<uint64_t>(digit) << point;
+ }
+ // If the first bit after the point is set we have to round up.
+ DOUBLE_CONVERSION_ASSERT(fractionals == 0 || point - 1 >= 0);
+ if ((fractionals != 0) && ((fractionals >> (point - 1)) & 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ } else { // We need 128 bits.
+ DOUBLE_CONVERSION_ASSERT(64 < -exponent && -exponent <= 128);
+ UInt128 fractionals128 = UInt128(fractionals, 0);
+ fractionals128.Shift(-exponent - 64);
+ int point = 128;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals128.IsZero()) break;
+ // As before: instead of multiplying by 10 we multiply by 5 and adjust the
+ // point location.
+ // This multiplication will not overflow for the same reasons as before.
+ fractionals128.Multiply(5);
+ point--;
+ int digit = fractionals128.DivModPowerOf2(point);
+ DOUBLE_CONVERSION_ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ }
+ if (fractionals128.BitAt(point - 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ }
+}
+
+
+// Removes leading and trailing zeros.
+// If leading zeros are removed then the decimal point position is adjusted.
+static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
+ while (*length > 0 && buffer[(*length) - 1] == '0') {
+ (*length)--;
+ }
+ int first_non_zero = 0;
+ while (first_non_zero < *length && buffer[first_non_zero] == '0') {
+ first_non_zero++;
+ }
+ if (first_non_zero != 0) {
+ for (int i = first_non_zero; i < *length; ++i) {
+ buffer[i - first_non_zero] = buffer[i];
+ }
+ *length -= first_non_zero;
+ *decimal_point -= first_non_zero;
+ }
+}
+
+
+bool FastFixedDtoa(double v,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ const uint32_t kMaxUInt32 = 0xFFFFFFFF;
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+ // v = significand * 2^exponent (with significand a 53bit integer).
+ // If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
+ // don't know how to compute the representation. 2^73 ~= 9.5*10^21.
+ // If necessary this limit could probably be increased, but we don't need
+ // more.
+ if (exponent > 20) return false;
+ if (fractional_count > 20) return false;
+ *length = 0;
+ // At most kDoubleSignificandSize bits of the significand are non-zero.
+ // Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
+ // bits: 0..11*..0xxx..53*..xx
+ if (exponent + kDoubleSignificandSize > 64) {
+ // The exponent must be > 11.
+ //
+ // We know that v = significand * 2^exponent.
+ // And the exponent > 11.
+ // We simplify the task by dividing v by 10^17.
+ // The quotient delivers the first digits, and the remainder fits into a 64
+ // bit number.
+ // Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
+ const uint64_t kFive17 = DOUBLE_CONVERSION_UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17
+ uint64_t divisor = kFive17;
+ int divisor_power = 17;
+ uint64_t dividend = significand;
+ uint32_t quotient;
+ uint64_t remainder;
+ // Let v = f * 2^e with f == significand and e == exponent.
+ // Then need q (quotient) and r (remainder) as follows:
+ // v = q * 10^17 + r
+ // f * 2^e = q * 10^17 + r
+ // f * 2^e = q * 5^17 * 2^17 + r
+ // If e > 17 then
+ // f * 2^(e-17) = q * 5^17 + r/2^17
+ // else
+ // f = q * 5^17 * 2^(17-e) + r/2^e
+ if (exponent > divisor_power) {
+ // We only allow exponents of up to 20 and therefore (17 - e) <= 3
+ dividend <<= exponent - divisor_power;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << divisor_power;
+ } else {
+ divisor <<= divisor_power - exponent;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << exponent;
+ }
+ FillDigits32(quotient, buffer, length);
+ FillDigits64FixedLength(remainder, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent >= 0) {
+ // 0 <= exponent <= 11
+ significand <<= exponent;
+ FillDigits64(significand, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent > -kDoubleSignificandSize) {
+ // We have to cut the number.
+ uint64_t integrals = significand >> -exponent;
+ uint64_t fractionals = significand - (integrals << -exponent);
+ if (integrals > kMaxUInt32) {
+ FillDigits64(integrals, buffer, length);
+ } else {
+ FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
+ }
+ *decimal_point = *length;
+ FillFractionals(fractionals, exponent, fractional_count,
+ buffer, length, decimal_point);
+ } else if (exponent < -128) {
+ // This configuration (with at most 20 digits) means that all digits must be
+ // 0.
+ DOUBLE_CONVERSION_ASSERT(fractional_count <= 20);
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -fractional_count;
+ } else {
+ *decimal_point = 0;
+ FillFractionals(significand, exponent, fractional_count,
+ buffer, length, decimal_point);
+ }
+ TrimZeros(buffer, length, decimal_point);
+ buffer[*length] = '\0';
+ if ((*length) == 0) {
+ // The string is empty and the decimal_point thus has no importance. Mimick
+ // Gay's dtoa and and set it to -fractional_count.
+ *decimal_point = -fractional_count;
+ }
+ return true;
+}
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.h
new file mode 100644
index 0000000000..3bdd08e21f
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.h
@@ -0,0 +1,56 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_FIXED_DTOA_H_
+#define DOUBLE_CONVERSION_FIXED_DTOA_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+// Produces digits necessary to print a given number with
+// 'fractional_count' digits after the decimal point.
+// The buffer must be big enough to hold the result plus one terminating null
+// character.
+//
+// The produced digits might be too short in which case the caller has to fill
+// the gaps with '0's.
+// Example: FastFixedDtoa(0.001, 5, ...) is allowed to return buffer = "1", and
+// decimal_point = -2.
+// Halfway cases are rounded towards +/-Infinity (away from 0). The call
+// FastFixedDtoa(0.15, 2, ...) thus returns buffer = "2", decimal_point = 0.
+// The returned buffer may contain digits that would be truncated from the
+// shortest representation of the input.
+//
+// This method only works for some parameters. If it can't handle the input it
+// returns false. The output is null-terminated when the function succeeds.
+bool FastFixedDtoa(double v, int fractional_count,
+ Vector<char> buffer, int* length, int* decimal_point);
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_FIXED_DTOA_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/ieee.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/ieee.h
new file mode 100644
index 0000000000..8c3b862e8c
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/ieee.h
@@ -0,0 +1,402 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_DOUBLE_H_
+#define DOUBLE_CONVERSION_DOUBLE_H_
+
+#include "diy-fp.h"
+
+namespace double_conversion {
+
+// We assume that doubles and uint64_t have the same endianness.
+static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); }
+static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); }
+static uint32_t float_to_uint32(float f) { return BitCast<uint32_t>(f); }
+static float uint32_to_float(uint32_t d32) { return BitCast<float>(d32); }
+
+// Helper functions for doubles.
+class Double {
+ public:
+ static const uint64_t kSignMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x80000000, 00000000);
+ static const uint64_t kExponentMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kSignificandMask = DOUBLE_CONVERSION_UINT64_2PART_C(0x000FFFFF, FFFFFFFF);
+ static const uint64_t kHiddenBit = DOUBLE_CONVERSION_UINT64_2PART_C(0x00100000, 00000000);
+ static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
+ static const int kSignificandSize = 53;
+ static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+ static const int kMaxExponent = 0x7FF - kExponentBias;
+
+ Double() : d64_(0) {}
+ explicit Double(double d) : d64_(double_to_uint64(d)) {}
+ explicit Double(uint64_t d64) : d64_(d64) {}
+ explicit Double(DiyFp diy_fp)
+ : d64_(DiyFpToUint64(diy_fp)) {}
+
+ // The value encoded by this Double must be greater or equal to +0.0.
+ // It must not be special (infinity, or NaN).
+ DiyFp AsDiyFp() const {
+ DOUBLE_CONVERSION_ASSERT(Sign() > 0);
+ DOUBLE_CONVERSION_ASSERT(!IsSpecial());
+ return DiyFp(Significand(), Exponent());
+ }
+
+ // The value encoded by this Double must be strictly greater than 0.
+ DiyFp AsNormalizedDiyFp() const {
+ DOUBLE_CONVERSION_ASSERT(value() > 0.0);
+ uint64_t f = Significand();
+ int e = Exponent();
+
+ // The current double could be a denormal.
+ while ((f & kHiddenBit) == 0) {
+ f <<= 1;
+ e--;
+ }
+ // Do the final shifts in one go.
+ f <<= DiyFp::kSignificandSize - kSignificandSize;
+ e -= DiyFp::kSignificandSize - kSignificandSize;
+ return DiyFp(f, e);
+ }
+
+ // Returns the double's bit as uint64.
+ uint64_t AsUint64() const {
+ return d64_;
+ }
+
+ // Returns the next greater double. Returns +infinity on input +infinity.
+ double NextDouble() const {
+ if (d64_ == kInfinity) return Double(kInfinity).value();
+ if (Sign() < 0 && Significand() == 0) {
+ // -0.0
+ return 0.0;
+ }
+ if (Sign() < 0) {
+ return Double(d64_ - 1).value();
+ } else {
+ return Double(d64_ + 1).value();
+ }
+ }
+
+ double PreviousDouble() const {
+ if (d64_ == (kInfinity | kSignMask)) return -Infinity();
+ if (Sign() < 0) {
+ return Double(d64_ + 1).value();
+ } else {
+ if (Significand() == 0) return -0.0;
+ return Double(d64_ - 1).value();
+ }
+ }
+
+ int Exponent() const {
+ if (IsDenormal()) return kDenormalExponent;
+
+ uint64_t d64 = AsUint64();
+ int biased_e =
+ static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
+ return biased_e - kExponentBias;
+ }
+
+ uint64_t Significand() const {
+ uint64_t d64 = AsUint64();
+ uint64_t significand = d64 & kSignificandMask;
+ if (!IsDenormal()) {
+ return significand + kHiddenBit;
+ } else {
+ return significand;
+ }
+ }
+
+ // Returns true if the double is a denormal.
+ bool IsDenormal() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == 0;
+ }
+
+ // We consider denormals not to be special.
+ // Hence only Infinity and NaN are special.
+ bool IsSpecial() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == kExponentMask;
+ }
+
+ bool IsNan() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) != 0);
+ }
+
+ bool IsInfinite() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) == 0);
+ }
+
+ int Sign() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kSignMask) == 0? 1: -1;
+ }
+
+ // Precondition: the value encoded by this Double must be greater or equal
+ // than +0.0.
+ DiyFp UpperBoundary() const {
+ DOUBLE_CONVERSION_ASSERT(Sign() > 0);
+ return DiyFp(Significand() * 2 + 1, Exponent() - 1);
+ }
+
+ // Computes the two boundaries of this.
+ // The bigger boundary (m_plus) is normalized. The lower boundary has the same
+ // exponent as m_plus.
+ // Precondition: the value encoded by this Double must be greater than 0.
+ void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
+ DOUBLE_CONVERSION_ASSERT(value() > 0.0);
+ DiyFp v = this->AsDiyFp();
+ DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
+ DiyFp m_minus;
+ if (LowerBoundaryIsCloser()) {
+ m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
+ } else {
+ m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
+ }
+ m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
+ m_minus.set_e(m_plus.e());
+ *out_m_plus = m_plus;
+ *out_m_minus = m_minus;
+ }
+
+ bool LowerBoundaryIsCloser() const {
+ // The boundary is closer if the significand is of the form f == 2^p-1 then
+ // the lower boundary is closer.
+ // Think of v = 1000e10 and v- = 9999e9.
+ // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
+ // at a distance of 1e8.
+ // The only exception is for the smallest normal: the largest denormal is
+ // at the same distance as its successor.
+ // Note: denormals have the same exponent as the smallest normals.
+ bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0);
+ return physical_significand_is_zero && (Exponent() != kDenormalExponent);
+ }
+
+ double value() const { return uint64_to_double(d64_); }
+
+ // Returns the significand size for a given order of magnitude.
+ // If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
+ // This function returns the number of significant binary digits v will have
+ // once it's encoded into a double. In almost all cases this is equal to
+ // kSignificandSize. The only exceptions are denormals. They start with
+ // leading zeroes and their effective significand-size is hence smaller.
+ static int SignificandSizeForOrderOfMagnitude(int order) {
+ if (order >= (kDenormalExponent + kSignificandSize)) {
+ return kSignificandSize;
+ }
+ if (order <= kDenormalExponent) return 0;
+ return order - kDenormalExponent;
+ }
+
+ static double Infinity() {
+ return Double(kInfinity).value();
+ }
+
+ static double NaN() {
+ return Double(kNaN).value();
+ }
+
+ private:
+ static const int kDenormalExponent = -kExponentBias + 1;
+ static const uint64_t kInfinity = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kNaN = DOUBLE_CONVERSION_UINT64_2PART_C(0x7FF80000, 00000000);
+
+ const uint64_t d64_;
+
+ static uint64_t DiyFpToUint64(DiyFp diy_fp) {
+ uint64_t significand = diy_fp.f();
+ int exponent = diy_fp.e();
+ while (significand > kHiddenBit + kSignificandMask) {
+ significand >>= 1;
+ exponent++;
+ }
+ if (exponent >= kMaxExponent) {
+ return kInfinity;
+ }
+ if (exponent < kDenormalExponent) {
+ return 0;
+ }
+ while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
+ significand <<= 1;
+ exponent--;
+ }
+ uint64_t biased_exponent;
+ if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
+ biased_exponent = 0;
+ } else {
+ biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
+ }
+ return (significand & kSignificandMask) |
+ (biased_exponent << kPhysicalSignificandSize);
+ }
+
+ DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Double);
+};
+
+class Single {
+ public:
+ static const uint32_t kSignMask = 0x80000000;
+ static const uint32_t kExponentMask = 0x7F800000;
+ static const uint32_t kSignificandMask = 0x007FFFFF;
+ static const uint32_t kHiddenBit = 0x00800000;
+ static const int kPhysicalSignificandSize = 23; // Excludes the hidden bit.
+ static const int kSignificandSize = 24;
+
+ Single() : d32_(0) {}
+ explicit Single(float f) : d32_(float_to_uint32(f)) {}
+ explicit Single(uint32_t d32) : d32_(d32) {}
+
+ // The value encoded by this Single must be greater or equal to +0.0.
+ // It must not be special (infinity, or NaN).
+ DiyFp AsDiyFp() const {
+ DOUBLE_CONVERSION_ASSERT(Sign() > 0);
+ DOUBLE_CONVERSION_ASSERT(!IsSpecial());
+ return DiyFp(Significand(), Exponent());
+ }
+
+ // Returns the single's bit as uint64.
+ uint32_t AsUint32() const {
+ return d32_;
+ }
+
+ int Exponent() const {
+ if (IsDenormal()) return kDenormalExponent;
+
+ uint32_t d32 = AsUint32();
+ int biased_e =
+ static_cast<int>((d32 & kExponentMask) >> kPhysicalSignificandSize);
+ return biased_e - kExponentBias;
+ }
+
+ uint32_t Significand() const {
+ uint32_t d32 = AsUint32();
+ uint32_t significand = d32 & kSignificandMask;
+ if (!IsDenormal()) {
+ return significand + kHiddenBit;
+ } else {
+ return significand;
+ }
+ }
+
+ // Returns true if the single is a denormal.
+ bool IsDenormal() const {
+ uint32_t d32 = AsUint32();
+ return (d32 & kExponentMask) == 0;
+ }
+
+ // We consider denormals not to be special.
+ // Hence only Infinity and NaN are special.
+ bool IsSpecial() const {
+ uint32_t d32 = AsUint32();
+ return (d32 & kExponentMask) == kExponentMask;
+ }
+
+ bool IsNan() const {
+ uint32_t d32 = AsUint32();
+ return ((d32 & kExponentMask) == kExponentMask) &&
+ ((d32 & kSignificandMask) != 0);
+ }
+
+ bool IsInfinite() const {
+ uint32_t d32 = AsUint32();
+ return ((d32 & kExponentMask) == kExponentMask) &&
+ ((d32 & kSignificandMask) == 0);
+ }
+
+ int Sign() const {
+ uint32_t d32 = AsUint32();
+ return (d32 & kSignMask) == 0? 1: -1;
+ }
+
+ // Computes the two boundaries of this.
+ // The bigger boundary (m_plus) is normalized. The lower boundary has the same
+ // exponent as m_plus.
+ // Precondition: the value encoded by this Single must be greater than 0.
+ void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
+ DOUBLE_CONVERSION_ASSERT(value() > 0.0);
+ DiyFp v = this->AsDiyFp();
+ DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
+ DiyFp m_minus;
+ if (LowerBoundaryIsCloser()) {
+ m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
+ } else {
+ m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
+ }
+ m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
+ m_minus.set_e(m_plus.e());
+ *out_m_plus = m_plus;
+ *out_m_minus = m_minus;
+ }
+
+ // Precondition: the value encoded by this Single must be greater or equal
+ // than +0.0.
+ DiyFp UpperBoundary() const {
+ DOUBLE_CONVERSION_ASSERT(Sign() > 0);
+ return DiyFp(Significand() * 2 + 1, Exponent() - 1);
+ }
+
+ bool LowerBoundaryIsCloser() const {
+ // The boundary is closer if the significand is of the form f == 2^p-1 then
+ // the lower boundary is closer.
+ // Think of v = 1000e10 and v- = 9999e9.
+ // Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
+ // at a distance of 1e8.
+ // The only exception is for the smallest normal: the largest denormal is
+ // at the same distance as its successor.
+ // Note: denormals have the same exponent as the smallest normals.
+ bool physical_significand_is_zero = ((AsUint32() & kSignificandMask) == 0);
+ return physical_significand_is_zero && (Exponent() != kDenormalExponent);
+ }
+
+ float value() const { return uint32_to_float(d32_); }
+
+ static float Infinity() {
+ return Single(kInfinity).value();
+ }
+
+ static float NaN() {
+ return Single(kNaN).value();
+ }
+
+ private:
+ static const int kExponentBias = 0x7F + kPhysicalSignificandSize;
+ static const int kDenormalExponent = -kExponentBias + 1;
+ static const int kMaxExponent = 0xFF - kExponentBias;
+ static const uint32_t kInfinity = 0x7F800000;
+ static const uint32_t kNaN = 0x7FC00000;
+
+ const uint32_t d32_;
+
+ DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(Single);
+};
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_DOUBLE_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.cc
new file mode 100644
index 0000000000..12b88f9b80
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.cc
@@ -0,0 +1,764 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <climits>
+#include <locale>
+#include <cmath>
+
+#include "string-to-double.h"
+
+#include "ieee.h"
+#include "strtod.h"
+#include "utils.h"
+
+namespace double_conversion {
+
+namespace {
+
+inline char ToLower(char ch) {
+ static const std::ctype<char>& cType =
+ std::use_facet<std::ctype<char> >(std::locale::classic());
+ return cType.tolower(ch);
+}
+
+inline char Pass(char ch) {
+ return ch;
+}
+
+template <class Iterator, class Converter>
+static inline bool ConsumeSubStringImpl(Iterator* current,
+ Iterator end,
+ const char* substring,
+ Converter converter) {
+ DOUBLE_CONVERSION_ASSERT(converter(**current) == *substring);
+ for (substring++; *substring != '\0'; substring++) {
+ ++*current;
+ if (*current == end || converter(**current) != *substring) {
+ return false;
+ }
+ }
+ ++*current;
+ return true;
+}
+
+// Consumes the given substring from the iterator.
+// Returns false, if the substring does not match.
+template <class Iterator>
+static bool ConsumeSubString(Iterator* current,
+ Iterator end,
+ const char* substring,
+ bool allow_case_insensitivity) {
+ if (allow_case_insensitivity) {
+ return ConsumeSubStringImpl(current, end, substring, ToLower);
+ } else {
+ return ConsumeSubStringImpl(current, end, substring, Pass);
+ }
+}
+
+// Consumes first character of the str is equal to ch
+inline bool ConsumeFirstCharacter(char ch,
+ const char* str,
+ bool case_insensitivity) {
+ return case_insensitivity ? ToLower(ch) == str[0] : ch == str[0];
+}
+} // namespace
+
+// Maximum number of significant digits in decimal representation.
+// The longest possible double in decimal representation is
+// (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074
+// (768 digits). If we parse a number whose first digits are equal to a
+// mean of 2 adjacent doubles (that could have up to 769 digits) the result
+// must be rounded to the bigger one unless the tail consists of zeros, so
+// we don't need to preserve all the digits.
+const int kMaxSignificantDigits = 772;
+
+
+static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 };
+static const int kWhitespaceTable7Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable7);
+
+
+static const uc16 kWhitespaceTable16[] = {
+ 160, 8232, 8233, 5760, 6158, 8192, 8193, 8194, 8195,
+ 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8239, 8287, 12288, 65279
+};
+static const int kWhitespaceTable16Length = DOUBLE_CONVERSION_ARRAY_SIZE(kWhitespaceTable16);
+
+
+static bool isWhitespace(int x) {
+ if (x < 128) {
+ for (int i = 0; i < kWhitespaceTable7Length; i++) {
+ if (kWhitespaceTable7[i] == x) return true;
+ }
+ } else {
+ for (int i = 0; i < kWhitespaceTable16Length; i++) {
+ if (kWhitespaceTable16[i] == x) return true;
+ }
+ }
+ return false;
+}
+
+
+// Returns true if a nonspace found and false if the end has reached.
+template <class Iterator>
+static inline bool AdvanceToNonspace(Iterator* current, Iterator end) {
+ while (*current != end) {
+ if (!isWhitespace(**current)) return true;
+ ++*current;
+ }
+ return false;
+}
+
+
+static bool isDigit(int x, int radix) {
+ return (x >= '0' && x <= '9' && x < '0' + radix)
+ || (radix > 10 && x >= 'a' && x < 'a' + radix - 10)
+ || (radix > 10 && x >= 'A' && x < 'A' + radix - 10);
+}
+
+
+static double SignedZero(bool sign) {
+ return sign ? -0.0 : 0.0;
+}
+
+
+// Returns true if 'c' is a decimal digit that is valid for the given radix.
+//
+// The function is small and could be inlined, but VS2012 emitted a warning
+// because it constant-propagated the radix and concluded that the last
+// condition was always true. By moving it into a separate function the
+// compiler wouldn't warn anymore.
+#ifdef _MSC_VER
+#pragma optimize("",off)
+static bool IsDecimalDigitForRadix(int c, int radix) {
+ return '0' <= c && c <= '9' && (c - '0') < radix;
+}
+#pragma optimize("",on)
+#else
+static bool inline IsDecimalDigitForRadix(int c, int radix) {
+ return '0' <= c && c <= '9' && (c - '0') < radix;
+}
+#endif
+// Returns true if 'c' is a character digit that is valid for the given radix.
+// The 'a_character' should be 'a' or 'A'.
+//
+// The function is small and could be inlined, but VS2012 emitted a warning
+// because it constant-propagated the radix and concluded that the first
+// condition was always false. By moving it into a separate function the
+// compiler wouldn't warn anymore.
+static bool IsCharacterDigitForRadix(int c, int radix, char a_character) {
+ return radix > 10 && c >= a_character && c < a_character + radix - 10;
+}
+
+// Returns true, when the iterator is equal to end.
+template<class Iterator>
+static bool Advance (Iterator* it, uc16 separator, int base, Iterator& end) {
+ if (separator == StringToDoubleConverter::kNoSeparator) {
+ ++(*it);
+ return *it == end;
+ }
+ if (!isDigit(**it, base)) {
+ ++(*it);
+ return *it == end;
+ }
+ ++(*it);
+ if (*it == end) return true;
+ if (*it + 1 == end) return false;
+ if (**it == separator && isDigit(*(*it + 1), base)) {
+ ++(*it);
+ }
+ return *it == end;
+}
+
+// Checks whether the string in the range start-end is a hex-float string.
+// This function assumes that the leading '0x'/'0X' is already consumed.
+//
+// Hex float strings are of one of the following forms:
+// - hex_digits+ 'p' ('+'|'-')? exponent_digits+
+// - hex_digits* '.' hex_digits+ 'p' ('+'|'-')? exponent_digits+
+// - hex_digits+ '.' 'p' ('+'|'-')? exponent_digits+
+template<class Iterator>
+static bool IsHexFloatString(Iterator start,
+ Iterator end,
+ uc16 separator,
+ bool allow_trailing_junk) {
+ DOUBLE_CONVERSION_ASSERT(start != end);
+
+ Iterator current = start;
+
+ bool saw_digit = false;
+ while (isDigit(*current, 16)) {
+ saw_digit = true;
+ if (Advance(&current, separator, 16, end)) return false;
+ }
+ if (*current == '.') {
+ if (Advance(&current, separator, 16, end)) return false;
+ while (isDigit(*current, 16)) {
+ saw_digit = true;
+ if (Advance(&current, separator, 16, end)) return false;
+ }
+ }
+ if (!saw_digit) return false;
+ if (*current != 'p' && *current != 'P') return false;
+ if (Advance(&current, separator, 16, end)) return false;
+ if (*current == '+' || *current == '-') {
+ if (Advance(&current, separator, 16, end)) return false;
+ }
+ if (!isDigit(*current, 10)) return false;
+ if (Advance(&current, separator, 16, end)) return true;
+ while (isDigit(*current, 10)) {
+ if (Advance(&current, separator, 16, end)) return true;
+ }
+ return allow_trailing_junk || !AdvanceToNonspace(&current, end);
+}
+
+
+// Parsing integers with radix 2, 4, 8, 16, 32. Assumes current != end.
+//
+// If parse_as_hex_float is true, then the string must be a valid
+// hex-float.
+template <int radix_log_2, class Iterator>
+static double RadixStringToIeee(Iterator* current,
+ Iterator end,
+ bool sign,
+ uc16 separator,
+ bool parse_as_hex_float,
+ bool allow_trailing_junk,
+ double junk_string_value,
+ bool read_as_double,
+ bool* result_is_junk) {
+ DOUBLE_CONVERSION_ASSERT(*current != end);
+ DOUBLE_CONVERSION_ASSERT(!parse_as_hex_float ||
+ IsHexFloatString(*current, end, separator, allow_trailing_junk));
+
+ const int kDoubleSize = Double::kSignificandSize;
+ const int kSingleSize = Single::kSignificandSize;
+ const int kSignificandSize = read_as_double? kDoubleSize: kSingleSize;
+
+ *result_is_junk = true;
+
+ int64_t number = 0;
+ int exponent = 0;
+ const int radix = (1 << radix_log_2);
+ // Whether we have encountered a '.' and are parsing the decimal digits.
+ // Only relevant if parse_as_hex_float is true.
+ bool post_decimal = false;
+
+ // Skip leading 0s.
+ while (**current == '0') {
+ if (Advance(current, separator, radix, end)) {
+ *result_is_junk = false;
+ return SignedZero(sign);
+ }
+ }
+
+ while (true) {
+ int digit;
+ if (IsDecimalDigitForRadix(**current, radix)) {
+ digit = static_cast<char>(**current) - '0';
+ if (post_decimal) exponent -= radix_log_2;
+ } else if (IsCharacterDigitForRadix(**current, radix, 'a')) {
+ digit = static_cast<char>(**current) - 'a' + 10;
+ if (post_decimal) exponent -= radix_log_2;
+ } else if (IsCharacterDigitForRadix(**current, radix, 'A')) {
+ digit = static_cast<char>(**current) - 'A' + 10;
+ if (post_decimal) exponent -= radix_log_2;
+ } else if (parse_as_hex_float && **current == '.') {
+ post_decimal = true;
+ Advance(current, separator, radix, end);
+ DOUBLE_CONVERSION_ASSERT(*current != end);
+ continue;
+ } else if (parse_as_hex_float && (**current == 'p' || **current == 'P')) {
+ break;
+ } else {
+ if (allow_trailing_junk || !AdvanceToNonspace(current, end)) {
+ break;
+ } else {
+ return junk_string_value;
+ }
+ }
+
+ number = number * radix + digit;
+ int overflow = static_cast<int>(number >> kSignificandSize);
+ if (overflow != 0) {
+ // Overflow occurred. Need to determine which direction to round the
+ // result.
+ int overflow_bits_count = 1;
+ while (overflow > 1) {
+ overflow_bits_count++;
+ overflow >>= 1;
+ }
+
+ int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
+ int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
+ number >>= overflow_bits_count;
+ exponent += overflow_bits_count;
+
+ bool zero_tail = true;
+ for (;;) {
+ if (Advance(current, separator, radix, end)) break;
+ if (parse_as_hex_float && **current == '.') {
+ // Just run over the '.'. We are just trying to see whether there is
+ // a non-zero digit somewhere.
+ Advance(current, separator, radix, end);
+ DOUBLE_CONVERSION_ASSERT(*current != end);
+ post_decimal = true;
+ }
+ if (!isDigit(**current, radix)) break;
+ zero_tail = zero_tail && **current == '0';
+ if (!post_decimal) exponent += radix_log_2;
+ }
+
+ if (!parse_as_hex_float &&
+ !allow_trailing_junk &&
+ AdvanceToNonspace(current, end)) {
+ return junk_string_value;
+ }
+
+ int middle_value = (1 << (overflow_bits_count - 1));
+ if (dropped_bits > middle_value) {
+ number++; // Rounding up.
+ } else if (dropped_bits == middle_value) {
+ // Rounding to even to consistency with decimals: half-way case rounds
+ // up if significant part is odd and down otherwise.
+ if ((number & 1) != 0 || !zero_tail) {
+ number++; // Rounding up.
+ }
+ }
+
+ // Rounding up may cause overflow.
+ if ((number & ((int64_t)1 << kSignificandSize)) != 0) {
+ exponent++;
+ number >>= 1;
+ }
+ break;
+ }
+ if (Advance(current, separator, radix, end)) break;
+ }
+
+ DOUBLE_CONVERSION_ASSERT(number < ((int64_t)1 << kSignificandSize));
+ DOUBLE_CONVERSION_ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number);
+
+ *result_is_junk = false;
+
+ if (parse_as_hex_float) {
+ DOUBLE_CONVERSION_ASSERT(**current == 'p' || **current == 'P');
+ Advance(current, separator, radix, end);
+ DOUBLE_CONVERSION_ASSERT(*current != end);
+ bool is_negative = false;
+ if (**current == '+') {
+ Advance(current, separator, radix, end);
+ DOUBLE_CONVERSION_ASSERT(*current != end);
+ } else if (**current == '-') {
+ is_negative = true;
+ Advance(current, separator, radix, end);
+ DOUBLE_CONVERSION_ASSERT(*current != end);
+ }
+ int written_exponent = 0;
+ while (IsDecimalDigitForRadix(**current, 10)) {
+ // No need to read exponents if they are too big. That could potentially overflow
+ // the `written_exponent` variable.
+ if (abs(written_exponent) <= 100 * Double::kMaxExponent) {
+ written_exponent = 10 * written_exponent + **current - '0';
+ }
+ if (Advance(current, separator, radix, end)) break;
+ }
+ if (is_negative) written_exponent = -written_exponent;
+ exponent += written_exponent;
+ }
+
+ if (exponent == 0 || number == 0) {
+ if (sign) {
+ if (number == 0) return -0.0;
+ number = -number;
+ }
+ return static_cast<double>(number);
+ }
+
+ DOUBLE_CONVERSION_ASSERT(number != 0);
+ double result = Double(DiyFp(number, exponent)).value();
+ return sign ? -result : result;
+}
+
+template <class Iterator>
+double StringToDoubleConverter::StringToIeee(
+ Iterator input,
+ int length,
+ bool read_as_double,
+ int* processed_characters_count) const {
+ Iterator current = input;
+ Iterator end = input + length;
+
+ *processed_characters_count = 0;
+
+ const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0;
+ const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0;
+ const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0;
+ const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0;
+ const bool allow_case_insensitivity = (flags_ & ALLOW_CASE_INSENSITIVITY) != 0;
+
+ // To make sure that iterator dereferencing is valid the following
+ // convention is used:
+ // 1. Each '++current' statement is followed by check for equality to 'end'.
+ // 2. If AdvanceToNonspace returned false then current == end.
+ // 3. If 'current' becomes equal to 'end' the function returns or goes to
+ // 'parsing_done'.
+ // 4. 'current' is not dereferenced after the 'parsing_done' label.
+ // 5. Code before 'parsing_done' may rely on 'current != end'.
+ if (current == end) return empty_string_value_;
+
+ if (allow_leading_spaces || allow_trailing_spaces) {
+ if (!AdvanceToNonspace(&current, end)) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return empty_string_value_;
+ }
+ if (!allow_leading_spaces && (input != current)) {
+ // No leading spaces allowed, but AdvanceToNonspace moved forward.
+ return junk_string_value_;
+ }
+ }
+
+ // Exponent will be adjusted if insignificant digits of the integer part
+ // or insignificant leading zeros of the fractional part are dropped.
+ int exponent = 0;
+ int significant_digits = 0;
+ int insignificant_digits = 0;
+ bool nonzero_digit_dropped = false;
+
+ bool sign = false;
+
+ if (*current == '+' || *current == '-') {
+ sign = (*current == '-');
+ ++current;
+ Iterator next_non_space = current;
+ // Skip following spaces (if allowed).
+ if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_;
+ if (!allow_spaces_after_sign && (current != next_non_space)) {
+ return junk_string_value_;
+ }
+ current = next_non_space;
+ }
+
+ if (infinity_symbol_ != NULL) {
+ if (ConsumeFirstCharacter(*current, infinity_symbol_, allow_case_insensitivity)) {
+ if (!ConsumeSubString(&current, end, infinity_symbol_, allow_case_insensitivity)) {
+ return junk_string_value_;
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign ? -Double::Infinity() : Double::Infinity();
+ }
+ }
+
+ if (nan_symbol_ != NULL) {
+ if (ConsumeFirstCharacter(*current, nan_symbol_, allow_case_insensitivity)) {
+ if (!ConsumeSubString(&current, end, nan_symbol_, allow_case_insensitivity)) {
+ return junk_string_value_;
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign ? -Double::NaN() : Double::NaN();
+ }
+ }
+
+ bool leading_zero = false;
+ if (*current == '0') {
+ if (Advance(&current, separator_, 10, end)) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+
+ leading_zero = true;
+
+ // It could be hexadecimal value.
+ if (((flags_ & ALLOW_HEX) || (flags_ & ALLOW_HEX_FLOATS)) &&
+ (*current == 'x' || *current == 'X')) {
+ ++current;
+
+ if (current == end) return junk_string_value_; // "0x"
+
+ bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) &&
+ IsHexFloatString(current, end, separator_, allow_trailing_junk);
+
+ if (!parse_as_hex_float && !isDigit(*current, 16)) {
+ return junk_string_value_;
+ }
+
+ bool result_is_junk;
+ double result = RadixStringToIeee<4>(&current,
+ end,
+ sign,
+ separator_,
+ parse_as_hex_float,
+ allow_trailing_junk,
+ junk_string_value_,
+ read_as_double,
+ &result_is_junk);
+ if (!result_is_junk) {
+ if (allow_trailing_spaces) AdvanceToNonspace(&current, end);
+ *processed_characters_count = static_cast<int>(current - input);
+ }
+ return result;
+ }
+
+ // Ignore leading zeros in the integer part.
+ while (*current == '0') {
+ if (Advance(&current, separator_, 10, end)) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+ }
+ }
+
+ bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0;
+
+ // The longest form of simplified number is: "-<significant digits>.1eXXX\0".
+ const int kBufferSize = kMaxSignificantDigits + 10;
+ DOUBLE_CONVERSION_STACK_UNINITIALIZED char
+ buffer[kBufferSize]; // NOLINT: size is known at compile time.
+ int buffer_pos = 0;
+
+ // Copy significant digits of the integer part (if any) to the buffer.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ // Will later check if it's an octal in the buffer.
+ } else {
+ insignificant_digits++; // Move the digit into the exponential part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ octal = octal && *current < '8';
+ if (Advance(&current, separator_, 10, end)) goto parsing_done;
+ }
+
+ if (significant_digits == 0) {
+ octal = false;
+ }
+
+ if (*current == '.') {
+ if (octal && !allow_trailing_junk) return junk_string_value_;
+ if (octal) goto parsing_done;
+
+ if (Advance(&current, separator_, 10, end)) {
+ if (significant_digits == 0 && !leading_zero) {
+ return junk_string_value_;
+ } else {
+ goto parsing_done;
+ }
+ }
+
+ if (significant_digits == 0) {
+ // octal = false;
+ // Integer part consists of 0 or is absent. Significant digits start after
+ // leading zeros (if any).
+ while (*current == '0') {
+ if (Advance(&current, separator_, 10, end)) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+ exponent--; // Move this 0 into the exponent.
+ }
+ }
+
+ // There is a fractional part.
+ // We don't emit a '.', but adjust the exponent instead.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ exponent--;
+ } else {
+ // Ignore insignificant digits in the fractional part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ if (Advance(&current, separator_, 10, end)) goto parsing_done;
+ }
+ }
+
+ if (!leading_zero && exponent == 0 && significant_digits == 0) {
+ // If leading_zeros is true then the string contains zeros.
+ // If exponent < 0 then string was [+-]\.0*...
+ // If significant_digits != 0 the string is not equal to 0.
+ // Otherwise there are no digits in the string.
+ return junk_string_value_;
+ }
+
+ // Parse exponential part.
+ if (*current == 'e' || *current == 'E') {
+ if (octal && !allow_trailing_junk) return junk_string_value_;
+ if (octal) goto parsing_done;
+ Iterator junk_begin = current;
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ current = junk_begin;
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+ char exponen_sign = '+';
+ if (*current == '+' || *current == '-') {
+ exponen_sign = static_cast<char>(*current);
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ current = junk_begin;
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+ }
+
+ if (current == end || *current < '0' || *current > '9') {
+ if (allow_trailing_junk) {
+ current = junk_begin;
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+
+ const int max_exponent = INT_MAX / 2;
+ DOUBLE_CONVERSION_ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
+ int num = 0;
+ do {
+ // Check overflow.
+ int digit = *current - '0';
+ if (num >= max_exponent / 10
+ && !(num == max_exponent / 10 && digit <= max_exponent % 10)) {
+ num = max_exponent;
+ } else {
+ num = num * 10 + digit;
+ }
+ ++current;
+ } while (current != end && *current >= '0' && *current <= '9');
+
+ exponent += (exponen_sign == '-' ? -num : num);
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+ if (allow_trailing_spaces) {
+ AdvanceToNonspace(&current, end);
+ }
+
+ parsing_done:
+ exponent += insignificant_digits;
+
+ if (octal) {
+ double result;
+ bool result_is_junk;
+ char* start = buffer;
+ result = RadixStringToIeee<3>(&start,
+ buffer + buffer_pos,
+ sign,
+ separator_,
+ false, // Don't parse as hex_float.
+ allow_trailing_junk,
+ junk_string_value_,
+ read_as_double,
+ &result_is_junk);
+ DOUBLE_CONVERSION_ASSERT(!result_is_junk);
+ *processed_characters_count = static_cast<int>(current - input);
+ return result;
+ }
+
+ if (nonzero_digit_dropped) {
+ buffer[buffer_pos++] = '1';
+ exponent--;
+ }
+
+ DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos] = '\0';
+
+ double converted;
+ if (read_as_double) {
+ converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
+ } else {
+ converted = Strtof(Vector<const char>(buffer, buffer_pos), exponent);
+ }
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign? -converted: converted;
+}
+
+
+double StringToDoubleConverter::StringToDouble(
+ const char* buffer,
+ int length,
+ int* processed_characters_count) const {
+ return StringToIeee(buffer, length, true, processed_characters_count);
+}
+
+
+double StringToDoubleConverter::StringToDouble(
+ const uc16* buffer,
+ int length,
+ int* processed_characters_count) const {
+ return StringToIeee(buffer, length, true, processed_characters_count);
+}
+
+
+float StringToDoubleConverter::StringToFloat(
+ const char* buffer,
+ int length,
+ int* processed_characters_count) const {
+ return static_cast<float>(StringToIeee(buffer, length, false,
+ processed_characters_count));
+}
+
+
+float StringToDoubleConverter::StringToFloat(
+ const uc16* buffer,
+ int length,
+ int* processed_characters_count) const {
+ return static_cast<float>(StringToIeee(buffer, length, false,
+ processed_characters_count));
+}
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.h
new file mode 100644
index 0000000000..ecd6c76197
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/string-to-double.h
@@ -0,0 +1,226 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_
+#define DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+class StringToDoubleConverter {
+ public:
+ // Enumeration for allowing octals and ignoring junk when converting
+ // strings to numbers.
+ enum Flags {
+ NO_FLAGS = 0,
+ ALLOW_HEX = 1,
+ ALLOW_OCTALS = 2,
+ ALLOW_TRAILING_JUNK = 4,
+ ALLOW_LEADING_SPACES = 8,
+ ALLOW_TRAILING_SPACES = 16,
+ ALLOW_SPACES_AFTER_SIGN = 32,
+ ALLOW_CASE_INSENSITIVITY = 64,
+ ALLOW_CASE_INSENSIBILITY = 64, // Deprecated
+ ALLOW_HEX_FLOATS = 128,
+ };
+
+ static const uc16 kNoSeparator = '\0';
+
+ // Flags should be a bit-or combination of the possible Flags-enum.
+ // - NO_FLAGS: no special flags.
+ // - ALLOW_HEX: recognizes the prefix "0x". Hex numbers may only be integers.
+ // Ex: StringToDouble("0x1234") -> 4660.0
+ // In StringToDouble("0x1234.56") the characters ".56" are trailing
+ // junk. The result of the call is hence dependent on
+ // the ALLOW_TRAILING_JUNK flag and/or the junk value.
+ // With this flag "0x" is a junk-string. Even with ALLOW_TRAILING_JUNK,
+ // the string will not be parsed as "0" followed by junk.
+ //
+ // - ALLOW_OCTALS: recognizes the prefix "0" for octals:
+ // If a sequence of octal digits starts with '0', then the number is
+ // read as octal integer. Octal numbers may only be integers.
+ // Ex: StringToDouble("01234") -> 668.0
+ // StringToDouble("012349") -> 12349.0 // Not a sequence of octal
+ // // digits.
+ // In StringToDouble("01234.56") the characters ".56" are trailing
+ // junk. The result of the call is hence dependent on
+ // the ALLOW_TRAILING_JUNK flag and/or the junk value.
+ // In StringToDouble("01234e56") the characters "e56" are trailing
+ // junk, too.
+ // - ALLOW_TRAILING_JUNK: ignore trailing characters that are not part of
+ // a double literal.
+ // - ALLOW_LEADING_SPACES: skip over leading whitespace, including spaces,
+ // new-lines, and tabs.
+ // - ALLOW_TRAILING_SPACES: ignore trailing whitespace.
+ // - ALLOW_SPACES_AFTER_SIGN: ignore whitespace after the sign.
+ // Ex: StringToDouble("- 123.2") -> -123.2.
+ // StringToDouble("+ 123.2") -> 123.2
+ // - ALLOW_CASE_INSENSITIVITY: ignore case of characters for special values:
+ // infinity and nan.
+ // - ALLOW_HEX_FLOATS: allows hexadecimal float literals.
+ // This *must* start with "0x" and separate the exponent with "p".
+ // Examples: 0x1.2p3 == 9.0
+ // 0x10.1p0 == 16.0625
+ // ALLOW_HEX and ALLOW_HEX_FLOATS are indendent.
+ //
+ // empty_string_value is returned when an empty string is given as input.
+ // If ALLOW_LEADING_SPACES or ALLOW_TRAILING_SPACES are set, then a string
+ // containing only spaces is converted to the 'empty_string_value', too.
+ //
+ // junk_string_value is returned when
+ // a) ALLOW_TRAILING_JUNK is not set, and a junk character (a character not
+ // part of a double-literal) is found.
+ // b) ALLOW_TRAILING_JUNK is set, but the string does not start with a
+ // double literal.
+ //
+ // infinity_symbol and nan_symbol are strings that are used to detect
+ // inputs that represent infinity and NaN. They can be null, in which case
+ // they are ignored.
+ // The conversion routine first reads any possible signs. Then it compares the
+ // following character of the input-string with the first character of
+ // the infinity, and nan-symbol. If either matches, the function assumes, that
+ // a match has been found, and expects the following input characters to match
+ // the remaining characters of the special-value symbol.
+ // This means that the following restrictions apply to special-value symbols:
+ // - they must not start with signs ('+', or '-'),
+ // - they must not have the same first character.
+ // - they must not start with digits.
+ //
+ // If the separator character is not kNoSeparator, then that specific
+ // character is ignored when in between two valid digits of the significant.
+ // It is not allowed to appear in the exponent.
+ // It is not allowed to lead or trail the number.
+ // It is not allowed to appear twice next to each other.
+ //
+ // Examples:
+ // flags = ALLOW_HEX | ALLOW_TRAILING_JUNK,
+ // empty_string_value = 0.0,
+ // junk_string_value = NaN,
+ // infinity_symbol = "infinity",
+ // nan_symbol = "nan":
+ // StringToDouble("0x1234") -> 4660.0.
+ // StringToDouble("0x1234K") -> 4660.0.
+ // StringToDouble("") -> 0.0 // empty_string_value.
+ // StringToDouble(" ") -> NaN // junk_string_value.
+ // StringToDouble(" 1") -> NaN // junk_string_value.
+ // StringToDouble("0x") -> NaN // junk_string_value.
+ // StringToDouble("-123.45") -> -123.45.
+ // StringToDouble("--123.45") -> NaN // junk_string_value.
+ // StringToDouble("123e45") -> 123e45.
+ // StringToDouble("123E45") -> 123e45.
+ // StringToDouble("123e+45") -> 123e45.
+ // StringToDouble("123E-45") -> 123e-45.
+ // StringToDouble("123e") -> 123.0 // trailing junk ignored.
+ // StringToDouble("123e-") -> 123.0 // trailing junk ignored.
+ // StringToDouble("+NaN") -> NaN // NaN string literal.
+ // StringToDouble("-infinity") -> -inf. // infinity literal.
+ // StringToDouble("Infinity") -> NaN // junk_string_value.
+ //
+ // flags = ALLOW_OCTAL | ALLOW_LEADING_SPACES,
+ // empty_string_value = 0.0,
+ // junk_string_value = NaN,
+ // infinity_symbol = NULL,
+ // nan_symbol = NULL:
+ // StringToDouble("0x1234") -> NaN // junk_string_value.
+ // StringToDouble("01234") -> 668.0.
+ // StringToDouble("") -> 0.0 // empty_string_value.
+ // StringToDouble(" ") -> 0.0 // empty_string_value.
+ // StringToDouble(" 1") -> 1.0
+ // StringToDouble("0x") -> NaN // junk_string_value.
+ // StringToDouble("0123e45") -> NaN // junk_string_value.
+ // StringToDouble("01239E45") -> 1239e45.
+ // StringToDouble("-infinity") -> NaN // junk_string_value.
+ // StringToDouble("NaN") -> NaN // junk_string_value.
+ //
+ // flags = NO_FLAGS,
+ // separator = ' ':
+ // StringToDouble("1 2 3 4") -> 1234.0
+ // StringToDouble("1 2") -> NaN // junk_string_value
+ // StringToDouble("1 000 000.0") -> 1000000.0
+ // StringToDouble("1.000 000") -> 1.0
+ // StringToDouble("1.0e1 000") -> NaN // junk_string_value
+ StringToDoubleConverter(int flags,
+ double empty_string_value,
+ double junk_string_value,
+ const char* infinity_symbol,
+ const char* nan_symbol,
+ uc16 separator = kNoSeparator)
+ : flags_(flags),
+ empty_string_value_(empty_string_value),
+ junk_string_value_(junk_string_value),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol),
+ separator_(separator) {
+ }
+
+ // Performs the conversion.
+ // The output parameter 'processed_characters_count' is set to the number
+ // of characters that have been processed to read the number.
+ // Spaces than are processed with ALLOW_{LEADING|TRAILING}_SPACES are included
+ // in the 'processed_characters_count'. Trailing junk is never included.
+ double StringToDouble(const char* buffer,
+ int length,
+ int* processed_characters_count) const;
+
+ // Same as StringToDouble above but for 16 bit characters.
+ double StringToDouble(const uc16* buffer,
+ int length,
+ int* processed_characters_count) const;
+
+ // Same as StringToDouble but reads a float.
+ // Note that this is not equivalent to static_cast<float>(StringToDouble(...))
+ // due to potential double-rounding.
+ float StringToFloat(const char* buffer,
+ int length,
+ int* processed_characters_count) const;
+
+ // Same as StringToFloat above but for 16 bit characters.
+ float StringToFloat(const uc16* buffer,
+ int length,
+ int* processed_characters_count) const;
+
+ private:
+ const int flags_;
+ const double empty_string_value_;
+ const double junk_string_value_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+ const uc16 separator_;
+
+ template <class Iterator>
+ double StringToIeee(Iterator start_pointer,
+ int length,
+ bool read_as_double,
+ int* processed_characters_count) const;
+
+ DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
+};
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_STRING_TO_DOUBLE_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc
new file mode 100644
index 0000000000..a77885104f
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.cc
@@ -0,0 +1,588 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#include <climits>
+#include <cstdarg>
+
+#include "bignum.h"
+#include "cached-powers.h"
+#include "ieee.h"
+#include "strtod.h"
+
+namespace double_conversion {
+
+#if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+// 2^53 = 9007199254740992.
+// Any integer with at most 15 decimal digits will hence fit into a double
+// (which has a 53bit significand) without loss of precision.
+static const int kMaxExactDoubleIntegerDecimalDigits = 15;
+#endif // #if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+// 2^64 = 18446744073709551616 > 10^19
+static const int kMaxUint64DecimalDigits = 19;
+
+// Max double: 1.7976931348623157 x 10^308
+// Min non-zero double: 4.9406564584124654 x 10^-324
+// Any x >= 10^309 is interpreted as +infinity.
+// Any x <= 10^-324 is interpreted as 0.
+// Note that 2.5e-324 (despite being smaller than the min double) will be read
+// as non-zero (equal to the min non-zero double).
+static const int kMaxDecimalPower = 309;
+static const int kMinDecimalPower = -324;
+
+// 2^64 = 18446744073709551616
+static const uint64_t kMaxUint64 = DOUBLE_CONVERSION_UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF);
+
+
+#if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+static const double exact_powers_of_ten[] = {
+ 1.0, // 10^0
+ 10.0,
+ 100.0,
+ 1000.0,
+ 10000.0,
+ 100000.0,
+ 1000000.0,
+ 10000000.0,
+ 100000000.0,
+ 1000000000.0,
+ 10000000000.0, // 10^10
+ 100000000000.0,
+ 1000000000000.0,
+ 10000000000000.0,
+ 100000000000000.0,
+ 1000000000000000.0,
+ 10000000000000000.0,
+ 100000000000000000.0,
+ 1000000000000000000.0,
+ 10000000000000000000.0,
+ 100000000000000000000.0, // 10^20
+ 1000000000000000000000.0,
+ // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22
+ 10000000000000000000000.0
+};
+static const int kExactPowersOfTenSize = DOUBLE_CONVERSION_ARRAY_SIZE(exact_powers_of_ten);
+#endif // #if defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+
+// Maximum number of significant digits in the decimal representation.
+// In fact the value is 772 (see conversions.cc), but to give us some margin
+// we round up to 780.
+static const int kMaxSignificantDecimalDigits = 780;
+
+static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
+ for (int i = 0; i < buffer.length(); i++) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(i, buffer.length());
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
+ for (int i = buffer.length() - 1; i >= 0; --i) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(0, i + 1);
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static void CutToMaxSignificantDigits(Vector<const char> buffer,
+ int exponent,
+ char* significant_buffer,
+ int* significant_exponent) {
+ for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
+ significant_buffer[i] = buffer[i];
+ }
+ // The input buffer has been trimmed. Therefore the last digit must be
+ // different from '0'.
+ DOUBLE_CONVERSION_ASSERT(buffer[buffer.length() - 1] != '0');
+ // Set the last digit to be non-zero. This is sufficient to guarantee
+ // correct rounding.
+ significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
+ *significant_exponent =
+ exponent + (buffer.length() - kMaxSignificantDecimalDigits);
+}
+
+
+// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits.
+// If possible the input-buffer is reused, but if the buffer needs to be
+// modified (due to cutting), then the input needs to be copied into the
+// buffer_copy_space.
+static void TrimAndCut(Vector<const char> buffer, int exponent,
+ char* buffer_copy_space, int space_size,
+ Vector<const char>* trimmed, int* updated_exponent) {
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+ Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed);
+ exponent += left_trimmed.length() - right_trimmed.length();
+ if (right_trimmed.length() > kMaxSignificantDecimalDigits) {
+ (void) space_size; // Mark variable as used.
+ DOUBLE_CONVERSION_ASSERT(space_size >= kMaxSignificantDecimalDigits);
+ CutToMaxSignificantDigits(right_trimmed, exponent,
+ buffer_copy_space, updated_exponent);
+ *trimmed = Vector<const char>(buffer_copy_space,
+ kMaxSignificantDecimalDigits);
+ } else {
+ *trimmed = right_trimmed;
+ *updated_exponent = exponent;
+ }
+}
+
+
+// Reads digits from the buffer and converts them to a uint64.
+// Reads in as many digits as fit into a uint64.
+// When the string starts with "1844674407370955161" no further digit is read.
+// Since 2^64 = 18446744073709551616 it would still be possible read another
+// digit if it was less or equal than 6, but this would complicate the code.
+static uint64_t ReadUint64(Vector<const char> buffer,
+ int* number_of_read_digits) {
+ uint64_t result = 0;
+ int i = 0;
+ while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
+ int digit = buffer[i++] - '0';
+ DOUBLE_CONVERSION_ASSERT(0 <= digit && digit <= 9);
+ result = 10 * result + digit;
+ }
+ *number_of_read_digits = i;
+ return result;
+}
+
+
+// Reads a DiyFp from the buffer.
+// The returned DiyFp is not necessarily normalized.
+// If remaining_decimals is zero then the returned DiyFp is accurate.
+// Otherwise it has been rounded and has error of at most 1/2 ulp.
+static void ReadDiyFp(Vector<const char> buffer,
+ DiyFp* result,
+ int* remaining_decimals) {
+ int read_digits;
+ uint64_t significand = ReadUint64(buffer, &read_digits);
+ if (buffer.length() == read_digits) {
+ *result = DiyFp(significand, 0);
+ *remaining_decimals = 0;
+ } else {
+ // Round the significand.
+ if (buffer[read_digits] >= '5') {
+ significand++;
+ }
+ // Compute the binary exponent.
+ int exponent = 0;
+ *result = DiyFp(significand, exponent);
+ *remaining_decimals = buffer.length() - read_digits;
+ }
+}
+
+
+static bool DoubleStrtod(Vector<const char> trimmed,
+ int exponent,
+ double* result) {
+#if !defined(DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS)
+ // On x86 the floating-point stack can be 64 or 80 bits wide. If it is
+ // 80 bits wide (as is the case on Linux) then double-rounding occurs and the
+ // result is not accurate.
+ // We know that Windows32 uses 64 bits and is therefore accurate.
+ // Note that the ARM simulator is compiled for 32bits. It therefore exhibits
+ // the same problem.
+ return false;
+#else
+ if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
+ int read_digits;
+ // The trimmed input fits into a double.
+ // If the 10^exponent (resp. 10^-exponent) fits into a double too then we
+ // can compute the result-double simply by multiplying (resp. dividing) the
+ // two numbers.
+ // This is possible because IEEE guarantees that floating-point operations
+ // return the best possible approximation.
+ if (exponent < 0 && -exponent < kExactPowersOfTenSize) {
+ // 10^-exponent fits into a double.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length());
+ *result /= exact_powers_of_ten[-exponent];
+ return true;
+ }
+ if (0 <= exponent && exponent < kExactPowersOfTenSize) {
+ // 10^exponent fits into a double.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length());
+ *result *= exact_powers_of_ten[exponent];
+ return true;
+ }
+ int remaining_digits =
+ kMaxExactDoubleIntegerDecimalDigits - trimmed.length();
+ if ((0 <= exponent) &&
+ (exponent - remaining_digits < kExactPowersOfTenSize)) {
+ // The trimmed string was short and we can multiply it with
+ // 10^remaining_digits. As a result the remaining exponent now fits
+ // into a double too.
+ *result = static_cast<double>(ReadUint64(trimmed, &read_digits));
+ DOUBLE_CONVERSION_ASSERT(read_digits == trimmed.length());
+ *result *= exact_powers_of_ten[remaining_digits];
+ *result *= exact_powers_of_ten[exponent - remaining_digits];
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+
+// Returns 10^exponent as an exact DiyFp.
+// The given exponent must be in the range [1; kDecimalExponentDistance[.
+static DiyFp AdjustmentPowerOfTen(int exponent) {
+ DOUBLE_CONVERSION_ASSERT(0 < exponent);
+ DOUBLE_CONVERSION_ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance);
+ // Simply hardcode the remaining powers for the given decimal exponent
+ // distance.
+ DOUBLE_CONVERSION_ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8);
+ switch (exponent) {
+ case 1: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xa0000000, 00000000), -60);
+ case 2: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc8000000, 00000000), -57);
+ case 3: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xfa000000, 00000000), -54);
+ case 4: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x9c400000, 00000000), -50);
+ case 5: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xc3500000, 00000000), -47);
+ case 6: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0xf4240000, 00000000), -44);
+ case 7: return DiyFp(DOUBLE_CONVERSION_UINT64_2PART_C(0x98968000, 00000000), -40);
+ default:
+ DOUBLE_CONVERSION_UNREACHABLE();
+ }
+}
+
+
+// If the function returns true then the result is the correct double.
+// Otherwise it is either the correct double or the double that is just below
+// the correct double.
+static bool DiyFpStrtod(Vector<const char> buffer,
+ int exponent,
+ double* result) {
+ DiyFp input;
+ int remaining_decimals;
+ ReadDiyFp(buffer, &input, &remaining_decimals);
+ // Since we may have dropped some digits the input is not accurate.
+ // If remaining_decimals is different than 0 than the error is at most
+ // .5 ulp (unit in the last place).
+ // We don't want to deal with fractions and therefore keep a common
+ // denominator.
+ const int kDenominatorLog = 3;
+ const int kDenominator = 1 << kDenominatorLog;
+ // Move the remaining decimals into the exponent.
+ exponent += remaining_decimals;
+ uint64_t error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
+
+ int old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ DOUBLE_CONVERSION_ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent);
+ if (exponent < PowersOfTenCache::kMinDecimalExponent) {
+ *result = 0.0;
+ return true;
+ }
+ DiyFp cached_power;
+ int cached_decimal_exponent;
+ PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent,
+ &cached_power,
+ &cached_decimal_exponent);
+
+ if (cached_decimal_exponent != exponent) {
+ int adjustment_exponent = exponent - cached_decimal_exponent;
+ DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
+ input.Multiply(adjustment_power);
+ if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
+ // The product of input with the adjustment power fits into a 64 bit
+ // integer.
+ DOUBLE_CONVERSION_ASSERT(DiyFp::kSignificandSize == 64);
+ } else {
+ // The adjustment power is exact. There is hence only an error of 0.5.
+ error += kDenominator / 2;
+ }
+ }
+
+ input.Multiply(cached_power);
+ // The error introduced by a multiplication of a*b equals
+ // error_a + error_b + error_a*error_b/2^64 + 0.5
+ // Substituting a with 'input' and b with 'cached_power' we have
+ // error_b = 0.5 (all cached powers have an error of less than 0.5 ulp),
+ // error_ab = 0 or 1 / kDenominator > error_a*error_b/ 2^64
+ int error_b = kDenominator / 2;
+ int error_ab = (error == 0 ? 0 : 1); // We round up to 1.
+ int fixed_error = kDenominator / 2;
+ error += error_b + error_ab + fixed_error;
+
+ old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ // See if the double's significand changes if we add/subtract the error.
+ int order_of_magnitude = DiyFp::kSignificandSize + input.e();
+ int effective_significand_size =
+ Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
+ int precision_digits_count =
+ DiyFp::kSignificandSize - effective_significand_size;
+ if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
+ // This can only happen for very small denormals. In this case the
+ // half-way multiplied by the denominator exceeds the range of an uint64.
+ // Simply shift everything to the right.
+ int shift_amount = (precision_digits_count + kDenominatorLog) -
+ DiyFp::kSignificandSize + 1;
+ input.set_f(input.f() >> shift_amount);
+ input.set_e(input.e() + shift_amount);
+ // We add 1 for the lost precision of error, and kDenominator for
+ // the lost precision of input.f().
+ error = (error >> shift_amount) + 1 + kDenominator;
+ precision_digits_count -= shift_amount;
+ }
+ // We use uint64_ts now. This only works if the DiyFp uses uint64_ts too.
+ DOUBLE_CONVERSION_ASSERT(DiyFp::kSignificandSize == 64);
+ DOUBLE_CONVERSION_ASSERT(precision_digits_count < 64);
+ uint64_t one64 = 1;
+ uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
+ uint64_t precision_bits = input.f() & precision_bits_mask;
+ uint64_t half_way = one64 << (precision_digits_count - 1);
+ precision_bits *= kDenominator;
+ half_way *= kDenominator;
+ DiyFp rounded_input(input.f() >> precision_digits_count,
+ input.e() + precision_digits_count);
+ if (precision_bits >= half_way + error) {
+ rounded_input.set_f(rounded_input.f() + 1);
+ }
+ // If the last_bits are too close to the half-way case than we are too
+ // inaccurate and round down. In this case we return false so that we can
+ // fall back to a more precise algorithm.
+
+ *result = Double(rounded_input).value();
+ if (half_way - error < precision_bits && precision_bits < half_way + error) {
+ // Too imprecise. The caller will have to fall back to a slower version.
+ // However the returned number is guaranteed to be either the correct
+ // double, or the next-lower double.
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+// Returns
+// - -1 if buffer*10^exponent < diy_fp.
+// - 0 if buffer*10^exponent == diy_fp.
+// - +1 if buffer*10^exponent > diy_fp.
+// Preconditions:
+// buffer.length() + exponent <= kMaxDecimalPower + 1
+// buffer.length() + exponent > kMinDecimalPower
+// buffer.length() <= kMaxDecimalSignificantDigits
+static int CompareBufferWithDiyFp(Vector<const char> buffer,
+ int exponent,
+ DiyFp diy_fp) {
+ DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
+ DOUBLE_CONVERSION_ASSERT(buffer.length() + exponent > kMinDecimalPower);
+ DOUBLE_CONVERSION_ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
+ // Make sure that the Bignum will be able to hold all our numbers.
+ // Our Bignum implementation has a separate field for exponents. Shifts will
+ // consume at most one bigit (< 64 bits).
+ // ln(10) == 3.3219...
+ DOUBLE_CONVERSION_ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
+ Bignum buffer_bignum;
+ Bignum diy_fp_bignum;
+ buffer_bignum.AssignDecimalString(buffer);
+ diy_fp_bignum.AssignUInt64(diy_fp.f());
+ if (exponent >= 0) {
+ buffer_bignum.MultiplyByPowerOfTen(exponent);
+ } else {
+ diy_fp_bignum.MultiplyByPowerOfTen(-exponent);
+ }
+ if (diy_fp.e() > 0) {
+ diy_fp_bignum.ShiftLeft(diy_fp.e());
+ } else {
+ buffer_bignum.ShiftLeft(-diy_fp.e());
+ }
+ return Bignum::Compare(buffer_bignum, diy_fp_bignum);
+}
+
+
+// Returns true if the guess is the correct double.
+// Returns false, when guess is either correct or the next-lower double.
+static bool ComputeGuess(Vector<const char> trimmed, int exponent,
+ double* guess) {
+ if (trimmed.length() == 0) {
+ *guess = 0.0;
+ return true;
+ }
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) {
+ *guess = Double::Infinity();
+ return true;
+ }
+ if (exponent + trimmed.length() <= kMinDecimalPower) {
+ *guess = 0.0;
+ return true;
+ }
+
+ if (DoubleStrtod(trimmed, exponent, guess) ||
+ DiyFpStrtod(trimmed, exponent, guess)) {
+ return true;
+ }
+ if (*guess == Double::Infinity()) {
+ return true;
+ }
+ return false;
+}
+
+double StrtodTrimmed(Vector<const char> trimmed, int exponent) {
+ DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits);
+ double guess;
+ const bool is_correct = ComputeGuess(trimmed, exponent, &guess);
+ if (is_correct) {
+ return guess;
+ }
+ DiyFp upper_boundary = Double(guess).UpperBoundary();
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+ if (comparison < 0) {
+ return guess;
+ } else if (comparison > 0) {
+ return Double(guess).NextDouble();
+ } else if ((Double(guess).Significand() & 1) == 0) {
+ // Round towards even.
+ return guess;
+ } else {
+ return Double(guess).NextDouble();
+ }
+}
+
+double Strtod(Vector<const char> buffer, int exponent) {
+ char copy_buffer[kMaxSignificantDecimalDigits];
+ Vector<const char> trimmed;
+ int updated_exponent;
+ TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
+ &trimmed, &updated_exponent);
+ return StrtodTrimmed(trimmed, updated_exponent);
+}
+
+static float SanitizedDoubletof(double d) {
+ DOUBLE_CONVERSION_ASSERT(d >= 0.0);
+ // ASAN has a sanitize check that disallows casting doubles to floats if
+ // they are too big.
+ // https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks
+ // The behavior should be covered by IEEE 754, but some projects use this
+ // flag, so work around it.
+ float max_finite = 3.4028234663852885981170418348451692544e+38;
+ // The half-way point between the max-finite and infinity value.
+ // Since infinity has an even significand everything equal or greater than
+ // this value should become infinity.
+ double half_max_finite_infinity =
+ 3.40282356779733661637539395458142568448e+38;
+ if (d >= max_finite) {
+ if (d >= half_max_finite_infinity) {
+ return Single::Infinity();
+ } else {
+ return max_finite;
+ }
+ } else {
+ return static_cast<float>(d);
+ }
+}
+
+float Strtof(Vector<const char> buffer, int exponent) {
+ char copy_buffer[kMaxSignificantDecimalDigits];
+ Vector<const char> trimmed;
+ int updated_exponent;
+ TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
+ &trimmed, &updated_exponent);
+ exponent = updated_exponent;
+
+ double double_guess;
+ bool is_correct = ComputeGuess(trimmed, exponent, &double_guess);
+
+ float float_guess = SanitizedDoubletof(double_guess);
+ if (float_guess == double_guess) {
+ // This shortcut triggers for integer values.
+ return float_guess;
+ }
+
+ // We must catch double-rounding. Say the double has been rounded up, and is
+ // now a boundary of a float, and rounds up again. This is why we have to
+ // look at previous too.
+ // Example (in decimal numbers):
+ // input: 12349
+ // high-precision (4 digits): 1235
+ // low-precision (3 digits):
+ // when read from input: 123
+ // when rounded from high precision: 124.
+ // To do this we simply look at the neigbors of the correct result and see
+ // if they would round to the same float. If the guess is not correct we have
+ // to look at four values (since two different doubles could be the correct
+ // double).
+
+ double double_next = Double(double_guess).NextDouble();
+ double double_previous = Double(double_guess).PreviousDouble();
+
+ float f1 = SanitizedDoubletof(double_previous);
+ float f2 = float_guess;
+ float f3 = SanitizedDoubletof(double_next);
+ float f4;
+ if (is_correct) {
+ f4 = f3;
+ } else {
+ double double_next2 = Double(double_next).NextDouble();
+ f4 = SanitizedDoubletof(double_next2);
+ }
+ (void) f2; // Mark variable as used.
+ DOUBLE_CONVERSION_ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4);
+
+ // If the guess doesn't lie near a single-precision boundary we can simply
+ // return its float-value.
+ if (f1 == f4) {
+ return float_guess;
+ }
+
+ DOUBLE_CONVERSION_ASSERT((f1 != f2 && f2 == f3 && f3 == f4) ||
+ (f1 == f2 && f2 != f3 && f3 == f4) ||
+ (f1 == f2 && f2 == f3 && f3 != f4));
+
+ // guess and next are the two possible candidates (in the same way that
+ // double_guess was the lower candidate for a double-precision guess).
+ float guess = f1;
+ float next = f4;
+ DiyFp upper_boundary;
+ if (guess == 0.0f) {
+ float min_float = 1e-45f;
+ upper_boundary = Double(static_cast<double>(min_float) / 2).AsDiyFp();
+ } else {
+ upper_boundary = Single(guess).UpperBoundary();
+ }
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+ if (comparison < 0) {
+ return guess;
+ } else if (comparison > 0) {
+ return next;
+ } else if ((Single(guess).Significand() & 1) == 0) {
+ // Round towards even.
+ return guess;
+ } else {
+ return next;
+ }
+}
+
+} // namespace double_conversion
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.h
new file mode 100644
index 0000000000..ff0ee47092
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/strtod.h
@@ -0,0 +1,50 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_STRTOD_H_
+#define DOUBLE_CONVERSION_STRTOD_H_
+
+#include "utils.h"
+
+namespace double_conversion {
+
+// The buffer must only contain digits in the range [0-9]. It must not
+// contain a dot or a sign. It must not start with '0', and must not be empty.
+double Strtod(Vector<const char> buffer, int exponent);
+
+// The buffer must only contain digits in the range [0-9]. It must not
+// contain a dot or a sign. It must not start with '0', and must not be empty.
+float Strtof(Vector<const char> buffer, int exponent);
+
+// For special use cases, the heart of the Strtod() function is also available
+// separately, it assumes that 'trimmed' is as produced by TrimAndCut(), i.e.
+// no leading or trailing zeros, also no lone zero, and not 'too many' digits.
+double StrtodTrimmed(Vector<const char> trimmed, int exponent);
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_STRTOD_H_
diff --git a/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/utils.h b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/utils.h
new file mode 100644
index 0000000000..471c3da84c
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/double_conversion/double-conversion/utils.h
@@ -0,0 +1,364 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "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 COPYRIGHT
+// OWNER OR CONTRIBUTORS 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.
+
+#ifndef DOUBLE_CONVERSION_UTILS_H_
+#define DOUBLE_CONVERSION_UTILS_H_
+
+#include <cstdlib>
+#include <cstring>
+
+#include <cassert>
+#ifndef DOUBLE_CONVERSION_ASSERT
+#define DOUBLE_CONVERSION_ASSERT(condition) \
+ assert(condition);
+#endif
+#ifndef DOUBLE_CONVERSION_UNIMPLEMENTED
+#define DOUBLE_CONVERSION_UNIMPLEMENTED() (abort())
+#endif
+#ifndef DOUBLE_CONVERSION_NO_RETURN
+#ifdef _MSC_VER
+#define DOUBLE_CONVERSION_NO_RETURN __declspec(noreturn)
+#else
+#define DOUBLE_CONVERSION_NO_RETURN __attribute__((noreturn))
+#endif
+#endif
+#ifndef DOUBLE_CONVERSION_UNREACHABLE
+#ifdef _MSC_VER
+void DOUBLE_CONVERSION_NO_RETURN abort_noreturn();
+inline void abort_noreturn() { abort(); }
+#define DOUBLE_CONVERSION_UNREACHABLE() (abort_noreturn())
+#else
+#define DOUBLE_CONVERSION_UNREACHABLE() (abort())
+#endif
+#endif
+
+#ifndef DOUBLE_CONVERSION_UNUSED
+#ifdef __GNUC__
+#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
+#else
+#define DOUBLE_CONVERSION_UNUSED
+#endif
+#endif
+
+#if defined(__clang__) && __has_attribute(uninitialized)
+#define DOUBLE_CONVERSION_STACK_UNINITIALIZED __attribute__((uninitialized))
+#else
+#define DOUBLE_CONVERSION_STACK_UNINITIALIZED
+#endif
+
+// Double operations detection based on target architecture.
+// Linux uses a 80bit wide floating point stack on x86. This induces double
+// rounding, which in turn leads to wrong results.
+// An easy way to test if the floating-point operations are correct is to
+// evaluate: 89255.0/1e22. If the floating-point stack is 64 bits wide then
+// the result is equal to 89255e-22.
+// The best way to test this, is to create a division-function and to compare
+// the output of the division with the expected result. (Inlining must be
+// disabled.)
+// On Linux,x86 89255e-22 != Div_double(89255.0/1e22)
+//
+// For example:
+/*
+// -- in div.c
+double Div_double(double x, double y) { return x / y; }
+
+// -- in main.c
+double Div_double(double x, double y); // Forward declaration.
+
+int main(int argc, char** argv) {
+ return Div_double(89255.0, 1e22) == 89255e-22;
+}
+*/
+// Run as follows ./main || echo "correct"
+//
+// If it prints "correct" then the architecture should be here, in the "correct" section.
+#if defined(_M_X64) || defined(__x86_64__) || \
+ defined(__ARMEL__) || defined(__avr32__) || defined(_M_ARM) || defined(_M_ARM64) || \
+ defined(__hppa__) || defined(__ia64__) || \
+ defined(__mips__) || \
+ defined(__nios2__) || \
+ defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || \
+ defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
+ defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
+ defined(__SH4__) || defined(__alpha__) || \
+ defined(_MIPS_ARCH_MIPS32R2) || defined(__ARMEB__) ||\
+ defined(__AARCH64EL__) || defined(__aarch64__) || defined(__AARCH64EB__) || \
+ defined(__riscv) || defined(__e2k__) || \
+ defined(__or1k__) || defined(__arc__) || \
+ defined(__microblaze__) || defined(__XTENSA__) || \
+ defined(__EMSCRIPTEN__) || defined(__wasm32__)
+#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
+#elif defined(__mc68000__) || \
+ defined(__pnacl__) || defined(__native_client__)
+#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
+#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
+#if defined(_WIN32)
+// Windows uses a 64bit wide floating point stack.
+#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
+#else
+#undef DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS
+#endif // _WIN32
+#else
+#error Target architecture was not detected as supported by Double-Conversion.
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t; // NOLINT
+typedef unsigned short uint16_t; // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include <stdint.h>
+
+#endif
+
+typedef uint16_t uc16;
+
+// The following macro works on both 32 and 64-bit platforms.
+// Usage: instead of writing 0x1234567890123456
+// write DOUBLE_CONVERSION_UINT64_2PART_C(0x12345678,90123456);
+#define DOUBLE_CONVERSION_UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
+
+
+// The expression DOUBLE_CONVERSION_ARRAY_SIZE(a) is a compile-time constant of type
+// size_t which represents the number of elements of the given
+// array. You should only use DOUBLE_CONVERSION_ARRAY_SIZE on statically allocated
+// arrays.
+#ifndef DOUBLE_CONVERSION_ARRAY_SIZE
+#define DOUBLE_CONVERSION_ARRAY_SIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+#endif
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#ifndef DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN
+#define DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#ifndef DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS
+#define DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DOUBLE_CONVERSION_DISALLOW_COPY_AND_ASSIGN(TypeName)
+#endif
+
+namespace double_conversion {
+
+inline int StrLength(const char* string) {
+ size_t length = strlen(string);
+ DOUBLE_CONVERSION_ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
+ return static_cast<int>(length);
+}
+
+// This is a simplified version of V8's Vector class.
+template <typename T>
+class Vector {
+ public:
+ Vector() : start_(NULL), length_(0) {}
+ Vector(T* data, int len) : start_(data), length_(len) {
+ DOUBLE_CONVERSION_ASSERT(len == 0 || (len > 0 && data != NULL));
+ }
+
+ // Returns a vector using the same backing storage as this one,
+ // spanning from and including 'from', to but not including 'to'.
+ Vector<T> SubVector(int from, int to) {
+ DOUBLE_CONVERSION_ASSERT(to <= length_);
+ DOUBLE_CONVERSION_ASSERT(from < to);
+ DOUBLE_CONVERSION_ASSERT(0 <= from);
+ return Vector<T>(start() + from, to - from);
+ }
+
+ // Returns the length of the vector.
+ int length() const { return length_; }
+
+ // Returns whether or not the vector is empty.
+ bool is_empty() const { return length_ == 0; }
+
+ // Returns the pointer to the start of the data in the vector.
+ T* start() const { return start_; }
+
+ // Access individual vector elements - checks bounds in debug mode.
+ T& operator[](int index) const {
+ DOUBLE_CONVERSION_ASSERT(0 <= index && index < length_);
+ return start_[index];
+ }
+
+ T& first() { return start_[0]; }
+
+ T& last() { return start_[length_ - 1]; }
+
+ void pop_back() {
+ DOUBLE_CONVERSION_ASSERT(!is_empty());
+ --length_;
+ }
+
+ private:
+ T* start_;
+ int length_;
+};
+
+
+// Helper class for building result strings in a character buffer. The
+// purpose of the class is to use safe operations that checks the
+// buffer bounds on all operations in debug mode.
+class StringBuilder {
+ public:
+ StringBuilder(char* buffer, int buffer_size)
+ : buffer_(buffer, buffer_size), position_(0) { }
+
+ ~StringBuilder() { if (!is_finalized()) Finalize(); }
+
+ int size() const { return buffer_.length(); }
+
+ // Get the current position in the builder.
+ int position() const {
+ DOUBLE_CONVERSION_ASSERT(!is_finalized());
+ return position_;
+ }
+
+ // Reset the position.
+ void Reset() { position_ = 0; }
+
+ // Add a single character to the builder. It is not allowed to add
+ // 0-characters; use the Finalize() method to terminate the string
+ // instead.
+ void AddCharacter(char c) {
+ DOUBLE_CONVERSION_ASSERT(c != '\0');
+ DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length());
+ buffer_[position_++] = c;
+ }
+
+ // Add an entire string to the builder. Uses strlen() internally to
+ // compute the length of the input string.
+ void AddString(const char* s) {
+ AddSubstring(s, StrLength(s));
+ }
+
+ // Add the first 'n' characters of the given string 's' to the
+ // builder. The input string must have enough characters.
+ void AddSubstring(const char* s, int n) {
+ DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ + n < buffer_.length());
+ DOUBLE_CONVERSION_ASSERT(static_cast<size_t>(n) <= strlen(s));
+ memmove(&buffer_[position_], s, n);
+ position_ += n;
+ }
+
+
+ // Add character padding to the builder. If count is non-positive,
+ // nothing is added to the builder.
+ void AddPadding(char c, int count) {
+ for (int i = 0; i < count; i++) {
+ AddCharacter(c);
+ }
+ }
+
+ // Finalize the string by 0-terminating it and returning the buffer.
+ char* Finalize() {
+ DOUBLE_CONVERSION_ASSERT(!is_finalized() && position_ < buffer_.length());
+ buffer_[position_] = '\0';
+ // Make sure nobody managed to add a 0-character to the
+ // buffer while building the string.
+ DOUBLE_CONVERSION_ASSERT(strlen(buffer_.start()) == static_cast<size_t>(position_));
+ position_ = -1;
+ DOUBLE_CONVERSION_ASSERT(is_finalized());
+ return buffer_.start();
+ }
+
+ private:
+ Vector<char> buffer_;
+ int position_;
+
+ bool is_finalized() const { return position_ < 0; }
+
+ DOUBLE_CONVERSION_DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
+};
+
+// The type-based aliasing rule allows the compiler to assume that pointers of
+// different types (for some definition of different) never alias each other.
+// Thus the following code does not work:
+//
+// float f = foo();
+// int fbits = *(int*)(&f);
+//
+// The compiler 'knows' that the int pointer can't refer to f since the types
+// don't match, so the compiler may cache f in a register, leaving random data
+// in fbits. Using C++ style casts makes no difference, however a pointer to
+// char data is assumed to alias any other pointer. This is the 'memcpy
+// exception'.
+//
+// Bit_cast uses the memcpy exception to move the bits from a variable of one
+// type of a variable of another type. Of course the end result is likely to
+// be implementation dependent. Most compilers (gcc-4.2 and MSVC 2005)
+// will completely optimize BitCast away.
+//
+// There is an additional use for BitCast.
+// Recent gccs will warn when they see casts that may result in breakage due to
+// the type-based aliasing rule. If you have checked that there is no breakage
+// you can use BitCast to cast one pointer type to another. This confuses gcc
+// enough that it can no longer see that you have cast one pointer type to
+// another thus avoiding the warning.
+template <class Dest, class Source>
+Dest BitCast(const Source& source) {
+ // Compile time assertion: sizeof(Dest) == sizeof(Source)
+ // A compile error here means your Dest and Source have different sizes.
+#if __cplusplus >= 201103L
+ static_assert(sizeof(Dest) == sizeof(Source),
+ "source and destination size mismatch");
+#else
+ DOUBLE_CONVERSION_UNUSED
+ typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
+#endif
+
+ Dest dest;
+ memmove(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+template <class Dest, class Source>
+Dest BitCast(Source* source) {
+ return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
+}
+
+} // namespace double_conversion
+
+#endif // DOUBLE_CONVERSION_UTILS_H_
diff --git a/security/sandbox/chromium/base/third_party/dynamic_annotations/LICENSE b/security/sandbox/chromium/base/third_party/dynamic_annotations/LICENSE
new file mode 100644
index 0000000000..5c581a9391
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/dynamic_annotations/LICENSE
@@ -0,0 +1,28 @@
+/* Copyright (c) 2008-2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ *
+ * ---
+ * Author: Kostya Serebryany
+ */
diff --git a/security/sandbox/chromium/base/third_party/dynamic_annotations/dynamic_annotations.h b/security/sandbox/chromium/base/third_party/dynamic_annotations/dynamic_annotations.h
new file mode 100644
index 0000000000..8d7f05202b
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/dynamic_annotations/dynamic_annotations.h
@@ -0,0 +1,595 @@
+/* Copyright (c) 2011, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 COPYRIGHT
+ * OWNER OR CONTRIBUTORS 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.
+ */
+
+/* This file defines dynamic annotations for use with dynamic analysis
+ tool such as valgrind, PIN, etc.
+
+ Dynamic annotation is a source code annotation that affects
+ the generated code (that is, the annotation is not a comment).
+ Each such annotation is attached to a particular
+ instruction and/or to a particular object (address) in the program.
+
+ The annotations that should be used by users are macros in all upper-case
+ (e.g., ANNOTATE_NEW_MEMORY).
+
+ Actual implementation of these macros may differ depending on the
+ dynamic analysis tool being used.
+
+ See http://code.google.com/p/data-race-test/ for more information.
+
+ This file supports the following dynamic analysis tools:
+ - None (DYNAMIC_ANNOTATIONS_ENABLED is not defined or zero).
+ Macros are defined empty.
+ - ThreadSanitizer, Helgrind, DRD (DYNAMIC_ANNOTATIONS_ENABLED is 1).
+ Macros are defined as calls to non-inlinable empty functions
+ that are intercepted by Valgrind. */
+
+#ifndef __DYNAMIC_ANNOTATIONS_H__
+#define __DYNAMIC_ANNOTATIONS_H__
+
+#ifndef DYNAMIC_ANNOTATIONS_PREFIX
+# define DYNAMIC_ANNOTATIONS_PREFIX
+#endif
+
+#ifndef DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND
+# define DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND 1
+#endif
+
+#ifdef DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK
+# ifdef __GNUC__
+# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak))
+# else
+/* TODO(glider): for Windows support we may want to change this macro in order
+ to prepend __declspec(selectany) to the annotations' declarations. */
+# error weak annotations are not supported for your compiler
+# endif
+#else
+# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK
+#endif
+
+/* The following preprocessor magic prepends the value of
+ DYNAMIC_ANNOTATIONS_PREFIX to annotation function names. */
+#define DYNAMIC_ANNOTATIONS_GLUE0(A, B) A##B
+#define DYNAMIC_ANNOTATIONS_GLUE(A, B) DYNAMIC_ANNOTATIONS_GLUE0(A, B)
+#define DYNAMIC_ANNOTATIONS_NAME(name) \
+ DYNAMIC_ANNOTATIONS_GLUE(DYNAMIC_ANNOTATIONS_PREFIX, name)
+
+#ifndef DYNAMIC_ANNOTATIONS_ENABLED
+# define DYNAMIC_ANNOTATIONS_ENABLED 0
+#endif
+
+#if DYNAMIC_ANNOTATIONS_ENABLED != 0
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing condition variables such as CondVar,
+ using conditional critical sections (Await/LockWhen) and when constructing
+ user-defined synchronization mechanisms.
+
+ The annotations ANNOTATE_HAPPENS_BEFORE() and ANNOTATE_HAPPENS_AFTER() can
+ be used to define happens-before arcs in user-defined synchronization
+ mechanisms: the race detector will infer an arc from the former to the
+ latter when they share the same argument pointer.
+
+ Example 1 (reference counting):
+
+ void Unref() {
+ ANNOTATE_HAPPENS_BEFORE(&refcount_);
+ if (AtomicDecrementByOne(&refcount_) == 0) {
+ ANNOTATE_HAPPENS_AFTER(&refcount_);
+ delete this;
+ }
+ }
+
+ Example 2 (message queue):
+
+ void MyQueue::Put(Type *e) {
+ MutexLock lock(&mu_);
+ ANNOTATE_HAPPENS_BEFORE(e);
+ PutElementIntoMyQueue(e);
+ }
+
+ Type *MyQueue::Get() {
+ MutexLock lock(&mu_);
+ Type *e = GetElementFromMyQueue();
+ ANNOTATE_HAPPENS_AFTER(e);
+ return e;
+ }
+
+ Note: when possible, please use the existing reference counting and message
+ queue implementations instead of inventing new ones. */
+
+ /* Report that wait on the condition variable at address "cv" has succeeded
+ and the lock at address "lock" is held. */
+ #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, lock)
+
+ /* Report that wait on the condition variable at "cv" has succeeded. Variant
+ w/o lock. */
+ #define ANNOTATE_CONDVAR_WAIT(cv) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, NULL)
+
+ /* Report that we are about to signal on the condition variable at address
+ "cv". */
+ #define ANNOTATE_CONDVAR_SIGNAL(cv) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(__FILE__, __LINE__, cv)
+
+ /* Report that we are about to signal_all on the condition variable at address
+ "cv". */
+ #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(__FILE__, __LINE__, cv)
+
+ /* Annotations for user-defined synchronization mechanisms. */
+ #define ANNOTATE_HAPPENS_BEFORE(obj) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(__FILE__, __LINE__, obj)
+ #define ANNOTATE_HAPPENS_AFTER(obj) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(__FILE__, __LINE__, obj)
+
+ /* DEPRECATED. Don't use it. */
+ #define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(__FILE__, __LINE__, \
+ pointer, size)
+
+ /* DEPRECATED. Don't use it. */
+ #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(__FILE__, __LINE__, \
+ pointer, size)
+
+ /* DEPRECATED. Don't use it. */
+ #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) \
+ do { \
+ ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size); \
+ ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size); \
+ } while (0)
+
+ /* Instruct the tool to create a happens-before arc between mu->Unlock() and
+ mu->Lock(). This annotation may slow down the race detector and hide real
+ races. Normally it is used only when it would be difficult to annotate each
+ of the mutex's critical sections individually using the annotations above.
+ This annotation makes sense only for hybrid race detectors. For pure
+ happens-before detectors this is a no-op. For more details see
+ http://code.google.com/p/data-race-test/wiki/PureHappensBeforeVsHybrid . */
+ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \
+ mu)
+
+ /* Opposite to ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX.
+ Instruct the tool to NOT create h-b arcs between Unlock and Lock, even in
+ pure happens-before mode. For a hybrid mode this is a no-op. */
+ #define ANNOTATE_NOT_HAPPENS_BEFORE_MUTEX(mu) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(__FILE__, __LINE__, mu)
+
+ /* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */
+ #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \
+ mu)
+
+ /* -------------------------------------------------------------
+ Annotations useful when defining memory allocators, or when memory that
+ was protected in one way starts to be protected in another. */
+
+ /* Report that a new memory at "address" of size "size" has been allocated.
+ This might be used when the memory has been retrieved from a free list and
+ is about to be reused, or when a the locking discipline for a variable
+ changes. */
+ #define ANNOTATE_NEW_MEMORY(address, size) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(__FILE__, __LINE__, address, \
+ size)
+
+ /* -------------------------------------------------------------
+ Annotations useful when defining FIFO queues that transfer data between
+ threads. */
+
+ /* Report that the producer-consumer queue (such as ProducerConsumerQueue) at
+ address "pcq" has been created. The ANNOTATE_PCQ_* annotations
+ should be used only for FIFO queues. For non-FIFO queues use
+ ANNOTATE_HAPPENS_BEFORE (for put) and ANNOTATE_HAPPENS_AFTER (for get). */
+ #define ANNOTATE_PCQ_CREATE(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(__FILE__, __LINE__, pcq)
+
+ /* Report that the queue at address "pcq" is about to be destroyed. */
+ #define ANNOTATE_PCQ_DESTROY(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(__FILE__, __LINE__, pcq)
+
+ /* Report that we are about to put an element into a FIFO queue at address
+ "pcq". */
+ #define ANNOTATE_PCQ_PUT(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(__FILE__, __LINE__, pcq)
+
+ /* Report that we've just got an element from a FIFO queue at address
+ "pcq". */
+ #define ANNOTATE_PCQ_GET(pcq) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(__FILE__, __LINE__, pcq)
+
+ /* -------------------------------------------------------------
+ Annotations that suppress errors. It is usually better to express the
+ program's synchronization using the other annotations, but these can
+ be used when all else fails. */
+
+ /* Report that we may have a benign race at "pointer", with size
+ "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the
+ point where "pointer" has been allocated, preferably close to the point
+ where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */
+ #define ANNOTATE_BENIGN_RACE(pointer, description) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \
+ pointer, sizeof(*(pointer)), description)
+
+ /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
+ the memory range [address, address+size). */
+ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \
+ address, size, description)
+
+ /* Request the analysis tool to ignore all reads in the current thread
+ until ANNOTATE_IGNORE_READS_END is called.
+ Useful to ignore intentional racey reads, while still checking
+ other reads and all writes.
+ See also ANNOTATE_UNPROTECTED_READ. */
+ #define ANNOTATE_IGNORE_READS_BEGIN() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__)
+
+ /* Stop ignoring reads. */
+ #define ANNOTATE_IGNORE_READS_END() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__)
+
+ /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__)
+
+ /* Stop ignoring writes. */
+ #define ANNOTATE_IGNORE_WRITES_END() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__)
+
+ /* Start ignoring all memory accesses (reads and writes). */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
+ do {\
+ ANNOTATE_IGNORE_READS_BEGIN();\
+ ANNOTATE_IGNORE_WRITES_BEGIN();\
+ }while(0)\
+
+ /* Stop ignoring all memory accesses. */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
+ do {\
+ ANNOTATE_IGNORE_WRITES_END();\
+ ANNOTATE_IGNORE_READS_END();\
+ }while(0)\
+
+ /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore synchronization events:
+ RWLOCK* and CONDVAR*. */
+ #define ANNOTATE_IGNORE_SYNC_BEGIN() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(__FILE__, __LINE__)
+
+ /* Stop ignoring sync events. */
+ #define ANNOTATE_IGNORE_SYNC_END() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(__FILE__, __LINE__)
+
+
+ /* Enable (enable!=0) or disable (enable==0) race detection for all threads.
+ This annotation could be useful if you want to skip expensive race analysis
+ during some period of program execution, e.g. during initialization. */
+ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(__FILE__, __LINE__, \
+ enable)
+
+ /* -------------------------------------------------------------
+ Annotations useful for debugging. */
+
+ /* Request to trace every access to "address". */
+ #define ANNOTATE_TRACE_MEMORY(address) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(__FILE__, __LINE__, address)
+
+ /* Report the current thread name to a race detector. */
+ #define ANNOTATE_THREAD_NAME(name) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(__FILE__, __LINE__, name)
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing locks. They are not
+ normally needed by modules that merely use locks.
+ The "lock" argument is a pointer to the lock object. */
+
+ /* Report that a lock has been created at address "lock". */
+ #define ANNOTATE_RWLOCK_CREATE(lock) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(__FILE__, __LINE__, lock)
+
+ /* Report that the lock at address "lock" is about to be destroyed. */
+ #define ANNOTATE_RWLOCK_DESTROY(lock) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock)
+
+ /* Report that the lock at address "lock" has been acquired.
+ is_w=1 for writer lock, is_w=0 for reader lock. */
+ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(__FILE__, __LINE__, lock, \
+ is_w)
+
+ /* Report that the lock at address "lock" is about to be released. */
+ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(__FILE__, __LINE__, lock, \
+ is_w)
+
+ /* -------------------------------------------------------------
+ Annotations useful when implementing barriers. They are not
+ normally needed by modules that merely use barriers.
+ The "barrier" argument is a pointer to the barrier object. */
+
+ /* Report that the "barrier" has been initialized with initial "count".
+ If 'reinitialization_allowed' is true, initialization is allowed to happen
+ multiple times w/o calling barrier_destroy() */
+ #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(__FILE__, __LINE__, barrier, \
+ count, reinitialization_allowed)
+
+ /* Report that we are about to enter barrier_wait("barrier"). */
+ #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(__FILE__, __LINE__, \
+ barrier)
+
+ /* Report that we just exited barrier_wait("barrier"). */
+ #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(__FILE__, __LINE__, \
+ barrier)
+
+ /* Report that the "barrier" has been destroyed. */
+ #define ANNOTATE_BARRIER_DESTROY(barrier) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(__FILE__, __LINE__, \
+ barrier)
+
+ /* -------------------------------------------------------------
+ Annotations useful for testing race detectors. */
+
+ /* Report that we expect a race on the variable at "address".
+ Use only in unit tests for a race detector. */
+ #define ANNOTATE_EXPECT_RACE(address, description) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(__FILE__, __LINE__, address, \
+ description)
+
+ #define ANNOTATE_FLUSH_EXPECTED_RACES() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(__FILE__, __LINE__)
+
+ /* A no-op. Insert where you like to test the interceptors. */
+ #define ANNOTATE_NO_OP(arg) \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(__FILE__, __LINE__, arg)
+
+ /* Force the race detector to flush its state. The actual effect depends on
+ * the implementation of the detector. */
+ #define ANNOTATE_FLUSH_STATE() \
+ DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(__FILE__, __LINE__)
+
+
+#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
+
+ #define ANNOTATE_RWLOCK_CREATE(lock) /* empty */
+ #define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */
+ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */
+ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */
+ #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) /* */
+ #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) /* empty */
+ #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) /* empty */
+ #define ANNOTATE_BARRIER_DESTROY(barrier) /* empty */
+ #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) /* empty */
+ #define ANNOTATE_CONDVAR_WAIT(cv) /* empty */
+ #define ANNOTATE_CONDVAR_SIGNAL(cv) /* empty */
+ #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) /* empty */
+ #define ANNOTATE_HAPPENS_BEFORE(obj) /* empty */
+ #define ANNOTATE_HAPPENS_AFTER(obj) /* empty */
+ #define ANNOTATE_PUBLISH_MEMORY_RANGE(address, size) /* empty */
+ #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(address, size) /* empty */
+ #define ANNOTATE_SWAP_MEMORY_RANGE(address, size) /* empty */
+ #define ANNOTATE_PCQ_CREATE(pcq) /* empty */
+ #define ANNOTATE_PCQ_DESTROY(pcq) /* empty */
+ #define ANNOTATE_PCQ_PUT(pcq) /* empty */
+ #define ANNOTATE_PCQ_GET(pcq) /* empty */
+ #define ANNOTATE_NEW_MEMORY(address, size) /* empty */
+ #define ANNOTATE_EXPECT_RACE(address, description) /* empty */
+ #define ANNOTATE_FLUSH_EXPECTED_RACES(address, description) /* empty */
+ #define ANNOTATE_BENIGN_RACE(address, description) /* empty */
+ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */
+ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) /* empty */
+ #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) /* empty */
+ #define ANNOTATE_TRACE_MEMORY(arg) /* empty */
+ #define ANNOTATE_THREAD_NAME(name) /* empty */
+ #define ANNOTATE_IGNORE_READS_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_READS_END() /* empty */
+ #define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_WRITES_END() /* empty */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */
+ #define ANNOTATE_IGNORE_SYNC_BEGIN() /* empty */
+ #define ANNOTATE_IGNORE_SYNC_END() /* empty */
+ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */
+ #define ANNOTATE_NO_OP(arg) /* empty */
+ #define ANNOTATE_FLUSH_STATE() /* empty */
+
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
+
+/* Use the macros above rather than using these functions directly. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(
+ const char *file, int line,
+ const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(
+ const char *file, int line,
+ const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(
+ const char *file, int line,
+ const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(
+ const char *file, int line,
+ const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(
+ const char *file, int line, const volatile void *barrier, long count,
+ long reinitialization_allowed) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(
+ const char *file, int line,
+ const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(
+ const char *file, int line,
+ const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(
+ const char *file, int line,
+ const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(
+ const char *file, int line, const volatile void *cv,
+ const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(
+ const char *file, int line,
+ const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(
+ const char *file, int line,
+ const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(
+ const char *file, int line,
+ const volatile void *obj) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(
+ const char *file, int line,
+ const volatile void *obj) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(
+ const char *file, int line,
+ const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(
+ const char *file, int line,
+ const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(
+ const char *file, int line,
+ const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(
+ const char *file, int line,
+ const volatile void *mem, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(
+ const char *file, int line, const volatile void *mem,
+ const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)(
+ const char *file, int line, const volatile void *mem,
+ const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(
+ const char *file, int line, const volatile void *mem, long size,
+ const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(
+ const char *file, int line,
+ const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(
+ const char *file, int line,
+ const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(
+ const char *file, int line,
+ const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(
+ const char *file, int line,
+ const char *name) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(
+ const char *file, int line, int enable) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(
+ const char *file, int line,
+ const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(
+ const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+
+#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1
+/* Return non-zero value if running under valgrind.
+
+ If "valgrind.h" is included into dynamic_annotations.c,
+ the regular valgrind mechanism will be used.
+ See http://valgrind.org/docs/manual/manual-core-adv.html about
+ RUNNING_ON_VALGRIND and other valgrind "client requests".
+ The file "valgrind.h" may be obtained by doing
+ svn co svn://svn.valgrind.org/valgrind/trunk/include
+
+ If for some reason you can't use "valgrind.h" or want to fake valgrind,
+ there are two ways to make this function return non-zero:
+ - Use environment variable: export RUNNING_ON_VALGRIND=1
+ - Make your tool intercept the function RunningOnValgrind() and
+ change its return value.
+ */
+int RunningOnValgrind(void) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
+#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus)
+
+ /* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads.
+
+ Instead of doing
+ ANNOTATE_IGNORE_READS_BEGIN();
+ ... = x;
+ ANNOTATE_IGNORE_READS_END();
+ one can use
+ ... = ANNOTATE_UNPROTECTED_READ(x); */
+ template <class T>
+ inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) {
+ ANNOTATE_IGNORE_READS_BEGIN();
+ T res = x;
+ ANNOTATE_IGNORE_READS_END();
+ return res;
+ }
+ /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */
+ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \
+ namespace { \
+ class static_var ## _annotator { \
+ public: \
+ static_var ## _annotator() { \
+ ANNOTATE_BENIGN_RACE_SIZED(&static_var, \
+ sizeof(static_var), \
+ # static_var ": " description); \
+ } \
+ }; \
+ static static_var ## _annotator the ## static_var ## _annotator;\
+ }
+#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */
+
+ #define ANNOTATE_UNPROTECTED_READ(x) (x)
+ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */
+
+#endif /* DYNAMIC_ANNOTATIONS_ENABLED */
+
+#endif /* __DYNAMIC_ANNOTATIONS_H__ */
diff --git a/security/sandbox/chromium/base/third_party/icu/LICENSE b/security/sandbox/chromium/base/third_party/icu/LICENSE
new file mode 100644
index 0000000000..2882e4ebda
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/icu/LICENSE
@@ -0,0 +1,76 @@
+COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+
+Copyright © 1991-2017 Unicode, Inc. All rights reserved.
+Distributed under the Terms of Use in http://www.unicode.org/copyright.html
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Unicode data files and any associated documentation
+(the "Data Files") or Unicode software and any associated documentation
+(the "Software") to deal in the Data Files or Software
+without restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, and/or sell copies of
+the Data Files or Software, and to permit persons to whom the Data Files
+or Software are furnished to do so, provided that either
+(a) this copyright and permission notice appear with all copies
+of the Data Files or Software, or
+(b) this copyright and permission notice appear in associated
+Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
+DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THE DATA FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale,
+use or other dealings in these Data Files or Software without prior
+written authorization of the copyright holder.
+
+---------------------
+
+Third-Party Software Licenses
+
+This section contains third-party software notices and/or additional
+terms for licensed third-party software components included within ICU
+libraries.
+
+1. ICU License - ICU 1.8.1 to ICU 57.1
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2016 International Business Machines Corporation and others
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+All trademarks and registered trademarks mentioned herein are the
+property of their respective owners.
diff --git a/security/sandbox/chromium/base/third_party/icu/icu_utf.cc b/security/sandbox/chromium/base/third_party/icu/icu_utf.cc
new file mode 100644
index 0000000000..a3262b04d3
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/icu/icu_utf.cc
@@ -0,0 +1,131 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+******************************************************************************
+*
+* Copyright (C) 1999-2012, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+******************************************************************************
+* file name: utf_impl.cpp
+* encoding: UTF-8
+* tab size: 8 (not used)
+* indentation:4
+*
+* created on: 1999sep13
+* created by: Markus W. Scherer
+*
+* This file provides implementation functions for macros in the utfXX.h
+* that would otherwise be too long as macros.
+*/
+
+#include "base/third_party/icu/icu_utf.h"
+
+namespace base_icu {
+
+// source/common/utf_impl.cpp
+
+static const UChar32
+utf8_errorValue[6]={
+ // Same values as UTF8_ERROR_VALUE_1, UTF8_ERROR_VALUE_2, UTF_ERROR_VALUE,
+ // but without relying on the obsolete unicode/utf_old.h.
+ 0x15, 0x9f, 0xffff,
+ 0x10ffff
+};
+
+static UChar32
+errorValue(int32_t count, int8_t strict) {
+ if(strict>=0) {
+ return utf8_errorValue[count];
+ } else if(strict==-3) {
+ return 0xfffd;
+ } else {
+ return CBU_SENTINEL;
+ }
+}
+
+/*
+ * Handle the non-inline part of the U8_NEXT() and U8_NEXT_FFFD() macros
+ * and their obsolete sibling UTF8_NEXT_CHAR_SAFE().
+ *
+ * U8_NEXT() supports NUL-terminated strings indicated via length<0.
+ *
+ * The "strict" parameter controls the error behavior:
+ * <0 "Safe" behavior of U8_NEXT():
+ * -1: All illegal byte sequences yield U_SENTINEL=-1.
+ * -2: Same as -1, except for lenient treatment of surrogate code points as legal.
+ * Some implementations use this for roundtripping of
+ * Unicode 16-bit strings that are not well-formed UTF-16, that is, they
+ * contain unpaired surrogates.
+ * -3: All illegal byte sequences yield U+FFFD.
+ * 0 Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE):
+ * All illegal byte sequences yield a positive code point such that this
+ * result code point would be encoded with the same number of bytes as
+ * the illegal sequence.
+ * >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., TRUE):
+ * Same as the obsolete "safe" behavior, but non-characters are also treated
+ * like illegal sequences.
+ *
+ * Note that a UBool is the same as an int8_t.
+ */
+UChar32
+utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, UChar32 c, UBool strict) {
+ // *pi is one after byte c.
+ int32_t i=*pi;
+ // length can be negative for NUL-terminated strings: Read and validate one byte at a time.
+ if(i==length || c>0xf4) {
+ // end of string, or not a lead byte
+ } else if(c>=0xf0) {
+ // Test for 4-byte sequences first because
+ // U8_NEXT() handles shorter valid sequences inline.
+ uint8_t t1=s[i], t2, t3;
+ c&=7;
+ if(CBU8_IS_VALID_LEAD4_AND_T1(c, t1) &&
+ ++i!=length && (t2=s[i]-0x80)<=0x3f &&
+ ++i!=length && (t3=s[i]-0x80)<=0x3f) {
+ ++i;
+ c=(c<<18)|((t1&0x3f)<<12)|(t2<<6)|t3;
+ // strict: forbid non-characters like U+fffe
+ if(strict<=0 || !CBU_IS_UNICODE_NONCHAR(c)) {
+ *pi=i;
+ return c;
+ }
+ }
+ } else if(c>=0xe0) {
+ c&=0xf;
+ if(strict!=-2) {
+ uint8_t t1=s[i], t2;
+ if(CBU8_IS_VALID_LEAD3_AND_T1(c, t1) &&
+ ++i!=length && (t2=s[i]-0x80)<=0x3f) {
+ ++i;
+ c=(c<<12)|((t1&0x3f)<<6)|t2;
+ // strict: forbid non-characters like U+fffe
+ if(strict<=0 || !CBU_IS_UNICODE_NONCHAR(c)) {
+ *pi=i;
+ return c;
+ }
+ }
+ } else {
+ // strict=-2 -> lenient: allow surrogates
+ uint8_t t1=s[i]-0x80, t2;
+ if(t1<=0x3f && (c>0 || t1>=0x20) &&
+ ++i!=length && (t2=s[i]-0x80)<=0x3f) {
+ *pi=i+1;
+ return (c<<12)|(t1<<6)|t2;
+ }
+ }
+ } else if(c>=0xc2) {
+ uint8_t t1=s[i]-0x80;
+ if(t1<=0x3f) {
+ *pi=i+1;
+ return ((c-0xc0)<<6)|t1;
+ }
+ } // else 0x80<=c<0xc2 is not a lead byte
+
+ /* error handling */
+ c=errorValue(i-*pi, strict);
+ *pi=i;
+ return c;
+}
+
+} // namespace base_icu
diff --git a/security/sandbox/chromium/base/third_party/icu/icu_utf.h b/security/sandbox/chromium/base/third_party/icu/icu_utf.h
new file mode 100644
index 0000000000..2ba82316c2
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/icu/icu_utf.h
@@ -0,0 +1,442 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/*
+******************************************************************************
+*
+* Copyright (C) 1999-2015, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+******************************************************************************
+*/
+
+#ifndef BASE_THIRD_PARTY_ICU_ICU_UTF_H_
+#define BASE_THIRD_PARTY_ICU_ICU_UTF_H_
+
+#include <stdint.h>
+
+namespace base_icu {
+
+// source/common/unicode/umachine.h
+
+/** The ICU boolean type @stable ICU 2.0 */
+typedef int8_t UBool;
+
+/**
+ * Define UChar32 as a type for single Unicode code points.
+ * UChar32 is a signed 32-bit integer (same as int32_t).
+ *
+ * The Unicode code point range is 0..0x10ffff.
+ * All other values (negative or >=0x110000) are illegal as Unicode code points.
+ * They may be used as sentinel values to indicate "done", "error"
+ * or similar non-code point conditions.
+ *
+ * Before ICU 2.4 (Jitterbug 2146), UChar32 was defined
+ * to be wchar_t if that is 32 bits wide (wchar_t may be signed or unsigned)
+ * or else to be uint32_t.
+ * That is, the definition of UChar32 was platform-dependent.
+ *
+ * @see U_SENTINEL
+ * @stable ICU 2.4
+ */
+typedef int32_t UChar32;
+
+/**
+ * This value is intended for sentinel values for APIs that
+ * (take or) return single code points (UChar32).
+ * It is outside of the Unicode code point range 0..0x10ffff.
+ *
+ * For example, a "done" or "error" value in a new API
+ * could be indicated with U_SENTINEL.
+ *
+ * ICU APIs designed before ICU 2.4 usually define service-specific "done"
+ * values, mostly 0xffff.
+ * Those may need to be distinguished from
+ * actual U+ffff text contents by calling functions like
+ * CharacterIterator::hasNext() or UnicodeString::length().
+ *
+ * @return -1
+ * @see UChar32
+ * @stable ICU 2.4
+ */
+#define CBU_SENTINEL (-1)
+
+// source/common/unicode/utf.h
+
+/**
+ * Is this code point a Unicode noncharacter?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_UNICODE_NONCHAR(c) \
+ ((c)>=0xfdd0 && \
+ ((c)<=0xfdef || ((c)&0xfffe)==0xfffe) && (c)<=0x10ffff)
+
+/**
+ * Is c a Unicode code point value (0..U+10ffff)
+ * that can be assigned a character?
+ *
+ * Code points that are not characters include:
+ * - single surrogate code points (U+d800..U+dfff, 2048 code points)
+ * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points)
+ * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points)
+ * - the highest Unicode code point value is U+10ffff
+ *
+ * This means that all code points below U+d800 are character code points,
+ * and that boundary is tested first for performance.
+ *
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_UNICODE_CHAR(c) \
+ ((uint32_t)(c)<0xd800 || \
+ (0xdfff<(c) && (c)<=0x10ffff && !CBU_IS_UNICODE_NONCHAR(c)))
+
+/**
+ * Is this code point a surrogate (U+d800..U+dfff)?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800)
+
+/**
+ * Assuming c is a surrogate code point (U_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 32-bit code point
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
+
+// source/common/unicode/utf8.h
+
+/**
+ * Internal bit vector for 3-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD3_AND_T1.
+ * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
+ * Lead byte E0..EF bits 3..0 are used as byte index,
+ * first trail byte bits 7..5 are used as bit index into that byte.
+ * @see U8_IS_VALID_LEAD3_AND_T1
+ * @internal
+ */
+#define CBU8_LEAD3_T1_BITS "\x20\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x10\x30\x30"
+
+/**
+ * Internal 3-byte UTF-8 validity check.
+ * Non-zero if lead byte E0..EF and first trail byte 00..FF start a valid sequence.
+ * @internal
+ */
+#define CBU8_IS_VALID_LEAD3_AND_T1(lead, t1) (CBU8_LEAD3_T1_BITS[(lead)&0xf]&(1<<((uint8_t)(t1)>>5)))
+
+/**
+ * Internal bit vector for 4-byte UTF-8 validity check, for use in U8_IS_VALID_LEAD4_AND_T1.
+ * Each bit indicates whether one lead byte + first trail byte pair starts a valid sequence.
+ * First trail byte bits 7..4 are used as byte index,
+ * lead byte F0..F4 bits 2..0 are used as bit index into that byte.
+ * @see U8_IS_VALID_LEAD4_AND_T1
+ * @internal
+ */
+#define CBU8_LEAD4_T1_BITS "\x00\x00\x00\x00\x00\x00\x00\x00\x1E\x0F\x0F\x0F\x00\x00\x00\x00"
+
+/**
+ * Internal 4-byte UTF-8 validity check.
+ * Non-zero if lead byte F0..F4 and first trail byte 00..FF start a valid sequence.
+ * @internal
+ */
+#define CBU8_IS_VALID_LEAD4_AND_T1(lead, t1) (CBU8_LEAD4_T1_BITS[(uint8_t)(t1)>>4]&(1<<((lead)&7)))
+
+/**
+ * Function for handling "next code point" with error-checking.
+ *
+ * This is internal since it is not meant to be called directly by external clie
+nts;
+ * however it is U_STABLE (not U_INTERNAL) since it is called by public macros i
+n this
+ * file and thus must remain stable, and should not be hidden when other interna
+l
+ * functions are hidden (otherwise public macros would fail to compile).
+ * @internal
+ */
+UChar32
+utf8_nextCharSafeBody(const uint8_t *s, int32_t *pi, int32_t length, ::base_icu::UChar32 c, ::base_icu::UBool strict);
+
+/**
+ * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)?
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_SINGLE(c) (((c)&0x80)==0)
+
+/**
+ * Is this code unit (byte) a UTF-8 lead byte? (0xC2..0xF4)
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_LEAD(c) ((uint8_t)((c)-0xc2)<=0x32)
+
+/**
+ * Is this code unit (byte) a UTF-8 trail byte? (0x80..0xBF)
+ * @param c 8-bit code unit (byte)
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU8_IS_TRAIL(c) ((int8_t)(c)<-0x40)
+
+/**
+ * How many code units (bytes) are used for the UTF-8 encoding
+ * of this Unicode code point?
+ * @param c 32-bit code point
+ * @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_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).
+ * @return 4
+ * @stable ICU 2.4
+ */
+#define CBU8_MAX_LENGTH 4
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, checks for illegal sequences and for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * The offset may point to the lead byte of a multi-byte sequence,
+ * in which case the macro will read the whole sequence.
+ * 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_t * string
+ * @param i int32_t string offset, must be i<length
+ * @param length int32_t string length
+ * @param c output UChar32 variable, set to <0 in case of an error
+ * @see U8_NEXT_UNSAFE
+ * @stable ICU 2.4
+ */
+#define CBU8_NEXT(s, i, length, c) { \
+ (c)=(uint8_t)(s)[(i)++]; \
+ if(!CBU8_IS_SINGLE(c)) { \
+ uint8_t __t1, __t2; \
+ if( /* handle U+0800..U+FFFF inline */ \
+ (0xe0<=(c) && (c)<0xf0) && \
+ (((i)+1)<(length) || (length)<0) && \
+ CBU8_IS_VALID_LEAD3_AND_T1((c), __t1=(s)[i]) && \
+ (__t2=(s)[(i)+1]-0x80)<=0x3f) { \
+ (c)=(((c)&0xf)<<12)|((__t1&0x3f)<<6)|__t2; \
+ (i)+=2; \
+ } else if( /* handle U+0080..U+07FF inline */ \
+ ((c)<0xe0 && (c)>=0xc2) && \
+ ((i)!=(length)) && \
+ (__t1=(s)[i]-0x80)<=0x3f) { \
+ (c)=(((c)&0x1f)<<6)|__t1; \
+ ++(i); \
+ } else { \
+ /* function call for "complicated" and error cases */ \
+ (c)=::base_icu::utf8_nextCharSafeBody((const uint8_t *)s, &(i), (length), c, -1); \
+ } \
+ } \
+}
+
+/**
+ * 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.
+ * Otherwise, the result is undefined.
+ *
+ * @param s const uint8_t * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see U8_APPEND
+ * @stable ICU 2.4
+ */
+#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); \
+ } \
+}
+
+// source/common/unicode/utf16.h
+
+/**
+ * Does this code unit alone encode a code point (BMP, not a surrogate)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SINGLE(c) !CBU_IS_SURROGATE(c)
+
+/**
+ * Is this code unit a lead surrogate (U+d800..U+dbff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800)
+
+/**
+ * Is this code unit a trail surrogate (U+dc00..U+dfff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00)
+
+/**
+ * Is this code unit a surrogate (U+d800..U+dfff)?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SURROGATE(c) CBU_IS_SURROGATE(c)
+
+/**
+ * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)),
+ * is it a lead surrogate?
+ * @param c 16-bit code unit
+ * @return TRUE or FALSE
+ * @stable ICU 2.4
+ */
+#define CBU16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0)
+
+/**
+ * Helper constant for U16_GET_SUPPLEMENTARY.
+ * @internal
+ */
+#define CBU16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
+
+/**
+ * Get a supplementary code point value (U+10000..U+10ffff)
+ * from its lead and trail surrogates.
+ * The result is undefined if the input values are not
+ * lead and trail surrogates.
+ *
+ * @param lead lead surrogate (U+d800..U+dbff)
+ * @param trail trail surrogate (U+dc00..U+dfff)
+ * @return supplementary code point (U+10000..U+10ffff)
+ * @stable ICU 2.4
+ */
+#define CBU16_GET_SUPPLEMENTARY(lead, trail) \
+ (((::base_icu::UChar32)(lead)<<10UL)+(::base_icu::UChar32)(trail)-CBU16_SURROGATE_OFFSET)
+
+/**
+ * Get the lead surrogate (0xd800..0xdbff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return lead surrogate (U+d800..U+dbff) for supplementary
+ * @stable ICU 2.4
+ */
+#define CBU16_LEAD(supplementary) (::base_icu::UChar)(((supplementary)>>10)+0xd7c0)
+
+/**
+ * Get the trail surrogate (0xdc00..0xdfff) for a
+ * supplementary code point (0x10000..0x10ffff).
+ * @param supplementary 32-bit code point (U+10000..U+10ffff)
+ * @return trail surrogate (U+dc00..U+dfff) for supplementary
+ * @stable ICU 2.4
+ */
+#define CBU16_TRAIL(supplementary) (::base_icu::UChar)(((supplementary)&0x3ff)|0xdc00)
+
+/**
+ * How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
+ * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff).
+ * @param c 32-bit code point
+ * @return 1 or 2
+ * @stable ICU 2.4
+ */
+#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).
+ * @return 2
+ * @stable ICU 2.4
+ */
+#define CBU16_MAX_LENGTH 2
+
+/**
+ * Get a code point from a string at a code point boundary offset,
+ * and advance the offset to the next code point boundary.
+ * (Post-incrementing forward iteration.)
+ * "Safe" macro, handles unpaired surrogates and checks for string boundaries.
+ *
+ * The length can be negative for a NUL-terminated string.
+ *
+ * The offset may point to the lead surrogate unit
+ * for a supplementary code point, in which case the macro will read
+ * the following trail surrogate as well.
+ * If the offset points to a trail surrogate or
+ * to a single, unpaired lead surrogate, then c is set to that unpaired surrogate.
+ *
+ * @param s const UChar * string
+ * @param i string offset, must be i<length
+ * @param length string length
+ * @param c output UChar32 variable
+ * @see U16_NEXT_UNSAFE
+ * @stable ICU 2.4
+ */
+#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.
+ * 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.
+ * Otherwise, the result is undefined.
+ *
+ * @param s const UChar * string buffer
+ * @param i string offset
+ * @param c code point to append
+ * @see U16_APPEND
+ * @stable ICU 2.4
+ */
+#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
+
+#endif // BASE_THIRD_PARTY_ICU_ICU_UTF_H_
diff --git a/security/sandbox/chromium/base/third_party/superfasthash/LICENSE b/security/sandbox/chromium/base/third_party/superfasthash/LICENSE
new file mode 100644
index 0000000000..3c40a3ecd7
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/superfasthash/LICENSE
@@ -0,0 +1,27 @@
+Paul Hsieh OLD BSD license
+
+Copyright (c) 2010, Paul Hsieh
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+* Neither my name, Paul Hsieh, nor the names of any other contributors to the
+ code use may not be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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/superfasthash/README.chromium b/security/sandbox/chromium/base/third_party/superfasthash/README.chromium
new file mode 100644
index 0000000000..d41ed7724a
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/superfasthash/README.chromium
@@ -0,0 +1,29 @@
+Name: Paul Hsieh's SuperFastHash
+Short Name: SuperFastHash
+URL: http://www.azillionmonkeys.com/qed/hash.html
+Version: 0
+Date: 2012-02-21
+License: BSD
+License File: LICENSE
+Security Critical: yes
+
+Description:
+A fast string hashing algorithm.
+
+Local Modifications:
+- Added LICENSE.
+- Added license text as a comment to the top of superfasthash.c.
+- #include <stdint.h> instead of "pstdint.h".
+- #include <stdlib.h>.
+
+The license is a standard 3-clause BSD license with the following minor changes:
+
+"nor the names of its contributors may be used"
+is replaced with:
+"nor the names of any other contributors to the code use may not be used"
+
+and
+
+"IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE"
+is replaced with:
+"IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE"
diff --git a/security/sandbox/chromium/base/third_party/superfasthash/superfasthash.c b/security/sandbox/chromium/base/third_party/superfasthash/superfasthash.c
new file mode 100644
index 0000000000..6e7687e131
--- /dev/null
+++ b/security/sandbox/chromium/base/third_party/superfasthash/superfasthash.c
@@ -0,0 +1,84 @@
+// Copyright (c) 2010, Paul Hsieh
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither my name, Paul Hsieh, nor the names of any other contributors to the
+// code use may not be used to endorse or promote products derived from this
+// software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+
+#include <stdint.h>
+#include <stdlib.h>
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+uint32_t SuperFastHash (const char * data, int len) {
+uint32_t hash = len, tmp;
+int rem;
+
+ if (len <= 0 || data == NULL) return 0;
+
+ rem = len & 3;
+ len >>= 2;
+
+ /* Main loop */
+ for (;len > 0; len--) {
+ hash += get16bits (data);
+ tmp = (get16bits (data+2) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ data += 2*sizeof (uint16_t);
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3: hash += get16bits (data);
+ hash ^= hash << 16;
+ hash ^= ((signed char)data[sizeof (uint16_t)]) << 18;
+ hash += hash >> 11;
+ break;
+ case 2: hash += get16bits (data);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1: hash += (signed char)*data;
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
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 0000000000..41f677bd17
--- /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 0000000000..0bae0aa130
--- /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 <stdarg.h>
+
+/* 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 "**<pid>** " 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/thread_annotations.h b/security/sandbox/chromium/base/thread_annotations.h
new file mode 100644
index 0000000000..fdd32f8490
--- /dev/null
+++ b/security/sandbox/chromium/base/thread_annotations.h
@@ -0,0 +1,264 @@
+// Copyright (c) 2018 The Chromium 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 file contains macro definitions for thread safety annotations
+// that allow developers to document the locking policies of multi-threaded
+// code. The annotations can also help program analysis tools to identify
+// potential thread safety issues.
+//
+// Note that the annotations we use are described as deprecated in the Clang
+// documentation, linked below. E.g. we use EXCLUSIVE_LOCKS_REQUIRED where the
+// Clang docs use REQUIRES.
+//
+// http://clang.llvm.org/docs/ThreadSafetyAnalysis.html
+//
+// We use the deprecated Clang annotations to match Abseil (relevant header
+// linked below) and its ecosystem of libraries. We will follow Abseil with
+// respect to upgrading to more modern annotations.
+//
+// https://github.com/abseil/abseil-cpp/blob/master/absl/base/thread_annotations.h
+//
+// These annotations are implemented using compiler attributes. Using the macros
+// defined here instead of raw attributes allow for portability and future
+// compatibility.
+//
+// When referring to mutexes in the arguments of the attributes, you should
+// use variable names or more complex expressions (e.g. my_object->mutex_)
+// that evaluate to a concrete mutex object whenever possible. If the mutex
+// you want to refer to is not in scope, you may use a member pointer
+// (e.g. &MyClass::mutex_) to refer to a mutex in some (unknown) object.
+
+#ifndef BASE_THREAD_ANNOTATIONS_H_
+#define BASE_THREAD_ANNOTATIONS_H_
+
+#include "base/logging.h"
+#include "build/build_config.h"
+
+#if defined(__clang__)
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
+#else
+#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
+#endif
+
+// GUARDED_BY()
+//
+// Documents if a shared field or global variable needs to be protected by a
+// mutex. GUARDED_BY() allows the user to specify a particular mutex that
+// should be held when accessing the annotated variable.
+//
+// Example:
+//
+// Mutex mu;
+// int p1 GUARDED_BY(mu);
+#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
+
+// PT_GUARDED_BY()
+//
+// Documents if the memory location pointed to by a pointer should be guarded
+// by a mutex when dereferencing the pointer.
+//
+// Example:
+// Mutex mu;
+// int *p1 PT_GUARDED_BY(mu);
+//
+// Note that a pointer variable to a shared memory location could itself be a
+// shared variable.
+//
+// Example:
+//
+// // `q`, guarded by `mu1`, points to a shared memory location that is
+// // guarded by `mu2`:
+// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
+#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+
+// ACQUIRED_AFTER() / ACQUIRED_BEFORE()
+//
+// Documents the acquisition order between locks that can be held
+// simultaneously by a thread. For any two locks that need to be annotated
+// to establish an acquisition order, only one of them needs the annotation.
+// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
+// and ACQUIRED_BEFORE.)
+//
+// Example:
+//
+// Mutex m1;
+// Mutex m2 ACQUIRED_AFTER(m1);
+#define ACQUIRED_AFTER(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
+
+#define ACQUIRED_BEFORE(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
+
+// EXCLUSIVE_LOCKS_REQUIRED() / SHARED_LOCKS_REQUIRED()
+//
+// Documents a function that expects a mutex to be held prior to entry.
+// The mutex is expected to be held both on entry to, and exit from, the
+// function.
+//
+// Example:
+//
+// Mutex mu1, mu2;
+// int a GUARDED_BY(mu1);
+// int b GUARDED_BY(mu2);
+//
+// void foo() EXCLUSIVE_LOCKS_REQUIRED(mu1, mu2) { ... };
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+#define SHARED_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
+// LOCKS_EXCLUDED()
+//
+// Documents the locks acquired in the body of the function. These locks
+// cannot be held when calling this function (as Abseil's `Mutex` locks are
+// non-reentrant).
+#define LOCKS_EXCLUDED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
+
+// LOCK_RETURNED()
+//
+// Documents a function that returns a mutex without acquiring it. For example,
+// a public getter method that returns a pointer to a private mutex should
+// be annotated with LOCK_RETURNED.
+#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
+// LOCKABLE
+//
+// Documents if a class/type is a lockable type (such as the `Mutex` class).
+#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
+
+// SCOPED_LOCKABLE
+//
+// Documents if a class does RAII locking (such as the `MutexLock` class).
+// The constructor should use `LOCK_FUNCTION()` to specify the mutex that is
+// acquired, and the destructor should use `UNLOCK_FUNCTION()` with no
+// arguments; the analysis will assume that the destructor unlocks whatever the
+// constructor locked.
+#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+// EXCLUSIVE_LOCK_FUNCTION()
+//
+// Documents functions that acquire a lock in the body of a function, and do
+// not release it.
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+// SHARED_LOCK_FUNCTION()
+//
+// Documents functions that acquire a shared (reader) lock in the body of a
+// function, and do not release it.
+#define SHARED_LOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+// UNLOCK_FUNCTION()
+//
+// Documents functions that expect a lock to be held on entry to the function,
+// and release it in the body of the function.
+#define UNLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+// EXCLUSIVE_TRYLOCK_FUNCTION() / SHARED_TRYLOCK_FUNCTION()
+//
+// Documents functions that try to acquire a lock, and return success or failure
+// (or a non-boolean value that can be interpreted as a boolean).
+// The first argument should be `true` for functions that return `true` on
+// success, or `false` for functions that return `false` on success. The second
+// argument specifies the mutex that is locked on success. If unspecified, this
+// mutex is assumed to be `this`.
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+#define SHARED_TRYLOCK_FUNCTION(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+// ASSERT_EXCLUSIVE_LOCK() / ASSERT_SHARED_LOCK()
+//
+// Documents functions that dynamically check to see if a lock is held, and fail
+// if it is not held.
+#define ASSERT_EXCLUSIVE_LOCK(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
+
+#define ASSERT_SHARED_LOCK(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
+
+// NO_THREAD_SAFETY_ANALYSIS
+//
+// Turns off thread safety checking within the body of a particular function.
+// This annotation is used to mark functions that are known to be correct, but
+// the locking behavior is more complicated than the analyzer can handle.
+#define NO_THREAD_SAFETY_ANALYSIS \
+ THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
+
+//------------------------------------------------------------------------------
+// Tool-Supplied Annotations
+//------------------------------------------------------------------------------
+
+// TS_UNCHECKED should be placed around lock expressions that are not valid
+// C++ syntax, but which are present for documentation purposes. These
+// annotations will be ignored by the analysis.
+#define TS_UNCHECKED(x) ""
+
+// TS_FIXME is used to mark lock expressions that are not valid C++ syntax.
+// It is used by automated tools to mark and disable invalid expressions.
+// The annotation should either be fixed, or changed to TS_UNCHECKED.
+#define TS_FIXME(x) ""
+
+// Like NO_THREAD_SAFETY_ANALYSIS, this turns off checking within the body of
+// a particular function. However, this attribute is used to mark functions
+// that are incorrect and need to be fixed. It is used by automated tools to
+// avoid breaking the build when the analysis is updated.
+// Code owners are expected to eventually fix the routine.
+#define NO_THREAD_SAFETY_ANALYSIS_FIXME NO_THREAD_SAFETY_ANALYSIS
+
+// Similar to NO_THREAD_SAFETY_ANALYSIS_FIXME, this macro marks a GUARDED_BY
+// annotation that needs to be fixed, because it is producing thread safety
+// warning. It disables the GUARDED_BY.
+#define GUARDED_BY_FIXME(x)
+
+// Disables warnings for a single read operation. This can be used to avoid
+// warnings when it is known that the read is not actually involved in a race,
+// but the compiler cannot confirm that.
+#define TS_UNCHECKED_READ(x) thread_safety_analysis::ts_unchecked_read(x)
+
+namespace thread_safety_analysis {
+
+// Takes a reference to a guarded data member, and returns an unguarded
+// reference.
+template <typename T>
+inline const T& ts_unchecked_read(const T& v) NO_THREAD_SAFETY_ANALYSIS {
+ return v;
+}
+
+template <typename T>
+inline T& ts_unchecked_read(T& v) NO_THREAD_SAFETY_ANALYSIS {
+ return v;
+}
+
+} // namespace thread_safety_analysis
+
+// The above is imported as-is from abseil-cpp. The following Chromium-specific
+// synonyms are added for Chromium concepts (SequenceChecker/ThreadChecker).
+#if DCHECK_IS_ON()
+
+// Equivalent to GUARDED_BY for SequenceChecker/ThreadChecker. Currently,
+// clang's error message "requires holding mutex" is misleading. Usage of this
+// macro is discouraged until the message is updated.
+// TODO(etiennep): Update comment above once clang's error message is updated.
+#define GUARDED_BY_CONTEXT(name) GUARDED_BY(name)
+
+// Equivalent to EXCLUSIVE_LOCKS_REQUIRED for SequenceChecker/ThreadChecker.
+// Currently, clang's error message "requires holding mutex" is misleading.
+// Usage of this macro is discouraged until the message is updated.
+// TODO(etiennep): Update comment above once clang's error message is updated.
+#define VALID_CONTEXT_REQUIRED(name) EXCLUSIVE_LOCKS_REQUIRED(name)
+
+#else // DCHECK_IS_ON()
+
+#define GUARDED_BY_CONTEXT(name)
+#define VALID_CONTEXT_REQUIRED(name)
+
+#endif // DCHECK_IS_ON()
+
+#endif // BASE_THREAD_ANNOTATIONS_H_
diff --git a/security/sandbox/chromium/base/threading/platform_thread.cc b/security/sandbox/chromium/base/threading/platform_thread.cc
new file mode 100644
index 0000000000..ae0a4499e7
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread.cc
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. 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.h"
+
+#include <atomic>
+#include <memory>
+
+#include "base/feature_list.h"
+
+namespace base {
+
+namespace {
+
+// Whether thread priorities should be used. When disabled,
+// PlatformThread::SetCurrentThreadPriority() no-ops.
+const Feature kThreadPrioritiesFeature{"ThreadPriorities",
+ FEATURE_ENABLED_BY_DEFAULT};
+
+// Whether thread priorities should be used.
+//
+// PlatformThread::SetCurrentThreadPriority() doesn't query the state of the
+// feature directly because FeatureList initialization is not always
+// synchronized with PlatformThread::SetCurrentThreadPriority().
+std::atomic<bool> g_use_thread_priorities(true);
+
+} // namespace
+
+// static
+void PlatformThread::SetCurrentThreadPriority(ThreadPriority priority) {
+ if (g_use_thread_priorities.load())
+ SetCurrentThreadPriorityImpl(priority);
+}
+
+namespace internal {
+
+void InitializeThreadPrioritiesFeature() {
+ // A DCHECK is triggered on FeatureList initialization if the state of a
+ // feature has been checked before. To avoid triggering this DCHECK in unit
+ // tests that call this before initializing the FeatureList, only check the
+ // state of the feature if the FeatureList is initialized.
+ if (FeatureList::GetInstance() &&
+ !FeatureList::IsEnabled(kThreadPrioritiesFeature)) {
+ g_use_thread_priorities.store(false);
+ }
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/platform_thread.h b/security/sandbox/chromium/base/threading/platform_thread.h
new file mode 100644
index 0000000000..aa5b9526c1
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread.h
@@ -0,0 +1,259 @@
+// 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.
+
+// WARNING: You should *NOT* be using this class directly. PlatformThread is
+// the low-level platform-specific abstraction to the OS's threading interface.
+// You should instead be using a message-loop driven Thread, see thread.h.
+
+#ifndef BASE_THREADING_PLATFORM_THREAD_H_
+#define BASE_THREADING_PLATFORM_THREAD_H_
+
+#include <stddef.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_types.h"
+#elif defined(OS_FUCHSIA)
+#include <zircon/types.h>
+#elif defined(OS_MACOSX)
+#include <mach/mach_types.h>
+#elif defined(OS_POSIX)
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+namespace base {
+
+// Used for logging. Always an integer value.
+#if defined(OS_WIN)
+typedef DWORD PlatformThreadId;
+#elif defined(OS_FUCHSIA)
+typedef zx_handle_t PlatformThreadId;
+#elif defined(OS_MACOSX)
+typedef mach_port_t PlatformThreadId;
+#elif defined(OS_POSIX)
+typedef pid_t PlatformThreadId;
+#endif
+
+// Used for thread checking and debugging.
+// Meant to be as fast as possible.
+// These are produced by PlatformThread::CurrentRef(), and used to later
+// check if we are on the same thread or not by using ==. These are safe
+// to copy between threads, but can't be copied to another process as they
+// have no meaning there. Also, the internal identifier can be re-used
+// after a thread dies, so a PlatformThreadRef cannot be reliably used
+// to distinguish a new thread from an old, dead thread.
+class PlatformThreadRef {
+ public:
+#if defined(OS_WIN)
+ typedef DWORD RefType;
+#else // OS_POSIX
+ typedef pthread_t RefType;
+#endif
+ constexpr PlatformThreadRef() : id_(0) {}
+
+ explicit constexpr PlatformThreadRef(RefType id) : id_(id) {}
+
+ bool operator==(PlatformThreadRef other) const {
+ return id_ == other.id_;
+ }
+
+ bool operator!=(PlatformThreadRef other) const { return id_ != other.id_; }
+
+ bool is_null() const {
+ return id_ == 0;
+ }
+ private:
+ RefType id_;
+};
+
+// Used to operate on threads.
+class PlatformThreadHandle {
+ public:
+#if defined(OS_WIN)
+ typedef void* Handle;
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ typedef pthread_t Handle;
+#endif
+
+ constexpr PlatformThreadHandle() : handle_(0) {}
+
+ explicit constexpr PlatformThreadHandle(Handle handle) : handle_(handle) {}
+
+ bool is_equal(const PlatformThreadHandle& other) const {
+ return handle_ == other.handle_;
+ }
+
+ bool is_null() const {
+ return !handle_;
+ }
+
+ Handle platform_handle() const {
+ return handle_;
+ }
+
+ private:
+ Handle handle_;
+};
+
+const PlatformThreadId kInvalidThreadId(0);
+
+// Valid values for priority of Thread::Options and SimpleThread::Options, and
+// SetCurrentThreadPriority(), listed in increasing order of importance.
+enum class ThreadPriority : int {
+ // Suitable for threads that shouldn't disrupt high priority work.
+ 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.
+class BASE_EXPORT PlatformThread {
+ public:
+ // Implement this interface to run code on a background thread. Your
+ // ThreadMain method will be called on the newly created thread.
+ class BASE_EXPORT Delegate {
+ public:
+ virtual void ThreadMain() = 0;
+
+ protected:
+ virtual ~Delegate() = default;
+ };
+
+ // Gets the current thread id, which may be useful for logging purposes.
+ static PlatformThreadId CurrentId();
+
+ // Gets the current thread reference, which can be used to check if
+ // we're on the right thread quickly.
+ static PlatformThreadRef CurrentRef();
+
+ // 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.
+ static void YieldCurrentThread();
+
+ // Sleeps for the specified duration (real-time; ignores time overrides).
+ // Note: The sleep duration may be in base::Time or base::TimeTicks, depending
+ // on platform. If you're looking to use this in unit tests testing delayed
+ // tasks, this will be unreliable - instead, use
+ // base::test::TaskEnvironment with MOCK_TIME mode.
+ static void Sleep(base::TimeDelta duration);
+
+ // Sets the thread name visible to debuggers/tools. This will try to
+ // initialize the context for current thread unless it's a WorkerThread.
+ static void SetName(const std::string& name);
+
+ // Gets the thread name, if previously set by SetName.
+ static const char* GetName();
+
+ // Creates a new thread. The |stack_size| parameter can be 0 to indicate
+ // that the default stack size should be used. Upon success,
+ // |*thread_handle| will be assigned a handle to the newly created thread,
+ // and |delegate|'s ThreadMain method will be executed on the newly created
+ // thread.
+ // 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) {
+ 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|.
+ static bool CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority);
+
+ // CreateNonJoinable() does the same thing as Create() except the thread
+ // cannot be Join()'d. Therefore, it also does not output a
+ // PlatformThreadHandle.
+ static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);
+
+ // CreateNonJoinableWithPriority() does the same thing as CreateNonJoinable()
+ // except the priority of the thread is set based on |priority|.
+ static bool CreateNonJoinableWithPriority(size_t stack_size,
+ Delegate* delegate,
+ ThreadPriority priority);
+
+ // Joins with a thread created via the Create function. This function blocks
+ // the caller until the designated thread exits. This will invalidate
+ // |thread_handle|.
+ static void Join(PlatformThreadHandle thread_handle);
+
+ // Detaches and releases the thread handle. The thread is no longer joinable
+ // and |thread_handle| is invalidated after this call.
+ static void Detach(PlatformThreadHandle thread_handle);
+
+ // Returns true if SetCurrentThreadPriority() should be able to increase the
+ // priority of a thread to |priority|.
+ static bool CanIncreaseThreadPriority(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.
+ // A thread may not be able to lower its priority back down after raising it
+ // to REALTIME_AUDIO.
+ //
+ // This function must not be called from the main thread on Mac. This is to
+ // avoid performance regressions (https://crbug.com/601270).
+ //
+ // 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();
+
+#if defined(OS_LINUX)
+ // Toggles a specific thread's priority at runtime. This can be used to
+ // change the priority of a thread in a different process and will fail
+ // if the calling process does not have proper permissions. The
+ // SetCurrentThreadPriority() function above is preferred in favor of
+ // security but on platforms where sandboxed processes are not allowed to
+ // change priority this function exists to allow a non-sandboxed process
+ // to change the priority of sandboxed threads for improved performance.
+ // Warning: Don't use this for a main thread because that will change the
+ // whole thread group's (i.e. process) priority.
+ static void SetThreadPriority(PlatformThreadId thread_id,
+ ThreadPriority priority);
+#endif
+
+ // Returns the default thread stack size set by chrome. If we do not
+ // explicitly set default size then returns 0.
+ static size_t GetDefaultThreadStackSize();
+
+ private:
+ static void SetCurrentThreadPriorityImpl(ThreadPriority priority);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
+};
+
+namespace internal {
+
+// Initializes the "ThreadPriorities" feature. The feature state is only taken
+// into account after this initialization. This initialization must be
+// synchronized with calls to PlatformThread::SetCurrentThreadPriority().
+void InitializeThreadPrioritiesFeature();
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_THREADING_PLATFORM_THREAD_H_
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 0000000000..378a24d0d1
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.cc
@@ -0,0 +1,39 @@
+// 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/containers/adapters.h"
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+int ThreadPriorityToNiceValue(ThreadPriority priority) {
+ for (const auto& pair : kThreadPriorityToNiceValueMap) {
+ if (pair.priority == priority)
+ return pair.nice_value;
+ }
+ NOTREACHED() << "Unknown ThreadPriority";
+ return 0;
+}
+
+ThreadPriority NiceValueToThreadPriority(int nice_value) {
+ // Try to find a priority that best describes |nice_value|. If there isn't
+ // an exact match, this method returns the closest priority whose nice value
+ // is higher (lower priority) than |nice_value|.
+ for (const auto& pair : Reversed(kThreadPriorityToNiceValueMap)) {
+ if (pair.nice_value >= nice_value)
+ return pair.priority;
+ }
+
+ // Reaching here means |nice_value| is more than any of the defined
+ // priorities. The lowest priority is suitable in this case.
+ return ThreadPriority::BACKGROUND;
+}
+
+} // 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 0000000000..d248fa9bfd
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread_internal_posix.h
@@ -0,0 +1,62 @@
+// 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/base_export.h"
+#include "base/optional.h"
+#include "base/threading/platform_thread.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace internal {
+
+struct ThreadPriorityToNiceValuePair {
+ ThreadPriority priority;
+ int nice_value;
+};
+// The elements must be listed in the order of increasing priority (lowest
+// priority first), that is, in the order of decreasing nice values (highest
+// nice value first).
+BASE_EXPORT 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.
+BASE_EXPORT ThreadPriority NiceValueToThreadPriority(int nice_value);
+
+// If non-nullopt, this return value will be used as the platform-specific
+// result of CanIncreaseThreadPriority().
+Optional<bool> CanIncreaseCurrentThreadPriorityForPlatform(
+ ThreadPriority priority);
+
+// 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);
+
+// If non-null, this return value will be used as the platform-specific result
+// of CanIncreaseThreadPriority().
+Optional<ThreadPriority> GetCurrentThreadPriorityForPlatform();
+
+#if defined(OS_LINUX)
+// Current thread id is cached in thread local storage for performance reasons.
+// In some rare cases it's important to clear that cache explicitly (e.g. after
+// going through clone() syscall which does not call pthread_atfork()
+// handlers).
+BASE_EXPORT void ClearTidCache();
+#endif // defined(OS_LINUX)
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_THREADING_PLATFORM_THREAD_INTERNAL_POSIX_H_
diff --git a/security/sandbox/chromium/base/threading/platform_thread_posix.cc b/security/sandbox/chromium/base/threading/platform_thread_posix.cc
new file mode 100644
index 0000000000..335848e329
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread_posix.cc
@@ -0,0 +1,361 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include "base/debug/activity_tracker.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/threading/platform_thread_internal_posix.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "build/build_config.h"
+
+#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA) && !defined(OS_NACL)
+#include "base/posix/can_lower_nice_to.h"
+#endif
+
+#if defined(OS_LINUX)
+#include <sys/syscall.h>
+#endif
+
+#if defined(OS_FUCHSIA)
+#include <zircon/process.h>
+#else
+#include <sys/resource.h>
+#endif
+
+namespace base {
+
+void InitThreading();
+void TerminateOnThread();
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes);
+
+namespace {
+
+struct ThreadParams {
+ ThreadParams()
+ : delegate(nullptr), joinable(false), priority(ThreadPriority::NORMAL) {}
+
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+ ThreadPriority priority;
+};
+
+void* ThreadFunc(void* params) {
+ PlatformThread::Delegate* delegate = nullptr;
+
+ {
+ std::unique_ptr<ThreadParams> thread_params(
+ static_cast<ThreadParams*>(params));
+
+ delegate = thread_params->delegate;
+ if (!thread_params->joinable)
+ base::ThreadRestrictions::SetSingletonAllowed(false);
+
+#if !defined(OS_NACL)
+ // Threads on linux/android may inherit their priority from the thread
+ // where they were created. This explicitly sets the priority of all new
+ // threads.
+ PlatformThread::SetCurrentThreadPriority(thread_params->priority);
+#endif
+ }
+
+ ThreadIdNameManager::GetInstance()->RegisterThread(
+ PlatformThread::CurrentHandle().platform_handle(),
+ PlatformThread::CurrentId());
+
+ delegate->ThreadMain();
+
+ ThreadIdNameManager::GetInstance()->RemoveName(
+ PlatformThread::CurrentHandle().platform_handle(),
+ PlatformThread::CurrentId());
+
+ base::TerminateOnThread();
+ return nullptr;
+}
+
+bool CreateThread(size_t stack_size,
+ bool joinable,
+ PlatformThread::Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ DCHECK(thread_handle);
+ base::InitThreading();
+
+ 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)
+ pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
+
+ // Get a better default if available.
+ if (stack_size == 0)
+ stack_size = base::GetDefaultThreadStackSize(attributes);
+
+ if (stack_size > 0)
+ pthread_attr_setstacksize(&attributes, stack_size);
+
+ std::unique_ptr<ThreadParams> params(new ThreadParams);
+ params->delegate = delegate;
+ params->joinable = joinable;
+ params->priority = priority;
+
+ pthread_t handle;
+ 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);
+
+ return success;
+}
+
+#if defined(OS_LINUX)
+
+// Store the thread ids in local storage since calling the SWI can
+// expensive and PlatformThread::CurrentId is used liberally. Clear
+// the stored value after a fork() because forking changes the thread
+// id. Forking without going through fork() (e.g. clone()) is not
+// supported, but there is no known usage. Using thread_local is
+// fine here (despite being banned) since it is going to be allowed
+// but is blocked on a clang bug for Mac (https://crbug.com/829078)
+// and we can't use ThreadLocalStorage because of re-entrancy due to
+// CHECK/DCHECKs.
+thread_local pid_t g_thread_id = -1;
+
+class InitAtFork {
+ public:
+ InitAtFork() { pthread_atfork(nullptr, nullptr, internal::ClearTidCache); }
+};
+
+#endif // defined(OS_LINUX)
+
+} // namespace
+
+#if defined(OS_LINUX)
+
+namespace internal {
+
+void ClearTidCache() {
+ g_thread_id = -1;
+}
+
+} // namespace internal
+
+#endif // defined(OS_LINUX)
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ // Pthreads doesn't have the concept of a thread ID, so we have to reach down
+ // into the kernel.
+#if defined(OS_MACOSX)
+ return pthread_mach_thread_np(pthread_self());
+#elif defined(OS_LINUX)
+ static NoDestructor<InitAtFork> init_at_fork;
+ if (g_thread_id == -1) {
+ g_thread_id = syscall(__NR_gettid);
+ } else {
+ DCHECK_EQ(g_thread_id, syscall(__NR_gettid))
+ << "Thread id stored in TLS is different from thread id returned by "
+ "the system. It is likely that the process was forked without going "
+ "through fork().";
+ }
+ return g_thread_id;
+#elif defined(OS_ANDROID)
+ return gettid();
+#elif defined(OS_FUCHSIA)
+ return zx_thread_self();
+#elif defined(OS_SOLARIS) || defined(OS_QNX)
+ return pthread_self();
+#elif defined(OS_NACL) && defined(__GLIBC__)
+ return pthread_self();
+#elif defined(OS_NACL) && !defined(__GLIBC__)
+ // Pointers are 32-bits in NaCl.
+ return reinterpret_cast<int32_t>(pthread_self());
+#elif defined(OS_POSIX) && defined(OS_AIX)
+ return pthread_self();
+#elif defined(OS_POSIX) && !defined(OS_AIX)
+ return reinterpret_cast<int64_t>(pthread_self());
+#endif
+}
+
+// static
+PlatformThreadRef PlatformThread::CurrentRef() {
+ return PlatformThreadRef(pthread_self());
+}
+
+// static
+PlatformThreadHandle PlatformThread::CurrentHandle() {
+ return PlatformThreadHandle(pthread_self());
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ sched_yield();
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ struct timespec sleep_time, remaining;
+
+ // Break the duration into seconds and nanoseconds.
+ // NOTE: TimeDelta's microseconds are int64s while timespec's
+ // nanoseconds are longs, so this unpacking must prevent overflow.
+ sleep_time.tv_sec = duration.InSeconds();
+ duration -= TimeDelta::FromSeconds(sleep_time.tv_sec);
+ sleep_time.tv_nsec = duration.InMicroseconds() * 1000; // nanoseconds
+
+ while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
+ sleep_time = remaining;
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return ThreadIdNameManager::GetInstance()->GetName(CurrentId());
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ return CreateThread(stack_size, true /* joinable thread */, delegate,
+ thread_handle, priority);
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ return CreateNonJoinableWithPriority(stack_size, delegate,
+ ThreadPriority::NORMAL);
+}
+
+// static
+bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size,
+ Delegate* delegate,
+ ThreadPriority priority) {
+ PlatformThreadHandle unused;
+
+ bool result = CreateThread(stack_size, false /* non-joinable thread */,
+ delegate, &unused, priority);
+ return result;
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ // Record the event that this thread is blocking upon (for hang diagnosis).
+ base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
+
+ // Joining another thread may block the current thread for a long time, since
+ // the thread referred to by |thread_handle| may still be running long-lived /
+ // blocking tasks.
+ base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
+ FROM_HERE, base::BlockingType::MAY_BLOCK);
+ CHECK_EQ(0, pthread_join(thread_handle.platform_handle(), nullptr));
+}
+
+// static
+void PlatformThread::Detach(PlatformThreadHandle thread_handle) {
+ CHECK_EQ(0, pthread_detach(thread_handle.platform_handle()));
+}
+
+// Mac and Fuchsia have their own Set/GetCurrentThreadPriority()
+// implementations.
+#if !defined(OS_MACOSX) && !defined(OS_FUCHSIA)
+
+// static
+bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) {
+#if defined(OS_NACL)
+ return false;
+#else
+ auto platform_specific_ability =
+ internal::CanIncreaseCurrentThreadPriorityForPlatform(priority);
+ if (platform_specific_ability)
+ return platform_specific_ability.value();
+
+ return internal::CanLowerNiceTo(
+ internal::ThreadPriorityToNiceValue(priority));
+#endif // defined(OS_NACL)
+}
+
+// static
+void PlatformThread::SetCurrentThreadPriorityImpl(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.
+ auto platform_specific_priority =
+ internal::GetCurrentThreadPriorityForPlatform();
+ if (platform_specific_priority)
+ return platform_specific_priority.value();
+
+ // 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) && !defined(OS_FUCHSIA)
+
+// static
+size_t PlatformThread::GetDefaultThreadStackSize() {
+ pthread_attr_t attributes;
+ pthread_attr_init(&attributes);
+ return base::GetDefaultThreadStackSize(attributes);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/platform_thread_win.cc b/security/sandbox/chromium/base/threading/platform_thread_win.cc
new file mode 100644
index 0000000000..0bcf6db247
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread_win.cc
@@ -0,0 +1,463 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread_win.h"
+
+#include <stddef.h>
+
+#include "base/debug/activity_tracker.h"
+#include "base/debug/alias.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/profiler.h"
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/process/memory.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time/time_override.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "build/build_config.h"
+
+#include <windows.h>
+
+namespace base {
+
+namespace {
+
+// The most common value returned by ::GetThreadPriority() after background
+// thread mode is enabled on Windows 7.
+constexpr int kWin7BackgroundThreadModePriority = 4;
+
+// Value sometimes returned by ::GetThreadPriority() after thread priority is
+// set to normal on Windows 7.
+constexpr int kWin7NormalPriority = 3;
+
+// These values are sometimes returned by ::GetThreadPriority().
+constexpr int kWinNormalPriority1 = 5;
+constexpr int kWinNormalPriority2 = 6;
+
+// The information on how to set the thread name comes from
+// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
+const DWORD kVCThreadNameException = 0x406D1388;
+
+typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+
+// The SetThreadDescription API was brought in version 1607 of Windows 10.
+typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread,
+ PCWSTR lpThreadDescription);
+
+// This function has try handling, so it is separated out of its caller.
+void SetNameInternal(PlatformThreadId thread_id, const char* name) {
+ //This function is only used for debugging purposes, as you can find by its caller
+#ifndef __MINGW32__
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = thread_id;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
+ reinterpret_cast<DWORD_PTR*>(&info));
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+#endif
+}
+
+struct ThreadParams {
+ PlatformThread::Delegate* delegate;
+ bool joinable;
+ ThreadPriority priority;
+};
+
+DWORD __stdcall ThreadFunc(void* params) {
+ ThreadParams* thread_params = static_cast<ThreadParams*>(params);
+ PlatformThread::Delegate* delegate = thread_params->delegate;
+ 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;
+ BOOL did_dup = DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &platform_handle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+
+ win::ScopedHandle scoped_platform_handle;
+
+ if (did_dup) {
+ scoped_platform_handle.Set(platform_handle);
+ ThreadIdNameManager::GetInstance()->RegisterThread(
+ scoped_platform_handle.Get(),
+ PlatformThread::CurrentId());
+ }
+
+ delete thread_params;
+ delegate->ThreadMain();
+
+ if (did_dup) {
+ ThreadIdNameManager::GetInstance()->RemoveName(
+ scoped_platform_handle.Get(),
+ PlatformThread::CurrentId());
+ }
+
+ return 0;
+}
+
+// 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,
+ ThreadPriority priority) {
+ unsigned int flags = 0;
+ if (stack_size > 0) {
+ flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+#if defined(ARCH_CPU_32_BITS)
+ } else {
+ // The process stack size is increased to give spaces to |RendererMain| in
+ // |chrome/BUILD.gn|, but keep the default stack size of other threads to
+ // 1MB for the address space pressure.
+ flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+ stack_size = 1024 * 1024;
+#endif
+ }
+
+ ThreadParams* params = new ThreadParams;
+ params->delegate = delegate;
+ 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(nullptr, stack_size, ThreadFunc, params, flags, nullptr);
+
+ if (!thread_handle) {
+ DWORD last_error = ::GetLastError();
+
+ switch (last_error) {
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_COMMITMENT_LIMIT:
+ TerminateBecauseOutOfMemory(stack_size);
+ break;
+
+ default:
+ static auto* last_error_crash_key = debug::AllocateCrashKeyString(
+ "create_thread_last_error", debug::CrashKeySize::Size32);
+ debug::SetCrashKeyString(last_error_crash_key,
+ base::NumberToString(last_error));
+ break;
+ }
+
+ delete params;
+ return false;
+ }
+
+ if (out_thread_handle)
+ *out_thread_handle = PlatformThreadHandle(thread_handle);
+ else
+ CloseHandle(thread_handle);
+ return true;
+}
+
+} // namespace
+
+namespace internal {
+
+void AssertMemoryPriority(HANDLE thread, int memory_priority) {
+#if DCHECK_IS_ON()
+ static const auto get_thread_information_fn =
+ reinterpret_cast<decltype(&::GetThreadInformation)>(::GetProcAddress(
+ ::GetModuleHandle(L"Kernel32.dll"), "GetThreadInformation"));
+
+ if (!get_thread_information_fn) {
+ DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
+ return;
+ }
+
+ MEMORY_PRIORITY_INFORMATION memory_priority_information = {};
+ DCHECK(get_thread_information_fn(thread, ::ThreadMemoryPriority,
+ &memory_priority_information,
+ sizeof(memory_priority_information)));
+
+ DCHECK_EQ(memory_priority,
+ static_cast<int>(memory_priority_information.MemoryPriority));
+#endif
+}
+
+} // namespace internal
+
+// static
+PlatformThreadId PlatformThread::CurrentId() {
+ return ::GetCurrentThreadId();
+}
+
+// static
+PlatformThreadRef PlatformThread::CurrentRef() {
+ return PlatformThreadRef(::GetCurrentThreadId());
+}
+
+// static
+PlatformThreadHandle PlatformThread::CurrentHandle() {
+ return PlatformThreadHandle(::GetCurrentThread());
+}
+
+// static
+void PlatformThread::YieldCurrentThread() {
+ ::Sleep(0);
+}
+
+// static
+void PlatformThread::Sleep(TimeDelta duration) {
+ // When measured with a high resolution clock, Sleep() sometimes returns much
+ // too early. We may need to call it repeatedly to get the desired duration.
+ // PlatformThread::Sleep doesn't support mock-time, so this always uses
+ // real-time.
+ const TimeTicks end = subtle::TimeTicksNowIgnoringOverride() + duration;
+ for (TimeTicks now = subtle::TimeTicksNowIgnoringOverride(); now < end;
+ now = subtle::TimeTicksNowIgnoringOverride()) {
+ ::Sleep(static_cast<DWORD>((end - now).InMillisecondsRoundedUp()));
+ }
+}
+
+// static
+void PlatformThread::SetName(const std::string& name) {
+ ThreadIdNameManager::GetInstance()->SetName(name);
+
+ // The SetThreadDescription API works even if no debugger is attached.
+ static auto set_thread_description_func =
+ reinterpret_cast<SetThreadDescription>(::GetProcAddress(
+ ::GetModuleHandle(L"Kernel32.dll"), "SetThreadDescription"));
+ if (set_thread_description_func) {
+ set_thread_description_func(::GetCurrentThread(),
+ base::UTF8ToWide(name).c_str());
+ }
+
+ // The debugger needs to be around to catch the name in the exception. If
+ // there isn't a debugger, we are just needlessly throwing an exception.
+ if (!::IsDebuggerPresent())
+ return;
+
+ SetNameInternal(CurrentId(), name.c_str());
+}
+
+// static
+const char* PlatformThread::GetName() {
+ return ThreadIdNameManager::GetInstance()->GetName(CurrentId());
+}
+
+// static
+bool PlatformThread::CreateWithPriority(size_t stack_size, Delegate* delegate,
+ PlatformThreadHandle* thread_handle,
+ ThreadPriority priority) {
+ DCHECK(thread_handle);
+ return CreateThreadInternal(stack_size, delegate, thread_handle, priority);
+}
+
+// static
+bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
+ return CreateNonJoinableWithPriority(stack_size, delegate,
+ ThreadPriority::NORMAL);
+}
+
+// static
+bool PlatformThread::CreateNonJoinableWithPriority(size_t stack_size,
+ Delegate* delegate,
+ ThreadPriority priority) {
+ return CreateThreadInternal(stack_size, delegate, nullptr /* non-joinable */,
+ priority);
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ DCHECK(thread_handle.platform_handle());
+
+ DWORD thread_id = 0;
+ thread_id = ::GetThreadId(thread_handle.platform_handle());
+ DWORD last_error = 0;
+ if (!thread_id)
+ last_error = ::GetLastError();
+
+ // Record information about the exiting thread in case joining hangs.
+ base::debug::Alias(&thread_id);
+ base::debug::Alias(&last_error);
+
+ // Record the event that this thread is blocking upon (for hang diagnosis).
+ base::debug::ScopedThreadJoinActivity thread_activity(&thread_handle);
+
+ base::internal::ScopedBlockingCallWithBaseSyncPrimitives scoped_blocking_call(
+ FROM_HERE, base::BlockingType::MAY_BLOCK);
+
+ // Wait for the thread to exit. It should already have terminated but make
+ // sure this assumption is valid.
+ CHECK_EQ(WAIT_OBJECT_0,
+ WaitForSingleObject(thread_handle.platform_handle(), INFINITE));
+ CloseHandle(thread_handle.platform_handle());
+}
+
+// static
+void PlatformThread::Detach(PlatformThreadHandle thread_handle) {
+ CloseHandle(thread_handle.platform_handle());
+}
+
+// static
+bool PlatformThread::CanIncreaseThreadPriority(ThreadPriority priority) {
+ return true;
+}
+
+// static
+void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) {
+ PlatformThreadHandle::Handle thread_handle =
+ PlatformThread::CurrentHandle().platform_handle();
+
+ if (priority != ThreadPriority::BACKGROUND) {
+ // Exit background mode if the new priority is not BACKGROUND. This is a
+ // no-op if not in background mode.
+ ::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END);
+ internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
+ }
+
+ int desired_priority = THREAD_PRIORITY_ERROR_RETURN;
+ switch (priority) {
+ case ThreadPriority::BACKGROUND:
+ // Using THREAD_MODE_BACKGROUND_BEGIN instead of THREAD_PRIORITY_LOWEST
+ // improves input latency and navigation time. See
+ // https://docs.google.com/document/d/16XrOwuwTwKWdgPbcKKajTmNqtB4Am8TgS9GjbzBYLc0
+ //
+ // MSDN recommends THREAD_MODE_BACKGROUND_BEGIN for threads that perform
+ // background work, as it reduces disk and memory priority in addition to
+ // CPU priority.
+ desired_priority = THREAD_MODE_BACKGROUND_BEGIN;
+ break;
+ 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);
+
+#if DCHECK_IS_ON()
+ const BOOL success =
+#endif
+ ::SetThreadPriority(thread_handle, desired_priority);
+ DPLOG_IF(ERROR, !success) << "Failed to set thread priority to "
+ << desired_priority;
+
+ if (priority == ThreadPriority::BACKGROUND) {
+ // In a background process, THREAD_MODE_BACKGROUND_BEGIN lowers the memory
+ // and I/O priorities but not the CPU priority (kernel bug?). Use
+ // THREAD_PRIORITY_LOWEST to also lower the CPU priority.
+ // https://crbug.com/901483
+ if (GetCurrentThreadPriority() != ThreadPriority::BACKGROUND) {
+ ::SetThreadPriority(thread_handle, THREAD_PRIORITY_LOWEST);
+ // Make sure that using THREAD_PRIORITY_LOWEST didn't affect the memory
+ // priority set by THREAD_MODE_BACKGROUND_BEGIN. There is no practical
+ // way to verify the I/O priority.
+ internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
+ }
+ }
+
+ DCHECK_EQ(GetCurrentThreadPriority(), priority);
+}
+
+// static
+ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+ static_assert(
+ THREAD_PRIORITY_IDLE < 0,
+ "THREAD_PRIORITY_IDLE is >= 0 and will incorrectly cause errors.");
+ static_assert(
+ THREAD_PRIORITY_LOWEST < 0,
+ "THREAD_PRIORITY_LOWEST is >= 0 and will incorrectly cause errors.");
+ static_assert(THREAD_PRIORITY_BELOW_NORMAL < 0,
+ "THREAD_PRIORITY_BELOW_NORMAL is >= 0 and will incorrectly "
+ "cause errors.");
+ static_assert(
+ THREAD_PRIORITY_NORMAL == 0,
+ "The logic below assumes that THREAD_PRIORITY_NORMAL is zero. If it is "
+ "not, ThreadPriority::BACKGROUND may be incorrectly detected.");
+ static_assert(THREAD_PRIORITY_ABOVE_NORMAL >= 0,
+ "THREAD_PRIORITY_ABOVE_NORMAL is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+ static_assert(THREAD_PRIORITY_HIGHEST >= 0,
+ "THREAD_PRIORITY_HIGHEST is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+ static_assert(THREAD_PRIORITY_TIME_CRITICAL >= 0,
+ "THREAD_PRIORITY_TIME_CRITICAL is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+ static_assert(THREAD_PRIORITY_ERROR_RETURN >= 0,
+ "THREAD_PRIORITY_ERROR_RETURN is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+
+ const int priority =
+ ::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle());
+
+ // Negative values represent a background priority. We have observed -3, -4,
+ // -6 when THREAD_MODE_BACKGROUND_* is used. THREAD_PRIORITY_IDLE,
+ // THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_BELOW_NORMAL are other possible
+ // negative values.
+ if (priority < THREAD_PRIORITY_NORMAL)
+ return ThreadPriority::BACKGROUND;
+
+ switch (priority) {
+ case kWin7BackgroundThreadModePriority:
+ DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
+ return ThreadPriority::BACKGROUND;
+ case kWin7NormalPriority:
+ DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
+ FALLTHROUGH;
+ case THREAD_PRIORITY_NORMAL:
+ return ThreadPriority::NORMAL;
+ case kWinNormalPriority1:
+ FALLTHROUGH;
+ case kWinNormalPriority2:
+ return ThreadPriority::NORMAL;
+ case THREAD_PRIORITY_ABOVE_NORMAL:
+ case THREAD_PRIORITY_HIGHEST:
+ return ThreadPriority::DISPLAY;
+ case THREAD_PRIORITY_TIME_CRITICAL:
+ return ThreadPriority::REALTIME_AUDIO;
+ case THREAD_PRIORITY_ERROR_RETURN:
+ DPCHECK(false) << "::GetThreadPriority error";
+ }
+
+ NOTREACHED() << "::GetThreadPriority returned " << priority << ".";
+ return ThreadPriority::NORMAL;
+}
+
+// static
+size_t PlatformThread::GetDefaultThreadStackSize() {
+ return 0;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/platform_thread_win.h b/security/sandbox/chromium/base/threading/platform_thread_win.h
new file mode 100644
index 0000000000..3e833178e3
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/platform_thread_win.h
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. 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_WIN_H_
+#define BASE_THREADING_PLATFORM_THREAD_WIN_H_
+
+#include "base/threading/platform_thread.h"
+
+#include "base/base_export.h"
+
+namespace base {
+namespace internal {
+
+// Assert that the memory priority of |thread| is |memory_priority|. No-op on
+// Windows 7 because ::GetThreadInformation() is not available. Exposed for unit
+// tests.
+BASE_EXPORT void AssertMemoryPriority(HANDLE thread, int memory_priority);
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_THREADING_PLATFORM_THREAD_WIN_H_
diff --git a/security/sandbox/chromium/base/threading/thread_checker_impl.h b/security/sandbox/chromium/base/threading/thread_checker_impl.h
new file mode 100644
index 0000000000..b325db6ae8
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_checker_impl.h
@@ -0,0 +1,74 @@
+// 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_THREADING_THREAD_CHECKER_IMPL_H_
+#define BASE_THREADING_THREAD_CHECKER_IMPL_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/sequence_token.h"
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// Real implementation of ThreadChecker, for use in debug mode, or for temporary
+// use in release mode (e.g. to CHECK on a threading issue seen only in the
+// wild).
+//
+// Note: You should almost always use the ThreadChecker class to get the right
+// version for your build configuration.
+// Note: This is only a check, not a "lock". It is marked "LOCKABLE" only in
+// order to support thread_annotations.h.
+class LOCKABLE BASE_EXPORT ThreadCheckerImpl {
+ public:
+ ThreadCheckerImpl();
+ ~ThreadCheckerImpl();
+
+ // Allow move construct/assign. This must be called on |other|'s associated
+ // thread and assignment can only be made into a ThreadCheckerImpl which is
+ // detached or already associated with the current thread. This isn't
+ // thread-safe (|this| and |other| shouldn't be in use while this move is
+ // performed). If the assignment was legal, the resulting ThreadCheckerImpl
+ // will be bound to the current thread and |other| will be detached.
+ ThreadCheckerImpl(ThreadCheckerImpl&& other);
+ ThreadCheckerImpl& operator=(ThreadCheckerImpl&& other);
+
+ 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
+ // exclusively on another thread.
+ void DetachFromThread();
+
+ private:
+ void EnsureAssignedLockRequired() const EXCLUSIVE_LOCKS_REQUIRED(lock_);
+
+ // Members are mutable so that CalledOnValidThread() can set them.
+
+ // Synchronizes access to all members.
+ mutable base::Lock lock_;
+
+ // Thread on which CalledOnValidThread() may return true.
+ mutable PlatformThreadRef thread_id_ GUARDED_BY(lock_);
+
+ // TaskToken for which CalledOnValidThread() always returns true. This allows
+ // CalledOnValidThread() to return true when called multiple times from the
+ // same task, even if it's not running in a single-threaded context itself
+ // (allowing usage of ThreadChecker objects on the stack in the scope of one-
+ // off tasks). Note: CalledOnValidThread() may return true even if the current
+ // TaskToken is not equal to this.
+ mutable TaskToken task_token_ GUARDED_BY(lock_);
+
+ // SequenceToken for which CalledOnValidThread() may return true. Used to
+ // ensure that CalledOnValidThread() doesn't return true for ThreadPool
+ // tasks that happen to run on the same thread but weren't posted to the same
+ // SingleThreadTaskRunner.
+ mutable SequenceToken sequence_token_ GUARDED_BY(lock_);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_CHECKER_IMPL_H_
diff --git a/security/sandbox/chromium/base/threading/thread_collision_warner.cc b/security/sandbox/chromium/base/threading/thread_collision_warner.cc
new file mode 100644
index 0000000000..547e11ca66
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_collision_warner.cc
@@ -0,0 +1,64 @@
+// 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.
+
+#include "base/threading/thread_collision_warner.h"
+
+#include "base/logging.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+void DCheckAsserter::warn() {
+ NOTREACHED() << "Thread Collision";
+}
+
+static subtle::Atomic32 CurrentThread() {
+ const PlatformThreadId current_thread_id = PlatformThread::CurrentId();
+ // We need to get the thread id into an atomic data type. This might be a
+ // truncating conversion, but any loss-of-information just increases the
+ // chance of a fault negative, not a false positive.
+ const subtle::Atomic32 atomic_thread_id =
+ static_cast<subtle::Atomic32>(current_thread_id);
+
+ return atomic_thread_id;
+}
+
+void ThreadCollisionWarner::EnterSelf() {
+ // If the active thread is 0 then I'll write the current thread ID
+ // if two or more threads arrive here only one will succeed to
+ // write on valid_thread_id_ the current thread ID.
+ subtle::Atomic32 current_thread_id = CurrentThread();
+
+ int previous_value = subtle::NoBarrier_CompareAndSwap(&valid_thread_id_,
+ 0,
+ current_thread_id);
+ if (previous_value != 0 && previous_value != current_thread_id) {
+ // gotcha! a thread is trying to use the same class and that is
+ // not current thread.
+ asserter_->warn();
+ }
+
+ subtle::NoBarrier_AtomicIncrement(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Enter() {
+ subtle::Atomic32 current_thread_id = CurrentThread();
+
+ if (subtle::NoBarrier_CompareAndSwap(&valid_thread_id_,
+ 0,
+ current_thread_id) != 0) {
+ // gotcha! another thread is trying to use the same class.
+ asserter_->warn();
+ }
+
+ subtle::NoBarrier_AtomicIncrement(&counter_, 1);
+}
+
+void ThreadCollisionWarner::Leave() {
+ if (subtle::Barrier_AtomicIncrement(&counter_, -1) == 0) {
+ subtle::NoBarrier_Store(&valid_thread_id_, 0);
+ }
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/thread_collision_warner.h b/security/sandbox/chromium/base/threading/thread_collision_warner.h
new file mode 100644
index 0000000000..7f7443b21d
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_collision_warner.h
@@ -0,0 +1,252 @@
+// 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_THREADING_THREAD_COLLISION_WARNER_H_
+#define BASE_THREADING_THREAD_COLLISION_WARNER_H_
+
+#include <memory>
+
+#include "base/atomicops.h"
+#include "base/base_export.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.
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+// are synchronized somehow.
+//
+// In this case the macro DFAKE_SCOPED_LOCK has to be
+// used, it checks that if a thread is inside the push/pop then
+// noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+// public:
+// ...
+// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... }
+// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation non thread-safe but still usable if clients
+// are synchronized somehow, it calls a method to "protect" from
+// a "protected" method
+//
+// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK
+// has to be used, it checks that if a thread is inside the push/pop
+// then noone else is still inside the pop/push
+//
+// class NonThreadSafeQueue {
+// public:
+// void push(int) {
+// DFAKE_SCOPED_LOCK(push_pop_);
+// ...
+// }
+// int pop() {
+// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
+// bar();
+// ...
+// }
+// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Queue implementation not usable even if clients are synchronized,
+// so only one thread in the class life cycle can use the two members
+// push/pop.
+//
+// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the
+// specified
+// critical section the first time a thread enters push or pop, from
+// that time on only that thread is allowed to execute push or pop.
+//
+// class NonThreadSafeQueue {
+// public:
+// ...
+// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(push_pop_);
+// };
+//
+//
+// Example: Class that has to be contructed/destroyed on same thread, it has
+// a "shareable" method (with external synchronization) and a not
+// shareable method (even with external synchronization).
+//
+// In this case 3 Critical sections have to be defined
+//
+// class ExoticClass {
+// public:
+// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+//
+// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... }
+// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
+// ...
+// private:
+// DFAKE_MUTEX(ctor_dtor_);
+// DFAKE_MUTEX(shareable_section_);
+// };
+
+
+#if !defined(NDEBUG)
+
+#define DFAKE_UNIQUE_VARIABLE_CONCAT(a, b) a##b
+// CONCAT1 provides extra level of indirection so that __LINE__ macro expands.
+#define DFAKE_UNIQUE_VARIABLE_CONCAT1(a, b) DFAKE_UNIQUE_VARIABLE_CONCAT(a, b)
+#define DFAKE_UNIQUE_VARIABLE_NAME(a) DFAKE_UNIQUE_VARIABLE_CONCAT1(a, __LINE__)
+
+// Defines a class member that acts like a mutex. It is used only as a
+// verification tool.
+#define DFAKE_MUTEX(obj) \
+ mutable base::ThreadCollisionWarner obj
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope.
+#define DFAKE_SCOPED_LOCK(obj) \
+ base::ThreadCollisionWarner::ScopedCheck DFAKE_UNIQUE_VARIABLE_NAME( \
+ s_check_)(&obj)
+// Asserts the call is never called simultaneously in two threads. Used at
+// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks.
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \
+ base::ThreadCollisionWarner::ScopedRecursiveCheck \
+ DFAKE_UNIQUE_VARIABLE_NAME(sr_check)(&obj)
+// Asserts the code is always executed in the same thread.
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \
+ base::ThreadCollisionWarner::Check DFAKE_UNIQUE_VARIABLE_NAME(check_)(&obj)
+
+#else
+
+#define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj
+#define DFAKE_SCOPED_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0)
+#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0)
+
+#endif
+
+namespace base {
+
+// The class ThreadCollisionWarner uses an Asserter to notify the collision
+// AsserterBase is the interfaces and DCheckAsserter is the default asserter
+// used. During the unit tests is used another class that doesn't "DCHECK"
+// in case of collision (check thread_collision_warner_unittests.cc)
+struct BASE_EXPORT AsserterBase {
+ virtual ~AsserterBase() = default;
+ virtual void warn() = 0;
+};
+
+struct BASE_EXPORT DCheckAsserter : public AsserterBase {
+ ~DCheckAsserter() override = default;
+ void warn() override;
+};
+
+class BASE_EXPORT ThreadCollisionWarner {
+ public:
+ // The parameter asserter is there only for test purpose
+ explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
+ : valid_thread_id_(0),
+ counter_(0),
+ asserter_(asserter) {}
+
+ ~ThreadCollisionWarner() {
+ delete asserter_;
+ }
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED
+ // it doesn't leave the critical section, as opposed to ScopedCheck,
+ // because the critical section being pinned is allowed to be used only
+ // from one thread
+ class BASE_EXPORT Check {
+ public:
+ explicit Check(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->EnterSelf();
+ }
+
+ ~Check() = default;
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(Check);
+ };
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_LOCK
+ class BASE_EXPORT ScopedCheck {
+ public:
+ explicit ScopedCheck(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->Enter();
+ }
+
+ ~ScopedCheck() {
+ warner_->Leave();
+ }
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
+ };
+
+ // This class is meant to be used through the macro
+ // DFAKE_SCOPED_RECURSIVE_LOCK
+ class BASE_EXPORT ScopedRecursiveCheck {
+ public:
+ explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
+ : warner_(warner) {
+ warner_->EnterSelf();
+ }
+
+ ~ScopedRecursiveCheck() {
+ warner_->Leave();
+ }
+
+ private:
+ ThreadCollisionWarner* warner_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck);
+ };
+
+ private:
+ // This method stores the current thread identifier and does a DCHECK
+ // if a another thread has already done it, it is safe if same thread
+ // calls this multiple time (recursion allowed).
+ void EnterSelf();
+
+ // Same as EnterSelf but recursion is not allowed.
+ void Enter();
+
+ // Removes the thread_id stored in order to allow other threads to
+ // call EnterSelf or Enter.
+ void Leave();
+
+ // This stores the thread id that is inside the critical section, if the
+ // value is 0 then no thread is inside.
+ volatile subtle::Atomic32 valid_thread_id_;
+
+ // Counter to trace how many time a critical section was "pinned"
+ // (when allowed) in order to unpin it when counter_ reaches 0.
+ volatile subtle::Atomic32 counter_;
+
+ // Here only for class unit tests purpose, during the test I need to not
+ // DCHECK but notify the collision with something else.
+ AsserterBase* asserter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_
diff --git a/security/sandbox/chromium/base/threading/thread_id_name_manager.cc b/security/sandbox/chromium/base/threading/thread_id_name_manager.cc
new file mode 100644
index 0000000000..ba2f9b41cb
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_id_name_manager.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_id_name_manager.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/no_destructor.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_local.h"
+#include "base/trace_event/heap_profiler_allocation_context_tracker.h"
+
+namespace base {
+namespace {
+
+static const char kDefaultName[] = "";
+static std::string* g_default_name;
+
+ThreadLocalStorage::Slot& GetThreadNameTLS() {
+ static base::NoDestructor<base::ThreadLocalStorage::Slot> thread_name_tls;
+ return *thread_name_tls;
+}
+}
+
+ThreadIdNameManager::Observer::~Observer() = default;
+
+ThreadIdNameManager::ThreadIdNameManager()
+ : main_process_name_(nullptr), main_process_id_(kInvalidThreadId) {
+ g_default_name = new std::string(kDefaultName);
+
+ AutoLock locked(lock_);
+ name_to_interned_name_[kDefaultName] = g_default_name;
+}
+
+ThreadIdNameManager::~ThreadIdNameManager() = default;
+
+ThreadIdNameManager* ThreadIdNameManager::GetInstance() {
+ return Singleton<ThreadIdNameManager,
+ LeakySingletonTraits<ThreadIdNameManager> >::get();
+}
+
+const char* ThreadIdNameManager::GetDefaultInternedString() {
+ return g_default_name->c_str();
+}
+
+void ThreadIdNameManager::RegisterThread(PlatformThreadHandle::Handle handle,
+ PlatformThreadId id) {
+ AutoLock locked(lock_);
+ thread_id_to_handle_[id] = handle;
+ thread_handle_to_interned_name_[handle] =
+ name_to_interned_name_[kDefaultName];
+}
+
+void ThreadIdNameManager::AddObserver(Observer* obs) {
+ AutoLock locked(lock_);
+ DCHECK(!base::Contains(observers_, obs));
+ observers_.push_back(obs);
+}
+
+void ThreadIdNameManager::RemoveObserver(Observer* obs) {
+ AutoLock locked(lock_);
+ DCHECK(base::Contains(observers_, obs));
+ base::Erase(observers_, obs);
+}
+
+void ThreadIdNameManager::SetName(const std::string& name) {
+ PlatformThreadId id = PlatformThread::CurrentId();
+ std::string* leaked_str = nullptr;
+ {
+ AutoLock locked(lock_);
+ auto iter = name_to_interned_name_.find(name);
+ if (iter != name_to_interned_name_.end()) {
+ leaked_str = iter->second;
+ } else {
+ leaked_str = new std::string(name);
+ name_to_interned_name_[name] = leaked_str;
+ }
+
+ auto id_to_handle_iter = thread_id_to_handle_.find(id);
+
+ GetThreadNameTLS().Set(const_cast<char*>(leaked_str->c_str()));
+ for (Observer* obs : observers_)
+ obs->OnThreadNameChanged(leaked_str->c_str());
+
+ // The main thread of a process will not be created as a Thread object which
+ // means there is no PlatformThreadHandler registered.
+ if (id_to_handle_iter == thread_id_to_handle_.end()) {
+ main_process_name_ = leaked_str;
+ main_process_id_ = id;
+ return;
+ }
+ thread_handle_to_interned_name_[id_to_handle_iter->second] = leaked_str;
+ }
+
+ // Add the leaked thread name to heap profiler context tracker. The name added
+ // is valid for the lifetime of the process. AllocationContextTracker cannot
+ // call GetName(which holds a lock) during the first allocation because it can
+ // cause a deadlock when the first allocation happens in the
+ // ThreadIdNameManager itself when holding the lock.
+ trace_event::AllocationContextTracker::SetCurrentThreadName(
+ leaked_str->c_str());
+}
+
+const char* ThreadIdNameManager::GetName(PlatformThreadId id) {
+ AutoLock locked(lock_);
+
+ if (id == main_process_id_)
+ return main_process_name_->c_str();
+
+ auto id_to_handle_iter = thread_id_to_handle_.find(id);
+ if (id_to_handle_iter == thread_id_to_handle_.end())
+ return name_to_interned_name_[kDefaultName]->c_str();
+
+ auto handle_to_name_iter =
+ thread_handle_to_interned_name_.find(id_to_handle_iter->second);
+ return handle_to_name_iter->second->c_str();
+}
+
+const char* ThreadIdNameManager::GetNameForCurrentThread() {
+ const char* name = reinterpret_cast<const char*>(GetThreadNameTLS().Get());
+ return name ? name : kDefaultName;
+}
+
+void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle,
+ PlatformThreadId id) {
+ AutoLock locked(lock_);
+ auto handle_to_name_iter = thread_handle_to_interned_name_.find(handle);
+
+ DCHECK(handle_to_name_iter != thread_handle_to_interned_name_.end());
+ thread_handle_to_interned_name_.erase(handle_to_name_iter);
+
+ auto id_to_handle_iter = thread_id_to_handle_.find(id);
+ DCHECK((id_to_handle_iter!= thread_id_to_handle_.end()));
+ // The given |id| may have been re-used by the system. Make sure the
+ // mapping points to the provided |handle| before removal.
+ if (id_to_handle_iter->second != handle)
+ return;
+
+ thread_id_to_handle_.erase(id_to_handle_iter);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/thread_id_name_manager.h b/security/sandbox/chromium/base/threading/thread_id_name_manager.h
new file mode 100644
index 0000000000..e413da5d03
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_id_name_manager.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_THREAD_ID_NAME_MANAGER_H_
+#define BASE_THREADING_THREAD_ID_NAME_MANAGER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+template <typename T>
+struct DefaultSingletonTraits;
+
+class BASE_EXPORT ThreadIdNameManager {
+ public:
+ static ThreadIdNameManager* GetInstance();
+
+ static const char* GetDefaultInternedString();
+
+ class BASE_EXPORT Observer {
+ public:
+ virtual ~Observer();
+
+ // Called on the thread whose name is changing, immediately after the name
+ // is set. |name| is a pointer to a C string that is guaranteed to remain
+ // valid for the duration of the process.
+ //
+ // NOTE: Will be called while ThreadIdNameManager's lock is held, so don't
+ // call back into it.
+ virtual void OnThreadNameChanged(const char* name) = 0;
+ };
+
+ // Register the mapping between a thread |id| and |handle|.
+ void RegisterThread(PlatformThreadHandle::Handle handle, PlatformThreadId id);
+
+ void AddObserver(Observer*);
+ void RemoveObserver(Observer*);
+
+ // Set the name for the current thread.
+ void SetName(const std::string& name);
+
+ // Get the name for the given id.
+ const char* GetName(PlatformThreadId id);
+
+ // Unlike |GetName|, this method using TLS and avoids touching |lock_|.
+ const char* GetNameForCurrentThread();
+
+ // Remove the name for the given id.
+ void RemoveName(PlatformThreadHandle::Handle handle, PlatformThreadId id);
+
+ private:
+ friend struct DefaultSingletonTraits<ThreadIdNameManager>;
+
+ typedef std::map<PlatformThreadId, PlatformThreadHandle::Handle>
+ ThreadIdToHandleMap;
+ typedef std::map<PlatformThreadHandle::Handle, std::string*>
+ ThreadHandleToInternedNameMap;
+ typedef std::map<std::string, std::string*> NameToInternedNameMap;
+
+ ThreadIdNameManager();
+ ~ThreadIdNameManager();
+
+ // lock_ protects the name_to_interned_name_, thread_id_to_handle_ and
+ // thread_handle_to_interned_name_ maps.
+ Lock lock_;
+
+ NameToInternedNameMap name_to_interned_name_;
+ ThreadIdToHandleMap thread_id_to_handle_;
+ ThreadHandleToInternedNameMap thread_handle_to_interned_name_;
+
+ // Treat the main process specially as there is no PlatformThreadHandle.
+ std::string* main_process_name_;
+ PlatformThreadId main_process_id_;
+
+ // There's no point using a base::ObserverList behind a lock, so we just use
+ // an std::vector instead.
+ std::vector<Observer*> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadIdNameManager);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_ID_NAME_MANAGER_H_
diff --git a/security/sandbox/chromium/base/threading/thread_local.h b/security/sandbox/chromium/base/threading/thread_local.h
new file mode 100644
index 0000000000..f9762050b6
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_local.h
@@ -0,0 +1,136 @@
+// 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.
+
+// WARNING: Thread local storage is a bit tricky to get right. Please make sure
+// that this is really the proper solution for what you're trying to achieve.
+// Don't prematurely optimize, most likely you can just use a Lock.
+//
+// These classes implement a wrapper around ThreadLocalStorage::Slot. On
+// construction, they will allocate a TLS slot, and free the TLS slot on
+// destruction. No memory management (creation or destruction) is handled. This
+// means for uses of ThreadLocalPointer, you must correctly manage the memory
+// yourself, these classes will not destroy the pointer for you. There are no
+// at-thread-exit actions taken by these classes.
+//
+// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or
+// destruction, so memory management must be handled elsewhere. The first call
+// to Get() on a thread will return NULL. You can update the pointer with a call
+// to Set().
+//
+// ThreadLocalBoolean wraps a bool. It will default to false if it has never
+// been set otherwise with Set().
+//
+// Thread Safety: An instance of ThreadLocalStorage is completely thread safe
+// once it has been created. If you want to dynamically create an instance, you
+// must of course properly deal with safety and race conditions.
+//
+// In Android, the system TLS is limited.
+//
+// Example usage:
+// // My class is logically attached to a single thread. We cache a pointer
+// // on the thread it was created on, so we can implement current().
+// MyClass::MyClass() {
+// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL);
+// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this);
+// }
+//
+// MyClass::~MyClass() {
+// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL);
+// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL);
+// }
+//
+// // Return the current MyClass associated with the calling thread, can be
+// // NULL if there isn't a MyClass associated.
+// MyClass* MyClass::current() {
+// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get();
+// }
+
+#ifndef BASE_THREADING_THREAD_LOCAL_H_
+#define BASE_THREADING_THREAD_LOCAL_H_
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/threading/thread_local_internal.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace base {
+
+template <typename T>
+class ThreadLocalPointer {
+ public:
+ ThreadLocalPointer() = default;
+ ~ThreadLocalPointer() = default;
+
+ T* Get() const { return static_cast<T*>(slot_.Get()); }
+
+ void Set(T* ptr) {
+ slot_.Set(const_cast<void*>(static_cast<const void*>(ptr)));
+ }
+
+ private:
+ ThreadLocalStorage::Slot slot_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<T>);
+};
+
+// A ThreadLocalOwnedPointer<T> is like a ThreadLocalPointer<T> except that
+// pointers handed to it are owned and automatically deleted during their
+// associated thread's exit phase (or when replaced if Set() is invoked multiple
+// times on the same thread).
+// The ThreadLocalOwnedPointer instance itself can only be destroyed when no
+// threads, other than the one it is destroyed on, have remaining state set in
+// it. Typically this means that ThreadLocalOwnedPointer instances are held in
+// static storage or at the very least only recycled in the single-threaded
+// phase between tests in the same process.
+#if DCHECK_IS_ON()
+template <typename T>
+using ThreadLocalOwnedPointer = internal::CheckedThreadLocalOwnedPointer<T>;
+#else // DCHECK_IS_ON()
+template <typename T>
+class ThreadLocalOwnedPointer {
+ public:
+ ThreadLocalOwnedPointer() = default;
+
+ ~ThreadLocalOwnedPointer() {
+ // Assume that this thread is the only one with potential state left. This
+ // is verified in ~CheckedThreadLocalOwnedPointer().
+ Set(nullptr);
+ }
+
+ T* Get() const { return static_cast<T*>(slot_.Get()); }
+
+ void Set(std::unique_ptr<T> ptr) {
+ delete Get();
+ slot_.Set(const_cast<void*>(static_cast<const void*>(ptr.release())));
+ }
+
+ private:
+ static void DeleteTlsPtr(void* ptr) { delete static_cast<T*>(ptr); }
+
+ ThreadLocalStorage::Slot slot_{&DeleteTlsPtr};
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalOwnedPointer<T>);
+};
+#endif // DCHECK_IS_ON()
+
+class ThreadLocalBoolean {
+ public:
+ ThreadLocalBoolean() = default;
+ ~ThreadLocalBoolean() = default;
+
+ bool Get() const { return tlp_.Get() != nullptr; }
+
+ void Set(bool val) { tlp_.Set(val ? this : nullptr); }
+
+ private:
+ ThreadLocalPointer<void> tlp_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_LOCAL_H_
diff --git a/security/sandbox/chromium/base/threading/thread_local_internal.h b/security/sandbox/chromium/base/threading/thread_local_internal.h
new file mode 100644
index 0000000000..6f7fdc9768
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_local_internal.h
@@ -0,0 +1,80 @@
+// Copyright 2019 The Chromium Authors. 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_THREAD_LOCAL_INTERNAL_H_
+#define BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
+
+#if DCHECK_IS_ON()
+
+#include <atomic>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/threading/thread_local_storage.h"
+
+namespace base {
+namespace internal {
+
+// A version of ThreadLocalOwnedPointer which verifies that it's only destroyed
+// when no threads, other than the one it is destroyed on, have remaining state
+// set in it. A ThreadLocalOwnedPointer instance being destroyed too early would
+// result in leaks per unregistering the TLS slot (and thus the DeleteTlsPtr
+// hook).
+template <typename T>
+class CheckedThreadLocalOwnedPointer {
+ public:
+ CheckedThreadLocalOwnedPointer() = default;
+
+ ~CheckedThreadLocalOwnedPointer() {
+ Set(nullptr);
+
+ DCHECK_EQ(num_assigned_threads_.load(std::memory_order_relaxed), 0)
+ << "Memory leak: Must join all threads or release all associated "
+ "thread-local slots before ~ThreadLocalOwnedPointer";
+ }
+
+ T* Get() const {
+ PtrTracker* const ptr_tracker = static_cast<PtrTracker*>(slot_.Get());
+ return ptr_tracker ? ptr_tracker->ptr_.get() : nullptr;
+ }
+
+ void Set(std::unique_ptr<T> ptr) {
+ delete static_cast<PtrTracker*>(slot_.Get());
+ if (ptr)
+ slot_.Set(new PtrTracker(this, std::move(ptr)));
+ else
+ slot_.Set(nullptr);
+ }
+
+ private:
+ struct PtrTracker {
+ public:
+ PtrTracker(CheckedThreadLocalOwnedPointer<T>* outer, std::unique_ptr<T> ptr)
+ : outer_(outer), ptr_(std::move(ptr)) {
+ outer_->num_assigned_threads_.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ ~PtrTracker() {
+ outer_->num_assigned_threads_.fetch_sub(1, std::memory_order_relaxed);
+ }
+
+ CheckedThreadLocalOwnedPointer<T>* const outer_;
+ const std::unique_ptr<T> ptr_;
+ };
+
+ static void DeleteTlsPtr(void* ptr) { delete static_cast<PtrTracker*>(ptr); }
+
+ ThreadLocalStorage::Slot slot_{&DeleteTlsPtr};
+
+ std::atomic_int num_assigned_threads_{0};
+
+ DISALLOW_COPY_AND_ASSIGN(CheckedThreadLocalOwnedPointer<T>);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // DCHECK_IS_ON()
+
+#endif // BASE_THREADING_THREAD_LOCAL_INTERNAL_H_
diff --git a/security/sandbox/chromium/base/threading/thread_local_storage.cc b/security/sandbox/chromium/base/threading/thread_local_storage.cc
new file mode 100644
index 0000000000..204f34c272
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_local_storage.cc
@@ -0,0 +1,461 @@
+// 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 "base/threading/thread_local_storage.h"
+
+#include "base/atomicops.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+
+using base::internal::PlatformThreadLocalStorage;
+
+// Chrome Thread Local Storage (TLS)
+//
+// This TLS system allows Chrome to use a single OS level TLS slot process-wide,
+// and allows us to control the slot limits instead of being at the mercy of the
+// platform. To do this, Chrome TLS replicates an array commonly found in the OS
+// thread metadata.
+//
+// Overview:
+//
+// OS TLS Slots Per-Thread Per-Process Global
+// ...
+// [] Chrome TLS Array Chrome TLS Metadata
+// [] ----------> [][][][][ ][][][][] [][][][][ ][][][][]
+// [] | |
+// ... V V
+// Metadata Version Slot Information
+// Your Data!
+//
+// Using a single OS TLS slot, Chrome TLS allocates an array on demand for the
+// lifetime of each thread that requests Chrome TLS data. Each per-thread TLS
+// array matches the length of the per-process global metadata array.
+//
+// A per-process global TLS metadata array tracks information about each item in
+// the per-thread array:
+// * Status: Tracks if the slot is allocated or free to assign.
+// * Destructor: An optional destructor to call on thread destruction for that
+// specific slot.
+// * Version: Tracks the current version of the TLS slot. Each TLS slot
+// allocation is associated with a unique version number.
+//
+// Most OS TLS APIs guarantee that a newly allocated TLS slot is
+// initialized to 0 for all threads. The Chrome TLS system provides
+// this guarantee by tracking the version for each TLS slot here
+// on each per-thread Chrome TLS array entry. Threads that access
+// a slot with a mismatched version will receive 0 as their value.
+// The metadata version is incremented when the client frees a
+// slot. The per-thread metadata version is updated when a client
+// writes to the slot. This scheme allows for constant time
+// invalidation and avoids the need to iterate through each Chrome
+// TLS array to mark the slot as zero.
+//
+// Just like an OS TLS API, clients of the Chrome TLS are responsible for
+// managing any necessary lifetime of the data in their slots. The only
+// convenience provided is automatic destruction when a thread ends. If a client
+// frees a slot, that client is responsible for destroying the data in the slot.
+
+namespace {
+// In order to make TLS destructors work, we need to keep around a function
+// pointer to the destructor for each slot. We keep this array of pointers in a
+// global (static) array.
+// We use the single OS-level TLS slot (giving us one pointer per thread) to
+// hold a pointer to a per-thread array (table) of slots that we allocate to
+// Chromium consumers.
+
+// g_native_tls_key is the one native TLS that we use. It stores our table.
+base::subtle::Atomic32 g_native_tls_key =
+ PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES;
+
+// The OS TLS slot has the following states. The TLS slot's lower 2 bits contain
+// the state, the upper bits the TlsVectorEntry*.
+// * kUninitialized: Any call to Slot::Get()/Set() will create the base
+// per-thread TLS state. kUninitialized must be null.
+// * kInUse: value has been created and is in use.
+// * kDestroying: Set when the thread is exiting prior to deleting any of the
+// values stored in the TlsVectorEntry*. This state is necessary so that
+// sequence/task checks won't be done while in the process of deleting the
+// tls entries (see comments in SequenceCheckerImpl for more details).
+// * kDestroyed: All of the values in the vector have been deallocated and
+// the TlsVectorEntry has been deleted.
+//
+// Final States:
+// * Windows: kDestroyed. Windows does not iterate through the OS TLS to clean
+// up the values.
+// * POSIX: kUninitialized. POSIX iterates through TLS until all slots contain
+// nullptr.
+//
+// More details on this design:
+// We need some type of thread-local state to indicate that the TLS system has
+// been destroyed. To do so, we leverage the multi-pass nature of destruction
+// of pthread_key.
+//
+// a) After destruction of TLS system, we set the pthread_key to a sentinel
+// kDestroyed.
+// b) All calls to Slot::Get() DCHECK that the state is not kDestroyed, and
+// any system which might potentially invoke Slot::Get() after destruction
+// of TLS must check ThreadLocalStorage::ThreadIsBeingDestroyed().
+// c) After a full pass of the pthread_keys, on the next invocation of
+// ConstructTlsVector(), we'll then set the key to nullptr.
+// d) At this stage, the TLS system is back in its uninitialized state.
+// e) If in the second pass of destruction of pthread_keys something were to
+// re-initialize TLS [this should never happen! Since the only code which
+// uses Chrome TLS is Chrome controlled, we should really be striving for
+// single-pass destruction], then TLS will be re-initialized and then go
+// through the 2-pass destruction system again. Everything should just
+// work (TM).
+
+// The state of the tls-entry.
+enum class TlsVectorState {
+ kUninitialized = 0,
+
+ // In the process of destroying the entries in the vector.
+ kDestroying,
+
+ // All of the entries and the vector has been destroyed.
+ kDestroyed,
+
+ // The vector has been initialized and is in use.
+ kInUse,
+
+ kMaxValue = kInUse
+};
+
+// Bit-mask used to store TlsVectorState.
+constexpr uintptr_t kVectorStateBitMask = 3;
+static_assert(static_cast<int>(TlsVectorState::kMaxValue) <=
+ kVectorStateBitMask,
+ "number of states must fit in header");
+static_assert(static_cast<int>(TlsVectorState::kUninitialized) == 0,
+ "kUninitialized must be null");
+
+// The maximum number of slots in our thread local storage stack.
+constexpr int kThreadLocalStorageSize = 256;
+
+enum TlsStatus {
+ FREE,
+ IN_USE,
+};
+
+struct TlsMetadata {
+ TlsStatus status;
+ base::ThreadLocalStorage::TLSDestructorFunc destructor;
+ // Incremented every time a slot is reused. Used to detect reuse of slots.
+ uint32_t version;
+};
+
+struct TlsVectorEntry {
+ void* data;
+ uint32_t version;
+};
+
+// This lock isn't needed until after we've constructed the per-thread TLS
+// vector, so it's safe to use.
+base::Lock* GetTLSMetadataLock() {
+ static auto* lock = new base::Lock();
+ return lock;
+}
+TlsMetadata g_tls_metadata[kThreadLocalStorageSize];
+size_t g_last_assigned_slot = 0;
+
+// The maximum number of times to try to clear slots by calling destructors.
+// Use pthread naming convention for clarity.
+constexpr int kMaxDestructorIterations = kThreadLocalStorageSize;
+
+// Sets the value and state of the vector.
+void SetTlsVectorValue(PlatformThreadLocalStorage::TLSKey key,
+ TlsVectorEntry* tls_data,
+ TlsVectorState state) {
+ DCHECK(tls_data || (state == TlsVectorState::kUninitialized) ||
+ (state == TlsVectorState::kDestroyed));
+ PlatformThreadLocalStorage::SetTLSValue(
+ key, reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(tls_data) |
+ static_cast<uintptr_t>(state)));
+}
+
+// Returns the tls vector and current state from the raw tls value.
+TlsVectorState GetTlsVectorStateAndValue(void* tls_value,
+ TlsVectorEntry** entry = nullptr) {
+ if (entry) {
+ *entry = reinterpret_cast<TlsVectorEntry*>(
+ reinterpret_cast<uintptr_t>(tls_value) & ~kVectorStateBitMask);
+ }
+ return static_cast<TlsVectorState>(reinterpret_cast<uintptr_t>(tls_value) &
+ kVectorStateBitMask);
+}
+
+// Returns the tls vector and state using the tls key.
+TlsVectorState GetTlsVectorStateAndValue(PlatformThreadLocalStorage::TLSKey key,
+ TlsVectorEntry** entry = nullptr) {
+ return GetTlsVectorStateAndValue(PlatformThreadLocalStorage::GetTLSValue(key),
+ entry);
+}
+
+// This function is called to initialize our entire Chromium TLS system.
+// It may be called very early, and we need to complete most all of the setup
+// (initialization) before calling *any* memory allocator functions, which may
+// recursively depend on this initialization.
+// As a result, we use Atomics, and avoid anything (like a singleton) that might
+// require memory allocations.
+TlsVectorEntry* ConstructTlsVector() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
+ CHECK(PlatformThreadLocalStorage::AllocTLS(&key));
+
+ // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or
+ // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we
+ // define an almost impossible value be it.
+ // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc
+ // another TLS slot.
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
+ PlatformThreadLocalStorage::TLSKey tmp = key;
+ CHECK(PlatformThreadLocalStorage::AllocTLS(&key) &&
+ key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES);
+ PlatformThreadLocalStorage::FreeTLS(tmp);
+ }
+ // Atomically test-and-set the tls_key. If the key is
+ // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as
+ // another thread already did our dirty work.
+ if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES !=
+ static_cast<PlatformThreadLocalStorage::TLSKey>(
+ base::subtle::NoBarrier_CompareAndSwap(
+ &g_native_tls_key,
+ PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key))) {
+ // We've been shortcut. Another thread replaced g_native_tls_key first so
+ // we need to destroy our index and use the one the other thread got
+ // first.
+ PlatformThreadLocalStorage::FreeTLS(key);
+ key = base::subtle::NoBarrier_Load(&g_native_tls_key);
+ }
+ }
+ CHECK_EQ(GetTlsVectorStateAndValue(key), TlsVectorState::kUninitialized);
+
+ // Some allocators, such as TCMalloc, make use of thread local storage. As a
+ // result, any attempt to call new (or malloc) will lazily cause such a system
+ // to initialize, which will include registering for a TLS key. If we are not
+ // careful here, then that request to create a key will call new back, and
+ // we'll have an infinite loop. We avoid that as follows: Use a stack
+ // allocated vector, so that we don't have dependence on our allocator until
+ // our service is in place. (i.e., don't even call new until after we're
+ // setup)
+ TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize];
+ memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
+ // Ensure that any rentrant calls change the temp version.
+ SetTlsVectorValue(key, stack_allocated_tls_data, TlsVectorState::kInUse);
+
+ // Allocate an array to store our data.
+ TlsVectorEntry* tls_data = new TlsVectorEntry[kThreadLocalStorageSize];
+ memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
+ SetTlsVectorValue(key, tls_data, TlsVectorState::kInUse);
+ return tls_data;
+}
+
+void OnThreadExitInternal(TlsVectorEntry* tls_data) {
+ DCHECK(tls_data);
+ // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
+ // terminates, one of the destructor calls we make may be to shut down an
+ // allocator. We have to be careful that after we've shutdown all of the known
+ // destructors (perchance including an allocator), that we don't call the
+ // allocator and cause it to resurrect itself (with no possibly destructor
+ // call to follow). We handle this problem as follows: Switch to using a stack
+ // allocated vector, so that we don't have dependence on our allocator after
+ // we have called all g_tls_metadata destructors. (i.e., don't even call
+ // delete[] after we're done with destructors.)
+ TlsVectorEntry stack_allocated_tls_data[kThreadLocalStorageSize];
+ memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
+ // Ensure that any re-entrant calls change the temp version.
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ SetTlsVectorValue(key, stack_allocated_tls_data, TlsVectorState::kDestroying);
+ delete[] tls_data; // Our last dependence on an allocator.
+
+ // Snapshot the TLS Metadata so we don't have to lock on every access.
+ TlsMetadata tls_metadata[kThreadLocalStorageSize];
+ {
+ base::AutoLock auto_lock(*GetTLSMetadataLock());
+ memcpy(tls_metadata, g_tls_metadata, sizeof(g_tls_metadata));
+ }
+
+ int remaining_attempts = kMaxDestructorIterations;
+ bool need_to_scan_destructors = true;
+ while (need_to_scan_destructors) {
+ need_to_scan_destructors = false;
+ // Try to destroy the first-created-slot (which is slot 1) in our last
+ // destructor call. That user was able to function, and define a slot with
+ // no other services running, so perhaps it is a basic service (like an
+ // allocator) and should also be destroyed last. If we get the order wrong,
+ // then we'll iterate several more times, so it is really not that critical
+ // (but it might help).
+ for (int slot = 0; slot < kThreadLocalStorageSize; ++slot) {
+ void* tls_value = stack_allocated_tls_data[slot].data;
+ if (!tls_value || tls_metadata[slot].status == TlsStatus::FREE ||
+ stack_allocated_tls_data[slot].version != tls_metadata[slot].version)
+ continue;
+
+ base::ThreadLocalStorage::TLSDestructorFunc destructor =
+ tls_metadata[slot].destructor;
+ if (!destructor)
+ continue;
+ stack_allocated_tls_data[slot].data = nullptr; // pre-clear the slot.
+ destructor(tls_value);
+ // Any destructor might have called a different service, which then set a
+ // different slot to a non-null value. Hence we need to check the whole
+ // vector again. This is a pthread standard.
+ need_to_scan_destructors = true;
+ }
+ if (--remaining_attempts <= 0) {
+ NOTREACHED(); // Destructors might not have been called.
+ break;
+ }
+ }
+
+ // Remove our stack allocated vector.
+ SetTlsVectorValue(key, nullptr, TlsVectorState::kDestroyed);
+}
+
+} // namespace
+
+namespace base {
+
+namespace internal {
+
+#if defined(OS_WIN)
+void PlatformThreadLocalStorage::OnThreadExit() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
+ return;
+ TlsVectorEntry* tls_vector = nullptr;
+ const TlsVectorState state = GetTlsVectorStateAndValue(key, &tls_vector);
+
+ // On Windows, thread destruction callbacks are only invoked once per module,
+ // so there should be no way that this could be invoked twice.
+ DCHECK_NE(state, TlsVectorState::kDestroyed);
+
+ // Maybe we have never initialized TLS for this thread.
+ if (state == TlsVectorState::kUninitialized)
+ return;
+ OnThreadExitInternal(tls_vector);
+}
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+void PlatformThreadLocalStorage::OnThreadExit(void* value) {
+ // On posix this function may be called twice. The first pass calls dtors and
+ // sets state to kDestroyed. The second pass sets kDestroyed to
+ // kUninitialized.
+ TlsVectorEntry* tls_vector = nullptr;
+ const TlsVectorState state = GetTlsVectorStateAndValue(value, &tls_vector);
+ if (state == TlsVectorState::kDestroyed) {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ SetTlsVectorValue(key, nullptr, TlsVectorState::kUninitialized);
+ return;
+ }
+
+ OnThreadExitInternal(tls_vector);
+}
+#endif // defined(OS_WIN)
+
+} // namespace internal
+
+// static
+bool ThreadLocalStorage::HasBeenDestroyed() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
+ return false;
+ const TlsVectorState state = GetTlsVectorStateAndValue(key);
+ return state == TlsVectorState::kDestroying ||
+ state == TlsVectorState::kDestroyed;
+}
+
+void ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES ||
+ GetTlsVectorStateAndValue(key) == TlsVectorState::kUninitialized) {
+ ConstructTlsVector();
+ }
+
+ // Grab a new slot.
+ {
+ base::AutoLock auto_lock(*GetTLSMetadataLock());
+ for (int i = 0; i < kThreadLocalStorageSize; ++i) {
+ // Tracking the last assigned slot is an attempt to find the next
+ // available slot within one iteration. Under normal usage, slots remain
+ // in use for the lifetime of the process (otherwise before we reclaimed
+ // slots, we would have run out of slots). This makes it highly likely the
+ // next slot is going to be a free slot.
+ size_t slot_candidate =
+ (g_last_assigned_slot + 1 + i) % kThreadLocalStorageSize;
+ if (g_tls_metadata[slot_candidate].status == TlsStatus::FREE) {
+ g_tls_metadata[slot_candidate].status = TlsStatus::IN_USE;
+ g_tls_metadata[slot_candidate].destructor = destructor;
+ g_last_assigned_slot = slot_candidate;
+ DCHECK_EQ(kInvalidSlotValue, slot_);
+ slot_ = slot_candidate;
+ version_ = g_tls_metadata[slot_candidate].version;
+ break;
+ }
+ }
+ }
+ CHECK_NE(slot_, kInvalidSlotValue);
+ CHECK_LT(slot_, kThreadLocalStorageSize);
+}
+
+void ThreadLocalStorage::Slot::Free() {
+ DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ {
+ base::AutoLock auto_lock(*GetTLSMetadataLock());
+ g_tls_metadata[slot_].status = TlsStatus::FREE;
+ g_tls_metadata[slot_].destructor = nullptr;
+ ++(g_tls_metadata[slot_].version);
+ }
+ slot_ = kInvalidSlotValue;
+}
+
+void* ThreadLocalStorage::Slot::Get() const {
+ TlsVectorEntry* tls_data = nullptr;
+ const TlsVectorState state = GetTlsVectorStateAndValue(
+ base::subtle::NoBarrier_Load(&g_native_tls_key), &tls_data);
+ DCHECK_NE(state, TlsVectorState::kDestroyed);
+ if (!tls_data)
+ return nullptr;
+ DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ // Version mismatches means this slot was previously freed.
+ if (tls_data[slot_].version != version_)
+ return nullptr;
+ return tls_data[slot_].data;
+}
+
+void ThreadLocalStorage::Slot::Set(void* value) {
+ TlsVectorEntry* tls_data = nullptr;
+ const TlsVectorState state = GetTlsVectorStateAndValue(
+ base::subtle::NoBarrier_Load(&g_native_tls_key), &tls_data);
+ DCHECK_NE(state, TlsVectorState::kDestroyed);
+ if (!tls_data) {
+ if (!value)
+ return;
+ tls_data = ConstructTlsVector();
+ }
+ DCHECK_NE(slot_, kInvalidSlotValue);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ tls_data[slot_].data = value;
+ tls_data[slot_].version = version_;
+}
+
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ Initialize(destructor);
+}
+
+ThreadLocalStorage::Slot::~Slot() {
+ Free();
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/thread_local_storage.h b/security/sandbox/chromium/base/threading/thread_local_storage.h
new file mode 100644
index 0000000000..73b845ef56
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_local_storage.h
@@ -0,0 +1,175 @@
+// 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_THREADING_THREAD_LOCAL_STORAGE_H_
+#define BASE_THREADING_THREAD_LOCAL_STORAGE_H_
+
+#include <stdint.h>
+
+#include "base/atomicops.h"
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/win/windows_types.h"
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <pthread.h>
+#endif
+
+namespace ui {
+class TLSDestructionCheckerForX11;
+}
+
+namespace base {
+
+class SamplingHeapProfiler;
+
+namespace debug {
+class GlobalActivityTracker;
+} // namespace debug
+
+namespace trace_event {
+class MallocDumpProvider;
+} // namespace trace_event
+
+namespace internal {
+
+class ThreadLocalStorageTestInternal;
+
+// WARNING: You should *NOT* use this class directly.
+// PlatformThreadLocalStorage is a low-level abstraction of the OS's TLS
+// interface. Instead, you should use one of the following:
+// * ThreadLocalBoolean (from thread_local.h) for booleans.
+// * ThreadLocalPointer (from thread_local.h) for pointers.
+// * ThreadLocalStorage::StaticSlot/Slot for more direct control of the slot.
+class BASE_EXPORT PlatformThreadLocalStorage {
+ public:
+
+#if defined(OS_WIN)
+ typedef unsigned long TLSKey;
+ enum : unsigned { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES };
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ typedef pthread_key_t TLSKey;
+ // The following is a "reserved key" which is used in our generic Chromium
+ // ThreadLocalStorage implementation. We expect that an OS will not return
+ // such a key, but if it is returned (i.e., the OS tries to allocate it) we
+ // will just request another key.
+ enum { TLS_KEY_OUT_OF_INDEXES = 0x7FFFFFFF };
+#endif
+
+ // The following methods need to be supported on each OS platform, so that
+ // the Chromium ThreadLocalStore functionality can be constructed.
+ // Chromium will use these methods to acquire a single OS slot, and then use
+ // that to support a much larger number of Chromium slots (independent of the
+ // OS restrictions).
+ // The following returns true if it successfully is able to return an OS
+ // key in |key|.
+ static bool AllocTLS(TLSKey* key);
+ // Note: FreeTLS() doesn't have to be called, it is fine with this leak, OS
+ // might not reuse released slot, you might just reset the TLS value with
+ // SetTLSValue().
+ static void FreeTLS(TLSKey key);
+ static void SetTLSValue(TLSKey key, void* value);
+ static void* GetTLSValue(TLSKey key) {
+#if defined(OS_WIN)
+ return TlsGetValue(key);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ return pthread_getspecific(key);
+#endif
+ }
+
+ // Each platform (OS implementation) is required to call this method on each
+ // terminating thread when the thread is about to terminate. This method
+ // will then call all registered destructors for slots in Chromium
+ // ThreadLocalStorage, until there are no slot values remaining as having
+ // been set on this thread.
+ // Destructors may end up being called multiple times on a terminating
+ // thread, as other destructors may re-set slots that were previously
+ // destroyed.
+#if defined(OS_WIN)
+ // Since Windows which doesn't support TLS destructor, the implementation
+ // should use GetTLSValue() to retrieve the value of TLS slot.
+ static void OnThreadExit();
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // |Value| is the data stored in TLS slot, The implementation can't use
+ // GetTLSValue() to retrieve the value of slot as it has already been reset
+ // in Posix.
+ static void OnThreadExit(void* value);
+#endif
+};
+
+} // namespace internal
+
+// Wrapper for thread local storage. This class doesn't do much except provide
+// an API for portability.
+class BASE_EXPORT ThreadLocalStorage {
+ public:
+ // Prototype for the TLS destructor function, which can be optionally used to
+ // cleanup thread local storage on thread exit. 'value' is the data that is
+ // stored in thread local storage.
+ typedef void (*TLSDestructorFunc)(void* value);
+
+ // A key representing one value stored in TLS. Use as a class member or a
+ // local variable. If you need a static storage duration variable, use the
+ // following pattern with a NoDestructor<Slot>:
+ // void MyDestructorFunc(void* value);
+ // ThreadLocalStorage::Slot& ImportantContentTLS() {
+ // static NoDestructor<ThreadLocalStorage::Slot> important_content_tls(
+ // &MyDestructorFunc);
+ // return *important_content_tls;
+ // }
+ class BASE_EXPORT Slot final {
+ public:
+ // |destructor| is a pointer to a function to perform per-thread cleanup of
+ // this object. If set to nullptr, no cleanup is done for this TLS slot.
+ explicit Slot(TLSDestructorFunc destructor = nullptr);
+ // If a destructor was set for this slot, removes the destructor so that
+ // remaining threads exiting will not free data.
+ ~Slot();
+
+ // Get the thread-local value stored in slot 'slot'.
+ // Values are guaranteed to initially be zero.
+ void* Get() const;
+
+ // Set the thread-local value stored in slot 'slot' to
+ // value 'value'.
+ void Set(void* value);
+
+ private:
+ void Initialize(TLSDestructorFunc destructor);
+ void Free();
+
+ static constexpr int kInvalidSlotValue = -1;
+ int slot_ = kInvalidSlotValue;
+ uint32_t version_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Slot);
+ };
+
+ private:
+ // In most cases, most callers should not need access to HasBeenDestroyed().
+ // If you are working in code that runs during thread destruction, contact the
+ // base OWNERs for advice and then make a friend request.
+ //
+ // Returns |true| if Chrome's implementation of TLS is being or has been
+ // destroyed during thread destruction. Attempting to call Slot::Get() during
+ // destruction is disallowed and will hit a DCHECK. Any code that relies on
+ // TLS during thread destruction must first check this method before calling
+ // Slot::Get().
+ friend class SequenceCheckerImpl;
+ friend class SamplingHeapProfiler;
+ friend class ThreadCheckerImpl;
+ friend class internal::ThreadLocalStorageTestInternal;
+ friend class trace_event::MallocDumpProvider;
+ friend class debug::GlobalActivityTracker;
+ friend class ui::TLSDestructionCheckerForX11;
+ static bool HasBeenDestroyed();
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage);
+};
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_LOCAL_STORAGE_H_
diff --git a/security/sandbox/chromium/base/threading/thread_local_storage_posix.cc b/security/sandbox/chromium/base/threading/thread_local_storage_posix.cc
new file mode 100644
index 0000000000..89edeee1d2
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_local_storage_posix.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) {
+ return !pthread_key_create(key,
+ base::internal::PlatformThreadLocalStorage::OnThreadExit);
+}
+
+void PlatformThreadLocalStorage::FreeTLS(TLSKey key) {
+ int ret = pthread_key_delete(key);
+ DCHECK_EQ(ret, 0);
+}
+
+void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) {
+ int ret = pthread_setspecific(key, value);
+ DCHECK_EQ(ret, 0);
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/threading/thread_local_storage_win.cc b/security/sandbox/chromium/base/threading/thread_local_storage_win.cc
new file mode 100644
index 0000000000..a9aec31da5
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_local_storage_win.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace base {
+
+namespace internal {
+
+bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) {
+ TLSKey value = TlsAlloc();
+ if (value != TLS_OUT_OF_INDEXES) {
+ *key = value;
+ return true;
+ }
+ return false;
+}
+
+void PlatformThreadLocalStorage::FreeTLS(TLSKey key) {
+ BOOL ret = TlsFree(key);
+ DCHECK(ret);
+}
+
+void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) {
+ BOOL ret = TlsSetValue(key, value);
+ DCHECK(ret);
+}
+
+} // namespace internal
+
+} // namespace base
+
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from http://www.codeproject.com/threads/tls.asp
+// and it works for VC++ 7.0 and later.
+
+// Force a reference to _tls_used to make the linker create the TLS directory
+// if it's not already there. (e.g. if __declspec(thread) is not used).
+// Force a reference to p_thread_callback_base to prevent whole program
+// optimization from discarding the variable.
+#ifdef _WIN64
+
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:p_thread_callback_base")
+
+#else // _WIN64
+
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_p_thread_callback_base")
+
+#endif // _WIN64
+
+// Static callback function to call with each thread termination.
+void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
+ // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+
+ // and on W2K and W2K3. So don't assume it is sent.
+ if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason)
+ base::internal::PlatformThreadLocalStorage::OnThreadExit();
+}
+
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// By implicitly loaded, I mean that it is directly referenced by the main EXE
+// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being
+// implicitly loaded.
+//
+// See VC\crt\src\tlssup.c for reference.
+
+// extern "C" suppresses C++ name mangling so we know the symbol name for the
+// linker /INCLUDE:symbol pragma above.
+extern "C" {
+// The linker must not discard p_thread_callback_base. (We force a reference
+// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If
+// this variable is discarded, the OnThreadExit function will never be called.
+#ifdef _WIN64
+
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma const_seg(".CRT$XLB")
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK p_thread_callback_base;
+const PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit;
+
+// Reset the default section.
+#pragma const_seg()
+
+#else // _WIN64
+
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit;
+
+// Reset the default section.
+#pragma data_seg()
+
+#endif // _WIN64
+} // extern "C"
diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.cc b/security/sandbox/chromium/base/threading/thread_restrictions.cc
new file mode 100644
index 0000000000..75c37eab4f
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_restrictions.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_restrictions.h"
+
+#if DCHECK_IS_ON()
+
+#include "base/debug/stack_trace.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/threading/thread_local.h"
+#include "build/build_config.h"
+
+namespace base {
+
+std::ostream& operator<<(std::ostream&out, const ThreadLocalBoolean& tl) {
+ out << "currently set to " << (tl.Get() ? "true" : "false");
+ return out;
+}
+
+namespace {
+
+#if defined(OS_NACL) || defined(OS_ANDROID)
+// NaCL doesn't support stack sampling and Android is slow at stack
+// sampling and this causes timeouts (crbug.com/959139).
+using ThreadLocalBooleanWithStacks = ThreadLocalBoolean;
+#else
+class ThreadLocalBooleanWithStacks {
+ public:
+ ThreadLocalBooleanWithStacks() = default;
+
+ bool Get() const { return bool_.Get(); }
+
+ void Set(bool val) {
+ stack_.Set(std::make_unique<debug::StackTrace>());
+ bool_.Set(val);
+ }
+
+ friend std::ostream& operator<<(std::ostream& out,
+ const ThreadLocalBooleanWithStacks& tl) {
+ out << tl.bool_ << " by ";
+
+ if (!tl.stack_.Get())
+ return out << "default value\n";
+ out << "\n";
+ tl.stack_.Get()->OutputToStream(&out);
+ return out;
+ }
+
+ private:
+ ThreadLocalBoolean bool_;
+ ThreadLocalOwnedPointer<debug::StackTrace> stack_;
+
+ DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanWithStacks);
+};
+#endif // defined(OS_NACL)
+
+LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_blocking_disallowed =
+ LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBooleanWithStacks>::Leaky g_singleton_disallowed =
+ LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBooleanWithStacks>::Leaky
+ g_base_sync_primitives_disallowed = LAZY_INSTANCE_INITIALIZER;
+
+LazyInstance<ThreadLocalBooleanWithStacks>::Leaky
+ g_cpu_intensive_work_disallowed = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace internal {
+
+void AssertBlockingAllowed() {
+ DCHECK(!g_blocking_disallowed.Get().Get())
+ << "Function marked as blocking was called from a scope that disallows "
+ "blocking! If this task is running inside the ThreadPool, it needs "
+ "to have MayBlock() in its TaskTraits. Otherwise, consider making "
+ "this blocking work asynchronous or, as a last resort, you may use "
+ "ScopedAllowBlocking (see its documentation for best practices).\n"
+ << "g_blocking_disallowed " << g_blocking_disallowed.Get();
+}
+
+} // namespace internal
+
+void DisallowBlocking() {
+ g_blocking_disallowed.Get().Set(true);
+}
+
+ScopedDisallowBlocking::ScopedDisallowBlocking()
+ : was_disallowed_(g_blocking_disallowed.Get().Get()) {
+ g_blocking_disallowed.Get().Set(true);
+}
+
+ScopedDisallowBlocking::~ScopedDisallowBlocking() {
+ DCHECK(g_blocking_disallowed.Get().Get());
+ g_blocking_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowBlocking::ScopedAllowBlocking()
+ : was_disallowed_(g_blocking_disallowed.Get().Get()) {
+ g_blocking_disallowed.Get().Set(false);
+}
+
+ScopedAllowBlocking::~ScopedAllowBlocking() {
+ DCHECK(!g_blocking_disallowed.Get().Get());
+ g_blocking_disallowed.Get().Set(was_disallowed_);
+}
+
+void DisallowBaseSyncPrimitives() {
+ g_base_sync_primitives_disallowed.Get().Set(true);
+}
+
+ScopedAllowBaseSyncPrimitives::ScopedAllowBaseSyncPrimitives()
+ : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
+ DCHECK(!g_blocking_disallowed.Get().Get())
+ << "To allow //base sync primitives in a scope where blocking is "
+ "disallowed use ScopedAllowBaseSyncPrimitivesOutsideBlockingScope.\n"
+ << "g_blocking_disallowed " << g_blocking_disallowed.Get();
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+ScopedAllowBaseSyncPrimitives::~ScopedAllowBaseSyncPrimitives() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
+ : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+ScopedAllowBaseSyncPrimitivesOutsideBlockingScope::
+ ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowBaseSyncPrimitivesForTesting::
+ ScopedAllowBaseSyncPrimitivesForTesting()
+ : was_disallowed_(g_base_sync_primitives_disallowed.Get().Get()) {
+ g_base_sync_primitives_disallowed.Get().Set(false);
+}
+
+ScopedAllowBaseSyncPrimitivesForTesting::
+ ~ScopedAllowBaseSyncPrimitivesForTesting() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_);
+}
+
+ScopedAllowUnresponsiveTasksForTesting::ScopedAllowUnresponsiveTasksForTesting()
+ : was_disallowed_base_sync_(g_base_sync_primitives_disallowed.Get().Get()),
+ was_disallowed_blocking_(g_blocking_disallowed.Get().Get()),
+ was_disallowed_cpu_(g_cpu_intensive_work_disallowed.Get().Get()) {
+ g_base_sync_primitives_disallowed.Get().Set(false);
+ g_blocking_disallowed.Get().Set(false);
+ g_cpu_intensive_work_disallowed.Get().Set(false);
+}
+
+ScopedAllowUnresponsiveTasksForTesting::
+ ~ScopedAllowUnresponsiveTasksForTesting() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get());
+ DCHECK(!g_blocking_disallowed.Get().Get());
+ DCHECK(!g_cpu_intensive_work_disallowed.Get().Get());
+ g_base_sync_primitives_disallowed.Get().Set(was_disallowed_base_sync_);
+ g_blocking_disallowed.Get().Set(was_disallowed_blocking_);
+ g_cpu_intensive_work_disallowed.Get().Set(was_disallowed_cpu_);
+}
+
+namespace internal {
+
+void AssertBaseSyncPrimitivesAllowed() {
+ DCHECK(!g_base_sync_primitives_disallowed.Get().Get())
+ << "Waiting on a //base sync primitive is not allowed on this thread to "
+ "prevent jank and deadlock. If waiting on a //base sync primitive is "
+ "unavoidable, do it within the scope of a "
+ "ScopedAllowBaseSyncPrimitives. If in a test, "
+ "use ScopedAllowBaseSyncPrimitivesForTesting.\n"
+ << "g_base_sync_primitives_disallowed "
+ << g_base_sync_primitives_disallowed.Get()
+ << "It can be useful to know that g_blocking_disallowed is "
+ << g_blocking_disallowed.Get();
+}
+
+void ResetThreadRestrictionsForTesting() {
+ g_blocking_disallowed.Get().Set(false);
+ g_singleton_disallowed.Get().Set(false);
+ g_base_sync_primitives_disallowed.Get().Set(false);
+ g_cpu_intensive_work_disallowed.Get().Set(false);
+}
+
+} // namespace internal
+
+void AssertLongCPUWorkAllowed() {
+ DCHECK(!g_cpu_intensive_work_disallowed.Get().Get())
+ << "Function marked as CPU intensive was called from a scope that "
+ "disallows this kind of work! Consider making this work "
+ "asynchronous.\n"
+ << "g_cpu_intensive_work_disallowed "
+ << g_cpu_intensive_work_disallowed.Get();
+}
+
+void DisallowUnresponsiveTasks() {
+ DisallowBlocking();
+ DisallowBaseSyncPrimitives();
+ g_cpu_intensive_work_disallowed.Get().Set(true);
+}
+
+ThreadRestrictions::ScopedAllowIO::ScopedAllowIO()
+ : was_allowed_(SetIOAllowed(true)) {}
+
+ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() {
+ SetIOAllowed(was_allowed_);
+}
+
+// static
+bool ThreadRestrictions::SetIOAllowed(bool allowed) {
+ bool previous_disallowed = g_blocking_disallowed.Get().Get();
+ g_blocking_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+// static
+bool ThreadRestrictions::SetSingletonAllowed(bool allowed) {
+ bool previous_disallowed = g_singleton_disallowed.Get().Get();
+ g_singleton_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+// static
+void ThreadRestrictions::AssertSingletonAllowed() {
+ DCHECK(!g_singleton_disallowed.Get().Get())
+ << "LazyInstance/Singleton is not allowed to be used on this thread. "
+ "Most likely it's because this thread is not joinable (or the current "
+ "task is running with TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN "
+ "semantics), so AtExitManager may have deleted the object on "
+ "shutdown, leading to a potential shutdown crash. If you need to use "
+ "the object from this context, it'll have to be updated to use Leaky "
+ "traits.\n"
+ << "g_singleton_disallowed " << g_singleton_disallowed.Get();
+}
+
+// static
+void ThreadRestrictions::DisallowWaiting() {
+ DisallowBaseSyncPrimitives();
+}
+
+bool ThreadRestrictions::SetWaitAllowed(bool allowed) {
+ bool previous_disallowed = g_base_sync_primitives_disallowed.Get().Get();
+ g_base_sync_primitives_disallowed.Get().Set(!allowed);
+ return !previous_disallowed;
+}
+
+} // namespace base
+
+#endif // DCHECK_IS_ON()
diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.h b/security/sandbox/chromium/base/threading/thread_restrictions.h
new file mode 100644
index 0000000000..55047c5b40
--- /dev/null
+++ b/security/sandbox/chromium/base/threading/thread_restrictions.h
@@ -0,0 +1,680 @@
+// 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_THREADING_THREAD_RESTRICTIONS_H_
+#define BASE_THREADING_THREAD_RESTRICTIONS_H_
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+// -----------------------------------------------------------------------------
+// Usage documentation
+// -----------------------------------------------------------------------------
+//
+// Overview:
+// This file exposes functions to ban and allow certain slow operations
+// on a per-thread basis. To annotate *usage* of such slow operations, refer to
+// scoped_blocking_call.h instead.
+//
+// Specific allowances that can be controlled in this file are:
+// - Blocking call: Refers to any call that causes the calling thread to wait
+// off-CPU. It includes but is not limited to calls that wait on synchronous
+// file I/O operations: read or write a file from disk, interact with a pipe
+// or a socket, rename or delete a file, enumerate files in a directory, etc.
+// Acquiring a low contention lock is not considered a blocking call.
+//
+// - Waiting on a //base sync primitive: Refers to calling one of these methods:
+// - base::WaitableEvent::*Wait*
+// - base::ConditionVariable::*Wait*
+// - base::Process::WaitForExit*
+//
+// - Long CPU work: Refers to any code that takes more than 100 ms to
+// run when there is no CPU contention and no hard page faults and therefore,
+// is not suitable to run on a thread required to keep the browser responsive
+// (where jank could be visible to the user).
+//
+// The following disallowance functions are offered:
+// - DisallowBlocking(): Disallows blocking calls on the current thread.
+// - DisallowBaseSyncPrimitives(): Disallows waiting on a //base sync primitive
+// on the current thread.
+// - DisallowUnresponsiveTasks() Disallows blocking calls, waiting on a //base
+// sync primitive, and long cpu work on the current thread.
+//
+// In addition, scoped-allowance mechanisms are offered to make an exception
+// within a scope for a behavior that is normally disallowed.
+// - ScopedAllowBlocking(ForTesting): Allows blocking calls.
+// - ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope): Allow
+// waiting on a //base sync primitive. The OutsideBlockingScope suffix allows
+// uses in a scope where blocking is also disallowed.
+//
+// Avoid using allowances outside of unit tests. In unit tests, use allowances
+// with the suffix "ForTesting".
+//
+// Prefer making blocking calls from tasks posted to base::ThreadPoolInstance
+// with base::MayBlock().
+//
+// Instead of waiting on a WaitableEvent or a ConditionVariable, prefer putting
+// the work that should happen after the wait in a continuation callback and
+// post it from where the WaitableEvent or ConditionVariable would have been
+// signaled. If something needs to be scheduled after many tasks have executed,
+// use base::BarrierClosure.
+//
+// On Windows, join processes asynchronously using base::win::ObjectWatcher.
+//
+// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible
+// in the caller making the blocking call but no further down. For example: if a
+// Cleanup() method needs to do a blocking call, document Cleanup() as blocking
+// and add a ScopedAllowBlocking instance in callers that can't avoid making
+// this call from a context where blocking is banned, as such:
+//
+// void Client::MyMethod() {
+// (...)
+// {
+// // Blocking is okay here because XYZ.
+// ScopedAllowBlocking allow_blocking;
+// my_foo_->Cleanup();
+// }
+// (...)
+// }
+//
+// // This method can block.
+// void Foo::Cleanup() {
+// // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides
+// // its blocking nature from unknowing callers and defeats the purpose of
+// // these checks.
+// FlushStateToDisk();
+// }
+//
+// Note: In rare situations where the blocking call is an implementation detail
+// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it
+// somehow knows that in practice this will not block), it might be okay to hide
+// the ScopedAllowBlocking instance in the impl with a comment explaining why
+// that's okay.
+
+class BrowserProcessImpl;
+class HistogramSynchronizer;
+class KeyStorageLinux;
+class NativeBackendKWallet;
+class NativeDesktopMediaList;
+class StartupTimeBomb;
+
+namespace android_webview {
+class AwFormDatabaseService;
+class CookieManager;
+class ScopedAllowInitGLBindings;
+class VizCompositorThreadRunnerWebView;
+}
+namespace audio {
+class OutputDevice;
+}
+namespace blink {
+class RTCVideoDecoderAdapter;
+class RTCVideoEncoder;
+class SourceStream;
+class VideoFrameResourceProvider;
+class WorkerThread;
+namespace scheduler {
+class WorkerThread;
+}
+}
+namespace cc {
+class CompletionEvent;
+class TileTaskManagerImpl;
+}
+namespace chromeos {
+class BlockingMethodCaller;
+namespace system {
+class StatisticsProviderImpl;
+}
+}
+namespace chrome_browser_net {
+class Predictor;
+}
+namespace chrome_cleaner {
+class SystemReportComponent;
+}
+namespace content {
+class BrowserGpuChannelHostFactory;
+class BrowserMainLoop;
+class BrowserProcessSubThread;
+class BrowserShutdownProfileDumper;
+class BrowserTestBase;
+class CategorizedWorkerPool;
+class DesktopCaptureDevice;
+class InProcessUtilityThread;
+class NestedMessagePumpAndroid;
+class RenderProcessHostImpl;
+class RenderWidgetHostViewMac;
+class RTCVideoDecoder;
+class SandboxHostLinux;
+class ScopedAllowWaitForDebugURL;
+class ServiceWorkerContextClient;
+class SoftwareOutputDeviceMus;
+class SynchronousCompositor;
+class SynchronousCompositorHost;
+class SynchronousCompositorSyncCallBridge;
+class TextInputClientMac;
+class WebContentsViewMac;
+} // namespace content
+namespace cronet {
+class CronetPrefsManager;
+class CronetURLRequestContext;
+} // namespace cronet
+namespace dbus {
+class Bus;
+}
+namespace disk_cache {
+class BackendImpl;
+class InFlightIO;
+}
+namespace functions {
+class ExecScriptScopedAllowBaseSyncPrimitives;
+}
+namespace history_report {
+class HistoryReportJniBridge;
+}
+namespace gpu {
+class GpuChannelHost;
+}
+namespace leveldb_env {
+class DBTracker;
+}
+namespace media {
+class AudioInputDevice;
+class AudioOutputDevice;
+class BlockingUrlProtocol;
+class PaintCanvasVideoRenderer;
+}
+namespace memory_instrumentation {
+class OSMetrics;
+}
+namespace midi {
+class TaskService; // https://crbug.com/796830
+}
+namespace module_installer {
+class ScopedAllowModulePakLoad;
+}
+namespace mojo {
+class CoreLibraryInitializer;
+class SyncCallRestrictions;
+namespace core {
+class ScopedIPCSupport;
+}
+}
+namespace printing {
+class PrintJobWorker;
+class PrinterQuery;
+}
+namespace rlz_lib {
+class FinancialPing;
+}
+namespace syncer {
+class GetLocalChangesRequest;
+class HttpBridge;
+class ModelSafeWorker;
+}
+namespace ui {
+class CommandBufferClientImpl;
+class CommandBufferLocal;
+class GpuState;
+class MaterialDesignController;
+}
+namespace weblayer {
+class WebLayerPathProvider;
+}
+namespace net {
+class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
+class MultiThreadedProxyResolverScopedAllowJoinOnIO;
+class NetworkChangeNotifierMac;
+class NetworkConfigWatcherMacThread;
+namespace internal {
+class AddressTrackerLinux;
+}
+}
+
+namespace proxy_resolver {
+class ScopedAllowThreadJoinForProxyResolverV8Tracing;
+}
+
+namespace remoting {
+class AutoThread;
+namespace protocol {
+class ScopedAllowThreadJoinForWebRtcTransport;
+}
+}
+
+namespace resource_coordinator {
+class TabManagerDelegate;
+}
+
+namespace service_manager {
+class ServiceProcessLauncher;
+}
+
+namespace shell_integration_linux {
+class LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
+}
+
+namespace ui {
+class WindowResizeHelperMac;
+}
+
+namespace viz {
+class HostGpuMemoryBufferManager;
+}
+
+namespace vr {
+class VrShell;
+}
+
+namespace web {
+class WebMainLoop;
+class WebSubThread;
+}
+
+namespace weblayer {
+class ProfileImpl;
+}
+
+namespace webrtc {
+class DesktopConfigurationMonitor;
+}
+
+namespace base {
+
+namespace sequence_manager {
+namespace internal {
+class TaskQueueImpl;
+}
+} // namespace sequence_manager
+
+namespace android {
+class JavaHandlerThread;
+}
+
+namespace internal {
+class JobTaskSource;
+class TaskTracker;
+}
+
+class AdjustOOMScoreHelper;
+class FileDescriptorWatcher;
+class GetAppOutputScopedAllowBaseSyncPrimitives;
+class ScopedAllowThreadRecallForStackSamplingProfiler;
+class SimpleThread;
+class StackSamplingProfiler;
+class Thread;
+
+#if DCHECK_IS_ON()
+#define INLINE_IF_DCHECK_IS_OFF BASE_EXPORT
+#define EMPTY_BODY_IF_DCHECK_IS_OFF
+#else
+#define INLINE_IF_DCHECK_IS_OFF inline
+
+// The static_assert() eats follow-on semicolons. `= default` would work
+// too, but it makes clang realize that all the Scoped classes are no-ops in
+// non-dcheck builds and it starts emitting many -Wunused-variable warnings.
+#define EMPTY_BODY_IF_DCHECK_IS_OFF \
+ {} \
+ static_assert(true, "")
+#endif
+
+namespace internal {
+
+// Asserts that blocking calls are allowed in the current scope. This is an
+// internal call, external code should use ScopedBlockingCall instead, which
+// serves as a precise annotation of the scope that may/will block.
+INLINE_IF_DCHECK_IS_OFF void AssertBlockingAllowed()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+} // namespace internal
+
+// Disallows blocking on the current thread.
+INLINE_IF_DCHECK_IS_OFF void DisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+// Disallows blocking calls within its scope.
+class BASE_EXPORT ScopedDisallowBlocking {
+ public:
+ ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisallowBlocking);
+};
+
+class BASE_EXPORT ScopedAllowBlocking {
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking);
+ friend class ScopedAllowBlockingForTesting;
+
+ // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting
+ // in unit tests to avoid the friend requirement.
+ friend class AdjustOOMScoreHelper;
+ friend class android_webview::ScopedAllowInitGLBindings;
+ friend class content::BrowserProcessSubThread;
+ friend class content::RenderWidgetHostViewMac; // http://crbug.com/121917
+ friend class content::WebContentsViewMac;
+ friend class cronet::CronetPrefsManager;
+ friend class cronet::CronetURLRequestContext;
+ friend class memory_instrumentation::OSMetrics;
+ friend class module_installer::ScopedAllowModulePakLoad;
+ friend class mojo::CoreLibraryInitializer;
+ friend class printing::PrintJobWorker;
+ friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703
+ friend class ui::MaterialDesignController;
+ friend class web::WebSubThread;
+ friend class StackSamplingProfiler;
+ friend class weblayer::ProfileImpl;
+ friend class content::RenderProcessHostImpl;
+ friend class weblayer::WebLayerPathProvider;
+
+ ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlocking);
+};
+
+class ScopedAllowBlockingForTesting {
+ public:
+ ScopedAllowBlockingForTesting() {}
+ ~ScopedAllowBlockingForTesting() {}
+
+ private:
+#if DCHECK_IS_ON()
+ ScopedAllowBlocking scoped_allow_blocking_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlockingForTesting);
+};
+
+INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+class BASE_EXPORT ScopedAllowBaseSyncPrimitives {
+ private:
+ // This can only be instantiated by friends. Use
+ // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend
+ // requirement.
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitives);
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesResetsState);
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesWithBlockingDisallowed);
+
+ // Allowed usage:
+ friend class SimpleThread;
+ friend class base::GetAppOutputScopedAllowBaseSyncPrimitives;
+ friend class blink::SourceStream;
+ friend class blink::WorkerThread;
+ friend class blink::scheduler::WorkerThread;
+ friend class chrome_cleaner::SystemReportComponent;
+ friend class content::BrowserMainLoop;
+ friend class content::BrowserProcessSubThread;
+ friend class content::ServiceWorkerContextClient;
+ friend class functions::ExecScriptScopedAllowBaseSyncPrimitives;
+ friend class history_report::HistoryReportJniBridge;
+ friend class internal::TaskTracker;
+ friend class leveldb_env::DBTracker;
+ friend class media::BlockingUrlProtocol;
+ friend class mojo::core::ScopedIPCSupport;
+ friend class net::MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
+ friend class rlz_lib::FinancialPing;
+ friend class shell_integration_linux::
+ LaunchXdgUtilityScopedAllowBaseSyncPrimitives;
+ friend class syncer::HttpBridge;
+ friend class syncer::GetLocalChangesRequest;
+ friend class syncer::ModelSafeWorker;
+ friend class webrtc::DesktopConfigurationMonitor;
+
+ // Usage that should be fixed:
+ friend class ::NativeBackendKWallet; // http://crbug.com/125331
+ friend class ::chromeos::system::
+ StatisticsProviderImpl; // http://crbug.com/125385
+ friend class content::TextInputClientMac; // http://crbug.com/121917
+ friend class blink::VideoFrameResourceProvider; // http://crbug.com/878070
+
+ ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitives);
+};
+
+class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {
+ private:
+ // This can only be instantiated by friends. Use
+ // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend
+ // requirement.
+ FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
+ FRIEND_TEST_ALL_PREFIXES(
+ ThreadRestrictionsTest,
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScopeResetsState);
+
+ // Allowed usage:
+ friend class ::BrowserProcessImpl; // http://crbug.com/125207
+ friend class ::KeyStorageLinux;
+ friend class ::NativeDesktopMediaList;
+ friend class ::StartupTimeBomb;
+ friend class android::JavaHandlerThread;
+ friend class android_webview::
+ AwFormDatabaseService; // http://crbug.com/904431
+ friend class android_webview::CookieManager;
+ friend class android_webview::VizCompositorThreadRunnerWebView;
+ friend class audio::OutputDevice;
+ friend class base::sequence_manager::internal::TaskQueueImpl;
+ friend class base::FileDescriptorWatcher;
+ friend class base::internal::JobTaskSource;
+ friend class base::ScopedAllowThreadRecallForStackSamplingProfiler;
+ friend class base::StackSamplingProfiler;
+ friend class blink::RTCVideoDecoderAdapter;
+ friend class blink::RTCVideoEncoder;
+ friend class cc::TileTaskManagerImpl;
+ friend class content::CategorizedWorkerPool;
+ friend class content::DesktopCaptureDevice;
+ friend class content::InProcessUtilityThread;
+ friend class content::RTCVideoDecoder;
+ friend class content::SandboxHostLinux;
+ friend class content::ScopedAllowWaitForDebugURL;
+ friend class content::SynchronousCompositor;
+ friend class content::SynchronousCompositorHost;
+ friend class content::SynchronousCompositorSyncCallBridge;
+ friend class media::AudioInputDevice;
+ friend class media::AudioOutputDevice;
+ friend class media::PaintCanvasVideoRenderer;
+ friend class mojo::SyncCallRestrictions;
+ friend class net::NetworkConfigWatcherMacThread;
+ friend class viz::HostGpuMemoryBufferManager;
+ friend class vr::VrShell;
+
+ // Usage that should be fixed:
+ friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360
+ friend class base::Thread; // http://crbug.com/918039
+ friend class cc::CompletionEvent; // http://crbug.com/902653
+ friend class content::
+ BrowserGpuChannelHostFactory; // http://crbug.com/125248
+ friend class dbus::Bus; // http://crbug.com/125222
+ friend class disk_cache::BackendImpl; // http://crbug.com/74623
+ friend class disk_cache::InFlightIO; // http://crbug.com/74623
+ friend class gpu::GpuChannelHost; // http://crbug.com/125264
+ friend class remoting::protocol::
+ ScopedAllowThreadJoinForWebRtcTransport; // http://crbug.com/660081
+ friend class midi::TaskService; // https://crbug.com/796830
+ friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097
+ friend class net::
+ MultiThreadedProxyResolverScopedAllowJoinOnIO; // http://crbug.com/69710
+ friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097
+ friend class printing::PrinterQuery; // http://crbug.com/66082
+ friend class proxy_resolver::
+ ScopedAllowThreadJoinForProxyResolverV8Tracing; // http://crbug.com/69710
+ friend class remoting::AutoThread; // https://crbug.com/944316
+ // Not used in production yet, https://crbug.com/844078.
+ friend class service_manager::ServiceProcessLauncher;
+ friend class ui::WindowResizeHelperMac; // http://crbug.com/902829
+
+ ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
+};
+
+class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting {
+ public:
+ ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_disallowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesForTesting);
+};
+
+// Counterpart to base::DisallowUnresponsiveTasks() for tests to allow them to
+// block their thread after it was banned.
+class BASE_EXPORT ScopedAllowUnresponsiveTasksForTesting {
+ public:
+ ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_disallowed_base_sync_;
+ const bool was_disallowed_blocking_;
+ const bool was_disallowed_cpu_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowUnresponsiveTasksForTesting);
+};
+
+namespace internal {
+
+// Asserts that waiting on a //base sync primitive is allowed in the current
+// scope.
+INLINE_IF_DCHECK_IS_OFF void AssertBaseSyncPrimitivesAllowed()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+// Resets all thread restrictions on the current thread.
+INLINE_IF_DCHECK_IS_OFF void ResetThreadRestrictionsForTesting()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+} // namespace internal
+
+// Asserts that running long CPU work is allowed in the current scope.
+INLINE_IF_DCHECK_IS_OFF void AssertLongCPUWorkAllowed()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+INLINE_IF_DCHECK_IS_OFF void DisallowUnresponsiveTasks()
+ EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+class BASE_EXPORT ThreadRestrictions {
+ public:
+ // Constructing a ScopedAllowIO temporarily allows IO for the current
+ // thread. Doing this is almost certainly always incorrect.
+ //
+ // DEPRECATED. Use ScopedAllowBlocking(ForTesting).
+ class BASE_EXPORT ScopedAllowIO {
+ public:
+ ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF;
+ ~ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF;
+
+ private:
+#if DCHECK_IS_ON()
+ const bool was_allowed_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO);
+ };
+
+#if DCHECK_IS_ON()
+ // Set whether the current thread to make IO calls.
+ // Threads start out in the *allowed* state.
+ // Returns the previous value.
+ //
+ // DEPRECATED. Use ScopedAllowBlocking(ForTesting) or ScopedDisallowBlocking.
+ static bool SetIOAllowed(bool allowed);
+
+ // Set whether the current thread can use singletons. Returns the previous
+ // value.
+ static bool SetSingletonAllowed(bool allowed);
+
+ // Check whether the current thread is allowed to use singletons (Singleton /
+ // LazyInstance). DCHECKs if not.
+ static void AssertSingletonAllowed();
+
+ // Disable waiting on the current thread. Threads start out in the *allowed*
+ // state. Returns the previous value.
+ //
+ // DEPRECATED. Use DisallowBaseSyncPrimitives.
+ static void DisallowWaiting();
+#else
+ // Inline the empty definitions of these functions so that they can be
+ // compiled out.
+ static bool SetIOAllowed(bool allowed) { return true; }
+ static bool SetSingletonAllowed(bool allowed) { return true; }
+ static void AssertSingletonAllowed() {}
+ static void DisallowWaiting() {}
+#endif
+
+ private:
+ // DO NOT ADD ANY OTHER FRIEND STATEMENTS.
+ // BEGIN ALLOWED USAGE.
+ friend class content::BrowserMainLoop;
+ friend class content::BrowserShutdownProfileDumper;
+ friend class content::BrowserTestBase;
+ friend class content::ScopedAllowWaitForDebugURL;
+ friend class ::HistogramSynchronizer;
+ friend class internal::TaskTracker;
+ friend class web::WebMainLoop;
+ friend class MessagePumpDefault;
+ friend class PlatformThread;
+ friend class ui::CommandBufferClientImpl;
+ friend class ui::CommandBufferLocal;
+ friend class ui::GpuState;
+
+ // END ALLOWED USAGE.
+ // BEGIN USAGE THAT NEEDS TO BE FIXED.
+ friend class chrome_browser_net::Predictor; // http://crbug.com/78451
+#if !defined(OFFICIAL_BUILD)
+ friend class content::SoftwareOutputDeviceMus; // Interim non-production code
+#endif
+// END USAGE THAT NEEDS TO BE FIXED.
+
+#if DCHECK_IS_ON()
+ // DEPRECATED. Use ScopedAllowBaseSyncPrimitives.
+ static bool SetWaitAllowed(bool allowed);
+#else
+ static bool SetWaitAllowed(bool allowed) { return true; }
+#endif
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions);
+};
+
+#undef INLINE_IF_DCHECK_IS_OFF
+#undef EMPTY_BODY_IF_DCHECK_IS_OFF
+
+} // namespace base
+
+#endif // BASE_THREADING_THREAD_RESTRICTIONS_H_
diff --git a/security/sandbox/chromium/base/time/time.cc b/security/sandbox/chromium/base/time/time.cc
new file mode 100644
index 0000000000..1293cdcaad
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time.cc
@@ -0,0 +1,433 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/time/time.h"
+
+#include <cmath>
+#include <ios>
+#include <limits>
+#include <ostream>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/no_destructor.h"
+#include "base/strings/stringprintf.h"
+#include "base/third_party/nspr/prtime.h"
+#include "base/time/time_override.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace internal {
+
+TimeNowFunction g_time_now_function = &subtle::TimeNowIgnoringOverride;
+
+TimeNowFunction g_time_now_from_system_time_function =
+ &subtle::TimeNowFromSystemTimeIgnoringOverride;
+
+TimeTicksNowFunction g_time_ticks_now_function =
+ &subtle::TimeTicksNowIgnoringOverride;
+
+ThreadTicksNowFunction g_thread_ticks_now_function =
+ &subtle::ThreadTicksNowIgnoringOverride;
+
+} // namespace internal
+
+// TimeDelta ------------------------------------------------------------------
+
+int TimeDelta::InDays() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
+}
+
+int TimeDelta::InDaysFloored() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ int result = delta_ / Time::kMicrosecondsPerDay;
+ int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay);
+ if (remainder < 0) {
+ --result; // Use floor(), not trunc() rounding behavior.
+ }
+ return result;
+}
+
+int TimeDelta::InHours() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
+}
+
+int TimeDelta::InMinutes() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute);
+}
+
+double TimeDelta::InSecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
+}
+
+int64_t TimeDelta::InSeconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64_t>::max();
+ }
+ return delta_ / Time::kMicrosecondsPerSecond;
+}
+
+double TimeDelta::InMillisecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
+}
+
+int64_t TimeDelta::InMilliseconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64_t>::max();
+ }
+ return delta_ / Time::kMicrosecondsPerMillisecond;
+}
+
+int64_t TimeDelta::InMillisecondsRoundedUp() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64_t>::max();
+ }
+ int64_t result = delta_ / Time::kMicrosecondsPerMillisecond;
+ int64_t remainder = delta_ - (result * Time::kMicrosecondsPerMillisecond);
+ if (remainder > 0) {
+ ++result; // Use ceil(), not trunc() rounding behavior.
+ }
+ return result;
+}
+
+double TimeDelta::InMicrosecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return static_cast<double>(delta_);
+}
+
+int64_t TimeDelta::InNanoseconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64_t>::max();
+ }
+ return delta_ * Time::kNanosecondsPerMicrosecond;
+}
+
+std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
+ return os << time_delta.InSecondsF() << " s";
+}
+
+// Time -----------------------------------------------------------------------
+
+// static
+Time Time::Now() {
+ return internal::g_time_now_function();
+}
+
+// static
+Time Time::NowFromSystemTime() {
+ // Just use g_time_now_function because it returns the system time.
+ return internal::g_time_now_from_system_time_function();
+}
+
+// static
+Time Time::FromDeltaSinceWindowsEpoch(TimeDelta delta) {
+ return Time(delta.InMicroseconds());
+}
+
+TimeDelta Time::ToDeltaSinceWindowsEpoch() const {
+ return TimeDelta::FromMicroseconds(us_);
+}
+
+// static
+Time Time::FromTimeT(time_t tt) {
+ if (tt == 0)
+ return Time(); // Preserve 0 so we can tell it doesn't exist.
+ if (tt == std::numeric_limits<time_t>::max())
+ return Max();
+ return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSeconds(tt);
+}
+
+time_t Time::ToTimeT() const {
+ if (is_null())
+ return 0; // Preserve 0 so we can tell it doesn't exist.
+ if (is_max()) {
+ // Preserve max without offset to prevent overflow.
+ return std::numeric_limits<time_t>::max();
+ }
+ if (std::numeric_limits<int64_t>::max() - kTimeTToMicrosecondsOffset <= us_) {
+ DLOG(WARNING) << "Overflow when converting base::Time with internal " <<
+ "value " << us_ << " to time_t.";
+ return std::numeric_limits<time_t>::max();
+ }
+ return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond;
+}
+
+// static
+Time Time::FromDoubleT(double dt) {
+ if (dt == 0 || std::isnan(dt))
+ return Time(); // Preserve 0 so we can tell it doesn't exist.
+ return Time(kTimeTToMicrosecondsOffset) + TimeDelta::FromSecondsD(dt);
+}
+
+double Time::ToDoubleT() const {
+ if (is_null())
+ return 0; // Preserve 0 so we can tell it doesn't exist.
+ if (is_max()) {
+ // Preserve max without offset to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
+ static_cast<double>(kMicrosecondsPerSecond));
+}
+
+#if defined(OS_POSIX)
+// static
+Time Time::FromTimeSpec(const timespec& ts) {
+ return FromDoubleT(ts.tv_sec +
+ static_cast<double>(ts.tv_nsec) /
+ base::Time::kNanosecondsPerSecond);
+}
+#endif
+
+// static
+Time Time::FromJsTime(double ms_since_epoch) {
+ // The epoch is a valid time, so this constructor doesn't interpret
+ // 0 as the null time.
+ return Time(kTimeTToMicrosecondsOffset) +
+ TimeDelta::FromMillisecondsD(ms_since_epoch);
+}
+
+double Time::ToJsTime() const {
+ if (is_null()) {
+ // Preserve 0 so the invalid result doesn't depend on the platform.
+ return 0;
+ }
+ return ToJsTimeIgnoringNull();
+}
+
+double Time::ToJsTimeIgnoringNull() const {
+ if (is_max()) {
+ // Preserve max without offset to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
+ return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
+ kMicrosecondsPerMillisecond);
+}
+
+Time Time::FromJavaTime(int64_t ms_since_epoch) {
+ return base::Time::UnixEpoch() +
+ base::TimeDelta::FromMilliseconds(ms_since_epoch);
+}
+
+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<int64_t>::max();
+ }
+ return ((us_ - kTimeTToMicrosecondsOffset) /
+ kMicrosecondsPerMillisecond);
+}
+
+// static
+Time Time::UnixEpoch() {
+ Time time;
+ time.us_ = kTimeTToMicrosecondsOffset;
+ return time;
+}
+
+Time Time::Midnight(bool is_local) const {
+ Exploded exploded;
+ Explode(is_local, &exploded);
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+ Time out_time;
+ if (FromExploded(is_local, exploded, &out_time)) {
+ return out_time;
+ } else if (is_local) {
+ // Hitting this branch means 00:00:00am of the current day
+ // does not exist (due to Daylight Saving Time in some countries
+ // where clocks are shifted at midnight). In this case, midnight
+ // should be defined as 01:00:00am.
+ exploded.hour = 1;
+ if (FromExploded(is_local, exploded, &out_time))
+ return out_time;
+ }
+ // This function must not fail.
+ NOTREACHED();
+ return Time();
+}
+
+#if !defined(MOZ_SANDBOX)
+// static
+bool Time::FromStringInternal(const char* time_string,
+ bool is_local,
+ Time* parsed_time) {
+ DCHECK((time_string != nullptr) && (parsed_time != nullptr));
+
+ if (time_string[0] == '\0')
+ return false;
+
+ PRTime result_time = 0;
+ PRStatus result = PR_ParseTimeString(time_string,
+ is_local ? PR_FALSE : PR_TRUE,
+ &result_time);
+ if (PR_SUCCESS != result)
+ return false;
+
+ result_time += kTimeTToMicrosecondsOffset;
+ *parsed_time = Time(result_time);
+ return true;
+}
+#endif
+
+// static
+bool Time::ExplodedMostlyEquals(const Exploded& lhs, const Exploded& rhs) {
+ return lhs.year == rhs.year && lhs.month == rhs.month &&
+ lhs.day_of_month == rhs.day_of_month && lhs.hour == rhs.hour &&
+ lhs.minute == rhs.minute && lhs.second == rhs.second &&
+ lhs.millisecond == rhs.millisecond;
+}
+
+// static
+bool Time::FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds,
+ Time* time) {
+ // Adjust the provided time from milliseconds since the Unix epoch (1970) to
+ // microseconds since the Windows epoch (1601), avoiding overflows.
+ base::CheckedNumeric<int64_t> checked_microseconds_win_epoch =
+ unix_milliseconds;
+ checked_microseconds_win_epoch *= kMicrosecondsPerMillisecond;
+ checked_microseconds_win_epoch += kTimeTToMicrosecondsOffset;
+ if (!checked_microseconds_win_epoch.IsValid()) {
+ *time = base::Time(0);
+ return false;
+ }
+
+ *time = Time(checked_microseconds_win_epoch.ValueOrDie());
+ return true;
+}
+
+int64_t Time::ToRoundedDownMillisecondsSinceUnixEpoch() const {
+ // Adjust from Windows epoch (1601) to Unix epoch (1970).
+ int64_t microseconds = us_ - kTimeTToMicrosecondsOffset;
+
+ // Round the microseconds towards -infinity.
+ if (microseconds >= 0) {
+ // In this case, rounding towards -infinity means rounding towards 0.
+ return microseconds / kMicrosecondsPerMillisecond;
+ } else {
+ return (microseconds - kMicrosecondsPerMillisecond + 1) /
+ kMicrosecondsPerMillisecond;
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, Time time) {
+ Time::Exploded exploded;
+ time.UTCExplode(&exploded);
+ // Use StringPrintf because iostreams formatting is painful.
+ return os << StringPrintf("%04d-%02d-%02d %02d:%02d:%02d.%03d UTC",
+ exploded.year,
+ exploded.month,
+ exploded.day_of_month,
+ exploded.hour,
+ exploded.minute,
+ exploded.second,
+ exploded.millisecond);
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+// static
+TimeTicks TimeTicks::Now() {
+ return internal::g_time_ticks_now_function();
+}
+
+// static
+TimeTicks TimeTicks::UnixEpoch() {
+ static const base::NoDestructor<base::TimeTicks> epoch([]() {
+ return subtle::TimeTicksNowIgnoringOverride() -
+ (subtle::TimeNowIgnoringOverride() - Time::UnixEpoch());
+ }());
+ return *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
+ // from run to run. Although bogo-microseconds usually roughly correspond to
+ // real microseconds, the only real guarantee is that the number never goes
+ // down during a single run.
+ const TimeDelta as_time_delta = time_ticks - TimeTicks();
+ return os << as_time_delta.InMicroseconds() << " bogo-microseconds";
+}
+
+// ThreadTicks ----------------------------------------------------------------
+
+// static
+ThreadTicks ThreadTicks::Now() {
+ return internal::g_thread_ticks_now_function();
+}
+
+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) {
+ return lo <= value && value <= hi;
+}
+
+bool Time::Exploded::HasValidValues() const {
+ return is_in_range(month, 1, 12) &&
+ is_in_range(day_of_week, 0, 6) &&
+ is_in_range(day_of_month, 1, 31) &&
+ is_in_range(hour, 0, 23) &&
+ is_in_range(minute, 0, 59) &&
+ is_in_range(second, 0, 60) &&
+ is_in_range(millisecond, 0, 999);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/time/time.h b/security/sandbox/chromium/base/time/time.h
new file mode 100644
index 0000000000..7214e000f0
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time.h
@@ -0,0 +1,1077 @@
+// 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.
+
+// 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). 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 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 cannot 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.
+//
+// All time classes are copyable, assignable, and occupy 64-bits per instance.
+// As a result, prefer passing them by value:
+// void MyFunction(TimeDelta arg);
+// If circumstances require, you may also pass by const reference:
+// void MyFunction(const TimeDelta& arg); // Not preferred.
+//
+// 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 <stdint.h>
+#include <time.h>
+
+#include <iosfwd>
+#include <limits>
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "build/build_config.h"
+
+#if defined(OS_FUCHSIA)
+#include <zircon/types.h>
+#endif
+
+#if defined(OS_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+// Avoid Mac system header macro leak.
+#undef TYPE_BOOL
+#endif
+
+#if defined(OS_ANDROID)
+#include <jni.h>
+#endif
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+#include <unistd.h>
+#include <sys/time.h>
+#endif
+
+#if defined(OS_WIN)
+#include "base/gtest_prod_util.h"
+#include "base/win/windows_types.h"
+#endif
+
+namespace ABI {
+namespace Windows {
+namespace Foundation {
+struct DateTime;
+} // namespace Foundation
+} // namespace Windows
+} // namespace ABI
+
+namespace base {
+
+class PlatformThreadHandle;
+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 a TimeDelta from |value|. TimeDelta::Min()/Max() are treated
+// as infinity and will always saturate the return value (infinity math applies
+// if |value| also is at either limit of its spectrum). The int64_t argument and
+// return value are in terms of a microsecond timebase.
+BASE_EXPORT constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta);
+BASE_EXPORT constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta);
+
+} // namespace time_internal
+
+// TimeDelta ------------------------------------------------------------------
+
+class BASE_EXPORT TimeDelta {
+ public:
+ constexpr TimeDelta() : delta_(0) {}
+
+ // Converts units of time to TimeDeltas.
+ // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may
+ // not precisely equal |t|. Hence, floating point values should not be used
+ // for storage.
+ static constexpr TimeDelta FromDays(int days);
+ static constexpr TimeDelta FromHours(int hours);
+ static constexpr TimeDelta FromMinutes(int minutes);
+ static constexpr TimeDelta FromSeconds(int64_t secs);
+ static constexpr TimeDelta FromMilliseconds(int64_t ms);
+ static constexpr TimeDelta FromMicroseconds(int64_t us);
+ static constexpr TimeDelta FromNanoseconds(int64_t ns);
+ static constexpr TimeDelta FromSecondsD(double secs);
+ static constexpr TimeDelta FromMillisecondsD(double ms);
+ static constexpr TimeDelta FromMicrosecondsD(double us);
+ static constexpr TimeDelta FromNanosecondsD(double ns);
+#if defined(OS_WIN)
+ static TimeDelta FromQPCValue(LONGLONG qpc_value);
+ // TODO(crbug.com/989694): Avoid base::TimeDelta factory functions
+ // based on absolute time
+ static TimeDelta FromFileTime(FILETIME ft);
+ static TimeDelta FromWinrtDateTime(ABI::Windows::Foundation::DateTime dt);
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+ static TimeDelta FromTimeSpec(const timespec& ts);
+#endif
+#if defined(OS_FUCHSIA)
+ static TimeDelta FromZxDuration(zx_duration_t nanos);
+#endif
+
+ // Converts an integer value representing TimeDelta to a class. This is used
+ // 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.
+ //
+ // DEPRECATED - Do not use in new code. http://crbug.com/634507
+ static constexpr 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
+ // delta to a time or another time delta has an undefined result.
+ static constexpr TimeDelta Max();
+
+ // Returns the minimum time delta, which should be less than than any
+ // reasonable time delta we might compare it to. Adding or subtracting the
+ // minimum time delta to a time or another time delta has an undefined result.
+ static constexpr TimeDelta Min();
+
+ // Returns the internal numeric value of the TimeDelta object. Please don't
+ // use this and do arithmetic on it, as it is more error prone than using the
+ // provided operators.
+ // For serializing, use FromInternalValue to reconstitute.
+ //
+ // DEPRECATED - Do not use in new code. http://crbug.com/634507
+ constexpr int64_t ToInternalValue() const { return delta_; }
+
+ // Returns the magnitude (absolute value) of this TimeDelta.
+ constexpr 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.
+ constexpr bool is_zero() const { return delta_ == 0; }
+
+ // Returns true if the time delta is the maximum/minimum time delta.
+ constexpr bool is_max() const {
+ return delta_ == std::numeric_limits<int64_t>::max();
+ }
+ constexpr bool is_min() const {
+ return delta_ == std::numeric_limits<int64_t>::min();
+ }
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ struct timespec ToTimeSpec() const;
+#endif
+#if defined(OS_FUCHSIA)
+ zx_duration_t ToZxDuration() const;
+#endif
+#if defined(OS_WIN)
+ ABI::Windows::Foundation::DateTime ToWinrtDateTime() const;
+#endif
+
+ // Returns the time delta in some unit. The InXYZF versions return a floating
+ // point value. The InXYZ versions return a truncated value (aka rounded
+ // towards zero, std::trunc() behavior). The InXYZFloored() versions round to
+ // lesser integers (std::floor() behavior). The XYZRoundedUp() versions round
+ // up to greater integers (std::ceil() behavior).
+ // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may
+ // not precisely equal |t|. Hence, floating point values should not be used
+ // for storage.
+ int InDays() const;
+ int InDaysFloored() const;
+ int InHours() const;
+ int InMinutes() const;
+ double InSecondsF() const;
+ int64_t InSeconds() const;
+ double InMillisecondsF() const;
+ int64_t InMilliseconds() const;
+ int64_t InMillisecondsRoundedUp() const;
+ constexpr int64_t InMicroseconds() const { return delta_; }
+ double InMicrosecondsF() const;
+ int64_t InNanoseconds() const;
+
+ // Computations with other deltas.
+ constexpr TimeDelta operator+(TimeDelta other) const {
+ return TimeDelta(time_internal::SaturatedAdd(delta_, other));
+ }
+ constexpr TimeDelta operator-(TimeDelta other) const {
+ return TimeDelta(time_internal::SaturatedSub(delta_, other));
+ }
+
+ constexpr TimeDelta& operator+=(TimeDelta other) {
+ return *this = (*this + other);
+ }
+ constexpr TimeDelta& operator-=(TimeDelta other) {
+ return *this = (*this - other);
+ }
+ constexpr TimeDelta operator-() const { return TimeDelta(-delta_); }
+
+ // Computations with numeric types.
+ template <typename T>
+ constexpr TimeDelta operator*(T a) const {
+ CheckedNumeric<int64_t> rv(delta_);
+ rv *= a;
+ if (rv.IsValid())
+ return TimeDelta(rv.ValueOrDie());
+ // Matched sign overflows. Mismatched sign underflows.
+ if ((delta_ < 0) ^ (a < 0))
+ return TimeDelta(std::numeric_limits<int64_t>::min());
+ return TimeDelta(std::numeric_limits<int64_t>::max());
+ }
+ template <typename T>
+ constexpr TimeDelta operator/(T a) const {
+ CheckedNumeric<int64_t> rv(delta_);
+ rv /= a;
+ if (rv.IsValid())
+ return TimeDelta(rv.ValueOrDie());
+ // Matched sign overflows. Mismatched sign underflows.
+ // Special case to catch divide by zero.
+ if ((delta_ < 0) ^ (a <= 0))
+ return TimeDelta(std::numeric_limits<int64_t>::min());
+ return TimeDelta(std::numeric_limits<int64_t>::max());
+ }
+ template <typename T>
+ constexpr TimeDelta& operator*=(T a) {
+ return *this = (*this * a);
+ }
+ template <typename T>
+ constexpr TimeDelta& operator/=(T a) {
+ return *this = (*this / a);
+ }
+
+ constexpr int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; }
+
+ constexpr TimeDelta operator%(TimeDelta a) const {
+ return TimeDelta(delta_ % a.delta_);
+ }
+ TimeDelta& operator%=(TimeDelta other) { return *this = (*this % other); }
+
+ // Comparison operators.
+ constexpr bool operator==(TimeDelta other) const {
+ return delta_ == other.delta_;
+ }
+ constexpr bool operator!=(TimeDelta other) const {
+ return delta_ != other.delta_;
+ }
+ constexpr bool operator<(TimeDelta other) const {
+ return delta_ < other.delta_;
+ }
+ constexpr bool operator<=(TimeDelta other) const {
+ return delta_ <= other.delta_;
+ }
+ constexpr bool operator>(TimeDelta other) const {
+ return delta_ > other.delta_;
+ }
+ constexpr bool operator>=(TimeDelta other) const {
+ return delta_ >= other.delta_;
+ }
+
+ private:
+ friend constexpr int64_t time_internal::SaturatedAdd(int64_t value,
+ TimeDelta delta);
+ friend constexpr int64_t time_internal::SaturatedSub(int64_t value,
+ TimeDelta delta);
+
+ // 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.
+ constexpr explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {}
+
+ // Private method to build a delta from a double.
+ static constexpr TimeDelta FromDouble(double value);
+
+ // Private method to build a delta from the product of a user-provided value
+ // and a known-positive value.
+ static constexpr TimeDelta FromProduct(int64_t value, int64_t positive_value);
+
+ // Delta in microseconds.
+ int64_t delta_;
+};
+
+template <typename T>
+constexpr 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 {
+
+constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta) {
+ // Treat Min/Max() as +/- infinity (additions involving two infinities are
+ // only valid if signs match).
+ if (delta.is_max()) {
+ CHECK_GT(value, std::numeric_limits<int64_t>::min());
+ return std::numeric_limits<int64_t>::max();
+ } else if (delta.is_min()) {
+ CHECK_LT(value, std::numeric_limits<int64_t>::max());
+ return std::numeric_limits<int64_t>::min();
+ }
+
+ return base::ClampAdd(value, delta.delta_);
+}
+
+constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta) {
+ // Treat Min/Max() as +/- infinity (subtractions involving two infinities are
+ // only valid if signs are opposite).
+ if (delta.is_max()) {
+ CHECK_LT(value, std::numeric_limits<int64_t>::max());
+ return std::numeric_limits<int64_t>::min();
+ } else if (delta.is_min()) {
+ CHECK_GT(value, std::numeric_limits<int64_t>::min());
+ return std::numeric_limits<int64_t>::max();
+ }
+
+ return base::ClampSub(value, delta.delta_);
+}
+
+// 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 TimeClass>
+class TimeBase {
+ public:
+ static constexpr int64_t kHoursPerDay = 24;
+ static constexpr int64_t kSecondsPerMinute = 60;
+ static constexpr int64_t kSecondsPerHour = 60 * kSecondsPerMinute;
+ static constexpr int64_t kMillisecondsPerSecond = 1000;
+ static constexpr int64_t kMillisecondsPerDay =
+ kMillisecondsPerSecond * 60 * 60 * kHoursPerDay;
+ static constexpr int64_t kMicrosecondsPerMillisecond = 1000;
+ static constexpr int64_t kMicrosecondsPerSecond =
+ kMicrosecondsPerMillisecond * kMillisecondsPerSecond;
+ static constexpr int64_t kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60;
+ static constexpr int64_t kMicrosecondsPerHour = kMicrosecondsPerMinute * 60;
+ static constexpr int64_t kMicrosecondsPerDay =
+ kMicrosecondsPerHour * kHoursPerDay;
+ static constexpr int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7;
+ static constexpr int64_t kNanosecondsPerMicrosecond = 1000;
+ static constexpr 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.
+ constexpr bool is_null() const { return us_ == 0; }
+
+ // Returns true if this object represents the maximum/minimum time.
+ constexpr bool is_max() const {
+ return us_ == std::numeric_limits<int64_t>::max();
+ }
+ constexpr bool is_min() const {
+ return us_ == std::numeric_limits<int64_t>::min();
+ }
+
+ // Returns the maximum/minimum times, which should be greater/less than than
+ // any reasonable time with which we might compare it.
+ static constexpr TimeClass Max() {
+ return TimeClass(std::numeric_limits<int64_t>::max());
+ }
+
+ static constexpr TimeClass Min() {
+ return TimeClass(std::numeric_limits<int64_t>::min());
+ }
+
+ // 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.
+ //
+ // DEPRECATED - Do not use in new code. For serializing Time values, prefer
+ // Time::ToDeltaSinceWindowsEpoch().InMicroseconds(). http://crbug.com/634507
+ constexpr int64_t ToInternalValue() const { return us_; }
+
+ // The amount of time since the origin (or "zero") point. This is a syntactic
+ // convenience to aid in code readability, mainly for debugging/testing use
+ // cases.
+ //
+ // Warning: While the Time subclass has a fixed origin point, the origin for
+ // the other subclasses can vary each time the application is restarted.
+ constexpr TimeDelta since_origin() const {
+ return TimeDelta::FromMicroseconds(us_);
+ }
+
+ constexpr TimeClass& operator=(TimeClass other) {
+ us_ = other.us_;
+ return *(static_cast<TimeClass*>(this));
+ }
+
+ // Compute the difference between two times.
+ constexpr TimeDelta operator-(TimeClass other) const {
+ return TimeDelta::FromMicroseconds(us_ - other.us_);
+ }
+
+ // Return a new time modified by some delta.
+ constexpr TimeClass operator+(TimeDelta delta) const {
+ return TimeClass(time_internal::SaturatedAdd(us_, delta));
+ }
+ constexpr TimeClass operator-(TimeDelta delta) const {
+ return TimeClass(time_internal::SaturatedSub(us_, delta));
+ }
+
+ // Modify by some time delta.
+ constexpr TimeClass& operator+=(TimeDelta delta) {
+ return static_cast<TimeClass&>(*this = (*this + delta));
+ }
+ constexpr TimeClass& operator-=(TimeDelta delta) {
+ return static_cast<TimeClass&>(*this = (*this - delta));
+ }
+
+ // Comparison operators
+ constexpr bool operator==(TimeClass other) const { return us_ == other.us_; }
+ constexpr bool operator!=(TimeClass other) const { return us_ != other.us_; }
+ constexpr bool operator<(TimeClass other) const { return us_ < other.us_; }
+ constexpr bool operator<=(TimeClass other) const { return us_ <= other.us_; }
+ constexpr bool operator>(TimeClass other) const { return us_ > other.us_; }
+ constexpr bool operator>=(TimeClass other) const { return us_ >= other.us_; }
+
+ protected:
+ constexpr explicit TimeBase(int64_t us) : us_(us) {}
+
+ // Time value in a microsecond timebase.
+ int64_t us_;
+};
+
+} // namespace time_internal
+
+template <class TimeClass>
+inline constexpr TimeClass operator+(TimeDelta delta, TimeClass t) {
+ return t + delta;
+}
+
+// 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.
+// Time is stored internally as microseconds since the Windows epoch (1601).
+class BASE_EXPORT Time : public time_internal::TimeBase<Time> {
+ public:
+ // Offset of UNIX epoch (1970-01-01 00:00:00 UTC) from Windows FILETIME epoch
+ // (1601-01-01 00:00:00 UTC), in microseconds. This value is derived from the
+ // following: ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the number
+ // of leap year days between 1601 and 1970: (1970-1601)/4 excluding 1700,
+ // 1800, and 1900.
+ static constexpr int64_t kTimeTToMicrosecondsOffset =
+ INT64_C(11644473600000000);
+
+#if defined(OS_WIN)
+ // To avoid overflow in QPC to Microseconds calculations, since we multiply
+ // by kMicrosecondsPerSecond, then the QPC value should not exceed
+ // (2^63 - 1) / 1E6. If it exceeds that threshold, we divide then multiply.
+ static constexpr int64_t kQPCOverflowThreshold = INT64_C(0x8637BD05AF7);
+#endif
+
+// kExplodedMinYear and kExplodedMaxYear define the platform-specific limits
+// for values passed to FromUTCExploded() and FromLocalExploded(). Those
+// functions will return false if passed values outside these limits. The limits
+// are inclusive, meaning that the API should support all dates within a given
+// limit year.
+#if defined(OS_WIN)
+ static constexpr int kExplodedMinYear = 1601;
+ static constexpr int kExplodedMaxYear = 30827;
+#elif defined(OS_IOS) && !__LP64__
+ static constexpr int kExplodedMinYear = std::numeric_limits<int>::min();
+ static constexpr int kExplodedMaxYear = std::numeric_limits<int>::max();
+#elif defined(OS_MACOSX)
+ static constexpr int kExplodedMinYear = 1902;
+ static constexpr int kExplodedMaxYear = std::numeric_limits<int>::max();
+#elif defined(OS_ANDROID)
+ // Though we use 64-bit time APIs on both 32 and 64 bit Android, some OS
+ // versions like KitKat (ARM but not x86 emulator) can't handle some early
+ // dates (e.g. before 1170). So we set min conservatively here.
+ static constexpr int kExplodedMinYear = 1902;
+ static constexpr int kExplodedMaxYear = std::numeric_limits<int>::max();
+#else
+ static constexpr int kExplodedMinYear =
+ (sizeof(time_t) == 4 ? 1902 : std::numeric_limits<int>::min());
+ static constexpr int kExplodedMaxYear =
+ (sizeof(time_t) == 4 ? 2037 : std::numeric_limits<int>::max());
+#endif
+
+ // Represents an exploded time that can be formatted nicely. This is kind of
+ // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few
+ // additions and changes to prevent errors.
+ struct BASE_EXPORT Exploded {
+ int year; // Four digit year "2007"
+ int month; // 1-based month (values 1 = January, etc.)
+ int day_of_week; // 0-based day of week (0 = Sunday, etc.)
+ int day_of_month; // 1-based day of month (1-31)
+ int hour; // Hour within the current day (0-23)
+ int minute; // Minute within the current hour (0-59)
+ int second; // Second within the current minute (0-59 plus leap
+ // seconds which may take it up to 60).
+ int millisecond; // Milliseconds within the current second (0-999)
+
+ // A cursory test for whether the data members are within their
+ // respective ranges. A 'true' return value does not guarantee the
+ // Exploded value can be successfully converted to a Time value.
+ bool HasValidValues() const;
+ };
+
+ // Contains the NULL time. Use Time::Now() to get the current time.
+ constexpr Time() : TimeBase(0) {}
+
+ // Returns the time for epoch in Unix-like system (Jan 1, 1970).
+ static Time UnixEpoch();
+
+ // Returns the current time. Watch out, the system might adjust its clock
+ // in which case time will actually go backwards. We don't guarantee that
+ // times are increasing, or that two calls to Now() won't be the same.
+ static Time Now();
+
+ // Returns the current time. Same as Now() except that this function always
+ // uses system time so that there are no discrepancies between the returned
+ // time and system time even on virtual environments including our test bot.
+ // For timing sensitive unittests, this function should be used.
+ static Time NowFromSystemTime();
+
+ // Converts to/from TimeDeltas relative to the Windows epoch (1601-01-01
+ // 00:00:00 UTC). Prefer these methods for opaque serialization and
+ // deserialization of time values, e.g.
+ //
+ // // Serialization:
+ // base::Time last_updated = ...;
+ // SaveToDatabase(last_updated.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ //
+ // // Deserialization:
+ // base::Time last_updated = base::Time::FromDeltaSinceWindowsEpoch(
+ // base::TimeDelta::FromMicroseconds(LoadFromDatabase()));
+ static Time FromDeltaSinceWindowsEpoch(TimeDelta delta);
+ TimeDelta ToDeltaSinceWindowsEpoch() const;
+
+ // Converts to/from time_t in UTC and a Time class.
+ static Time FromTimeT(time_t tt);
+ time_t ToTimeT() const;
+
+ // Converts time to/from a double which is the number of seconds since epoch
+ // (Jan 1, 1970). Webkit uses this format to represent time.
+ // Because WebKit initializes double time value to 0 to indicate "not
+ // initialized", we map it to empty Time object that also means "not
+ // initialized".
+ static Time FromDoubleT(double dt);
+ double ToDoubleT() const;
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ // Converts the timespec structure to time. MacOS X 10.8.3 (and tentatively,
+ // earlier versions) will have the |ts|'s tv_nsec component zeroed out,
+ // having a 1 second resolution, which agrees with
+ // https://developer.apple.com/legacy/library/#technotes/tn/tn1150.html#HFSPlusDates.
+ static Time FromTimeSpec(const timespec& ts);
+#endif
+
+ // Converts to/from the Javascript convention for times, a number of
+ // milliseconds since the epoch:
+ // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/getTime.
+ //
+ // Don't use ToJsTime() in new code, since it contains a subtle hack (only
+ // exactly 1601-01-01 00:00 UTC is represented as 1970-01-01 00:00 UTC), and
+ // that is not appropriate for general use. Try to use ToJsTimeIgnoringNull()
+ // unless you have a very good reason to use ToJsTime().
+ static Time FromJsTime(double ms_since_epoch);
+ double ToJsTime() const;
+ double ToJsTimeIgnoringNull() const;
+
+ // Converts to/from Java convention for times, a number of milliseconds since
+ // the epoch. Because the Java format has less resolution, converting to Java
+ // time is a lossy operation.
+ static Time FromJavaTime(int64_t ms_since_epoch);
+ int64_t ToJavaTime() const;
+
+#if defined(OS_POSIX) || defined(OS_FUCHSIA)
+ static Time FromTimeVal(struct timeval t);
+ struct timeval ToTimeVal() const;
+#endif
+
+#if defined(OS_FUCHSIA)
+ static Time FromZxTime(zx_time_t time);
+ zx_time_t ToZxTime() const;
+#endif
+
+#if defined(OS_MACOSX)
+ static Time FromCFAbsoluteTime(CFAbsoluteTime t);
+ CFAbsoluteTime ToCFAbsoluteTime() const;
+#endif
+
+#if defined(OS_WIN)
+ static Time FromFileTime(FILETIME ft);
+ FILETIME ToFileTime() const;
+
+ // The minimum time of a low resolution timer. This is basically a windows
+ // constant of ~15.6ms. While it does vary on some older OS versions, we'll
+ // treat it as static across all windows versions.
+ static const int kMinLowResolutionThresholdMs = 16;
+
+ // Enable or disable Windows high resolution timer.
+ static void EnableHighResolutionTimer(bool enable);
+
+ // Read the minimum timer interval from the feature list. This should be
+ // called once after the feature list is initialized. This is needed for
+ // an experiment - see https://crbug.com/927165
+ static void ReadMinTimerIntervalLowResMs();
+
+ // Activates or deactivates the high resolution timer based on the |activate|
+ // flag. If the HighResolutionTimer is not Enabled (see
+ // EnableHighResolutionTimer), this function will return false. Otherwise
+ // returns true. Each successful activate call must be paired with a
+ // subsequent deactivate call.
+ // All callers to activate the high resolution timer must eventually call
+ // this function to deactivate the high resolution timer.
+ static bool ActivateHighResolutionTimer(bool activate);
+
+ // Returns true if the high resolution timer is both enabled and activated.
+ // This is provided for testing only, and is not tracked in a thread-safe
+ // way.
+ static bool IsHighResolutionTimerInUse();
+
+ // The following two functions are used to report the fraction of elapsed time
+ // that the high resolution timer is activated.
+ // ResetHighResolutionTimerUsage() resets the cumulative usage and starts the
+ // measurement interval and GetHighResolutionTimerUsage() returns the
+ // percentage of time since the reset that the high resolution timer was
+ // activated.
+ // ResetHighResolutionTimerUsage() must be called at least once before calling
+ // GetHighResolutionTimerUsage(); otherwise the usage result would be
+ // undefined.
+ static void ResetHighResolutionTimerUsage();
+ static double GetHighResolutionTimerUsage();
+#endif // defined(OS_WIN)
+
+ // Converts an exploded structure representing either the local time or UTC
+ // into a Time class. Returns false on a failure when, for example, a day of
+ // month is set to 31 on a 28-30 day month. Returns Time(0) on overflow.
+ static bool FromUTCExploded(const Exploded& exploded,
+ Time* time) WARN_UNUSED_RESULT {
+ return FromExploded(false, exploded, time);
+ }
+ static bool FromLocalExploded(const Exploded& exploded,
+ Time* time) WARN_UNUSED_RESULT {
+ return FromExploded(true, exploded, time);
+ }
+
+ // Converts a string representation of time to a Time object.
+ // An example of a time string which is converted is as below:-
+ // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
+ // in the input string, FromString assumes local time and FromUTCString
+ // assumes UTC. A timezone that cannot be parsed (e.g. "UTC" which is not
+ // specified in RFC822) is treated as if the timezone is not specified.
+ //
+ // WARNING: the underlying converter is very permissive. For example: it is
+ // not checked whether a given day of the week matches the date; Feb 29
+ // silently becomes Mar 1 in non-leap years; under certain conditions, whole
+ // English sentences may be parsed successfully and yield unexpected results.
+ //
+ // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to
+ // a new time converter class.
+ static bool FromString(const char* time_string,
+ Time* parsed_time) WARN_UNUSED_RESULT {
+ return FromStringInternal(time_string, true, parsed_time);
+ }
+ static bool FromUTCString(const char* time_string,
+ Time* parsed_time) WARN_UNUSED_RESULT {
+ return FromStringInternal(time_string, false, parsed_time);
+ }
+
+ // Fills the given exploded structure with either the local time or UTC from
+ // this time structure (containing UTC).
+ void UTCExplode(Exploded* exploded) const {
+ return Explode(false, exploded);
+ }
+ void LocalExplode(Exploded* exploded) const {
+ return Explode(true, exploded);
+ }
+
+ // The following two functions round down the time to the nearest day in
+ // either UTC or local time. It will represent midnight on that day.
+ Time UTCMidnight() const { return Midnight(false); }
+ Time LocalMidnight() const { return Midnight(true); }
+
+ // Converts an integer value representing Time to a class. This may be used
+ // when deserializing a |Time| 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.
+ //
+ // DEPRECATED - Do not use in new code. For deserializing Time values, prefer
+ // Time::FromDeltaSinceWindowsEpoch(). http://crbug.com/634507
+ static constexpr Time FromInternalValue(int64_t us) { return Time(us); }
+
+ private:
+ friend class time_internal::TimeBase<Time>;
+
+ constexpr explicit Time(int64_t microseconds_since_win_epoch)
+ : TimeBase(microseconds_since_win_epoch) {}
+
+ // Explodes the given time to either local time |is_local = true| or UTC
+ // |is_local = false|.
+ void Explode(bool is_local, Exploded* exploded) const;
+
+ // Unexplodes a given time assuming the source is either local time
+ // |is_local = true| or UTC |is_local = false|. Function returns false on
+ // failure and sets |time| to Time(0). Otherwise returns true and sets |time|
+ // to non-exploded time.
+ static bool FromExploded(bool is_local,
+ const Exploded& exploded,
+ Time* time) WARN_UNUSED_RESULT;
+
+ // Rounds down the time to the nearest day in either local time
+ // |is_local = true| or UTC |is_local = false|.
+ Time Midnight(bool is_local) const;
+
+ // Converts a string representation of time to a Time object.
+ // An example of a time string which is converted is as below:-
+ // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
+ // in the input string, local time |is_local = true| or
+ // UTC |is_local = false| is assumed. A timezone that cannot be parsed
+ // (e.g. "UTC" which is not specified in RFC822) is treated as if the
+ // timezone is not specified.
+ static bool FromStringInternal(const char* time_string,
+ bool is_local,
+ Time* parsed_time) WARN_UNUSED_RESULT;
+
+ // Comparison does not consider |day_of_week| when doing the operation.
+ static bool ExplodedMostlyEquals(const Exploded& lhs,
+ const Exploded& rhs) WARN_UNUSED_RESULT;
+
+ // Converts the provided time in milliseconds since the Unix epoch (1970) to a
+ // Time object, avoiding overflows.
+ static bool FromMillisecondsSinceUnixEpoch(int64_t unix_milliseconds,
+ Time* time) WARN_UNUSED_RESULT;
+
+ // Returns the milliseconds since the Unix epoch (1970), rounding the
+ // microseconds towards -infinity.
+ int64_t ToRoundedDownMillisecondsSinceUnixEpoch() const;
+};
+
+// static
+constexpr TimeDelta TimeDelta::FromDays(int days) {
+ return days == std::numeric_limits<int>::max()
+ ? Max()
+ : TimeDelta(days * Time::kMicrosecondsPerDay);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromHours(int hours) {
+ return hours == std::numeric_limits<int>::max()
+ ? Max()
+ : TimeDelta(hours * Time::kMicrosecondsPerHour);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromMinutes(int minutes) {
+ return minutes == std::numeric_limits<int>::max()
+ ? Max()
+ : TimeDelta(minutes * Time::kMicrosecondsPerMinute);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromSeconds(int64_t secs) {
+ return FromProduct(secs, Time::kMicrosecondsPerSecond);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromMilliseconds(int64_t ms) {
+ return FromProduct(ms, Time::kMicrosecondsPerMillisecond);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromMicroseconds(int64_t us) {
+ return TimeDelta(us);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromNanoseconds(int64_t ns) {
+ return TimeDelta(ns / Time::kNanosecondsPerMicrosecond);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromSecondsD(double secs) {
+ return FromDouble(secs * Time::kMicrosecondsPerSecond);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromMillisecondsD(double ms) {
+ return FromDouble(ms * Time::kMicrosecondsPerMillisecond);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromMicrosecondsD(double us) {
+ return FromDouble(us);
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromNanosecondsD(double ns) {
+ return FromDouble(ns / Time::kNanosecondsPerMicrosecond);
+}
+
+// static
+constexpr TimeDelta TimeDelta::Max() {
+ return TimeDelta(std::numeric_limits<int64_t>::max());
+}
+
+// static
+constexpr TimeDelta TimeDelta::Min() {
+ return TimeDelta(std::numeric_limits<int64_t>::min());
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromDouble(double value) {
+ return TimeDelta(saturated_cast<int64_t>(value));
+}
+
+// static
+constexpr TimeDelta TimeDelta::FromProduct(int64_t value,
+ int64_t positive_value) {
+ DCHECK(positive_value > 0); // NOLINT, DCHECK_GT isn't constexpr.
+ return value > std::numeric_limits<int64_t>::max() / positive_value
+ ? Max()
+ : value < std::numeric_limits<int64_t>::min() / positive_value
+ ? Min()
+ : TimeDelta(value * positive_value);
+}
+
+// For logging use only.
+BASE_EXPORT std::ostream& operator<<(std::ostream& os, Time time);
+
+// TimeTicks ------------------------------------------------------------------
+
+// Represents monotonically non-decreasing clock time.
+class BASE_EXPORT TimeTicks : public time_internal::TimeBase<TimeTicks> {
+ public:
+ // The underlying clock used to generate new TimeTicks.
+ enum class Clock {
+ FUCHSIA_ZX_CLOCK_MONOTONIC,
+ LINUX_CLOCK_MONOTONIC,
+ IOS_CF_ABSOLUTE_TIME_MINUS_KERN_BOOTTIME,
+ MAC_MACH_ABSOLUTE_TIME,
+ WIN_QPC,
+ WIN_ROLLOVER_PROTECTED_TIME_GET_TIME
+ };
+
+ constexpr TimeTicks() : TimeBase(0) {}
+
+ // Platform-dependent tick count representing "right now." When
+ // IsHighResolution() returns false, the resolution of the clock could be
+ // as coarse as ~15.6ms. Otherwise, the resolution should be no worse than one
+ // microsecond.
+ static TimeTicks Now();
+
+ // Returns true if the high resolution clock is working on this system and
+ // Now() will return high resolution values. Note that, on systems where the
+ // high resolution clock works but is deemed inefficient, the low resolution
+ // clock will be used instead.
+ static bool IsHighResolution() WARN_UNUSED_RESULT;
+
+ // Returns true if TimeTicks is consistent across processes, meaning that
+ // timestamps taken on different processes can be safely compared with one
+ // another. (Note that, even on platforms where this returns true, time values
+ // from different threads that are within one tick of each other must be
+ // considered to have an ambiguous ordering.)
+ static bool IsConsistentAcrossProcesses() WARN_UNUSED_RESULT;
+
+#if defined(OS_FUCHSIA)
+ // Converts between TimeTicks and an ZX_CLOCK_MONOTONIC zx_time_t value.
+ static TimeTicks FromZxTime(zx_time_t nanos_since_boot);
+ zx_time_t ToZxTime() const;
+#endif
+
+#if defined(OS_WIN)
+ // Translates an absolute QPC timestamp into a TimeTicks value. The returned
+ // value has the same origin as Now(). Do NOT attempt to use this if
+ // IsHighResolution() returns false.
+ static TimeTicks FromQPCValue(LONGLONG qpc_value);
+#endif
+
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ static TimeTicks FromMachAbsoluteTime(uint64_t mach_absolute_time);
+#endif // defined(OS_MACOSX) && !defined(OS_IOS)
+
+#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
+ // Converts to TimeTicks the value obtained from SystemClock.uptimeMillis().
+ // Note: this convertion may be non-monotonic in relation to previously
+ // obtained TimeTicks::Now() values because of the truncation (to
+ // milliseconds) performed by uptimeMillis().
+ static TimeTicks FromUptimeMillis(int64_t uptime_millis_value);
+#endif
+
+ // Get an estimate of the TimeTick value at the time of the UnixEpoch. Because
+ // Time and TimeTicks respond differently to user-set time and NTP
+ // adjustments, this number is only an estimate. Nevertheless, this can be
+ // useful when you need to relate the value of TimeTicks to a real time and
+ // date. Note: Upon first invocation, this function takes a snapshot of the
+ // realtime clock to establish a reference point. This function will return
+ // the same value for the duration of the application, but will be different
+ // in future application runs.
+ static TimeTicks UnixEpoch();
+
+ // Returns |this| snapped to the next tick, given a |tick_phase| and
+ // repeating |tick_interval| in both directions. |this| may be before,
+ // after, or equal to the |tick_phase|.
+ TimeTicks SnappedToNextTick(TimeTicks tick_phase,
+ TimeDelta tick_interval) const;
+
+ // Returns an enum indicating the underlying clock being used to generate
+ // TimeTicks timestamps. This function should only be used for debugging and
+ // logging purposes.
+ static Clock GetClock();
+
+ // Converts an integer value representing TimeTicks to a class. This may be
+ // used when deserializing a |TimeTicks| 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.
+ //
+ // DEPRECATED - Do not use in new code. For deserializing TimeTicks values,
+ // prefer TimeTicks + TimeDelta(). http://crbug.com/634507
+ static constexpr TimeTicks FromInternalValue(int64_t us) {
+ return TimeTicks(us);
+ }
+
+ protected:
+#if defined(OS_WIN)
+ typedef DWORD (*TickFunctionType)(void);
+ static TickFunctionType SetMockTickFunction(TickFunctionType ticker);
+#endif
+
+ private:
+ friend class time_internal::TimeBase<TimeTicks>;
+
+ // Please use Now() to create a new object. This is for internal use
+ // and testing.
+ constexpr explicit TimeTicks(int64_t us) : TimeBase(us) {}
+};
+
+// For logging use only.
+BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeTicks time_ticks);
+
+// ThreadTicks ----------------------------------------------------------------
+
+// Represents a clock, specific to a particular thread, than runs only while the
+// thread is running.
+class BASE_EXPORT ThreadTicks : public time_internal::TimeBase<ThreadTicks> {
+ public:
+ constexpr ThreadTicks() : TimeBase(0) {}
+
+ // Returns true if ThreadTicks::Now() is supported on this system.
+ static bool IsSupported() WARN_UNUSED_RESULT {
+#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
+ (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID) || \
+ defined(OS_FUCHSIA)
+ return true;
+#elif defined(OS_WIN)
+ return IsSupportedWin();
+#else
+ return false;
+#endif
+ }
+
+ // Waits until the initialization is completed. Needs to be guarded with a
+ // call to IsSupported().
+ static void WaitUntilInitialized() {
+#if defined(OS_WIN)
+ WaitUntilInitializedWin();
+#endif
+ }
+
+ // Returns thread-specific CPU-time on systems that support this feature.
+ // Needs to be guarded with a call to IsSupported(). Use this timer
+ // to (approximately) measure how much time the calling thread spent doing
+ // actual work vs. being de-scheduled. May return bogus results if the thread
+ // migrates to another CPU between two calls. Returns an empty ThreadTicks
+ // object until the initialization is completed. If a clock reading is
+ // absolutely needed, call WaitUntilInitialized() before this method.
+ static ThreadTicks Now();
+
+#if defined(OS_WIN)
+ // Similar to Now() above except this returns thread-specific CPU time for an
+ // arbitrary thread. All comments for Now() method above apply apply to this
+ // method as well.
+ static ThreadTicks GetForThread(const PlatformThreadHandle& thread_handle);
+#endif
+
+ // Converts an integer value representing ThreadTicks to a class. This may be
+ // used when deserializing a |ThreadTicks| 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.
+ //
+ // DEPRECATED - Do not use in new code. For deserializing ThreadTicks values,
+ // prefer ThreadTicks + TimeDelta(). http://crbug.com/634507
+ static constexpr ThreadTicks FromInternalValue(int64_t us) {
+ return ThreadTicks(us);
+ }
+
+ private:
+ friend class time_internal::TimeBase<ThreadTicks>;
+
+ // Please use Now() or GetForThread() to create a new object. This is for
+ // internal use and testing.
+ constexpr explicit ThreadTicks(int64_t us) : TimeBase(us) {}
+
+#if defined(OS_WIN)
+ FRIEND_TEST_ALL_PREFIXES(TimeTicks, TSCTicksPerSecond);
+
+#if defined(ARCH_CPU_ARM64)
+ // TSCTicksPerSecond is not supported on Windows on Arm systems because the
+ // cycle-counting methods use the actual CPU cycle count, and not a consistent
+ // incrementing counter.
+#else
+ // Returns the frequency of the TSC in ticks per second, or 0 if it hasn't
+ // been measured yet. Needs to be guarded with a call to IsSupported().
+ // This method is declared here rather than in the anonymous namespace to
+ // allow testing.
+ static double TSCTicksPerSecond();
+#endif
+
+ static bool IsSupportedWin() WARN_UNUSED_RESULT;
+ static void WaitUntilInitializedWin();
+#endif
+};
+
+// For logging use only.
+BASE_EXPORT std::ostream& operator<<(std::ostream& os, ThreadTicks time_ticks);
+
+} // namespace base
+
+#endif // BASE_TIME_TIME_H_
diff --git a/security/sandbox/chromium/base/time/time_exploded_posix.cc b/security/sandbox/chromium/base/time/time_exploded_posix.cc
new file mode 100644
index 0000000000..ca8a7880bf
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time_exploded_posix.cc
@@ -0,0 +1,287 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/time/time.h"
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+#if defined(OS_ANDROID) && !defined(__LP64__)
+#include <time64.h>
+#endif
+#include <unistd.h>
+
+#include <limits>
+
+#include "base/numerics/safe_math.h"
+#include "base/synchronization/lock.h"
+#include "build/build_config.h"
+
+#if defined(OS_ANDROID)
+#include "base/os_compat_android.h"
+#elif defined(OS_NACL)
+#include "base/os_compat_nacl.h"
+#endif
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+static_assert(sizeof(time_t) >= 8, "Y2038 problem!");
+#endif
+
+namespace {
+
+// This prevents a crash on traversing the environment global and looking up
+// the 'TZ' variable in libc. See: crbug.com/390567.
+base::Lock* GetSysTimeToTimeStructLock() {
+ static auto* lock = new base::Lock();
+ return lock;
+}
+
+// Define a system-specific SysTime that wraps either to a time_t or
+// a time64_t depending on the host system, and associated convertion.
+// See crbug.com/162007
+#if defined(OS_ANDROID) && !defined(__LP64__)
+typedef time64_t SysTime;
+
+SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
+ base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ if (is_local)
+ return mktime64(timestruct);
+ else
+ return timegm64(timestruct);
+}
+
+void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
+ base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ if (is_local)
+ localtime64_r(&t, timestruct);
+ else
+ gmtime64_r(&t, timestruct);
+}
+#elif defined(OS_AIX)
+// The function timegm is not available on AIX.
+time_t aix_timegm(struct tm* tm) {
+ time_t ret;
+ char* tz;
+
+ tz = getenv("TZ");
+ if (tz) {
+ tz = strdup(tz);
+ }
+ setenv("TZ", "GMT0", 1);
+ tzset();
+ ret = mktime(tm);
+ if (tz) {
+ setenv("TZ", tz, 1);
+ free(tz);
+ } else {
+ unsetenv("TZ");
+ }
+ tzset();
+ return ret;
+}
+
+typedef time_t SysTime;
+
+SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
+ base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ if (is_local)
+ return mktime(timestruct);
+ else
+ return aix_timegm(timestruct);
+}
+
+void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
+ base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ if (is_local)
+ localtime_r(&t, timestruct);
+ else
+ gmtime_r(&t, timestruct);
+}
+
+#else
+typedef time_t SysTime;
+
+SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
+ base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ return is_local ? mktime(timestruct) : timegm(timestruct);
+}
+
+void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
+ base::AutoLock locked(*GetSysTimeToTimeStructLock());
+ if (is_local)
+ localtime_r(&t, timestruct);
+ else
+ gmtime_r(&t, timestruct);
+}
+#endif // defined(OS_ANDROID) && !defined(__LP64__)
+
+} // namespace
+
+namespace base {
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ // The following values are all rounded towards -infinity.
+ int64_t milliseconds = ToRoundedDownMillisecondsSinceUnixEpoch();
+ SysTime seconds; // Seconds since epoch.
+ int millisecond; // Exploded millisecond value (0-999).
+
+ // If the microseconds were negative, the rounded down milliseconds will also
+ // be negative. For example, -1 us becomes -1 ms.
+ if (milliseconds >= 0) {
+ // Rounding towards -infinity <=> rounding towards 0, in this case.
+ seconds = milliseconds / kMillisecondsPerSecond;
+ millisecond = milliseconds % kMillisecondsPerSecond;
+ } else {
+ // Round these *down* (towards -infinity).
+ seconds =
+ (milliseconds - kMillisecondsPerSecond + 1) / kMillisecondsPerSecond;
+ // Make this nonnegative (and between 0 and 999 inclusive).
+ millisecond = milliseconds % kMillisecondsPerSecond;
+ if (millisecond < 0)
+ millisecond += kMillisecondsPerSecond;
+ }
+
+ struct tm timestruct;
+ SysTimeToTimeStruct(seconds, &timestruct, is_local);
+
+ exploded->year = timestruct.tm_year + 1900;
+ exploded->month = timestruct.tm_mon + 1;
+ exploded->day_of_week = timestruct.tm_wday;
+ exploded->day_of_month = timestruct.tm_mday;
+ exploded->hour = timestruct.tm_hour;
+ exploded->minute = timestruct.tm_min;
+ exploded->second = timestruct.tm_sec;
+ exploded->millisecond = millisecond;
+}
+
+// static
+bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
+ CheckedNumeric<int> month = exploded.month;
+ month--;
+ CheckedNumeric<int> year = exploded.year;
+ year -= 1900;
+ if (!month.IsValid() || !year.IsValid()) {
+ *time = Time(0);
+ return false;
+ }
+
+ struct tm timestruct;
+ timestruct.tm_sec = exploded.second;
+ timestruct.tm_min = exploded.minute;
+ timestruct.tm_hour = exploded.hour;
+ timestruct.tm_mday = exploded.day_of_month;
+ timestruct.tm_mon = month.ValueOrDie();
+ timestruct.tm_year = year.ValueOrDie();
+ timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this
+ timestruct.tm_yday = 0; // mktime/timegm ignore this
+ timestruct.tm_isdst = -1; // attempt to figure it out
+#if !defined(OS_NACL) && !defined(OS_SOLARIS) && !defined(OS_AIX)
+ timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore
+ timestruct.tm_zone = nullptr; // not a POSIX field, so mktime/timegm ignore
+#endif
+
+ SysTime seconds;
+
+ // Certain exploded dates do not really exist due to daylight saving times,
+ // and this causes mktime() to return implementation-defined values when
+ // tm_isdst is set to -1. On Android, the function will return -1, while the
+ // C libraries of other platforms typically return a liberally-chosen value.
+ // Handling this requires the special code below.
+
+ // SysTimeFromTimeStruct() modifies the input structure, save current value.
+ struct tm timestruct0 = timestruct;
+
+ seconds = SysTimeFromTimeStruct(&timestruct, is_local);
+ if (seconds == -1) {
+ // Get the time values with tm_isdst == 0 and 1, then select the closest one
+ // to UTC 00:00:00 that isn't -1.
+ timestruct = timestruct0;
+ timestruct.tm_isdst = 0;
+ int64_t seconds_isdst0 = SysTimeFromTimeStruct(&timestruct, is_local);
+
+ timestruct = timestruct0;
+ timestruct.tm_isdst = 1;
+ int64_t seconds_isdst1 = SysTimeFromTimeStruct(&timestruct, is_local);
+
+ // seconds_isdst0 or seconds_isdst1 can be -1 for some timezones.
+ // E.g. "CLST" (Chile Summer Time) returns -1 for 'tm_isdt == 1'.
+ if (seconds_isdst0 < 0)
+ seconds = seconds_isdst1;
+ else if (seconds_isdst1 < 0)
+ seconds = seconds_isdst0;
+ else
+ seconds = std::min(seconds_isdst0, seconds_isdst1);
+ }
+
+ // Handle overflow. Clamping the range to what mktime and timegm might
+ // return is the best that can be done here. It's not ideal, but it's better
+ // than failing here or ignoring the overflow case and treating each time
+ // overflow as one second prior to the epoch.
+ int64_t milliseconds = 0;
+ if (seconds == -1 && (exploded.year < 1969 || exploded.year > 1970)) {
+ // If exploded.year is 1969 or 1970, take -1 as correct, with the
+ // time indicating 1 second prior to the epoch. (1970 is allowed to handle
+ // time zone and DST offsets.) Otherwise, return the most future or past
+ // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC.
+ //
+ // The minimum and maximum representible times that mktime and timegm could
+ // return are used here instead of values outside that range to allow for
+ // proper round-tripping between exploded and counter-type time
+ // representations in the presence of possible truncation to time_t by
+ // division and use with other functions that accept time_t.
+ //
+ // When representing the most distant time in the future, add in an extra
+ // 999ms to avoid the time being less than any other possible value that
+ // this function can return.
+
+ // On Android, SysTime is int64_t, special care must be taken to avoid
+ // overflows.
+ const int64_t min_seconds = (sizeof(SysTime) < sizeof(int64_t))
+ ? std::numeric_limits<SysTime>::min()
+ : std::numeric_limits<int32_t>::min();
+ const int64_t max_seconds = (sizeof(SysTime) < sizeof(int64_t))
+ ? std::numeric_limits<SysTime>::max()
+ : std::numeric_limits<int32_t>::max();
+ if (exploded.year < 1969) {
+ milliseconds = min_seconds * kMillisecondsPerSecond;
+ } else {
+ milliseconds = max_seconds * kMillisecondsPerSecond;
+ milliseconds += (kMillisecondsPerSecond - 1);
+ }
+ } else {
+ CheckedNumeric<int64_t> checked_millis = seconds;
+ checked_millis *= kMillisecondsPerSecond;
+ checked_millis += exploded.millisecond;
+ if (!checked_millis.IsValid()) {
+ *time = Time(0);
+ return false;
+ }
+ milliseconds = checked_millis.ValueOrDie();
+ }
+
+ Time converted_time;
+ if (!FromMillisecondsSinceUnixEpoch(milliseconds, &converted_time)) {
+ *time = base::Time(0);
+ return false;
+ }
+
+ // If |exploded.day_of_month| is set to 31 on a 28-30 day month, it will
+ // return the first day of the next month. Thus round-trip the time and
+ // compare the initial |exploded| with |utc_to_exploded| time.
+ Time::Exploded to_exploded;
+ if (!is_local)
+ converted_time.UTCExplode(&to_exploded);
+ else
+ converted_time.LocalExplode(&to_exploded);
+
+ if (ExplodedMostlyEquals(to_exploded, exploded)) {
+ *time = converted_time;
+ return true;
+ }
+
+ *time = Time(0);
+ return false;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/time/time_now_posix.cc b/security/sandbox/chromium/base/time/time_now_posix.cc
new file mode 100644
index 0000000000..4ce93c0811
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time_now_posix.cc
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/time/time.h"
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+#if defined(OS_ANDROID) && !defined(__LP64__)
+#include <time64.h>
+#endif
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/numerics/safe_math.h"
+#include "base/time/time_override.h"
+#include "build/build_config.h"
+
+// Ensure the Fuchsia and Mac builds do not include this module. Instead,
+// non-POSIX implementation is used for sampling the system clocks.
+#if defined(OS_FUCHSIA) || defined(OS_MACOSX)
+#error "This implementation is for POSIX platforms other than Fuchsia or Mac."
+#endif
+
+namespace {
+
+int64_t ConvertTimespecToMicros(const struct timespec& ts) {
+ // On 32-bit systems, the calculation cannot overflow int64_t.
+ // 2**32 * 1000000 + 2**64 / 1000 < 2**63
+ if (sizeof(ts.tv_sec) <= 4 && sizeof(ts.tv_nsec) <= 8) {
+ int64_t result = ts.tv_sec;
+ result *= base::Time::kMicrosecondsPerSecond;
+ result += (ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond);
+ return result;
+ }
+ base::CheckedNumeric<int64_t> result(ts.tv_sec);
+ result *= base::Time::kMicrosecondsPerSecond;
+ result += (ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond);
+ return result.ValueOrDie();
+}
+
+// Helper function to get results from clock_gettime() and convert to a
+// microsecond timebase. Minimum requirement is MONOTONIC_CLOCK to be supported
+// on the system. FreeBSD 6 has CLOCK_MONOTONIC but defines
+// _POSIX_MONOTONIC_CLOCK to -1.
+#if (defined(OS_POSIX) && defined(_POSIX_MONOTONIC_CLOCK) && \
+ _POSIX_MONOTONIC_CLOCK >= 0) || \
+ defined(OS_BSD) || defined(OS_ANDROID)
+int64_t ClockNow(clockid_t clk_id) {
+ struct timespec ts;
+ CHECK(clock_gettime(clk_id, &ts) == 0);
+ return ConvertTimespecToMicros(ts);
+}
+#else // _POSIX_MONOTONIC_CLOCK
+#error No usable tick clock function on this platform.
+#endif // _POSIX_MONOTONIC_CLOCK
+
+} // namespace
+
+namespace base {
+
+// Time -----------------------------------------------------------------------
+
+namespace subtle {
+Time TimeNowIgnoringOverride() {
+ struct timeval tv;
+ struct timezone tz = {0, 0}; // UTC
+ CHECK(gettimeofday(&tv, &tz) == 0);
+ // Combine seconds and microseconds in a 64-bit field containing microseconds
+ // since the epoch. That's enough for nearly 600 centuries. Adjust from
+ // Unix (1970) to Windows (1601) epoch.
+ return Time() + TimeDelta::FromMicroseconds(
+ (tv.tv_sec * Time::kMicrosecondsPerSecond + tv.tv_usec) +
+ Time::kTimeTToMicrosecondsOffset);
+}
+
+Time TimeNowFromSystemTimeIgnoringOverride() {
+ // Just use TimeNowIgnoringOverride() because it returns the system time.
+ return TimeNowIgnoringOverride();
+}
+} // namespace subtle
+
+// TimeTicks ------------------------------------------------------------------
+
+namespace subtle {
+TimeTicks TimeTicksNowIgnoringOverride() {
+ return TimeTicks() + TimeDelta::FromMicroseconds(ClockNow(CLOCK_MONOTONIC));
+}
+} // namespace subtle
+
+// static
+TimeTicks::Clock TimeTicks::GetClock() {
+ return Clock::LINUX_CLOCK_MONOTONIC;
+}
+
+// static
+bool TimeTicks::IsHighResolution() {
+ return true;
+}
+
+// static
+bool TimeTicks::IsConsistentAcrossProcesses() {
+ return true;
+}
+
+// ThreadTicks ----------------------------------------------------------------
+
+namespace subtle {
+ThreadTicks ThreadTicksNowIgnoringOverride() {
+#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
+ defined(OS_ANDROID)
+ return ThreadTicks() +
+ TimeDelta::FromMicroseconds(ClockNow(CLOCK_THREAD_CPUTIME_ID));
+#else
+ NOTREACHED();
+ return ThreadTicks();
+#endif
+}
+} // namespace subtle
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/time/time_override.h b/security/sandbox/chromium/base/time/time_override.h
new file mode 100644
index 0000000000..ad3180c62a
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time_override.h
@@ -0,0 +1,74 @@
+// Copyright 2018 The Chromium Authors. 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_TIME_TIME_OVERRIDE_H_
+#define BASE_TIME_TIME_OVERRIDE_H_
+
+#include "base/base_export.h"
+#include "base/time/time.h"
+
+namespace base {
+
+using TimeNowFunction = decltype(&Time::Now);
+using TimeTicksNowFunction = decltype(&TimeTicks::Now);
+using ThreadTicksNowFunction = decltype(&ThreadTicks::Now);
+
+// Time overrides should be used with extreme caution. Discuss with //base/time
+// OWNERS before adding a new one.
+namespace subtle {
+
+// Override the return value of Time::Now and Time::NowFromSystemTime /
+// TimeTicks::Now / ThreadTicks::Now to emulate time, e.g. for tests or to
+// modify progression of time. Note that the override should be set while
+// single-threaded and before the first call to Now() to avoid threading issues
+// and inconsistencies in returned values. Nested overrides are not allowed.
+class BASE_EXPORT ScopedTimeClockOverrides {
+ public:
+ // Pass |nullptr| for any override if it shouldn't be overriden.
+ ScopedTimeClockOverrides(TimeNowFunction time_override,
+ TimeTicksNowFunction time_ticks_override,
+ ThreadTicksNowFunction thread_ticks_override);
+
+ // Restores the platform default Now() functions.
+ ~ScopedTimeClockOverrides();
+
+ static bool overrides_active() { return overrides_active_; }
+
+ private:
+ static bool overrides_active_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedTimeClockOverrides);
+};
+
+// These methods return the platform default Time::Now / TimeTicks::Now /
+// ThreadTicks::Now values even while an override is in place. These methods
+// should only be used in places where emulated time should be disregarded. For
+// example, they can be used to implement test timeouts for tests that may
+// override time.
+BASE_EXPORT Time TimeNowIgnoringOverride();
+BASE_EXPORT Time TimeNowFromSystemTimeIgnoringOverride();
+BASE_EXPORT TimeTicks TimeTicksNowIgnoringOverride();
+BASE_EXPORT ThreadTicks ThreadTicksNowIgnoringOverride();
+
+} // namespace subtle
+
+namespace internal {
+
+// These function pointers are used by platform-independent implementations of
+// the Now() methods and ScopedTimeClockOverrides. They are set to point to the
+// respective NowIgnoringOverride functions by default, but can also be set by
+// platform-specific code to select a default implementation at runtime, thereby
+// avoiding the indirection via the NowIgnoringOverride functions. Note that the
+// pointers can be overridden and later reset to the NowIgnoringOverride
+// functions by ScopedTimeClockOverrides.
+extern TimeNowFunction g_time_now_function;
+extern TimeNowFunction g_time_now_from_system_time_function;
+extern TimeTicksNowFunction g_time_ticks_now_function;
+extern ThreadTicksNowFunction g_thread_ticks_now_function;
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_TIME_TIME_OVERRIDE_H_
diff --git a/security/sandbox/chromium/base/time/time_win.cc b/security/sandbox/chromium/base/time/time_win.cc
new file mode 100644
index 0000000000..c1976e64a6
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time_win.cc
@@ -0,0 +1,810 @@
+// 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.
+
+
+// Windows Timer Primer
+//
+// A good article: http://www.ddj.com/windows/184416651
+// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258
+//
+// The default windows timer, GetSystemTimeAsFileTime is not very precise.
+// It is only good to ~15.5ms.
+//
+// QueryPerformanceCounter is the logical choice for a high-precision timer.
+// However, it is known to be buggy on some hardware. Specifically, it can
+// sometimes "jump". On laptops, QPC can also be very expensive to call.
+// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower
+// on laptops. A unittest exists which will show the relative cost of various
+// timers on any system.
+//
+// The next logical choice is timeGetTime(). timeGetTime has a precision of
+// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
+// applications on the system. By default, precision is only 15.5ms.
+// Unfortunately, we don't want to call timeBeginPeriod because we don't
+// want to affect other applications. Further, on mobile platforms, use of
+// faster multimedia timers can hurt battery life. See the intel
+// article about this here:
+// http://softwarecommunity.intel.com/articles/eng/1086.htm
+//
+// To work around all this, we're going to generally use timeGetTime(). We
+// will only increase the system-wide timer if we're not running on battery
+// power.
+
+#include "base/time/time.h"
+
+#include <windows.foundation.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <stdint.h>
+
+#include "base/atomicops.h"
+#include "base/bit_cast.h"
+#include "base/cpu.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time_override.h"
+#include "base/time/time_win_features.h"
+
+namespace base {
+
+namespace {
+
+// From MSDN, FILETIME "Contains a 64-bit value representing the number of
+// 100-nanosecond intervals since January 1, 1601 (UTC)."
+int64_t FileTimeToMicroseconds(const FILETIME& ft) {
+ // Need to bit_cast to fix alignment, then divide by 10 to convert
+ // 100-nanoseconds to microseconds. This only works on little-endian
+ // machines.
+ return bit_cast<int64_t, FILETIME>(ft) / 10;
+}
+
+void MicrosecondsToFileTime(int64_t us, FILETIME* ft) {
+ DCHECK_GE(us, 0LL) << "Time is less than 0, negative values are not "
+ "representable in FILETIME";
+
+ // Multiply by 10 to convert microseconds to 100-nanoseconds. Bit_cast will
+ // handle alignment problems. This only works on little-endian machines.
+ *ft = bit_cast<FILETIME, int64_t>(us * 10);
+}
+
+int64_t CurrentWallclockMicroseconds() {
+ FILETIME ft;
+ ::GetSystemTimeAsFileTime(&ft);
+ return FileTimeToMicroseconds(ft);
+}
+
+// Time between resampling the un-granular clock for this API.
+constexpr TimeDelta kMaxTimeToAvoidDrift = TimeDelta::FromSeconds(60);
+
+int64_t g_initial_time = 0;
+TimeTicks g_initial_ticks;
+
+void InitializeClock() {
+ g_initial_ticks = subtle::TimeTicksNowIgnoringOverride();
+ g_initial_time = CurrentWallclockMicroseconds();
+}
+
+// Interval to use when on DC power.
+UINT g_battery_power_interval_ms = 4;
+// Track the last value passed to timeBeginPeriod so that we can cancel that
+// call by calling timeEndPeriod with the same value. A value of zero means that
+// the timer frequency is not currently raised.
+UINT g_last_interval_requested_ms = 0;
+// Track if MinTimerIntervalHighResMs() or MinTimerIntervalLowResMs() is active.
+// For most purposes this could also be named g_is_on_ac_power.
+bool g_high_res_timer_enabled = false;
+// How many times the high resolution timer has been called.
+uint32_t g_high_res_timer_count = 0;
+// Start time of the high resolution timer usage monitoring. This is needed
+// to calculate the usage as percentage of the total elapsed time.
+TimeTicks g_high_res_timer_usage_start;
+// The cumulative time the high resolution timer has been in use since
+// |g_high_res_timer_usage_start| moment.
+TimeDelta g_high_res_timer_usage;
+// Timestamp of the last activation change of the high resolution timer. This
+// is used to calculate the cumulative usage.
+TimeTicks g_high_res_timer_last_activation;
+// The lock to control access to the above set of variables.
+Lock* GetHighResLock() {
+ static auto* lock = new Lock();
+ return lock;
+}
+
+// The two values that ActivateHighResolutionTimer uses to set the systemwide
+// timer interrupt frequency on Windows. These control how precise timers are
+// but also have a big impact on battery life.
+
+// Used when a faster timer has been requested (g_high_res_timer_count > 0) and
+// the computer is running on AC power (plugged in) so that it's okay to go to
+// the highest frequency.
+UINT MinTimerIntervalHighResMs() {
+ return 1;
+}
+
+// Used when a faster timer has been requested (g_high_res_timer_count > 0) and
+// the computer is running on DC power (battery) so that we don't want to raise
+// the timer frequency as much.
+UINT MinTimerIntervalLowResMs() {
+ return g_battery_power_interval_ms;
+}
+
+// Calculate the desired timer interrupt interval. Note that zero means that the
+// system default should be used.
+UINT GetIntervalMs() {
+ if (!g_high_res_timer_count)
+ return 0; // Use the default, typically 15.625
+ if (g_high_res_timer_enabled)
+ return MinTimerIntervalHighResMs();
+ return MinTimerIntervalLowResMs();
+}
+
+// Compare the currently requested timer interrupt interval to the last interval
+// requested and update if necessary (by cancelling the old request and making a
+// new request). If there is no change then do nothing.
+void UpdateTimerIntervalLocked() {
+ UINT new_interval = GetIntervalMs();
+ if (new_interval == g_last_interval_requested_ms)
+ return;
+ if (g_last_interval_requested_ms) {
+ // Record how long the timer interrupt frequency was raised.
+ g_high_res_timer_usage += subtle::TimeTicksNowIgnoringOverride() -
+ g_high_res_timer_last_activation;
+ // Reset the timer interrupt back to the default.
+ timeEndPeriod(g_last_interval_requested_ms);
+ }
+ g_last_interval_requested_ms = new_interval;
+ if (g_last_interval_requested_ms) {
+ // Record when the timer interrupt was raised.
+ g_high_res_timer_last_activation = subtle::TimeTicksNowIgnoringOverride();
+ timeBeginPeriod(g_last_interval_requested_ms);
+ }
+}
+
+// Returns the current value of the performance counter.
+uint64_t QPCNowRaw() {
+ LARGE_INTEGER perf_counter_now = {};
+ // According to the MSDN documentation for QueryPerformanceCounter(), this
+ // will never fail on systems that run XP or later.
+ // https://msdn.microsoft.com/library/windows/desktop/ms644904.aspx
+ ::QueryPerformanceCounter(&perf_counter_now);
+ return perf_counter_now.QuadPart;
+}
+
+bool SafeConvertToWord(int in, WORD* out) {
+ CheckedNumeric<WORD> result = in;
+ *out = result.ValueOrDefault(std::numeric_limits<WORD>::max());
+ return result.IsValid();
+}
+
+} // namespace
+
+// Time -----------------------------------------------------------------------
+
+namespace subtle {
+Time TimeNowIgnoringOverride() {
+ if (g_initial_time == 0)
+ InitializeClock();
+
+ // We implement time using the high-resolution timers so that we can get
+ // timeouts which are smaller than 10-15ms. If we just used
+ // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
+ //
+ // To make this work, we initialize the clock (g_initial_time) and the
+ // counter (initial_ctr). To compute the initial time, we can check
+ // the number of ticks that have elapsed, and compute the delta.
+ //
+ // To avoid any drift, we periodically resync the counters to the system
+ // clock.
+ while (true) {
+ TimeTicks ticks = TimeTicksNowIgnoringOverride();
+
+ // Calculate the time elapsed since we started our timer
+ TimeDelta elapsed = ticks - g_initial_ticks;
+
+ // Check if enough time has elapsed that we need to resync the clock.
+ if (elapsed > kMaxTimeToAvoidDrift) {
+ InitializeClock();
+ continue;
+ }
+
+ return Time() + elapsed + TimeDelta::FromMicroseconds(g_initial_time);
+ }
+}
+
+Time TimeNowFromSystemTimeIgnoringOverride() {
+ // Force resync.
+ InitializeClock();
+ return Time() + TimeDelta::FromMicroseconds(g_initial_time);
+}
+} // namespace subtle
+
+// static
+Time Time::FromFileTime(FILETIME ft) {
+ if (bit_cast<int64_t, FILETIME>(ft) == 0)
+ return Time();
+ if (ft.dwHighDateTime == std::numeric_limits<DWORD>::max() &&
+ ft.dwLowDateTime == std::numeric_limits<DWORD>::max())
+ return Max();
+ return Time(FileTimeToMicroseconds(ft));
+}
+
+FILETIME Time::ToFileTime() const {
+ if (is_null())
+ return bit_cast<FILETIME, int64_t>(0);
+ if (is_max()) {
+ FILETIME result;
+ result.dwHighDateTime = std::numeric_limits<DWORD>::max();
+ result.dwLowDateTime = std::numeric_limits<DWORD>::max();
+ return result;
+ }
+ FILETIME utc_ft;
+ MicrosecondsToFileTime(us_, &utc_ft);
+ return utc_ft;
+}
+
+void Time::ReadMinTimerIntervalLowResMs() {
+ AutoLock lock(*GetHighResLock());
+ // Read the setting for what interval to use on battery power.
+ g_battery_power_interval_ms =
+ base::FeatureList::IsEnabled(base::kSlowDCTimerInterruptsWin) ? 8 : 4;
+ UpdateTimerIntervalLocked();
+}
+
+// static
+// Enable raising of the system-global timer interrupt frequency to 1 kHz (when
+// enable is true, which happens when on AC power) or some lower frequency when
+// on battery power (when enable is false). If the g_high_res_timer_enabled
+// setting hasn't actually changed or if if there are no outstanding requests
+// (if g_high_res_timer_count is zero) then do nothing.
+// TL;DR - call this when going from AC to DC power or vice-versa.
+void Time::EnableHighResolutionTimer(bool enable) {
+ AutoLock lock(*GetHighResLock());
+ g_high_res_timer_enabled = enable;
+ UpdateTimerIntervalLocked();
+}
+
+// static
+// Request that the system-global Windows timer interrupt frequency be raised.
+// How high the frequency is raised depends on the system's power state and
+// possibly other options.
+// TL;DR - call this at the beginning and end of a time period where you want
+// higher frequency timer interrupts. Each call with activating=true must be
+// paired with a subsequent activating=false call.
+bool Time::ActivateHighResolutionTimer(bool activating) {
+ // We only do work on the transition from zero to one or one to zero so we
+ // can easily undo the effect (if necessary) when EnableHighResolutionTimer is
+ // called.
+ const uint32_t max = std::numeric_limits<uint32_t>::max();
+
+ AutoLock lock(*GetHighResLock());
+ if (activating) {
+ DCHECK_NE(g_high_res_timer_count, max);
+ ++g_high_res_timer_count;
+ } else {
+ DCHECK_NE(g_high_res_timer_count, 0u);
+ --g_high_res_timer_count;
+ }
+ UpdateTimerIntervalLocked();
+ return true;
+}
+
+// static
+// See if the timer interrupt interval has been set to the lowest value.
+bool Time::IsHighResolutionTimerInUse() {
+ AutoLock lock(*GetHighResLock());
+ return g_last_interval_requested_ms == MinTimerIntervalHighResMs();
+}
+
+// static
+void Time::ResetHighResolutionTimerUsage() {
+ AutoLock lock(*GetHighResLock());
+ g_high_res_timer_usage = TimeDelta();
+ g_high_res_timer_usage_start = subtle::TimeTicksNowIgnoringOverride();
+ if (g_high_res_timer_count > 0)
+ g_high_res_timer_last_activation = g_high_res_timer_usage_start;
+}
+
+// static
+double Time::GetHighResolutionTimerUsage() {
+ AutoLock lock(*GetHighResLock());
+ TimeTicks now = subtle::TimeTicksNowIgnoringOverride();
+ TimeDelta elapsed_time = now - g_high_res_timer_usage_start;
+ if (elapsed_time.is_zero()) {
+ // This is unexpected but possible if TimeTicks resolution is low and
+ // GetHighResolutionTimerUsage() is called promptly after
+ // ResetHighResolutionTimerUsage().
+ return 0.0;
+ }
+ TimeDelta used_time = g_high_res_timer_usage;
+ if (g_high_res_timer_count > 0) {
+ // If currently activated add the remainder of time since the last
+ // activation.
+ used_time += now - g_high_res_timer_last_activation;
+ }
+ return used_time.InMillisecondsF() / elapsed_time.InMillisecondsF() * 100;
+}
+
+// static
+bool Time::FromExploded(bool is_local, const Exploded& exploded, Time* time) {
+ // Create the system struct representing our exploded time. It will either be
+ // in local time or UTC.If casting from int to WORD results in overflow,
+ // fail and return Time(0).
+ SYSTEMTIME st;
+ if (!SafeConvertToWord(exploded.year, &st.wYear) ||
+ !SafeConvertToWord(exploded.month, &st.wMonth) ||
+ !SafeConvertToWord(exploded.day_of_week, &st.wDayOfWeek) ||
+ !SafeConvertToWord(exploded.day_of_month, &st.wDay) ||
+ !SafeConvertToWord(exploded.hour, &st.wHour) ||
+ !SafeConvertToWord(exploded.minute, &st.wMinute) ||
+ !SafeConvertToWord(exploded.second, &st.wSecond) ||
+ !SafeConvertToWord(exploded.millisecond, &st.wMilliseconds)) {
+ *time = Time(0);
+ return false;
+ }
+
+ FILETIME ft;
+ bool success = true;
+ // Ensure that it's in UTC.
+ if (is_local) {
+ SYSTEMTIME utc_st;
+ success = TzSpecificLocalTimeToSystemTime(nullptr, &st, &utc_st) &&
+ SystemTimeToFileTime(&utc_st, &ft);
+ } else {
+ success = !!SystemTimeToFileTime(&st, &ft);
+ }
+
+ if (!success) {
+ *time = Time(0);
+ return false;
+ }
+
+ *time = Time(FileTimeToMicroseconds(ft));
+ return true;
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ if (us_ < 0LL) {
+ // We are not able to convert it to FILETIME.
+ ZeroMemory(exploded, sizeof(*exploded));
+ return;
+ }
+
+ // FILETIME in UTC.
+ FILETIME utc_ft;
+ MicrosecondsToFileTime(us_, &utc_ft);
+
+ // FILETIME in local time if necessary.
+ bool success = true;
+ // FILETIME in SYSTEMTIME (exploded).
+ SYSTEMTIME st = {0};
+ if (is_local) {
+ SYSTEMTIME utc_st;
+ // We don't use FileTimeToLocalFileTime here, since it uses the current
+ // settings for the time zone and daylight saving time. Therefore, if it is
+ // daylight saving time, it will take daylight saving time into account,
+ // even if the time you are converting is in standard time.
+ success = FileTimeToSystemTime(&utc_ft, &utc_st) &&
+ SystemTimeToTzSpecificLocalTime(nullptr, &utc_st, &st);
+ } else {
+ success = !!FileTimeToSystemTime(&utc_ft, &st);
+ }
+
+ if (!success) {
+ NOTREACHED() << "Unable to convert time, don't know why";
+ ZeroMemory(exploded, sizeof(*exploded));
+ return;
+ }
+
+ exploded->year = st.wYear;
+ exploded->month = st.wMonth;
+ exploded->day_of_week = st.wDayOfWeek;
+ exploded->day_of_month = st.wDay;
+ exploded->hour = st.wHour;
+ exploded->minute = st.wMinute;
+ exploded->second = st.wSecond;
+ exploded->millisecond = st.wMilliseconds;
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+namespace {
+
+// We define a wrapper to adapt between the __stdcall and __cdecl call of the
+// mock function, and to avoid a static constructor. Assigning an import to a
+// function pointer directly would require setup code to fetch from the IAT.
+DWORD timeGetTimeWrapper() {
+ return timeGetTime();
+}
+
+DWORD (*g_tick_function)(void) = &timeGetTimeWrapper;
+
+// A structure holding the most significant bits of "last seen" and a
+// "rollover" counter.
+union LastTimeAndRolloversState {
+ // The state as a single 32-bit opaque value.
+ subtle::Atomic32 as_opaque_32;
+
+ // The state as usable values.
+ struct {
+ // The top 8-bits of the "last" time. This is enough to check for rollovers
+ // and the small bit-size means fewer CompareAndSwap operations to store
+ // changes in state, which in turn makes for fewer retries.
+ uint8_t last_8;
+ // A count of the number of detected rollovers. Using this as bits 47-32
+ // of the upper half of a 64-bit value results in a 48-bit tick counter.
+ // This extends the total rollover period from about 49 days to about 8800
+ // years while still allowing it to be stored with last_8 in a single
+ // 32-bit value.
+ uint16_t rollovers;
+ } as_values;
+};
+subtle::Atomic32 g_last_time_and_rollovers = 0;
+static_assert(
+ sizeof(LastTimeAndRolloversState) <= sizeof(g_last_time_and_rollovers),
+ "LastTimeAndRolloversState does not fit in a single atomic word");
+
+// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
+// because it returns the number of milliseconds since Windows has started,
+// which will roll over the 32-bit value every ~49 days. We try to track
+// rollover ourselves, which works if TimeTicks::Now() is called at least every
+// 48.8 days (not 49 days because only changes in the top 8 bits get noticed).
+TimeTicks RolloverProtectedNow() {
+ LastTimeAndRolloversState state;
+ DWORD now; // DWORD is always unsigned 32 bits.
+
+ while (true) {
+ // Fetch the "now" and "last" tick values, updating "last" with "now" and
+ // incrementing the "rollovers" counter if the tick-value has wrapped back
+ // around. Atomic operations ensure that both "last" and "rollovers" are
+ // always updated together.
+ int32_t original = subtle::Acquire_Load(&g_last_time_and_rollovers);
+ state.as_opaque_32 = original;
+ now = g_tick_function();
+ uint8_t now_8 = static_cast<uint8_t>(now >> 24);
+ if (now_8 < state.as_values.last_8)
+ ++state.as_values.rollovers;
+ state.as_values.last_8 = now_8;
+
+ // If the state hasn't changed, exit the loop.
+ if (state.as_opaque_32 == original)
+ break;
+
+ // Save the changed state. If the existing value is unchanged from the
+ // original, exit the loop.
+ int32_t check = subtle::Release_CompareAndSwap(
+ &g_last_time_and_rollovers, original, state.as_opaque_32);
+ if (check == original)
+ break;
+
+ // Another thread has done something in between so retry from the top.
+ }
+
+ return TimeTicks() +
+ TimeDelta::FromMilliseconds(
+ now + (static_cast<uint64_t>(state.as_values.rollovers) << 32));
+}
+
+// Discussion of tick counter options on Windows:
+//
+// (1) CPU cycle counter. (Retrieved via RDTSC)
+// The CPU counter provides the highest resolution time stamp and is the least
+// expensive to retrieve. However, on older CPUs, two issues can affect its
+// reliability: First it is maintained per processor and not synchronized
+// between processors. Also, the counters will change frequency due to thermal
+// and power changes, and stop in some states.
+//
+// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high-
+// resolution (<1 microsecond) time stamp. On most hardware running today, it
+// auto-detects and uses the constant-rate RDTSC counter to provide extremely
+// efficient and reliable time stamps.
+//
+// On older CPUs where RDTSC is unreliable, it falls back to using more
+// expensive (20X to 40X more costly) alternate clocks, such as HPET or the ACPI
+// PM timer, and can involve system calls; and all this is up to the HAL (with
+// some help from ACPI). According to
+// http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx, in the
+// worst case, it gets the counter from the rollover interrupt on the
+// programmable interrupt timer. In best cases, the HAL may conclude that the
+// RDTSC counter runs at a constant frequency, then it uses that instead. On
+// multiprocessor machines, it will try to verify the values returned from
+// RDTSC on each processor are consistent with each other, and apply a handful
+// of workarounds for known buggy hardware. In other words, QPC is supposed to
+// give consistent results on a multiprocessor computer, but for older CPUs it
+// can be unreliable due bugs in BIOS or HAL.
+//
+// (3) System time. The system time provides a low-resolution (from ~1 to ~15.6
+// milliseconds) time stamp but is comparatively less expensive to retrieve and
+// more reliable. Time::EnableHighResolutionTimer() and
+// Time::ActivateHighResolutionTimer() can be called to alter the resolution of
+// this timer; and also other Windows applications can alter it, affecting this
+// one.
+
+TimeTicks InitialNowFunction();
+
+// See "threading notes" in InitializeNowFunctionPointer() for details on how
+// concurrent reads/writes to these globals has been made safe.
+TimeTicksNowFunction g_time_ticks_now_ignoring_override_function =
+ &InitialNowFunction;
+int64_t g_qpc_ticks_per_second = 0;
+
+// As of January 2015, use of <atomic> is forbidden in Chromium code. This is
+// what std::atomic_thread_fence does on Windows on all Intel architectures when
+// the memory_order argument is anything but std::memory_order_seq_cst:
+#define ATOMIC_THREAD_FENCE(memory_order) _ReadWriteBarrier();
+
+TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) {
+ // Ensure that the assignment to |g_qpc_ticks_per_second|, made in
+ // InitializeNowFunctionPointer(), has happened by this point.
+ ATOMIC_THREAD_FENCE(memory_order_acquire);
+
+ DCHECK_GT(g_qpc_ticks_per_second, 0);
+
+ // If the QPC Value is below the overflow threshold, we proceed with
+ // simple multiply and divide.
+ if (qpc_value < Time::kQPCOverflowThreshold) {
+ return TimeDelta::FromMicroseconds(
+ qpc_value * Time::kMicrosecondsPerSecond / g_qpc_ticks_per_second);
+ }
+ // Otherwise, calculate microseconds in a round about manner to avoid
+ // overflow and precision issues.
+ int64_t whole_seconds = qpc_value / g_qpc_ticks_per_second;
+ int64_t leftover_ticks = qpc_value - (whole_seconds * g_qpc_ticks_per_second);
+ return TimeDelta::FromMicroseconds(
+ (whole_seconds * Time::kMicrosecondsPerSecond) +
+ ((leftover_ticks * Time::kMicrosecondsPerSecond) /
+ g_qpc_ticks_per_second));
+}
+
+TimeTicks QPCNow() {
+ return TimeTicks() + QPCValueToTimeDelta(QPCNowRaw());
+}
+
+void InitializeNowFunctionPointer() {
+ LARGE_INTEGER ticks_per_sec = {};
+ if (!QueryPerformanceFrequency(&ticks_per_sec))
+ ticks_per_sec.QuadPart = 0;
+
+ // If Windows cannot provide a QPC implementation, TimeTicks::Now() must use
+ // the low-resolution clock.
+ //
+ // If the QPC implementation is expensive and/or unreliable, TimeTicks::Now()
+ // will still use the low-resolution clock. A CPU lacking a non-stop time
+ // counter will cause Windows to provide an alternate QPC implementation that
+ // works, but is expensive to use.
+ //
+ // Otherwise, Now uses the high-resolution QPC clock. As of 21 August 2015,
+ // ~72% of users fall within this category.
+ TimeTicksNowFunction now_function;
+ CPU cpu;
+ if (ticks_per_sec.QuadPart <= 0 || !cpu.has_non_stop_time_stamp_counter()) {
+ now_function = &RolloverProtectedNow;
+ } else {
+ now_function = &QPCNow;
+ }
+
+ // Threading note 1: In an unlikely race condition, it's possible for two or
+ // more threads to enter InitializeNowFunctionPointer() in parallel. This is
+ // not a problem since all threads should end up writing out the same values
+ // to the global variables.
+ //
+ // Threading note 2: A release fence is placed here to ensure, from the
+ // perspective of other threads using the function pointers, that the
+ // assignment to |g_qpc_ticks_per_second| happens before the function pointers
+ // are changed.
+ g_qpc_ticks_per_second = ticks_per_sec.QuadPart;
+ ATOMIC_THREAD_FENCE(memory_order_release);
+ // Also set g_time_ticks_now_function to avoid the additional indirection via
+ // TimeTicksNowIgnoringOverride() for future calls to TimeTicks::Now(). But
+ // g_time_ticks_now_function may have already be overridden.
+ if (internal::g_time_ticks_now_function ==
+ &subtle::TimeTicksNowIgnoringOverride) {
+ internal::g_time_ticks_now_function = now_function;
+ }
+ g_time_ticks_now_ignoring_override_function = now_function;
+}
+
+TimeTicks InitialNowFunction() {
+ InitializeNowFunctionPointer();
+ return g_time_ticks_now_ignoring_override_function();
+}
+
+} // namespace
+
+// static
+TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
+ TickFunctionType ticker) {
+ TickFunctionType old = g_tick_function;
+ g_tick_function = ticker;
+ subtle::NoBarrier_Store(&g_last_time_and_rollovers, 0);
+ return old;
+}
+
+namespace subtle {
+TimeTicks TimeTicksNowIgnoringOverride() {
+ return g_time_ticks_now_ignoring_override_function();
+}
+} // namespace subtle
+
+// static
+bool TimeTicks::IsHighResolution() {
+ if (g_time_ticks_now_ignoring_override_function == &InitialNowFunction)
+ InitializeNowFunctionPointer();
+ return g_time_ticks_now_ignoring_override_function == &QPCNow;
+}
+
+// static
+bool TimeTicks::IsConsistentAcrossProcesses() {
+ // According to Windows documentation [1] QPC is consistent post-Windows
+ // Vista. So if we are using QPC then we are consistent which is the same as
+ // being high resolution.
+ //
+ // [1] https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
+ //
+ // "In general, the performance counter results are consistent across all
+ // processors in multi-core and multi-processor systems, even when measured on
+ // different threads or processes. Here are some exceptions to this rule:
+ // - Pre-Windows Vista operating systems that run on certain processors might
+ // violate this consistency because of one of these reasons:
+ // 1. The hardware processors have a non-invariant TSC and the BIOS
+ // doesn't indicate this condition correctly.
+ // 2. The TSC synchronization algorithm that was used wasn't suitable for
+ // systems with large numbers of processors."
+ return IsHighResolution();
+}
+
+// static
+TimeTicks::Clock TimeTicks::GetClock() {
+ return IsHighResolution() ?
+ Clock::WIN_QPC : Clock::WIN_ROLLOVER_PROTECTED_TIME_GET_TIME;
+}
+
+// ThreadTicks ----------------------------------------------------------------
+
+namespace subtle {
+ThreadTicks ThreadTicksNowIgnoringOverride() {
+ return ThreadTicks::GetForThread(PlatformThread::CurrentHandle());
+}
+} // namespace subtle
+
+// static
+ThreadTicks ThreadTicks::GetForThread(
+ const PlatformThreadHandle& thread_handle) {
+ DCHECK(IsSupported());
+
+#if defined(ARCH_CPU_ARM64)
+ // QueryThreadCycleTime versus TSCTicksPerSecond doesn't have much relation to
+ // actual elapsed time on Windows on Arm, because QueryThreadCycleTime is
+ // backed by the actual number of CPU cycles executed, rather than a
+ // constant-rate timer like Intel. To work around this, use GetThreadTimes
+ // (which isn't as accurate but is meaningful as a measure of elapsed
+ // per-thread time).
+ FILETIME creation_time, exit_time, kernel_time, user_time;
+ ::GetThreadTimes(thread_handle.platform_handle(), &creation_time, &exit_time,
+ &kernel_time, &user_time);
+
+ int64_t us = FileTimeToMicroseconds(user_time);
+ return ThreadTicks(us);
+#else
+ // Get the number of TSC ticks used by the current thread.
+ ULONG64 thread_cycle_time = 0;
+ ::QueryThreadCycleTime(thread_handle.platform_handle(), &thread_cycle_time);
+
+ // Get the frequency of the TSC.
+ double tsc_ticks_per_second = TSCTicksPerSecond();
+ if (tsc_ticks_per_second == 0)
+ return ThreadTicks();
+
+ // Return the CPU time of the current thread.
+ double thread_time_seconds = thread_cycle_time / tsc_ticks_per_second;
+ return ThreadTicks(
+ static_cast<int64_t>(thread_time_seconds * Time::kMicrosecondsPerSecond));
+#endif
+}
+
+// static
+bool ThreadTicks::IsSupportedWin() {
+ static bool is_supported = CPU().has_non_stop_time_stamp_counter();
+ return is_supported;
+}
+
+// static
+void ThreadTicks::WaitUntilInitializedWin() {
+#if !defined(ARCH_CPU_ARM64)
+ while (TSCTicksPerSecond() == 0)
+ ::Sleep(10);
+#endif
+}
+
+#if !defined(ARCH_CPU_ARM64)
+double ThreadTicks::TSCTicksPerSecond() {
+ DCHECK(IsSupported());
+ // The value returned by QueryPerformanceFrequency() cannot be used as the TSC
+ // frequency, because there is no guarantee that the TSC frequency is equal to
+ // the performance counter frequency.
+ // The TSC frequency is cached in a static variable because it takes some time
+ // to compute it.
+ static double tsc_ticks_per_second = 0;
+ if (tsc_ticks_per_second != 0)
+ return tsc_ticks_per_second;
+
+ // Increase the thread priority to reduces the chances of having a context
+ // switch during a reading of the TSC and the performance counter.
+ int previous_priority = ::GetThreadPriority(::GetCurrentThread());
+ ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+
+ // The first time that this function is called, make an initial reading of the
+ // TSC and the performance counter.
+
+ static const uint64_t tsc_initial = __rdtsc();
+ static const uint64_t perf_counter_initial = QPCNowRaw();
+
+ // Make a another reading of the TSC and the performance counter every time
+ // that this function is called.
+ uint64_t tsc_now = __rdtsc();
+ uint64_t perf_counter_now = QPCNowRaw();
+
+ // Reset the thread priority.
+ ::SetThreadPriority(::GetCurrentThread(), previous_priority);
+
+ // Make sure that at least 50 ms elapsed between the 2 readings. The first
+ // time that this function is called, we don't expect this to be the case.
+ // Note: The longer the elapsed time between the 2 readings is, the more
+ // accurate the computed TSC frequency will be. The 50 ms value was
+ // chosen because local benchmarks show that it allows us to get a
+ // stddev of less than 1 tick/us between multiple runs.
+ // Note: According to the MSDN documentation for QueryPerformanceFrequency(),
+ // this will never fail on systems that run XP or later.
+ // https://msdn.microsoft.com/library/windows/desktop/ms644905.aspx
+ LARGE_INTEGER perf_counter_frequency = {};
+ ::QueryPerformanceFrequency(&perf_counter_frequency);
+ DCHECK_GE(perf_counter_now, perf_counter_initial);
+ uint64_t perf_counter_ticks = perf_counter_now - perf_counter_initial;
+ double elapsed_time_seconds =
+ perf_counter_ticks / static_cast<double>(perf_counter_frequency.QuadPart);
+
+ static constexpr double kMinimumEvaluationPeriodSeconds = 0.05;
+ if (elapsed_time_seconds < kMinimumEvaluationPeriodSeconds)
+ return 0;
+
+ // Compute the frequency of the TSC.
+ DCHECK_GE(tsc_now, tsc_initial);
+ uint64_t tsc_ticks = tsc_now - tsc_initial;
+ tsc_ticks_per_second = tsc_ticks / elapsed_time_seconds;
+
+ return tsc_ticks_per_second;
+}
+#endif // defined(ARCH_CPU_ARM64)
+
+// static
+TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) {
+ return TimeTicks() + QPCValueToTimeDelta(qpc_value);
+}
+
+// TimeDelta ------------------------------------------------------------------
+
+// static
+TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
+ return QPCValueToTimeDelta(qpc_value);
+}
+
+// static
+TimeDelta TimeDelta::FromFileTime(FILETIME ft) {
+ return TimeDelta::FromMicroseconds(FileTimeToMicroseconds(ft));
+}
+
+// static
+TimeDelta TimeDelta::FromWinrtDateTime(ABI::Windows::Foundation::DateTime dt) {
+ // UniversalTime is 100 ns intervals since January 1, 1601 (UTC)
+ return TimeDelta::FromMicroseconds(dt.UniversalTime / 10);
+}
+
+ABI::Windows::Foundation::DateTime TimeDelta::ToWinrtDateTime() const {
+ ABI::Windows::Foundation::DateTime date_time;
+ date_time.UniversalTime = InMicroseconds() * 10;
+ return date_time;
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/time/time_win_features.cc b/security/sandbox/chromium/base/time/time_win_features.cc
new file mode 100644
index 0000000000..edf3024e70
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time_win_features.cc
@@ -0,0 +1,14 @@
+// Copyright 2019 The Chromium Authors. 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/time/time_win_features.h"
+
+#include "base/feature_list.h"
+
+namespace base {
+
+const Feature kSlowDCTimerInterruptsWin{"SlowDCTimerInterruptsWin",
+ FEATURE_DISABLED_BY_DEFAULT};
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/time/time_win_features.h b/security/sandbox/chromium/base/time/time_win_features.h
new file mode 100644
index 0000000000..b586ec2419
--- /dev/null
+++ b/security/sandbox/chromium/base/time/time_win_features.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Chromium Authors. 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_TIME_TIME_WIN_FEATURES_H_
+#define BASE_TIME_TIME_WIN_FEATURES_H_
+
+#include "base/base_export.h"
+
+namespace base {
+
+struct Feature;
+
+// Slow the maximum interrupt timer on battery power to 8 ms, instead of the
+// default 4 ms.
+extern const BASE_EXPORT Feature kSlowDCTimerInterruptsWin;
+
+} // namespace base
+
+#endif // BASE_TIME_TIME_WIN_FEATURES_H_
diff --git a/security/sandbox/chromium/base/token.cc b/security/sandbox/chromium/base/token.cc
new file mode 100644
index 0000000000..e7ad896713
--- /dev/null
+++ b/security/sandbox/chromium/base/token.cc
@@ -0,0 +1,28 @@
+// Copyright 2018 The Chromium Authors. 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/token.h"
+
+#include <inttypes.h>
+
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace base {
+
+// static
+Token Token::CreateRandom() {
+ Token token;
+
+ // Use base::RandBytes instead of crypto::RandBytes, because crypto calls the
+ // base version directly, and to prevent the dependency from base/ to crypto/.
+ base::RandBytes(&token, sizeof(token));
+ return token;
+}
+
+std::string Token::ToString() const {
+ return base::StringPrintf("%016" PRIX64 "%016" PRIX64, high_, low_);
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/token.h b/security/sandbox/chromium/base/token.h
new file mode 100644
index 0000000000..e2843a6d14
--- /dev/null
+++ b/security/sandbox/chromium/base/token.h
@@ -0,0 +1,72 @@
+// Copyright 2018 The Chromium Authors. 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_TOKEN_H_
+#define BASE_TOKEN_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <tuple>
+
+#include "base/base_export.h"
+#include "base/hash/hash.h"
+
+namespace base {
+
+// A Token is a randomly chosen 128-bit integer. This class supports generation
+// from a cryptographically strong random source, or constexpr construction over
+// fixed values (e.g. to store a pre-generated constant value). Tokens are
+// similar in spirit and purpose to UUIDs, without many of the constraints and
+// expectations (such as byte layout and string representation) clasically
+// associated with UUIDs.
+class BASE_EXPORT Token {
+ public:
+ // Constructs a zero Token.
+ constexpr Token() : high_(0), low_(0) {}
+
+ // Constructs a Token with |high| and |low| as its contents.
+ constexpr Token(uint64_t high, uint64_t low) : high_(high), low_(low) {}
+
+ // Constructs a new Token with random |high| and |low| values taken from a
+ // cryptographically strong random source.
+ static Token CreateRandom();
+
+ // The high and low 64 bits of this Token.
+ uint64_t high() const { return high_; }
+ uint64_t low() const { return low_; }
+
+ bool is_zero() const { return high_ == 0 && low_ == 0; }
+
+ bool operator==(const Token& other) const {
+ return high_ == other.high_ && low_ == other.low_;
+ }
+
+ bool operator!=(const Token& other) const { return !(*this == other); }
+
+ bool operator<(const Token& other) const {
+ return std::tie(high_, low_) < std::tie(other.high_, other.low_);
+ }
+
+ // Generates a string representation of this Token useful for e.g. logging.
+ std::string ToString() const;
+
+ private:
+ // Note: Two uint64_t are used instead of uint8_t[16] in order to have a
+ // simpler implementation, paricularly for |ToString()|, |is_zero()|, and
+ // constexpr value construction.
+ uint64_t high_;
+ uint64_t low_;
+};
+
+// For use in std::unordered_map.
+struct TokenHash {
+ size_t operator()(const base::Token& token) const {
+ return base::HashInts64(token.high(), token.low());
+ }
+};
+
+} // namespace base
+
+#endif // BASE_TOKEN_H_
diff --git a/security/sandbox/chromium/base/tuple.h b/security/sandbox/chromium/base/tuple.h
new file mode 100644
index 0000000000..58681d515a
--- /dev/null
+++ b/security/sandbox/chromium/base/tuple.h
@@ -0,0 +1,112 @@
+// 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.
+
+// Use std::tuple as tuple type. This file contains helper functions for
+// working with std::tuples.
+// The functions DispatchToMethod and DispatchToFunction take a function pointer
+// or instance and method pointer, and unpack a tuple into arguments to the
+// call.
+//
+// Example usage:
+// // These two methods of creating a Tuple are identical.
+// std::tuple<int, const char*> tuple_a(1, "wee");
+// std::tuple<int, const char*> tuple_b = std::make_tuple(1, "wee");
+//
+// void SomeFunc(int a, const char* b) { }
+// DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee")
+// DispatchToFunction(
+// &SomeFunc, std::make_tuple(10, "foo")); // SomeFunc(10, "foo")
+//
+// struct { void SomeMeth(int a, int b, int c) { } } foo;
+// DispatchToMethod(&foo, &Foo::SomeMeth, std::make_tuple(1, 2, 3));
+// // foo->SomeMeth(1, 2, 3);
+
+#ifndef BASE_TUPLE_H_
+#define BASE_TUPLE_H_
+
+#include <stddef.h>
+#include <tuple>
+#include <utility>
+
+#include "build/build_config.h"
+
+namespace base {
+
+// Dispatchers ----------------------------------------------------------------
+//
+// Helper functions that call the given method on an object, with the unpacked
+// tuple arguments. Notice that they all have the same number of arguments,
+// so you need only write:
+// DispatchToMethod(object, &Object::method, args);
+// This is very useful for templated dispatchers, since they don't need to know
+// what type |args| is.
+
+// Non-Static Dispatchers with no out params.
+
+template <typename ObjT, typename Method, typename Tuple, size_t... Ns>
+inline void DispatchToMethodImpl(const ObjT& obj,
+ Method method,
+ Tuple&& args,
+ std::index_sequence<Ns...>) {
+ (obj->*method)(std::get<Ns>(std::forward<Tuple>(args))...);
+}
+
+template <typename ObjT, typename Method, typename Tuple>
+inline void DispatchToMethod(const ObjT& obj,
+ Method method,
+ Tuple&& args) {
+ constexpr size_t size = std::tuple_size<std::decay_t<Tuple>>::value;
+ DispatchToMethodImpl(obj, method, std::forward<Tuple>(args),
+ std::make_index_sequence<size>());
+}
+
+// Static Dispatchers with no out params.
+
+template <typename Function, typename Tuple, size_t... Ns>
+inline void DispatchToFunctionImpl(Function function,
+ Tuple&& args,
+ std::index_sequence<Ns...>) {
+ (*function)(std::get<Ns>(std::forward<Tuple>(args))...);
+}
+
+template <typename Function, typename Tuple>
+inline void DispatchToFunction(Function function, Tuple&& args) {
+ constexpr size_t size = std::tuple_size<std::decay_t<Tuple>>::value;
+ DispatchToFunctionImpl(function, std::forward<Tuple>(args),
+ std::make_index_sequence<size>());
+}
+
+// Dispatchers with out parameters.
+
+template <typename ObjT,
+ typename Method,
+ typename InTuple,
+ typename OutTuple,
+ size_t... InNs,
+ size_t... OutNs>
+inline void DispatchToMethodImpl(const ObjT& obj,
+ Method method,
+ InTuple&& in,
+ OutTuple* out,
+ std::index_sequence<InNs...>,
+ std::index_sequence<OutNs...>) {
+ (obj->*method)(std::get<InNs>(std::forward<InTuple>(in))...,
+ &std::get<OutNs>(*out)...);
+}
+
+template <typename ObjT, typename Method, typename InTuple, typename OutTuple>
+inline void DispatchToMethod(const ObjT& obj,
+ Method method,
+ InTuple&& in,
+ OutTuple* out) {
+ constexpr size_t in_size = std::tuple_size<std::decay_t<InTuple>>::value;
+ constexpr size_t out_size = std::tuple_size<OutTuple>::value;
+ DispatchToMethodImpl(obj, method, std::forward<InTuple>(in), out,
+ std::make_index_sequence<in_size>(),
+ std::make_index_sequence<out_size>());
+}
+
+} // namespace base
+
+#endif // BASE_TUPLE_H_
diff --git a/security/sandbox/chromium/base/unguessable_token.cc b/security/sandbox/chromium/base/unguessable_token.cc
new file mode 100644
index 0000000000..973b4167bd
--- /dev/null
+++ b/security/sandbox/chromium/base/unguessable_token.cc
@@ -0,0 +1,39 @@
+// 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.
+
+#include "base/unguessable_token.h"
+
+#include "base/format_macros.h"
+#include "base/no_destructor.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace base {
+
+UnguessableToken::UnguessableToken(const base::Token& token) : token_(token) {}
+
+// static
+UnguessableToken UnguessableToken::Create() {
+ return UnguessableToken(Token::CreateRandom());
+}
+
+// static
+const UnguessableToken& UnguessableToken::Null() {
+ static const NoDestructor<UnguessableToken> null_token;
+ return *null_token;
+}
+
+// static
+UnguessableToken UnguessableToken::Deserialize(uint64_t high, uint64_t low) {
+ // Receiving a zeroed out UnguessableToken from another process means that it
+ // was never initialized via Create(). Treat this case as a security issue.
+ DCHECK(!(high == 0 && low == 0));
+ return UnguessableToken(Token{high, low});
+}
+
+std::ostream& operator<<(std::ostream& out, const UnguessableToken& token) {
+ return out << "(" << token.ToString() << ")";
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/unguessable_token.h b/security/sandbox/chromium/base/unguessable_token.h
new file mode 100644
index 0000000000..895dbc46c4
--- /dev/null
+++ b/security/sandbox/chromium/base/unguessable_token.h
@@ -0,0 +1,120 @@
+// 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_UNGUESSABLE_TOKEN_H_
+#define BASE_UNGUESSABLE_TOKEN_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <iosfwd>
+#include <tuple>
+
+#include "base/base_export.h"
+#include "base/hash/hash.h"
+#include "base/logging.h"
+#include "base/token.h"
+
+namespace base {
+
+struct UnguessableTokenHash;
+
+// UnguessableToken is, like Token, a randomly chosen 128-bit value. Unlike
+// Token, a new UnguessableToken is always generated at runtime from a
+// cryptographically strong random source (or copied or serialized and
+// deserialized from another such UnguessableToken). It can be used as part of a
+// larger aggregate type, or as an ID in and of itself.
+//
+// An UnguessableToken is a strong *bearer token*. Bearer tokens are like HTTP
+// cookies: if a caller has the token, the callee thereby considers the caller
+// authorized to request the operation the callee performs.
+//
+// UnguessableToken can be used when the resource associated with the ID needs
+// to be protected against manipulation by other untrusted agents in the system,
+// and there is no other convenient way to verify the authority of the agent to
+// do so (because the resource is part of a table shared across processes, for
+// instance). In such a scheme, knowledge of the token value in and of itself is
+// sufficient proof of authority to carry out an operation on the associated
+// resource.
+//
+// Use Create() for creating new UnguessableTokens.
+//
+// NOTE: It is illegal to send empty UnguessableTokens across processes, and
+// sending/receiving empty tokens should be treated as a security issue. If
+// there is a valid scenario for sending "no token" across processes, use
+// base::Optional instead of an empty token.
+
+class BASE_EXPORT UnguessableToken {
+ public:
+ // Create a unique UnguessableToken.
+ static UnguessableToken Create();
+
+ // Returns a reference to a global null UnguessableToken. This should only be
+ // used for functions that need to return a reference to an UnguessableToken,
+ // and should not be used as a general-purpose substitute for invoking the
+ // default constructor.
+ static const UnguessableToken& Null();
+
+ // Return a UnguessableToken built from the high/low bytes provided.
+ // It should only be used in deserialization scenarios.
+ //
+ // NOTE: If the deserialized token is empty, it means that it was never
+ // initialized via Create(). This is a security issue, and should be handled.
+ static UnguessableToken Deserialize(uint64_t high, uint64_t low);
+
+ // Creates an empty UnguessableToken.
+ // Assign to it with Create() before using it.
+ constexpr UnguessableToken() = default;
+
+ // NOTE: Serializing an empty UnguessableToken is an illegal operation.
+ uint64_t GetHighForSerialization() const {
+ DCHECK(!is_empty());
+ return token_.high();
+ }
+
+ // NOTE: Serializing an empty UnguessableToken is an illegal operation.
+ uint64_t GetLowForSerialization() const {
+ DCHECK(!is_empty());
+ return token_.low();
+ }
+
+ bool is_empty() const { return token_.is_zero(); }
+
+ // Hex representation of the unguessable token.
+ std::string ToString() const { return token_.ToString(); }
+
+ explicit operator bool() const { return !is_empty(); }
+
+ bool operator<(const UnguessableToken& other) const {
+ return token_ < other.token_;
+ }
+
+ bool operator==(const UnguessableToken& other) const {
+ return token_ == other.token_;
+ }
+
+ bool operator!=(const UnguessableToken& other) const {
+ return !(*this == other);
+ }
+
+ private:
+ friend struct UnguessableTokenHash;
+ explicit UnguessableToken(const Token& token);
+
+ base::Token token_;
+};
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+ const UnguessableToken& token);
+
+// For use in std::unordered_map.
+struct UnguessableTokenHash {
+ size_t operator()(const base::UnguessableToken& token) const {
+ DCHECK(token);
+ return TokenHash()(token.token_);
+ }
+};
+
+} // namespace base
+
+#endif // BASE_UNGUESSABLE_TOKEN_H_
diff --git a/security/sandbox/chromium/base/version.cc b/security/sandbox/chromium/base/version.cc
new file mode 100644
index 0000000000..2ee8793c03
--- /dev/null
+++ b/security/sandbox/chromium/base/version.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/version.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+
+namespace base {
+
+namespace {
+
+// Parses the |numbers| vector representing the different numbers
+// inside the version string and constructs a vector of valid integers. It stops
+// when it reaches an invalid item (including the wildcard character). |parsed|
+// is the resulting integer vector. Function returns true if all numbers were
+// parsed successfully, false otherwise.
+bool ParseVersionNumbers(StringPiece version_str,
+ std::vector<uint32_t>* parsed) {
+ std::vector<StringPiece> numbers =
+ SplitStringPiece(version_str, ".", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+ if (numbers.empty())
+ return false;
+
+ for (auto it = numbers.begin(); it != numbers.end(); ++it) {
+ if (StartsWith(*it, "+", CompareCase::SENSITIVE))
+ return false;
+
+ unsigned int num;
+ if (!StringToUint(*it, &num))
+ return false;
+
+ // This throws out leading zeros for the first item only.
+ if (it == numbers.begin() && NumberToString(num) != *it)
+ return false;
+
+ // StringToUint returns unsigned int but Version fields are uint32_t.
+ static_assert(sizeof (uint32_t) == sizeof (unsigned int),
+ "uint32_t must be same as unsigned int");
+ parsed->push_back(num);
+ }
+ return true;
+}
+
+// Compares version components in |components1| with components in
+// |components2|. Returns -1, 0 or 1 if |components1| is less than, equal to,
+// or greater than |components2|, respectively.
+int CompareVersionComponents(const std::vector<uint32_t>& components1,
+ const std::vector<uint32_t>& components2) {
+ const size_t count = std::min(components1.size(), components2.size());
+ for (size_t i = 0; i < count; ++i) {
+ if (components1[i] > components2[i])
+ return 1;
+ if (components1[i] < components2[i])
+ return -1;
+ }
+ if (components1.size() > components2.size()) {
+ for (size_t i = count; i < components1.size(); ++i) {
+ if (components1[i] > 0)
+ return 1;
+ }
+ } else if (components1.size() < components2.size()) {
+ for (size_t i = count; i < components2.size(); ++i) {
+ if (components2[i] > 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+} // namespace
+
+Version::Version() = default;
+
+Version::Version(const Version& other) = default;
+
+Version::~Version() = default;
+
+Version::Version(StringPiece version_str) {
+ std::vector<uint32_t> parsed;
+ if (!ParseVersionNumbers(version_str, &parsed))
+ return;
+
+ components_.swap(parsed);
+}
+
+Version::Version(std::vector<uint32_t> components)
+ : components_(std::move(components)) {}
+
+bool Version::IsValid() const {
+ return (!components_.empty());
+}
+
+// static
+bool Version::IsValidWildcardString(StringPiece wildcard_string) {
+ StringPiece version_string = wildcard_string;
+ if (EndsWith(version_string, ".*", CompareCase::SENSITIVE))
+ version_string = version_string.substr(0, version_string.size() - 2);
+
+ Version version(version_string);
+ return version.IsValid();
+}
+
+int Version::CompareToWildcardString(StringPiece wildcard_string) const {
+ DCHECK(IsValid());
+ DCHECK(Version::IsValidWildcardString(wildcard_string));
+
+ // Default behavior if the string doesn't end with a wildcard.
+ if (!EndsWith(wildcard_string, ".*", CompareCase::SENSITIVE)) {
+ Version version(wildcard_string);
+ DCHECK(version.IsValid());
+ return CompareTo(version);
+ }
+
+ std::vector<uint32_t> parsed;
+ const bool success = ParseVersionNumbers(
+ wildcard_string.substr(0, wildcard_string.length() - 2), &parsed);
+ DCHECK(success);
+ const int comparison = CompareVersionComponents(components_, parsed);
+ // If the version is smaller than the wildcard version's |parsed| vector,
+ // then the wildcard has no effect (e.g. comparing 1.2.3 and 1.3.*) and the
+ // version is still smaller. Same logic for equality (e.g. comparing 1.2.2 to
+ // 1.2.2.* is 0 regardless of the wildcard). Under this logic,
+ // 1.2.0.0.0.0 compared to 1.2.* is 0.
+ if (comparison == -1 || comparison == 0)
+ return comparison;
+
+ // Catch the case where the digits of |parsed| are found in |components_|,
+ // which means that the two are equal since |parsed| has a trailing "*".
+ // (e.g. 1.2.3 vs. 1.2.* will return 0). All other cases return 1 since
+ // components is greater (e.g. 3.2.3 vs 1.*).
+ DCHECK_GT(parsed.size(), 0UL);
+ const size_t min_num_comp = std::min(components_.size(), parsed.size());
+ for (size_t i = 0; i < min_num_comp; ++i) {
+ if (components_[i] != parsed[i])
+ return 1;
+ }
+ return 0;
+}
+
+int Version::CompareTo(const Version& other) const {
+ DCHECK(IsValid());
+ DCHECK(other.IsValid());
+ return CompareVersionComponents(components_, other.components_);
+}
+
+std::string Version::GetString() const {
+ DCHECK(IsValid());
+ std::string version_str;
+ size_t count = components_.size();
+ for (size_t i = 0; i < count - 1; ++i) {
+ version_str.append(NumberToString(components_[i]));
+ version_str.append(".");
+ }
+ version_str.append(NumberToString(components_[count - 1]));
+ return version_str;
+}
+
+bool operator==(const Version& v1, const Version& v2) {
+ return v1.CompareTo(v2) == 0;
+}
+
+bool operator!=(const Version& v1, const Version& v2) {
+ return !(v1 == v2);
+}
+
+bool operator<(const Version& v1, const Version& v2) {
+ return v1.CompareTo(v2) < 0;
+}
+
+bool operator<=(const Version& v1, const Version& v2) {
+ return v1.CompareTo(v2) <= 0;
+}
+
+bool operator>(const Version& v1, const Version& v2) {
+ return v1.CompareTo(v2) > 0;
+}
+
+bool operator>=(const Version& v1, const Version& v2) {
+ return v1.CompareTo(v2) >= 0;
+}
+
+std::ostream& operator<<(std::ostream& stream, const Version& v) {
+ return stream << v.GetString();
+}
+
+} // namespace base
diff --git a/security/sandbox/chromium/base/version.h b/security/sandbox/chromium/base/version.h
new file mode 100644
index 0000000000..9199449de6
--- /dev/null
+++ b/security/sandbox/chromium/base/version.h
@@ -0,0 +1,77 @@
+// 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_VERSION_H_
+#define BASE_VERSION_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// Version represents a dotted version number, like "1.2.3.4", supporting
+// parsing and comparison.
+class BASE_EXPORT Version {
+ public:
+ // The only thing you can legally do to a default constructed
+ // Version object is assign to it.
+ Version();
+
+ Version(const Version& other);
+
+ // Initializes from a decimal dotted version number, like "0.1.1".
+ // Each component is limited to a uint16_t. Call IsValid() to learn
+ // the outcome.
+ explicit Version(StringPiece version_str);
+
+ // Initializes from a vector of components, like {1, 2, 3, 4}. Call IsValid()
+ // to learn the outcome.
+ explicit Version(std::vector<uint32_t> components);
+
+ ~Version();
+
+ // Returns true if the object contains a valid version number.
+ bool IsValid() const;
+
+ // Returns true if the version wildcard string is valid. The version wildcard
+ // string may end with ".*" (e.g. 1.2.*, 1.*). Any other arrangement with "*"
+ // is invalid (e.g. 1.*.3 or 1.2.3*). This functions defaults to standard
+ // Version behavior (IsValid) if no wildcard is present.
+ static bool IsValidWildcardString(StringPiece wildcard_string);
+
+ // Returns -1, 0, 1 for <, ==, >.
+ int CompareTo(const Version& other) const;
+
+ // Given a valid version object, compare if a |wildcard_string| results in a
+ // newer version. This function will default to CompareTo if the string does
+ // not end in wildcard sequence ".*". IsValidWildcard(wildcard_string) must be
+ // true before using this function.
+ int CompareToWildcardString(StringPiece wildcard_string) const;
+
+ // Return the string representation of this version.
+ std::string GetString() const;
+
+ const std::vector<uint32_t>& components() const { return components_; }
+
+ private:
+ std::vector<uint32_t> components_;
+};
+
+BASE_EXPORT bool operator==(const Version& v1, const Version& v2);
+BASE_EXPORT bool operator!=(const Version& v1, const Version& v2);
+BASE_EXPORT bool operator<(const Version& v1, const Version& v2);
+BASE_EXPORT bool operator<=(const Version& v1, const Version& v2);
+BASE_EXPORT bool operator>(const Version& v1, const Version& v2);
+BASE_EXPORT bool operator>=(const Version& v1, const Version& v2);
+BASE_EXPORT std::ostream& operator<<(std::ostream& stream, const Version& v);
+
+} // namespace base
+
+#endif // BASE_VERSION_H_
diff --git a/security/sandbox/chromium/base/win/current_module.h b/security/sandbox/chromium/base/win/current_module.h
new file mode 100644
index 0000000000..ee141db211
--- /dev/null
+++ b/security/sandbox/chromium/base/win/current_module.h
@@ -0,0 +1,17 @@
+// 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_WIN_CURRENT_MODULE_H_
+#define BASE_WIN_CURRENT_MODULE_H_
+
+#include <windows.h>
+
+// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+
+// Returns the HMODULE of the dll the macro was expanded in.
+// Only use in cc files, not in h files.
+#define CURRENT_MODULE() reinterpret_cast<HMODULE>(&__ImageBase)
+
+#endif // BASE_WIN_CURRENT_MODULE_H_
diff --git a/security/sandbox/chromium/base/win/pe_image.cc b/security/sandbox/chromium/base/win/pe_image.cc
new file mode 100644
index 0000000000..3d52964a24
--- /dev/null
+++ b/security/sandbox/chromium/base/win/pe_image.cc
@@ -0,0 +1,652 @@
+// 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.
+
+// This file implements PEImage, a generic class to manipulate PE files.
+// This file was adapted from GreenBorder's Code.
+
+#include "base/win/pe_image.h"
+
+#include <stddef.h>
+#include <set>
+#include <string>
+
+#include "base/no_destructor.h"
+#include "base/win/current_module.h"
+
+namespace base {
+namespace win {
+
+// Structure to perform imports enumerations.
+struct EnumAllImportsStorage {
+ PEImage::EnumImportsFunction callback;
+ PVOID cookie;
+};
+
+namespace {
+
+// PdbInfo Signature
+const DWORD kPdbInfoSignature = 'SDSR';
+
+// 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<const unsigned char*>(s1) -
+ *reinterpret_cast<const unsigned char*>(s2));
+}
+
+struct PdbInfo {
+ DWORD Signature;
+ GUID Guid;
+ DWORD Age;
+ char PdbFileName[1];
+};
+
+#define LDR_IS_DATAFILE(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)1)
+#define LDR_IS_IMAGEMAPPING(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)2)
+#define LDR_IS_RESOURCE(handle) \
+ (LDR_IS_IMAGEMAPPING(handle) || LDR_IS_DATAFILE(handle))
+
+} // 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<EnumAllImportsStorage*>(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,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) {
+ EnumAllImportsStorage& storage =
+ *reinterpret_cast<EnumAllImportsStorage*>(cookie);
+
+ return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
+ module, name_table, iat, storage.cookie);
+}
+
+void PEImage::set_module(HMODULE module) {
+ module_ = module;
+}
+
+PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
+ return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
+}
+
+PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
+ PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+ return reinterpret_cast<PIMAGE_NT_HEADERS>(
+ reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+ PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
+
+ if (section < nt_headers->FileHeader.NumberOfSections)
+ return first_section + section;
+ else
+ return nullptr;
+}
+
+WORD PEImage::GetNumSections() const {
+ return GetNTHeaders()->FileHeader.NumberOfSections;
+}
+
+DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
+ const IMAGE_DATA_DIRECTORY* const entry = GetDataDirectory(directory);
+ return entry ? entry->Size : 0;
+}
+
+PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
+ const IMAGE_DATA_DIRECTORY* const entry = GetDataDirectory(directory);
+ return entry ? RVAToAddr(entry->VirtualAddress) : nullptr;
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
+ PBYTE target = reinterpret_cast<PBYTE>(address);
+ PIMAGE_SECTION_HEADER section;
+
+ for (UINT i = 0; nullptr != (section = GetSectionHeader(i)); i++) {
+ // Don't use the virtual RVAToAddr.
+ PBYTE start =
+ reinterpret_cast<PBYTE>(PEImage::RVAToAddr(section->VirtualAddress));
+
+ DWORD size = section->Misc.VirtualSize;
+
+ if ((start <= target) && (start + size > target))
+ return section;
+ }
+
+ return nullptr;
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
+ LPCSTR section_name) const {
+ if (nullptr == section_name)
+ return nullptr;
+
+ PIMAGE_SECTION_HEADER ret = nullptr;
+ int num_sections = GetNumSections();
+
+ for (int i = 0; i < num_sections; i++) {
+ PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
+ if (_strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
+ sizeof(section->Name)) == 0) {
+ ret = section;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+bool PEImage::GetDebugId(LPGUID guid,
+ LPDWORD age,
+ LPCSTR* pdb_filename,
+ size_t* pdb_filename_length) const {
+ DWORD debug_directory_size =
+ GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG);
+ PIMAGE_DEBUG_DIRECTORY debug_directory =
+ reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DEBUG));
+ if (!debug_directory)
+ return false;
+
+ size_t directory_count = debug_directory_size / sizeof(IMAGE_DEBUG_DIRECTORY);
+ for (size_t index = 0; index < directory_count; ++index) {
+ const IMAGE_DEBUG_DIRECTORY& entry = debug_directory[index];
+ if (entry.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
+ continue; // Unsupported debugging info format.
+ if (entry.SizeOfData < sizeof(PdbInfo))
+ continue; // The data is too small to hold PDB info.
+ const PdbInfo* pdb_info =
+ reinterpret_cast<const PdbInfo*>(RVAToAddr(entry.AddressOfRawData));
+ if (!pdb_info)
+ continue; // The data is not present in a mapped section.
+ if (pdb_info->Signature != kPdbInfoSignature)
+ continue; // Unsupported PdbInfo signature
+
+ if (guid)
+ *guid = pdb_info->Guid;
+ if (age)
+ *age = pdb_info->Age;
+ if (pdb_filename) {
+ const size_t length_max =
+ entry.SizeOfData - offsetof(PdbInfo, PdbFileName);
+ const char* eos = pdb_info->PdbFileName;
+ for (const char* const end = pdb_info->PdbFileName + length_max;
+ eos < end && *eos; ++eos)
+ ;
+ *pdb_filename_length = eos - pdb_info->PdbFileName;
+ *pdb_filename = pdb_info->PdbFileName;
+ }
+ return true;
+ }
+ return false;
+}
+
+PDWORD PEImage::GetExportEntry(LPCSTR name) const {
+ PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+ if (nullptr == exports)
+ return nullptr;
+
+ WORD ordinal = 0;
+ if (!GetProcOrdinal(name, &ordinal))
+ return nullptr;
+
+ PDWORD functions =
+ reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfFunctions));
+
+ return functions + ordinal - exports->Base;
+}
+
+FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
+ PDWORD export_entry = GetExportEntry(function_name);
+ if (nullptr == export_entry)
+ return nullptr;
+
+ PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
+
+ PBYTE exports = reinterpret_cast<PBYTE>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+ if (!exports || !size)
+ return nullptr;
+
+ // Check for forwarded exports as a special case.
+ if (exports <= function && exports + size > function)
+ return reinterpret_cast<FARPROC>(-1);
+
+ return reinterpret_cast<FARPROC>(function);
+}
+
+bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD* ordinal) const {
+ if (nullptr == ordinal)
+ return false;
+
+ PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+ if (nullptr == exports)
+ return false;
+
+ if (IsOrdinal(function_name)) {
+ *ordinal = ToOrdinal(function_name);
+ } else {
+ PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+ PDWORD lower = names;
+ PDWORD upper = names + exports->NumberOfNames;
+ int cmp = -1;
+
+ // Binary Search for the name.
+ while (lower != upper) {
+ PDWORD middle = lower + (upper - lower) / 2;
+ LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
+
+ // This may be called by sandbox before MSVCRT dll loads, so can't use
+ // CRT function here.
+ cmp = StrCmpByByte(function_name, name);
+
+ if (cmp == 0) {
+ lower = middle;
+ break;
+ }
+
+ if (cmp > 0)
+ lower = middle + 1;
+ else
+ upper = middle;
+ }
+
+ if (cmp != 0)
+ return false;
+
+ PWORD ordinals =
+ reinterpret_cast<PWORD>(RVAToAddr(exports->AddressOfNameOrdinals));
+
+ *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
+ }
+
+ return true;
+}
+
+bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+ UINT num_sections = nt_headers->FileHeader.NumberOfSections;
+ PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
+
+ for (UINT i = 0; i < num_sections; i++, section++) {
+ PVOID section_start = RVAToAddr(section->VirtualAddress);
+ DWORD size = section->Misc.VirtualSize;
+
+ if (!callback(*this, section, section_start, size, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ // Check if there are any exports at all.
+ if (!directory || !size)
+ return true;
+
+ PIMAGE_EXPORT_DIRECTORY exports =
+ reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(directory);
+ UINT ordinal_base = exports->Base;
+ UINT num_funcs = exports->NumberOfFunctions;
+ UINT num_names = exports->NumberOfNames;
+ PDWORD functions =
+ reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfFunctions));
+ PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+ PWORD ordinals =
+ reinterpret_cast<PWORD>(RVAToAddr(exports->AddressOfNameOrdinals));
+
+ for (UINT count = 0; count < num_funcs; count++) {
+ PVOID func = RVAToAddr(functions[count]);
+ if (nullptr == func)
+ continue;
+
+ // Check for a name.
+ LPCSTR name = nullptr;
+ UINT hint;
+ for (hint = 0; hint < num_names; hint++) {
+ if (ordinals[hint] == count) {
+ name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
+ break;
+ }
+ }
+
+ if (name == nullptr)
+ hint = 0;
+
+ // Check for forwarded exports.
+ LPCSTR forward = nullptr;
+ if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
+ reinterpret_cast<char*>(func) <=
+ reinterpret_cast<char*>(directory) + size) {
+ forward = reinterpret_cast<LPCSTR>(func);
+ func = nullptr;
+ }
+
+ if (!callback(*this, ordinal_base + count, hint, name, func, forward,
+ cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+
+ if (!directory || !size)
+ return true;
+
+ PIMAGE_BASE_RELOCATION base =
+ reinterpret_cast<PIMAGE_BASE_RELOCATION>(directory);
+ while (size >= sizeof(IMAGE_BASE_RELOCATION) && base->SizeOfBlock &&
+ size >= base->SizeOfBlock) {
+ PWORD reloc = reinterpret_cast<PWORD>(base + 1);
+ UINT num_relocs =
+ (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
+
+ for (UINT i = 0; i < num_relocs; i++, reloc++) {
+ WORD type = *reloc >> 12;
+ PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
+
+ if (!callback(*this, type, address, cookie))
+ return false;
+ }
+
+ size -= base->SizeOfBlock;
+ base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+ reinterpret_cast<char*>(base) + base->SizeOfBlock);
+ }
+
+ return true;
+}
+
+bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const {
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
+ PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
+
+ if (import == nullptr || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
+ return true;
+
+ for (; import->FirstThunk; import++) {
+ LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
+ PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(import->OriginalFirstThunk));
+ PIMAGE_THUNK_DATA iat =
+ reinterpret_cast<PIMAGE_THUNK_DATA>(RVAToAddr(import->FirstThunk));
+
+ if (target_module_name == nullptr ||
+ (lstrcmpiA(module_name, target_module_name) == 0)) {
+ if (!callback(*this, module_name, name_table, iat, cookie))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) const {
+ if (nullptr == name_table)
+ return false;
+
+ for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
+ LPCSTR name = nullptr;
+ WORD ordinal = 0;
+ WORD hint = 0;
+
+ if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+ } else {
+ PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ RVAToAddr(name_table->u1.ForwarderString));
+
+ hint = import->Hint;
+ name = reinterpret_cast<LPCSTR>(&import->Name);
+ }
+
+ if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumAllImports(EnumImportsFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const {
+ EnumAllImportsStorage temp = {callback, cookie};
+ return EnumImportChunks(ProcessImportChunk, &temp, target_module_name);
+}
+
+bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const {
+ PVOID directory =
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+
+ if (!directory || !size)
+ return true;
+
+ PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
+ for (; delay_descriptor->rvaHmod; delay_descriptor++) {
+ PIMAGE_THUNK_DATA name_table;
+ PIMAGE_THUNK_DATA iat;
+ LPCSTR module_name;
+
+ // check if VC7-style imports, using RVAs instead of
+ // VC6-style addresses.
+ bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+ if (rvas) {
+ module_name =
+ reinterpret_cast<LPCSTR>(RVAToAddr(delay_descriptor->rvaDLLName));
+ name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaINT));
+ iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaIAT));
+ } else {
+ // 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<LPCSTR>(
+ static_cast<uintptr_t>(delay_descriptor->rvaDLLName));
+ name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ static_cast<uintptr_t>(delay_descriptor->rvaINT));
+ iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ static_cast<uintptr_t>(delay_descriptor->rvaIAT));
+ }
+
+ if (target_module_name == nullptr ||
+ (lstrcmpiA(module_name, target_module_name) == 0)) {
+ if (target_module_name) {
+ // Ensure all imports are properly loaded for the target module so that
+ // the callback is operating on a fully-realized set of imports.
+ // This call only loads the imports for the module where this code is
+ // executing, so it is only helpful or meaningful to do this if the
+ // current module is the module whose IAT we are enumerating.
+ // Use the module_name as retrieved from the IAT because this method
+ // is case sensitive.
+ if (module_ == CURRENT_MODULE() && !LDR_IS_RESOURCE(module_)) {
+ static base::NoDestructor<std::set<std::string>> loaded_dlls;
+ // pair.second is true if this is a new element
+ if (loaded_dlls.get()->emplace(module_name).second)
+ ::__HrLoadAllImportsForDll(module_name);
+ }
+ }
+
+ if (!callback(*this, delay_descriptor, module_name, name_table, iat,
+ cookie))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) const {
+ for (; name_table->u1.Ordinal; name_table++, iat++) {
+ LPCSTR name = nullptr;
+ WORD ordinal = 0;
+ WORD hint = 0;
+
+ if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+ } else {
+ PIMAGE_IMPORT_BY_NAME import;
+ bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+ if (rvas) {
+ import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ RVAToAddr(name_table->u1.ForwarderString));
+ } else {
+ import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ name_table->u1.ForwarderString);
+ }
+
+ hint = import->Hint;
+ name = reinterpret_cast<LPCSTR>(&import->Name);
+ }
+
+ if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const {
+ EnumAllImportsStorage temp = {callback, cookie};
+ return EnumDelayImportChunks(ProcessDelayImportChunk, &temp,
+ target_module_name);
+}
+
+bool PEImage::VerifyMagic() const {
+ PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return false;
+
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+ return false;
+
+ if (nt_headers->FileHeader.SizeOfOptionalHeader !=
+ sizeof(IMAGE_OPTIONAL_HEADER))
+ return false;
+
+ if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return false;
+
+ return true;
+}
+
+bool PEImage::ImageRVAToOnDiskOffset(uintptr_t 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 {
+ if (nullptr == address)
+ return false;
+
+ // Get the section that this address belongs to.
+ PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
+ if (nullptr == section_header)
+ return false;
+
+ // Don't follow the virtual RVAToAddr, use the one on the base.
+ DWORD offset_within_section =
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(address)) -
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(
+ PEImage::RVAToAddr(section_header->VirtualAddress)));
+
+ *on_disk_offset = section_header->PointerToRawData + offset_within_section;
+ return true;
+}
+
+PVOID PEImage::RVAToAddr(uintptr_t rva) const {
+ if (rva == 0)
+ return nullptr;
+
+ return reinterpret_cast<char*>(module_) + rva;
+}
+
+const IMAGE_DATA_DIRECTORY* PEImage::GetDataDirectory(UINT directory) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ // Does the image report that it includes this directory entry?
+ if (directory >= nt_headers->OptionalHeader.NumberOfRvaAndSizes)
+ return nullptr;
+
+ // Is there space for this directory entry in the optional header?
+ if (nt_headers->FileHeader.SizeOfOptionalHeader <
+ (offsetof(IMAGE_OPTIONAL_HEADER, DataDirectory) +
+ (directory + 1) * sizeof(IMAGE_DATA_DIRECTORY))) {
+ return nullptr;
+ }
+
+ return &nt_headers->OptionalHeader.DataDirectory[directory];
+}
+
+PVOID PEImageAsData::RVAToAddr(uintptr_t rva) const {
+ if (rva == 0)
+ return nullptr;
+
+ PVOID in_memory = PEImage::RVAToAddr(rva);
+ DWORD disk_offset;
+
+ if (!ImageAddrToOnDiskOffset(in_memory, &disk_offset))
+ return nullptr;
+
+ return PEImage::RVAToAddr(disk_offset);
+}
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/pe_image.h b/security/sandbox/chromium/base/win/pe_image.h
new file mode 100644
index 0000000000..cec959a940
--- /dev/null
+++ b/security/sandbox/chromium/base/win/pe_image.h
@@ -0,0 +1,308 @@
+// 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.
+
+// This file was adapted from GreenBorder's Code.
+// To understand what this class is about (for other than well known functions
+// as GetProcAddress), a good starting point is "An In-Depth Look into the
+// Win32 Portable Executable File Format" by Matt Pietrek:
+// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx
+
+#ifndef BASE_WIN_PE_IMAGE_H_
+#define BASE_WIN_PE_IMAGE_H_
+
+#include <windows.h>
+
+#include <stdint.h>
+
+#if defined(_WIN32_WINNT_WIN8)
+// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h.
+#undef FACILITY_VISUALCPP
+#endif
+#include <delayimp.h>
+
+namespace base {
+namespace win {
+
+// This class is a wrapper for the Portable Executable File Format (PE).
+// Its main purpose is to provide an easy way to work with imports and exports
+// from a file, mapped in memory as image.
+class PEImage {
+ public:
+ // Callback to enumerate sections.
+ // cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ using EnumSectionsFunction =
+ bool (*)(const PEImage&, PIMAGE_SECTION_HEADER, PVOID, DWORD, PVOID);
+
+ // Callback to enumerate exports.
+ // function is the actual address of the symbol. If forward is not null, it
+ // contains the dll and symbol to forward this export to. cookie is the value
+ // passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ using EnumExportsFunction =
+ bool (*)(const PEImage&, DWORD, DWORD, LPCSTR, PVOID, LPCSTR, PVOID);
+
+ // Callback to enumerate import blocks.
+ // name_table and iat point to the imports name table and address table for
+ // this block. cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ using EnumImportChunksFunction = bool (*)(const PEImage&,
+ LPCSTR,
+ PIMAGE_THUNK_DATA,
+ PIMAGE_THUNK_DATA,
+ PVOID);
+
+ // Callback to enumerate imports.
+ // module is the dll that exports this symbol. cookie is the value passed to
+ // the enumerate method.
+ // Returns true to continue the enumeration.
+ using EnumImportsFunction = bool (*)(const PEImage&,
+ LPCSTR,
+ DWORD,
+ LPCSTR,
+ DWORD,
+ PIMAGE_THUNK_DATA,
+ PVOID);
+
+ // Callback to enumerate delayed import blocks.
+ // module is the dll that exports this block of symbols. cookie is the value
+ // passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ using EnumDelayImportChunksFunction = bool (*)(const PEImage&,
+ PImgDelayDescr,
+ LPCSTR,
+ PIMAGE_THUNK_DATA,
+ PIMAGE_THUNK_DATA,
+ PVOID);
+
+ // Callback to enumerate relocations.
+ // cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ using EnumRelocsFunction = bool (*)(const PEImage&, WORD, PVOID, PVOID);
+
+ explicit PEImage(HMODULE module) : module_(module) {}
+ explicit PEImage(const void* module) {
+ module_ = reinterpret_cast<HMODULE>(const_cast<void*>(module));
+ }
+
+ virtual ~PEImage() = default;
+
+ // Gets the HMODULE for this object.
+ HMODULE module() const;
+
+ // Sets this object's HMODULE.
+ void set_module(HMODULE module);
+
+ // Checks if this symbol is actually an ordinal.
+ static bool IsOrdinal(LPCSTR name);
+
+ // Converts a named symbol to the corresponding ordinal.
+ static WORD ToOrdinal(LPCSTR name);
+
+ // Returns the DOS_HEADER for this PE.
+ PIMAGE_DOS_HEADER GetDosHeader() const;
+
+ // Returns the NT_HEADER for this PE.
+ PIMAGE_NT_HEADERS GetNTHeaders() const;
+
+ // Returns number of sections of this PE.
+ WORD GetNumSections() const;
+
+ // Returns the header for a given section.
+ // returns NULL if there is no such section.
+ PIMAGE_SECTION_HEADER GetSectionHeader(UINT section) const;
+
+ // Returns the size of a given directory entry or 0 if |directory| is out of
+ // bounds.
+ DWORD GetImageDirectoryEntrySize(UINT directory) const;
+
+ // Returns the address of a given directory entry or NULL if |directory| is
+ // out of bounds.
+ PVOID GetImageDirectoryEntryAddr(UINT directory) const;
+
+ // Returns the section header for a given address.
+ // Use: s = image.GetImageSectionFromAddr(a);
+ // Post: 's' is the section header of the section that contains 'a'
+ // or NULL if there is no such section.
+ PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const;
+
+ // Returns the section header for a given section.
+ PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const;
+
+ // Returns the first block of imports.
+ PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const;
+
+ // Returns the exports directory.
+ PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
+
+ // Retrieves the contents of the image's CodeView debug entry, returning true
+ // if such an entry is found and is within a section mapped into the current
+ // process's memory. |guid|, |age|, and |pdb_filename| are each optional and
+ // may be NULL. |pdb_filename_length| is mandatory if |pdb_filename| is not
+ // NULL, as the latter is populated with a direct reference to a string in the
+ // image that is is not guaranteed to be terminated (note: informal
+ // documentation indicates that it should be terminated, but the data is
+ // untrusted). Furthermore, owing to its nature of being a string in the
+ // image, it is only valid while the image is mapped into the process, and the
+ // caller is not responsible for freeing it. |pdb_filename_length| is
+ // populated with the string length of |pdb_filename| (not including a
+ // terminator) and must be used rather than relying on |pdb_filename| being
+ // properly terminated.
+ bool GetDebugId(LPGUID guid,
+ LPDWORD age,
+ LPCSTR* pdb_filename,
+ size_t* pdb_filename_length) const;
+
+ // Returns a given export entry.
+ // Use: e = image.GetExportEntry(f);
+ // Pre: 'f' is either a zero terminated string or ordinal
+ // Post: 'e' is a pointer to the export directory entry
+ // that contains 'f's export RVA, or NULL if 'f'
+ // is not exported from this image
+ PDWORD GetExportEntry(LPCSTR name) const;
+
+ // Returns the address for a given exported symbol.
+ // Use: p = image.GetProcAddress(f);
+ // 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 -1. In this case
+ // RVAToAddr(*GetExportEntry) can be used to resolve
+ // the string that describes the forward.
+ FARPROC GetProcAddress(LPCSTR function_name) const;
+
+ // Retrieves the ordinal for a given exported symbol.
+ // Returns true if the symbol was found.
+ bool GetProcOrdinal(LPCSTR function_name, WORD* ordinal) const;
+
+ // Enumerates PE sections.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE exports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumExports(EnumExportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE imports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ // Use |target_module_name| to ensure the callback is only invoked for the
+ // specified module.
+ bool EnumAllImports(EnumImportsFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const;
+
+ // Enumerates PE import blocks.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ // Use |target_module_name| to ensure the callback is only invoked for the
+ // specified module.
+ bool EnumImportChunks(EnumImportChunksFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const;
+
+ // Enumerates the imports from a single PE import block.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumOneImportChunk(EnumImportsFunction callback,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) const;
+
+ // Enumerates PE delay imports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ // Use |target_module_name| to ensure the callback is only invoked for the
+ // specified module. If this parameter is non-null then all delayloaded
+ // imports are resolved when the target module is found.
+ bool EnumAllDelayImports(EnumImportsFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const;
+
+ // Enumerates PE delay import blocks.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ // Use |target_module_name| to ensure the callback is only invoked for the
+ // specified module. If this parameter is non-null then all delayloaded
+ // imports are resolved when the target module is found.
+ bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+ PVOID cookie,
+ LPCSTR target_module_name) const;
+
+ // Enumerates imports from a single PE delay import block.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumOneDelayImportChunk(EnumImportsFunction callback,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) const;
+
+ // Enumerates PE relocation entries.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const;
+
+ // Verifies the magic values on the PE file.
+ // Returns true if all values are correct.
+ bool VerifyMagic() const;
+
+ // Converts an rva value to the appropriate address.
+ virtual PVOID RVAToAddr(uintptr_t rva) const;
+
+ // Converts an rva value to an offset on disk.
+ // Returns true on success.
+ bool ImageRVAToOnDiskOffset(uintptr_t rva, DWORD* on_disk_offset) const;
+
+ // Converts an address to an offset on disk.
+ // Returns true on success.
+ bool ImageAddrToOnDiskOffset(LPVOID address, DWORD* on_disk_offset) const;
+
+ private:
+ // Returns a pointer to a data directory, or NULL if |directory| is out of
+ // range.
+ const IMAGE_DATA_DIRECTORY* GetDataDirectory(UINT directory) const;
+
+ HMODULE module_;
+};
+
+// This class is an extension to the PEImage class that allows working with PE
+// files mapped as data instead of as image file.
+class PEImageAsData : public PEImage {
+ public:
+ explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {}
+
+ PVOID RVAToAddr(uintptr_t rva) const override;
+};
+
+inline bool PEImage::IsOrdinal(LPCSTR name) {
+ return reinterpret_cast<uintptr_t>(name) <= 0xFFFF;
+}
+
+inline WORD PEImage::ToOrdinal(LPCSTR name) {
+ return static_cast<WORD>(reinterpret_cast<intptr_t>(name));
+}
+
+inline HMODULE PEImage::module() const {
+ return module_;
+}
+
+inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const {
+ return reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT));
+}
+
+inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const {
+ return reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+}
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_PE_IMAGE_H_
diff --git a/security/sandbox/chromium/base/win/scoped_handle.cc b/security/sandbox/chromium/base/win/scoped_handle.cc
new file mode 100644
index 0000000000..de6854591b
--- /dev/null
+++ b/security/sandbox/chromium/base/win/scoped_handle.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_handle_verifier.h"
+#include "base/win/windows_types.h"
+
+namespace base {
+namespace win {
+
+using base::win::internal::ScopedHandleVerifier;
+
+// Static.
+bool HandleTraits::CloseHandle(HANDLE handle) {
+ return ScopedHandleVerifier::Get()->CloseHandle(handle);
+}
+
+// Static.
+void VerifierTraits::StartTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {
+ return ScopedHandleVerifier::Get()->StartTracking(handle, owner, pc1, pc2);
+}
+
+// Static.
+void VerifierTraits::StopTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {
+ return ScopedHandleVerifier::Get()->StopTracking(handle, owner, pc1, pc2);
+}
+
+void DisableHandleVerifier() {
+ return ScopedHandleVerifier::Get()->Disable();
+}
+
+void OnHandleBeingClosed(HANDLE handle) {
+ return ScopedHandleVerifier::Get()->OnHandleBeingClosed(handle);
+}
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/scoped_handle.h b/security/sandbox/chromium/base/win/scoped_handle.h
new file mode 100644
index 0000000000..02c2533649
--- /dev/null
+++ b/security/sandbox/chromium/base/win/scoped_handle.h
@@ -0,0 +1,184 @@
+// 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_WIN_SCOPED_HANDLE_H_
+#define BASE_WIN_SCOPED_HANDLE_H_
+
+#include "base/win/windows_types.h"
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
+// TODO(rvargas): remove this with the rest of the verifier.
+#if defined(COMPILER_MSVC)
+#include <intrin.h>
+#define BASE_WIN_GET_CALLER _ReturnAddress()
+#elif defined(COMPILER_GCC)
+#define BASE_WIN_GET_CALLER \
+ __builtin_extract_return_addr(__builtin_return_address(0))
+#endif
+
+namespace base {
+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 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 Traits, class Verifier>
+class GenericScopedHandle {
+ public:
+ using Handle = typename Traits::Handle;
+
+ GenericScopedHandle() : handle_(Traits::NullHandle()) {}
+
+ explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) {
+ Set(handle);
+ }
+
+ GenericScopedHandle(GenericScopedHandle&& other)
+ : handle_(Traits::NullHandle()) {
+ Set(other.Take());
+ }
+
+ ~GenericScopedHandle() { Close(); }
+
+ bool IsValid() const { return Traits::IsHandleValid(handle_); }
+
+ 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)) {
+ handle_ = handle;
+ Verifier::StartTracking(handle, this, BASE_WIN_GET_CALLER,
+ GetProgramCounter());
+ }
+ ::SetLastError(last_error);
+ }
+ }
+
+ Handle Get() const { return handle_; }
+
+ // Transfers ownership away from this object.
+ Handle Take() WARN_UNUSED_RESULT {
+ Handle temp = handle_;
+ handle_ = Traits::NullHandle();
+ if (Traits::IsHandleValid(temp)) {
+ Verifier::StopTracking(temp, this, BASE_WIN_GET_CALLER,
+ GetProgramCounter());
+ }
+ return temp;
+ }
+
+ // Explicitly closes the owned handle.
+ void Close() {
+ if (Traits::IsHandleValid(handle_)) {
+ Verifier::StopTracking(handle_, this, BASE_WIN_GET_CALLER,
+ GetProgramCounter());
+
+ Traits::CloseHandle(handle_);
+ handle_ = Traits::NullHandle();
+ }
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierWrongOwner);
+ FRIEND_TEST_ALL_PREFIXES(ScopedHandleTest, ActiveVerifierUntrackedHandle);
+ Handle handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(GenericScopedHandle);
+};
+
+#undef BASE_WIN_GET_CALLER
+
+// The traits class for Win32 handles that can be closed via CloseHandle() API.
+class HandleTraits {
+ public:
+ using Handle = HANDLE;
+
+ // Closes the handle.
+ static bool BASE_EXPORT CloseHandle(HANDLE handle);
+
+ // Returns true if the handle value is valid.
+ static bool IsHandleValid(HANDLE handle) {
+ return handle != nullptr && handle != INVALID_HANDLE_VALUE;
+ }
+
+ // Returns NULL handle value.
+ static HANDLE NullHandle() { return nullptr; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HandleTraits);
+};
+
+// Do-nothing verifier.
+class DummyVerifierTraits {
+ public:
+ using Handle = HANDLE;
+
+ static void StartTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {}
+ static void StopTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2) {}
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DummyVerifierTraits);
+};
+
+// Performs actual run-time tracking.
+class BASE_EXPORT VerifierTraits {
+ public:
+ using Handle = HANDLE;
+
+ static void StartTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2);
+ static void StopTracking(HANDLE handle,
+ const void* owner,
+ const void* pc1,
+ const void* pc2);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits);
+};
+
+using ScopedHandle = GenericScopedHandle<HandleTraits, VerifierTraits>;
+
+// This function may be called by the embedder to disable the use of
+// VerifierTraits at runtime. It has no effect if DummyVerifierTraits is used
+// for ScopedHandle.
+BASE_EXPORT void DisableHandleVerifier();
+
+// This should be called whenever the OS is closing a handle, if extended
+// verification of improper handle closing is desired. If |handle| is being
+// tracked by the handle verifier and ScopedHandle is not the one closing it,
+// a CHECK is generated.
+BASE_EXPORT void OnHandleBeingClosed(HANDLE handle);
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_HANDLE_H_
diff --git a/security/sandbox/chromium/base/win/scoped_handle_verifier.cc b/security/sandbox/chromium/base/win/scoped_handle_verifier.cc
new file mode 100644
index 0000000000..316606c0bf
--- /dev/null
+++ b/security/sandbox/chromium/base/win/scoped_handle_verifier.cc
@@ -0,0 +1,238 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_handle_verifier.h"
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include <unordered_map>
+
+#include "base/debug/alias.h"
+#include "base/debug/stack_trace.h"
+#include "base/synchronization/lock_impl.h"
+#include "base/win/base_win_buildflags.h"
+#include "base/win/current_module.h"
+
+extern "C" {
+__declspec(dllexport) void* GetHandleVerifier();
+
+void* GetHandleVerifier() {
+ return base::win::internal::ScopedHandleVerifier::Get();
+}
+} // extern C
+
+namespace {
+
+base::win::internal::ScopedHandleVerifier* g_active_verifier = nullptr;
+using GetHandleVerifierFn = void* (*)();
+using HandleMap =
+ std::unordered_map<HANDLE,
+ base::win::internal::ScopedHandleVerifierInfo,
+ base::win::internal::HandleHash>;
+using NativeLock = base::internal::LockImpl;
+
+NativeLock* GetLock() {
+ static auto* native_lock = new NativeLock();
+ return native_lock;
+}
+
+// Simple automatic locking using a native critical section so it supports
+// recursive locking.
+class AutoNativeLock {
+ public:
+ explicit AutoNativeLock(NativeLock& lock) : lock_(lock) { lock_.Lock(); }
+
+ ~AutoNativeLock() { lock_.Unlock(); }
+
+ private:
+ NativeLock& lock_;
+ DISALLOW_COPY_AND_ASSIGN(AutoNativeLock);
+};
+
+} // namespace
+
+namespace base {
+namespace win {
+namespace internal {
+
+ScopedHandleVerifier::ScopedHandleVerifier(bool enabled)
+ : enabled_(enabled), lock_(GetLock()) {}
+
+// static
+ScopedHandleVerifier* ScopedHandleVerifier::Get() {
+ if (!g_active_verifier)
+ ScopedHandleVerifier::InstallVerifier();
+
+ return g_active_verifier;
+}
+
+bool CloseHandleWrapper(HANDLE handle) {
+ if (!::CloseHandle(handle))
+ // Making this DCHECK on non-Nighly as we are hitting this frequently,
+ // looks like we are closing handles twice somehow. See bug 1564899.
+#if defined(NIGHTLY_BUILD)
+ CHECK(false); // CloseHandle failed.
+#else
+ DCHECK(false); // CloseHandle failed.
+#endif
+ return true;
+}
+
+// Assigns the g_active_verifier global within the GetLock() lock.
+// If |existing_verifier| is non-null then |enabled| is ignored.
+void ThreadSafeAssignOrCreateScopedHandleVerifier(
+ ScopedHandleVerifier* existing_verifier,
+ bool enabled) {
+ AutoNativeLock lock(*GetLock());
+ // Another thread in this module might be trying to assign the global
+ // verifier, so check that within the lock here.
+ if (g_active_verifier)
+ return;
+ g_active_verifier =
+ existing_verifier ? existing_verifier : new ScopedHandleVerifier(enabled);
+}
+
+// static
+void ScopedHandleVerifier::InstallVerifier() {
+#if BUILDFLAG(SINGLE_MODULE_MODE_HANDLE_VERIFIER)
+ // Component build has one Active Verifier per module.
+ ThreadSafeAssignOrCreateScopedHandleVerifier(nullptr, 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<GetHandleVerifierFn>(
+ ::GetProcAddress(main_module, "GetHandleVerifier"));
+
+ // This should only happen if running in a DLL is linked with base but the
+ // hosting EXE is not. In this case, create an ScopedHandleVerifier for the
+ // current
+ // module but leave it disabled.
+ if (!get_handle_verifier) {
+ ThreadSafeAssignOrCreateScopedHandleVerifier(nullptr, false);
+ return;
+ }
+
+ // Check if in the main module.
+ if (get_handle_verifier == GetHandleVerifier) {
+ ThreadSafeAssignOrCreateScopedHandleVerifier(nullptr, true);
+ return;
+ }
+
+ ScopedHandleVerifier* main_module_verifier =
+ reinterpret_cast<ScopedHandleVerifier*>(get_handle_verifier());
+
+ // Main module should always on-demand create a verifier.
+ DCHECK(main_module_verifier);
+
+ ThreadSafeAssignOrCreateScopedHandleVerifier(main_module_verifier, false);
+#endif
+}
+
+bool ScopedHandleVerifier::CloseHandle(HANDLE handle) {
+ if (!enabled_)
+ return CloseHandleWrapper(handle);
+
+ closing_.Set(true);
+ CloseHandleWrapper(handle);
+ closing_.Set(false);
+
+ return true;
+}
+
+// static
+NativeLock* ScopedHandleVerifier::GetLock() {
+ return ::GetLock();
+}
+
+void ScopedHandleVerifier::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_);
+
+ ScopedHandleVerifierInfo handle_info = {owner, pc1, pc2,
+ base::debug::StackTrace(), thread_id};
+ std::pair<HANDLE, ScopedHandleVerifierInfo> item(handle, handle_info);
+ std::pair<HandleMap::iterator, bool> result = map_.insert(item);
+ if (!result.second) {
+ ScopedHandleVerifierInfo other = result.first->second;
+ base::debug::Alias(&other);
+ auto creation_stack = creation_stack_;
+ base::debug::Alias(&creation_stack);
+ CHECK(false); // Attempt to start tracking already tracked handle.
+ }
+}
+
+void ScopedHandleVerifier::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()) {
+ auto creation_stack = creation_stack_;
+ base::debug::Alias(&creation_stack);
+ CHECK(false); // Attempting to close an untracked handle.
+ }
+
+ ScopedHandleVerifierInfo other = i->second;
+ if (other.owner != owner) {
+ base::debug::Alias(&other);
+ auto creation_stack = creation_stack_;
+ base::debug::Alias(&creation_stack);
+ CHECK(false); // Attempting to close a handle not owned by opener.
+ }
+
+ map_.erase(i);
+}
+
+void ScopedHandleVerifier::Disable() {
+ enabled_ = false;
+}
+
+void ScopedHandleVerifier::OnHandleBeingClosed(HANDLE handle) {
+ if (!enabled_)
+ return;
+
+ if (closing_.Get())
+ return;
+
+ AutoNativeLock lock(*lock_);
+ HandleMap::iterator i = map_.find(handle);
+ if (i == map_.end())
+ return;
+
+ ScopedHandleVerifierInfo other = i->second;
+ base::debug::Alias(&other);
+ auto creation_stack = creation_stack_;
+ base::debug::Alias(&creation_stack);
+ CHECK(false); // CloseHandle called on tracked handle.
+}
+
+HMODULE ScopedHandleVerifier::GetModule() const {
+ return CURRENT_MODULE();
+}
+
+HMODULE GetHandleVerifierModuleForTesting() {
+ return g_active_verifier->GetModule();
+}
+
+} // namespace internal
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/scoped_handle_verifier.h b/security/sandbox/chromium/base/win/scoped_handle_verifier.h
new file mode 100644
index 0000000000..596e2c47eb
--- /dev/null
+++ b/security/sandbox/chromium/base/win/scoped_handle_verifier.h
@@ -0,0 +1,88 @@
+// Copyright 2018 The Chromium Authors. 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_WIN_SCOPED_HANDLE_VERIFIER_H_
+#define BASE_WIN_SCOPED_HANDLE_VERIFIER_H_
+
+#include "base/win/windows_types.h"
+
+#include <unordered_map>
+
+#include "base/base_export.h"
+#include "base/debug/stack_trace.h"
+#include "base/hash/hash.h"
+#include "base/synchronization/lock_impl.h"
+#include "base/threading/thread_local.h"
+
+namespace base {
+namespace win {
+namespace internal {
+
+struct HandleHash {
+ size_t operator()(const HANDLE& handle) const {
+ return base::FastHash(as_bytes(make_span(&handle, 1)));
+ }
+};
+
+struct ScopedHandleVerifierInfo {
+ const void* owner;
+ const void* pc1;
+ const void* pc2;
+ base::debug::StackTrace stack;
+ DWORD thread_id;
+};
+
+// 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).
+// We need [[clang::lto_visibility_public]] because instances of this class are
+// passed across module boundaries. This means different modules must have
+// compatible definitions of the class even when whole program optimization is
+// enabled - which is what this attribute accomplishes. The pragma stops MSVC
+// from emitting an unrecognized attribute warning.
+#pragma warning(push)
+#pragma warning(disable : 5030)
+class [[clang::lto_visibility_public]] ScopedHandleVerifier {
+#pragma warning(pop)
+ public:
+ explicit ScopedHandleVerifier(bool enabled);
+
+ // Retrieves the current verifier.
+ static ScopedHandleVerifier* 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);
+ virtual HMODULE GetModule() const;
+
+ private:
+ ~ScopedHandleVerifier(); // Not implemented.
+
+ static base::internal::LockImpl* GetLock();
+ static void InstallVerifier();
+
+ base::debug::StackTrace creation_stack_;
+ bool enabled_;
+ base::ThreadLocalBoolean closing_;
+ base::internal::LockImpl* lock_;
+ std::unordered_map<HANDLE, ScopedHandleVerifierInfo, HandleHash> map_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedHandleVerifier);
+};
+
+// This testing function returns the module that the ActiveVerifier concrete
+// implementation was instantiated in.
+BASE_EXPORT HMODULE GetHandleVerifierModuleForTesting();
+
+} // namespace internal
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_HANDLE_VERIFIER_H_
diff --git a/security/sandbox/chromium/base/win/scoped_process_information.cc b/security/sandbox/chromium/base/win/scoped_process_information.cc
new file mode 100644
index 0000000000..d3024bb5e9
--- /dev/null
+++ b/security/sandbox/chromium/base/win/scoped_process_information.cc
@@ -0,0 +1,107 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_process_information.h"
+
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+namespace {
+
+// Duplicates source into target, returning true upon success. |target| is
+// guaranteed to be untouched in case of failure. Succeeds with no side-effects
+// if source is NULL.
+bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) {
+ if (!source)
+ return true;
+
+ HANDLE temp = nullptr;
+ if (!::DuplicateHandle(::GetCurrentProcess(), source, ::GetCurrentProcess(),
+ &temp, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
+ DWORD last_error = ::GetLastError();
+ DPLOG(ERROR) << "Failed to duplicate a handle " << last_error;
+ ::SetLastError(last_error);
+ return false;
+ }
+ target->Set(temp);
+ return true;
+}
+
+} // namespace
+
+ScopedProcessInformation::ScopedProcessInformation() = default;
+
+ScopedProcessInformation::ScopedProcessInformation(
+ const PROCESS_INFORMATION& process_info) {
+ Set(process_info);
+}
+
+ScopedProcessInformation::~ScopedProcessInformation() {
+ Close();
+}
+
+bool ScopedProcessInformation::IsValid() const {
+ return process_id_ || process_handle_.Get() || thread_id_ ||
+ thread_handle_.Get();
+}
+
+void ScopedProcessInformation::Close() {
+ process_handle_.Close();
+ thread_handle_.Close();
+ process_id_ = 0;
+ thread_id_ = 0;
+}
+
+void ScopedProcessInformation::Set(const PROCESS_INFORMATION& process_info) {
+ if (IsValid())
+ Close();
+
+ process_handle_.Set(process_info.hProcess);
+ thread_handle_.Set(process_info.hThread);
+ process_id_ = process_info.dwProcessId;
+ thread_id_ = process_info.dwThreadId;
+}
+
+bool ScopedProcessInformation::DuplicateFrom(
+ const ScopedProcessInformation& other) {
+ DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL";
+ DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid";
+
+ if (CheckAndDuplicateHandle(other.process_handle(), &process_handle_) &&
+ CheckAndDuplicateHandle(other.thread_handle(), &thread_handle_)) {
+ process_id_ = other.process_id();
+ thread_id_ = other.thread_id();
+ return true;
+ }
+
+ return false;
+}
+
+PROCESS_INFORMATION ScopedProcessInformation::Take() {
+ PROCESS_INFORMATION process_information = {};
+ process_information.hProcess = process_handle_.Take();
+ process_information.hThread = thread_handle_.Take();
+ process_information.dwProcessId = process_id();
+ process_information.dwThreadId = thread_id();
+ process_id_ = 0;
+ thread_id_ = 0;
+
+ return process_information;
+}
+
+HANDLE ScopedProcessInformation::TakeProcessHandle() {
+ process_id_ = 0;
+ return process_handle_.Take();
+}
+
+HANDLE ScopedProcessInformation::TakeThreadHandle() {
+ thread_id_ = 0;
+ return thread_handle_.Take();
+}
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/scoped_process_information.h b/security/sandbox/chromium/base/win/scoped_process_information.h
new file mode 100644
index 0000000000..3b85d1bfab
--- /dev/null
+++ b/security/sandbox/chromium/base/win/scoped_process_information.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
+#define BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+
+namespace base {
+namespace win {
+
+// Manages the closing of process and thread handles from PROCESS_INFORMATION
+// structures. Allows clients to take ownership of either handle independently.
+class BASE_EXPORT ScopedProcessInformation {
+ public:
+ ScopedProcessInformation();
+ explicit ScopedProcessInformation(const PROCESS_INFORMATION& process_info);
+ ~ScopedProcessInformation();
+
+ // Returns true iff this instance is holding a thread and/or process handle.
+ bool IsValid() const;
+
+ // Closes the held thread and process handles, if any.
+ void Close();
+
+ // Populates this instance with the provided |process_info|.
+ void Set(const PROCESS_INFORMATION& process_info);
+
+ // Populates this instance with duplicate handles and the thread/process IDs
+ // from |other|. Returns false in case of failure, in which case this instance
+ // will be completely unpopulated.
+ bool DuplicateFrom(const ScopedProcessInformation& other);
+
+ // Transfers ownership of the held PROCESS_INFORMATION, if any, away from this
+ // instance.
+ PROCESS_INFORMATION Take();
+
+ // Transfers ownership of the held process handle, if any, away from this
+ // instance. Note that the related process_id will also be cleared.
+ HANDLE TakeProcessHandle();
+
+ // Transfers ownership of the held thread handle, if any, away from this
+ // instance. Note that the related thread_id will also be cleared.
+ HANDLE TakeThreadHandle();
+
+ // Returns the held process handle, if any, while retaining ownership.
+ HANDLE process_handle() const { return process_handle_.Get(); }
+
+ // Returns the held thread handle, if any, while retaining ownership.
+ HANDLE thread_handle() const { return thread_handle_.Get(); }
+
+ // Returns the held process id, if any.
+ DWORD process_id() const { return process_id_; }
+
+ // Returns the held thread id, if any.
+ DWORD thread_id() const { return thread_id_; }
+
+ private:
+ ScopedHandle process_handle_;
+ ScopedHandle thread_handle_;
+ DWORD process_id_ = 0;
+ DWORD thread_id_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedProcessInformation);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_SCOPED_PROCESS_INFORMATION_H_
diff --git a/security/sandbox/chromium/base/win/startup_information.cc b/security/sandbox/chromium/base/win/startup_information.cc
new file mode 100644
index 0000000000..a78508dcad
--- /dev/null
+++ b/security/sandbox/chromium/base/win/startup_information.cc
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/startup_information.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace win {
+
+StartupInformation::StartupInformation() : startup_info_() {
+ startup_info_.StartupInfo.cb = sizeof(startup_info_);
+}
+
+StartupInformation::~StartupInformation() {
+ if (startup_info_.lpAttributeList) {
+ ::DeleteProcThreadAttributeList(startup_info_.lpAttributeList);
+ }
+}
+
+bool StartupInformation::InitializeProcThreadAttributeList(
+ DWORD attribute_count) {
+ if (startup_info_.StartupInfo.cb != sizeof(startup_info_) ||
+ startup_info_.lpAttributeList) {
+ return false;
+ }
+
+ SIZE_T size = 0;
+ ::InitializeProcThreadAttributeList(nullptr, attribute_count, 0, &size);
+ if (size == 0)
+ return false;
+
+ auto attribute_list = std::make_unique<char[]>(size);
+ auto* attribute_list_ptr =
+ reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(attribute_list.get());
+ if (!::InitializeProcThreadAttributeList(attribute_list_ptr, attribute_count,
+ 0, &size)) {
+ return false;
+ }
+
+ attribute_list_ = std::move(attribute_list);
+ startup_info_.lpAttributeList = attribute_list_ptr;
+
+ return true;
+}
+
+bool StartupInformation::UpdateProcThreadAttribute(DWORD_PTR attribute,
+ void* value,
+ size_t size) {
+ if (!startup_info_.lpAttributeList)
+ return false;
+ return !!::UpdateProcThreadAttribute(startup_info_.lpAttributeList, 0,
+ attribute, value, size, nullptr,
+ nullptr);
+}
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/startup_information.h b/security/sandbox/chromium/base/win/startup_information.h
new file mode 100644
index 0000000000..7ef6965dd5
--- /dev/null
+++ b/security/sandbox/chromium/base/win/startup_information.h
@@ -0,0 +1,53 @@
+// 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_WIN_STARTUP_INFORMATION_H_
+#define BASE_WIN_STARTUP_INFORMATION_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/macros.h"
+
+namespace base {
+namespace win {
+
+// Manages the lifetime of additional attributes in STARTUPINFOEX.
+class BASE_EXPORT StartupInformation {
+ public:
+ StartupInformation();
+
+ ~StartupInformation();
+
+ // Initialize the attribute list for the specified number of entries.
+ 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; }
+ LPSTARTUPINFOW startup_info() const {
+ return const_cast<const LPSTARTUPINFOW>(&startup_info_.StartupInfo);
+ }
+
+ bool has_extended_startup_info() const {
+ return !!startup_info_.lpAttributeList;
+ }
+
+ private:
+ std::unique_ptr<char[]> attribute_list_;
+ STARTUPINFOEXW startup_info_;
+ DISALLOW_COPY_AND_ASSIGN(StartupInformation);
+};
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_STARTUP_INFORMATION_H_
diff --git a/security/sandbox/chromium/base/win/static_constants.cc b/security/sandbox/chromium/base/win/static_constants.cc
new file mode 100644
index 0000000000..f9894a22bd
--- /dev/null
+++ b/security/sandbox/chromium/base/win/static_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/static_constants.h"
+
+namespace base {
+namespace win {
+
+const char kApplicationVerifierDllName[] = "verifier.dll";
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/static_constants.h b/security/sandbox/chromium/base/win/static_constants.h
new file mode 100644
index 0000000000..98a631f7cf
--- /dev/null
+++ b/security/sandbox/chromium/base/win/static_constants.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines constants needed before imports (like base.dll) are fully resolved.
+// For example, constants defined here can be used by interceptions (i.e. hooks)
+// in the sandbox, which run before imports are resolved, and can therefore only
+// reference static variables.
+
+#ifndef BASE_WIN_STATIC_CONSTANTS_H_
+#define BASE_WIN_STATIC_CONSTANTS_H_
+
+namespace base {
+namespace win {
+
+extern const char kApplicationVerifierDllName[];
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_STATIC_CONSTANTS_H_
diff --git a/security/sandbox/chromium/base/win/windows_types.h b/security/sandbox/chromium/base/win/windows_types.h
new file mode 100644
index 0000000000..9be05f3c25
--- /dev/null
+++ b/security/sandbox/chromium/base/win/windows_types.h
@@ -0,0 +1,278 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains defines and typedefs that allow popular Windows types to
+// be used without the overhead of including windows.h.
+
+#ifndef BASE_WIN_WINDOWS_TYPES_H
+#define BASE_WIN_WINDOWS_TYPES_H
+
+// Needed for function prototypes.
+#if defined(__MINGW32__)
+// MinGW doesn't have this file yet, but we only need this define.
+// Bug 1552706 tracks removing this and the one below.
+#define _Releases_exclusive_lock_(lock)
+// MinGW doesn't appear to have this in specstrings.h either.
+#define _Post_equals_last_error_
+#else
+#include <concurrencysal.h>
+#endif
+#include <sal.h>
+#include <specstrings.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// typedef and define the most commonly used Windows integer types.
+
+typedef unsigned long DWORD;
+typedef long LONG;
+typedef __int64 LONGLONG;
+typedef unsigned __int64 ULONGLONG;
+
+#define VOID void
+typedef char CHAR;
+typedef short SHORT;
+typedef long LONG;
+typedef int INT;
+typedef unsigned int UINT;
+typedef unsigned int* PUINT;
+typedef void* LPVOID;
+typedef void* PVOID;
+typedef void* HANDLE;
+typedef int BOOL;
+typedef unsigned char BYTE;
+typedef BYTE BOOLEAN;
+typedef DWORD ULONG;
+typedef unsigned short WORD;
+typedef WORD UWORD;
+typedef WORD ATOM;
+
+#if defined(_WIN64)
+typedef __int64 INT_PTR, *PINT_PTR;
+typedef unsigned __int64 UINT_PTR, *PUINT_PTR;
+
+typedef __int64 LONG_PTR, *PLONG_PTR;
+typedef unsigned __int64 ULONG_PTR, *PULONG_PTR;
+#else
+typedef __w64 int INT_PTR, *PINT_PTR;
+typedef __w64 unsigned int UINT_PTR, *PUINT_PTR;
+
+typedef __w64 long LONG_PTR, *PLONG_PTR;
+typedef __w64 unsigned long ULONG_PTR, *PULONG_PTR;
+#endif
+
+typedef UINT_PTR WPARAM;
+typedef LONG_PTR LPARAM;
+typedef LONG_PTR LRESULT;
+#define LRESULT LONG_PTR
+typedef _Return_type_success_(return >= 0) long HRESULT;
+
+typedef ULONG_PTR SIZE_T, *PSIZE_T;
+typedef LONG_PTR SSIZE_T, *PSSIZE_T;
+
+typedef DWORD ACCESS_MASK;
+typedef ACCESS_MASK REGSAM;
+
+// As defined in guiddef.h.
+#ifndef _REFGUID_DEFINED
+#define _REFGUID_DEFINED
+#define REFGUID const GUID&
+#endif
+
+// Forward declare Windows compatible handles.
+
+#define CHROME_DECLARE_HANDLE(name) \
+ struct name##__; \
+ typedef struct name##__* name
+CHROME_DECLARE_HANDLE(HDESK);
+CHROME_DECLARE_HANDLE(HGLRC);
+CHROME_DECLARE_HANDLE(HICON);
+CHROME_DECLARE_HANDLE(HINSTANCE);
+CHROME_DECLARE_HANDLE(HKEY);
+CHROME_DECLARE_HANDLE(HKL);
+CHROME_DECLARE_HANDLE(HMENU);
+CHROME_DECLARE_HANDLE(HWINSTA);
+CHROME_DECLARE_HANDLE(HWND);
+#undef CHROME_DECLARE_HANDLE
+
+typedef LPVOID HINTERNET;
+typedef HINSTANCE HMODULE;
+typedef PVOID LSA_HANDLE;
+
+// Forward declare some Windows struct/typedef sets.
+
+typedef struct _OVERLAPPED OVERLAPPED;
+typedef struct tagMSG MSG, *PMSG, *NPMSG, *LPMSG;
+
+typedef struct _RTL_SRWLOCK RTL_SRWLOCK;
+typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK;
+
+typedef struct _GUID GUID;
+typedef GUID CLSID;
+
+typedef struct tagLOGFONTW LOGFONTW, *PLOGFONTW, *NPLOGFONTW, *LPLOGFONTW;
+typedef LOGFONTW LOGFONT;
+
+typedef struct _FILETIME FILETIME;
+
+typedef struct tagMENUITEMINFOW MENUITEMINFOW, MENUITEMINFO;
+
+typedef struct tagNMHDR NMHDR;
+
+typedef PVOID PSID;
+
+// Declare Chrome versions of some Windows structures. These are needed for
+// when we need a concrete type but don't want to pull in Windows.h. We can't
+// declare the Windows types so we declare our types and cast to the Windows
+// types in a few places.
+
+struct CHROME_SRWLOCK {
+ PVOID Ptr;
+};
+
+struct CHROME_CONDITION_VARIABLE {
+ PVOID Ptr;
+};
+
+// Define some commonly used Windows constants. Note that the layout of these
+// macros - including internal spacing - must be 100% consistent with windows.h.
+
+// clang-format off
+
+#ifndef INVALID_HANDLE_VALUE
+// Work around there being two slightly different definitions in the SDK.
+#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
+#endif
+#define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF)
+#define HTNOWHERE 0
+#define MAX_PATH 260
+#define CS_GLOBALCLASS 0x4000
+
+#define ERROR_SUCCESS 0L
+#define ERROR_FILE_NOT_FOUND 2L
+#define ERROR_ACCESS_DENIED 5L
+#define ERROR_INVALID_HANDLE 6L
+#define ERROR_SHARING_VIOLATION 32L
+#define ERROR_LOCK_VIOLATION 33L
+#define REG_BINARY ( 3ul )
+
+#define STATUS_PENDING ((DWORD )0x00000103L)
+#define STILL_ACTIVE STATUS_PENDING
+#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
+#define FAILED(hr) (((HRESULT)(hr)) < 0)
+
+#define HKEY_CLASSES_ROOT (( HKEY ) (ULONG_PTR)((LONG)0x80000000) )
+#define HKEY_LOCAL_MACHINE (( HKEY ) (ULONG_PTR)((LONG)0x80000002) )
+#define HKEY_CURRENT_USER (( HKEY ) (ULONG_PTR)((LONG)0x80000001) )
+#define KEY_QUERY_VALUE (0x0001)
+#define KEY_SET_VALUE (0x0002)
+#define KEY_CREATE_SUB_KEY (0x0004)
+#define KEY_ENUMERATE_SUB_KEYS (0x0008)
+#define KEY_NOTIFY (0x0010)
+#define KEY_CREATE_LINK (0x0020)
+#define KEY_WOW64_32KEY (0x0200)
+#define KEY_WOW64_64KEY (0x0100)
+#define KEY_WOW64_RES (0x0300)
+
+#define READ_CONTROL (0x00020000L)
+#define SYNCHRONIZE (0x00100000L)
+
+#define STANDARD_RIGHTS_READ (READ_CONTROL)
+#define STANDARD_RIGHTS_WRITE (READ_CONTROL)
+#define STANDARD_RIGHTS_ALL (0x001F0000L)
+
+#define KEY_READ ((STANDARD_RIGHTS_READ |\
+ KEY_QUERY_VALUE |\
+ KEY_ENUMERATE_SUB_KEYS |\
+ KEY_NOTIFY) \
+ & \
+ (~SYNCHRONIZE))
+
+
+#define KEY_WRITE ((STANDARD_RIGHTS_WRITE |\
+ KEY_SET_VALUE |\
+ KEY_CREATE_SUB_KEY) \
+ & \
+ (~SYNCHRONIZE))
+
+#define KEY_ALL_ACCESS ((STANDARD_RIGHTS_ALL |\
+ KEY_QUERY_VALUE |\
+ KEY_SET_VALUE |\
+ KEY_CREATE_SUB_KEY |\
+ KEY_ENUMERATE_SUB_KEYS |\
+ KEY_NOTIFY |\
+ KEY_CREATE_LINK) \
+ & \
+ (~SYNCHRONIZE))
+
+// clang-format on
+
+// Define some macros needed when prototyping Windows functions.
+
+#define DECLSPEC_IMPORT __declspec(dllimport)
+#define WINBASEAPI DECLSPEC_IMPORT
+#define WINUSERAPI DECLSPEC_IMPORT
+#define WINAPI __stdcall
+#define CALLBACK __stdcall
+
+// Needed for optimal lock performance.
+WINBASEAPI _Releases_exclusive_lock_(*SRWLock) VOID WINAPI
+ ReleaseSRWLockExclusive(_Inout_ PSRWLOCK SRWLock);
+
+// Needed to support protobuf's GetMessage macro magic.
+WINUSERAPI BOOL WINAPI GetMessageW(_Out_ LPMSG lpMsg,
+ _In_opt_ HWND hWnd,
+ _In_ UINT wMsgFilterMin,
+ _In_ UINT wMsgFilterMax);
+
+// Needed for thread_local_storage.h
+WINBASEAPI LPVOID WINAPI TlsGetValue(_In_ DWORD dwTlsIndex);
+
+// Needed for scoped_handle.h
+WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI
+ GetLastError(VOID);
+
+WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode);
+
+#ifdef __cplusplus
+}
+#endif
+
+// These macros are all defined by windows.h and are also used as the names of
+// functions in the Chromium code base. Add to this list as needed whenever
+// there is a Windows macro which causes a function call to be renamed. This
+// ensures that the same renaming will happen everywhere. Includes of this file
+// can be added wherever needed to ensure this consistent renaming.
+
+#define CopyFile CopyFileW
+#define CreateDirectory CreateDirectoryW
+#define CreateEvent CreateEventW
+#define CreateFile CreateFileW
+#define CreateService CreateServiceW
+#define DeleteFile DeleteFileW
+#define DispatchMessage DispatchMessageW
+#define DrawText DrawTextW
+#define FindFirstFile FindFirstFileW
+#define FindNextFile FindNextFileW
+#define GetComputerName GetComputerNameW
+#define GetCurrentDirectory GetCurrentDirectoryW
+#define GetCurrentTime() GetTickCount()
+#define GetFileAttributes GetFileAttributesW
+#define GetMessage GetMessageW
+#define GetUserName GetUserNameW
+#define LoadIcon LoadIconW
+#define LoadImage LoadImageW
+#define PostMessage PostMessageW
+#define RemoveDirectory RemoveDirectoryW
+#define ReplaceFile ReplaceFileW
+#define ReportEvent ReportEventW
+#define SendMessage SendMessageW
+#define SendMessageCallback SendMessageCallbackW
+#define SetCurrentDirectory SetCurrentDirectoryW
+#define StartService StartServiceW
+#define UpdateResource UpdateResourceW
+
+#endif // BASE_WIN_WINDOWS_TYPES_H
diff --git a/security/sandbox/chromium/base/win/windows_version.cc b/security/sandbox/chromium/base/win/windows_version.cc
new file mode 100644
index 0000000000..ef96f8796f
--- /dev/null
+++ b/security/sandbox/chromium/base/win/windows_version.cc
@@ -0,0 +1,313 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/windows_version.h"
+
+#include <windows.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "base/file_version_info_win.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/registry.h"
+
+#if !defined(__clang__) && _MSC_FULL_VER < 191125507
+#error VS 2017 Update 3.2 or higher is required
+#endif
+
+#if !defined(NTDDI_WIN10_RS4)
+#error Windows 10.0.17134.0 SDK or higher required.
+#endif
+
+namespace base {
+namespace win {
+
+namespace {
+
+// The values under the CurrentVersion registry hive are mirrored under
+// the corresponding Wow6432 hive.
+constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] =
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
+
+// Returns the "UBR" (Windows 10 patch number) and "ReleaseId" (Windows 10
+// release number) from the registry. "UBR" is an undocumented value and will be
+// 0 if the value was not found. "ReleaseId" will be an empty string if the
+// value is not found.
+std::pair<int, std::string> GetVersionData() {
+ DWORD ubr = 0;
+ std::wstring release_id;
+ RegKey key;
+
+ if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion,
+ KEY_QUERY_VALUE) == ERROR_SUCCESS) {
+ key.ReadValueDW(L"UBR", &ubr);
+ key.ReadValue(L"ReleaseId", &release_id);
+ }
+
+ return std::make_pair(static_cast<int>(ubr), WideToUTF8(release_id));
+}
+
+const _SYSTEM_INFO& GetSystemInfoStorage() {
+ static const NoDestructor<_SYSTEM_INFO> system_info([] {
+ _SYSTEM_INFO info = {};
+ ::GetNativeSystemInfo(&info);
+ return info;
+ }());
+ return *system_info;
+}
+
+} // namespace
+
+// static
+OSInfo** OSInfo::GetInstanceStorage() {
+ // Note: we don't use the Singleton class because it depends on AtExitManager,
+ // and it's convenient for other modules to use this class without it.
+ static OSInfo* info = []() {
+ _OSVERSIONINFOEXW version_info = {sizeof(version_info)};
+ ::GetVersionEx(reinterpret_cast<_OSVERSIONINFOW*>(&version_info));
+
+ DWORD os_type = 0;
+ ::GetProductInfo(version_info.dwMajorVersion, version_info.dwMinorVersion,
+ 0, 0, &os_type);
+
+ return new OSInfo(version_info, GetSystemInfoStorage(), os_type);
+ }();
+
+ return &info;
+}
+
+// static
+OSInfo* OSInfo::GetInstance() {
+ return *GetInstanceStorage();
+}
+
+// static
+OSInfo::WindowsArchitecture OSInfo::GetArchitecture() {
+ switch (GetSystemInfoStorage().wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ return X86_ARCHITECTURE;
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ return X64_ARCHITECTURE;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ return IA64_ARCHITECTURE;
+ case PROCESSOR_ARCHITECTURE_ARM64:
+ return ARM64_ARCHITECTURE;
+ default:
+ return OTHER_ARCHITECTURE;
+ }
+}
+
+OSInfo::OSInfo(const _OSVERSIONINFOEXW& version_info,
+ const _SYSTEM_INFO& system_info,
+ int os_type)
+ : version_(Version::PRE_XP),
+ wow64_status_(GetWOW64StatusForProcess(GetCurrentProcess())) {
+ version_number_.major = version_info.dwMajorVersion;
+ version_number_.minor = version_info.dwMinorVersion;
+ version_number_.build = version_info.dwBuildNumber;
+ std::tie(version_number_.patch, release_id_) = GetVersionData();
+ version_ = MajorMinorBuildToVersion(
+ version_number_.major, version_number_.minor, version_number_.build);
+ service_pack_.major = version_info.wServicePackMajor;
+ service_pack_.minor = version_info.wServicePackMinor;
+ service_pack_str_ = WideToUTF8(version_info.szCSDVersion);
+
+ processors_ = system_info.dwNumberOfProcessors;
+ allocation_granularity_ = system_info.dwAllocationGranularity;
+
+ if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) {
+ // Only present on Vista+.
+ switch (os_type) {
+ case PRODUCT_CLUSTER_SERVER:
+ case PRODUCT_DATACENTER_SERVER:
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ case PRODUCT_ENTERPRISE_SERVER:
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ case PRODUCT_ENTERPRISE_SERVER_IA64:
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ case PRODUCT_STANDARD_SERVER:
+ case PRODUCT_STANDARD_SERVER_CORE:
+ case PRODUCT_WEB_SERVER:
+ version_type_ = SUITE_SERVER;
+ break;
+ case PRODUCT_PROFESSIONAL:
+ case PRODUCT_ULTIMATE:
+ version_type_ = SUITE_PROFESSIONAL;
+ break;
+ case PRODUCT_ENTERPRISE:
+ case PRODUCT_ENTERPRISE_E:
+ case PRODUCT_ENTERPRISE_EVALUATION:
+ case PRODUCT_ENTERPRISE_N:
+ case PRODUCT_ENTERPRISE_N_EVALUATION:
+ case PRODUCT_ENTERPRISE_S:
+ case PRODUCT_ENTERPRISE_S_EVALUATION:
+ case PRODUCT_ENTERPRISE_S_N:
+ case PRODUCT_ENTERPRISE_S_N_EVALUATION:
+ case PRODUCT_BUSINESS:
+ case PRODUCT_BUSINESS_N:
+ version_type_ = SUITE_ENTERPRISE;
+ break;
+ case PRODUCT_EDUCATION:
+ case PRODUCT_EDUCATION_N:
+ version_type_ = SUITE_EDUCATION;
+ break;
+ case PRODUCT_HOME_BASIC:
+ case PRODUCT_HOME_PREMIUM:
+ case PRODUCT_STARTER:
+ default:
+ version_type_ = SUITE_HOME;
+ break;
+ }
+ } else if (version_info.dwMajorVersion == 5 &&
+ version_info.dwMinorVersion == 2) {
+ if (version_info.wProductType == VER_NT_WORKSTATION &&
+ system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
+ version_type_ = SUITE_PROFESSIONAL;
+ } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER) {
+ version_type_ = SUITE_HOME;
+ } else {
+ version_type_ = SUITE_SERVER;
+ }
+ } else if (version_info.dwMajorVersion == 5 &&
+ version_info.dwMinorVersion == 1) {
+ if (version_info.wSuiteMask & VER_SUITE_PERSONAL)
+ version_type_ = SUITE_HOME;
+ else
+ version_type_ = SUITE_PROFESSIONAL;
+ } else {
+ // Windows is pre XP so we don't care but pick a safe default.
+ version_type_ = SUITE_HOME;
+ }
+}
+
+OSInfo::~OSInfo() = default;
+
+Version OSInfo::Kernel32Version() const {
+ static const Version kernel32_version =
+ MajorMinorBuildToVersion(Kernel32BaseVersion().components()[0],
+ Kernel32BaseVersion().components()[1],
+ Kernel32BaseVersion().components()[2]);
+ return kernel32_version;
+}
+
+Version OSInfo::UcrtVersion() const {
+ auto ucrt_version_info = FileVersionInfoWin::CreateFileVersionInfoWin(
+ FilePath(FILE_PATH_LITERAL("ucrtbase.dll")));
+ if (ucrt_version_info) {
+ auto ucrt_components = ucrt_version_info->GetFileVersion().components();
+ if (ucrt_components.size() == 4) {
+ return MajorMinorBuildToVersion(ucrt_components[0], ucrt_components[1],
+ ucrt_components[2]);
+ }
+ }
+ return Version();
+}
+
+// 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.
+base::Version OSInfo::Kernel32BaseVersion() const {
+ static const NoDestructor<base::Version> version([] {
+ std::unique_ptr<FileVersionInfoWin> file_version_info =
+ FileVersionInfoWin::CreateFileVersionInfoWin(
+ FilePath(FILE_PATH_LITERAL("kernel32.dll")));
+ if (!file_version_info) {
+ // crbug.com/912061: on some systems it seems kernel32.dll might be
+ // corrupted or not in a state to get version info. In this case try
+ // kernelbase.dll as a fallback.
+ file_version_info = FileVersionInfoWin::CreateFileVersionInfoWin(
+ FilePath(FILE_PATH_LITERAL("kernelbase.dll")));
+ }
+ CHECK(file_version_info);
+ return file_version_info->GetFileVersion();
+ }());
+ return *version;
+}
+
+std::string OSInfo::processor_model_name() {
+ if (processor_model_name_.empty()) {
+ const wchar_t kProcessorNameString[] =
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
+ RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ);
+ std::wstring value;
+ key.ReadValue(L"ProcessorNameString", &value);
+ processor_model_name_ = WideToUTF8(value);
+ }
+ return processor_model_name_;
+}
+
+// static
+OSInfo::WOW64Status OSInfo::GetWOW64StatusForProcess(HANDLE process_handle) {
+ BOOL is_wow64 = FALSE;
+ if (!::IsWow64Process(process_handle, &is_wow64))
+ return WOW64_UNKNOWN;
+ return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED;
+}
+
+// With the exception of Server 2003, server variants are treated the same as
+// the corresponding workstation release.
+// static
+Version OSInfo::MajorMinorBuildToVersion(int major, int minor, int build) {
+ if (major == 10) {
+ if (build >= 19041)
+ return Version::WIN10_20H1;
+ if (build >= 18362)
+ return Version::WIN10_19H1;
+ if (build >= 17763)
+ return Version::WIN10_RS5;
+ if (build >= 17134)
+ return Version::WIN10_RS4;
+ if (build >= 16299)
+ return Version::WIN10_RS3;
+ if (build >= 15063)
+ return Version::WIN10_RS2;
+ if (build >= 14393)
+ return Version::WIN10_RS1;
+ if (build >= 10586)
+ return Version::WIN10_TH2;
+ return Version::WIN10;
+ }
+
+ if (major > 6) {
+ // Hitting this likely means that it's time for a >10 block above.
+ NOTREACHED() << major << "." << minor << "." << build;
+ return Version::WIN_LAST;
+ }
+
+ if (major == 6) {
+ switch (minor) {
+ case 0:
+ return Version::VISTA;
+ case 1:
+ return Version::WIN7;
+ case 2:
+ return Version::WIN8;
+ default:
+ DCHECK_EQ(minor, 3);
+ return Version::WIN8_1;
+ }
+ }
+
+ 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;
+ }
+
+ // Win 2000 or older.
+ return Version::PRE_XP;
+}
+
+Version GetVersion() {
+ return OSInfo::GetInstance()->version();
+}
+
+} // namespace win
+} // namespace base
diff --git a/security/sandbox/chromium/base/win/windows_version.h b/security/sandbox/chromium/base/win/windows_version.h
new file mode 100644
index 0000000000..08d6e274ef
--- /dev/null
+++ b/security/sandbox/chromium/base/win/windows_version.h
@@ -0,0 +1,187 @@
+// 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_WIN_WINDOWS_VERSION_H_
+#define BASE_WIN_WINDOWS_VERSION_H_
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/version.h"
+
+using HANDLE = void*;
+struct _OSVERSIONINFOEXW;
+struct _SYSTEM_INFO;
+
+namespace base {
+namespace test {
+class ScopedOSInfoOverride;
+} // namespace test
+} // namespace base
+
+namespace base {
+namespace win {
+
+// The running version of Windows. This is declared outside OSInfo for
+// 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 class Version {
+ PRE_XP = 0, // Not supported.
+ XP = 1,
+ SERVER_2003 = 2, // Also includes XP Pro x64 and Server 2003 R2.
+ VISTA = 3, // Also includes Windows Server 2008.
+ WIN7 = 4, // Also includes Windows Server 2008 R2.
+ WIN8 = 5, // Also includes Windows Server 2012.
+ WIN8_1 = 6, // Also includes Windows Server 2012 R2.
+ WIN10 = 7, // Threshold 1: Version 1507, Build 10240.
+ WIN10_TH2 = 8, // Threshold 2: Version 1511, Build 10586.
+ WIN10_RS1 = 9, // Redstone 1: Version 1607, Build 14393.
+ WIN10_RS2 = 10, // Redstone 2: Version 1703, Build 15063.
+ WIN10_RS3 = 11, // Redstone 3: Version 1709, Build 16299.
+ WIN10_RS4 = 12, // Redstone 4: Version 1803, Build 17134.
+ WIN10_RS5 = 13, // Redstone 5: Version 1809, Build 17763.
+ WIN10_19H1 = 14, // 19H1: Version 1903, Build 18362.
+ WIN10_20H1 = 15, // 20H1: Version 2004, Build 19041.
+ // On edit, update tools\metrics\histograms\enums.xml "WindowsVersion" and
+ // "GpuBlacklistFeatureTestResultsWindows2".
+ WIN_LAST, // Indicates error condition.
+};
+
+// A rough bucketing of the available types of versions of Windows. This is used
+// to distinguish enterprise enabled versions from home versions and potentially
+// server versions. Keep these values in the same order, since they are used as
+// is for metrics histogram ids.
+enum VersionType {
+ SUITE_HOME = 0,
+ SUITE_PROFESSIONAL,
+ SUITE_SERVER,
+ SUITE_ENTERPRISE,
+ SUITE_EDUCATION,
+ SUITE_LAST,
+};
+
+// A singleton that can be used to query various pieces of information about the
+// OS and process state. Note that this doesn't use the base Singleton class, so
+// it can be used without an AtExitManager.
+class BASE_EXPORT OSInfo {
+ public:
+ struct VersionNumber {
+ int major;
+ int minor;
+ int build;
+ int patch;
+ };
+
+ struct ServicePack {
+ int major;
+ int minor;
+ };
+
+ // The processor architecture this copy of Windows natively uses. For
+ // example, given an x64-capable processor, we have three possibilities:
+ // 32-bit Chrome running on 32-bit Windows: X86_ARCHITECTURE
+ // 32-bit Chrome running on 64-bit Windows via WOW64: X64_ARCHITECTURE
+ // 64-bit Chrome running on 64-bit Windows: X64_ARCHITECTURE
+ enum WindowsArchitecture {
+ X86_ARCHITECTURE,
+ X64_ARCHITECTURE,
+ IA64_ARCHITECTURE,
+ ARM64_ARCHITECTURE,
+ OTHER_ARCHITECTURE,
+ };
+
+ // Whether a process is running under WOW64 (the wrapper that allows 32-bit
+ // processes to run on 64-bit versions of Windows). This will return
+ // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit
+ // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g.
+ // the process does not have sufficient access rights to determine this.
+ enum WOW64Status {
+ WOW64_DISABLED,
+ WOW64_ENABLED,
+ WOW64_UNKNOWN,
+ };
+
+ static OSInfo* GetInstance();
+
+ // Separate from the rest of OSInfo so it can be used during early process
+ // initialization.
+ static WindowsArchitecture GetArchitecture();
+
+ // Like wow64_status(), but for the supplied handle instead of the current
+ // process. This doesn't touch member state, so you can bypass the singleton.
+ static WOW64Status GetWOW64StatusForProcess(HANDLE process_handle);
+
+ const Version& version() const { return version_; }
+ Version Kernel32Version() const;
+ Version UcrtVersion() const;
+ base::Version Kernel32BaseVersion() const;
+ // The next two functions return arrays of values, [major, minor(, build)].
+ const VersionNumber& version_number() const { return version_number_; }
+ const VersionType& version_type() const { return version_type_; }
+ const ServicePack& service_pack() const { return service_pack_; }
+ const std::string& service_pack_str() const { return service_pack_str_; }
+ const int& processors() const { return processors_; }
+ const size_t& allocation_granularity() const {
+ return allocation_granularity_;
+ }
+ const WOW64Status& wow64_status() const { return wow64_status_; }
+ std::string processor_model_name();
+ const std::string& release_id() const { return release_id_; }
+
+ private:
+ friend class base::test::ScopedOSInfoOverride;
+ FRIEND_TEST_ALL_PREFIXES(OSInfo, MajorMinorBuildToVersion);
+ static OSInfo** GetInstanceStorage();
+
+ OSInfo(const _OSVERSIONINFOEXW& version_info,
+ const _SYSTEM_INFO& system_info,
+ int os_type);
+ ~OSInfo();
+
+ // Returns a Version value for a given OS version tuple.
+ static Version MajorMinorBuildToVersion(int major, int minor, int build);
+
+ Version version_;
+ VersionNumber version_number_;
+ VersionType version_type_;
+ ServicePack service_pack_;
+
+ // Represents the version of the OS associated to a release of
+ // Windows 10. Each version may have different releases (such as patch
+ // updates). This is the identifier of the release.
+ // Example:
+ // Windows 10 Version 1809 (OS build 17763) has multiple releases
+ // (i.e. build 17763.1, build 17763.195, build 17763.379, ...).
+ // See https://docs.microsoft.com/en-us/windows/windows-10/release-information
+ // for more information.
+ std::string release_id_;
+
+ // A string, such as "Service Pack 3", that indicates the latest Service Pack
+ // installed on the system. If no Service Pack has been installed, the string
+ // is empty.
+ std::string service_pack_str_;
+ int processors_;
+ size_t allocation_granularity_;
+ WOW64Status wow64_status_;
+ std::string processor_model_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(OSInfo);
+};
+
+// Because this is by far the most commonly-requested value from the above
+// singleton, we add a global-scope accessor here as syntactic sugar.
+BASE_EXPORT Version GetVersion();
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_WINDOWS_VERSION_H_
diff --git a/security/sandbox/chromium/build/build_config.h b/security/sandbox/chromium/build/build_config.h
new file mode 100644
index 0000000000..48109c4d46
--- /dev/null
+++ b/security/sandbox/chromium/build/build_config.h
@@ -0,0 +1,205 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file adds defines about the platform we're currently building on.
+// Operating System:
+// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) /
+// OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI
+// OS_CHROMEOS is set by the build system
+// Compiler:
+// COMPILER_MSVC / COMPILER_GCC
+// Processor:
+// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
+// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+
+#ifndef BUILD_BUILD_CONFIG_H_
+#define BUILD_BUILD_CONFIG_H_
+
+// A set of macros to use for platform detection.
+#if defined(__native_client__)
+// __native_client__ must be first, so that other OS_ defines are not set.
+#define OS_NACL 1
+// OS_NACL comes in two sandboxing technology flavors, SFI or Non-SFI.
+// PNaCl toolchain defines __native_client_nonsfi__ macro in Non-SFI build
+// mode, while it does not in SFI build mode.
+#if defined(__native_client_nonsfi__)
+#define OS_NACL_NONSFI
+#else
+#define OS_NACL_SFI
+#endif
+#elif defined(ANDROID)
+#define OS_ANDROID 1
+#elif defined(__APPLE__)
+// only include TargetConditions after testing ANDROID as some android builds
+// on mac don't have this header available and it's not needed unless the target
+// is really mac/ios.
+#include <TargetConditionals.h>
+#define OS_MACOSX 1
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OS_IOS 1
+#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#elif defined(__linux__)
+#define OS_LINUX 1
+// include a system header to pull in features.h for glibc/uclibc macros.
+#include <unistd.h>
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+// we really are using glibc, not uClibc pretending to be glibc
+#define LIBC_GLIBC 1
+#endif
+#elif defined(_WIN32)
+#define OS_WIN 1
+#elif defined(__Fuchsia__)
+#define OS_FUCHSIA 1
+#elif defined(__FreeBSD__)
+#define OS_FREEBSD 1
+#elif defined(__NetBSD__)
+#define OS_NETBSD 1
+#elif defined(__OpenBSD__)
+#define OS_OPENBSD 1
+#elif defined(__sun)
+#define OS_SOLARIS 1
+#elif defined(__QNXNTO__)
+#define OS_QNX 1
+#elif defined(_AIX)
+#define OS_AIX 1
+#elif defined(__asmjs__) || defined(__wasm__)
+#define OS_ASMJS
+#else
+#error Please add support for your platform in build/build_config.h
+#endif
+// NOTE: Adding a new port? Please follow
+// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md
+
+// For access to standard BSD features, use OS_BSD instead of a
+// more specific macro.
+#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD)
+#define OS_BSD 1
+#endif
+
+// For access to standard POSIXish features, use OS_POSIX instead of a
+// more specific macro.
+#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \
+ defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \
+ defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || \
+ defined(OS_QNX) || defined(OS_SOLARIS)
+#define OS_POSIX 1
+#endif
+
+// Compiler detection.
+#if defined(__GNUC__)
+#define COMPILER_GCC 1
+#elif defined(_MSC_VER)
+#define COMPILER_MSVC 1
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+// Processor architecture detection. For more info on what's defined, see:
+// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+// http://www.agner.org/optimize/calling_conventions.pdf
+// or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86_64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__s390x__)
+#define ARCH_CPU_S390_FAMILY 1
+#define ARCH_CPU_S390X 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif defined(__s390__)
+#define ARCH_CPU_S390_FAMILY 1
+#define ARCH_CPU_S390 1
+#define ARCH_CPU_31_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__)
+#define ARCH_CPU_PPC64_FAMILY 1
+#define ARCH_CPU_PPC64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#elif defined(__PPC64__)
+#define ARCH_CPU_PPC64_FAMILY 1
+#define ARCH_CPU_PPC64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__ARMEL__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARMEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__aarch64__) || defined(_M_ARM64)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__)
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__MIPSEL__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS64EL 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPSEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#endif
+#elif defined(__MIPSEB__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPS 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#endif
+#elif defined(__riscv) && __riscv_xlen == 64
+#define ARCH_CPU_RISCV64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#error Please add support for your architecture in build/build_config.h
+#endif
+
+// Type detection for wchar_t.
+#if defined(OS_WIN)
+#define WCHAR_T_IS_UTF16
+#elif defined(OS_FUCHSIA)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
+ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \
+ (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
+// compile in this mode (in particular, Chrome doesn't). This is intended for
+// other projects using base who manage their own dependencies and make sure
+// short wchar works for them.
+#define WCHAR_T_IS_UTF16
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+#if defined(OS_ANDROID)
+// The compiler thinks std::string::const_iterator and "const char*" are
+// equivalent types.
+#define STD_STRING_ITERATOR_IS_CHAR_POINTER
+// The compiler thinks base::string16::const_iterator and "char16*" are
+// equivalent types.
+#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER
+#endif
+
+#endif // BUILD_BUILD_CONFIG_H_
diff --git a/security/sandbox/chromium/build/buildflag.h b/security/sandbox/chromium/build/buildflag.h
new file mode 100644
index 0000000000..5776a754c4
--- /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/buildflag_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
new file mode 100644
index 0000000000..fed6368db6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.cc
@@ -0,0 +1,343 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/macros.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/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+namespace {
+
+class ReturnResultExprImpl : public internal::ResultExprImpl {
+ public:
+ explicit ReturnResultExprImpl(uint32_t ret) : ret_(ret) {}
+ ~ReturnResultExprImpl() override {}
+
+ 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:
+ bool IsAction(uint32_t action) const {
+ return (ret_ & SECCOMP_RET_ACTION) == action;
+ }
+
+ uint32_t ret_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReturnResultExprImpl);
+};
+
+class TrapResultExprImpl : public internal::ResultExprImpl {
+ public:
+ TrapResultExprImpl(TrapRegistry::TrapFnc func, const void* arg, bool safe)
+ : func_(func), arg_(arg), safe_(safe) {
+ DCHECK(func_);
+ }
+ ~TrapResultExprImpl() override {}
+
+ 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:
+ TrapRegistry::TrapFnc func_;
+ const void* arg_;
+ bool safe_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrapResultExprImpl);
+};
+
+class IfThenResultExprImpl : public internal::ResultExprImpl {
+ public:
+ IfThenResultExprImpl(BoolExpr cond,
+ ResultExpr then_result,
+ ResultExpr else_result)
+ : cond_(std::move(cond)),
+ then_result_(std::move(then_result)),
+ else_result_(std::move(else_result)) {}
+ ~IfThenResultExprImpl() override {}
+
+ 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 {
+ return then_result_->HasUnsafeTraps() || else_result_->HasUnsafeTraps();
+ }
+
+ private:
+ BoolExpr cond_;
+ ResultExpr then_result_;
+ ResultExpr else_result_;
+
+ DISALLOW_COPY_AND_ASSIGN(IfThenResultExprImpl);
+};
+
+class ConstBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ ConstBoolExprImpl(bool value) : value_(value) {}
+ ~ConstBoolExprImpl() override {}
+
+ CodeGen::Node Compile(PolicyCompiler* pc,
+ CodeGen::Node then_node,
+ CodeGen::Node else_node) const override {
+ return value_ ? then_node : else_node;
+ }
+
+ private:
+ bool value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstBoolExprImpl);
+};
+
+class MaskedEqualBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ MaskedEqualBoolExprImpl(int argno,
+ size_t width,
+ uint64_t mask,
+ uint64_t value)
+ : argno_(argno), width_(width), mask_(mask), value_(value) {}
+ ~MaskedEqualBoolExprImpl() override {}
+
+ 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:
+ int argno_;
+ size_t width_;
+ uint64_t mask_;
+ uint64_t value_;
+
+ DISALLOW_COPY_AND_ASSIGN(MaskedEqualBoolExprImpl);
+};
+
+class NegateBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ explicit NegateBoolExprImpl(BoolExpr cond) : cond_(std::move(cond)) {}
+ ~NegateBoolExprImpl() override {}
+
+ CodeGen::Node Compile(PolicyCompiler* pc,
+ CodeGen::Node then_node,
+ CodeGen::Node else_node) const override {
+ return cond_->Compile(pc, else_node, then_node);
+ }
+
+ private:
+ BoolExpr cond_;
+
+ DISALLOW_COPY_AND_ASSIGN(NegateBoolExprImpl);
+};
+
+class AndBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ AndBoolExprImpl(BoolExpr lhs, BoolExpr rhs)
+ : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {}
+ ~AndBoolExprImpl() override {}
+
+ 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:
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(AndBoolExprImpl);
+};
+
+class OrBoolExprImpl : public internal::BoolExprImpl {
+ public:
+ OrBoolExprImpl(BoolExpr lhs, BoolExpr rhs)
+ : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {}
+ ~OrBoolExprImpl() override {}
+
+ 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:
+ BoolExpr lhs_;
+ BoolExpr rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(OrBoolExprImpl);
+};
+
+} // namespace
+
+namespace internal {
+
+bool ResultExprImpl::HasUnsafeTraps() const {
+ return false;
+}
+
+bool ResultExprImpl::IsAllow() const {
+ return false;
+}
+
+bool ResultExprImpl::IsDeny() const {
+ return false;
+}
+
+uint64_t DefaultMask(size_t size) {
+ switch (size) {
+ case 4:
+ return std::numeric_limits<uint32_t>::max();
+ case 8:
+ return std::numeric_limits<uint64_t>::max();
+ default:
+ CHECK(false) << "Unimplemented DefaultMask case";
+ return 0;
+ }
+}
+
+BoolExpr ArgEq(int num, size_t size, uint64_t mask, uint64_t val) {
+ // If this is changed, update Arg<T>::EqualTo's static_cast rules
+ // accordingly.
+ CHECK(size == 4 || size == 8);
+
+ return std::make_shared<MaskedEqualBoolExprImpl>(num, size, mask, val);
+}
+
+} // namespace internal
+
+ResultExpr Allow() {
+ return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_ALLOW);
+}
+
+ResultExpr Error(int err) {
+ CHECK(err >= ErrorCode::ERR_MIN_ERRNO && err <= ErrorCode::ERR_MAX_ERRNO);
+ return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_ERRNO + err);
+}
+
+ResultExpr Kill() {
+ return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_KILL);
+}
+
+ResultExpr Trace(uint16_t aux) {
+ return std::make_shared<ReturnResultExprImpl>(SECCOMP_RET_TRACE + aux);
+}
+
+ResultExpr Trap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return std::make_shared<TrapResultExprImpl>(trap_func, aux, true /* safe */);
+}
+
+ResultExpr UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux) {
+ return std::make_shared<TrapResultExprImpl>(trap_func, aux,
+ false /* unsafe */);
+}
+
+BoolExpr BoolConst(bool value) {
+ return std::make_shared<ConstBoolExprImpl>(value);
+}
+
+BoolExpr Not(BoolExpr cond) {
+ return std::make_shared<NegateBoolExprImpl>(std::move(cond));
+}
+
+BoolExpr AllOf() {
+ return BoolConst(true);
+}
+
+BoolExpr AllOf(BoolExpr lhs, BoolExpr rhs) {
+ return std::make_shared<AndBoolExprImpl>(std::move(lhs), std::move(rhs));
+}
+
+BoolExpr AnyOf() {
+ return BoolConst(false);
+}
+
+BoolExpr AnyOf(BoolExpr lhs, BoolExpr rhs) {
+ return std::make_shared<OrBoolExprImpl>(std::move(lhs), std::move(rhs));
+}
+
+Elser If(BoolExpr cond, ResultExpr then_result) {
+ return Elser(nullptr).ElseIf(std::move(cond), std::move(then_result));
+}
+
+Elser::Elser(cons::List<Clause> clause_list) : clause_list_(clause_list) {
+}
+
+Elser::Elser(const Elser& elser) : clause_list_(elser.clause_list_) {
+}
+
+Elser::~Elser() {
+}
+
+Elser Elser::ElseIf(BoolExpr cond, ResultExpr then_result) const {
+ return Elser(Cons(std::make_pair(std::move(cond), std::move(then_result)),
+ clause_list_));
+}
+
+ResultExpr Elser::Else(ResultExpr else_result) const {
+ // We finally have the default result expression for this
+ // if/then/else sequence. Also, we've already accumulated all
+ // if/then pairs into a list of reverse order (i.e., lower priority
+ // conditions are listed before higher priority ones). E.g., an
+ // expression like
+ //
+ // If(b1, e1).ElseIf(b2, e2).ElseIf(b3, e3).Else(e4)
+ //
+ // will have built up a list like
+ //
+ // [(b3, e3), (b2, e2), (b1, e1)].
+ //
+ // Now that we have e4, we can walk the list and create a ResultExpr
+ // tree like:
+ //
+ // expr = e4
+ // expr = (b3 ? e3 : expr) = (b3 ? e3 : e4)
+ // expr = (b2 ? e2 : expr) = (b2 ? e2 : (b3 ? e3 : e4))
+ // expr = (b1 ? e1 : expr) = (b1 ? e1 : (b2 ? e2 : (b3 ? e3 : e4)))
+ //
+ // and end up with an appropriately chained tree.
+
+ ResultExpr expr = std::move(else_result);
+ for (const Clause& clause : clause_list_) {
+ expr = std::make_shared<IfThenResultExprImpl>(clause.first, clause.second,
+ std::move(expr));
+ }
+ return expr;
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+namespace std {
+template class shared_ptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+template class shared_ptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
+} // namespace std
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h
new file mode 100644
index 0000000000..555d820e01
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl.h
@@ -0,0 +1,338 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/linux/bpf_dsl/cons.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/sandbox_export.h"
+
+// The sandbox::bpf_dsl namespace provides a domain-specific language
+// to make writing BPF policies more expressive. In general, the
+// object types all have value semantics (i.e., they can be copied
+// around, returned from or passed to function calls, etc. without any
+// surprising side effects), though not all support assignment.
+//
+// An idiomatic and demonstrative (albeit silly) example of this API
+// would be:
+//
+// #include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+//
+// namespace dsl = sandbox::bpf_dsl;
+//
+// class SillyPolicy : public dsl::Policy {
+// public:
+// SillyPolicy() = default;
+// SillyPolicy(const SillyPolicy&) = delete;
+// SillyPolicy& operator=(const SillyPolicy&) = delete;
+// ~SillyPolicy() override = default;
+//
+// dsl::ResultExpr EvaluateSyscall(int sysno) const override {
+// if (sysno != __NR_fcntl)
+// return dsl::Allow();
+// dsl::Arg<int> fd(0), cmd(1);
+// dsl::Arg<unsigned long> flags(2);
+// constexpr uint64_t kGoodFlags = O_ACCMODE | O_NONBLOCK;
+// return dsl::If(dsl::AllOf(fd == 0,
+// cmd == F_SETFL,
+// (flags & ~kGoodFlags) == 0),
+// dsl::Allow())
+// .dsl::ElseIf(dsl::AnyOf(cmd == F_DUPFD, cmd == F_DUPFD_CLOEXEC),
+// dsl::Error(EMFILE))
+// .dsl::Else(dsl::Trap(SetFlagHandler, nullptr));
+// }
+// };
+//
+// More generally, the DSL currently supports the following grammar:
+//
+// 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) | Not(bool) | AllOf(bool...) | AnyOf(bool...)
+// | arg == val | arg != val
+// arg = Arg<T>(num) | arg & mask
+//
+// The semantics of each function and operator are intended to be
+// intuitive, but are described in more detail below.
+//
+// (Credit to Sean Parent's "Inheritance is the Base Class of Evil"
+// talk at Going Native 2013 for promoting value semantics via shared
+// pointers to immutable state.)
+
+namespace sandbox {
+namespace bpf_dsl {
+
+template <typename T>
+class Caser;
+
+class Elser;
+
+// ResultExpr is an opaque reference to an immutable result expression tree.
+using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>;
+
+// BoolExpr is an opaque reference to an immutable boolean expression tree.
+using BoolExpr = std::shared_ptr<const internal::BoolExprImpl>;
+
+// Allow specifies a result that the system call should be allowed to
+// execute normally.
+SANDBOX_EXPORT ResultExpr Allow();
+
+// Error specifies a result that the system call should fail with
+// error number |err|. As a special case, Error(0) will result in the
+// system call appearing to have succeeded, but without having any
+// side effects.
+SANDBOX_EXPORT ResultExpr Error(int err);
+
+// Kill specifies a result to kill the 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.
+// The value of |aux| will be available to the tracer via PTRACE_GETEVENTMSG.
+SANDBOX_EXPORT ResultExpr Trace(uint16_t aux);
+
+// Trap specifies a result that the system call should be handled by
+// trapping back into userspace and invoking |trap_func|, passing
+// |aux| as the second parameter.
+SANDBOX_EXPORT ResultExpr
+ Trap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// UnsafeTrap is like Trap, except the policy is marked as "unsafe"
+// and allowed to use SandboxSyscall to invoke any system call.
+//
+// NOTE: This feature, by definition, disables all security features of
+// the sandbox. It should never be used in production, but it can be
+// very useful to diagnose code that is incompatible with the sandbox.
+// If even a single system call returns "UnsafeTrap", the security of
+// entire sandbox should be considered compromised.
+SANDBOX_EXPORT ResultExpr
+ UnsafeTrap(TrapRegistry::TrapFnc trap_func, const void* aux);
+
+// BoolConst converts a bool value into a BoolExpr.
+SANDBOX_EXPORT BoolExpr BoolConst(bool value);
+
+// Not returns a BoolExpr representing the logical negation of |cond|.
+SANDBOX_EXPORT BoolExpr Not(BoolExpr cond);
+
+// AllOf returns a BoolExpr representing the logical conjunction ("and")
+// of zero or more BoolExprs.
+SANDBOX_EXPORT BoolExpr AllOf();
+SANDBOX_EXPORT BoolExpr AllOf(BoolExpr lhs, BoolExpr rhs);
+template <typename... Rest>
+SANDBOX_EXPORT BoolExpr AllOf(BoolExpr first, Rest&&... rest);
+
+// AnyOf returns a BoolExpr representing the logical disjunction ("or")
+// of zero or more BoolExprs.
+SANDBOX_EXPORT BoolExpr AnyOf();
+SANDBOX_EXPORT BoolExpr AnyOf(BoolExpr lhs, BoolExpr rhs);
+template <typename... Rest>
+SANDBOX_EXPORT BoolExpr AnyOf(BoolExpr first, Rest&&... rest);
+
+template <typename T>
+class SANDBOX_EXPORT Arg {
+ public:
+ // Initializes the Arg to represent the |num|th system call
+ // argument (indexed from 0), which is of type |T|.
+ explicit Arg(int num);
+
+ Arg(const Arg& arg) : num_(arg.num_), mask_(arg.mask_) {}
+
+ // Returns an Arg representing the current argument, but after
+ // bitwise-and'ing it with |rhs|.
+ friend Arg operator&(const Arg& lhs, uint64_t rhs) {
+ return Arg(lhs.num_, lhs.mask_ & rhs);
+ }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) equals |rhs|.
+ friend BoolExpr operator==(const Arg& lhs, T rhs) { return lhs.EqualTo(rhs); }
+
+ // Returns a boolean expression comparing whether the system call argument
+ // (after applying any bitmasks, if appropriate) does not equal |rhs|.
+ friend BoolExpr operator!=(const Arg& lhs, T rhs) { return Not(lhs == rhs); }
+
+ private:
+ Arg(int num, uint64_t mask) : num_(num), mask_(mask) {}
+
+ BoolExpr EqualTo(T val) const;
+
+ int num_;
+ uint64_t mask_;
+
+ DISALLOW_ASSIGN(Arg);
+};
+
+// If begins a conditional result expression predicated on the
+// specified boolean expression.
+SANDBOX_EXPORT Elser If(BoolExpr cond, ResultExpr then_result);
+
+class SANDBOX_EXPORT Elser {
+ public:
+ Elser(const Elser& elser);
+ ~Elser();
+
+ // ElseIf extends the conditional result expression with another
+ // "if then" clause, predicated on the specified boolean expression.
+ Elser ElseIf(BoolExpr cond, ResultExpr then_result) const;
+
+ // Else terminates a conditional result expression using |else_result| as
+ // the default fallback result expression.
+ ResultExpr Else(ResultExpr else_result) const;
+
+ private:
+ using Clause = std::pair<BoolExpr, ResultExpr>;
+
+ explicit Elser(cons::List<Clause> clause_list);
+
+ cons::List<Clause> clause_list_;
+
+ friend Elser If(BoolExpr, ResultExpr);
+ template <typename T>
+ friend Caser<T> Switch(const Arg<T>&);
+ DISALLOW_ASSIGN(Elser);
+};
+
+// Switch begins a switch expression dispatched according to the
+// specified argument value.
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg);
+
+template <typename T>
+class SANDBOX_EXPORT Caser {
+ public:
+ Caser(const Caser<T>& caser) : arg_(caser.arg_), elser_(caser.elser_) {}
+ ~Caser() {}
+
+ // Case adds a single-value "case" clause to the switch.
+ Caser<T> Case(T value, ResultExpr result) const;
+
+ // Cases adds a multiple-value "case" clause to the switch.
+ // See also the SANDBOX_BPF_DSL_CASES macro below for a more idiomatic way
+ // of using this function.
+ template <typename... Values>
+ Caser<T> CasesImpl(ResultExpr result, const Values&... values) const;
+
+ // Terminate the switch with a "default" clause.
+ ResultExpr Default(ResultExpr result) const;
+
+ private:
+ Caser(const Arg<T>& arg, Elser elser) : arg_(arg), elser_(elser) {}
+
+ Arg<T> arg_;
+ Elser elser_;
+
+ template <typename U>
+ friend Caser<U> Switch(const Arg<U>&);
+ DISALLOW_ASSIGN(Caser);
+};
+
+// Recommended usage is to put
+// #define CASES SANDBOX_BPF_DSL_CASES
+// near the top of the .cc file (e.g., nearby any "using" statements), then
+// use like:
+// Switch(arg).CASES((3, 5, 7), result)...;
+#define SANDBOX_BPF_DSL_CASES(values, result) \
+ CasesImpl(result, SANDBOX_BPF_DSL_CASES_HELPER values)
+
+// Helper macro to strip parentheses.
+#define SANDBOX_BPF_DSL_CASES_HELPER(...) __VA_ARGS__
+
+// =====================================================================
+// Official API ends here.
+// =====================================================================
+
+namespace internal {
+
+// Make argument-dependent lookup work. This is necessary because although
+// BoolExpr is defined in bpf_dsl, since it's merely a typedef for
+// scoped_refptr<const internal::BoolExplImpl>, argument-dependent lookup only
+// searches the "internal" nested namespace.
+using bpf_dsl::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
+// according to |mask|. Users should use the Arg template class below
+// instead of using this API directly.
+SANDBOX_EXPORT BoolExpr
+ ArgEq(int num, size_t size, uint64_t mask, uint64_t val);
+
+// Returns the default mask for a system call argument of the specified size.
+SANDBOX_EXPORT uint64_t DefaultMask(size_t size);
+
+} // namespace internal
+
+template <typename T>
+Arg<T>::Arg(int num)
+ : num_(num), mask_(internal::DefaultMask(sizeof(T))) {
+}
+
+// Definition requires ArgEq to have been declared. Moved out-of-line
+// to minimize how much internal clutter users have to ignore while
+// reading the header documentation.
+//
+// Additionally, we use this helper member function to avoid linker errors
+// caused by defining operator== out-of-line. For a more detailed explanation,
+// see http://www.parashift.com/c++-faq-lite/template-friends.html.
+template <typename T>
+BoolExpr Arg<T>::EqualTo(T val) const {
+ if (sizeof(T) == 4) {
+ // Prevent sign-extension of negative int32_t values.
+ return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint32_t>(val));
+ }
+ return internal::ArgEq(num_, sizeof(T), mask_, static_cast<uint64_t>(val));
+}
+
+template <typename T>
+SANDBOX_EXPORT Caser<T> Switch(const Arg<T>& arg) {
+ return Caser<T>(arg, Elser(nullptr));
+}
+
+template <typename T>
+Caser<T> Caser<T>::Case(T value, ResultExpr result) const {
+ return SANDBOX_BPF_DSL_CASES((value), std::move(result));
+}
+
+template <typename T>
+template <typename... Values>
+Caser<T> Caser<T>::CasesImpl(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.
+
+ return Caser<T>(arg_,
+ elser_.ElseIf(AnyOf((arg_ == values)...), std::move(result)));
+}
+
+template <typename T>
+ResultExpr Caser<T>::Default(ResultExpr result) const {
+ return elser_.Else(std::move(result));
+}
+
+template <typename... Rest>
+BoolExpr AllOf(BoolExpr first, Rest&&... rest) {
+ return AllOf(std::move(first), AllOf(std::forward<Rest>(rest)...));
+}
+
+template <typename... Rest>
+BoolExpr AnyOf(BoolExpr first, Rest&&... rest) {
+ return AnyOf(std::move(first), AnyOf(std::forward<Rest>(rest)...));
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_H_
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_forward.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
new file mode 100644
index 0000000000..af1b48b407
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_forward.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_
+
+#include <memory>
+
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// The bpf_dsl_forward.h header provides forward declarations for the
+// types defined in bpf_dsl.h. It's intended for use in user headers
+// that need to reference bpf_dsl types, but don't require definitions.
+
+namespace internal {
+class ResultExprImpl;
+class BoolExprImpl;
+}
+
+using ResultExpr = std::shared_ptr<const internal::ResultExprImpl>;
+using BoolExpr = std::shared_ptr<const internal::BoolExprImpl>;
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+namespace std {
+extern template class SANDBOX_EXPORT
+ shared_ptr<const sandbox::bpf_dsl::internal::BoolExprImpl>;
+extern template class SANDBOX_EXPORT
+ shared_ptr<const sandbox::bpf_dsl::internal::ResultExprImpl>;
+} // namespace std
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_FORWARD_H_
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
new file mode 100644
index 0000000000..f397321edd
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/bpf_dsl_impl.h
@@ -0,0 +1,67 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
+#define SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class PolicyCompiler;
+
+namespace internal {
+
+// Internal interface implemented by BoolExpr implementations.
+class BoolExprImpl {
+ public:
+ // 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 CodeGen::Node Compile(PolicyCompiler* pc,
+ CodeGen::Node then_node,
+ CodeGen::Node else_node) const = 0;
+
+ protected:
+ BoolExprImpl() {}
+ virtual ~BoolExprImpl() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BoolExprImpl);
+};
+
+// Internal interface implemented by ResultExpr implementations.
+class ResultExprImpl {
+ public:
+ // 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() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResultExprImpl);
+};
+
+} // namespace internal
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_BPF_DSL_IMPL_H_
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 0000000000..d88bd531a2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.cc
@@ -0,0 +1,147 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include <limits>
+#include <utility>
+
+#include "base/logging.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+
+// This CodeGen implementation strives for simplicity while still
+// generating acceptable BPF programs under typical usage patterns
+// (e.g., by PolicyCompiler).
+//
+// The key to its simplicity is that BPF programs only support forward
+// jumps/branches, which allows constraining the DAG construction API
+// to make instruction nodes immutable. Immutable nodes admits a
+// simple greedy approach of emitting new instructions as needed and
+// then reusing existing ones that have already been emitted. This
+// cleanly avoids any need to compute basic blocks or apply
+// topological sorting because the API effectively sorts instructions
+// for us (e.g., before MakeInstruction() can be called to emit a
+// branch instruction, it must have already been called for each
+// branch path).
+//
+// This greedy algorithm is not without (theoretical) weakness though:
+//
+// 1. In the general case, we don't eliminate dead code. If needed,
+// we could trace back through the program in Compile() and elide
+// any unneeded instructions, but in practice we only emit live
+// instructions anyway.
+//
+// 2. By not dividing instructions into basic blocks and sorting, we
+// lose an opportunity to move non-branch/non-return instructions
+// adjacent to their successor instructions, which means we might
+// need to emit additional jumps. But in practice, they'll
+// already be nearby as long as callers don't go out of their way
+// to interleave MakeInstruction() calls for unrelated code
+// sequences.
+
+namespace sandbox {
+
+// kBranchRange is the maximum value that can be stored in
+// sock_filter's 8-bit jt and jf fields.
+const size_t kBranchRange = std::numeric_limits<uint8_t>::max();
+
+const CodeGen::Node CodeGen::kNullNode;
+
+CodeGen::CodeGen() : program_(), equivalent_(), memos_() {
+}
+
+CodeGen::~CodeGen() {
+}
+
+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<size_t>(BPF_MAXINSNS));
+ CHECK_EQ(program_.size(), equivalent_.size());
+
+ Node res = program_.size();
+ program_.push_back(sock_filter{
+ code, static_cast<uint8_t>(jt), static_cast<uint8_t>(jf), k});
+ equivalent_.push_back(res);
+ return res;
+}
+
+size_t CodeGen::Offset(Node target) const {
+ CHECK_LT(target, program_.size()) << "Bogus offset target node";
+ return (program_.size() - 1) - target;
+}
+
+} // 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 0000000000..3fc3f35a0d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/codegen.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
+#define SANDBOX_LINUX_BPF_DSL_CODEGEN_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <tuple>
+#include <vector>
+
+#include "base/macros.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<unsigned short>(program.size()), &program[0] };
+// prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+//
+class SANDBOX_EXPORT CodeGen {
+ public:
+ // A vector of BPF instructions that need to be installed as a filter
+ // program in the kernel.
+ typedef std::vector<struct sock_filter> Program;
+
+ // Node represents a node within the instruction DAG being compiled.
+ using Node = Program::size_type;
+
+ // kNullNode represents the "null" node; i.e., the reserved node
+ // value guaranteed to not equal any actual nodes.
+ static const Node kNullNode = -1;
+
+ CodeGen();
+ ~CodeGen();
+
+ // MakeInstruction creates a node representing the specified
+ // instruction, or returns and existing equivalent node if one
+ // exists. For details on the possible parameters refer to
+ // https://www.kernel.org/doc/Documentation/networking/filter.txt.
+ // TODO(mdempsky): Reconsider using default arguments here.
+ Node MakeInstruction(uint16_t code,
+ uint32_t k,
+ Node jt = kNullNode,
+ Node jf = kNullNode);
+
+ // Compile linearizes the instruction DAG rooted at |head| into a
+ // program that can be executed by a BPF virtual machine.
+ Program Compile(Node head);
+
+ private:
+ using MemoKey = std::tuple<uint16_t, uint32_t, Node, Node>;
+
+ // AppendInstruction adds a new instruction, ensuring that |jt| and
+ // |jf| are within range as necessary for |code|.
+ Node AppendInstruction(uint16_t code, uint32_t k, Node jt, Node jf);
+
+ // WithinRange returns a node equivalent to |next| that is at most
+ // |range| instructions away from the (logical) beginning of the
+ // program.
+ Node WithinRange(Node next, size_t range);
+
+ // Append appends a new instruction to the physical end (i.e.,
+ // logical beginning) of |program_|.
+ Node Append(uint16_t code, uint32_t k, size_t jt, size_t jf);
+
+ // Offset returns how many instructions exist in |program_| after |target|.
+ size_t Offset(Node target) const;
+
+ // NOTE: program_ is the compiled program in *reverse*, so that
+ // indices remain stable as we add instructions.
+ Program program_;
+
+ // equivalent_ stores the most recent semantically-equivalent node for each
+ // instruction in program_. A node is defined as semantically-equivalent to N
+ // if it has the same instruction code and constant as N and its successor
+ // nodes (if any) are semantically-equivalent to N's successor nodes, or
+ // if it's an unconditional jump to a node semantically-equivalent to N.
+ std::vector<Node> equivalent_;
+
+ std::map<MemoKey, Node> 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
new file mode 100644
index 0000000000..07ac3dfc23
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/cons.h
@@ -0,0 +1,137 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_CONS_H_
+#define SANDBOX_LINUX_BPF_DSL_CONS_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace cons {
+
+// Namespace cons provides an abstraction for immutable "cons list"
+// data structures as commonly provided in functional programming
+// languages like Lisp or Haskell.
+//
+// A cons list is a linked list consisting of "cells", each of which
+// have a "head" and a "tail" element. A cell's head element contains
+// a user specified value, while the tail element contains a (possibly
+// null) pointer to another cell.
+//
+// An empty list (idiomatically referred to as "nil") can be
+// constructed as "cons::List<Foo>()" or simply as "nullptr" if Foo
+// can be inferred from context (e.g., calling a function that has a
+// "cons::List<Foo>" parameter).
+//
+// Existing lists (including empty lists) can be extended by
+// prepending new values to the front using the "Cons(head, tail)"
+// function, which will allocate a new cons cell. Notably, cons lists
+// support creating multiple lists that share a common tail sequence.
+//
+// Lastly, lists support iteration via C++11's range-based for loop
+// construct.
+//
+// Examples:
+//
+// // basic construction
+// const cons::List<char> kNil = nullptr;
+// cons::List<char> ba = Cons('b', Cons('a', kNil));
+//
+// // common tail sequence
+// cons::List<char> cba = Cons('c', ba);
+// cons::List<char> dba = Cons('d', ba);
+//
+// // iteration
+// for (const char& ch : cba) {
+// // iterates 'c', 'b', 'a'
+// }
+// for (const char& ch : dba) {
+// // iterates 'd', 'b', 'a'
+// }
+
+// Forward declarations.
+template <typename T>
+class Cell;
+template <typename T>
+class ListIterator;
+
+// List represents a (possibly null) pointer to a cons cell.
+template <typename T>
+using List = std::shared_ptr<const Cell<T>>;
+
+// Cons extends a cons list by prepending a new value to the front.
+template <typename T>
+List<T> Cons(const T& head, List<T> tail) {
+ return std::make_shared<Cell<T>>(head, std::move(tail));
+}
+
+// Cell represents an individual "cons cell" within a cons list.
+template <typename T>
+class Cell {
+ public:
+ Cell(const T& head, List<T> tail) : head_(head), tail_(std::move(tail)) {}
+
+ // Head returns this cell's head element.
+ const T& head() const { return head_; }
+
+ // Tail returns this cell's tail element.
+ const List<T>& tail() const { return tail_; }
+
+ private:
+ T head_;
+ List<T> tail_;
+
+ DISALLOW_COPY_AND_ASSIGN(Cell);
+};
+
+// Begin returns a list iterator pointing to the first element of the
+// cons list. It's provided to support range-based for loops.
+template <typename T>
+ListIterator<T> begin(const List<T>& list) {
+ return ListIterator<T>(list);
+}
+
+// End returns a list iterator pointing to the "past-the-end" element
+// of the cons list (i.e., nil). It's provided to support range-based
+// for loops.
+template <typename T>
+ListIterator<T> end(const List<T>& list) {
+ return ListIterator<T>();
+}
+
+// ListIterator provides C++ forward iterator semantics for traversing
+// a cons list.
+template <typename T>
+class ListIterator {
+ public:
+ ListIterator() : list_() {}
+ explicit ListIterator(const List<T>& list) : list_(list) {}
+
+ const T& operator*() const { return list_->head(); }
+
+ ListIterator& operator++() {
+ list_ = list_->tail();
+ return *this;
+ }
+
+ friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) {
+ return lhs.list_ == rhs.list_;
+ }
+
+ private:
+ List<T> list_;
+};
+
+template <typename T>
+bool operator!=(const ListIterator<T>& lhs, const ListIterator<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace cons
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_CONS_H_
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc
new file mode 100644
index 0000000000..2edf592f68
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.cc
@@ -0,0 +1,159 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/dump_bpf.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <string>
+
+#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/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+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 "???";
+ }
+}
+
+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
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h
new file mode 100644
index 0000000000..34f1aa99f2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/dump_bpf.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_DUMP_BPF_H_
+#define SANDBOX_LINUX_BPF_DSL_DUMP_BPF_H_
+
+#include <string>
+
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+class SANDBOX_EXPORT DumpBPF {
+ public:
+ // PrintProgram writes |program| in a human-readable format to stderr.
+ static void PrintProgram(const CodeGen::Program& program);
+
+ // StringPrintProgram writes |program| in a human-readable format to
+ // a std::string.
+ static std::string StringPrintProgram(const CodeGen::Program& program);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_DUMP_BPF_H_
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 0000000000..611c27dd80
--- /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 0000000000..313511f22e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/linux_syscall_ranges.h
@@ -0,0 +1,63 @@
+// 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_
+
+#include "build/build_config.h"
+
+#if defined(__x86_64__)
+
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#elif defined(__i386__)
+
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL 1024u
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+
+// ARM EABI includes "ARM private" system calls starting at |__ARM_NR_BASE|,
+// and a "ghost syscall private to the kernel", cmpxchg,
+// at |__ARM_NR_BASE+0x00fff0|.
+// See </arch/arm/include/asm/unistd.h> in the Linux kernel.
+
+// __NR_SYSCALL_BASE is 0 in thumb and ARM EABI.
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + 1024u)
+// __ARM_NR_BASE is __NR_SYSCALL_BASE + 0xf0000u
+#define MIN_PRIVATE_SYSCALL 0xf0000u
+#define MAX_PRIVATE_SYSCALL (MIN_PRIVATE_SYSCALL + 16u)
+#define MIN_GHOST_SYSCALL (MIN_PRIVATE_SYSCALL + 0xfff0u)
+#define MAX_SYSCALL (MIN_GHOST_SYSCALL + 4u)
+
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)
+
+#include <asm/unistd.h> // for __NR_O32_Linux and __NR_Linux_syscalls
+#define MIN_SYSCALL __NR_O32_Linux
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_Linux_syscalls)
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)
+
+#include <asm/unistd.h> // for __NR_64_Linux and __NR_64_Linux_syscalls
+#define MIN_SYSCALL __NR_64_Linux
+#define MAX_PUBLIC_SYSCALL (MIN_SYSCALL + __NR_64_Linux_syscalls)
+#define MAX_SYSCALL MAX_PUBLIC_SYSCALL
+
+#elif defined(__aarch64__)
+
+#include <asm-generic/unistd.h>
+#define MIN_SYSCALL 0u
+#define MAX_PUBLIC_SYSCALL __NR_syscalls
+#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.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.cc
new file mode 100644
index 0000000000..c20edc6da8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.cc
@@ -0,0 +1,19 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/policy.h"
+
+#include <errno.h>
+
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+ResultExpr Policy::InvalidSyscall() const {
+ return Error(ENOSYS);
+}
+
+} // namespace bpf_dsl
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.h
new file mode 100644
index 0000000000..6c67589456
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy.h
@@ -0,0 +1,37 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_H_
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl_forward.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+// Interface to implement to define a BPF sandbox policy.
+class SANDBOX_EXPORT Policy {
+ public:
+ Policy() {}
+ virtual ~Policy() {}
+
+ // User extension point for writing custom sandbox policies.
+ // The returned ResultExpr will control how the kernel responds to the
+ // specified system call number.
+ virtual ResultExpr EvaluateSyscall(int sysno) const = 0;
+
+ // Optional overload for specifying alternate behavior for invalid
+ // system calls. The default is to return ENOSYS.
+ virtual ResultExpr InvalidSyscall() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Policy);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc
new file mode 100644
index 0000000000..b909fc37f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.cc
@@ -0,0 +1,481 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/syscall.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/stl_util.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/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+
+namespace {
+
+#if defined(__i386__) || defined(__x86_64__)
+const bool kIsIntel = true;
+#else
+const bool kIsIntel = false;
+#endif
+#if defined(__x86_64__) && defined(__ILP32__)
+const bool kIsX32 = true;
+#else
+const bool kIsX32 = false;
+#endif
+
+const int kSyscallsRequiredForUnsafeTraps[] = {
+ __NR_rt_sigprocmask,
+ __NR_rt_sigreturn,
+#if defined(__NR_sigprocmask)
+ __NR_sigprocmask,
+#endif
+#if defined(__NR_sigreturn)
+ __NR_sigreturn,
+#endif
+};
+
+bool HasExactlyOneBit(uint64_t x) {
+ // Common trick; e.g., see http://stackoverflow.com/a/108329.
+ return x != 0 && (x & (x - 1)) == 0;
+}
+
+ResultExpr DefaultPanic(const char* error) {
+ return Kill();
+}
+
+// A Trap() handler that returns an "errno" value. The value is encoded
+// in the "aux" parameter.
+intptr_t ReturnErrno(const struct arch_seccomp_data&, void* aux) {
+ // TrapFnc functions report error by following the native kernel convention
+ // of returning an exit code in the range of -1..-4096. They do not try to
+ // set errno themselves. The glibc wrapper that triggered the SIGSYS will
+ // ultimately do so for us.
+ int err = reinterpret_cast<intptr_t>(aux) & SECCOMP_RET_DATA;
+ return -err;
+}
+
+bool HasUnsafeTraps(const Policy* policy) {
+ DCHECK(policy);
+ for (uint32_t sysnum : SyscallSet::ValidOnly()) {
+ if (policy->EvaluateSyscall(sysnum)->HasUnsafeTraps()) {
+ return true;
+ }
+ }
+ return policy->InvalidSyscall()->HasUnsafeTraps();
+}
+
+} // namespace
+
+struct PolicyCompiler::Range {
+ uint32_t from;
+ CodeGen::Node node;
+};
+
+PolicyCompiler::PolicyCompiler(const Policy* policy, TrapRegistry* registry)
+ : policy_(policy),
+ registry_(registry),
+ escapepc_(0),
+ panic_func_(DefaultPanic),
+ gen_(),
+ has_unsafe_traps_(HasUnsafeTraps(policy_)) {
+ DCHECK(policy);
+}
+
+PolicyCompiler::~PolicyCompiler() {
+}
+
+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_) {
+ CHECK_NE(0U, escapepc_) << "UnsafeTrap() requires a valid escape PC";
+
+ for (int sysnum : kSyscallsRequiredForUnsafeTraps) {
+ CHECK(policy_->EvaluateSyscall(sysnum)->IsAllow())
+ << "Policies that use UnsafeTrap() must unconditionally allow all "
+ "required system calls";
+ }
+
+ CHECK(registry_->EnableUnsafeTraps())
+ << "We'd rather die than enable unsafe traps";
+ }
+
+ // Assemble the BPF filter program.
+ return gen_.Compile(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
+ // invoked by Syscall::Call, and then allow it unconditionally.
+ // 3. Check the system call number and jump to the appropriate compiled
+ // system call policy number.
+ return CheckArch(MaybeAddEscapeHatch(DispatchSyscall()));
+}
+
+CodeGen::Node PolicyCompiler::CheckArch(CodeGen::Node passed) {
+ // If the architecture doesn't match SECCOMP_ARCH, disallow the
+ // system call.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_ARCH_IDX,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_ARCH, passed,
+ CompileResult(panic_func_(
+ "Invalid audit architecture in BPF filter"))));
+}
+
+CodeGen::Node PolicyCompiler::MaybeAddEscapeHatch(CodeGen::Node rest) {
+ // If no unsafe traps, then simply return |rest|.
+ if (!has_unsafe_traps_) {
+ return rest;
+ }
+
+ // We already enabled unsafe traps in Compile, but enable them again to give
+ // the trap registry a second chance to complain before we add the backdoor.
+ CHECK(registry_->EnableUnsafeTraps());
+
+ // Allow system calls, if they originate from our magic return address.
+ const uint32_t lopc = static_cast<uint32_t>(escapepc_);
+ const uint32_t hipc = static_cast<uint32_t>(escapepc_ >> 32);
+
+ // BPF cannot do native 64-bit comparisons, so we have to compare
+ // both 32-bit halves of the instruction pointer. If they match what
+ // we expect, we return ERR_ALLOWED. If either or both don't match,
+ // we continue evalutating the rest of the sandbox policy.
+ //
+ // For simplicity, we check the full 64-bit instruction pointer even
+ // on 32-bit architectures.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_LSB_IDX,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, lopc,
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_IP_MSB_IDX,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, hipc,
+ CompileResult(Allow()), rest)),
+ rest));
+}
+
+CodeGen::Node PolicyCompiler::DispatchSyscall() {
+ // Evaluate all possible system calls and group their Nodes into
+ // ranges of identical codes.
+ Ranges ranges;
+ FindRanges(&ranges);
+
+ // Compile the system call ranges to an optimized BPF jumptable
+ CodeGen::Node jumptable = AssembleJumpTable(ranges.begin(), ranges.end());
+
+ // Grab the system call number, so that we can check it and then
+ // execute the jump table.
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS, SECCOMP_NR_IDX, CheckSyscallNumber(jumptable));
+}
+
+CodeGen::Node PolicyCompiler::CheckSyscallNumber(CodeGen::Node passed) {
+ if (kIsIntel) {
+ // On Intel architectures, verify that system call numbers are in the
+ // expected number range.
+ CodeGen::Node invalidX32 =
+ CompileResult(panic_func_("Illegal mixing of system call ABIs"));
+ if (kIsX32) {
+ // The newer x32 API always sets bit 30.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, passed, invalidX32);
+ } else {
+ // The older i386 and x86-64 APIs clear bit 30 on all system calls.
+ return gen_.MakeInstruction(
+ BPF_JMP + BPF_JSET + BPF_K, 0x40000000, invalidX32, passed);
+ }
+ }
+
+ // TODO(mdempsky): Similar validation for other architectures?
+ return passed;
+}
+
+void PolicyCompiler::FindRanges(Ranges* ranges) {
+ // Please note that "struct seccomp_data" defines system calls as a signed
+ // int32_t, but BPF instructions always operate on unsigned quantities. We
+ // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL,
+ // and then verifying that the rest of the number range (both positive and
+ // negative) all return the same Node.
+ const CodeGen::Node invalid_node = CompileResult(policy_->InvalidSyscall());
+ uint32_t old_sysnum = 0;
+ CodeGen::Node old_node =
+ SyscallSet::IsValid(old_sysnum)
+ ? CompileResult(policy_->EvaluateSyscall(old_sysnum))
+ : invalid_node;
+
+ for (uint32_t sysnum : SyscallSet::All()) {
+ CodeGen::Node node =
+ SyscallSet::IsValid(sysnum)
+ ? CompileResult(policy_->EvaluateSyscall(static_cast<int>(sysnum)))
+ : invalid_node;
+ // N.B., here we rely on CodeGen folding (i.e., returning the same
+ // node value for) identical code sequences, otherwise our jump
+ // table will blow up in size.
+ if (node != old_node) {
+ ranges->push_back(Range{old_sysnum, old_node});
+ old_sysnum = sysnum;
+ old_node = node;
+ }
+ }
+ ranges->push_back(Range{old_sysnum, old_node});
+}
+
+CodeGen::Node PolicyCompiler::AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop) {
+ // We convert the list of system call ranges into jump table that performs
+ // a binary search over the ranges.
+ // As a sanity check, we need to have at least one distinct ranges for us
+ // to be able to build a jump table.
+ CHECK(start < stop) << "Invalid iterator range";
+ const auto n = stop - start;
+ if (n == 1) {
+ // If we have narrowed things down to a single range object, we can
+ // return from the BPF filter program.
+ return start->node;
+ }
+
+ // Pick the range object that is located at the mid point of our list.
+ // We compare our system call number against the lowest valid system call
+ // number in this range object. If our number is lower, it is outside of
+ // this range object. If it is greater or equal, it might be inside.
+ Ranges::const_iterator mid = start + n / 2;
+
+ // Sub-divide the list of ranges and continue recursively.
+ CodeGen::Node jf = AssembleJumpTable(start, mid);
+ CodeGen::Node jt = AssembleJumpTable(mid, stop);
+ return gen_.MakeInstruction(BPF_JMP + BPF_JGE + BPF_K, mid->from, jt, jf);
+}
+
+CodeGen::Node PolicyCompiler::CompileResult(const ResultExpr& res) {
+ return res->Compile(this);
+}
+
+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 (width == 4) {
+ CHECK_EQ(0U, mask >> 32) << "Mask exceeds argument size";
+ CHECK_EQ(0U, value >> 32) << "Value exceeds argument size";
+ }
+
+ // 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 MaskedEqualHalf(argno, width, mask, value, ArgHalf::UPPER,
+ MaskedEqualHalf(argno, width, mask, value,
+ ArgHalf::LOWER, passed, failed),
+ failed);
+}
+
+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.
+
+ CodeGen::Node invalid_64bit = Unexpected64bitArgument(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:
+ // LDW [upper]
+ // JEQ 0, passed, invalid
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, 0, passed, invalid_64bit));
+ }
+
+ // On 64-bit platforms, the ABI (at least on x86_64) allows any value
+ // for the upper half, but to avoid potential vulnerabilties if an
+ // argument is incorrectly tested as a 32-bit type, we require it to be
+ // either zero-extended or sign-extended. That is, the upper 32-bits
+ // may be 0 or ~0; but we only allow ~0 if the sign bit of the lower
+ // 32-bits is set too:
+ //
+ // LDW [upper]
+ // JEQ 0, passed, (next)
+ // JEQ ~0, (next), invalid
+ // LDW [lower]
+ // JSET (1<<31), passed, invalid
+ //
+ // TODO(mdempsky): The JSET instruction could perhaps jump to passed->next
+ // instead, as the first instruction of passed should be "LDW [lower]".
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ upper,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ 0,
+ passed,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K,
+ std::numeric_limits<uint32_t>::max(),
+ gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ lower,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K,
+ 1U << 31,
+ passed,
+ invalid_64bit)),
+ invalid_64bit)));
+ }
+
+ const uint32_t idx = (half == 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.
+
+ // For (arg & 0) == 0, just return passed.
+ if (mask == 0) {
+ CHECK_EQ(0U, value);
+ return passed;
+ }
+
+ // For (arg & ~0) == value, emit:
+ // LDW [idx]
+ // JEQ value, passed, failed
+ if (mask == std::numeric_limits<uint32_t>::max()) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed));
+ }
+
+ // For (arg & mask) == 0, emit:
+ // LDW [idx]
+ // JSET mask, failed, passed
+ // (Note: failed and passed are intentionally swapped.)
+ if (value == 0) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, failed, passed));
+ }
+
+ // For (arg & x) == x where x is a single-bit value, emit:
+ // LDW [idx]
+ // JSET mask, passed, failed
+ if (mask == value && HasExactlyOneBit(mask)) {
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(BPF_JMP + BPF_JSET + BPF_K, mask, passed, failed));
+ }
+
+ // Generic fallback:
+ // LDW [idx]
+ // AND mask
+ // JEQ value, passed, failed
+ return gen_.MakeInstruction(
+ BPF_LD + BPF_W + BPF_ABS,
+ idx,
+ gen_.MakeInstruction(
+ BPF_ALU + BPF_AND + BPF_K,
+ mask,
+ gen_.MakeInstruction(
+ BPF_JMP + BPF_JEQ + BPF_K, value, passed, failed)));
+}
+
+CodeGen::Node PolicyCompiler::Unexpected64bitArgument(int argno) {
+ // This situation is unlikely, but possible. Return to userspace,
+ // zero-extend the problematic argument, and re-issue the syscall.
+ return CompileResult(bpf_dsl::Trap(
+ [](const arch_seccomp_data& args_ref, void* aux) -> intptr_t {
+ arch_seccomp_data args = args_ref;
+ int argno = (int)(intptr_t)aux;
+ args.args[argno] &= 0xFFFFFFFF;
+ return Syscall::Call(args.nr, args.args[0], args.args[1], args.args[2],
+ args.args[3], args.args[4], args.args[5]);
+ },
+ (void*)(intptr_t)argno));
+}
+
+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
+ // any state other than the syscall arguments.
+ // But if we redirect all error handlers to user-space, then we can easily
+ // make this decision.
+ // The performance penalty for this extra round-trip to user-space is not
+ // actually that bad, as we only ever pay it for denied system calls; and a
+ // typical program has very few of these.
+ return Trap(ReturnErrno, reinterpret_cast<void*>(ret & SECCOMP_RET_DATA),
+ true);
+ }
+
+ return gen_.MakeInstruction(BPF_RET + BPF_K, ret);
+}
+
+CodeGen::Node PolicyCompiler::Trap(TrapRegistry::TrapFnc fnc,
+ const void* aux,
+ bool safe) {
+ uint16_t trap_id = registry_->Add(fnc, aux, safe);
+ return gen_.MakeInstruction(BPF_RET + BPF_K, SECCOMP_RET_TRAP + trap_id);
+}
+
+bool PolicyCompiler::IsRequiredForUnsafeTrap(int sysno) {
+ for (size_t i = 0; i < base::size(kSyscallsRequiredForUnsafeTraps); ++i) {
+ if (sysno == kSyscallsRequiredForUnsafeTraps[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // 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
new file mode 100644
index 0000000000..2acf878474
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/policy_compiler.h
@@ -0,0 +1,155 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+#define SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.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 {
+namespace bpf_dsl {
+class Policy;
+
+// PolicyCompiler implements the bpf_dsl compiler, allowing users to
+// transform bpf_dsl policies into BPF programs to be executed by the
+// Linux kernel.
+class SANDBOX_EXPORT PolicyCompiler {
+ public:
+ 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.
+ CodeGen::Program Compile();
+
+ // DangerousSetEscapePC sets the "escape PC" that is allowed to issue any
+ // system calls, regardless of policy.
+ void DangerousSetEscapePC(uint64_t escapepc);
+
+ // 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);
+
+ // 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 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 panics).
+ CodeGen::Node MaskedEqual(int argno,
+ size_t width,
+ uint64_t mask,
+ uint64_t value,
+ CodeGen::Node passed,
+ CodeGen::Node failed);
+
+ private:
+ struct Range;
+ typedef std::vector<Range> Ranges;
+
+ // Used by MaskedEqualHalf to track which half of the argument it's
+ // emitting instructions for.
+ enum class ArgHalf {
+ LOWER,
+ UPPER,
+ };
+
+ // Compile the configured policy into a complete instruction sequence.
+ CodeGen::Node AssemblePolicy();
+
+ // Return an instruction sequence that checks the
+ // arch_seccomp_data's "arch" field is valid, and then passes
+ // control to |passed| if so.
+ CodeGen::Node CheckArch(CodeGen::Node passed);
+
+ // If |has_unsafe_traps_| is true, returns an instruction sequence
+ // that allows all system calls from |escapepc_|, and otherwise
+ // passes control to |rest|. Otherwise, simply returns |rest|.
+ CodeGen::Node MaybeAddEscapeHatch(CodeGen::Node rest);
+
+ // Return an instruction sequence that loads and checks the system
+ // call number, performs a binary search, and then dispatches to an
+ // appropriate instruction sequence compiled from the current
+ // policy.
+ CodeGen::Node DispatchSyscall();
+
+ // Return an instruction sequence that checks the system call number
+ // (expected to be loaded in register A) and if valid, passes
+ // control to |passed| (with register A still valid).
+ CodeGen::Node CheckSyscallNumber(CodeGen::Node passed);
+
+ // Finds all the ranges of system calls that need to be handled. Ranges are
+ // sorted in ascending order of system call numbers. There are no gaps in the
+ // ranges. System calls with identical 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.
+ CodeGen::Node AssembleJumpTable(Ranges::const_iterator start,
+ Ranges::const_iterator stop);
+
+ // CompileResult compiles an individual result expression into a
+ // CodeGen node.
+ CodeGen::Node CompileResult(const ResultExpr& res);
+
+ // Returns a BPF program that evaluates half of a conditional expression;
+ // it should only ever be called from CondExpression().
+ CodeGen::Node MaskedEqualHalf(int argno,
+ size_t width,
+ uint64_t full_mask,
+ uint64_t full_value,
+ ArgHalf half,
+ CodeGen::Node passed,
+ CodeGen::Node failed);
+
+ // Returns the CodeGen::Node that is used to handle the case where a
+ // system call argument was expected to be a 32-bit type, but the
+ // value in the 64-bit register doesn't correspond to a
+ // zero-extended or sign-extended 32-bit value.
+ CodeGen::Node Unexpected64bitArgument(int argno);
+
+ const Policy* policy_;
+ TrapRegistry* registry_;
+ uint64_t escapepc_;
+ PanicFunc panic_func_;
+
+ CodeGen gen_;
+ bool has_unsafe_traps_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyCompiler);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_POLICY_COMPILER_H_
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h
new file mode 100644
index 0000000000..1a407b9523
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/seccomp_macros.h
@@ -0,0 +1,354 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_
+#define SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_
+
+#include <sys/types.h> // 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__)
+#if !defined(__native_client_nonsfi__)
+#include <sys/user.h>
+#endif
+#if defined(__mips__)
+// sys/user.h in eglibc misses size_t definition
+#include <stddef.h>
+#endif
+#endif
+
+#include "build/build_config.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h" // For AUDIT_ARCH_*
+
+// Impose some reasonable maximum BPF program size. Realistically, the
+// kernel probably has much lower limits. But by limiting to less than
+// 30 bits, we can ease requirements on some of our data types.
+#define SECCOMP_MAX_PROGRAM_SIZE (1<<30)
+
+#if defined(__i386__)
+#define SECCOMP_ARCH AUDIT_ARCH_I386
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_EAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_EIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_EBX)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_ECX)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_EDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_ESI)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_EDI)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_EBP)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+
+#if defined(__BIONIC__) || defined(__native_client_nonsfi__)
+// Old Bionic versions and PNaCl toolchain don't have sys/user.h, so we just
+// define regs_struct directly. This can be removed once we no longer need to
+// support these old Bionic versions and PNaCl toolchain.
+struct regs_struct {
+ long int ebx;
+ long int ecx;
+ long int edx;
+ long int esi;
+ long int edi;
+ long int ebp;
+ long int eax;
+ long int xds;
+ long int xes;
+ long int xfs;
+ long int xgs;
+ long int orig_eax;
+ long int eip;
+ long int xcs;
+ long int eflags;
+ long int esp;
+ long int xss;
+};
+#else
+typedef user_regs_struct regs_struct;
+#endif
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).eax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_eax
+#define SECCOMP_PT_IP(_regs) (_regs).eip
+#define SECCOMP_PT_PARM1(_regs) (_regs).ebx
+#define SECCOMP_PT_PARM2(_regs) (_regs).ecx
+#define SECCOMP_PT_PARM3(_regs) (_regs).edx
+#define SECCOMP_PT_PARM4(_regs) (_regs).esi
+#define SECCOMP_PT_PARM5(_regs) (_regs).edi
+#define SECCOMP_PT_PARM6(_regs) (_regs).ebp
+
+#elif defined(__x86_64__)
+#define SECCOMP_ARCH AUDIT_ARCH_X86_64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[(_reg)])
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, REG_RAX)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, REG_RIP)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, REG_RDI)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, REG_RSI)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, REG_RDX)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, REG_R10)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, REG_R8)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, REG_R9)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+typedef user_regs_struct regs_struct;
+#define SECCOMP_PT_RESULT(_regs) (_regs).rax
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).orig_rax
+#define SECCOMP_PT_IP(_regs) (_regs).rip
+#define SECCOMP_PT_PARM1(_regs) (_regs).rdi
+#define SECCOMP_PT_PARM2(_regs) (_regs).rsi
+#define SECCOMP_PT_PARM3(_regs) (_regs).rdx
+#define SECCOMP_PT_PARM4(_regs) (_regs).r10
+#define SECCOMP_PT_PARM5(_regs) (_regs).r8
+#define SECCOMP_PT_PARM6(_regs) (_regs).r9
+
+#elif defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))
+#define SECCOMP_ARCH AUDIT_ARCH_ARM
+
+// ARM sigcontext_t is different from i386/x86_64.
+// See </arch/arm/include/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.arm_##_reg)
+// ARM EABI syscall convention.
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, r7)
+#define SECCOMP_IP(_ctx) SECCOMP_REG(_ctx, pc)
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, r0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, r1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, r2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, r3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, r4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, r5)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+#if defined(__BIONIC__) || defined(__native_client_nonsfi__)
+// Old Bionic versions and PNaCl toolchain don't have sys/user.h, so we just
+// define regs_struct directly. This can be removed once we no longer need to
+// support these old Bionic versions and PNaCl toolchain.
+struct regs_struct {
+ unsigned long uregs[18];
+};
+#else
+typedef user_regs regs_struct;
+#endif
+
+#define REG_cpsr uregs[16]
+#define REG_pc uregs[15]
+#define REG_lr uregs[14]
+#define REG_sp uregs[13]
+#define REG_ip uregs[12]
+#define REG_fp uregs[11]
+#define REG_r10 uregs[10]
+#define REG_r9 uregs[9]
+#define REG_r8 uregs[8]
+#define REG_r7 uregs[7]
+#define REG_r6 uregs[6]
+#define REG_r5 uregs[5]
+#define REG_r4 uregs[4]
+#define REG_r3 uregs[3]
+#define REG_r2 uregs[2]
+#define REG_r1 uregs[1]
+#define REG_r0 uregs[0]
+#define REG_ORIG_r0 uregs[17]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_r0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_r7
+#define SECCOMP_PT_IP(_regs) (_regs).REG_pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_r0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_r1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_r2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_r3
+#define SECCOMP_PT_PARM5(_regs) (_regs).REG_r4
+#define SECCOMP_PT_PARM6(_regs) (_regs).REG_r5
+
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)
+#define SECCOMP_ARCH AUDIT_ARCH_MIPSEL
+#define SYSCALL_EIGHT_ARGS
+// MIPS sigcontext_t is different from i386/x86_64 and ARM.
+// See </arch/mips/include/uapi/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[_reg])
+// Based on MIPS o32 ABI syscall convention.
+// On MIPS, when indirect syscall is being made (syscall(__NR_foo)),
+// real identificator (__NR_foo) is not in v0, but in a0
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 5)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 6)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 7)
+// Only the first 4 arguments of syscall are in registers.
+// The rest are on the stack.
+#define SECCOMP_STACKPARM(_ctx, n) (((long *)SECCOMP_REG(_ctx, 29))[(n)])
+#define SECCOMP_PARM5(_ctx) SECCOMP_STACKPARM(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_STACKPARM(_ctx, 5)
+#define SECCOMP_PARM7(_ctx) SECCOMP_STACKPARM(_ctx, 6)
+#define SECCOMP_PARM8(_ctx) SECCOMP_STACKPARM(_ctx, 7)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+// On MIPS we don't have structures like user_regs or user_regs_struct in
+// sys/user.h that we could use, so we just define regs_struct directly.
+struct regs_struct {
+ unsigned long long regs[32];
+};
+
+#define REG_a3 regs[7]
+#define REG_a2 regs[6]
+#define REG_a1 regs[5]
+#define REG_a0 regs[4]
+#define REG_v1 regs[3]
+#define REG_v0 regs[2]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_v0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_v0
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_a0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_a1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_a2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_a3
+
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)
+#define SECCOMP_ARCH AUDIT_ARCH_MIPSEL64
+#define SYSCALL_EIGHT_ARGS
+// MIPS sigcontext_t is different from i386/x86_64 and ARM.
+// See </arch/mips/include/uapi/asm/sigcontext.h> in the Linux kernel.
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.gregs[_reg])
+// Based on MIPS n64 ABI syscall convention.
+// On MIPS, when an indirect syscall is being made (syscall(__NR_foo)),
+// the real identifier (__NR_foo) is not in v0, but in a0.
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 5)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 6)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 7)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, 8)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, 9)
+#define SECCOMP_PARM7(_ctx) SECCOMP_REG(_ctx, 10)
+#define SECCOMP_PARM8(_ctx) SECCOMP_REG(_ctx, 11)
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX (offsetof(struct arch_seccomp_data, \
+ instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) (offsetof(struct arch_seccomp_data, args) + \
+ 8*(nr) + 0)
+
+// On MIPS we don't have structures like user_regs or user_regs_struct in
+// sys/user.h that we could use, so we just define regs_struct directly.
+struct regs_struct {
+ unsigned long long regs[32];
+};
+
+#define REG_a7 regs[11]
+#define REG_a6 regs[10]
+#define REG_a5 regs[9]
+#define REG_a4 regs[8]
+#define REG_a3 regs[7]
+#define REG_a2 regs[6]
+#define REG_a1 regs[5]
+#define REG_a0 regs[4]
+#define REG_v1 regs[3]
+#define REG_v0 regs[2]
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).REG_v0
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).REG_v0
+#define SECCOMP_PT_PARM1(_regs) (_regs).REG_a0
+#define SECCOMP_PT_PARM2(_regs) (_regs).REG_a1
+#define SECCOMP_PT_PARM3(_regs) (_regs).REG_a2
+#define SECCOMP_PT_PARM4(_regs) (_regs).REG_a3
+#define SECCOMP_PT_PARM5(_regs) (_regs).REG_a4
+#define SECCOMP_PT_PARM6(_regs) (_regs).REG_a5
+#define SECCOMP_PT_PARM7(_regs) (_regs).REG_a6
+#define SECCOMP_PT_PARM8(_regs) (_regs).REG_a7
+
+#elif defined(__aarch64__)
+struct regs_struct {
+ unsigned long long regs[31];
+ unsigned long long sp;
+ unsigned long long pc;
+ unsigned long long pstate;
+};
+
+#define SECCOMP_ARCH AUDIT_ARCH_AARCH64
+
+#define SECCOMP_REG(_ctx, _reg) ((_ctx)->uc_mcontext.regs[_reg])
+
+#define SECCOMP_RESULT(_ctx) SECCOMP_REG(_ctx, 0)
+#define SECCOMP_SYSCALL(_ctx) SECCOMP_REG(_ctx, 8)
+#define SECCOMP_IP(_ctx) (_ctx)->uc_mcontext.pc
+#define SECCOMP_PARM1(_ctx) SECCOMP_REG(_ctx, 0)
+#define SECCOMP_PARM2(_ctx) SECCOMP_REG(_ctx, 1)
+#define SECCOMP_PARM3(_ctx) SECCOMP_REG(_ctx, 2)
+#define SECCOMP_PARM4(_ctx) SECCOMP_REG(_ctx, 3)
+#define SECCOMP_PARM5(_ctx) SECCOMP_REG(_ctx, 4)
+#define SECCOMP_PARM6(_ctx) SECCOMP_REG(_ctx, 5)
+
+#define SECCOMP_NR_IDX (offsetof(struct arch_seccomp_data, nr))
+#define SECCOMP_ARCH_IDX (offsetof(struct arch_seccomp_data, arch))
+#define SECCOMP_IP_MSB_IDX \
+ (offsetof(struct arch_seccomp_data, instruction_pointer) + 4)
+#define SECCOMP_IP_LSB_IDX \
+ (offsetof(struct arch_seccomp_data, instruction_pointer) + 0)
+#define SECCOMP_ARG_MSB_IDX(nr) \
+ (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 4)
+#define SECCOMP_ARG_LSB_IDX(nr) \
+ (offsetof(struct arch_seccomp_data, args) + 8 * (nr) + 0)
+
+#define SECCOMP_PT_RESULT(_regs) (_regs).regs[0]
+#define SECCOMP_PT_SYSCALL(_regs) (_regs).regs[8]
+#define SECCOMP_PT_IP(_regs) (_regs).pc
+#define SECCOMP_PT_PARM1(_regs) (_regs).regs[0]
+#define SECCOMP_PT_PARM2(_regs) (_regs).regs[1]
+#define SECCOMP_PT_PARM3(_regs) (_regs).regs[2]
+#define SECCOMP_PT_PARM4(_regs) (_regs).regs[3]
+#define SECCOMP_PT_PARM5(_regs) (_regs).regs[4]
+#define SECCOMP_PT_PARM6(_regs) (_regs).regs[5]
+#else
+#error Unsupported target platform
+
+#endif
+
+#endif // SANDBOX_LINUX_BPF_DSL_SECCOMP_MACROS_H_
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc
new file mode 100644
index 0000000000..55b2d5a49a
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/linux_syscall_ranges.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS)
+// This is true for Mips O32 ABI.
+static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 4000");
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)
+// This is true for MIPS N64 ABI.
+static_assert(MIN_SYSCALL == __NR_Linux, "min syscall number should be 5000");
+#else
+// This true for supported architectures (Intel and ARM EABI).
+static_assert(MIN_SYSCALL == 0u,
+ "min syscall should always be zero");
+#endif
+
+// SyscallRange represents an inclusive range of system call numbers.
+struct SyscallRange {
+ uint32_t first;
+ uint32_t last;
+};
+
+const SyscallRange kValidSyscallRanges[] = {
+ // First we iterate up to MAX_PUBLIC_SYSCALL, which is equal to MAX_SYSCALL
+ // on Intel architectures, but leaves room for private syscalls on ARM.
+ {MIN_SYSCALL, MAX_PUBLIC_SYSCALL},
+#if defined(__arm__)
+ // ARM EABI includes "ARM private" system calls starting at
+ // MIN_PRIVATE_SYSCALL, and a "ghost syscall private to the kernel" at
+ // MIN_GHOST_SYSCALL.
+ {MIN_PRIVATE_SYSCALL, MAX_PRIVATE_SYSCALL},
+ {MIN_GHOST_SYSCALL, MAX_SYSCALL},
+#endif
+};
+
+} // namespace
+
+SyscallSet::Iterator SyscallSet::begin() const {
+ return Iterator(set_, false);
+}
+
+SyscallSet::Iterator SyscallSet::end() const {
+ return Iterator(set_, true);
+}
+
+bool SyscallSet::IsValid(uint32_t num) {
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (num >= range.first && num <= range.last) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool operator==(const SyscallSet& lhs, const SyscallSet& rhs) {
+ return (lhs.set_ == rhs.set_);
+}
+
+SyscallSet::Iterator::Iterator(Set set, bool done)
+ : set_(set), done_(done), num_(0) {
+ // If the set doesn't contain 0, we need to skip to the next element.
+ if (!done && set_ == (IsValid(num_) ? Set::INVALID_ONLY : Set::VALID_ONLY)) {
+ ++*this;
+ }
+}
+
+uint32_t SyscallSet::Iterator::operator*() const {
+ DCHECK(!done_);
+ return num_;
+}
+
+SyscallSet::Iterator& SyscallSet::Iterator::operator++() {
+ DCHECK(!done_);
+
+ num_ = NextSyscall();
+ if (num_ == 0) {
+ done_ = true;
+ }
+
+ return *this;
+}
+
+// NextSyscall returns the next system call in the iterated system
+// call set after |num_|, or 0 if no such system call exists.
+uint32_t SyscallSet::Iterator::NextSyscall() const {
+ const bool want_valid = (set_ != Set::INVALID_ONLY);
+ const bool want_invalid = (set_ != Set::VALID_ONLY);
+
+ for (const SyscallRange& range : kValidSyscallRanges) {
+ if (want_invalid && range.first > 0 && num_ < range.first - 1) {
+ // Even when iterating invalid syscalls, we only include the end points;
+ // so skip directly to just before the next (valid) range.
+ return range.first - 1;
+ }
+ if (want_valid && num_ < range.first) {
+ return range.first;
+ }
+ if (want_valid && num_ < range.last) {
+ return num_ + 1;
+ }
+ if (want_invalid && num_ <= range.last) {
+ return range.last + 1;
+ }
+ }
+
+ if (want_invalid) {
+ // BPF programs only ever operate on unsigned quantities. So,
+ // that's how we iterate; we return values from
+ // 0..0xFFFFFFFFu. But there are places, where the kernel might
+ // interpret system call numbers as signed quantities, so the
+ // boundaries between signed and unsigned values are potential
+ // problem cases. We want to explicitly return these values from
+ // our iterator.
+ if (num_ < 0x7FFFFFFFu)
+ return 0x7FFFFFFFu;
+ if (num_ < 0x80000000u)
+ return 0x80000000u;
+
+ if (num_ < 0xFFFFFFFFu)
+ return 0xFFFFFFFFu;
+ }
+
+ return 0;
+}
+
+bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ DCHECK(lhs.set_ == rhs.set_);
+ return (lhs.done_ == rhs.done_) && (lhs.num_ == rhs.num_);
+}
+
+bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs) {
+ return !(lhs == rhs);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h
new file mode 100644
index 0000000000..b9f076d932
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/syscall_set.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
+#define SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
+
+#include <stdint.h>
+
+#include <iterator>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// Iterates over the entire system call range from 0..0xFFFFFFFFu. This
+// iterator is aware of how system calls look like and will skip quickly
+// over ranges that can't contain system calls. It iterates more slowly
+// whenever it reaches a range that is potentially problematic, returning
+// the last invalid value before a valid range of system calls, and the
+// first invalid value after a valid range of syscalls. It iterates over
+// individual values whenever it is in the normal range for system calls
+// (typically MIN_SYSCALL..MAX_SYSCALL).
+//
+// Example usage:
+// for (uint32_t sysnum : SyscallSet::All()) {
+// // Do something with sysnum.
+// }
+class SANDBOX_EXPORT SyscallSet {
+ public:
+ class Iterator;
+
+ SyscallSet(const SyscallSet& ss) : set_(ss.set_) {}
+ ~SyscallSet() {}
+
+ Iterator begin() const;
+ Iterator end() const;
+
+ // All returns a SyscallSet that contains both valid and invalid
+ // system call numbers.
+ static SyscallSet All() { return SyscallSet(Set::ALL); }
+
+ // ValidOnly returns a SyscallSet that contains only valid system
+ // call numbers.
+ static SyscallSet ValidOnly() { return SyscallSet(Set::VALID_ONLY); }
+
+ // InvalidOnly returns a SyscallSet that contains only invalid
+ // system call numbers, but still omits numbers in the middle of a
+ // range of invalid system call numbers.
+ static SyscallSet InvalidOnly() { return SyscallSet(Set::INVALID_ONLY); }
+
+ // IsValid returns whether |num| specifies a valid system call
+ // number.
+ static bool IsValid(uint32_t num);
+
+ private:
+ enum class Set { ALL, VALID_ONLY, INVALID_ONLY };
+
+ explicit SyscallSet(Set set) : set_(set) {}
+
+ Set set_;
+
+ friend bool operator==(const SyscallSet&, const SyscallSet&);
+ DISALLOW_ASSIGN(SyscallSet);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet& lhs, const SyscallSet& rhs);
+
+// Iterator provides C++ input iterator semantics for traversing a
+// SyscallSet.
+class SyscallSet::Iterator
+ : public std::iterator<std::input_iterator_tag, uint32_t> {
+ public:
+ Iterator(const Iterator& it)
+ : set_(it.set_), done_(it.done_), num_(it.num_) {}
+ ~Iterator() {}
+
+ uint32_t operator*() const;
+ Iterator& operator++();
+
+ private:
+ Iterator(Set set, bool done);
+
+ uint32_t NextSyscall() const;
+
+ Set set_;
+ bool done_;
+ uint32_t num_;
+
+ friend SyscallSet;
+ friend bool operator==(const Iterator&, const Iterator&);
+ DISALLOW_ASSIGN(Iterator);
+};
+
+SANDBOX_EXPORT bool operator==(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+SANDBOX_EXPORT bool operator!=(const SyscallSet::Iterator& lhs,
+ const SyscallSet::Iterator& rhs);
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_SYSCALL_SET_H__
diff --git a/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h b/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h
new file mode 100644
index 0000000000..0a5d2f14cc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/bpf_dsl/trap_registry.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
+#define SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This must match the kernel's seccomp_data structure.
+struct arch_seccomp_data {
+ int nr;
+ uint32_t arch;
+ uint64_t instruction_pointer;
+ uint64_t args[6];
+};
+
+namespace bpf_dsl {
+
+// TrapRegistry provides an interface for registering "trap handlers"
+// by associating them with non-zero 16-bit trap IDs. Trap IDs should
+// remain valid for the lifetime of the trap registry.
+class SANDBOX_EXPORT TrapRegistry {
+ public:
+ // TrapFnc is a pointer to a function that fulfills the trap handler
+ // function signature.
+ //
+ // Trap handlers follow the calling convention of native system
+ // calls; e.g., to report an error, they return an exit code in the
+ // range -1..-4096 instead of directly modifying errno. However,
+ // modifying errno is harmless, as the original value will be
+ // restored afterwards.
+ //
+ // Trap handlers are executed from signal context and possibly an
+ // async-signal context, so they must be async-signal safe:
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+ typedef intptr_t (*TrapFnc)(const struct arch_seccomp_data& args, void* aux);
+
+ // Add registers the specified trap handler tuple and returns a
+ // non-zero trap ID that uniquely identifies the tuple for the life
+ // time of the trap registry. If the same tuple is registered
+ // multiple times, the same value will be returned each time.
+ virtual uint16_t Add(TrapFnc fnc, const void* aux, bool safe) = 0;
+
+ // EnableUnsafeTraps tries to enable unsafe traps and returns
+ // whether it was successful. This is a one-way operation.
+ //
+ // CAUTION: Enabling unsafe traps effectively defeats the security
+ // guarantees provided by the sandbox policy. TrapRegistry
+ // implementations should ensure unsafe traps are only enabled
+ // during testing.
+ virtual bool EnableUnsafeTraps() = 0;
+
+ protected:
+ TrapRegistry() {}
+
+ // TrapRegistry's destructor is intentionally non-virtual so that
+ // implementations can omit their destructor. Instead we protect against
+ // misuse by marking it protected.
+ ~TrapRegistry() {}
+
+ DISALLOW_COPY_AND_ASSIGN(TrapRegistry);
+};
+
+} // namespace bpf_dsl
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_BPF_DSL_TRAP_REGISTRY_H_
diff --git a/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
new file mode 100644
index 0000000000..a4315ba3c2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h
@@ -0,0 +1,56 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+namespace sandbox {
+
+// This templated class allows building a BPFTesterDelegate from a
+// deprecated-style BPF policy (that is a SyscallEvaluator function pointer,
+// instead of a SandboxBPFPolicy class), specified in |policy_function| and a
+// function pointer to a test in |test_function|.
+// This allows both the policy and the test function to take a pointer to an
+// object of type "Aux" as a parameter. This is used to implement the BPF_TEST
+// macro and should generally not be used directly.
+template <class Policy, class Aux>
+class BPFTesterCompatibilityDelegate : public BPFTesterDelegate {
+ public:
+ typedef void (*TestFunction)(Aux*);
+
+ explicit BPFTesterCompatibilityDelegate(TestFunction test_function)
+ : aux_(), test_function_(test_function) {}
+
+ ~BPFTesterCompatibilityDelegate() override {}
+
+ std::unique_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ // The current method is guaranteed to only run in the child process
+ // running the test. In this process, the current object is guaranteed
+ // to live forever. So it's ok to pass aux_pointer_for_policy_ to
+ // the policy, which could in turn pass it to the kernel via Trap().
+ return std::unique_ptr<bpf_dsl::Policy>(new Policy(&aux_));
+ }
+
+ void RunTestFunction() override {
+ // Run the actual test.
+ // The current object is guaranteed to live forever in the child process
+ // where this will run.
+ test_function_(&aux_);
+ }
+
+ private:
+ Aux aux_;
+ TestFunction test_function_;
+
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterCompatibilityDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTER_COMPATIBILITY_DELEGATE_H_
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
new file mode 100644
index 0000000000..8b2b12afd8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tester_compatibility_delegate.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+// BPF_TEST_C() is a special version of SANDBOX_TEST(). It runs a test function
+// in a sub-process, under a seccomp-bpf policy specified in
+// |bpf_policy_class_name| without failing on configurations that are allowed
+// to not support seccomp-bpf in their kernels.
+// This is the preferred format for new BPF tests. |bpf_policy_class_name| is a
+// class name (which will be default-constructed) that implements the
+// Policy interface.
+// The test function's body can simply follow. Test functions should use
+// the BPF_ASSERT macros defined below, not GTEST's macros. The use of
+// CHECK* macros is supported but less robust.
+#define BPF_TEST_C(test_case_name, test_name, bpf_policy_class_name) \
+ BPF_DEATH_TEST_C( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_policy_class_name)
+
+// Identical to BPF_TEST_C but allows to specify the nature of death.
+#define BPF_DEATH_TEST_C( \
+ test_case_name, test_name, death, bpf_policy_class_name) \
+ void BPF_TEST_C_##test_name(); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterSimpleDelegate<bpf_policy_class_name>( \
+ BPF_TEST_C_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_C_##test_name()
+
+// This form of BPF_TEST is a little verbose and should be reserved for complex
+// tests where a lot of control is required.
+// |bpf_tester_delegate_class| must be a classname implementing the
+// BPFTesterDelegate interface.
+#define BPF_TEST_D(test_case_name, test_name, bpf_tester_delegate_class) \
+ BPF_DEATH_TEST_D( \
+ test_case_name, test_name, DEATH_SUCCESS(), bpf_tester_delegate_class)
+
+// Identical to BPF_TEST_D but allows to specify the nature of death.
+#define BPF_DEATH_TEST_D( \
+ test_case_name, test_name, death, bpf_tester_delegate_class) \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new bpf_tester_delegate_class()); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ }
+
+// Assertions are handled exactly the same as with a normal SANDBOX_TEST()
+#define BPF_ASSERT SANDBOX_ASSERT
+#define BPF_ASSERT_EQ(x, y) BPF_ASSERT((x) == (y))
+#define BPF_ASSERT_NE(x, y) BPF_ASSERT((x) != (y))
+#define BPF_ASSERT_LT(x, y) BPF_ASSERT((x) < (y))
+#define BPF_ASSERT_GT(x, y) BPF_ASSERT((x) > (y))
+#define BPF_ASSERT_LE(x, y) BPF_ASSERT((x) <= (y))
+#define BPF_ASSERT_GE(x, y) BPF_ASSERT((x) >= (y))
+
+// This form of BPF_TEST is now discouraged (but still allowed) in favor of
+// BPF_TEST_D and BPF_TEST_C.
+// The |policy| parameter should be a Policy subclass.
+// BPF_TEST() takes a C++ data type as an fourth parameter. A variable
+// of this type will be allocated and a pointer to it will be
+// available within the test function as "BPF_AUX". The pointer will
+// also be passed as an argument to the policy's constructor. Policies
+// would typically use it as an argument to SandboxBPF::Trap(), if
+// they want to communicate data between the BPF_TEST() and a Trap()
+// function. The life-time of this object is the same as the life-time
+// of the process running under the seccomp-bpf policy.
+// |aux| must not be void.
+#define BPF_TEST(test_case_name, test_name, policy, aux) \
+ BPF_DEATH_TEST(test_case_name, test_name, DEATH_SUCCESS(), policy, aux)
+
+// A BPF_DEATH_TEST is just the same as a BPF_TEST, but it assumes that the
+// test will fail with a particular known error condition. Use the DEATH_XXX()
+// macros from unit_tests.h to specify the expected error condition.
+#define BPF_DEATH_TEST(test_case_name, test_name, death, policy, aux) \
+ void BPF_TEST_##test_name(aux* BPF_AUX); \
+ TEST(test_case_name, DISABLE_ON_TSAN(test_name)) { \
+ sandbox::SandboxBPFTestRunner bpf_test_runner( \
+ new sandbox::BPFTesterCompatibilityDelegate<policy, aux>( \
+ BPF_TEST_##test_name)); \
+ sandbox::UnitTests::RunTestInProcess(&bpf_test_runner, death); \
+ } \
+ void BPF_TEST_##test_name(aux* BPF_AUX)
+
+// This class takes a simple function pointer as a constructor parameter and a
+// class name as a template parameter to implement the BPFTesterDelegate
+// interface which can be used to build BPF unittests with
+// the SandboxBPFTestRunner class.
+template <class PolicyClass>
+class BPFTesterSimpleDelegate : public BPFTesterDelegate {
+ public:
+ explicit BPFTesterSimpleDelegate(void (*test_function)(void))
+ : test_function_(test_function) {}
+ ~BPFTesterSimpleDelegate() override {}
+
+ std::unique_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return std::unique_ptr<bpf_dsl::Policy>(new PolicyClass());
+ }
+ void RunTestFunction() override {
+ DCHECK(test_function_);
+ test_function_();
+ }
+
+ private:
+ void (*test_function_)(void);
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterSimpleDelegate);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_BPF_TESTS_H__
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
new file mode 100644
index 0000000000..d45bc87292
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/bpf_tests_unittest.cc
@@ -0,0 +1,155 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::Error;
+using sandbox::bpf_dsl::ResultExpr;
+
+namespace sandbox {
+
+namespace {
+
+class FourtyTwo {
+ public:
+ static const int kMagicValue = 42;
+ FourtyTwo() : value_(kMagicValue) {}
+ int value() { return value_; }
+
+ private:
+ int value_;
+ DISALLOW_COPY_AND_ASSIGN(FourtyTwo);
+};
+
+class EmptyClassTakingPolicy : public bpf_dsl::Policy {
+ public:
+ explicit EmptyClassTakingPolicy(FourtyTwo* fourty_two) {
+ BPF_ASSERT(fourty_two);
+ BPF_ASSERT(FourtyTwo::kMagicValue == fourty_two->value());
+ }
+ ~EmptyClassTakingPolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ return Allow();
+ }
+};
+
+BPF_TEST(BPFTest,
+ BPFAUXPointsToClass,
+ EmptyClassTakingPolicy,
+ FourtyTwo /* *BPF_AUX */) {
+ // BPF_AUX should point to an instance of FourtyTwo.
+ BPF_ASSERT(BPF_AUX);
+ BPF_ASSERT(FourtyTwo::kMagicValue == BPF_AUX->value());
+}
+
+void DummyTestFunction(FourtyTwo *fourty_two) {
+}
+
+TEST(BPFTest, BPFTesterCompatibilityDelegateLeakTest) {
+ // Don't do anything, simply gives dynamic tools an opportunity to detect
+ // leaks.
+ {
+ BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>
+ simple_delegate(DummyTestFunction);
+ }
+ {
+ // Test polymorphism.
+ std::unique_ptr<BPFTesterDelegate> simple_delegate(
+ new BPFTesterCompatibilityDelegate<EmptyClassTakingPolicy, FourtyTwo>(
+ DummyTestFunction));
+ }
+}
+
+class EnosysPtracePolicy : public bpf_dsl::Policy {
+ public:
+ EnosysPtracePolicy() { my_pid_ = sys_getpid(); }
+ ~EnosysPtracePolicy() override {
+ // Policies should be able to bind with the process on which they are
+ // created. They should never be created in a parent process.
+ BPF_ASSERT_EQ(my_pid_, sys_getpid());
+ }
+
+ ResultExpr EvaluateSyscall(int system_call_number) const override {
+ CHECK(SandboxBPF::IsValidSyscallNumber(system_call_number));
+ if (system_call_number == __NR_ptrace) {
+ // The EvaluateSyscall function should run in the process that created
+ // the current object.
+ BPF_ASSERT_EQ(my_pid_, sys_getpid());
+ return Error(ENOSYS);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ pid_t my_pid_;
+ DISALLOW_COPY_AND_ASSIGN(EnosysPtracePolicy);
+};
+
+class BasicBPFTesterDelegate : public BPFTesterDelegate {
+ public:
+ BasicBPFTesterDelegate() {}
+ ~BasicBPFTesterDelegate() override {}
+
+ std::unique_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() override {
+ return std::unique_ptr<bpf_dsl::Policy>(new EnosysPtracePolicy());
+ }
+ void RunTestFunction() override {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BasicBPFTesterDelegate);
+};
+
+// This is the most powerful and complex way to create a BPF test, but it
+// requires a full class definition (BasicBPFTesterDelegate).
+BPF_TEST_D(BPFTest, BPFTestWithDelegateClass, BasicBPFTesterDelegate)
+
+// This is the simplest form of BPF tests.
+BPF_TEST_C(BPFTest, BPFTestWithInlineTest, EnosysPtracePolicy) {
+ errno = 0;
+ int ret = ptrace(PTRACE_TRACEME, -1, NULL, NULL);
+ BPF_ASSERT(-1 == ret);
+ BPF_ASSERT(ENOSYS == errno);
+}
+
+const char kHelloMessage[] = "Hello";
+
+BPF_DEATH_TEST_C(BPFTest,
+ BPFDeathTestWithInlineTest,
+ DEATH_MESSAGE(kHelloMessage),
+ EnosysPtracePolicy) {
+ LOG(ERROR) << kHelloMessage;
+ _exit(1);
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc
new file mode 100644
index 0000000000..3baf1f13d9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/die.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace sandbox {
+
+void Die::ExitGroup() {
+ // exit_group() should exit our program. After all, it is defined as a
+ // function that doesn't return. But things can theoretically go wrong.
+ // Especially, since we are dealing with system call filters. Continuing
+ // execution would be very bad in most cases where ExitGroup() gets called.
+ // So, we'll try a few other strategies too.
+ Syscall::Call(__NR_exit_group, 1);
+
+ // We have no idea what our run-time environment looks like. So, signal
+ // handlers might or might not do the right thing. Try to reset settings
+ // to a defined state; but we have not way to verify whether we actually
+ // succeeded in doing so. Nonetheless, triggering a fatal signal could help
+ // us terminate.
+ struct sigaction sa = {};
+ sa.sa_handler = LINUX_SIG_DFL;
+ sa.sa_flags = LINUX_SA_RESTART;
+ sys_sigaction(LINUX_SIGSEGV, &sa, nullptr);
+ Syscall::Call(__NR_prctl, PR_SET_DUMPABLE, (void*)0, (void*)0, (void*)0);
+ if (*(volatile char*)0) {
+ }
+
+ // If there is no way for us to ask for the program to exit, the next
+ // best thing we can do is to loop indefinitely. Maybe, somebody will notice
+ // and file a bug...
+ // We in fact retry the system call inside of our loop so that it will
+ // stand out when somebody tries to diagnose the problem by using "strace".
+ for (;;) {
+ Syscall::Call(__NR_exit_group, 1);
+ }
+}
+
+void Die::SandboxDie(const char* msg, const char* file, int line) {
+ if (simple_exit_) {
+ LogToStderr(msg, file, line);
+ } else {
+ logging::LogMessage(file, line, logging::LOG_FATAL).stream() << msg;
+ }
+ ExitGroup();
+}
+
+void Die::RawSandboxDie(const char* msg) {
+ if (!msg)
+ msg = "";
+ RAW_LOG(FATAL, msg);
+ ExitGroup();
+}
+
+void Die::SandboxInfo(const char* msg, const char* file, int line) {
+ if (!suppress_info_) {
+ logging::LogMessage(file, line, logging::LOG_INFO).stream() << msg;
+ }
+}
+
+void Die::LogToStderr(const char* msg, const char* file, int line) {
+ if (msg) {
+ char buf[40];
+ snprintf(buf, sizeof(buf), "%d", line);
+ std::string s = std::string(file) + ":" + buf + ":" + msg + "\n";
+
+ // No need to loop. Short write()s are unlikely and if they happen we
+ // probably prefer them over a loop that blocks.
+ ignore_result(
+ HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length())));
+ }
+}
+
+bool Die::simple_exit_ = false;
+bool Die::suppress_info_ = false;
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.h
new file mode 100644
index 0000000000..b3f3f72c2f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/die.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
+
+#include "base/macros.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This is the main API for using this file. Prints a error message and
+// exits with a fatal error. This is not async-signal safe.
+#define SANDBOX_DIE(m) sandbox::Die::SandboxDie(m, __FILE__, __LINE__)
+
+// An async signal safe version of the same API. Won't print the filename
+// and line numbers.
+#define RAW_SANDBOX_DIE(m) sandbox::Die::RawSandboxDie(m)
+
+// Adds an informational message to the log file or stderr as appropriate.
+#define SANDBOX_INFO(m) sandbox::Die::SandboxInfo(m, __FILE__, __LINE__)
+
+class SANDBOX_EXPORT Die {
+ public:
+ // Terminate the program, even if the current sandbox policy prevents some
+ // of the more commonly used functions used for exiting.
+ // Most users would want to call SANDBOX_DIE() instead, as it logs extra
+ // information. But calling ExitGroup() is correct and in some rare cases
+ // preferable. So, we make it part of the public API.
+ static void ExitGroup() __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_DIE(). There is normally no reason
+ // to call it directly unless you are defining your own exiting macro.
+ static void SandboxDie(const char* msg, const char* file, int line)
+ __attribute__((noreturn));
+
+ static void RawSandboxDie(const char* msg) __attribute__((noreturn));
+
+ // This method gets called by SANDBOX_INFO(). There is normally no reason
+ // to call it directly unless you are defining your own logging macro.
+ static void SandboxInfo(const char* msg, const char* file, int line);
+
+ // Writes a message to stderr. Used as a fall-back choice, if we don't have
+ // any other way to report an error.
+ static void LogToStderr(const char* msg, const char* file, int line);
+
+ // We generally want to run all exit handlers. This means, on SANDBOX_DIE()
+ // we should be calling LOG(FATAL). But there are some situations where
+ // we just need to print a message and then terminate. This would typically
+ // happen in cases where we consume the error message internally (e.g. in
+ // unit tests or in the supportsSeccompSandbox() method).
+ static void EnableSimpleExit() { simple_exit_ = true; }
+
+ // Sometimes we need to disable all informational messages (e.g. from within
+ // unittests).
+ static void SuppressInfoMessages(bool flag) { suppress_info_ = flag; }
+
+ private:
+ static bool simple_exit_;
+ static bool suppress_info_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Die);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_DIE_H__
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
new file mode 100644
index 0000000000..72a79670d3
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -0,0 +1,259 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/compiler_specific.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/bpf_dsl/policy_compiler.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/bpf_dsl/syscall_set.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/seccomp-bpf/trap.h"
+#include "sandbox/linux/services/proc_util.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/services/thread_helpers.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace sandbox {
+
+namespace {
+
+// Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
+// prctl().
+bool KernelSupportsSeccompBPF() {
+ errno = 0;
+ const int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
+
+ if (rv == -1 && EFAULT == errno) {
+ return true;
+ }
+ return false;
+}
+
+// LG introduced a buggy syscall, sys_set_media_ext, with the same number as
+// seccomp. Return true if the current kernel has this buggy syscall.
+//
+// We want this to work with upcoming versions of seccomp, so we pass bogus
+// flags that are unlikely to ever be used by the kernel. A normal kernel would
+// return -EINVAL, but a buggy LG kernel would return 1.
+bool KernelHasLGBug() {
+#if defined(OS_ANDROID)
+ // sys_set_media will see this as NULL, which should be a safe (non-crashing)
+ // way to invoke it. A genuine seccomp syscall will see it as
+ // SECCOMP_SET_MODE_STRICT.
+ const unsigned int operation = 0;
+ // Chosen by fair dice roll. Guaranteed to be random.
+ const unsigned int flags = 0xf7a46a5c;
+ const int rv = sys_seccomp(operation, flags, nullptr);
+ // A genuine kernel would return -EINVAL (which would set rv to -1 and errno
+ // to EINVAL), or at the very least return some kind of error (which would
+ // set rv to -1). Any other behavior indicates that whatever code received
+ // our syscall was not the real seccomp.
+ if (rv != -1) {
+ return true;
+ }
+#endif // defined(OS_ANDROID)
+
+ return false;
+}
+
+// Check if the kernel supports seccomp-filter via the seccomp system call
+// and the TSYNC feature to enable seccomp on all threads.
+bool KernelSupportsSeccompTsync() {
+ if (KernelHasLGBug()) {
+ return false;
+ }
+
+ errno = 0;
+ const int rv =
+ sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, nullptr);
+
+ if (rv == -1 && errno == EFAULT) {
+ return true;
+ }
+
+ DCHECK_EQ(-1, rv);
+ DCHECK(ENOSYS == errno || EINVAL == errno);
+ return false;
+}
+
+uint64_t EscapePC() {
+ intptr_t rv = Syscall::Call(-1);
+ if (rv == -1 && errno == ENOSYS) {
+ return 0;
+ }
+ return static_cast<uint64_t>(static_cast<uintptr_t>(rv));
+}
+
+intptr_t SandboxPanicTrap(const struct arch_seccomp_data&, void* aux) {
+ SANDBOX_DIE(static_cast<const char*>(aux));
+}
+
+bpf_dsl::ResultExpr SandboxPanic(const char* error) {
+ return bpf_dsl::Trap(SandboxPanicTrap, error);
+}
+
+} // namespace
+
+SandboxBPF::SandboxBPF(std::unique_ptr<bpf_dsl::Policy> policy)
+ : proc_fd_(), sandbox_has_started_(false), policy_(std::move(policy)) {}
+
+SandboxBPF::~SandboxBPF() {
+}
+
+// static
+bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
+ switch (level) {
+ case SeccompLevel::SINGLE_THREADED:
+ return KernelSupportsSeccompBPF();
+ case SeccompLevel::MULTI_THREADED:
+ return KernelSupportsSeccompTsync();
+ }
+ NOTREACHED();
+ return false;
+}
+
+bool SandboxBPF::StartSandbox(SeccompLevel seccomp_level) {
+ DCHECK(policy_);
+ CHECK(seccomp_level == SeccompLevel::SINGLE_THREADED ||
+ seccomp_level == SeccompLevel::MULTI_THREADED);
+
+ if (sandbox_has_started_) {
+ SANDBOX_DIE(
+ "Cannot repeatedly start sandbox. Create a separate Sandbox "
+ "object instead.");
+ return false;
+ }
+
+ if (!proc_fd_.is_valid()) {
+ SetProcFd(ProcUtil::OpenProc());
+ }
+
+ const bool supports_tsync = KernelSupportsSeccompTsync();
+
+ if (seccomp_level == SeccompLevel::SINGLE_THREADED) {
+ // Wait for /proc/self/task/ to update if needed and assert the
+ // process is single threaded.
+ ThreadHelpers::AssertSingleThreaded(proc_fd_.get());
+ } else if (seccomp_level == SeccompLevel::MULTI_THREADED) {
+ if (!supports_tsync) {
+ SANDBOX_DIE("Cannot start sandbox; kernel does not support synchronizing "
+ "filters for a threadgroup");
+ return false;
+ }
+ }
+
+ // We no longer need access to any files in /proc. We want to do this
+ // before installing the filters, just in case that our policy denies
+ // close().
+ if (proc_fd_.is_valid()) {
+ proc_fd_.reset();
+ }
+
+ // Install the filters.
+ InstallFilter(supports_tsync ||
+ seccomp_level == SeccompLevel::MULTI_THREADED);
+
+ return true;
+}
+
+void SandboxBPF::SetProcFd(base::ScopedFD proc_fd) {
+ proc_fd_.swap(proc_fd);
+}
+
+// static
+bool SandboxBPF::IsValidSyscallNumber(int sysnum) {
+ return SyscallSet::IsValid(sysnum);
+}
+
+// static
+bool SandboxBPF::IsRequiredForUnsafeTrap(int sysno) {
+ return bpf_dsl::PolicyCompiler::IsRequiredForUnsafeTrap(sysno);
+}
+
+// static
+intptr_t SandboxBPF::ForwardSyscall(const struct arch_seccomp_data& args) {
+ return Syscall::Call(
+ args.nr, static_cast<intptr_t>(args.args[0]),
+ static_cast<intptr_t>(args.args[1]), static_cast<intptr_t>(args.args[2]),
+ static_cast<intptr_t>(args.args[3]), static_cast<intptr_t>(args.args[4]),
+ static_cast<intptr_t>(args.args[5]));
+}
+
+CodeGen::Program SandboxBPF::AssembleFilter() {
+ DCHECK(policy_);
+
+ bpf_dsl::PolicyCompiler compiler(policy_.get(), Trap::Registry());
+ if (Trap::SandboxDebuggingAllowedByUser()) {
+ compiler.DangerousSetEscapePC(EscapePC());
+ }
+ compiler.SetPanicFunc(SandboxPanic);
+ return compiler.Compile();
+}
+
+void SandboxBPF::InstallFilter(bool must_sync_threads) {
+ // We want to be very careful in not imposing any requirements on the
+ // policies that are set with SetSandboxPolicy(). This means, as soon as
+ // the sandbox is active, we shouldn't be relying on libraries that could
+ // be making system calls. This, for example, means we should avoid
+ // using the heap and we should avoid using STL functions.
+ // Temporarily copy the contents of the "program" vector into a
+ // stack-allocated array; and then explicitly destroy that object.
+ // This makes sure we don't ex- or implicitly call new/delete after we
+ // installed the BPF filter program in the kernel. Depending on the
+ // system memory allocator that is in effect, these operators can result
+ // in system calls to things like munmap() or brk().
+ CodeGen::Program program = AssembleFilter();
+
+ struct sock_filter bpf[program.size()];
+ const struct sock_fprog prog = {static_cast<unsigned short>(program.size()),
+ bpf};
+ 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
+ // what will be possible to do in the new (sandboxed) execution environment.
+ policy_.reset();
+
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ SANDBOX_DIE("Kernel refuses to enable no-new-privs");
+ }
+
+ // Install BPF filter program. If the thread state indicates multi-threading
+ // support, then the kernel hass the seccomp system call. Otherwise, fall
+ // back on prctl, which requires the process to be single-threaded.
+ if (must_sync_threads) {
+ int rv =
+ sys_seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, &prog);
+ if (rv) {
+ SANDBOX_DIE(
+ "Kernel refuses to turn on and synchronize threads for BPF filters");
+ }
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
+ SANDBOX_DIE("Kernel refuses to turn on BPF filters");
+ }
+ }
+
+ sandbox_has_started_ = true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
new file mode 100644
index 0000000000..282852992b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/files/scoped_file.h"
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/codegen.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+struct arch_seccomp_data;
+
+// This class can be used to apply a syscall sandboxing policy expressed in a
+// bpf_dsl::Policy object to the current process.
+// Syscall sandboxing policies get inherited by subprocesses and, once applied,
+// can never be removed for the lifetime of the process.
+class SANDBOX_EXPORT SandboxBPF {
+ public:
+ enum class SeccompLevel {
+ SINGLE_THREADED,
+ MULTI_THREADED,
+ };
+
+ // Ownership of |policy| is transfered here to the sandbox object.
+ // nullptr is allowed for unit tests.
+ explicit SandboxBPF(std::unique_ptr<bpf_dsl::Policy> policy);
+ // NOTE: Setting a policy and starting the sandbox is a one-way operation.
+ // The kernel does not provide any option for unloading a loaded sandbox. The
+ // sandbox remains engaged even when the object is destructed.
+ ~SandboxBPF();
+
+ // Detect if the kernel supports the specified seccomp level.
+ // See StartSandbox() for a description of these.
+ static bool SupportsSeccompSandbox(SeccompLevel level);
+
+ // This is the main public entry point. It sets up the resources needed by
+ // the sandbox, and enters Seccomp mode.
+ // The calling process must provide a |level| to tell the sandbox which type
+ // of kernel support it should engage.
+ // SINGLE_THREADED will only sandbox the calling thread. Since it would be a
+ // security risk, the sandbox will also check that the current process is
+ // single threaded and crash if it isn't the case.
+ // MULTI_THREADED requires more recent kernel support and allows to sandbox
+ // all the threads of the current process. Be mindful of potential races,
+ // with other threads using disallowed system calls either before or after
+ // the sandbox is engaged.
+ //
+ // It is possible to stack multiple sandboxes by creating separate "Sandbox"
+ // objects and calling "StartSandbox()" on each of them. Please note, that
+ // this requires special care, though, as newly stacked sandboxes can never
+ // relax restrictions imposed by earlier sandboxes. Furthermore, installing
+ // a new policy requires making system calls, that might already be
+ // disallowed.
+ // Finally, stacking does add more kernel overhead than having a single
+ // combined policy. So, it should only be used if there are no alternatives.
+ bool StartSandbox(SeccompLevel level) WARN_UNUSED_RESULT;
+
+ // The sandbox needs to be able to access files in "/proc/self/". If
+ // this directory is not accessible when "StartSandbox()" gets called, the
+ // caller must provide an already opened file descriptor by calling
+ // "SetProcFd()".
+ // The sandbox becomes the new owner of this file descriptor and will
+ // close it when "StartSandbox()" executes or when the sandbox object
+ // disappears.
+ void SetProcFd(base::ScopedFD proc_fd);
+
+ // Checks whether a particular system call number is valid on the current
+ // architecture.
+ static bool IsValidSyscallNumber(int sysnum);
+
+ // UnsafeTraps require some syscalls to always be allowed.
+ // This helper function returns true for these calls.
+ static bool IsRequiredForUnsafeTrap(int sysno);
+
+ // From within an UnsafeTrap() it is often useful to be able to execute
+ // the system call that triggered the trap. The ForwardSyscall() method
+ // makes this easy. It is more efficient than calling glibc's syscall()
+ // function, as it avoid the extra round-trip to the signal handler. And
+ // it automatically does the correct thing to report kernel-style error
+ // conditions, rather than setting errno. See the comments for TrapFnc for
+ // details. In other words, the return value from ForwardSyscall() is
+ // directly suitable as a return value for a trap handler.
+ static intptr_t ForwardSyscall(const struct arch_seccomp_data& args);
+
+ 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.
+ 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);
+
+ base::ScopedFD proc_fd_;
+ bool sandbox_has_started_;
+ std::unique_ptr<bpf_dsl::Policy> policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPF);
+};
+
+} // namespace sandbox
+
+#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
new file mode 100644
index 0000000000..36f3744b76
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -0,0 +1,66 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h"
+
+#include <fcntl.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/system_headers/linux_filter.h"
+#include "sandbox/linux/tests/unit_tests.h"
+
+namespace sandbox {
+
+SandboxBPFTestRunner::SandboxBPFTestRunner(
+ BPFTesterDelegate* bpf_tester_delegate)
+ : bpf_tester_delegate_(bpf_tester_delegate) {
+}
+
+SandboxBPFTestRunner::~SandboxBPFTestRunner() {
+}
+
+void SandboxBPFTestRunner::Run() {
+ DCHECK(bpf_tester_delegate_);
+ sandbox::Die::EnableSimpleExit();
+
+ std::unique_ptr<bpf_dsl::Policy> policy =
+ bpf_tester_delegate_->GetSandboxBPFPolicy();
+
+ if (sandbox::SandboxBPF::SupportsSeccompSandbox(
+ SandboxBPF::SeccompLevel::SINGLE_THREADED)) {
+ // Initialize and then start the sandbox with our custom policy
+ sandbox::SandboxBPF sandbox(std::move(policy));
+ SANDBOX_ASSERT(sandbox.StartSandbox(
+ sandbox::SandboxBPF::SeccompLevel::SINGLE_THREADED));
+
+ // Run the actual test.
+ bpf_tester_delegate_->RunTestFunction();
+ } else {
+ printf("This BPF test is not fully running in this configuration!\n");
+ // Android is the only configuration where we accept not having kernel
+ // BPF support.
+ if (!IsAndroid()) {
+ const bool seccomp_bpf_is_supported = false;
+ SANDBOX_ASSERT(seccomp_bpf_is_supported);
+ }
+ // Call the compiler and verify the policy. That's the least we can do,
+ // if we don't have kernel support.
+ sandbox::SandboxBPF sandbox(std::move(policy));
+ sandbox.AssembleFilter();
+ sandbox::UnitTests::IgnoreThisTest();
+ }
+}
+
+bool SandboxBPFTestRunner::ShouldCheckForLeaks() const {
+ // LSAN requires being able to use ptrace() and other system calls that could
+ // be denied.
+ return false;
+}
+
+} // namespace sandbox
diff --git a/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
new file mode 100644
index 0000000000..4fc3c5d169
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
@@ -0,0 +1,62 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/linux/tests/sandbox_test_runner.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class Policy;
+}
+
+// To create a SandboxBPFTestRunner object, one needs to implement this
+// interface and pass an instance to the SandboxBPFTestRunner constructor.
+// In the child process running the test, the BPFTesterDelegate object is
+// guaranteed to not be destroyed until the child process terminates.
+class BPFTesterDelegate {
+ public:
+ BPFTesterDelegate() {}
+ virtual ~BPFTesterDelegate() {}
+
+ // This will instanciate a policy suitable for the test we want to run. It is
+ // guaranteed to only be called from the child process that will run the
+ // test.
+ virtual std::unique_ptr<bpf_dsl::Policy> GetSandboxBPFPolicy() = 0;
+ // This will be called from a child process with the BPF sandbox turned on.
+ virtual void RunTestFunction() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BPFTesterDelegate);
+};
+
+// This class implements the SandboxTestRunner interface and Run() will
+// initialize a seccomp-bpf sandbox (specified by |bpf_tester_delegate|) and
+// run a test function (via |bpf_tester_delegate|) if the current kernel
+// configuration allows it. If it can not run the test under seccomp-bpf,
+// Run() will still compile the policy which should allow to get some coverage
+// under tools that behave like Valgrind.
+class SandboxBPFTestRunner : public SandboxTestRunner {
+ public:
+ // This constructor takes ownership of the |bpf_tester_delegate| object.
+ // (It doesn't take a std::unique_ptr since they make polymorphism verbose).
+ explicit SandboxBPFTestRunner(BPFTesterDelegate* bpf_tester_delegate);
+ ~SandboxBPFTestRunner() override;
+
+ void Run() override;
+
+ bool ShouldCheckForLeaks() const override;
+
+ private:
+ std::unique_ptr<BPFTesterDelegate> bpf_tester_delegate_;
+ DISALLOW_COPY_AND_ASSIGN(SandboxBPFTestRunner);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_TEST_RUNNER_H_
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc
new file mode 100644
index 0000000000..34edabd2b8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.cc
@@ -0,0 +1,481 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+
+namespace sandbox {
+
+namespace {
+
+#if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \
+ defined(ARCH_CPU_MIPS_FAMILY)
+// Number that's not currently used by any Linux kernel ABIs.
+const int kInvalidSyscallNumber = 0x351d3;
+#else
+#error Unrecognized architecture
+#endif
+
+asm(// We need to be able to tell the kernel exactly where we made a
+ // system call. The C++ compiler likes to sometimes clone or
+ // inline code, which would inadvertently end up duplicating
+ // the entry point.
+ // "gcc" can suppress code duplication with suitable function
+ // attributes, but "clang" doesn't have this ability.
+ // The "clang" developer mailing list suggested that the correct
+ // and portable solution is a file-scope assembly block.
+ // N.B. We do mark our code as a proper function so that backtraces
+ // work correctly. But we make absolutely no attempt to use the
+ // ABI's calling conventions for passing arguments. We will only
+ // ever be called from assembly code and thus can pick more
+ // suitable calling conventions.
+#if defined(__i386__)
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%eax" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "int $0x80". This address can be
+ // used as a marker that BPF code inspects.
+ "test %eax, %eax\n"
+ "jge 1f\n"
+ // Always, make sure that our code is position-independent, or
+ // address space randomization might not work on i386. This means,
+ // we can't use "lea", but instead have to rely on "call/pop".
+ "call 0f; .cfi_adjust_cfa_offset 4\n"
+ "0:pop %eax; .cfi_adjust_cfa_offset -4\n"
+ "addl $2f-0b, %eax\n"
+ "ret\n"
+ // Save register that we don't want to clobber. On i386, we need to
+ // save relatively aggressively, as there are a couple or registers
+ // that are used internally (e.g. %ebx for position-independent
+ // code, and %ebp for the frame pointer), and as we need to keep at
+ // least a few registers available for the register allocator.
+ "1:push %esi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset esi, 0\n"
+ "push %edi; .cfi_adjust_cfa_offset 4; .cfi_rel_offset edi, 0\n"
+ "push %ebx; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebx, 0\n"
+ "push %ebp; .cfi_adjust_cfa_offset 4; .cfi_rel_offset ebp, 0\n"
+ // Copy entries from the array holding the arguments into the
+ // correct CPU registers.
+ "movl 0(%edi), %ebx\n"
+ "movl 4(%edi), %ecx\n"
+ "movl 8(%edi), %edx\n"
+ "movl 12(%edi), %esi\n"
+ "movl 20(%edi), %ebp\n"
+ "movl 16(%edi), %edi\n"
+ // Enter the kernel.
+ "int $0x80\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:"
+ // Restore any clobbered registers that we didn't declare to the
+ // compiler.
+ "pop %ebp; .cfi_restore ebp; .cfi_adjust_cfa_offset -4\n"
+ "pop %ebx; .cfi_restore ebx; .cfi_adjust_cfa_offset -4\n"
+ "pop %edi; .cfi_restore edi; .cfi_adjust_cfa_offset -4\n"
+ "pop %esi; .cfi_restore esi; .cfi_adjust_cfa_offset -4\n"
+ "ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__x86_64__)
+ ".text\n"
+ ".align 16, 0x90\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.cfi_startproc\n"
+ // Check if "%rdi" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "test %rdi, %rdi\n"
+ "jge 1f\n"
+ // Always make sure that our code is position-independent, or the
+ // linker will throw a hissy fit on x86-64.
+ "lea 2f(%rip), %rax\n"
+ "ret\n"
+ // Now we load the registers used to pass arguments to the system
+ // call: system call number in %rax, and arguments in %rdi, %rsi,
+ // %rdx, %r10, %r8, %r9. Note: These are all caller-save registers
+ // (only %rbx, %rbp, %rsp, and %r12-%r15 are callee-save), so no
+ // need to worry here about spilling registers or CFI directives.
+ "1:movq %rdi, %rax\n"
+ "movq 0(%rsi), %rdi\n"
+ "movq 16(%rsi), %rdx\n"
+ "movq 24(%rsi), %r10\n"
+ "movq 32(%rsi), %r8\n"
+ "movq 40(%rsi), %r9\n"
+ "movq 8(%rsi), %rsi\n"
+ // Enter the kernel.
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ "2:ret\n"
+ ".cfi_endproc\n"
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif defined(__arm__)
+ // Throughout this file, we use the same mode (ARM vs. thumb)
+ // that the C++ compiler uses. This means, when transfering control
+ // from C++ to assembly code, we do not need to switch modes (e.g.
+ // by using the "bx" instruction). It also means that our assembly
+ // code should not be invoked directly from code that lives in
+ // other compilation units, as we don't bother implementing thumb
+ // interworking. That's OK, as we don't make any of the assembly
+ // symbols public. They are all local to this file.
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+#if defined(__thumb__)
+ ".thumb_func\n"
+#else
+ ".arm\n"
+#endif
+ "SyscallAsm:\n"
+#if !defined(__native_client_nonsfi__)
+ // .fnstart and .fnend pseudo operations creates unwind table.
+ // It also creates a reference to the symbol __aeabi_unwind_cpp_pr0, which
+ // is not provided by PNaCl toolchain. Disable it.
+ ".fnstart\n"
+#endif
+ "@ args = 0, pretend = 0, frame = 8\n"
+ "@ frame_needed = 1, uses_anonymous_args = 0\n"
+#if defined(__thumb__)
+ ".cfi_startproc\n"
+ "push {r7, lr}\n"
+ ".save {r7, lr}\n"
+ ".cfi_offset 14, -4\n"
+ ".cfi_offset 7, -8\n"
+ ".cfi_def_cfa_offset 8\n"
+#else
+ "stmfd sp!, {fp, lr}\n"
+ "add fp, sp, #4\n"
+#endif
+ // Check if "r0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "swi 0". This address can be
+ // used as a marker that BPF code inspects.
+ "cmp r0, #0\n"
+ "bge 1f\n"
+ "adr r0, 2f\n"
+ "b 2f\n"
+ // We declared (almost) all clobbered registers to the compiler. On
+ // ARM there is no particular register pressure. So, we can go
+ // ahead and directly copy the entries from the arguments array
+ // into the appropriate CPU registers.
+ "1:ldr r5, [r6, #20]\n"
+ "ldr r4, [r6, #16]\n"
+ "ldr r3, [r6, #12]\n"
+ "ldr r2, [r6, #8]\n"
+ "ldr r1, [r6, #4]\n"
+ "mov r7, r0\n"
+ "ldr r0, [r6, #0]\n"
+ // Enter the kernel
+ "swi 0\n"
+// Restore the frame pointer. Also restore the program counter from
+// the link register; this makes us return to the caller.
+#if defined(__thumb__)
+ "2:pop {r7, pc}\n"
+ ".cfi_endproc\n"
+#else
+ "2:ldmfd sp!, {fp, pc}\n"
+#endif
+#if !defined(__native_client_nonsfi__)
+ // Do not use .fnstart and .fnend for PNaCl toolchain. See above comment,
+ // for more details.
+ ".fnend\n"
+#endif
+ "9:.size SyscallAsm, 9b-SyscallAsm\n"
+#elif (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
+ ".text\n"
+ ".option pic2\n"
+ ".align 4\n"
+ ".global SyscallAsm\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.ent SyscallAsm\n"
+ ".frame $sp, 40, $ra\n"
+ ".set push\n"
+ ".set noreorder\n"
+ ".cpload $t9\n"
+ "addiu $sp, $sp, -40\n"
+ "sw $ra, 36($sp)\n"
+ // Check if "v0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "bgez $v0, 1f\n"
+ " nop\n"
+ // This is equivalent to "la $v0, 2f".
+ // LA macro has to be avoided since LLVM-AS has issue with LA in PIC mode
+ // https://llvm.org/bugs/show_bug.cgi?id=27644
+ "lw $v0, %got(2f)($gp)\n"
+ "addiu $v0, $v0, %lo(2f)\n"
+ "b 2f\n"
+ " nop\n"
+ // On MIPS first four arguments go to registers a0 - a3 and any
+ // argument after that goes to stack. We can go ahead and directly
+ // copy the entries from the arguments array into the appropriate
+ // CPU registers and on the stack.
+ "1:lw $a3, 28($a0)\n"
+ "lw $a2, 24($a0)\n"
+ "lw $a1, 20($a0)\n"
+ "lw $t0, 16($a0)\n"
+ "sw $a3, 28($sp)\n"
+ "sw $a2, 24($sp)\n"
+ "sw $a1, 20($sp)\n"
+ "sw $t0, 16($sp)\n"
+ "lw $a3, 12($a0)\n"
+ "lw $a2, 8($a0)\n"
+ "lw $a1, 4($a0)\n"
+ "lw $a0, 0($a0)\n"
+ // Enter the kernel
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ // Restore the return address from the stack.
+ "2:lw $ra, 36($sp)\n"
+ "jr $ra\n"
+ " addiu $sp, $sp, 40\n"
+ ".set pop\n"
+ ".end SyscallAsm\n"
+ ".size SyscallAsm,.-SyscallAsm\n"
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)
+ ".text\n"
+ ".option pic2\n"
+ ".global SyscallAsm\n"
+ ".type SyscallAsm, @function\n"
+ "SyscallAsm:.ent SyscallAsm\n"
+ ".frame $sp, 16, $ra\n"
+ ".set push\n"
+ ".set noreorder\n"
+ "daddiu $sp, $sp, -16\n"
+ ".cpsetup $25, 0, SyscallAsm\n"
+ "sd $ra, 8($sp)\n"
+ // Check if "v0" is negative. If so, do not attempt to make a
+ // system call. Instead, compute the return address that is visible
+ // to the kernel after we execute "syscall". This address can be
+ // used as a marker that BPF code inspects.
+ "bgez $v0, 1f\n"
+ " nop\n"
+ // This is equivalent to "la $v0, 2f".
+ // LA macro has to be avoided since LLVM-AS has issue with LA in PIC mode
+ // https://llvm.org/bugs/show_bug.cgi?id=27644
+ "ld $v0, %got(2f)($gp)\n"
+ "daddiu $v0, $v0, %lo(2f)\n"
+ "b 2f\n"
+ " nop\n"
+ // On MIPS N64 all eight arguments go to registers a0 - a7
+ // We can go ahead and directly copy the entries from the arguments array
+ // into the appropriate CPU registers.
+ "1:ld $a7, 56($a0)\n"
+ "ld $a6, 48($a0)\n"
+ "ld $a5, 40($a0)\n"
+ "ld $a4, 32($a0)\n"
+ "ld $a3, 24($a0)\n"
+ "ld $a2, 16($a0)\n"
+ "ld $a1, 8($a0)\n"
+ "ld $a0, 0($a0)\n"
+ // Enter the kernel
+ "syscall\n"
+ // This is our "magic" return address that the BPF filter sees.
+ // Restore the return address from the stack.
+ "2:ld $ra, 8($sp)\n"
+ ".cpreturn\n"
+ "jr $ra\n"
+ "daddiu $sp, $sp, 16\n"
+ ".set pop\n"
+ ".end SyscallAsm\n"
+ ".size SyscallAsm,.-SyscallAsm\n"
+#elif defined(__aarch64__)
+ ".text\n"
+ ".align 2\n"
+ ".type SyscallAsm, %function\n"
+ "SyscallAsm:\n"
+ ".cfi_startproc\n"
+ "cmp x0, #0\n"
+ "b.ge 1f\n"
+ "adr x0,2f\n"
+ "b 2f\n"
+ "1:ldr x5, [x6, #40]\n"
+ "ldr x4, [x6, #32]\n"
+ "ldr x3, [x6, #24]\n"
+ "ldr x2, [x6, #16]\n"
+ "ldr x1, [x6, #8]\n"
+ "mov x8, x0\n"
+ "ldr x0, [x6, #0]\n"
+ // Enter the kernel
+ "svc 0\n"
+ "2:ret\n"
+ ".cfi_endproc\n"
+ ".size SyscallAsm, .-SyscallAsm\n"
+#endif
+ ); // asm
+
+#if defined(__x86_64__)
+extern "C" {
+intptr_t SyscallAsm(intptr_t nr, const intptr_t args[6]);
+}
+#elif defined(__mips__)
+extern "C" {
+intptr_t SyscallAsm(intptr_t nr, const intptr_t args[8]);
+}
+#endif
+
+} // namespace
+
+intptr_t Syscall::InvalidCall() {
+ // Explicitly pass eight zero arguments just in case.
+ return Call(kInvalidSyscallNumber, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+intptr_t Syscall::Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7) {
+ // We rely on "intptr_t" to be the exact size as a "void *". This is
+ // typically true, but just in case, we add a check. The language
+ // specification allows platforms some leeway in cases, where
+ // "sizeof(void *)" is not the same as "sizeof(void (*)())". We expect
+ // that this would only be an issue for IA64, which we are currently not
+ // planning on supporting. And it is even possible that this would work
+ // on IA64, but for lack of actual hardware, I cannot test.
+ static_assert(sizeof(void*) == sizeof(intptr_t),
+ "pointer types and intptr_t must be exactly the same size");
+
+ // TODO(nedeljko): Enable use of more than six parameters on architectures
+ // where that makes sense.
+#if defined(__mips__)
+ const intptr_t args[8] = {p0, p1, p2, p3, p4, p5, p6, p7};
+#else
+ DCHECK_EQ(p6, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ DCHECK_EQ(p7, 0) << " Support for syscalls with more than six arguments not "
+ "added for this architecture";
+ const intptr_t args[6] = {p0, p1, p2, p3, p4, p5};
+#endif // defined(__mips__)
+
+// Invoke our file-scope assembly code. The constraints have been picked
+// carefully to match what the rest of the assembly code expects in input,
+// output, and clobbered registers.
+#if defined(__i386__)
+ intptr_t ret = nr;
+ asm volatile(
+ "call SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=a"(ret)
+ : "0"(ret), "D"(args)
+ : "cc", "esp", "memory", "ecx", "edx");
+#elif defined(__x86_64__)
+ intptr_t ret = SyscallAsm(nr, args);
+#elif defined(__arm__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("r0") = nr;
+ register const intptr_t* data __asm__("r6") = args;
+ asm volatile(
+ "bl SyscallAsm\n"
+ // N.B. These are not the calling conventions normally used by the ABI.
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "cc",
+ "lr",
+ "memory",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5"
+#if !defined(__thumb__)
+ // In thumb mode, we cannot use "r7" as a general purpose register, as
+ // it is our frame pointer. We have to manually manage and preserve
+ // it.
+ // In ARM mode, we have a dedicated frame pointer register and "r7" is
+ // thus available as a general purpose register. We don't preserve it,
+ // but instead mark it as clobbered.
+ ,
+ "r7"
+#endif // !defined(__thumb__)
+ );
+ ret = inout;
+ }
+#elif defined(__mips__)
+ intptr_t err_status;
+ intptr_t ret = Syscall::SandboxSyscallRaw(nr, args, &err_status);
+
+ if (err_status) {
+ // On error, MIPS returns errno from syscall instead of -errno.
+ // The purpose of this negation is for SandboxSyscall() to behave
+ // more like it would on other architectures.
+ ret = -ret;
+ }
+#elif defined(__aarch64__)
+ intptr_t ret;
+ {
+ register intptr_t inout __asm__("x0") = nr;
+ register const intptr_t* data __asm__("x6") = args;
+ asm volatile("bl SyscallAsm\n"
+ : "=r"(inout)
+ : "0"(inout), "r"(data)
+ : "memory", "x1", "x2", "x3", "x4", "x5", "x8", "x30");
+ ret = inout;
+ }
+
+#else
+#error "Unimplemented architecture"
+#endif
+ return ret;
+}
+
+void Syscall::PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx) {
+#if defined(__mips__)
+ // Mips ABI states that on error a3 CPU register has non zero value and if
+ // there is no error, it should be zero.
+ if (ret_val <= -1 && ret_val >= -4095) {
+ // |ret_val| followes the Syscall::Call() convention of being -errno on
+ // errors. In order to write correct value to return register this sign
+ // needs to be changed back.
+ ret_val = -ret_val;
+ SECCOMP_PARM4(ctx) = 1;
+ } else
+ SECCOMP_PARM4(ctx) = 0;
+#endif
+ SECCOMP_RESULT(ctx) = static_cast<greg_t>(ret_val);
+}
+
+#if defined(__mips__)
+intptr_t Syscall::SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_ret) {
+ register intptr_t ret __asm__("v0") = nr;
+ register intptr_t syscallasm __asm__("t9") = (intptr_t) &SyscallAsm;
+ // a3 register becomes non zero on error.
+ register intptr_t err_stat __asm__("a3") = 0;
+ {
+ register const intptr_t* data __asm__("a0") = args;
+ asm volatile(
+ "jalr $t9\n"
+ " nop\n"
+ : "=r"(ret), "=r"(err_stat)
+ : "0"(ret),
+ "r"(data),
+ "r"(syscallasm)
+ // a2 is in the clober list so inline assembly can not change its
+ // value.
+ : "memory", "ra", "a2");
+ }
+
+ // Set an error status so it can be used outside of this function
+ *err_ret = err_stat;
+
+ return ret;
+}
+#endif // defined(__mips__)
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h
new file mode 100644
index 0000000000..3b02a6723f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall.h
@@ -0,0 +1,166 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
+
+#include <signal.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// This purely static class can be used to perform system calls with some
+// low-level control.
+class SANDBOX_EXPORT Syscall {
+ public:
+ // InvalidCall() invokes Call() with a platform-appropriate syscall
+ // number that is guaranteed to not be implemented (i.e., normally
+ // returns -ENOSYS).
+ // This is primarily meant to be useful for writing sandbox policy
+ // unit tests.
+ static intptr_t InvalidCall();
+
+ // System calls can take up to six parameters (up to eight on some
+ // architectures). Traditionally, glibc
+ // implements this property by using variadic argument lists. This works, but
+ // confuses tools that behave like Valgrind, because we are nominally passing
+ // uninitialized data whenever we call through this function and pass less
+ // than the full six arguments.
+ // So, instead, we use C++'s template system to achieve a very similar
+ // effect. C++ automatically sets the unused parameters to zero for us, and
+ // it also does the correct type expansion (e.g. from 32bit to 64bit) where
+ // necessary.
+ // We have to use C-style cast operators as we want to be able to accept both
+ // integer and pointer types.
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6,
+ class T7>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ (intptr_t)p7);
+ }
+
+ template <class T0,
+ class T1,
+ class T2,
+ class T3,
+ class T4,
+ class T5,
+ class T6>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ (intptr_t)p6,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4, class T5>
+ static inline intptr_t
+ Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4, T5 p5) {
+ return Call(nr,
+ (intptr_t)p0,
+ (intptr_t)p1,
+ (intptr_t)p2,
+ (intptr_t)p3,
+ (intptr_t)p4,
+ (intptr_t)p5,
+ 0,
+ 0);
+ }
+
+ template <class T0, class T1, class T2, class T3, class T4>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3, T4 p4) {
+ return Call(nr, p0, p1, p2, p3, p4, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2, class T3>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2, T3 p3) {
+ return Call(nr, p0, p1, p2, p3, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1, class T2>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1, T2 p2) {
+ return Call(nr, p0, p1, p2, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0, class T1>
+ static inline intptr_t Call(int nr, T0 p0, T1 p1) {
+ return Call(nr, p0, p1, 0, 0, 0, 0, 0, 0);
+ }
+
+ template <class T0>
+ static inline intptr_t Call(int nr, T0 p0) {
+ return Call(nr, p0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ static inline intptr_t Call(int nr) {
+ return Call(nr, 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ // Set the registers in |ctx| to match what they would be after a system call
+ // returning |ret_val|. |ret_val| must follow the Syscall::Call() convention
+ // of being -errno on errors.
+ static void PutValueInUcontext(intptr_t ret_val, ucontext_t* ctx);
+
+ private:
+ // This performs system call |nr| with the arguments p0 to p7 from a constant
+ // userland address, which is for instance observable by seccomp-bpf filters.
+ // The constant userland address from which these system calls are made will
+ // be returned if |nr| is passed as -1.
+ // On error, this function will return a value between -1 and -4095 which
+ // should be interpreted as -errno.
+ static intptr_t Call(int nr,
+ intptr_t p0,
+ intptr_t p1,
+ intptr_t p2,
+ intptr_t p3,
+ intptr_t p4,
+ intptr_t p5,
+ intptr_t p6,
+ intptr_t p7);
+
+#if defined(__mips__)
+ // This function basically does on MIPS what SandboxSyscall() is doing on
+ // other architectures. However, because of specificity of MIPS regarding
+ // handling syscall errors, SandboxSyscall() is made as a wrapper for this
+ // function in order for SandboxSyscall() to behave more like on other
+ // architectures on places where return value from SandboxSyscall() is used
+ // directly (like in most tests).
+ // The syscall "nr" is called with arguments that are set in an array on which
+ // pointer "args" points to and an information weather there is an error or no
+ // is returned to SandboxSyscall() by err_stat.
+ static intptr_t SandboxSyscallRaw(int nr,
+ const intptr_t* args,
+ intptr_t* err_stat);
+#endif // defined(__mips__)
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Syscall);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_SYSCALL_H__
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
new file mode 100644
index 0000000000..2b776d287b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+
+#include <asm/unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
+#include "base/stl_util.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
+#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/tests/unit_tests.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using sandbox::bpf_dsl::Allow;
+using sandbox::bpf_dsl::ResultExpr;
+using sandbox::bpf_dsl::Trap;
+
+namespace sandbox {
+
+namespace {
+
+TEST(Syscall, InvalidCallReturnsENOSYS) {
+ EXPECT_EQ(-ENOSYS, Syscall::InvalidCall());
+}
+
+TEST(Syscall, WellKnownEntryPoint) {
+// Test that Syscall::Call(-1) is handled specially. Don't do this on ARM,
+// where syscall(-1) crashes with SIGILL. Not running the test is fine, as we
+// are still testing ARM code in the next set of tests.
+#if !defined(__arm__) && !defined(__aarch64__)
+ EXPECT_NE(Syscall::Call(-1), syscall(-1));
+#endif
+
+// If possible, test that Syscall::Call(-1) returns the address right
+// after
+// a kernel entry point.
+#if defined(__i386__)
+ EXPECT_EQ(0x80CDu, ((uint16_t*)Syscall::Call(-1))[-1]); // INT 0x80
+#elif defined(__x86_64__)
+ EXPECT_EQ(0x050Fu, ((uint16_t*)Syscall::Call(-1))[-1]); // SYSCALL
+#elif defined(__arm__)
+#if defined(__thumb__)
+ EXPECT_EQ(0xDF00u, ((uint16_t*)Syscall::Call(-1))[-1]); // SWI 0
+#else
+ EXPECT_EQ(0xEF000000u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#endif
+#elif defined(__mips__)
+ // Opcode for MIPS sycall is in the lower 16-bits
+ EXPECT_EQ(0x0cu, (((uint32_t*)Syscall::Call(-1))[-1]) & 0x0000FFFF);
+#elif defined(__aarch64__)
+ EXPECT_EQ(0xD4000001u, ((uint32_t*)Syscall::Call(-1))[-1]); // SVC 0
+#else
+#warning Incomplete test case; need port for target platform
+#endif
+}
+
+TEST(Syscall, TrivialSyscallNoArgs) {
+ // Test that we can do basic system calls
+ EXPECT_EQ(Syscall::Call(__NR_getpid), syscall(__NR_getpid));
+}
+
+TEST(Syscall, TrivialSyscallOneArg) {
+ int new_fd;
+ // Duplicate standard error and close it.
+ ASSERT_GE(new_fd = Syscall::Call(__NR_dup, 2), 0);
+ int close_return_value = IGNORE_EINTR(Syscall::Call(__NR_close, new_fd));
+ ASSERT_EQ(close_return_value, 0);
+}
+
+TEST(Syscall, TrivialFailingSyscall) {
+ errno = -42;
+ int ret = Syscall::Call(__NR_dup, -1);
+ ASSERT_EQ(-EBADF, ret);
+ // Verify that Syscall::Call does not touch errno.
+ ASSERT_EQ(-42, errno);
+}
+
+// SIGSYS trap handler that will be called on __NR_uname.
+intptr_t CopySyscallArgsToAux(const struct arch_seccomp_data& args, void* aux) {
+ // |aux| is our BPF_AUX pointer.
+ std::vector<uint64_t>* const seen_syscall_args =
+ static_cast<std::vector<uint64_t>*>(aux);
+ BPF_ASSERT(base::size(args.args) == 6);
+ seen_syscall_args->assign(args.args, args.args + base::size(args.args));
+ return -ENOMEM;
+}
+
+class CopyAllArgsOnUnamePolicy : public bpf_dsl::Policy {
+ public:
+ explicit CopyAllArgsOnUnamePolicy(std::vector<uint64_t>* aux) : aux_(aux) {}
+ ~CopyAllArgsOnUnamePolicy() override {}
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ DCHECK(SandboxBPF::IsValidSyscallNumber(sysno));
+ if (sysno == __NR_uname) {
+ return Trap(CopySyscallArgsToAux, aux_);
+ } else {
+ return Allow();
+ }
+ }
+
+ private:
+ std::vector<uint64_t>* aux_;
+
+ DISALLOW_COPY_AND_ASSIGN(CopyAllArgsOnUnamePolicy);
+};
+
+// We are testing Syscall::Call() by making use of a BPF filter that
+// allows us
+// to inspect the system call arguments that the kernel saw.
+BPF_TEST(Syscall,
+ SyntheticSixArgs,
+ CopyAllArgsOnUnamePolicy,
+ std::vector<uint64_t> /* (*BPF_AUX) */) {
+ const int kExpectedValue = 42;
+ // In this test we only pass integers to the kernel. We might want to make
+ // additional tests to try other types. What we will see depends on
+ // implementation details of kernel BPF filters and we will need to document
+ // the expected behavior very clearly.
+ int syscall_args[6];
+ for (size_t i = 0; i < base::size(syscall_args); ++i) {
+ syscall_args[i] = kExpectedValue + i;
+ }
+
+ // We could use pretty much any system call we don't need here. uname() is
+ // nice because it doesn't have any dangerous side effects.
+ BPF_ASSERT(Syscall::Call(__NR_uname,
+ syscall_args[0],
+ syscall_args[1],
+ syscall_args[2],
+ syscall_args[3],
+ syscall_args[4],
+ syscall_args[5]) == -ENOMEM);
+
+ // We expect the trap handler to have copied the 6 arguments.
+ BPF_ASSERT(BPF_AUX->size() == 6);
+
+ // Don't loop here so that we can see which argument does cause the failure
+ // easily from the failing line.
+ // uint64_t is the type passed to our SIGSYS handler.
+ BPF_ASSERT((*BPF_AUX)[0] == static_cast<uint64_t>(syscall_args[0]));
+ BPF_ASSERT((*BPF_AUX)[1] == static_cast<uint64_t>(syscall_args[1]));
+ BPF_ASSERT((*BPF_AUX)[2] == static_cast<uint64_t>(syscall_args[2]));
+ BPF_ASSERT((*BPF_AUX)[3] == static_cast<uint64_t>(syscall_args[3]));
+ BPF_ASSERT((*BPF_AUX)[4] == static_cast<uint64_t>(syscall_args[4]));
+ BPF_ASSERT((*BPF_AUX)[5] == static_cast<uint64_t>(syscall_args[5]));
+}
+
+TEST(Syscall, ComplexSyscallSixArgs) {
+ int fd;
+ const size_t kPageSize = base::GetPageSize();
+
+ ASSERT_LE(0,
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/dev/null", O_RDWR, 0L));
+
+ // Use mmap() to allocate some read-only memory
+ char* addr0;
+ ASSERT_NE(
+ (char*)NULL,
+ addr0 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ kPageSize,
+ PROT_READ,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ fd,
+ 0L)));
+
+ // Try to replace the existing mapping with a read-write mapping
+ char* addr1;
+ ASSERT_EQ(addr0,
+ addr1 = reinterpret_cast<char*>(
+ Syscall::Call(kMMapNr,
+ addr0,
+ kPageSize,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
+ fd,
+ 0L)));
+ ++*addr1; // This should not seg fault
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, kPageSize));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+
+ // Check that the offset argument (i.e. the sixth argument) is processed
+ // correctly.
+ ASSERT_GE(
+ fd = Syscall::Call(__NR_openat, AT_FDCWD, "/proc/self/exe", O_RDONLY, 0L),
+ 0);
+ char* addr2, *addr3;
+ ASSERT_NE((char*)NULL,
+ addr2 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 2 * kPageSize,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ 0L
+ )));
+ ASSERT_NE((char*)NULL,
+ addr3 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ kPageSize,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+#if defined(__NR_mmap2)
+ 1L
+#else
+ kPageSize
+#endif
+ )));
+ EXPECT_EQ(0, memcmp(addr2 + kPageSize, addr3, kPageSize));
+
+ // Just to be absolutely on the safe side, also verify that the file
+ // contents matches what we are getting from a read() operation.
+ char buf[2 * kPageSize];
+ EXPECT_EQ(2 * kPageSize, static_cast<size_t>(Syscall::Call(__NR_read,
+ fd,
+ buf,
+ 2 * kPageSize
+ )));
+ EXPECT_EQ(0, memcmp(addr2, buf, 2 * kPageSize));
+
+ // Clean up
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 2 * kPageSize));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, kPageSize));
+ EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
+}
+
+} // namespace
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc
new file mode 100644
index 0000000000..9884be8bb2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.cc
@@ -0,0 +1,394 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/seccomp-bpf/trap.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/syscall.h>
+
+#include <algorithm>
+#include <limits>
+#include <tuple>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#include "sandbox/linux/seccomp-bpf/die.h"
+#include "sandbox/linux/seccomp-bpf/syscall.h"
+#include "sandbox/linux/services/syscall_wrappers.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+
+namespace {
+
+struct arch_sigsys {
+ void* ip;
+ int nr;
+ unsigned int arch;
+};
+
+const int kCapacityIncrement = 20;
+
+// Unsafe traps can only be turned on, if the user explicitly allowed them
+// by setting the CHROME_SANDBOX_DEBUGGING environment variable.
+const char kSandboxDebuggingEnv[] = "CHROME_SANDBOX_DEBUGGING";
+
+// We need to tell whether we are performing a "normal" callback, or
+// whether we were called recursively from within a UnsafeTrap() callback.
+// This is a little tricky to do, because we need to somehow get access to
+// per-thread data from within a signal context. Normal TLS storage is not
+// safely accessible at this time. We could roll our own, but that involves
+// a lot of complexity. Instead, we co-opt one bit in the signal mask.
+// If BUS is blocked, we assume that we have been called recursively.
+// There is a possibility for collision with other code that needs to do
+// this, but in practice the risks are low.
+// If SIGBUS turns out to be a problem, we could instead co-opt one of the
+// realtime signals. There are plenty of them. Unfortunately, there is no
+// way to mark a signal as allocated. So, the potential for collision is
+// possibly even worse.
+bool GetIsInSigHandler(const ucontext_t* ctx) {
+ // Note: on Android, sigismember does not take a pointer to const.
+ return sigismember(const_cast<sigset_t*>(&ctx->uc_sigmask), LINUX_SIGBUS);
+}
+
+void SetIsInSigHandler() {
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGBUS) ||
+ sandbox::sys_sigprocmask(LINUX_SIG_BLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to block SIGBUS");
+ }
+}
+
+bool IsDefaultSignalAction(const struct sigaction& sa) {
+ if (sa.sa_flags & SA_SIGINFO || sa.sa_handler != SIG_DFL) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+Trap::Trap()
+ : trap_array_(NULL),
+ trap_array_size_(0),
+ trap_array_capacity_(0),
+ has_unsafe_traps_(false) {
+ // Set new SIGSYS handler
+ struct sigaction sa = {};
+ // In some toolchain, sa_sigaction is not declared in struct sigaction.
+ // So, here cast the pointer to the sa_handler's type. This works because
+ // |sa_handler| and |sa_sigaction| shares the same memory.
+ sa.sa_handler = reinterpret_cast<void (*)(int)>(SigSysAction);
+ sa.sa_flags = LINUX_SA_SIGINFO | LINUX_SA_NODEFER;
+ struct sigaction old_sa = {};
+ if (sys_sigaction(LINUX_SIGSYS, &sa, &old_sa) < 0) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+
+ if (!IsDefaultSignalAction(old_sa)) {
+ static const char kExistingSIGSYSMsg[] =
+ "Existing signal handler when trying to install SIGSYS. SIGSYS needs "
+ "to be reserved for seccomp-bpf.";
+ DLOG(FATAL) << kExistingSIGSYSMsg;
+ LOG(ERROR) << kExistingSIGSYSMsg;
+ }
+
+ // Unmask SIGSYS
+ sigset_t mask;
+ if (sigemptyset(&mask) || sigaddset(&mask, LINUX_SIGSYS) ||
+ sys_sigprocmask(LINUX_SIG_UNBLOCK, &mask, NULL)) {
+ SANDBOX_DIE("Failed to configure SIGSYS handler");
+ }
+}
+
+bpf_dsl::TrapRegistry* Trap::Registry() {
+ // Note: This class is not thread safe. It is the caller's responsibility
+ // to avoid race conditions. Normally, this is a non-issue as the sandbox
+ // can only be initialized if there are no other threads present.
+ // Also, this is not a normal singleton. Once created, the global trap
+ // object must never be destroyed again.
+ if (!global_trap_) {
+ global_trap_ = new Trap();
+ if (!global_trap_) {
+ SANDBOX_DIE("Failed to allocate global trap handler");
+ }
+ }
+ return global_trap_;
+}
+
+void Trap::SigSysAction(int nr, LinuxSigInfo* info, void* void_context) {
+ if (info) {
+ MSAN_UNPOISON(info, sizeof(*info));
+ }
+
+ // Obtain the signal context. This, most notably, gives us access to
+ // all CPU registers at the time of the signal.
+ ucontext_t* ctx = reinterpret_cast<ucontext_t*>(void_context);
+ if (ctx) {
+ MSAN_UNPOISON(ctx, sizeof(*ctx));
+ }
+
+ if (!global_trap_) {
+ RAW_SANDBOX_DIE(
+ "This can't happen. Found no global singleton instance "
+ "for Trap() handling.");
+ }
+ global_trap_->SigSys(nr, info, ctx);
+}
+
+void Trap::SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx) {
+ // Signal handlers should always preserve "errno". Otherwise, we could
+ // trigger really subtle bugs.
+ const int old_errno = errno;
+
+ // Various sanity checks to make sure we actually received a signal
+ // triggered by a BPF filter. If something else triggered SIGSYS
+ // (e.g. kill()), there is really nothing we can do with this signal.
+ if (nr != LINUX_SIGSYS || info->si_code != SYS_SECCOMP || !ctx ||
+ info->si_errno <= 0 ||
+ static_cast<size_t>(info->si_errno) > trap_array_size_) {
+ // ATI drivers seem to send SIGSYS, so this cannot be FATAL.
+ // See crbug.com/178166.
+ // TODO(jln): add a DCHECK or move back to FATAL.
+ RAW_LOG(ERROR, "Unexpected SIGSYS received.");
+ errno = old_errno;
+ return;
+ }
+
+
+ // Obtain the siginfo information that is specific to SIGSYS.
+ struct arch_sigsys sigsys;
+#if defined(si_call_addr) && !defined(__native_client_nonsfi__)
+ sigsys.ip = info->si_call_addr;
+ sigsys.nr = info->si_syscall;
+ sigsys.arch = info->si_arch;
+#else
+ // If the version of glibc doesn't include this information in
+ // siginfo_t (older than 2.17), we need to explicitly copy it
+ // into an arch_sigsys structure.
+ memcpy(&sigsys, &info->_sifields, sizeof(sigsys));
+#endif
+
+#if defined(__mips__)
+ // When indirect syscall (syscall(__NR_foo, ...)) is made on Mips, the
+ // number in register SECCOMP_SYSCALL(ctx) is always __NR_syscall and the
+ // real number of a syscall (__NR_foo) is in SECCOMP_PARM1(ctx)
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx)) &&
+ sigsys.nr != static_cast<int>(SECCOMP_PARM1(ctx));
+#else
+ bool sigsys_nr_is_bad = sigsys.nr != static_cast<int>(SECCOMP_SYSCALL(ctx));
+#endif
+
+ // Some more sanity checks.
+ if (sigsys.ip != reinterpret_cast<void*>(SECCOMP_IP(ctx)) ||
+ sigsys_nr_is_bad || sigsys.arch != SECCOMP_ARCH) {
+ // TODO(markus):
+ // SANDBOX_DIE() can call LOG(FATAL). This is not normally async-signal
+ // safe and can lead to bugs. We should eventually implement a different
+ // logging and reporting mechanism that is safe to be called from
+ // the sigSys() handler.
+ RAW_SANDBOX_DIE("Sanity checks are failing after receiving SIGSYS.");
+ }
+
+ intptr_t rc;
+ if (has_unsafe_traps_ && GetIsInSigHandler(ctx)) {
+ errno = old_errno;
+ if (sigsys.nr == __NR_clone) {
+ RAW_SANDBOX_DIE("Cannot call clone() from an UnsafeTrap() handler.");
+ }
+#if defined(__mips__)
+ // Mips supports up to eight arguments for syscall.
+ // However, seccomp bpf can filter only up to six arguments, so using eight
+ // arguments has sense only when using UnsafeTrap() handler.
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx),
+ SECCOMP_PARM7(ctx),
+ SECCOMP_PARM8(ctx));
+#else
+ rc = Syscall::Call(SECCOMP_SYSCALL(ctx),
+ SECCOMP_PARM1(ctx),
+ SECCOMP_PARM2(ctx),
+ SECCOMP_PARM3(ctx),
+ SECCOMP_PARM4(ctx),
+ SECCOMP_PARM5(ctx),
+ SECCOMP_PARM6(ctx));
+#endif // defined(__mips__)
+ } else {
+ const TrapKey& trap = trap_array_[info->si_errno - 1];
+ if (!trap.safe) {
+ SetIsInSigHandler();
+ }
+
+ // Copy the seccomp-specific data into a arch_seccomp_data structure. This
+ // is what we are showing to TrapFnc callbacks that the system call
+ // evaluator registered with the sandbox.
+ struct arch_seccomp_data data = {
+ static_cast<int>(SECCOMP_SYSCALL(ctx)),
+ SECCOMP_ARCH,
+ reinterpret_cast<uint64_t>(sigsys.ip),
+ {static_cast<uint64_t>(SECCOMP_PARM1(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM2(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM3(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM4(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM5(ctx)),
+ static_cast<uint64_t>(SECCOMP_PARM6(ctx))}};
+
+ // Now call the TrapFnc callback associated with this particular instance
+ // of SECCOMP_RET_TRAP.
+ rc = trap.fnc(data, const_cast<void*>(trap.aux));
+ }
+
+ // Update the CPU register that stores the return code of the system call
+ // that we just handled, and restore "errno" to the value that it had
+ // before entering the signal handler.
+ Syscall::PutValueInUcontext(rc, ctx);
+ errno = old_errno;
+
+ return;
+}
+
+bool Trap::TrapKey::operator<(const TrapKey& o) const {
+ 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) {
+ if (!safe && !SandboxDebuggingAllowedByUser()) {
+ // Unless the user set the CHROME_SANDBOX_DEBUGGING environment variable,
+ // we never return an ErrorCode that is marked as "unsafe". This also
+ // means, the BPF compiler will never emit code that allow unsafe system
+ // calls to by-pass the filter (because they use the magic return address
+ // from Syscall::Call(-1)).
+
+ // This SANDBOX_DIE() can optionally be removed. It won't break security,
+ // but it might make error messages from the BPF compiler a little harder
+ // to understand. Removing the SANDBOX_DIE() allows callers to easily check
+ // whether unsafe traps are supported (by checking whether the returned
+ // ErrorCode is ET_INVALID).
+ SANDBOX_DIE(
+ "Cannot use unsafe traps unless CHROME_SANDBOX_DEBUGGING "
+ "is enabled");
+
+ return 0;
+ }
+
+ // Each unique pair of TrapFnc and auxiliary data make up a distinct instance
+ // of a SECCOMP_RET_TRAP.
+ TrapKey key(fnc, aux, safe);
+
+ // We return unique identifiers together with SECCOMP_RET_TRAP. This allows
+ // us to associate trap with the appropriate handler. The kernel allows us
+ // identifiers in the range from 0 to SECCOMP_RET_DATA (0xFFFF). We want to
+ // avoid 0, as it could be confused for a trap without any specific id.
+ // The nice thing about sequentially numbered identifiers is that we can also
+ // trivially look them up from our signal handler without making any system
+ // calls that might be async-signal-unsafe.
+ // In order to do so, we store all of our traps in a C-style trap_array_.
+
+ TrapIds::const_iterator iter = trap_ids_.find(key);
+ if (iter != trap_ids_.end()) {
+ // We have seen this pair before. Return the same id that we assigned
+ // earlier.
+ return iter->second;
+ }
+
+ // This is a new pair. Remember it and assign a new id.
+ if (trap_array_size_ >= SECCOMP_RET_DATA /* 0xFFFF */ ||
+ trap_array_size_ >= std::numeric_limits<uint16_t>::max()) {
+ // In practice, this is pretty much impossible to trigger, as there
+ // are other kernel limitations that restrict overall BPF program sizes.
+ SANDBOX_DIE("Too many SECCOMP_RET_TRAP callback instances");
+ }
+
+ // Our callers ensure that there are no other threads accessing trap_array_
+ // concurrently (typically this is done by ensuring that we are single-
+ // threaded while the sandbox is being set up). But we nonetheless are
+ // modifying a live data structure that could be accessed any time a
+ // system call is made; as system calls could be triggering SIGSYS.
+ // So, we have to be extra careful that we update trap_array_ atomically.
+ // In particular, this means we shouldn't be using realloc() to resize it.
+ // Instead, we allocate a new array, copy the values, and then switch the
+ // pointer. We only really care about the pointer being updated atomically
+ // and the data that is pointed to being valid, as these are the only
+ // values accessed from the signal handler. It is OK if trap_array_size_
+ // is inconsistent with the pointer, as it is monotonously increasing.
+ // Also, we only care about compiler barriers, as the signal handler is
+ // triggered synchronously from a system call. We don't have to protect
+ // against issues with the memory model or with completely asynchronous
+ // events.
+ if (trap_array_size_ >= trap_array_capacity_) {
+ trap_array_capacity_ += kCapacityIncrement;
+ TrapKey* old_trap_array = trap_array_;
+ TrapKey* new_trap_array = new TrapKey[trap_array_capacity_];
+ std::copy_n(old_trap_array, trap_array_size_, new_trap_array);
+
+ // Language specs are unclear on whether the compiler is allowed to move
+ // the "delete[]" above our preceding assignments and/or memory moves,
+ // iff the compiler believes that "delete[]" doesn't have any other
+ // global side-effects.
+ // We insert optimization barriers to prevent this from happening.
+ // The first barrier is probably not needed, but better be explicit in
+ // what we want to tell the compiler.
+ // The clang developer mailing list couldn't answer whether this is a
+ // legitimate worry; but they at least thought that the barrier is
+ // sufficient to prevent the (so far hypothetical) problem of re-ordering
+ // of instructions by the compiler.
+ //
+ // TODO(mdempsky): Try to clean this up using base/atomicops or C++11
+ // atomics; see crbug.com/414363.
+ asm volatile("" : "=r"(new_trap_array) : "0"(new_trap_array) : "memory");
+ trap_array_ = new_trap_array;
+ asm volatile("" : "=r"(trap_array_) : "0"(trap_array_) : "memory");
+
+ delete[] old_trap_array;
+ }
+
+ uint16_t id = trap_array_size_ + 1;
+ trap_ids_[key] = id;
+ trap_array_[trap_array_size_] = key;
+ trap_array_size_++;
+ return id;
+}
+
+bool Trap::SandboxDebuggingAllowedByUser() {
+ const char* debug_flag = getenv(kSandboxDebuggingEnv);
+ return debug_flag && *debug_flag;
+}
+
+bool Trap::EnableUnsafeTraps() {
+ if (!has_unsafe_traps_) {
+ // Unsafe traps are a one-way fuse. Once enabled, they can never be turned
+ // off again.
+ // We only allow enabling unsafe traps, if the user explicitly set an
+ // appropriate environment variable. This prevents bugs that accidentally
+ // disable all sandboxing for all users.
+ if (SandboxDebuggingAllowedByUser()) {
+ // We only ever print this message once, when we enable unsafe traps the
+ // first time.
+ SANDBOX_INFO("WARNING! Disabling sandbox for debugging purposes");
+ has_unsafe_traps_ = true;
+ } else {
+ SANDBOX_INFO(
+ "Cannot disable sandbox and use unsafe traps unless "
+ "CHROME_SANDBOX_DEBUGGING is turned on first");
+ }
+ }
+ // Returns the, possibly updated, value of has_unsafe_traps_.
+ return has_unsafe_traps_;
+}
+
+Trap* Trap::global_trap_;
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h
new file mode 100644
index 0000000000..a73d2064b4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/seccomp-bpf/trap.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+#define SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+
+#include "base/macros.h"
+#include "sandbox/linux/bpf_dsl/trap_registry.h"
+#include "sandbox/linux/system_headers/linux_signal.h"
+#include "sandbox/sandbox_export.h"
+
+namespace sandbox {
+
+// The Trap class allows a BPF filter program to branch out to user space by
+// raising a SIGSYS signal.
+// N.B.: This class does not perform any synchronization operations. If
+// modifications are made to any of the traps, it is the caller's
+// responsibility to ensure that this happens in a thread-safe fashion.
+// Preferably, that means that no other threads should be running at that
+// time. For the purposes of our sandbox, this assertion should always be
+// true. Threads are incompatible with the seccomp sandbox anyway.
+class SANDBOX_EXPORT Trap : public bpf_dsl::TrapRegistry {
+ public:
+ uint16_t Add(TrapFnc fnc, const void* aux, bool safe) override;
+
+ bool EnableUnsafeTraps() override;
+
+ // Registry returns the trap registry used by Trap's SIGSYS handler,
+ // creating it if necessary.
+ static bpf_dsl::TrapRegistry* Registry();
+
+ // SandboxDebuggingAllowedByUser returns whether the
+ // "CHROME_SANDBOX_DEBUGGING" environment variable is set.
+ static bool SandboxDebuggingAllowedByUser();
+
+ private:
+ struct TrapKey {
+ TrapKey() : fnc(NULL), aux(NULL), safe(false) {}
+ TrapKey(TrapFnc f, const void* a, bool s) : fnc(f), aux(a), safe(s) {}
+ TrapFnc fnc;
+ const void* aux;
+ bool safe;
+ bool operator<(const TrapKey&) const;
+ };
+ typedef std::map<TrapKey, uint16_t> TrapIds;
+
+ // Our constructor is private. A shared global instance is created
+ // automatically as needed.
+ Trap();
+
+ // The destructor is unimplemented as destroying this object would
+ // break subsequent system calls that trigger a SIGSYS.
+ ~Trap() = delete;
+
+ static void SigSysAction(int nr, LinuxSigInfo* info, void* void_context);
+
+ // Make sure that SigSys is not inlined in order to get slightly better crash
+ // dumps.
+ void SigSys(int nr, LinuxSigInfo* info, ucontext_t* ctx)
+ __attribute__((noinline));
+ // We have a global singleton that handles all of our SIGSYS traps. This
+ // variable must never be deallocated after it has been set up initially, as
+ // there is no way to reset in-kernel BPF filters that generate SIGSYS
+ // events.
+ static Trap* global_trap_;
+
+ TrapIds trap_ids_; // Maps from TrapKeys to numeric ids
+ TrapKey* trap_array_; // Array of TrapKeys indexed by ids
+ size_t trap_array_size_; // Currently used size of array
+ size_t trap_array_capacity_; // Currently allocated capacity of array
+ bool has_unsafe_traps_; // Whether unsafe traps have been enabled
+
+ // Copying and assigning is unimplemented. It doesn't make sense for a
+ // singleton.
+ DISALLOW_COPY_AND_ASSIGN(Trap);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SECCOMP_BPF_TRAP_H__
diff --git a/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc
new file mode 100644
index 0000000000..fcfd2aa129
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.cc
@@ -0,0 +1,264 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/linux/services/syscall_wrappers.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <cstring>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "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);
+}
+
+ssize_t sys_write(int fd, const char* buffer, size_t buffer_size) {
+ return syscall(__NR_write, fd, buffer, buffer_size);
+}
+
+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)
+ // 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 0000000000..1975bfbd88
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/services/syscall_wrappers.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+#define SANDBOX_LINUX_SERVICES_SYSCALL_WRAPPERS_H_
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cstddef>
+
+#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 ssize_t sys_write(int fd,
+ const char* buffer,
+ size_t buffer_size);
+
+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/system_headers/arm64_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h
new file mode 100644
index 0000000000..2224d32438
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/arm64_linux_syscalls.h
@@ -0,0 +1,1197 @@
+// 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.
+
+/* Constructed by running:
+ * curl -vsSL https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/asm-generic/unistd.h?h=v5.8
+ * | gcc -D__BITS_PER_LONG=64 -D__ARCH_WANT_STAT64 -D__ARCH_WANT_SET_GET_RLIMIT -D__ARCH_WANT_SYS_CLONE3 -D__ARCH_WANT_RENAMEAT -E -dD -
+ * | grep __NR | grep -vE '__NR_arch_specific_syscall|__NR_syscalls' | sort -n -k 3 | sed -e 's/__NR3264/__NR/g'
+ * | awk '{ if ($2 != $3) { print "#if !defined(" $2 ")\n#define " $2 " " $3 "\n#endif\n"; } }
+ * */
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
+
+#include <asm-generic/unistd.h>
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 0
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat __NR_fstatat
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 1
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 2
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 3
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 4
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 5
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 6
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 7
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 8
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 9
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 10
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 11
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 12
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 13
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 14
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 15
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 16
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 17
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 18
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 19
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 20
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 21
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 22
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 23
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 24
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 25
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 26
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 27
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 28
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 29
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 30
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 31
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 32
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 33
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 34
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 35
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 36
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 37
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 38
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 39
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 40
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 41
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 42
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 43
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 44
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 45
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 46
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 47
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 48
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 49
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 50
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 51
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 52
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 53
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 54
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 55
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 56
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 57
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 58
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 59
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 60
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 61
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 62
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 63
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 64
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 65
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 66
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 67
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 68
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 69
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 70
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 71
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 72
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 73
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 74
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 75
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 76
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 77
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 78
+#endif
+
+#if !defined(__NR_fstatat)
+#define __NR_fstatat 79
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 80
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 81
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 82
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 83
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 84
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 85
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 86
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 87
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 88
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 89
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 90
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 91
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 92
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 93
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 94
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 95
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 96
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 97
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 98
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 99
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 100
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 101
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 102
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 103
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 104
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 105
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 106
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 107
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 108
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 109
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 110
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 111
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 112
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 113
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 114
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 115
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 116
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 117
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 118
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 119
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 120
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 121
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 122
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 123
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 124
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 125
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 126
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 127
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 128
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 129
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 130
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 131
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 132
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 133
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 134
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 135
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 136
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 137
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 138
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 139
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 140
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 141
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 142
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 143
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 144
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 145
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 146
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 147
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 148
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 149
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 150
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 151
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 152
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 153
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 154
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 155
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 156
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 157
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 158
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 159
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 160
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 161
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 162
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 163
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 164
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 165
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 166
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 167
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 168
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 169
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 170
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 171
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 172
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 173
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 174
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 175
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 176
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 177
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 178
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 179
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 180
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 181
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 182
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 183
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 184
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 185
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 186
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 187
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 188
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 189
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 190
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 191
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop 192
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop 193
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 194
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 195
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 196
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 197
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 198
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 199
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 200
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 201
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept 202
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 203
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 204
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 205
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 206
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 207
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 208
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 209
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 210
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 211
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 212
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 213
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 214
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 215
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 216
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 217
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 218
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 219
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 220
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 221
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 222
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 223
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 224
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 225
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 226
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 227
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 228
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 229
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 230
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 231
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 232
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 233
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 234
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 235
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 236
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 237
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 238
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 239
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 240
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 241
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 242
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 243
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 260
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 261
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 262
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 263
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 264
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 265
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 266
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 267
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 268
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 269
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 270
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 271
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 272
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 273
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 274
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 275
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 276
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 277
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 278
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 279
+#endif
+
+#if !defined(__NR_bpf)
+#define __NR_bpf 280
+#endif
+
+#if !defined(__NR_execveat)
+#define __NR_execveat 281
+#endif
+
+#if !defined(__NR_userfaultfd)
+#define __NR_userfaultfd 282
+#endif
+
+#if !defined(__NR_membarrier)
+#define __NR_membarrier 283
+#endif
+
+#if !defined(__NR_mlock2)
+#define __NR_mlock2 284
+#endif
+
+#if !defined(__NR_copy_file_range)
+#define __NR_copy_file_range 285
+#endif
+
+#if !defined(__NR_preadv2)
+#define __NR_preadv2 286
+#endif
+
+#if !defined(__NR_pwritev2)
+#define __NR_pwritev2 287
+#endif
+
+#if !defined(__NR_pkey_mprotect)
+#define __NR_pkey_mprotect 288
+#endif
+
+#if !defined(__NR_pkey_alloc)
+#define __NR_pkey_alloc 289
+#endif
+
+#if !defined(__NR_pkey_free)
+#define __NR_pkey_free 290
+#endif
+
+#if !defined(__NR_statx)
+#define __NR_statx 291
+#endif
+
+#if !defined(__NR_io_pgetevents)
+#define __NR_io_pgetevents 292
+#endif
+
+#if !defined(__NR_rseq)
+#define __NR_rseq 293
+#endif
+
+#if !defined(__NR_kexec_file_load)
+#define __NR_kexec_file_load 294
+#endif
+
+#if !defined(__NR_pidfd_send_signal)
+#define __NR_pidfd_send_signal 424
+#endif
+
+#if !defined(__NR_io_uring_setup)
+#define __NR_io_uring_setup 425
+#endif
+
+#if !defined(__NR_io_uring_enter)
+#define __NR_io_uring_enter 426
+#endif
+
+#if !defined(__NR_io_uring_register)
+#define __NR_io_uring_register 427
+#endif
+
+#if !defined(__NR_open_tree)
+#define __NR_open_tree 428
+#endif
+
+#if !defined(__NR_move_mount)
+#define __NR_move_mount 429
+#endif
+
+#if !defined(__NR_fsopen)
+#define __NR_fsopen 430
+#endif
+
+#if !defined(__NR_fsconfig)
+#define __NR_fsconfig 431
+#endif
+
+#if !defined(__NR_fsmount)
+#define __NR_fsmount 432
+#endif
+
+#if !defined(__NR_fspick)
+#define __NR_fspick 433
+#endif
+
+#if !defined(__NR_pidfd_open)
+#define __NR_pidfd_open 434
+#endif
+
+#if !defined(__NR_clone3)
+#define __NR_clone3 435
+#endif
+
+#if !defined(__NR_openat2)
+#define __NR_openat2 437
+#endif
+
+#if !defined(__NR_pidfd_getfd)
+#define __NR_pidfd_getfd 438
+#endif
+
+#if !defined(__NR_faccessat2)
+#define __NR_faccessat2 439
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM64_LINUX_SYSCALLS_H_
diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h
new file mode 100644
index 0000000000..5b7f4e511a
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_syscalls.h
@@ -0,0 +1,1623 @@
+// 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.
+
+/* Constructed by running:
+ * curl -vsSL https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/arch/arm/tools/syscall.tbl?h=v5.8
+ * | grep -vE '^#|^$'
+ * | awk '{ if ($2 != "oabi") { print "#if !defined(__NR_" $3 ")\n#define __NR_" $3 " (__NR_SYSCALL_BASE+" $1 ")\n#endif\n"; } }'
+ * */
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_
+
+#if !defined(__arm__) || !defined(__ARM_EABI__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_SYSCALL_BASE)
+// On ARM EABI arch, __NR_SYSCALL_BASE is 0.
+#define __NR_SYSCALL_BASE 0
+#endif
+
+// This syscall list has holes, because ARM EABI makes some syscalls obsolete.
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall (__NR_SYSCALL_BASE+0)
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit (__NR_SYSCALL_BASE+1)
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork (__NR_SYSCALL_BASE+2)
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read (__NR_SYSCALL_BASE+3)
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write (__NR_SYSCALL_BASE+4)
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open (__NR_SYSCALL_BASE+5)
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close (__NR_SYSCALL_BASE+6)
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat (__NR_SYSCALL_BASE+8)
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link (__NR_SYSCALL_BASE+9)
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink (__NR_SYSCALL_BASE+10)
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve (__NR_SYSCALL_BASE+11)
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir (__NR_SYSCALL_BASE+12)
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod (__NR_SYSCALL_BASE+14)
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod (__NR_SYSCALL_BASE+15)
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown (__NR_SYSCALL_BASE+16)
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek (__NR_SYSCALL_BASE+19)
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid (__NR_SYSCALL_BASE+20)
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount (__NR_SYSCALL_BASE+21)
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid (__NR_SYSCALL_BASE+23)
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid (__NR_SYSCALL_BASE+24)
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace (__NR_SYSCALL_BASE+26)
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause (__NR_SYSCALL_BASE+29)
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access (__NR_SYSCALL_BASE+33)
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice (__NR_SYSCALL_BASE+34)
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync (__NR_SYSCALL_BASE+36)
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill (__NR_SYSCALL_BASE+37)
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename (__NR_SYSCALL_BASE+38)
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir (__NR_SYSCALL_BASE+39)
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir (__NR_SYSCALL_BASE+40)
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup (__NR_SYSCALL_BASE+41)
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe (__NR_SYSCALL_BASE+42)
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times (__NR_SYSCALL_BASE+43)
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk (__NR_SYSCALL_BASE+45)
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid (__NR_SYSCALL_BASE+46)
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid (__NR_SYSCALL_BASE+47)
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid (__NR_SYSCALL_BASE+49)
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid (__NR_SYSCALL_BASE+50)
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct (__NR_SYSCALL_BASE+51)
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 (__NR_SYSCALL_BASE+52)
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl (__NR_SYSCALL_BASE+54)
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl (__NR_SYSCALL_BASE+55)
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid (__NR_SYSCALL_BASE+57)
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask (__NR_SYSCALL_BASE+60)
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot (__NR_SYSCALL_BASE+61)
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat (__NR_SYSCALL_BASE+62)
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 (__NR_SYSCALL_BASE+63)
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid (__NR_SYSCALL_BASE+64)
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp (__NR_SYSCALL_BASE+65)
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid (__NR_SYSCALL_BASE+66)
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction (__NR_SYSCALL_BASE+67)
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid (__NR_SYSCALL_BASE+70)
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid (__NR_SYSCALL_BASE+71)
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend (__NR_SYSCALL_BASE+72)
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending (__NR_SYSCALL_BASE+73)
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname (__NR_SYSCALL_BASE+74)
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit (__NR_SYSCALL_BASE+75)
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage (__NR_SYSCALL_BASE+77)
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday (__NR_SYSCALL_BASE+78)
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday (__NR_SYSCALL_BASE+79)
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups (__NR_SYSCALL_BASE+80)
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups (__NR_SYSCALL_BASE+81)
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink (__NR_SYSCALL_BASE+83)
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink (__NR_SYSCALL_BASE+85)
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib (__NR_SYSCALL_BASE+86)
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon (__NR_SYSCALL_BASE+87)
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot (__NR_SYSCALL_BASE+88)
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap (__NR_SYSCALL_BASE+91)
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate (__NR_SYSCALL_BASE+92)
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate (__NR_SYSCALL_BASE+93)
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod (__NR_SYSCALL_BASE+94)
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown (__NR_SYSCALL_BASE+95)
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority (__NR_SYSCALL_BASE+96)
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority (__NR_SYSCALL_BASE+97)
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs (__NR_SYSCALL_BASE+99)
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs (__NR_SYSCALL_BASE+100)
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog (__NR_SYSCALL_BASE+103)
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer (__NR_SYSCALL_BASE+104)
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer (__NR_SYSCALL_BASE+105)
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat (__NR_SYSCALL_BASE+106)
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat (__NR_SYSCALL_BASE+107)
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat (__NR_SYSCALL_BASE+108)
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup (__NR_SYSCALL_BASE+111)
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 (__NR_SYSCALL_BASE+114)
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff (__NR_SYSCALL_BASE+115)
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo (__NR_SYSCALL_BASE+116)
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync (__NR_SYSCALL_BASE+118)
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn (__NR_SYSCALL_BASE+119)
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone (__NR_SYSCALL_BASE+120)
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname (__NR_SYSCALL_BASE+121)
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname (__NR_SYSCALL_BASE+122)
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex (__NR_SYSCALL_BASE+124)
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect (__NR_SYSCALL_BASE+125)
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask (__NR_SYSCALL_BASE+126)
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module (__NR_SYSCALL_BASE+128)
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module (__NR_SYSCALL_BASE+129)
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl (__NR_SYSCALL_BASE+131)
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid (__NR_SYSCALL_BASE+132)
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir (__NR_SYSCALL_BASE+133)
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush (__NR_SYSCALL_BASE+134)
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs (__NR_SYSCALL_BASE+135)
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality (__NR_SYSCALL_BASE+136)
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid (__NR_SYSCALL_BASE+138)
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid (__NR_SYSCALL_BASE+139)
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek (__NR_SYSCALL_BASE+140)
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents (__NR_SYSCALL_BASE+141)
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect (__NR_SYSCALL_BASE+142)
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock (__NR_SYSCALL_BASE+143)
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync (__NR_SYSCALL_BASE+144)
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv (__NR_SYSCALL_BASE+145)
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev (__NR_SYSCALL_BASE+146)
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid (__NR_SYSCALL_BASE+147)
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync (__NR_SYSCALL_BASE+148)
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl (__NR_SYSCALL_BASE+149)
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock (__NR_SYSCALL_BASE+150)
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock (__NR_SYSCALL_BASE+151)
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall (__NR_SYSCALL_BASE+152)
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall (__NR_SYSCALL_BASE+153)
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam (__NR_SYSCALL_BASE+154)
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam (__NR_SYSCALL_BASE+155)
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler (__NR_SYSCALL_BASE+156)
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler (__NR_SYSCALL_BASE+157)
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield (__NR_SYSCALL_BASE+158)
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE+159)
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE+160)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE+161)
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep (__NR_SYSCALL_BASE+162)
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap (__NR_SYSCALL_BASE+163)
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid (__NR_SYSCALL_BASE+164)
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid (__NR_SYSCALL_BASE+165)
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll (__NR_SYSCALL_BASE+168)
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl (__NR_SYSCALL_BASE+169)
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid (__NR_SYSCALL_BASE+170)
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid (__NR_SYSCALL_BASE+171)
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl (__NR_SYSCALL_BASE+172)
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn (__NR_SYSCALL_BASE+173)
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction (__NR_SYSCALL_BASE+174)
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE+175)
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending (__NR_SYSCALL_BASE+176)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE+177)
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE+178)
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE+179)
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 (__NR_SYSCALL_BASE+180)
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 (__NR_SYSCALL_BASE+181)
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown (__NR_SYSCALL_BASE+182)
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd (__NR_SYSCALL_BASE+183)
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget (__NR_SYSCALL_BASE+184)
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset (__NR_SYSCALL_BASE+185)
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack (__NR_SYSCALL_BASE+186)
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile (__NR_SYSCALL_BASE+187)
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork (__NR_SYSCALL_BASE+190)
+#endif
+
+#if !defined(__NR_ugetrlimit)
+#define __NR_ugetrlimit (__NR_SYSCALL_BASE+191)
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 (__NR_SYSCALL_BASE+193)
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 (__NR_SYSCALL_BASE+194)
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 (__NR_SYSCALL_BASE+195)
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 (__NR_SYSCALL_BASE+196)
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 (__NR_SYSCALL_BASE+197)
+#endif
+
+#if !defined(__NR_lchown32)
+#define __NR_lchown32 (__NR_SYSCALL_BASE+198)
+#endif
+
+#if !defined(__NR_getuid32)
+#define __NR_getuid32 (__NR_SYSCALL_BASE+199)
+#endif
+
+#if !defined(__NR_getgid32)
+#define __NR_getgid32 (__NR_SYSCALL_BASE+200)
+#endif
+
+#if !defined(__NR_geteuid32)
+#define __NR_geteuid32 (__NR_SYSCALL_BASE+201)
+#endif
+
+#if !defined(__NR_getegid32)
+#define __NR_getegid32 (__NR_SYSCALL_BASE+202)
+#endif
+
+#if !defined(__NR_setreuid32)
+#define __NR_setreuid32 (__NR_SYSCALL_BASE+203)
+#endif
+
+#if !defined(__NR_setregid32)
+#define __NR_setregid32 (__NR_SYSCALL_BASE+204)
+#endif
+
+#if !defined(__NR_getgroups32)
+#define __NR_getgroups32 (__NR_SYSCALL_BASE+205)
+#endif
+
+#if !defined(__NR_setgroups32)
+#define __NR_setgroups32 (__NR_SYSCALL_BASE+206)
+#endif
+
+#if !defined(__NR_fchown32)
+#define __NR_fchown32 (__NR_SYSCALL_BASE+207)
+#endif
+
+#if !defined(__NR_setresuid32)
+#define __NR_setresuid32 (__NR_SYSCALL_BASE+208)
+#endif
+
+#if !defined(__NR_getresuid32)
+#define __NR_getresuid32 (__NR_SYSCALL_BASE+209)
+#endif
+
+#if !defined(__NR_setresgid32)
+#define __NR_setresgid32 (__NR_SYSCALL_BASE+210)
+#endif
+
+#if !defined(__NR_getresgid32)
+#define __NR_getresgid32 (__NR_SYSCALL_BASE+211)
+#endif
+
+#if !defined(__NR_chown32)
+#define __NR_chown32 (__NR_SYSCALL_BASE+212)
+#endif
+
+#if !defined(__NR_setuid32)
+#define __NR_setuid32 (__NR_SYSCALL_BASE+213)
+#endif
+
+#if !defined(__NR_setgid32)
+#define __NR_setgid32 (__NR_SYSCALL_BASE+214)
+#endif
+
+#if !defined(__NR_setfsuid32)
+#define __NR_setfsuid32 (__NR_SYSCALL_BASE+215)
+#endif
+
+#if !defined(__NR_setfsgid32)
+#define __NR_setfsgid32 (__NR_SYSCALL_BASE+216)
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 (__NR_SYSCALL_BASE+217)
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root (__NR_SYSCALL_BASE+218)
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore (__NR_SYSCALL_BASE+219)
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise (__NR_SYSCALL_BASE+220)
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 (__NR_SYSCALL_BASE+221)
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid (__NR_SYSCALL_BASE+224)
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead (__NR_SYSCALL_BASE+225)
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr (__NR_SYSCALL_BASE+226)
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr (__NR_SYSCALL_BASE+227)
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr (__NR_SYSCALL_BASE+228)
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr (__NR_SYSCALL_BASE+229)
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr (__NR_SYSCALL_BASE+230)
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr (__NR_SYSCALL_BASE+231)
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr (__NR_SYSCALL_BASE+232)
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr (__NR_SYSCALL_BASE+233)
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr (__NR_SYSCALL_BASE+234)
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr (__NR_SYSCALL_BASE+235)
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr (__NR_SYSCALL_BASE+236)
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr (__NR_SYSCALL_BASE+237)
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill (__NR_SYSCALL_BASE+238)
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 (__NR_SYSCALL_BASE+239)
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex (__NR_SYSCALL_BASE+240)
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity (__NR_SYSCALL_BASE+241)
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity (__NR_SYSCALL_BASE+242)
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup (__NR_SYSCALL_BASE+243)
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy (__NR_SYSCALL_BASE+244)
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents (__NR_SYSCALL_BASE+245)
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit (__NR_SYSCALL_BASE+246)
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel (__NR_SYSCALL_BASE+247)
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group (__NR_SYSCALL_BASE+248)
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie (__NR_SYSCALL_BASE+249)
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create (__NR_SYSCALL_BASE+250)
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl (__NR_SYSCALL_BASE+251)
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait (__NR_SYSCALL_BASE+252)
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages (__NR_SYSCALL_BASE+253)
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address (__NR_SYSCALL_BASE+256)
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create (__NR_SYSCALL_BASE+257)
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime (__NR_SYSCALL_BASE+258)
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime (__NR_SYSCALL_BASE+259)
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun (__NR_SYSCALL_BASE+260)
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete (__NR_SYSCALL_BASE+261)
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime (__NR_SYSCALL_BASE+262)
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime (__NR_SYSCALL_BASE+263)
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres (__NR_SYSCALL_BASE+264)
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep (__NR_SYSCALL_BASE+265)
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 (__NR_SYSCALL_BASE+266)
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 (__NR_SYSCALL_BASE+267)
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill (__NR_SYSCALL_BASE+268)
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes (__NR_SYSCALL_BASE+269)
+#endif
+
+#if !defined(__NR_arm_fadvise64_64)
+#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE+270)
+#endif
+
+#if !defined(__NR_pciconfig_iobase)
+#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE+271)
+#endif
+
+#if !defined(__NR_pciconfig_read)
+#define __NR_pciconfig_read (__NR_SYSCALL_BASE+272)
+#endif
+
+#if !defined(__NR_pciconfig_write)
+#define __NR_pciconfig_write (__NR_SYSCALL_BASE+273)
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open (__NR_SYSCALL_BASE+274)
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink (__NR_SYSCALL_BASE+275)
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend (__NR_SYSCALL_BASE+276)
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive (__NR_SYSCALL_BASE+277)
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify (__NR_SYSCALL_BASE+278)
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr (__NR_SYSCALL_BASE+279)
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid (__NR_SYSCALL_BASE+280)
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket (__NR_SYSCALL_BASE+281)
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind (__NR_SYSCALL_BASE+282)
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect (__NR_SYSCALL_BASE+283)
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen (__NR_SYSCALL_BASE+284)
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept (__NR_SYSCALL_BASE+285)
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname (__NR_SYSCALL_BASE+286)
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername (__NR_SYSCALL_BASE+287)
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair (__NR_SYSCALL_BASE+288)
+#endif
+
+#if !defined(__NR_send)
+#define __NR_send (__NR_SYSCALL_BASE+289)
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto (__NR_SYSCALL_BASE+290)
+#endif
+
+#if !defined(__NR_recv)
+#define __NR_recv (__NR_SYSCALL_BASE+291)
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom (__NR_SYSCALL_BASE+292)
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown (__NR_SYSCALL_BASE+293)
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt (__NR_SYSCALL_BASE+294)
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt (__NR_SYSCALL_BASE+295)
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg (__NR_SYSCALL_BASE+296)
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg (__NR_SYSCALL_BASE+297)
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop (__NR_SYSCALL_BASE+298)
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget (__NR_SYSCALL_BASE+299)
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl (__NR_SYSCALL_BASE+300)
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd (__NR_SYSCALL_BASE+301)
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv (__NR_SYSCALL_BASE+302)
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget (__NR_SYSCALL_BASE+303)
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl (__NR_SYSCALL_BASE+304)
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat (__NR_SYSCALL_BASE+305)
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt (__NR_SYSCALL_BASE+306)
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget (__NR_SYSCALL_BASE+307)
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl (__NR_SYSCALL_BASE+308)
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key (__NR_SYSCALL_BASE+309)
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key (__NR_SYSCALL_BASE+310)
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl (__NR_SYSCALL_BASE+311)
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop (__NR_SYSCALL_BASE+312)
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver (__NR_SYSCALL_BASE+313)
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set (__NR_SYSCALL_BASE+314)
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get (__NR_SYSCALL_BASE+315)
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init (__NR_SYSCALL_BASE+316)
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch (__NR_SYSCALL_BASE+317)
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE+318)
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind (__NR_SYSCALL_BASE+319)
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy (__NR_SYSCALL_BASE+320)
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat (__NR_SYSCALL_BASE+322)
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat (__NR_SYSCALL_BASE+323)
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat (__NR_SYSCALL_BASE+324)
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat (__NR_SYSCALL_BASE+325)
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat (__NR_SYSCALL_BASE+326)
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 (__NR_SYSCALL_BASE+327)
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat (__NR_SYSCALL_BASE+328)
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat (__NR_SYSCALL_BASE+329)
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat (__NR_SYSCALL_BASE+330)
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat (__NR_SYSCALL_BASE+331)
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat (__NR_SYSCALL_BASE+334)
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll (__NR_SYSCALL_BASE+336)
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare (__NR_SYSCALL_BASE+337)
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice (__NR_SYSCALL_BASE+340)
+#endif
+
+#if !defined(__NR_arm_sync_file_range)
+#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE+341)
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee (__NR_SYSCALL_BASE+342)
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages (__NR_SYSCALL_BASE+344)
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu (__NR_SYSCALL_BASE+345)
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat (__NR_SYSCALL_BASE+348)
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd (__NR_SYSCALL_BASE+349)
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create (__NR_SYSCALL_BASE+350)
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd (__NR_SYSCALL_BASE+351)
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate (__NR_SYSCALL_BASE+352)
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime (__NR_SYSCALL_BASE+353)
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime (__NR_SYSCALL_BASE+354)
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 (__NR_SYSCALL_BASE+355)
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 (__NR_SYSCALL_BASE+356)
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 (__NR_SYSCALL_BASE+357)
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 (__NR_SYSCALL_BASE+358)
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 (__NR_SYSCALL_BASE+359)
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 (__NR_SYSCALL_BASE+360)
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv (__NR_SYSCALL_BASE+361)
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev (__NR_SYSCALL_BASE+362)
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363)
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open (__NR_SYSCALL_BASE+364)
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg (__NR_SYSCALL_BASE+365)
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 (__NR_SYSCALL_BASE+366)
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init (__NR_SYSCALL_BASE+367)
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark (__NR_SYSCALL_BASE+368)
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 (__NR_SYSCALL_BASE+369)
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at (__NR_SYSCALL_BASE+370)
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at (__NR_SYSCALL_BASE+371)
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime (__NR_SYSCALL_BASE+372)
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs (__NR_SYSCALL_BASE+373)
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg (__NR_SYSCALL_BASE+374)
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns (__NR_SYSCALL_BASE+375)
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376)
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp (__NR_SYSCALL_BASE+378)
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module (__NR_SYSCALL_BASE+379)
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr (__NR_SYSCALL_BASE+380)
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr (__NR_SYSCALL_BASE+381)
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 (__NR_SYSCALL_BASE+382)
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp (__NR_SYSCALL_BASE+383)
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom (__NR_SYSCALL_BASE+384)
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create (__NR_SYSCALL_BASE+385)
+#endif
+
+#if !defined(__NR_bpf)
+#define __NR_bpf (__NR_SYSCALL_BASE+386)
+#endif
+
+#if !defined(__NR_execveat)
+#define __NR_execveat (__NR_SYSCALL_BASE+387)
+#endif
+
+#if !defined(__NR_userfaultfd)
+#define __NR_userfaultfd (__NR_SYSCALL_BASE+388)
+#endif
+
+#if !defined(__NR_membarrier)
+#define __NR_membarrier (__NR_SYSCALL_BASE+389)
+#endif
+
+#if !defined(__NR_mlock2)
+#define __NR_mlock2 (__NR_SYSCALL_BASE+390)
+#endif
+
+#if !defined(__NR_copy_file_range)
+#define __NR_copy_file_range (__NR_SYSCALL_BASE+391)
+#endif
+
+#if !defined(__NR_preadv2)
+#define __NR_preadv2 (__NR_SYSCALL_BASE+392)
+#endif
+
+#if !defined(__NR_pwritev2)
+#define __NR_pwritev2 (__NR_SYSCALL_BASE+393)
+#endif
+
+#if !defined(__NR_pkey_mprotect)
+#define __NR_pkey_mprotect (__NR_SYSCALL_BASE+394)
+#endif
+
+#if !defined(__NR_pkey_alloc)
+#define __NR_pkey_alloc (__NR_SYSCALL_BASE+395)
+#endif
+
+#if !defined(__NR_pkey_free)
+#define __NR_pkey_free (__NR_SYSCALL_BASE+396)
+#endif
+
+#if !defined(__NR_statx)
+#define __NR_statx (__NR_SYSCALL_BASE+397)
+#endif
+
+#if !defined(__NR_rseq)
+#define __NR_rseq (__NR_SYSCALL_BASE+398)
+#endif
+
+#if !defined(__NR_io_pgetevents)
+#define __NR_io_pgetevents (__NR_SYSCALL_BASE+399)
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages (__NR_SYSCALL_BASE+400)
+#endif
+
+#if !defined(__NR_kexec_file_load)
+#define __NR_kexec_file_load (__NR_SYSCALL_BASE+401)
+#endif
+
+#if !defined(__NR_clock_gettime64)
+#define __NR_clock_gettime64 (__NR_SYSCALL_BASE+403)
+#endif
+
+#if !defined(__NR_clock_settime64)
+#define __NR_clock_settime64 (__NR_SYSCALL_BASE+404)
+#endif
+
+#if !defined(__NR_clock_adjtime64)
+#define __NR_clock_adjtime64 (__NR_SYSCALL_BASE+405)
+#endif
+
+#if !defined(__NR_clock_getres_time64)
+#define __NR_clock_getres_time64 (__NR_SYSCALL_BASE+406)
+#endif
+
+#if !defined(__NR_clock_nanosleep_time64)
+#define __NR_clock_nanosleep_time64 (__NR_SYSCALL_BASE+407)
+#endif
+
+#if !defined(__NR_timer_gettime64)
+#define __NR_timer_gettime64 (__NR_SYSCALL_BASE+408)
+#endif
+
+#if !defined(__NR_timer_settime64)
+#define __NR_timer_settime64 (__NR_SYSCALL_BASE+409)
+#endif
+
+#if !defined(__NR_timerfd_gettime64)
+#define __NR_timerfd_gettime64 (__NR_SYSCALL_BASE+410)
+#endif
+
+#if !defined(__NR_timerfd_settime64)
+#define __NR_timerfd_settime64 (__NR_SYSCALL_BASE+411)
+#endif
+
+#if !defined(__NR_utimensat_time64)
+#define __NR_utimensat_time64 (__NR_SYSCALL_BASE+412)
+#endif
+
+#if !defined(__NR_pselect6_time64)
+#define __NR_pselect6_time64 (__NR_SYSCALL_BASE+413)
+#endif
+
+#if !defined(__NR_ppoll_time64)
+#define __NR_ppoll_time64 (__NR_SYSCALL_BASE+414)
+#endif
+
+#if !defined(__NR_io_pgetevents_time64)
+#define __NR_io_pgetevents_time64 (__NR_SYSCALL_BASE+416)
+#endif
+
+#if !defined(__NR_recvmmsg_time64)
+#define __NR_recvmmsg_time64 (__NR_SYSCALL_BASE+417)
+#endif
+
+#if !defined(__NR_mq_timedsend_time64)
+#define __NR_mq_timedsend_time64 (__NR_SYSCALL_BASE+418)
+#endif
+
+#if !defined(__NR_mq_timedreceive_time64)
+#define __NR_mq_timedreceive_time64 (__NR_SYSCALL_BASE+419)
+#endif
+
+#if !defined(__NR_semtimedop_time64)
+#define __NR_semtimedop_time64 (__NR_SYSCALL_BASE+420)
+#endif
+
+#if !defined(__NR_rt_sigtimedwait_time64)
+#define __NR_rt_sigtimedwait_time64 (__NR_SYSCALL_BASE+421)
+#endif
+
+#if !defined(__NR_futex_time64)
+#define __NR_futex_time64 (__NR_SYSCALL_BASE+422)
+#endif
+
+#if !defined(__NR_sched_rr_get_interval_time64)
+#define __NR_sched_rr_get_interval_time64 (__NR_SYSCALL_BASE+423)
+#endif
+
+#if !defined(__NR_pidfd_send_signal)
+#define __NR_pidfd_send_signal (__NR_SYSCALL_BASE+424)
+#endif
+
+#if !defined(__NR_io_uring_setup)
+#define __NR_io_uring_setup (__NR_SYSCALL_BASE+425)
+#endif
+
+#if !defined(__NR_io_uring_enter)
+#define __NR_io_uring_enter (__NR_SYSCALL_BASE+426)
+#endif
+
+#if !defined(__NR_io_uring_register)
+#define __NR_io_uring_register (__NR_SYSCALL_BASE+427)
+#endif
+
+#if !defined(__NR_open_tree)
+#define __NR_open_tree (__NR_SYSCALL_BASE+428)
+#endif
+
+#if !defined(__NR_move_mount)
+#define __NR_move_mount (__NR_SYSCALL_BASE+429)
+#endif
+
+#if !defined(__NR_fsopen)
+#define __NR_fsopen (__NR_SYSCALL_BASE+430)
+#endif
+
+#if !defined(__NR_fsconfig)
+#define __NR_fsconfig (__NR_SYSCALL_BASE+431)
+#endif
+
+#if !defined(__NR_fsmount)
+#define __NR_fsmount (__NR_SYSCALL_BASE+432)
+#endif
+
+#if !defined(__NR_fspick)
+#define __NR_fspick (__NR_SYSCALL_BASE+433)
+#endif
+
+#if !defined(__NR_pidfd_open)
+#define __NR_pidfd_open (__NR_SYSCALL_BASE+434)
+#endif
+
+#if !defined(__NR_clone3)
+#define __NR_clone3 (__NR_SYSCALL_BASE+435)
+#endif
+
+#if !defined(__NR_openat2)
+#define __NR_openat2 (__NR_SYSCALL_BASE+437)
+#endif
+
+#if !defined(__NR_pidfd_getfd)
+#define __NR_pidfd_getfd (__NR_SYSCALL_BASE+438)
+#endif
+
+#if !defined(__NR_faccessat2)
+#define __NR_faccessat2 (__NR_SYSCALL_BASE+439)
+#endif
+
+// ARM private syscalls.
+#if !defined(__ARM_NR_BASE)
+#define __ARM_NR_BASE (__NR_SYSCALL_BASE + 0xF0000)
+#endif
+
+#if !defined(__ARM_NR_breakpoint)
+#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
+#endif
+
+#if !defined(__ARM_NR_cacheflush)
+#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
+#endif
+
+#if !defined(__ARM_NR_usr26)
+#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
+#endif
+
+#if !defined(__ARM_NR_usr32)
+#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
+#endif
+
+#if !defined(__ARM_NR_set_tls)
+#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
+#endif
+
+// ARM kernel private syscall.
+#if !defined(__ARM_NR_cmpxchg)
+#define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_SYSCALLS_H_
diff --git a/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 0000000000..2b2090e392
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/arm_linux_ucontext.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ARM_LINUX_UCONTEXT_H_
+
+#include <stddef.h>
+
+// 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;
+
+
+// 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;
+
+#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 0000000000..f91fcf78ac
--- /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 <stdint.h>
+
+// The following macros are taken from linux/capability.h.
+// We only support capability version 3, which was introduced in Linux 2.6.26.
+#ifndef _LINUX_CAPABILITY_VERSION_3
+#define _LINUX_CAPABILITY_VERSION_3 0x20080522
+#endif
+#ifndef _LINUX_CAPABILITY_U32S_3
+#define _LINUX_CAPABILITY_U32S_3 2
+#endif
+#ifndef CAP_TO_INDEX
+#define CAP_TO_INDEX(x) ((x) >> 5) // 1 << 5 == bits in __u32
+#endif
+#ifndef CAP_TO_MASK
+#define CAP_TO_MASK(x) (1 << ((x) & 31)) // mask for indexed __u32
+#endif
+#ifndef CAP_SYS_CHROOT
+#define CAP_SYS_CHROOT 18
+#endif
+#ifndef CAP_SYS_ADMIN
+#define CAP_SYS_ADMIN 21
+#endif
+
+struct cap_hdr {
+ uint32_t version;
+ int pid;
+};
+
+struct cap_data {
+ uint32_t effective;
+ uint32_t permitted;
+ uint32_t inheritable;
+};
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_CAPABILITY_H_
diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h b/security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h
new file mode 100644
index 0000000000..1a7b975de8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/i386_linux_ucontext.h
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_ANDROID_I386_UCONTEXT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+// This is mostly copied from breakpad (common/android/include/sys/ucontext.h),
+// except we do use sigset_t for uc_sigmask instead of a custom type.
+
+// 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;
+
+/* 80-bit floating-point register */
+struct _libc_fpreg {
+ unsigned short significand[4];
+ unsigned short exponent;
+};
+
+/* Simple floating-point state, see FNSTENV instruction */
+struct _libc_fpstate {
+ unsigned long cw;
+ unsigned long sw;
+ unsigned long tag;
+ unsigned long ipoff;
+ unsigned long cssel;
+ unsigned long dataoff;
+ unsigned long datasel;
+ struct _libc_fpreg _st[8];
+ unsigned long status;
+};
+
+typedef uint32_t greg_t;
+
+typedef struct {
+ uint32_t gregs[19];
+ struct _libc_fpstate* fpregs;
+ uint32_t oldmask;
+ uint32_t cr2;
+} mcontext_t;
+
+enum {
+ REG_GS = 0,
+ REG_FS,
+ REG_ES,
+ REG_DS,
+ REG_EDI,
+ REG_ESI,
+ REG_EBP,
+ REG_ESP,
+ REG_EBX,
+ REG_EDX,
+ REG_ECX,
+ REG_EAX,
+ REG_TRAPNO,
+ REG_ERR,
+ REG_EIP,
+ REG_CS,
+ REG_EFL,
+ REG_UESP,
+ REG_SS,
+};
+
+typedef struct ucontext {
+ uint32_t uc_flags;
+ struct ucontext* uc_link;
+ stack_t uc_stack;
+ mcontext_t uc_mcontext;
+ // Android and PNaCl toolchain's sigset_t has only 32 bits, though Linux
+ // ABI requires 64 bits.
+ union {
+ sigset_t uc_sigmask;
+ uint32_t kernel_sigmask[2];
+ };
+ struct _libc_fpstate __fpregs_mem;
+} ucontext_t;
+
+#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 0000000000..b23b6eb0c1
--- /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 <stdint.h>
+
+// The following structs and macros are taken from linux/filter.h,
+// as some toolchain does not expose them.
+struct sock_filter {
+ uint16_t code;
+ uint8_t jt;
+ uint8_t jf;
+ uint32_t k;
+};
+
+struct sock_fprog {
+ uint16_t len;
+ struct sock_filter *filter;
+};
+
+#ifndef BPF_CLASS
+#define BPF_CLASS(code) ((code) & 0x07)
+#endif
+
+#ifndef BPF_LD
+#define BPF_LD 0x00
+#endif
+
+#ifndef BPF_ALU
+#define BPF_ALU 0x04
+#endif
+
+#ifndef BPF_JMP
+#define BPF_JMP 0x05
+#endif
+
+#ifndef BPF_RET
+#define BPF_RET 0x06
+#endif
+
+#ifndef BPF_SIZE
+#define BPF_SIZE(code) ((code) & 0x18)
+#endif
+
+#ifndef BPF_W
+#define BPF_W 0x00
+#endif
+
+#ifndef BPF_MODE
+#define BPF_MODE(code) ((code) & 0xe0)
+#endif
+
+#ifndef BPF_ABS
+#define BPF_ABS 0x20
+#endif
+
+#ifndef BPF_OP
+#define BPF_OP(code) ((code) & 0xf0)
+#endif
+
+#ifndef BPF_ADD
+#define BPF_ADD 0x00
+#endif
+
+#ifndef BPF_SUB
+#define BPF_SUB 0x10
+#endif
+
+#ifndef BPF_MUL
+#define BPF_MUL 0x20
+#endif
+
+#ifndef BPF_DIV
+#define BPF_DIV 0x30
+#endif
+
+#ifndef BPF_OR
+#define BPF_OR 0x40
+#endif
+
+#ifndef BPF_AND
+#define BPF_AND 0x50
+#endif
+
+#ifndef BPF_LSH
+#define BPF_LSH 0x60
+#endif
+
+#ifndef BPF_RSH
+#define BPF_RSH 0x70
+#endif
+
+#ifndef BPF_NEG
+#define BPF_NEG 0x80
+#endif
+
+#ifndef BPF_MOD
+#define BPF_MOD 0x90
+#endif
+
+#ifndef BPF_XOR
+#define BPF_XOR 0xA0
+#endif
+
+#ifndef BPF_JA
+#define BPF_JA 0x00
+#endif
+
+#ifndef BPF_JEQ
+#define BPF_JEQ 0x10
+#endif
+
+#ifndef BPF_JGT
+#define BPF_JGT 0x20
+#endif
+
+#ifndef BPF_JGE
+#define BPF_JGE 0x30
+#endif
+
+#ifndef BPF_JSET
+#define BPF_JSET 0x40
+#endif
+
+#ifndef BPF_SRC
+#define BPF_SRC(code) ((code) & 0x08)
+#endif
+
+#ifndef BPF_K
+#define BPF_K 0x00
+#endif
+
+#ifndef BPF_MAXINSNS
+#define BPF_MAXINSNS 4096
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FILTER_H_
diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h b/security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h
new file mode 100644
index 0000000000..4e28403336
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_futex.h
@@ -0,0 +1,84 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
+
+#if !defined(__native_client_nonsfi__)
+#include <linux/futex.h>
+#endif // !defined(__native_client_nonsfi__)
+
+#if !defined(FUTEX_WAIT)
+#define FUTEX_WAIT 0
+#endif
+
+#if !defined(FUTEX_WAKE)
+#define FUTEX_WAKE 1
+#endif
+
+#if !defined(FUTEX_FD)
+#define FUTEX_FD 2
+#endif
+
+#if !defined(FUTEX_REQUEUE)
+#define FUTEX_REQUEUE 3
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE)
+#define FUTEX_CMP_REQUEUE 4
+#endif
+
+#if !defined(FUTEX_WAKE_OP)
+#define FUTEX_WAKE_OP 5
+#endif
+
+#if !defined(FUTEX_LOCK_PI)
+#define FUTEX_LOCK_PI 6
+#endif
+
+#if !defined(FUTEX_UNLOCK_PI)
+#define FUTEX_UNLOCK_PI 7
+#endif
+
+#if !defined(FUTEX_TRYLOCK_PI)
+#define FUTEX_TRYLOCK_PI 8
+#endif
+
+#if !defined(FUTEX_WAIT_BITSET)
+#define FUTEX_WAIT_BITSET 9
+#endif
+
+#if !defined(FUTEX_WAKE_BITSET)
+#define FUTEX_WAKE_BITSET 10
+#endif
+
+#if !defined(FUTEX_WAIT_REQUEUE_PI)
+#define FUTEX_WAIT_REQUEUE_PI 11
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI)
+#define FUTEX_CMP_REQUEUE_PI 12
+#endif
+
+#if !defined(FUTEX_PRIVATE_FLAG)
+#define FUTEX_PRIVATE_FLAG 128
+#endif
+
+#if !defined FUTEX_CLOCK_REALTIME
+#define FUTEX_CLOCK_REALTIME 256
+#endif
+
+#if !defined(FUTEX_CMD_MASK)
+#define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME)
+#endif
+
+#if !defined(FUTEX_CMP_REQUEUE_PI_PRIVATE)
+#define FUTEX_CMP_REQUEUE_PI_PRIVATE (FUTEX_CMP_REQUEUE_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#if !defined(FUTEX_UNLOCK_PI_PRIVATE)
+#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG)
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_FUTEX_H_
diff --git a/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 0000000000..a60fe2ad3d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_seccomp.h
@@ -0,0 +1,110 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SECCOMP_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SECCOMP_H_
+
+// The Seccomp2 kernel ABI is not part of older versions of glibc.
+// As we can't break compilation with these versions of the library,
+// we explicitly define all missing symbols.
+// If we ever decide that we can now rely on system headers, the following
+// include files should be enabled:
+// #include <linux/audit.h>
+// #include <linux/seccomp.h>
+
+// For audit.h
+#ifndef EM_ARM
+#define EM_ARM 40
+#endif
+#ifndef EM_386
+#define EM_386 3
+#endif
+#ifndef EM_X86_64
+#define EM_X86_64 62
+#endif
+#ifndef EM_MIPS
+#define EM_MIPS 8
+#endif
+#ifndef EM_AARCH64
+#define EM_AARCH64 183
+#endif
+
+#ifndef __AUDIT_ARCH_64BIT
+#define __AUDIT_ARCH_64BIT 0x80000000
+#endif
+#ifndef __AUDIT_ARCH_LE
+#define __AUDIT_ARCH_LE 0x40000000
+#endif
+#ifndef AUDIT_ARCH_ARM
+#define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_I386
+#define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_X86_64
+#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_MIPSEL
+#define AUDIT_ARCH_MIPSEL (EM_MIPS|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_MIPSEL64
+#define AUDIT_ARCH_MIPSEL64 (EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#endif
+#ifndef AUDIT_ARCH_AARCH64
+#define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE)
+#endif
+
+// For prctl.h
+#ifndef PR_SET_SECCOMP
+#define PR_SET_SECCOMP 22
+#define PR_GET_SECCOMP 21
+#endif
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+#ifndef IPC_64
+#define IPC_64 0x0100
+#endif
+
+// In order to build will older tool chains, we currently have to avoid
+// including <linux/seccomp.h>. Until that can be fixed (if ever). Rely on
+// our own definitions of the seccomp kernel ABI.
+#ifndef SECCOMP_MODE_FILTER
+#define SECCOMP_MODE_DISABLED 0
+#define SECCOMP_MODE_STRICT 1
+#define SECCOMP_MODE_FILTER 2 // User user-supplied filter
+#endif
+
+#ifndef SECCOMP_SET_MODE_STRICT
+#define SECCOMP_SET_MODE_STRICT 0
+#endif
+#ifndef SECCOMP_SET_MODE_FILTER
+#define SECCOMP_SET_MODE_FILTER 1
+#endif
+#ifndef SECCOMP_FILTER_FLAG_TSYNC
+#define SECCOMP_FILTER_FLAG_TSYNC 1
+#endif
+
+#ifndef SECCOMP_RET_KILL
+// Return values supported for BPF filter programs. Please note that the
+// "illegal" SECCOMP_RET_INVALID is not supported by the kernel, should only
+// ever be used internally, and would result in the kernel killing our process.
+#define SECCOMP_RET_KILL 0x00000000U // Kill the task immediately
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#define SECCOMP_RET_TRAP 0x00030000U // Disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO 0x00050000U // Returns an errno
+#define SECCOMP_RET_TRACE 0x7ff00000U // Pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW 0x7fff0000U // Allow
+#define SECCOMP_RET_ACTION 0xffff0000U // Masks for the return value
+#define SECCOMP_RET_DATA 0x0000ffffU // sections
+#else
+#define SECCOMP_RET_INVALID 0x00010000U // Illegal return value
+#endif
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_SECCOMP_H_
diff --git a/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 0000000000..f5a7367617
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_signal.h
@@ -0,0 +1,150 @@
+// 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 <stdint.h>
+
+#include "build/build_config.h"
+
+// NOTE: On some toolchains, signal related ABI is incompatible with Linux's
+// (not undefined, but defined different values and in different memory
+// layouts). So, fill the gap here.
+#if defined(__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 <signal.h>
+
+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 <signal.h>
+
+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;
+
+#endif // !defined(__native_client_nonsfi__)
+
+// struct sigset_t is different size in PNaCl from the Linux's.
+#if (defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
+#if !defined(_NSIG_WORDS)
+#define _NSIG_WORDS 4
+#endif
+struct LinuxSigSet {
+ unsigned long sig[_NSIG_WORDS];
+};
+#elif defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)
+#if !defined(_NSIG_WORDS)
+#define _NSIG_WORDS 2
+#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 0000000000..2b78a0cc3b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_syscalls.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// 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_
+
+#include "build/build_config.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(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_32_BITS))
+#include "sandbox/linux/system_headers/mips_linux_syscalls.h"
+#endif
+
+#if defined(ARCH_CPU_MIPS_FAMILY) && defined(ARCH_CPU_64_BITS)
+#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 0000000000..22ce780274
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/linux_ucontext.h
@@ -0,0 +1,22 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_UCONTEXT_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_UCONTEXT_H_
+
+#if 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"
+#else
+#error "No support for your architecture in PNaCl header"
+#endif
+
+#else // defined(__native_client_nonsfi__)
+#error "The header file included on non PNaCl."
+#endif // defined(__native_client_nonsfi__)
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_LINUX_UCONTEXT_H_
diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h
new file mode 100644
index 0000000000..dc846ee7ad
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/x86_32_linux_syscalls.h
@@ -0,0 +1,1731 @@
+// 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.
+
+/* Constructed by running:
+ * curl -vsSL https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/arch/x86/entry/syscalls/syscall_32.tbl?h=v5.8
+ * | grep -vE '^#|^$'
+ * | awk '{ if ($2 == "i386") { print "#if !defined(__NR_" $3 ")\n#define __NR_" $3 " " $1 "\n#endif\n"; } }'
+ * */
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_
+
+#if !defined(__i386__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 0
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 1
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork 2
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 3
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 4
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open 5
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 6
+#endif
+
+#if !defined(__NR_waitpid)
+#define __NR_waitpid 7
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat 8
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link 9
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink 10
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 11
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 12
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time 13
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod 14
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod 15
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown 16
+#endif
+
+#if !defined(__NR_break)
+#define __NR_break 17
+#endif
+
+#if !defined(__NR_oldstat)
+#define __NR_oldstat 18
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 19
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 20
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 21
+#endif
+
+#if !defined(__NR_umount)
+#define __NR_umount 22
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 23
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 24
+#endif
+
+#if !defined(__NR_stime)
+#define __NR_stime 25
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 26
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm 27
+#endif
+
+#if !defined(__NR_oldfstat)
+#define __NR_oldfstat 28
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause 29
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime 30
+#endif
+
+#if !defined(__NR_stty)
+#define __NR_stty 31
+#endif
+
+#if !defined(__NR_gtty)
+#define __NR_gtty 32
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access 33
+#endif
+
+#if !defined(__NR_nice)
+#define __NR_nice 34
+#endif
+
+#if !defined(__NR_ftime)
+#define __NR_ftime 35
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 36
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 37
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename 38
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir 39
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir 40
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 41
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe 42
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 43
+#endif
+
+#if !defined(__NR_prof)
+#define __NR_prof 44
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 45
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 46
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 47
+#endif
+
+#if !defined(__NR_signal)
+#define __NR_signal 48
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 49
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 50
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 51
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 52
+#endif
+
+#if !defined(__NR_lock)
+#define __NR_lock 53
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 54
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 55
+#endif
+
+#if !defined(__NR_mpx)
+#define __NR_mpx 56
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 57
+#endif
+
+#if !defined(__NR_ulimit)
+#define __NR_ulimit 58
+#endif
+
+#if !defined(__NR_oldolduname)
+#define __NR_oldolduname 59
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 60
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 61
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat 62
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 63
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 64
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp 65
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 66
+#endif
+
+#if !defined(__NR_sigaction)
+#define __NR_sigaction 67
+#endif
+
+#if !defined(__NR_sgetmask)
+#define __NR_sgetmask 68
+#endif
+
+#if !defined(__NR_ssetmask)
+#define __NR_ssetmask 69
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 70
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 71
+#endif
+
+#if !defined(__NR_sigsuspend)
+#define __NR_sigsuspend 72
+#endif
+
+#if !defined(__NR_sigpending)
+#define __NR_sigpending 73
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 74
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 75
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 76
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 77
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 78
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 79
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 80
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 81
+#endif
+
+#if !defined(__NR_select)
+#define __NR_select 82
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink 83
+#endif
+
+#if !defined(__NR_oldlstat)
+#define __NR_oldlstat 84
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink 85
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib 86
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 87
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 88
+#endif
+
+#if !defined(__NR_readdir)
+#define __NR_readdir 89
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 90
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 91
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 92
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 93
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 94
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 95
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 96
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 97
+#endif
+
+#if !defined(__NR_profil)
+#define __NR_profil 98
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 99
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 100
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm 101
+#endif
+
+#if !defined(__NR_socketcall)
+#define __NR_socketcall 102
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 103
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 104
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 105
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat 106
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat 107
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 108
+#endif
+
+#if !defined(__NR_olduname)
+#define __NR_olduname 109
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl 110
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 111
+#endif
+
+#if !defined(__NR_idle)
+#define __NR_idle 112
+#endif
+
+#if !defined(__NR_vm86old)
+#define __NR_vm86old 113
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 114
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 115
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 116
+#endif
+
+#if !defined(__NR_ipc)
+#define __NR_ipc 117
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 118
+#endif
+
+#if !defined(__NR_sigreturn)
+#define __NR_sigreturn 119
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 120
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 121
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 122
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt 123
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 124
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 125
+#endif
+
+#if !defined(__NR_sigprocmask)
+#define __NR_sigprocmask 126
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module 127
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 128
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 129
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms 130
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 131
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 132
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 133
+#endif
+
+#if !defined(__NR_bdflush)
+#define __NR_bdflush 134
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs 135
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 136
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall 137
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 138
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 139
+#endif
+
+#if !defined(__NR__llseek)
+#define __NR__llseek 140
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents 141
+#endif
+
+#if !defined(__NR__newselect)
+#define __NR__newselect 142
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 143
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 144
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 145
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 146
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 147
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 148
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl 149
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 150
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 151
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 152
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 153
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 154
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 155
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 156
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 157
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 158
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 159
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 160
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 161
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 162
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 163
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 164
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 165
+#endif
+
+#if !defined(__NR_vm86)
+#define __NR_vm86 166
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module 167
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll 168
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 169
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 170
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 171
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 172
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 173
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 174
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 175
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 176
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 177
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 178
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 179
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 180
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 181
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown 182
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 183
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 184
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 185
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 186
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 187
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg 188
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg 189
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork 190
+#endif
+
+#if !defined(__NR_ugetrlimit)
+#define __NR_ugetrlimit 191
+#endif
+
+#if !defined(__NR_mmap2)
+#define __NR_mmap2 192
+#endif
+
+#if !defined(__NR_truncate64)
+#define __NR_truncate64 193
+#endif
+
+#if !defined(__NR_ftruncate64)
+#define __NR_ftruncate64 194
+#endif
+
+#if !defined(__NR_stat64)
+#define __NR_stat64 195
+#endif
+
+#if !defined(__NR_lstat64)
+#define __NR_lstat64 196
+#endif
+
+#if !defined(__NR_fstat64)
+#define __NR_fstat64 197
+#endif
+
+#if !defined(__NR_lchown32)
+#define __NR_lchown32 198
+#endif
+
+#if !defined(__NR_getuid32)
+#define __NR_getuid32 199
+#endif
+
+#if !defined(__NR_getgid32)
+#define __NR_getgid32 200
+#endif
+
+#if !defined(__NR_geteuid32)
+#define __NR_geteuid32 201
+#endif
+
+#if !defined(__NR_getegid32)
+#define __NR_getegid32 202
+#endif
+
+#if !defined(__NR_setreuid32)
+#define __NR_setreuid32 203
+#endif
+
+#if !defined(__NR_setregid32)
+#define __NR_setregid32 204
+#endif
+
+#if !defined(__NR_getgroups32)
+#define __NR_getgroups32 205
+#endif
+
+#if !defined(__NR_setgroups32)
+#define __NR_setgroups32 206
+#endif
+
+#if !defined(__NR_fchown32)
+#define __NR_fchown32 207
+#endif
+
+#if !defined(__NR_setresuid32)
+#define __NR_setresuid32 208
+#endif
+
+#if !defined(__NR_getresuid32)
+#define __NR_getresuid32 209
+#endif
+
+#if !defined(__NR_setresgid32)
+#define __NR_setresgid32 210
+#endif
+
+#if !defined(__NR_getresgid32)
+#define __NR_getresgid32 211
+#endif
+
+#if !defined(__NR_chown32)
+#define __NR_chown32 212
+#endif
+
+#if !defined(__NR_setuid32)
+#define __NR_setuid32 213
+#endif
+
+#if !defined(__NR_setgid32)
+#define __NR_setgid32 214
+#endif
+
+#if !defined(__NR_setfsuid32)
+#define __NR_setfsuid32 215
+#endif
+
+#if !defined(__NR_setfsgid32)
+#define __NR_setfsgid32 216
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 217
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 218
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 219
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 220
+#endif
+
+#if !defined(__NR_fcntl64)
+#define __NR_fcntl64 221
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 224
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 225
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 226
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 227
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 228
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 229
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 230
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 231
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 232
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 233
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 234
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 235
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 236
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 237
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 238
+#endif
+
+#if !defined(__NR_sendfile64)
+#define __NR_sendfile64 239
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 240
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 241
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 242
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area 243
+#endif
+
+#if !defined(__NR_get_thread_area)
+#define __NR_get_thread_area 244
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 245
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 246
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 247
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 248
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 249
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 250
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 252
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 253
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create 254
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 255
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait 256
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 257
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 258
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 259
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 260
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 261
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 262
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 263
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 264
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 265
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 266
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 267
+#endif
+
+#if !defined(__NR_statfs64)
+#define __NR_statfs64 268
+#endif
+
+#if !defined(__NR_fstatfs64)
+#define __NR_fstatfs64 269
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 270
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes 271
+#endif
+
+#if !defined(__NR_fadvise64_64)
+#define __NR_fadvise64_64 272
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver 273
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 274
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 275
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 276
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 277
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 278
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 279
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 280
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 281
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 282
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 283
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 284
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 286
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 287
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 288
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 289
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 290
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init 291
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 292
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 293
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 294
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 295
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 296
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 297
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 298
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat 299
+#endif
+
+#if !defined(__NR_fstatat64)
+#define __NR_fstatat64 300
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 301
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 302
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 303
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 304
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 305
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 306
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 307
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 308
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 309
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 310
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 311
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 312
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 313
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 314
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 315
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 316
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 317
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 318
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 319
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 320
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd 321
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 322
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd 323
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 324
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 325
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 326
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 327
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 328
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 329
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 330
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 331
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 332
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 333
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 334
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 335
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 336
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 337
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 338
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 339
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 340
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 341
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 342
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 343
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 344
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 345
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 346
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 347
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 348
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 349
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 350
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 351
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 352
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 353
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 354
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 355
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 356
+#endif
+
+#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
+
+#if !defined(__NR_userfaultfd)
+#define __NR_userfaultfd 374
+#endif
+
+#if !defined(__NR_membarrier)
+#define __NR_membarrier 375
+#endif
+
+#if !defined(__NR_mlock2)
+#define __NR_mlock2 376
+#endif
+
+#if !defined(__NR_copy_file_range)
+#define __NR_copy_file_range 377
+#endif
+
+#if !defined(__NR_preadv2)
+#define __NR_preadv2 378
+#endif
+
+#if !defined(__NR_pwritev2)
+#define __NR_pwritev2 379
+#endif
+
+#if !defined(__NR_pkey_mprotect)
+#define __NR_pkey_mprotect 380
+#endif
+
+#if !defined(__NR_pkey_alloc)
+#define __NR_pkey_alloc 381
+#endif
+
+#if !defined(__NR_pkey_free)
+#define __NR_pkey_free 382
+#endif
+
+#if !defined(__NR_statx)
+#define __NR_statx 383
+#endif
+
+#if !defined(__NR_arch_prctl)
+#define __NR_arch_prctl 384
+#endif
+
+#if !defined(__NR_io_pgetevents)
+#define __NR_io_pgetevents 385
+#endif
+
+#if !defined(__NR_rseq)
+#define __NR_rseq 386
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 393
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 394
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 395
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 396
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 397
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 398
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 399
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 400
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 401
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 402
+#endif
+
+#if !defined(__NR_clock_gettime64)
+#define __NR_clock_gettime64 403
+#endif
+
+#if !defined(__NR_clock_settime64)
+#define __NR_clock_settime64 404
+#endif
+
+#if !defined(__NR_clock_adjtime64)
+#define __NR_clock_adjtime64 405
+#endif
+
+#if !defined(__NR_clock_getres_time64)
+#define __NR_clock_getres_time64 406
+#endif
+
+#if !defined(__NR_clock_nanosleep_time64)
+#define __NR_clock_nanosleep_time64 407
+#endif
+
+#if !defined(__NR_timer_gettime64)
+#define __NR_timer_gettime64 408
+#endif
+
+#if !defined(__NR_timer_settime64)
+#define __NR_timer_settime64 409
+#endif
+
+#if !defined(__NR_timerfd_gettime64)
+#define __NR_timerfd_gettime64 410
+#endif
+
+#if !defined(__NR_timerfd_settime64)
+#define __NR_timerfd_settime64 411
+#endif
+
+#if !defined(__NR_utimensat_time64)
+#define __NR_utimensat_time64 412
+#endif
+
+#if !defined(__NR_pselect6_time64)
+#define __NR_pselect6_time64 413
+#endif
+
+#if !defined(__NR_ppoll_time64)
+#define __NR_ppoll_time64 414
+#endif
+
+#if !defined(__NR_io_pgetevents_time64)
+#define __NR_io_pgetevents_time64 416
+#endif
+
+#if !defined(__NR_recvmmsg_time64)
+#define __NR_recvmmsg_time64 417
+#endif
+
+#if !defined(__NR_mq_timedsend_time64)
+#define __NR_mq_timedsend_time64 418
+#endif
+
+#if !defined(__NR_mq_timedreceive_time64)
+#define __NR_mq_timedreceive_time64 419
+#endif
+
+#if !defined(__NR_semtimedop_time64)
+#define __NR_semtimedop_time64 420
+#endif
+
+#if !defined(__NR_rt_sigtimedwait_time64)
+#define __NR_rt_sigtimedwait_time64 421
+#endif
+
+#if !defined(__NR_futex_time64)
+#define __NR_futex_time64 422
+#endif
+
+#if !defined(__NR_sched_rr_get_interval_time64)
+#define __NR_sched_rr_get_interval_time64 423
+#endif
+
+#if !defined(__NR_pidfd_send_signal)
+#define __NR_pidfd_send_signal 424
+#endif
+
+#if !defined(__NR_io_uring_setup)
+#define __NR_io_uring_setup 425
+#endif
+
+#if !defined(__NR_io_uring_enter)
+#define __NR_io_uring_enter 426
+#endif
+
+#if !defined(__NR_io_uring_register)
+#define __NR_io_uring_register 427
+#endif
+
+#if !defined(__NR_open_tree)
+#define __NR_open_tree 428
+#endif
+
+#if !defined(__NR_move_mount)
+#define __NR_move_mount 429
+#endif
+
+#if !defined(__NR_fsopen)
+#define __NR_fsopen 430
+#endif
+
+#if !defined(__NR_fsconfig)
+#define __NR_fsconfig 431
+#endif
+
+#if !defined(__NR_fsmount)
+#define __NR_fsmount 432
+#endif
+
+#if !defined(__NR_fspick)
+#define __NR_fspick 433
+#endif
+
+#if !defined(__NR_pidfd_open)
+#define __NR_pidfd_open 434
+#endif
+
+#if !defined(__NR_clone3)
+#define __NR_clone3 435
+#endif
+
+#if !defined(__NR_openat2)
+#define __NR_openat2 437
+#endif
+
+#if !defined(__NR_pidfd_getfd)
+#define __NR_pidfd_getfd 438
+#endif
+
+#if !defined(__NR_faccessat2)
+#define __NR_faccessat2 439
+#endif
+
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_32_LINUX_SYSCALLS_H_
diff --git a/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h
new file mode 100644
index 0000000000..ab51703464
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/linux/system_headers/x86_64_linux_syscalls.h
@@ -0,0 +1,1418 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/* Constructed by running:
+ * curl -vsSL https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/arch/x86/entry/syscalls/syscall_64.tbl?h=v5.8
+ * | grep -vE '^#|^$'
+ * | awk '{ if ($2 != "x32") { print "#if !defined(__NR_" $3 ")\n#define __NR_" $3 " " $1 "\n#endif\n"; } }'
+ * */
+
+#ifndef SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+#define SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
+
+#if !defined(__x86_64__)
+#error "Including header on wrong architecture"
+#endif
+
+#if !defined(__NR_read)
+#define __NR_read 0
+#endif
+
+#if !defined(__NR_write)
+#define __NR_write 1
+#endif
+
+#if !defined(__NR_open)
+#define __NR_open 2
+#endif
+
+#if !defined(__NR_close)
+#define __NR_close 3
+#endif
+
+#if !defined(__NR_stat)
+#define __NR_stat 4
+#endif
+
+#if !defined(__NR_fstat)
+#define __NR_fstat 5
+#endif
+
+#if !defined(__NR_lstat)
+#define __NR_lstat 6
+#endif
+
+#if !defined(__NR_poll)
+#define __NR_poll 7
+#endif
+
+#if !defined(__NR_lseek)
+#define __NR_lseek 8
+#endif
+
+#if !defined(__NR_mmap)
+#define __NR_mmap 9
+#endif
+
+#if !defined(__NR_mprotect)
+#define __NR_mprotect 10
+#endif
+
+#if !defined(__NR_munmap)
+#define __NR_munmap 11
+#endif
+
+#if !defined(__NR_brk)
+#define __NR_brk 12
+#endif
+
+#if !defined(__NR_rt_sigaction)
+#define __NR_rt_sigaction 13
+#endif
+
+#if !defined(__NR_rt_sigprocmask)
+#define __NR_rt_sigprocmask 14
+#endif
+
+#if !defined(__NR_rt_sigreturn)
+#define __NR_rt_sigreturn 15
+#endif
+
+#if !defined(__NR_ioctl)
+#define __NR_ioctl 16
+#endif
+
+#if !defined(__NR_pread64)
+#define __NR_pread64 17
+#endif
+
+#if !defined(__NR_pwrite64)
+#define __NR_pwrite64 18
+#endif
+
+#if !defined(__NR_readv)
+#define __NR_readv 19
+#endif
+
+#if !defined(__NR_writev)
+#define __NR_writev 20
+#endif
+
+#if !defined(__NR_access)
+#define __NR_access 21
+#endif
+
+#if !defined(__NR_pipe)
+#define __NR_pipe 22
+#endif
+
+#if !defined(__NR_select)
+#define __NR_select 23
+#endif
+
+#if !defined(__NR_sched_yield)
+#define __NR_sched_yield 24
+#endif
+
+#if !defined(__NR_mremap)
+#define __NR_mremap 25
+#endif
+
+#if !defined(__NR_msync)
+#define __NR_msync 26
+#endif
+
+#if !defined(__NR_mincore)
+#define __NR_mincore 27
+#endif
+
+#if !defined(__NR_madvise)
+#define __NR_madvise 28
+#endif
+
+#if !defined(__NR_shmget)
+#define __NR_shmget 29
+#endif
+
+#if !defined(__NR_shmat)
+#define __NR_shmat 30
+#endif
+
+#if !defined(__NR_shmctl)
+#define __NR_shmctl 31
+#endif
+
+#if !defined(__NR_dup)
+#define __NR_dup 32
+#endif
+
+#if !defined(__NR_dup2)
+#define __NR_dup2 33
+#endif
+
+#if !defined(__NR_pause)
+#define __NR_pause 34
+#endif
+
+#if !defined(__NR_nanosleep)
+#define __NR_nanosleep 35
+#endif
+
+#if !defined(__NR_getitimer)
+#define __NR_getitimer 36
+#endif
+
+#if !defined(__NR_alarm)
+#define __NR_alarm 37
+#endif
+
+#if !defined(__NR_setitimer)
+#define __NR_setitimer 38
+#endif
+
+#if !defined(__NR_getpid)
+#define __NR_getpid 39
+#endif
+
+#if !defined(__NR_sendfile)
+#define __NR_sendfile 40
+#endif
+
+#if !defined(__NR_socket)
+#define __NR_socket 41
+#endif
+
+#if !defined(__NR_connect)
+#define __NR_connect 42
+#endif
+
+#if !defined(__NR_accept)
+#define __NR_accept 43
+#endif
+
+#if !defined(__NR_sendto)
+#define __NR_sendto 44
+#endif
+
+#if !defined(__NR_recvfrom)
+#define __NR_recvfrom 45
+#endif
+
+#if !defined(__NR_sendmsg)
+#define __NR_sendmsg 46
+#endif
+
+#if !defined(__NR_recvmsg)
+#define __NR_recvmsg 47
+#endif
+
+#if !defined(__NR_shutdown)
+#define __NR_shutdown 48
+#endif
+
+#if !defined(__NR_bind)
+#define __NR_bind 49
+#endif
+
+#if !defined(__NR_listen)
+#define __NR_listen 50
+#endif
+
+#if !defined(__NR_getsockname)
+#define __NR_getsockname 51
+#endif
+
+#if !defined(__NR_getpeername)
+#define __NR_getpeername 52
+#endif
+
+#if !defined(__NR_socketpair)
+#define __NR_socketpair 53
+#endif
+
+#if !defined(__NR_setsockopt)
+#define __NR_setsockopt 54
+#endif
+
+#if !defined(__NR_getsockopt)
+#define __NR_getsockopt 55
+#endif
+
+#if !defined(__NR_clone)
+#define __NR_clone 56
+#endif
+
+#if !defined(__NR_fork)
+#define __NR_fork 57
+#endif
+
+#if !defined(__NR_vfork)
+#define __NR_vfork 58
+#endif
+
+#if !defined(__NR_execve)
+#define __NR_execve 59
+#endif
+
+#if !defined(__NR_exit)
+#define __NR_exit 60
+#endif
+
+#if !defined(__NR_wait4)
+#define __NR_wait4 61
+#endif
+
+#if !defined(__NR_kill)
+#define __NR_kill 62
+#endif
+
+#if !defined(__NR_uname)
+#define __NR_uname 63
+#endif
+
+#if !defined(__NR_semget)
+#define __NR_semget 64
+#endif
+
+#if !defined(__NR_semop)
+#define __NR_semop 65
+#endif
+
+#if !defined(__NR_semctl)
+#define __NR_semctl 66
+#endif
+
+#if !defined(__NR_shmdt)
+#define __NR_shmdt 67
+#endif
+
+#if !defined(__NR_msgget)
+#define __NR_msgget 68
+#endif
+
+#if !defined(__NR_msgsnd)
+#define __NR_msgsnd 69
+#endif
+
+#if !defined(__NR_msgrcv)
+#define __NR_msgrcv 70
+#endif
+
+#if !defined(__NR_msgctl)
+#define __NR_msgctl 71
+#endif
+
+#if !defined(__NR_fcntl)
+#define __NR_fcntl 72
+#endif
+
+#if !defined(__NR_flock)
+#define __NR_flock 73
+#endif
+
+#if !defined(__NR_fsync)
+#define __NR_fsync 74
+#endif
+
+#if !defined(__NR_fdatasync)
+#define __NR_fdatasync 75
+#endif
+
+#if !defined(__NR_truncate)
+#define __NR_truncate 76
+#endif
+
+#if !defined(__NR_ftruncate)
+#define __NR_ftruncate 77
+#endif
+
+#if !defined(__NR_getdents)
+#define __NR_getdents 78
+#endif
+
+#if !defined(__NR_getcwd)
+#define __NR_getcwd 79
+#endif
+
+#if !defined(__NR_chdir)
+#define __NR_chdir 80
+#endif
+
+#if !defined(__NR_fchdir)
+#define __NR_fchdir 81
+#endif
+
+#if !defined(__NR_rename)
+#define __NR_rename 82
+#endif
+
+#if !defined(__NR_mkdir)
+#define __NR_mkdir 83
+#endif
+
+#if !defined(__NR_rmdir)
+#define __NR_rmdir 84
+#endif
+
+#if !defined(__NR_creat)
+#define __NR_creat 85
+#endif
+
+#if !defined(__NR_link)
+#define __NR_link 86
+#endif
+
+#if !defined(__NR_unlink)
+#define __NR_unlink 87
+#endif
+
+#if !defined(__NR_symlink)
+#define __NR_symlink 88
+#endif
+
+#if !defined(__NR_readlink)
+#define __NR_readlink 89
+#endif
+
+#if !defined(__NR_chmod)
+#define __NR_chmod 90
+#endif
+
+#if !defined(__NR_fchmod)
+#define __NR_fchmod 91
+#endif
+
+#if !defined(__NR_chown)
+#define __NR_chown 92
+#endif
+
+#if !defined(__NR_fchown)
+#define __NR_fchown 93
+#endif
+
+#if !defined(__NR_lchown)
+#define __NR_lchown 94
+#endif
+
+#if !defined(__NR_umask)
+#define __NR_umask 95
+#endif
+
+#if !defined(__NR_gettimeofday)
+#define __NR_gettimeofday 96
+#endif
+
+#if !defined(__NR_getrlimit)
+#define __NR_getrlimit 97
+#endif
+
+#if !defined(__NR_getrusage)
+#define __NR_getrusage 98
+#endif
+
+#if !defined(__NR_sysinfo)
+#define __NR_sysinfo 99
+#endif
+
+#if !defined(__NR_times)
+#define __NR_times 100
+#endif
+
+#if !defined(__NR_ptrace)
+#define __NR_ptrace 101
+#endif
+
+#if !defined(__NR_getuid)
+#define __NR_getuid 102
+#endif
+
+#if !defined(__NR_syslog)
+#define __NR_syslog 103
+#endif
+
+#if !defined(__NR_getgid)
+#define __NR_getgid 104
+#endif
+
+#if !defined(__NR_setuid)
+#define __NR_setuid 105
+#endif
+
+#if !defined(__NR_setgid)
+#define __NR_setgid 106
+#endif
+
+#if !defined(__NR_geteuid)
+#define __NR_geteuid 107
+#endif
+
+#if !defined(__NR_getegid)
+#define __NR_getegid 108
+#endif
+
+#if !defined(__NR_setpgid)
+#define __NR_setpgid 109
+#endif
+
+#if !defined(__NR_getppid)
+#define __NR_getppid 110
+#endif
+
+#if !defined(__NR_getpgrp)
+#define __NR_getpgrp 111
+#endif
+
+#if !defined(__NR_setsid)
+#define __NR_setsid 112
+#endif
+
+#if !defined(__NR_setreuid)
+#define __NR_setreuid 113
+#endif
+
+#if !defined(__NR_setregid)
+#define __NR_setregid 114
+#endif
+
+#if !defined(__NR_getgroups)
+#define __NR_getgroups 115
+#endif
+
+#if !defined(__NR_setgroups)
+#define __NR_setgroups 116
+#endif
+
+#if !defined(__NR_setresuid)
+#define __NR_setresuid 117
+#endif
+
+#if !defined(__NR_getresuid)
+#define __NR_getresuid 118
+#endif
+
+#if !defined(__NR_setresgid)
+#define __NR_setresgid 119
+#endif
+
+#if !defined(__NR_getresgid)
+#define __NR_getresgid 120
+#endif
+
+#if !defined(__NR_getpgid)
+#define __NR_getpgid 121
+#endif
+
+#if !defined(__NR_setfsuid)
+#define __NR_setfsuid 122
+#endif
+
+#if !defined(__NR_setfsgid)
+#define __NR_setfsgid 123
+#endif
+
+#if !defined(__NR_getsid)
+#define __NR_getsid 124
+#endif
+
+#if !defined(__NR_capget)
+#define __NR_capget 125
+#endif
+
+#if !defined(__NR_capset)
+#define __NR_capset 126
+#endif
+
+#if !defined(__NR_rt_sigpending)
+#define __NR_rt_sigpending 127
+#endif
+
+#if !defined(__NR_rt_sigtimedwait)
+#define __NR_rt_sigtimedwait 128
+#endif
+
+#if !defined(__NR_rt_sigqueueinfo)
+#define __NR_rt_sigqueueinfo 129
+#endif
+
+#if !defined(__NR_rt_sigsuspend)
+#define __NR_rt_sigsuspend 130
+#endif
+
+#if !defined(__NR_sigaltstack)
+#define __NR_sigaltstack 131
+#endif
+
+#if !defined(__NR_utime)
+#define __NR_utime 132
+#endif
+
+#if !defined(__NR_mknod)
+#define __NR_mknod 133
+#endif
+
+#if !defined(__NR_uselib)
+#define __NR_uselib 134
+#endif
+
+#if !defined(__NR_personality)
+#define __NR_personality 135
+#endif
+
+#if !defined(__NR_ustat)
+#define __NR_ustat 136
+#endif
+
+#if !defined(__NR_statfs)
+#define __NR_statfs 137
+#endif
+
+#if !defined(__NR_fstatfs)
+#define __NR_fstatfs 138
+#endif
+
+#if !defined(__NR_sysfs)
+#define __NR_sysfs 139
+#endif
+
+#if !defined(__NR_getpriority)
+#define __NR_getpriority 140
+#endif
+
+#if !defined(__NR_setpriority)
+#define __NR_setpriority 141
+#endif
+
+#if !defined(__NR_sched_setparam)
+#define __NR_sched_setparam 142
+#endif
+
+#if !defined(__NR_sched_getparam)
+#define __NR_sched_getparam 143
+#endif
+
+#if !defined(__NR_sched_setscheduler)
+#define __NR_sched_setscheduler 144
+#endif
+
+#if !defined(__NR_sched_getscheduler)
+#define __NR_sched_getscheduler 145
+#endif
+
+#if !defined(__NR_sched_get_priority_max)
+#define __NR_sched_get_priority_max 146
+#endif
+
+#if !defined(__NR_sched_get_priority_min)
+#define __NR_sched_get_priority_min 147
+#endif
+
+#if !defined(__NR_sched_rr_get_interval)
+#define __NR_sched_rr_get_interval 148
+#endif
+
+#if !defined(__NR_mlock)
+#define __NR_mlock 149
+#endif
+
+#if !defined(__NR_munlock)
+#define __NR_munlock 150
+#endif
+
+#if !defined(__NR_mlockall)
+#define __NR_mlockall 151
+#endif
+
+#if !defined(__NR_munlockall)
+#define __NR_munlockall 152
+#endif
+
+#if !defined(__NR_vhangup)
+#define __NR_vhangup 153
+#endif
+
+#if !defined(__NR_modify_ldt)
+#define __NR_modify_ldt 154
+#endif
+
+#if !defined(__NR_pivot_root)
+#define __NR_pivot_root 155
+#endif
+
+#if !defined(__NR__sysctl)
+#define __NR__sysctl 156
+#endif
+
+#if !defined(__NR_prctl)
+#define __NR_prctl 157
+#endif
+
+#if !defined(__NR_arch_prctl)
+#define __NR_arch_prctl 158
+#endif
+
+#if !defined(__NR_adjtimex)
+#define __NR_adjtimex 159
+#endif
+
+#if !defined(__NR_setrlimit)
+#define __NR_setrlimit 160
+#endif
+
+#if !defined(__NR_chroot)
+#define __NR_chroot 161
+#endif
+
+#if !defined(__NR_sync)
+#define __NR_sync 162
+#endif
+
+#if !defined(__NR_acct)
+#define __NR_acct 163
+#endif
+
+#if !defined(__NR_settimeofday)
+#define __NR_settimeofday 164
+#endif
+
+#if !defined(__NR_mount)
+#define __NR_mount 165
+#endif
+
+#if !defined(__NR_umount2)
+#define __NR_umount2 166
+#endif
+
+#if !defined(__NR_swapon)
+#define __NR_swapon 167
+#endif
+
+#if !defined(__NR_swapoff)
+#define __NR_swapoff 168
+#endif
+
+#if !defined(__NR_reboot)
+#define __NR_reboot 169
+#endif
+
+#if !defined(__NR_sethostname)
+#define __NR_sethostname 170
+#endif
+
+#if !defined(__NR_setdomainname)
+#define __NR_setdomainname 171
+#endif
+
+#if !defined(__NR_iopl)
+#define __NR_iopl 172
+#endif
+
+#if !defined(__NR_ioperm)
+#define __NR_ioperm 173
+#endif
+
+#if !defined(__NR_create_module)
+#define __NR_create_module 174
+#endif
+
+#if !defined(__NR_init_module)
+#define __NR_init_module 175
+#endif
+
+#if !defined(__NR_delete_module)
+#define __NR_delete_module 176
+#endif
+
+#if !defined(__NR_get_kernel_syms)
+#define __NR_get_kernel_syms 177
+#endif
+
+#if !defined(__NR_query_module)
+#define __NR_query_module 178
+#endif
+
+#if !defined(__NR_quotactl)
+#define __NR_quotactl 179
+#endif
+
+#if !defined(__NR_nfsservctl)
+#define __NR_nfsservctl 180
+#endif
+
+#if !defined(__NR_getpmsg)
+#define __NR_getpmsg 181
+#endif
+
+#if !defined(__NR_putpmsg)
+#define __NR_putpmsg 182
+#endif
+
+#if !defined(__NR_afs_syscall)
+#define __NR_afs_syscall 183
+#endif
+
+#if !defined(__NR_tuxcall)
+#define __NR_tuxcall 184
+#endif
+
+#if !defined(__NR_security)
+#define __NR_security 185
+#endif
+
+#if !defined(__NR_gettid)
+#define __NR_gettid 186
+#endif
+
+#if !defined(__NR_readahead)
+#define __NR_readahead 187
+#endif
+
+#if !defined(__NR_setxattr)
+#define __NR_setxattr 188
+#endif
+
+#if !defined(__NR_lsetxattr)
+#define __NR_lsetxattr 189
+#endif
+
+#if !defined(__NR_fsetxattr)
+#define __NR_fsetxattr 190
+#endif
+
+#if !defined(__NR_getxattr)
+#define __NR_getxattr 191
+#endif
+
+#if !defined(__NR_lgetxattr)
+#define __NR_lgetxattr 192
+#endif
+
+#if !defined(__NR_fgetxattr)
+#define __NR_fgetxattr 193
+#endif
+
+#if !defined(__NR_listxattr)
+#define __NR_listxattr 194
+#endif
+
+#if !defined(__NR_llistxattr)
+#define __NR_llistxattr 195
+#endif
+
+#if !defined(__NR_flistxattr)
+#define __NR_flistxattr 196
+#endif
+
+#if !defined(__NR_removexattr)
+#define __NR_removexattr 197
+#endif
+
+#if !defined(__NR_lremovexattr)
+#define __NR_lremovexattr 198
+#endif
+
+#if !defined(__NR_fremovexattr)
+#define __NR_fremovexattr 199
+#endif
+
+#if !defined(__NR_tkill)
+#define __NR_tkill 200
+#endif
+
+#if !defined(__NR_time)
+#define __NR_time 201
+#endif
+
+#if !defined(__NR_futex)
+#define __NR_futex 202
+#endif
+
+#if !defined(__NR_sched_setaffinity)
+#define __NR_sched_setaffinity 203
+#endif
+
+#if !defined(__NR_sched_getaffinity)
+#define __NR_sched_getaffinity 204
+#endif
+
+#if !defined(__NR_set_thread_area)
+#define __NR_set_thread_area 205
+#endif
+
+#if !defined(__NR_io_setup)
+#define __NR_io_setup 206
+#endif
+
+#if !defined(__NR_io_destroy)
+#define __NR_io_destroy 207
+#endif
+
+#if !defined(__NR_io_getevents)
+#define __NR_io_getevents 208
+#endif
+
+#if !defined(__NR_io_submit)
+#define __NR_io_submit 209
+#endif
+
+#if !defined(__NR_io_cancel)
+#define __NR_io_cancel 210
+#endif
+
+#if !defined(__NR_get_thread_area)
+#define __NR_get_thread_area 211
+#endif
+
+#if !defined(__NR_lookup_dcookie)
+#define __NR_lookup_dcookie 212
+#endif
+
+#if !defined(__NR_epoll_create)
+#define __NR_epoll_create 213
+#endif
+
+#if !defined(__NR_epoll_ctl_old)
+#define __NR_epoll_ctl_old 214
+#endif
+
+#if !defined(__NR_epoll_wait_old)
+#define __NR_epoll_wait_old 215
+#endif
+
+#if !defined(__NR_remap_file_pages)
+#define __NR_remap_file_pages 216
+#endif
+
+#if !defined(__NR_getdents64)
+#define __NR_getdents64 217
+#endif
+
+#if !defined(__NR_set_tid_address)
+#define __NR_set_tid_address 218
+#endif
+
+#if !defined(__NR_restart_syscall)
+#define __NR_restart_syscall 219
+#endif
+
+#if !defined(__NR_semtimedop)
+#define __NR_semtimedop 220
+#endif
+
+#if !defined(__NR_fadvise64)
+#define __NR_fadvise64 221
+#endif
+
+#if !defined(__NR_timer_create)
+#define __NR_timer_create 222
+#endif
+
+#if !defined(__NR_timer_settime)
+#define __NR_timer_settime 223
+#endif
+
+#if !defined(__NR_timer_gettime)
+#define __NR_timer_gettime 224
+#endif
+
+#if !defined(__NR_timer_getoverrun)
+#define __NR_timer_getoverrun 225
+#endif
+
+#if !defined(__NR_timer_delete)
+#define __NR_timer_delete 226
+#endif
+
+#if !defined(__NR_clock_settime)
+#define __NR_clock_settime 227
+#endif
+
+#if !defined(__NR_clock_gettime)
+#define __NR_clock_gettime 228
+#endif
+
+#if !defined(__NR_clock_getres)
+#define __NR_clock_getres 229
+#endif
+
+#if !defined(__NR_clock_nanosleep)
+#define __NR_clock_nanosleep 230
+#endif
+
+#if !defined(__NR_exit_group)
+#define __NR_exit_group 231
+#endif
+
+#if !defined(__NR_epoll_wait)
+#define __NR_epoll_wait 232
+#endif
+
+#if !defined(__NR_epoll_ctl)
+#define __NR_epoll_ctl 233
+#endif
+
+#if !defined(__NR_tgkill)
+#define __NR_tgkill 234
+#endif
+
+#if !defined(__NR_utimes)
+#define __NR_utimes 235
+#endif
+
+#if !defined(__NR_vserver)
+#define __NR_vserver 236
+#endif
+
+#if !defined(__NR_mbind)
+#define __NR_mbind 237
+#endif
+
+#if !defined(__NR_set_mempolicy)
+#define __NR_set_mempolicy 238
+#endif
+
+#if !defined(__NR_get_mempolicy)
+#define __NR_get_mempolicy 239
+#endif
+
+#if !defined(__NR_mq_open)
+#define __NR_mq_open 240
+#endif
+
+#if !defined(__NR_mq_unlink)
+#define __NR_mq_unlink 241
+#endif
+
+#if !defined(__NR_mq_timedsend)
+#define __NR_mq_timedsend 242
+#endif
+
+#if !defined(__NR_mq_timedreceive)
+#define __NR_mq_timedreceive 243
+#endif
+
+#if !defined(__NR_mq_notify)
+#define __NR_mq_notify 244
+#endif
+
+#if !defined(__NR_mq_getsetattr)
+#define __NR_mq_getsetattr 245
+#endif
+
+#if !defined(__NR_kexec_load)
+#define __NR_kexec_load 246
+#endif
+
+#if !defined(__NR_waitid)
+#define __NR_waitid 247
+#endif
+
+#if !defined(__NR_add_key)
+#define __NR_add_key 248
+#endif
+
+#if !defined(__NR_request_key)
+#define __NR_request_key 249
+#endif
+
+#if !defined(__NR_keyctl)
+#define __NR_keyctl 250
+#endif
+
+#if !defined(__NR_ioprio_set)
+#define __NR_ioprio_set 251
+#endif
+
+#if !defined(__NR_ioprio_get)
+#define __NR_ioprio_get 252
+#endif
+
+#if !defined(__NR_inotify_init)
+#define __NR_inotify_init 253
+#endif
+
+#if !defined(__NR_inotify_add_watch)
+#define __NR_inotify_add_watch 254
+#endif
+
+#if !defined(__NR_inotify_rm_watch)
+#define __NR_inotify_rm_watch 255
+#endif
+
+#if !defined(__NR_migrate_pages)
+#define __NR_migrate_pages 256
+#endif
+
+#if !defined(__NR_openat)
+#define __NR_openat 257
+#endif
+
+#if !defined(__NR_mkdirat)
+#define __NR_mkdirat 258
+#endif
+
+#if !defined(__NR_mknodat)
+#define __NR_mknodat 259
+#endif
+
+#if !defined(__NR_fchownat)
+#define __NR_fchownat 260
+#endif
+
+#if !defined(__NR_futimesat)
+#define __NR_futimesat 261
+#endif
+
+#if !defined(__NR_newfstatat)
+#define __NR_newfstatat 262
+#endif
+
+#if !defined(__NR_unlinkat)
+#define __NR_unlinkat 263
+#endif
+
+#if !defined(__NR_renameat)
+#define __NR_renameat 264
+#endif
+
+#if !defined(__NR_linkat)
+#define __NR_linkat 265
+#endif
+
+#if !defined(__NR_symlinkat)
+#define __NR_symlinkat 266
+#endif
+
+#if !defined(__NR_readlinkat)
+#define __NR_readlinkat 267
+#endif
+
+#if !defined(__NR_fchmodat)
+#define __NR_fchmodat 268
+#endif
+
+#if !defined(__NR_faccessat)
+#define __NR_faccessat 269
+#endif
+
+#if !defined(__NR_pselect6)
+#define __NR_pselect6 270
+#endif
+
+#if !defined(__NR_ppoll)
+#define __NR_ppoll 271
+#endif
+
+#if !defined(__NR_unshare)
+#define __NR_unshare 272
+#endif
+
+#if !defined(__NR_set_robust_list)
+#define __NR_set_robust_list 273
+#endif
+
+#if !defined(__NR_get_robust_list)
+#define __NR_get_robust_list 274
+#endif
+
+#if !defined(__NR_splice)
+#define __NR_splice 275
+#endif
+
+#if !defined(__NR_tee)
+#define __NR_tee 276
+#endif
+
+#if !defined(__NR_sync_file_range)
+#define __NR_sync_file_range 277
+#endif
+
+#if !defined(__NR_vmsplice)
+#define __NR_vmsplice 278
+#endif
+
+#if !defined(__NR_move_pages)
+#define __NR_move_pages 279
+#endif
+
+#if !defined(__NR_utimensat)
+#define __NR_utimensat 280
+#endif
+
+#if !defined(__NR_epoll_pwait)
+#define __NR_epoll_pwait 281
+#endif
+
+#if !defined(__NR_signalfd)
+#define __NR_signalfd 282
+#endif
+
+#if !defined(__NR_timerfd_create)
+#define __NR_timerfd_create 283
+#endif
+
+#if !defined(__NR_eventfd)
+#define __NR_eventfd 284
+#endif
+
+#if !defined(__NR_fallocate)
+#define __NR_fallocate 285
+#endif
+
+#if !defined(__NR_timerfd_settime)
+#define __NR_timerfd_settime 286
+#endif
+
+#if !defined(__NR_timerfd_gettime)
+#define __NR_timerfd_gettime 287
+#endif
+
+#if !defined(__NR_accept4)
+#define __NR_accept4 288
+#endif
+
+#if !defined(__NR_signalfd4)
+#define __NR_signalfd4 289
+#endif
+
+#if !defined(__NR_eventfd2)
+#define __NR_eventfd2 290
+#endif
+
+#if !defined(__NR_epoll_create1)
+#define __NR_epoll_create1 291
+#endif
+
+#if !defined(__NR_dup3)
+#define __NR_dup3 292
+#endif
+
+#if !defined(__NR_pipe2)
+#define __NR_pipe2 293
+#endif
+
+#if !defined(__NR_inotify_init1)
+#define __NR_inotify_init1 294
+#endif
+
+#if !defined(__NR_preadv)
+#define __NR_preadv 295
+#endif
+
+#if !defined(__NR_pwritev)
+#define __NR_pwritev 296
+#endif
+
+#if !defined(__NR_rt_tgsigqueueinfo)
+#define __NR_rt_tgsigqueueinfo 297
+#endif
+
+#if !defined(__NR_perf_event_open)
+#define __NR_perf_event_open 298
+#endif
+
+#if !defined(__NR_recvmmsg)
+#define __NR_recvmmsg 299
+#endif
+
+#if !defined(__NR_fanotify_init)
+#define __NR_fanotify_init 300
+#endif
+
+#if !defined(__NR_fanotify_mark)
+#define __NR_fanotify_mark 301
+#endif
+
+#if !defined(__NR_prlimit64)
+#define __NR_prlimit64 302
+#endif
+
+#if !defined(__NR_name_to_handle_at)
+#define __NR_name_to_handle_at 303
+#endif
+
+#if !defined(__NR_open_by_handle_at)
+#define __NR_open_by_handle_at 304
+#endif
+
+#if !defined(__NR_clock_adjtime)
+#define __NR_clock_adjtime 305
+#endif
+
+#if !defined(__NR_syncfs)
+#define __NR_syncfs 306
+#endif
+
+#if !defined(__NR_sendmmsg)
+#define __NR_sendmmsg 307
+#endif
+
+#if !defined(__NR_setns)
+#define __NR_setns 308
+#endif
+
+#if !defined(__NR_getcpu)
+#define __NR_getcpu 309
+#endif
+
+#if !defined(__NR_process_vm_readv)
+#define __NR_process_vm_readv 310
+#endif
+
+#if !defined(__NR_process_vm_writev)
+#define __NR_process_vm_writev 311
+#endif
+
+#if !defined(__NR_kcmp)
+#define __NR_kcmp 312
+#endif
+
+#if !defined(__NR_finit_module)
+#define __NR_finit_module 313
+#endif
+
+#if !defined(__NR_sched_setattr)
+#define __NR_sched_setattr 314
+#endif
+
+#if !defined(__NR_sched_getattr)
+#define __NR_sched_getattr 315
+#endif
+
+#if !defined(__NR_renameat2)
+#define __NR_renameat2 316
+#endif
+
+#if !defined(__NR_seccomp)
+#define __NR_seccomp 317
+#endif
+
+#if !defined(__NR_getrandom)
+#define __NR_getrandom 318
+#endif
+
+#if !defined(__NR_memfd_create)
+#define __NR_memfd_create 319
+#endif
+
+#if !defined(__NR_kexec_file_load)
+#define __NR_kexec_file_load 320
+#endif
+
+#if !defined(__NR_bpf)
+#define __NR_bpf 321
+#endif
+
+#if !defined(__NR_execveat)
+#define __NR_execveat 322
+#endif
+
+#if !defined(__NR_userfaultfd)
+#define __NR_userfaultfd 323
+#endif
+
+#if !defined(__NR_membarrier)
+#define __NR_membarrier 324
+#endif
+
+#if !defined(__NR_mlock2)
+#define __NR_mlock2 325
+#endif
+
+#if !defined(__NR_copy_file_range)
+#define __NR_copy_file_range 326
+#endif
+
+#if !defined(__NR_preadv2)
+#define __NR_preadv2 327
+#endif
+
+#if !defined(__NR_pwritev2)
+#define __NR_pwritev2 328
+#endif
+
+#if !defined(__NR_pkey_mprotect)
+#define __NR_pkey_mprotect 329
+#endif
+
+#if !defined(__NR_pkey_alloc)
+#define __NR_pkey_alloc 330
+#endif
+
+#if !defined(__NR_pkey_free)
+#define __NR_pkey_free 331
+#endif
+
+#if !defined(__NR_statx)
+#define __NR_statx 332
+#endif
+
+#if !defined(__NR_io_pgetevents)
+#define __NR_io_pgetevents 333
+#endif
+
+#if !defined(__NR_rseq)
+#define __NR_rseq 334
+#endif
+
+#if !defined(__NR_pidfd_send_signal)
+#define __NR_pidfd_send_signal 424
+#endif
+
+#if !defined(__NR_io_uring_setup)
+#define __NR_io_uring_setup 425
+#endif
+
+#if !defined(__NR_io_uring_enter)
+#define __NR_io_uring_enter 426
+#endif
+
+#if !defined(__NR_io_uring_register)
+#define __NR_io_uring_register 427
+#endif
+
+#if !defined(__NR_open_tree)
+#define __NR_open_tree 428
+#endif
+
+#if !defined(__NR_move_mount)
+#define __NR_move_mount 429
+#endif
+
+#if !defined(__NR_fsopen)
+#define __NR_fsopen 430
+#endif
+
+#if !defined(__NR_fsconfig)
+#define __NR_fsconfig 431
+#endif
+
+#if !defined(__NR_fsmount)
+#define __NR_fsmount 432
+#endif
+
+#if !defined(__NR_fspick)
+#define __NR_fspick 433
+#endif
+
+#if !defined(__NR_pidfd_open)
+#define __NR_pidfd_open 434
+#endif
+
+#if !defined(__NR_clone3)
+#define __NR_clone3 435
+#endif
+
+#if !defined(__NR_openat2)
+#define __NR_openat2 437
+#endif
+
+#if !defined(__NR_pidfd_getfd)
+#define __NR_pidfd_getfd 438
+#endif
+
+#if !defined(__NR_faccessat2)
+#define __NR_faccessat2 439
+#endif
+
+#endif // SANDBOX_LINUX_SYSTEM_HEADERS_X86_64_LINUX_SYSCALLS_H_
diff --git a/security/sandbox/chromium/sandbox/sandbox_export.h b/security/sandbox/chromium/sandbox/sandbox_export.h
new file mode 100644
index 0000000000..35d6a1ba26
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/sandbox_export.h
@@ -0,0 +1,26 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SANDBOX_EXPORT_H_
+#define SANDBOX_SANDBOX_EXPORT_H_
+
+#if defined(WIN32)
+#error "sandbox_export.h does not support WIN32."
+#endif
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(SANDBOX_IMPLEMENTATION)
+#define SANDBOX_EXPORT __attribute__((visibility("default")))
+#else
+#define SANDBOX_EXPORT
+#endif // defined(SANDBOX_IMPLEMENTATION)
+
+#else // defined(COMPONENT_BUILD)
+
+#define SANDBOX_EXPORT
+
+#endif // defined(COMPONENT_BUILD)
+
+#endif // SANDBOX_SANDBOX_EXPORT_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/acl.cc b/security/sandbox/chromium/sandbox/win/src/acl.cc
new file mode 100644
index 0000000000..bd0b181833
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/acl.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/acl.h"
+
+#include <aclapi.h>
+#include <sddl.h>
+
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+
+namespace sandbox {
+
+bool GetDefaultDacl(
+ HANDLE token,
+ std::unique_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_dacl) {
+ if (!token)
+ return false;
+
+ DCHECK(default_dacl);
+
+ unsigned long length = 0;
+ ::GetTokenInformation(token, TokenDefaultDacl, nullptr, 0, &length);
+ if (length == 0) {
+ NOTREACHED();
+ return false;
+ }
+
+ TOKEN_DEFAULT_DACL* acl =
+ reinterpret_cast<TOKEN_DEFAULT_DACL*>(malloc(length));
+ default_dacl->reset(acl);
+
+ if (!::GetTokenInformation(token, TokenDefaultDacl, default_dacl->get(),
+ length, &length))
+ return false;
+
+ return true;
+}
+
+bool AddSidToDacl(const Sid& sid,
+ ACL* old_dacl,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access,
+ ACL** new_dacl) {
+ EXPLICIT_ACCESS new_access = {0};
+ new_access.grfAccessMode = access_mode;
+ new_access.grfAccessPermissions = access;
+ new_access.grfInheritance = NO_INHERITANCE;
+
+ new_access.Trustee.pMultipleTrustee = nullptr;
+ new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(sid.GetPSID());
+
+ if (ERROR_SUCCESS != ::SetEntriesInAcl(1, &new_access, old_dacl, new_dacl))
+ return false;
+
+ return true;
+}
+
+bool AddSidToDefaultDacl(HANDLE token,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ if (!token)
+ return false;
+
+ std::unique_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter> default_dacl;
+ if (!GetDefaultDacl(token, &default_dacl))
+ return false;
+
+ ACL* new_dacl = nullptr;
+ if (!AddSidToDacl(sid, default_dacl->DefaultDacl, access_mode, access,
+ &new_dacl))
+ return false;
+
+ TOKEN_DEFAULT_DACL new_token_dacl = {0};
+ new_token_dacl.DefaultDacl = new_dacl;
+
+ bool ret = ::SetTokenInformation(token, TokenDefaultDacl, &new_token_dacl,
+ sizeof(new_token_dacl));
+ ::LocalFree(new_dacl);
+ return ret;
+}
+
+bool RevokeLogonSidFromDefaultDacl(HANDLE token) {
+ DWORD size = sizeof(TOKEN_GROUPS) + SECURITY_MAX_SID_SIZE;
+ TOKEN_GROUPS* logon_sid = reinterpret_cast<TOKEN_GROUPS*>(malloc(size));
+
+ std::unique_ptr<TOKEN_GROUPS, base::FreeDeleter> logon_sid_ptr(logon_sid);
+
+ if (!::GetTokenInformation(token, TokenLogonSid, logon_sid, size, &size)) {
+ // If no logon sid, there's nothing to revoke.
+ if (::GetLastError() == ERROR_NOT_FOUND)
+ return true;
+ return false;
+ }
+ if (logon_sid->GroupCount < 1) {
+ ::SetLastError(ERROR_INVALID_TOKEN);
+ return false;
+ }
+ return AddSidToDefaultDacl(token,
+ reinterpret_cast<SID*>(logon_sid->Groups[0].Sid),
+ REVOKE_ACCESS, 0);
+}
+
+bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) {
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(malloc(size));
+
+ std::unique_ptr<TOKEN_USER, base::FreeDeleter> token_user_ptr(token_user);
+
+ if (!::GetTokenInformation(token, TokenUser, token_user, size, &size))
+ return false;
+
+ return AddSidToDefaultDacl(token,
+ reinterpret_cast<SID*>(token_user->User.Sid),
+ GRANT_ACCESS, access);
+}
+
+bool AddKnownSidToObject(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ PSECURITY_DESCRIPTOR descriptor = nullptr;
+ PACL old_dacl = nullptr;
+ PACL new_dacl = nullptr;
+
+ if (ERROR_SUCCESS !=
+ ::GetSecurityInfo(object, object_type, DACL_SECURITY_INFORMATION, nullptr,
+ nullptr, &old_dacl, nullptr, &descriptor))
+ return false;
+
+ if (!AddSidToDacl(sid, old_dacl, access_mode, access, &new_dacl)) {
+ ::LocalFree(descriptor);
+ return false;
+ }
+
+ DWORD result =
+ ::SetSecurityInfo(object, object_type, DACL_SECURITY_INFORMATION, nullptr,
+ nullptr, new_dacl, nullptr);
+
+ ::LocalFree(new_dacl);
+ ::LocalFree(descriptor);
+
+ if (ERROR_SUCCESS != result)
+ return false;
+
+ return true;
+}
+
+bool ReplacePackageSidInDacl(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& package_sid,
+ ACCESS_MASK access) {
+ if (!AddKnownSidToObject(object, object_type, package_sid, REVOKE_ACCESS,
+ 0)) {
+ return false;
+ }
+
+ Sid any_package_sid(::WinBuiltinAnyPackageSid);
+ if (!AddKnownSidToObject(object, object_type, any_package_sid, GRANT_ACCESS,
+ access)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/acl.h b/security/sandbox/chromium/sandbox/win/src/acl.h
new file mode 100644
index 0000000000..194edb0988
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/acl.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_ACL_H_
+#define SANDBOX_SRC_ACL_H_
+
+#include <accctrl.h>
+#include <windows.h>
+
+#include <memory>
+
+#include "base/memory/free_deleter.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+// Returns the default dacl from the token passed in.
+bool GetDefaultDacl(
+ HANDLE token,
+ std::unique_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_dacl);
+
+// Appends an ACE represented by |sid|, |access_mode|, and |access| to
+// |old_dacl|. If the function succeeds, new_dacl contains the new dacl and
+// must be freed using LocalFree.
+bool AddSidToDacl(const Sid& sid,
+ ACL* old_dacl,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access,
+ ACL** new_dacl);
+
+// Adds an ACE represented by |sid| and |access| with |access_mode| to the
+// default dacl present in the token.
+bool AddSidToDefaultDacl(HANDLE token,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+// Revokes access to the logon SID for the default dacl present in the token.
+bool RevokeLogonSidFromDefaultDacl(HANDLE token);
+
+// Adds an ACE represented by the user sid and |access| to the default dacl
+// present in the token.
+bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access);
+
+// Adds an ACE represented by |known_sid|, |access_mode|, and |access| to
+// the dacl of the kernel object referenced by |object| and of |object_type|.
+bool AddKnownSidToObject(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+// Replace package SID in DACL to the "any package" SID. It allows Low-IL
+// tokens to open the object which is important for warm up when using renderer
+// AppContainer.
+bool ReplacePackageSidInDacl(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& package_sid,
+ ACCESS_MASK access);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_ACL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile.h b/security/sandbox/chromium/sandbox/win/src/app_container_profile.h
new file mode 100644
index 0000000000..c95c68e552
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile.h
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All 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_APP_CONTAINER_PROFILE_H_
+#define SANDBOX_SRC_APP_CONTAINER_PROFILE_H_
+
+#include <windows.h>
+
+#include <accctrl.h>
+
+#include "base/files/file_path.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+class AppContainerProfile {
+ public:
+ // Increments the reference count of this object. The reference count must
+ // be incremented if this interface is given to another component.
+ virtual void AddRef() = 0;
+
+ // Decrements the reference count of this object. When the reference count
+ // is zero the object is automatically destroyed.
+ // Indicates that the caller is done with this interface. After calling
+ // release no other method should be called.
+ virtual void Release() = 0;
+
+ // Get a handle to a registry key for this package.
+ virtual bool GetRegistryLocation(REGSAM desired_access,
+ base::win::ScopedHandle* key) = 0;
+
+ // Get a folder path to a location for this package.
+ virtual bool GetFolderPath(base::FilePath* file_path) = 0;
+
+ // Get a pipe name usable by this AC.
+ virtual bool GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) = 0;
+
+ // Do an access check based on this profile for a named object. If method
+ // returns true then access_status reflects whether access was granted and
+ // granted_access gives the final access rights. The object_type can be one of
+ // SE_FILE_OBJECT, SE_REGISTRY_KEY, SE_REGISTRY_WOW64_32KEY. See
+ // ::GetNamedSecurityInfo for more information about how the enumeration is
+ // used and what format object_name needs to be.
+ virtual bool AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) = 0;
+
+ // Adds a capability by name to this profile.
+ virtual bool AddCapability(const wchar_t* capability_name) = 0;
+ // Adds a capability from a known list.
+ virtual bool AddCapability(WellKnownCapabilities capability) = 0;
+ // Adds a capability from a SID
+ virtual bool AddCapabilitySddl(const wchar_t* sddl_sid) = 0;
+
+ // Adds an impersonation capability by name to this profile.
+ virtual bool AddImpersonationCapability(const wchar_t* capability_name) = 0;
+ // Adds an impersonation capability from a known list.
+ virtual bool AddImpersonationCapability(WellKnownCapabilities capability) = 0;
+ // Adds an impersonation capability from a SID
+ virtual bool AddImpersonationCapabilitySddl(const wchar_t* sddl_sid) = 0;
+
+ // Enable Low Privilege AC.
+ virtual void SetEnableLowPrivilegeAppContainer(bool enable) = 0;
+ virtual bool GetEnableLowPrivilegeAppContainer() = 0;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_APP_CONTAINER_PROFILE_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
new file mode 100644
index 0000000000..e388a9978e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
@@ -0,0 +1,337 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include <aclapi.h>
+#include <userenv.h>
+
+#include "base/strings/stringprintf.h"
+#if !defined(MOZ_SANDBOX)
+#include "base/win/scoped_co_mem.h"
+#endif
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile_base.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+typedef decltype(::CreateAppContainerProfile) CreateAppContainerProfileFunc;
+
+typedef decltype(::DeriveAppContainerSidFromAppContainerName)
+ DeriveAppContainerSidFromAppContainerNameFunc;
+
+typedef decltype(::DeleteAppContainerProfile) DeleteAppContainerProfileFunc;
+
+typedef decltype(::GetAppContainerFolderPath) GetAppContainerFolderPathFunc;
+
+typedef decltype(
+ ::GetAppContainerRegistryLocation) GetAppContainerRegistryLocationFunc;
+
+struct FreeSidDeleter {
+ inline void operator()(void* ptr) const { ::FreeSid(ptr); }
+};
+
+bool IsValidObjectType(SE_OBJECT_TYPE object_type) {
+ switch (object_type) {
+ case SE_FILE_OBJECT:
+ case SE_REGISTRY_KEY:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool GetGenericMappingForType(SE_OBJECT_TYPE object_type,
+ GENERIC_MAPPING* generic_mapping) {
+ if (!IsValidObjectType(object_type))
+ return false;
+ if (object_type == SE_FILE_OBJECT) {
+ generic_mapping->GenericRead = FILE_GENERIC_READ;
+ generic_mapping->GenericWrite = FILE_GENERIC_WRITE;
+ generic_mapping->GenericExecute = FILE_GENERIC_EXECUTE;
+ generic_mapping->GenericAll = FILE_ALL_ACCESS;
+ } else {
+ generic_mapping->GenericRead = KEY_READ;
+ generic_mapping->GenericWrite = KEY_WRITE;
+ generic_mapping->GenericExecute = KEY_EXECUTE;
+ generic_mapping->GenericAll = KEY_ALL_ACCESS;
+ }
+ return true;
+}
+
+class ScopedImpersonation {
+ public:
+ ScopedImpersonation(const base::win::ScopedHandle& token) {
+ BOOL result = ::ImpersonateLoggedOnUser(token.Get());
+ DCHECK(result);
+ }
+
+ ~ScopedImpersonation() {
+ BOOL result = ::RevertToSelf();
+ DCHECK(result);
+ }
+};
+
+} // namespace
+
+// static
+AppContainerProfileBase* AppContainerProfileBase::Create(
+ const wchar_t* package_name,
+ const wchar_t* display_name,
+ const wchar_t* description) {
+ static auto create_app_container_profile =
+ reinterpret_cast<CreateAppContainerProfileFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "CreateAppContainerProfile"));
+ if (!create_app_container_profile)
+ return nullptr;
+
+ PSID package_sid = nullptr;
+ HRESULT hr = create_app_container_profile(
+ package_name, display_name, description, nullptr, 0, &package_sid);
+ if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
+ return Open(package_name);
+
+ if (FAILED(hr))
+ return nullptr;
+ std::unique_ptr<void, FreeSidDeleter> sid_deleter(package_sid);
+ return new AppContainerProfileBase(Sid(package_sid));
+}
+
+// static
+AppContainerProfileBase* AppContainerProfileBase::Open(
+ const wchar_t* package_name) {
+ static auto derive_app_container_sid =
+ reinterpret_cast<DeriveAppContainerSidFromAppContainerNameFunc*>(
+ GetProcAddress(GetModuleHandle(L"userenv"),
+ "DeriveAppContainerSidFromAppContainerName"));
+ if (!derive_app_container_sid)
+ return nullptr;
+
+ PSID package_sid = nullptr;
+ HRESULT hr = derive_app_container_sid(package_name, &package_sid);
+ if (FAILED(hr))
+ return nullptr;
+
+ std::unique_ptr<void, FreeSidDeleter> sid_deleter(package_sid);
+ return new AppContainerProfileBase(Sid(package_sid));
+}
+
+// static
+bool AppContainerProfileBase::Delete(const wchar_t* package_name) {
+ static auto delete_app_container_profile =
+ reinterpret_cast<DeleteAppContainerProfileFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "DeleteAppContainerProfile"));
+ if (!delete_app_container_profile)
+ return false;
+
+ return SUCCEEDED(delete_app_container_profile(package_name));
+}
+
+AppContainerProfileBase::AppContainerProfileBase(const Sid& package_sid)
+ : ref_count_(0),
+ package_sid_(package_sid),
+ enable_low_privilege_app_container_(false) {}
+
+AppContainerProfileBase::~AppContainerProfileBase() {}
+
+void AppContainerProfileBase::AddRef() {
+ ::InterlockedIncrement(&ref_count_);
+}
+
+void AppContainerProfileBase::Release() {
+ LONG ref_count = ::InterlockedDecrement(&ref_count_);
+ if (ref_count == 0) {
+ delete this;
+ }
+}
+
+bool AppContainerProfileBase::GetRegistryLocation(
+ REGSAM desired_access,
+ base::win::ScopedHandle* key) {
+ static GetAppContainerRegistryLocationFunc*
+ get_app_container_registry_location =
+ reinterpret_cast<GetAppContainerRegistryLocationFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "GetAppContainerRegistryLocation"));
+ if (!get_app_container_registry_location)
+ return false;
+
+ base::win::ScopedHandle token;
+ if (!BuildLowBoxToken(&token))
+ return false;
+
+ ScopedImpersonation impersonation(token);
+ HKEY key_handle;
+ if (FAILED(get_app_container_registry_location(desired_access, &key_handle)))
+ return false;
+ key->Set(key_handle);
+ return true;
+}
+
+bool AppContainerProfileBase::GetFolderPath(base::FilePath* file_path) {
+#if defined(MOZ_SANDBOX)
+ IMMEDIATE_CRASH();
+#else
+ static GetAppContainerFolderPathFunc* get_app_container_folder_path =
+ reinterpret_cast<GetAppContainerFolderPathFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "GetAppContainerFolderPath"));
+ if (!get_app_container_folder_path)
+ return false;
+ std::wstring sddl_str;
+ if (!package_sid_.ToSddlString(&sddl_str))
+ return false;
+ base::win::ScopedCoMem<wchar_t> path_str;
+ if (FAILED(get_app_container_folder_path(sddl_str.c_str(), &path_str)))
+ return false;
+ *file_path = base::FilePath(path_str.get());
+ return true;
+#endif
+}
+
+bool AppContainerProfileBase::GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) {
+#if defined(MOZ_SANDBOX)
+ IMMEDIATE_CRASH();
+#else
+ std::wstring sddl_str;
+ if (!package_sid_.ToSddlString(&sddl_str))
+ return false;
+ *pipe_path = base::FilePath(base::StringPrintf(L"\\\\.\\pipe\\%ls\\%ls",
+ sddl_str.c_str(), pipe_name));
+ return true;
+#endif
+}
+
+bool AppContainerProfileBase::AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) {
+ GENERIC_MAPPING generic_mapping;
+ if (!GetGenericMappingForType(object_type, &generic_mapping))
+ return false;
+ MapGenericMask(&desired_access, &generic_mapping);
+ PSECURITY_DESCRIPTOR sd = nullptr;
+ PACL dacl = nullptr;
+ if (GetNamedSecurityInfo(
+ object_name, object_type,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
+ nullptr, nullptr, &dacl, nullptr, &sd) != ERROR_SUCCESS) {
+ return false;
+ }
+
+ std::unique_ptr<void, LocalFreeDeleter> sd_ptr(sd);
+
+ if (enable_low_privilege_app_container_) {
+ Sid any_package_sid(::WinBuiltinAnyPackageSid);
+ // We can't create a LPAC token directly, so modify the DACL to simulate it.
+ // Set mask for ALL APPLICATION PACKAGE Sid to 0.
+ for (WORD index = 0; index < dacl->AceCount; ++index) {
+ PVOID temp_ace;
+ if (!GetAce(dacl, index, &temp_ace))
+ return false;
+ PACE_HEADER header = static_cast<PACE_HEADER>(temp_ace);
+ if ((header->AceType != ACCESS_ALLOWED_ACE_TYPE) &&
+ (header->AceType != ACCESS_DENIED_ACE_TYPE)) {
+ continue;
+ }
+ // Allowed and deny aces have the same underlying structure.
+ PACCESS_ALLOWED_ACE ace = static_cast<PACCESS_ALLOWED_ACE>(temp_ace);
+ if (!::IsValidSid(&ace->SidStart)) {
+ continue;
+ }
+ if (::EqualSid(&ace->SidStart, any_package_sid.GetPSID())) {
+ ace->Mask = 0;
+ }
+ }
+ }
+
+ PRIVILEGE_SET priv_set = {};
+ DWORD priv_set_length = sizeof(PRIVILEGE_SET);
+
+ base::win::ScopedHandle token;
+ if (!BuildLowBoxToken(&token))
+ return false;
+
+ return !!::AccessCheck(sd, token.Get(), desired_access, &generic_mapping,
+ &priv_set, &priv_set_length, granted_access,
+ access_status);
+}
+
+bool AppContainerProfileBase::AddCapability(const wchar_t* capability_name) {
+ return AddCapability(Sid::FromNamedCapability(capability_name), false);
+}
+
+bool AppContainerProfileBase::AddCapability(WellKnownCapabilities capability) {
+ return AddCapability(Sid::FromKnownCapability(capability), false);
+}
+
+bool AppContainerProfileBase::AddCapabilitySddl(const wchar_t* sddl_sid) {
+ return AddCapability(Sid::FromSddlString(sddl_sid), false);
+}
+
+bool AppContainerProfileBase::AddCapability(const Sid& capability_sid,
+ bool impersonation_only) {
+ if (!capability_sid.IsValid())
+ return false;
+ if (!impersonation_only)
+ capabilities_.push_back(capability_sid);
+ impersonation_capabilities_.push_back(capability_sid);
+ return true;
+}
+
+bool AppContainerProfileBase::AddImpersonationCapability(
+ const wchar_t* capability_name) {
+ return AddCapability(Sid::FromNamedCapability(capability_name), true);
+}
+
+bool AppContainerProfileBase::AddImpersonationCapability(
+ WellKnownCapabilities capability) {
+ return AddCapability(Sid::FromKnownCapability(capability), true);
+}
+
+bool AppContainerProfileBase::AddImpersonationCapabilitySddl(
+ const wchar_t* sddl_sid) {
+ return AddCapability(Sid::FromSddlString(sddl_sid), true);
+}
+
+const std::vector<Sid>& AppContainerProfileBase::GetCapabilities() {
+ return capabilities_;
+}
+
+const std::vector<Sid>&
+AppContainerProfileBase::GetImpersonationCapabilities() {
+ return impersonation_capabilities_;
+}
+
+Sid AppContainerProfileBase::GetPackageSid() const {
+ return package_sid_;
+}
+
+void AppContainerProfileBase::SetEnableLowPrivilegeAppContainer(bool enable) {
+ enable_low_privilege_app_container_ = enable;
+}
+
+bool AppContainerProfileBase::GetEnableLowPrivilegeAppContainer() {
+ return enable_low_privilege_app_container_;
+}
+
+std::unique_ptr<SecurityCapabilities>
+AppContainerProfileBase::GetSecurityCapabilities() {
+ return std::unique_ptr<SecurityCapabilities>(
+ new SecurityCapabilities(package_sid_, capabilities_));
+}
+
+bool AppContainerProfileBase::BuildLowBoxToken(base::win::ScopedHandle* token) {
+ return CreateLowBoxToken(nullptr, IMPERSONATION,
+ GetSecurityCapabilities().get(), nullptr, 0,
+ token) == ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h
new file mode 100644
index 0000000000..35fb4efdf5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h
@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All 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_APP_CONTAINER_PROFILE_BASE_H_
+#define SANDBOX_SRC_APP_CONTAINER_PROFILE_BASE_H_
+
+#include <windows.h>
+
+#include <accctrl.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+class AppContainerProfileBase final : public AppContainerProfile {
+ public:
+ void AddRef() override;
+ void Release() override;
+ bool GetRegistryLocation(REGSAM desired_access,
+ base::win::ScopedHandle* key) override;
+ bool GetFolderPath(base::FilePath* file_path) override;
+ bool GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) override;
+ bool AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) override;
+ bool AddCapability(const wchar_t* capability_name) override;
+ bool AddCapability(WellKnownCapabilities capability) override;
+ bool AddCapabilitySddl(const wchar_t* sddl_sid) override;
+ bool AddImpersonationCapability(const wchar_t* capability_name) override;
+ bool AddImpersonationCapability(WellKnownCapabilities capability) override;
+ bool AddImpersonationCapabilitySddl(const wchar_t* sddl_sid) override;
+ void SetEnableLowPrivilegeAppContainer(bool enable) override;
+ bool GetEnableLowPrivilegeAppContainer() override;
+
+ // Get the package SID for this AC.
+ Sid GetPackageSid() const;
+
+ // Get an allocated SecurityCapabilities object for this App Container.
+ std::unique_ptr<SecurityCapabilities> GetSecurityCapabilities();
+
+ // Get a vector of capabilities.
+ const std::vector<Sid>& GetCapabilities();
+
+ // Get a vector of impersonation only capabilities. Used if the process needs
+ // a more privileged token to start.
+ const std::vector<Sid>& GetImpersonationCapabilities();
+
+ // Creates a new AppContainerProfile object. This will create a new profile
+ // if it doesn't already exist. The profile must be deleted manually using
+ // the Delete method if it's no longer required.
+ static AppContainerProfileBase* Create(const wchar_t* package_name,
+ const wchar_t* display_name,
+ const wchar_t* description);
+
+ // Opens an AppContainerProfile object. No checks will be made on
+ // whether the package exists or not.
+ static AppContainerProfileBase* Open(const wchar_t* package_name);
+
+ // Delete a profile based on name. Returns true if successful, or if the
+ // package doesn't already exist.
+ static bool Delete(const wchar_t* package_name);
+
+ private:
+ AppContainerProfileBase(const Sid& package_sid);
+ ~AppContainerProfileBase();
+
+ bool BuildLowBoxToken(base::win::ScopedHandle* token);
+ bool AddCapability(const Sid& capability_sid, bool impersonation_only);
+
+ // Standard object-lifetime reference counter.
+ volatile LONG ref_count_;
+ Sid package_sid_;
+ bool enable_low_privilege_app_container_;
+ std::vector<Sid> capabilities_;
+ std::vector<Sid> impersonation_capabilities_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppContainerProfileBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_APP_CONTAINER_PROFILE_BASE_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_test.cc b/security/sandbox/chromium/sandbox/win/src/app_container_test.cc
new file mode 100644
index 0000000000..a6c0948a94
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_test.cc
@@ -0,0 +1,342 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include <sddl.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile_base.h"
+#include "sandbox/win/src/sync_policy_test.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+const wchar_t kAppContainerSid[] =
+ L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-"
+ L"924012148-2839372144";
+
+std::wstring GenerateRandomPackageName() {
+ return base::StringPrintf(L"%016lX%016lX", base::RandUint64(),
+ base::RandUint64());
+}
+
+const char* TokenTypeToName(TOKEN_TYPE token_type) {
+ return token_type == ::TokenPrimary ? "Primary Token" : "Impersonation Token";
+}
+
+void CheckToken(HANDLE token,
+ TOKEN_TYPE token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ BOOL restricted) {
+ ASSERT_EQ(restricted, ::IsTokenRestricted(token))
+ << TokenTypeToName(token_type);
+
+ DWORD appcontainer;
+ DWORD return_length;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenIsAppContainer, &appcontainer,
+ sizeof(appcontainer), &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_TRUE(appcontainer) << TokenTypeToName(token_type);
+ TOKEN_TYPE token_type_real;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenType, &token_type_real,
+ sizeof(token_type_real), &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_EQ(token_type_real, token_type) << TokenTypeToName(token_type);
+ if (token_type == ::TokenImpersonation) {
+ SECURITY_IMPERSONATION_LEVEL imp_level;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenImpersonationLevel,
+ &imp_level, sizeof(imp_level),
+ &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_EQ(imp_level, ::SecurityImpersonation)
+ << TokenTypeToName(token_type);
+ }
+
+ std::unique_ptr<Sid> package_sid;
+ ASSERT_TRUE(GetTokenAppContainerSid(token, &package_sid))
+ << TokenTypeToName(token_type);
+ EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
+ package_sid->GetPSID()))
+ << TokenTypeToName(token_type);
+
+ std::vector<SidAndAttributes> capabilities;
+ ASSERT_TRUE(GetTokenGroups(token, ::TokenCapabilities, &capabilities))
+ << TokenTypeToName(token_type);
+
+ ASSERT_EQ(capabilities.size(), security_capabilities->CapabilityCount)
+ << TokenTypeToName(token_type);
+ for (size_t index = 0; index < capabilities.size(); ++index) {
+ EXPECT_EQ(capabilities[index].GetAttributes(),
+ security_capabilities->Capabilities[index].Attributes)
+ << TokenTypeToName(token_type);
+ EXPECT_TRUE(::EqualSid(capabilities[index].GetPSID(),
+ security_capabilities->Capabilities[index].Sid))
+ << TokenTypeToName(token_type);
+ }
+}
+
+void CheckProcessToken(HANDLE process,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool restricted) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ CheckToken(token_handle, ::TokenPrimary, security_capabilities, restricted);
+}
+
+void CheckThreadToken(HANDLE thread,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool restricted) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenThreadToken(thread, TOKEN_ALL_ACCESS, TRUE, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ CheckToken(token_handle, ::TokenImpersonation, security_capabilities,
+ restricted);
+}
+
+// Check for LPAC using an access check. We could query for a security attribute
+// but that's undocumented and has the potential to change.
+void CheckLpacToken(HANDLE process) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ ASSERT_TRUE(
+ ::DuplicateToken(token.Get(), ::SecurityImpersonation, &token_handle));
+ token.Set(token_handle);
+ PSECURITY_DESCRIPTOR security_desc_ptr;
+ // AC is AllPackages, S-1-15-2-2 is AllRestrictedPackages. An LPAC token
+ // will get granted access of 2, where as a normal AC token will get 3.
+ ASSERT_TRUE(::ConvertStringSecurityDescriptorToSecurityDescriptor(
+ L"O:SYG:SYD:(A;;0x3;;;WD)(A;;0x1;;;AC)(A;;0x2;;;S-1-15-2-2)",
+ SDDL_REVISION_1, &security_desc_ptr, nullptr));
+ std::unique_ptr<void, LocalFreeDeleter> security_desc(security_desc_ptr);
+ GENERIC_MAPPING generic_mapping = {};
+ PRIVILEGE_SET priv_set = {};
+ DWORD priv_set_length = sizeof(PRIVILEGE_SET);
+ DWORD granted_access;
+ BOOL access_status;
+ ASSERT_TRUE(::AccessCheck(security_desc_ptr, token.Get(), MAXIMUM_ALLOWED,
+ &generic_mapping, &priv_set, &priv_set_length,
+ &granted_access, &access_status));
+ ASSERT_TRUE(access_status);
+ ASSERT_EQ(DWORD{2}, granted_access);
+}
+
+class AppContainerProfileTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+ package_name_ = GenerateRandomPackageName();
+ broker_services_ = GetBroker();
+ policy_ = broker_services_->CreatePolicy();
+ ASSERT_EQ(SBOX_ALL_OK,
+ policy_->SetProcessMitigations(MITIGATION_HEAP_TERMINATE));
+ ASSERT_EQ(SBOX_ALL_OK,
+ policy_->AddAppContainerProfile(package_name_.c_str(), true));
+ // For testing purposes we known the base class so cast directly.
+ profile_ = static_cast<AppContainerProfileBase*>(
+ policy_->GetAppContainerProfile().get());
+ }
+
+ void TearDown() override {
+ if (scoped_process_info_.IsValid())
+ ::TerminateProcess(scoped_process_info_.process_handle(), 0);
+ if (profile_)
+ AppContainerProfileBase::Delete(package_name_.c_str());
+ }
+
+ protected:
+ void CreateProcess() {
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH] = {};
+ ASSERT_NE(DWORD{0}, ::GetModuleFileNameW(nullptr, prog_name, MAX_PATH));
+
+ PROCESS_INFORMATION process_info = {};
+ ResultCode last_warning = SBOX_ALL_OK;
+ DWORD last_error = 0;
+ ResultCode result = broker_services_->SpawnTarget(
+ prog_name, prog_name, policy_, &last_warning, &last_error,
+ &process_info);
+ ASSERT_EQ(SBOX_ALL_OK, result) << "Last Error: " << last_error;
+ scoped_process_info_.Set(process_info);
+ }
+
+ std::wstring package_name_;
+ BrokerServices* broker_services_;
+ scoped_refptr<AppContainerProfileBase> profile_;
+ scoped_refptr<TargetPolicy> policy_;
+ base::win::ScopedProcessInformation scoped_process_info_;
+};
+
+} // namespace
+
+
+TEST(AppContainerTest, DenyOpenEventForLowBox) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
+
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetLowBox(kAppContainerSid));
+ // Run test once, this ensures the app container directory exists, we
+ // ignore the result.
+ runner.RunTest(L"Event_Open f test");
+ std::wstring event_name = L"AppContainerNamedObjects\\";
+ event_name += kAppContainerSid;
+ event_name += L"\\test";
+
+ base::win::ScopedHandle event(
+ ::CreateEvent(nullptr, false, false, event_name.c_str()));
+ ASSERT_TRUE(event.IsValid());
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
+}
+
+TEST_F(AppContainerProfileTest, CheckIncompatibleOptions) {
+ if (!profile_)
+ return;
+ EXPECT_EQ(SBOX_ERROR_BAD_PARAMS,
+ policy_->SetIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED));
+ EXPECT_EQ(SBOX_ERROR_BAD_PARAMS, policy_->SetLowBox(kAppContainerSid));
+
+ MitigationFlags expected_mitigations = 0;
+ MitigationFlags expected_delayed = MITIGATION_HEAP_TERMINATE;
+ sandbox::ResultCode expected_result = SBOX_ERROR_BAD_PARAMS;
+
+ if (base::win::GetVersion() >= base::win::Version::WIN10_RS5) {
+ expected_mitigations = MITIGATION_HEAP_TERMINATE;
+ expected_delayed = 0;
+ expected_result = SBOX_ALL_OK;
+ }
+
+ EXPECT_EQ(expected_mitigations, policy_->GetProcessMitigations());
+ EXPECT_EQ(expected_delayed, policy_->GetDelayedProcessMitigations());
+ EXPECT_EQ(expected_result,
+ policy_->SetProcessMitigations(MITIGATION_HEAP_TERMINATE));
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilities) {
+ if (!profile_)
+ return;
+
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilitiesRestricted) {
+ if (!profile_)
+ return;
+
+ policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), TRUE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), TRUE);
+}
+
+TEST_F(AppContainerProfileTest, WithCapabilities) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+}
+
+TEST_F(AppContainerProfileTest, WithCapabilitiesRestricted) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), TRUE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), TRUE);
+}
+
+TEST_F(AppContainerProfileTest, WithImpersonationCapabilities) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ profile_->AddImpersonationCapability(kPrivateNetworkClientServer);
+ profile_->AddImpersonationCapability(kPicturesLibrary);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ SecurityCapabilities impersonation_security_capabilities(
+ profile_->GetPackageSid(), profile_->GetImpersonationCapabilities());
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ &impersonation_security_capabilities, FALSE);
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilitiesLPAC) {
+ if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
+ return;
+
+ profile_->SetEnableLowPrivilegeAppContainer(true);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+ CheckLpacToken(scoped_process_info_.process_handle());
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
new file mode 100644
index 0000000000..15ed321071
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
@@ -0,0 +1,745 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/broker_services.h"
+
+#include <aclapi.h>
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/sandbox_policy_diagnostic.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Utility function to associate a completion port to a job object.
+bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) {
+ JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = {key, port};
+ return ::SetInformationJobObject(job,
+ JobObjectAssociateCompletionPortInformation,
+ &job_acp, sizeof(job_acp))
+ ? true
+ : false;
+}
+
+// Utility function to do the cleanup necessary when something goes wrong
+// while in SpawnTarget and we must terminate the target process.
+sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target) {
+ target->Terminate();
+ delete target;
+ return sandbox::SBOX_ERROR_GENERIC;
+}
+
+// the different commands that you can send to the worker thread that
+// executes TargetEventsThread().
+enum {
+ THREAD_CTRL_NONE,
+ THREAD_CTRL_NEW_JOB_TRACKER,
+ THREAD_CTRL_NEW_PROCESS_TRACKER,
+ THREAD_CTRL_PROCESS_SIGNALLED,
+ THREAD_CTRL_GET_POLICY_INFO,
+ THREAD_CTRL_QUIT,
+ THREAD_CTRL_LAST,
+};
+
+// Helper structure that allows the Broker to associate a job notification
+// with a job object and with a policy.
+struct JobTracker {
+ JobTracker(base::win::ScopedHandle job,
+ scoped_refptr<sandbox::PolicyBase> policy,
+ DWORD process_id)
+ : job(std::move(job)), policy(policy), process_id(process_id) {}
+ ~JobTracker() { FreeResources(); }
+
+ // Releases the Job and notifies the associated Policy object to release its
+ // resources as well.
+ void FreeResources();
+
+ base::win::ScopedHandle job;
+ scoped_refptr<sandbox::PolicyBase> policy;
+ DWORD process_id;
+};
+
+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 = nullptr;
+ }
+}
+
+// tracks processes that are not in jobs
+struct ProcessTracker {
+ ProcessTracker(scoped_refptr<sandbox::PolicyBase> policy,
+ DWORD process_id,
+ base::win::ScopedHandle process)
+ : policy(policy), process_id(process_id), process(std::move(process)) {}
+ ~ProcessTracker() { FreeResources(); }
+
+ void FreeResources();
+
+ scoped_refptr<sandbox::PolicyBase> policy;
+ DWORD process_id;
+ base::win::ScopedHandle process;
+ // Used to UnregisterWait. Not a real handle so cannot CloseHandle().
+ HANDLE wait_handle;
+ // IOCP that is tracking this non-job process
+ HANDLE iocp;
+};
+
+void ProcessTracker::FreeResources() {
+ if (policy) {
+ policy->OnJobEmpty(nullptr);
+ policy = nullptr;
+ }
+}
+
+// Helper redispatches process events to tracker thread.
+void WINAPI ProcessEventCallback(PVOID param, BOOLEAN ignored) {
+ // This callback should do very little, and must be threadpool safe.
+ ProcessTracker* tracker = reinterpret_cast<ProcessTracker*>(param);
+ // If this fails we can do nothing... we will leak the policy.
+ ::PostQueuedCompletionStatus(tracker->iocp, 0, THREAD_CTRL_PROCESS_SIGNALLED,
+ reinterpret_cast<LPOVERLAPPED>(tracker));
+}
+
+// Helper class to send policy lists
+class PolicyDiagnosticList final : public sandbox::PolicyList {
+ public:
+ PolicyDiagnosticList() {}
+ ~PolicyDiagnosticList() override {}
+ void push_back(std::unique_ptr<sandbox::PolicyInfo> info) {
+ internal_list_.push_back(std::move(info));
+ }
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator begin() override {
+ return internal_list_.begin();
+ }
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator end() override {
+ return internal_list_.end();
+ }
+ size_t size() const override { return internal_list_.size(); }
+
+ private:
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>> internal_list_;
+};
+
+} // namespace
+
+namespace sandbox {
+
+BrokerServicesBase::BrokerServicesBase() {}
+
+// 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 (job_port_.IsValid() || thread_pool_)
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ ::InitializeCriticalSection(&lock_);
+
+ job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0));
+ if (!job_port_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ no_targets_.Set(::CreateEventW(nullptr, true, false, nullptr));
+
+ job_thread_.Set(::CreateThread(nullptr, 0, // Default security and stack.
+ TargetEventsThread, this, 0, nullptr));
+ if (!job_thread_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ return SBOX_ALL_OK;
+}
+
+// The destructor should only be called when the Broker process is terminating.
+// Since BrokerServicesBase is a singleton, this is called from the CRT
+// termination handlers, if this code lives on a DLL it is called during
+// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot
+// wait for threads here.
+BrokerServicesBase::~BrokerServicesBase() {
+ // If there is no port Init() was never called successfully.
+ if (!job_port_.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_.Get(), 0, THREAD_CTRL_QUIT, nullptr);
+
+ if (job_thread_.IsValid() &&
+ WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_.Get(), 1000)) {
+ // Cannot clean broker services.
+ NOTREACHED();
+ return;
+ }
+ thread_pool_.reset();
+ ::DeleteCriticalSection(&lock_);
+}
+
+scoped_refptr<TargetPolicy> BrokerServicesBase::CreatePolicy() {
+ // If you change the type of the object being created here you must also
+ // change the downcast to it in SpawnTarget().
+ scoped_refptr<TargetPolicy> policy(new PolicyBase);
+ // PolicyBase starts with refcount 1.
+ policy->Release();
+ return policy;
+}
+
+// The worker thread stays in a loop waiting for asynchronous notifications
+// from the job objects. Right now we only care about knowing when the last
+// process on a job terminates, but in general this is the place to tell
+// the policy about events.
+DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
+ if (!param)
+ return 1;
+
+ base::PlatformThread::SetName("BrokerEvent");
+
+ BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
+ HANDLE port = broker->job_port_.Get();
+ HANDLE no_targets = broker->no_targets_.Get();
+
+ std::set<DWORD> child_process_ids;
+ std::list<std::unique_ptr<JobTracker>> jobs;
+ std::list<std::unique_ptr<ProcessTracker>> processes;
+ int target_counter = 0;
+ int untracked_target_counter = 0;
+ ::ResetEvent(no_targets);
+
+ while (true) {
+ DWORD events = 0;
+ ULONG_PTR key = 0;
+ LPOVERLAPPED ovl = nullptr;
+
+ if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE)) {
+ // this call fails if the port has been closed before we have a
+ // chance to service the last packet which is 'exit' anyway so
+ // this is not an error.
+ return 1;
+ }
+
+ if (key > THREAD_CTRL_LAST) {
+ // The notification comes from a job object. There are nine notifications
+ // that jobs can send and some of them depend on the job attributes set.
+ JobTracker* tracker = reinterpret_cast<JobTracker*>(key);
+
+ // Processes may be added to a job after the process count has
+ // reached zero, leading us to manipulate a freed JobTracker
+ // object or job handle (as the key is no longer valid). We
+ // therefore check if the tracker has already been deleted.
+ if (std::find_if(jobs.begin(), jobs.end(), [&](auto&& p) -> bool {
+ return p.get() == tracker;
+ }) == jobs.end()) {
+ CHECK(false);
+ }
+
+ switch (events) {
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: {
+ // The job object has signaled that the last process associated
+ // with it has terminated. It is safe to free the tracker
+ // and release its reference to the associated policy object
+ // which will Close the job handle.
+ HANDLE job_handle = tracker->job.Get();
+
+ // Erase by comparing with the job handle.
+ jobs.erase(std::remove_if(jobs.begin(), jobs.end(),
+ [&](auto&& p) -> bool {
+ return p->job.Get() == job_handle;
+ }),
+ jobs.end());
+ break;
+ }
+
+ case JOB_OBJECT_MSG_NEW_PROCESS: {
+ // Child process created from sandboxed process.
+ DWORD process_id =
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl));
+ size_t count = child_process_ids.count(process_id);
+ if (count == 0)
+ untracked_target_counter++;
+ ++target_counter;
+ if (1 == target_counter) {
+ ::ResetEvent(no_targets);
+ }
+ break;
+ }
+
+ case JOB_OBJECT_MSG_EXIT_PROCESS:
+ case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: {
+ {
+ AutoLock lock(&broker->lock_);
+ broker->active_targets_.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ }
+ size_t erase_result = child_process_ids.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ if (erase_result != 1U) {
+ // The process was untracked e.g. a child process of the target.
+ --untracked_target_counter;
+ DCHECK(untracked_target_counter >= 0);
+ }
+ --target_counter;
+ if (0 == target_counter)
+ ::SetEvent(no_targets);
+
+ DCHECK(target_counter >= 0);
+ break;
+ }
+
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
+ // A child process attempted and failed to create a child process.
+ // Windows does not reveal the process id.
+ untracked_target_counter++;
+ target_counter++;
+ break;
+ }
+
+ case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
+ bool res = ::TerminateJobObject(tracker->job.Get(),
+ SBOX_FATAL_MEMORY_EXCEEDED);
+ DCHECK(res);
+ break;
+ }
+
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ } else if (THREAD_CTRL_NEW_JOB_TRACKER == key) {
+ std::unique_ptr<JobTracker> tracker;
+ tracker.reset(reinterpret_cast<JobTracker*>(ovl));
+ DCHECK(tracker->job.IsValid());
+
+ child_process_ids.insert(tracker->process_id);
+ jobs.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_NEW_PROCESS_TRACKER == key) {
+ std::unique_ptr<ProcessTracker> tracker;
+ tracker.reset(reinterpret_cast<ProcessTracker*>(ovl));
+
+ if (child_process_ids.empty()) {
+ ::SetEvent(broker->no_targets_.Get());
+ }
+
+ tracker->iocp = port;
+ if (!::RegisterWaitForSingleObject(&(tracker->wait_handle),
+ tracker->process.Get(),
+ ProcessEventCallback, tracker.get(),
+ INFINITE, WT_EXECUTEONLYONCE)) {
+ // Failed. Invalidate the wait_handle and store anyway.
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ processes.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_PROCESS_SIGNALLED == key) {
+ ProcessTracker* tracker =
+ static_cast<ProcessTracker*>(reinterpret_cast<void*>(ovl));
+
+ {
+ AutoLock lock(&broker->lock_);
+ broker->active_targets_.erase(tracker->process_id);
+ }
+
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ // Copy process_id so that we can legally reference it even after we have
+ // found the ProcessTracker object to delete.
+ const DWORD process_id = tracker->process_id;
+ // PID is unique until the process handle is closed in dtor.
+ processes.erase(std::remove_if(processes.begin(), processes.end(),
+ [&](auto&& p) -> bool {
+ return p->process_id == process_id;
+ }),
+ processes.end());
+ } else if (THREAD_CTRL_GET_POLICY_INFO == key) {
+ // Clone the policies for sandbox diagnostics.
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver;
+ receiver.reset(static_cast<PolicyDiagnosticsReceiver*>(
+ reinterpret_cast<void*>(ovl)));
+ // The PollicyInfo ctor copies essential information from the trackers.
+ auto policy_list = std::make_unique<PolicyDiagnosticList>();
+ for (auto&& process_tracker : processes) {
+ if (process_tracker->policy) {
+ policy_list->push_back(std::make_unique<PolicyDiagnostic>(
+ process_tracker->policy.get()));
+ }
+ }
+ for (auto&& job_tracker : jobs) {
+ if (job_tracker->policy) {
+ policy_list->push_back(
+ std::make_unique<PolicyDiagnostic>(job_tracker->policy.get()));
+ }
+ }
+ // Receiver should return quickly.
+ receiver->ReceiveDiagnostics(std::move(policy_list));
+
+ } else if (THREAD_CTRL_QUIT == key) {
+ // The broker object is being destroyed so the thread needs to exit.
+ for (auto&& tracker : processes) {
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ // After this point, so further calls to ProcessEventCallback can
+ // occur. Other tracked objects are destroyed as this thread ends.
+ return 0;
+ } else {
+ // We have not implemented more commands.
+ NOTREACHED();
+ }
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+// SpawnTarget does all the interesting sandbox setup and creates the target
+// process inside the sandbox.
+ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target_info) {
+ if (!exe_path)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!policy)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ // Even though the resources touched by SpawnTarget can be accessed in
+ // multiple threads, the method itself cannot be called from more than one
+ // thread. This is to protect the global variables used while setting up the
+ // child process, and to make sure launcher thread mitigations are applied
+ // correctly.
+ static DWORD thread_id = ::GetCurrentThreadId();
+ DCHECK(thread_id == ::GetCurrentThreadId());
+ *last_warning = SBOX_ALL_OK;
+
+ // Launcher thread only needs to be opted out of ACG once. Do this on the
+ // first child process being spawned.
+ static bool launcher_thread_opted_out = false;
+
+ if (!launcher_thread_opted_out) {
+ // Soft fail this call. It will fail if ACG is not enabled for this process.
+ sandbox::ApplyMitigationsToCurrentThread(
+ sandbox::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD);
+ launcher_thread_opted_out = true;
+ }
+
+ // This downcast is safe as long as we control CreatePolicy()
+ scoped_refptr<PolicyBase> policy_base(static_cast<PolicyBase*>(policy.get()));
+
+ // Construct the tokens and the job object that we are going to associate
+ // with the soon to be created target process.
+ base::win::ScopedHandle initial_token;
+ base::win::ScopedHandle lockdown_token;
+ base::win::ScopedHandle lowbox_token;
+ ResultCode result = SBOX_ALL_OK;
+
+ result =
+ policy_base->MakeTokens(&initial_token, &lockdown_token, &lowbox_token);
+ if (SBOX_ALL_OK != result)
+ return result;
+ if (lowbox_token.IsValid() &&
+ base::win::GetVersion() < base::win::Version::WIN8) {
+ // We don't allow lowbox_token below Windows 8.
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ 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;
+
+ // We don't want any child processes causing the IDC_APPSTARTING cursor.
+ startup_info.startup_info()->dwFlags |= STARTF_FORCEOFFFEEDBACK;
+
+ // The liftime of |mitigations|, |inherit_handle_list| and
+ // |child_process_creation| 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[2];
+ std::vector<HANDLE> inherited_handle_list;
+ DWORD child_process_creation = PROCESS_CREATION_CHILD_PROCESS_RESTRICTED;
+
+ std::wstring desktop = policy_base->GetAlternateDesktop();
+ if (!desktop.empty()) {
+ startup_info.startup_info()->lpDesktop =
+ const_cast<wchar_t*>(desktop.c_str());
+ }
+
+ bool inherit_handles = false;
+
+ int attribute_count = 0;
+
+ size_t mitigations_size;
+ ConvertProcessMitigationsToPolicy(policy_base->GetProcessMitigations(),
+ &mitigations[0], &mitigations_size);
+ if (mitigations[0] || mitigations[1])
+ ++attribute_count;
+
+ bool restrict_child_process_creation = false;
+ if (base::win::GetVersion() >= base::win::Version::WIN10_TH2 &&
+ policy_base->GetJobLevel() <= JOB_LIMITED_USER) {
+ restrict_child_process_creation = true;
+ ++attribute_count;
+ }
+
+ HANDLE stdout_handle = policy_base->GetStdoutHandle();
+ HANDLE stderr_handle = policy_base->GetStderrHandle();
+
+ if (stdout_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stdout_handle);
+
+ // Handles in the list must be unique.
+ if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stderr_handle);
+
+ const auto& policy_handle_list = policy_base->GetHandlesBeingShared();
+
+ for (HANDLE handle : policy_handle_list)
+ inherited_handle_list.push_back(handle);
+
+ if (inherited_handle_list.size())
+ ++attribute_count;
+
+ scoped_refptr<AppContainerProfileBase> profile =
+ policy_base->GetAppContainerProfileBase();
+ if (profile) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ // LPAC first supported in RS1.
+ if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ }
+ }
+
+ if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+
+ if (mitigations[0] || mitigations[1]) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations[0],
+ mitigations_size)) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (restrict_child_process_creation) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY, &child_process_creation,
+ sizeof(child_process_creation))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (inherited_handle_list.size()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &inherited_handle_list[0],
+ sizeof(HANDLE) * inherited_handle_list.size())) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
+ startup_info.startup_info()->hStdOutput = stdout_handle;
+ startup_info.startup_info()->hStdError = stderr_handle;
+ // Allowing inheritance of handles is only secure now that we
+ // have limited which handles will be inherited.
+ inherit_handles = true;
+ }
+
+ // Declared here to ensure they stay in scope until after process creation.
+ std::unique_ptr<SecurityCapabilities> security_capabilities;
+ DWORD all_applications_package_policy =
+ PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT;
+
+ if (profile) {
+ security_capabilities = profile->GetSecurityCapabilities();
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
+ security_capabilities.get(), sizeof(SECURITY_CAPABILITIES))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY,
+ &all_applications_package_policy,
+ sizeof(all_applications_package_policy))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+ }
+
+ // Construct the thread pool here in case it is expensive.
+ // The thread pool is shared by all the targets
+ if (!thread_pool_)
+ thread_pool_ = std::make_unique<Win2kThreadPool>();
+
+ // Create the TargetProcess 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(
+ std::move(initial_token), std::move(lockdown_token), job.Get(),
+ thread_pool_.get(),
+ profile ? profile->GetImpersonationCapabilities() : std::vector<Sid>());
+
+ result = target->Create(exe_path, command_line, inherit_handles, startup_info,
+ &process_info, env_map, last_error);
+
+ if (result != SBOX_ALL_OK) {
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (lowbox_token.IsValid()) {
+ *last_warning = target->AssignLowBoxToken(lowbox_token);
+ // If this fails we continue, but report the error as a warning.
+ // This is due to certain configurations causing the setting of the
+ // token to fail post creation, and we'd rather continue if possible.
+ if (*last_warning != SBOX_ALL_OK)
+ *last_error = ::GetLastError();
+ }
+
+ // Now the policy is the owner of the target.
+ result = policy_base->AddTarget(target);
+
+ if (result != SBOX_ALL_OK) {
+ *last_error = ::GetLastError();
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (job.IsValid()) {
+ JobTracker* tracker =
+ new JobTracker(std::move(job), policy_base, process_info.process_id());
+
+ // Post the tracker to the tracking thread, then associate the job with
+ // the tracker. The worker thread takes ownership of these objects.
+ CHECK(::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker)));
+ // 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));
+
+ AutoLock lock(&lock_);
+ active_targets_.insert(process_info.process_id());
+ } else {
+ result = AddTargetPeerInternal(process_info.process_handle(),
+ process_info.process_id(),
+ policy_base, last_error);
+ if (result != SBOX_ALL_OK) {
+ // This may fail in the same way as Job associated processes.
+ // crbug.com/480639.
+ target->Terminate();
+ return result;
+ }
+ }
+
+ *target_info = process_info.Take();
+ return result;
+}
+
+ResultCode BrokerServicesBase::WaitForAllTargets() {
+ ::WaitForSingleObject(no_targets_.Get(), INFINITE);
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::IsSafeDuplicationTarget(DWORD process_id) {
+ AutoLock lock(&lock_);
+ return active_targets_.find(process_id) != active_targets_.end();
+}
+
+ResultCode BrokerServicesBase::AddTargetPeerInternal(
+ HANDLE peer_process_handle,
+ DWORD peer_process_id,
+ scoped_refptr<PolicyBase> policy_base,
+ DWORD* last_error) {
+ // Duplicate the process handle to give the tracking machinery
+ // something valid to wait on in the tracking thread.
+ HANDLE tmp_process_handle = INVALID_HANDLE_VALUE;
+ if (!::DuplicateHandle(::GetCurrentProcess(), peer_process_handle,
+ ::GetCurrentProcess(), &tmp_process_handle,
+ SYNCHRONIZE, false, 0 /*no options*/)) {
+ *last_error = ::GetLastError();
+ return SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE;
+ }
+ base::win::ScopedHandle dup_process_handle(tmp_process_handle);
+ ProcessTracker* tracker = new ProcessTracker(
+ policy_base, peer_process_id, std::move(dup_process_handle));
+ // The tracker and policy will leak if this call fails.
+ ::PostQueuedCompletionStatus(job_port_.Get(), 0,
+ THREAD_CTRL_NEW_PROCESS_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker));
+
+ AutoLock lock(&lock_);
+ active_targets_.insert(peer_process_id);
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
+ DWORD last_error;
+ return AddTargetPeerInternal(peer_process, ::GetProcessId(peer_process),
+ nullptr, &last_error);
+}
+
+ResultCode BrokerServicesBase::GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) {
+ CHECK(job_thread_.IsValid());
+ // Post to the job thread.
+ if (!::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_GET_POLICY_INFO,
+ reinterpret_cast<LPOVERLAPPED>(receiver.get()))) {
+ receiver->OnError(SBOX_ERROR_GENERIC);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ // Ownership has passed to tracker thread.
+ receiver.release();
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::DeriveCapabilitySidFromName(const wchar_t* name,
+ PSID derived_sid,
+ DWORD sid_buffer_length) {
+ return ::CopySid(sid_buffer_length, derived_sid,
+ Sid::FromNamedCapability(name).GetPSID());
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.h b/security/sandbox/chromium/sandbox/win/src/broker_services.h
new file mode 100644
index 0000000000..64dc6d66e5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/broker_services.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+#define SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/environment.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+// BrokerServicesBase ---------------------------------------------------------
+// Broker implementation version 0
+//
+// This is an implementation of the interface BrokerServices and
+// of the associated TargetProcess interface. In this implementation
+// TargetProcess is a friend of BrokerServices where the later manages a
+// collection of the former.
+class BrokerServicesBase final : public BrokerServices,
+ public SingletonBase<BrokerServicesBase> {
+ public:
+ BrokerServicesBase();
+
+ ~BrokerServicesBase();
+
+ // BrokerServices interface.
+ ResultCode Init() override;
+ scoped_refptr<TargetPolicy> CreatePolicy() override;
+ ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) override;
+ ResultCode WaitForAllTargets() override;
+ ResultCode AddTargetPeer(HANDLE peer_process) override;
+
+ // Checks if the supplied process ID matches one of the broker's active
+ // target processes. We use this method for the specific purpose of
+ // checking if we can safely duplicate a handle to the supplied process
+ // in DuplicateHandleProxyAction.
+ bool IsSafeDuplicationTarget(DWORD process_id);
+
+ ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) override;
+
+ bool DeriveCapabilitySidFromName(const wchar_t* name, PSID derived_sid,
+ DWORD sid_buffer_length) override;
+
+ private:
+ // The routine that the worker thread executes. It is in charge of
+ // notifications and cleanup-related tasks.
+ static DWORD WINAPI TargetEventsThread(PVOID param);
+
+ // The completion port used by the job objects to communicate events to
+ // the worker thread.
+ base::win::ScopedHandle job_port_;
+
+ // Handle to a manual-reset event that is signaled when the total target
+ // process count reaches zero.
+ base::win::ScopedHandle no_targets_;
+
+ // Handle to the worker thread that reacts to job notifications.
+ base::win::ScopedHandle job_thread_;
+
+ // Provides a pool of threads that are used to wait on the IPC calls.
+ std::unique_ptr<ThreadProvider> thread_pool_;
+
+ // The set representing the broker's active target processes including
+ // both sandboxed and unsandboxed peer processes.
+ std::set<DWORD> active_targets_;
+
+ // Lock used to protect active_targets_ from being simultaneously accessed
+ // by multiple threads.
+ CRITICAL_SECTION lock_;
+
+ ResultCode AddTargetPeerInternal(HANDLE peer_process_handle,
+ DWORD peer_process_id,
+ scoped_refptr<PolicyBase> policy_base,
+ DWORD* last_error);
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_BROKER_SERVICES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_client.h b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
new file mode 100644
index 0000000000..a346aeb753
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
@@ -0,0 +1,509 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_
+#define SANDBOX_SRC_CROSSCALL_CLIENT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+// This header defines the CrossCall(..) family of templated functions
+// Their purpose is to simulate the syntax of regular call but to generate
+// and IPC from the client-side.
+//
+// The basic pattern is to
+// 1) use template argument deduction to compute the size of each
+// parameter and the appropriate copy method
+// 2) pack the parameters in the appropriate ActualCallParams< > object
+// 3) call the IPC interface IPCProvider::DoCall( )
+//
+// The general interface of CrossCall is:
+// ResultCode CrossCall(IPCProvider& ipc_provider,
+// uint32_t tag,
+// const Par1& p1, const Par2& p2,...pn
+// CrossCallReturn* answer)
+//
+// where:
+// ipc_provider: is a specific implementation of the ipc transport see
+// sharedmem_ipc_server.h for an example.
+// tag : is the unique id for this IPC call. Is used to route the call to
+// the appropriate service.
+// p1, p2,.. pn : The input parameters of the IPC. Use only simple types
+// and wide strings (can add support for others).
+// answer : If the IPC was successful. The server-side answer is here. The
+// interpretation of the answer is private to client and server.
+//
+// The return value is ALL_OK if the IPC was delivered to the server, other
+// return codes indicate that the IPC transport failed to deliver it.
+namespace sandbox {
+
+enum class IpcTag;
+
+// The copy helper uses templates to deduce the appropriate copy function to
+// copy the input parameters in the buffer that is going to be send across the
+// IPC. These template facility can be made more sophisticated as need arises.
+
+// The default copy helper. It catches the general case where no other
+// specialized template matches better. We set the type to UINT32_TYPE, so this
+// only works with objects whose size is 32 bits.
+template <typename T>
+class CopyHelper {
+ public:
+ CopyHelper(const T& t) : t_(t) {}
+
+ // Returns the pointer to the start of the input.
+ const void* GetStart() const { return &t_; }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the input in bytes.
+ uint32_t GetSize() const { return sizeof(T); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ // Returns this object's type.
+ ArgType GetType() {
+ static_assert(sizeof(T) == sizeof(uint32_t), "specialization needed");
+ return UINT32_TYPE;
+ }
+
+ private:
+ const T& t_;
+};
+
+// This copy helper template specialization if for the void pointer
+// case both 32 and 64 bit.
+template <>
+class CopyHelper<void*> {
+ public:
+ CopyHelper(void* t) : t_(t) {}
+
+ // Returns the pointer to the start of the input.
+ const void* GetStart() const { return &t_; }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the input in bytes.
+ uint32_t GetSize() const { return sizeof(t_); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ // Returns this object's type.
+ ArgType GetType() { return VOIDPTR_TYPE; }
+
+ private:
+ const void* t_;
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a pointer to a string.
+template <>
+class CopyHelper<const wchar_t*> {
+ public:
+ CopyHelper(const wchar_t* t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_; }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the string in bytes. We define a nullptr string to
+ // be of zero length.
+ uint32_t GetSize() const {
+ __try {
+ return (!t_) ? 0
+ : static_cast<uint32_t>(StringLength(t_) * sizeof(t_[0]));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return UINT32_MAX;
+ }
+ }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ ArgType GetType() { return WCHAR_TYPE; }
+
+ private:
+ // We provide our not very optimized version of wcslen(), since we don't
+ // want to risk having the linker use the version in the CRT since the CRT
+ // might not be present when we do an early IPC call.
+ static size_t CDECL StringLength(const wchar_t* wcs) {
+ const wchar_t* eos = wcs;
+ while (*eos++)
+ ;
+ return static_cast<size_t>(eos - wcs - 1);
+ }
+
+ const wchar_t* t_;
+};
+
+// Specialization for non-const strings. We just reuse the implementation of the
+// const string specialization.
+template <>
+class CopyHelper<wchar_t*> : public CopyHelper<const wchar_t*> {
+ public:
+ typedef CopyHelper<const wchar_t*> Base;
+ CopyHelper(wchar_t* t) : Base(t) {}
+
+ const void* GetStart() const { return Base::GetStart(); }
+
+ bool Update(void* buffer) { return Base::Update(buffer); }
+
+ uint32_t GetSize() const { return Base::GetSize(); }
+
+ bool IsInOut() { return Base::IsInOut(); }
+
+ ArgType GetType() { return Base::GetType(); }
+};
+
+// Specialization for wchar_t arrays strings. We just reuse the implementation
+// of the const string specialization.
+template <size_t n>
+class CopyHelper<const wchar_t[n]> : public CopyHelper<const wchar_t*> {
+ public:
+ typedef const wchar_t array[n];
+ typedef CopyHelper<const wchar_t*> Base;
+ CopyHelper(array t) : Base(t) {}
+
+ const void* GetStart() const { return Base::GetStart(); }
+
+ bool Update(void* buffer) { return Base::Update(buffer); }
+
+ uint32_t GetSize() const { return Base::GetSize(); }
+
+ bool IsInOut() { return Base::IsInOut(); }
+
+ ArgType GetType() { return Base::GetType(); }
+};
+
+// Generic encapsulation class containing a pointer to a buffer and the
+// size of the buffer. It is used by the IPC to be able to pass in/out
+// parameters.
+class InOutCountedBuffer : public CountedBuffer {
+ public:
+ InOutCountedBuffer(void* buffer, uint32_t size)
+ : CountedBuffer(buffer, size) {}
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a an input buffer.
+template <>
+class CopyHelper<CountedBuffer> {
+ public:
+ CopyHelper(const CountedBuffer t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_.Buffer(); }
+
+ // Update not required so just return true;
+ bool Update(void* buffer) { return true; }
+
+ // Returns the size of the string in bytes. We define a nullptr string to
+ // be of zero length.
+ uint32_t GetSize() const { return t_.Size(); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ ArgType GetType() { return INPTR_TYPE; }
+
+ private:
+ const CountedBuffer t_;
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a an input/output buffer.
+template <>
+class CopyHelper<InOutCountedBuffer> {
+ public:
+ CopyHelper(const InOutCountedBuffer t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_.Buffer(); }
+
+ // Updates the buffer with the value from the new buffer in parameter.
+ bool Update(void* buffer) {
+ // We are touching user memory, this has to be done from inside a try
+ // except.
+ __try {
+ memcpy_wrapper(t_.Buffer(), buffer, t_.Size());
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+ }
+
+ // Returns the size of the string in bytes. We define a nullptr string to
+ // be of zero length.
+ uint32_t GetSize() const { return t_.Size(); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return true; }
+
+ ArgType GetType() { return INOUTPTR_TYPE; }
+
+ private:
+ const InOutCountedBuffer t_;
+};
+
+// The following two macros make it less error prone the generation
+// of CrossCall functions with ever more input parameters.
+
+#define XCALL_GEN_PARAMS_OBJ(num, params) \
+ typedef ActualCallParams<num, kIPCChannelSize> ActualParams; \
+ void* raw_mem = ipc_provider.GetBuffer(); \
+ if (!raw_mem) \
+ return SBOX_ERROR_NO_SPACE; \
+ ActualParams* params = new (raw_mem) ActualParams(tag);
+
+#define XCALL_GEN_COPY_PARAM(num, params) \
+ static_assert(kMaxIpcParams >= num, "too many parameters"); \
+ CopyHelper<Par##num> ch##num(p##num); \
+ if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \
+ ch##num.IsInOut(), ch##num.GetType())) \
+ return SBOX_ERROR_NO_SPACE;
+
+#define XCALL_GEN_UPDATE_PARAM(num, params) \
+ if (!ch##num.Update(params->GetParamPtr(num - 1))) { \
+ ipc_provider.FreeBuffer(raw_mem); \
+ return SBOX_ERROR_BAD_PARAMS; \
+ }
+
+#define XCALL_GEN_FREE_CHANNEL() ipc_provider.FreeBuffer(raw_mem);
+
+// CrossCall template with one input parameter
+template <typename IPCProvider, typename Par1>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(1, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+
+ return result;
+}
+
+// CrossCall template with two input parameters.
+template <typename IPCProvider, typename Par1, typename Par2>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(2, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with three input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(3, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with four input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(4, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with five input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4,
+ typename Par5>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ const Par5& p5,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(5, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with six input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4,
+ typename Par5,
+ typename Par6>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ const Par5& p5,
+ const Par6& p6,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(6, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+ XCALL_GEN_COPY_PARAM(6, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_UPDATE_PARAM(6, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with seven input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4,
+ typename Par5,
+ typename Par6,
+ typename Par7>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ const Par5& p5,
+ const Par6& p6,
+ const Par7& p7,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(7, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+ XCALL_GEN_COPY_PARAM(6, call_params);
+ XCALL_GEN_COPY_PARAM(7, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_UPDATE_PARAM(6, call_params);
+ XCALL_GEN_UPDATE_PARAM(7, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_CLIENT_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
new file mode 100644
index 0000000000..6971b55cb7
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
@@ -0,0 +1,315 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__
+#define SANDBOX_SRC_CROSSCALL_PARAMS_H__
+
+#if !defined(SANDBOX_FUZZ_TARGET)
+#include <windows.h>
+
+#include <lmaccess.h>
+#else
+#include "sandbox/win/fuzzer/fuzzer_types.h"
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/win/src/internal_types.h"
+#if !defined(SANDBOX_FUZZ_TARGET)
+#include "sandbox/win/src/sandbox_nt_types.h"
+#endif
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+// 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_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.
+//
+// Like most IPC systems. CrossCall is designed to take as inputs 'simple'
+// types such as integers and strings. Classes, generic arrays or pointers to
+// them are not supported.
+//
+// Another limitation of CrossCall is that the return value and output
+// parameters can only be uint32_t integers. Returning complex structures or
+// strings is not supported.
+
+namespace sandbox {
+
+// this is the assumed channel size. This can be overridden in a given
+// IPC implementation.
+const uint32_t kIPCChannelSize = 1024;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+namespace {
+
+// Increases |value| until there is no need for padding given an int64_t
+// alignment. Returns the increased value.
+inline uint32_t Align(uint32_t value) {
+ uint32_t alignment = sizeof(int64_t);
+ return ((value + alignment - 1) / alignment) * alignment;
+}
+
+inline void* memcpy_wrapper(void* dest, const void* src, size_t count) {
+ if (g_nt.memcpy)
+ return g_nt.memcpy(dest, src, count);
+ return memcpy(dest, src, count);
+}
+
+} // namespace
+
+// max number of extended return parameters. See CrossCallReturn
+const size_t kExtendedReturnCount = 8;
+
+// Union of multiple types to be used as extended results
+// in the CrossCallReturn.
+union MultiType {
+ uint32_t unsigned_int;
+ void* pointer;
+ HANDLE handle;
+ ULONG_PTR ulong_ptr;
+};
+
+// Maximum number of IPC parameters currently supported.
+// To increase this value, we have to:
+// - Add another Callback typedef to Dispatcher.
+// - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
+// - Add another case to the switch in GetActualAndMaxBufferSize
+// - Add another case to the switch in GetMinDeclaredActualCallParamsSize
+const int kMaxIpcParams = 9;
+
+// Contains the information about a parameter in the ipc buffer.
+struct ParamInfo {
+ ArgType type_;
+ uint32_t offset_;
+ uint32_t size_;
+};
+
+// Models the return value and the return parameters of an IPC call
+// currently limited to one status code and eight generic return values
+// which cannot be pointers to other data. For x64 ports this structure
+// might have to use other integer types.
+struct CrossCallReturn {
+ // the IPC tag. It should match the original IPC tag.
+ uint32_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
+ // of this value depends on the specific service.
+ union {
+ NTSTATUS nt_status;
+ DWORD win32_result;
+ };
+ // Number of extended return values.
+ uint32_t extended_count;
+ // for calls that should return a windows handle. It is found here.
+ HANDLE handle;
+ // The array of extended values.
+ MultiType extended[kExtendedReturnCount];
+};
+
+// CrossCallParams base class that models the input params all packed in a
+// single compact memory blob. The representation can vary but in general a
+// given child of this class is meant to represent all input parameters
+// necessary to make a IPC call.
+//
+// This class cannot have virtual members because its assumed the IPC
+// parameters start from the 'this' pointer to the end, which is defined by
+// one of the subclasses
+//
+// Objects of this class cannot be constructed directly. Only derived
+// classes have the proper knowledge to construct it.
+class CrossCallParams {
+ public:
+ // Returns the tag (ipc unique id) associated with this IPC.
+ IpcTag GetTag() const { return tag_; }
+
+ // Returns the beggining of the buffer where the IPC params can be stored.
+ // prior to an IPC call
+ const void* GetBuffer() const { return this; }
+
+ // Returns how many parameter this IPC call should have.
+ uint32_t GetParamsCount() const { return params_count_; }
+
+ // Returns a pointer to the CrossCallReturn structure.
+ CrossCallReturn* GetCallReturn() { return &call_return; }
+
+ // Returns true if this call contains InOut parameters.
+ bool IsInOut() const { return (1 == is_in_out_); }
+
+ // Tells the CrossCall object if it contains InOut parameters.
+ void SetIsInOut(bool value) {
+ if (value)
+ is_in_out_ = 1;
+ else
+ is_in_out_ = 0;
+ }
+
+ protected:
+ // constructs the IPC call params. Called only from the derived classes
+ CrossCallParams(IpcTag tag, uint32_t params_count)
+ : tag_(tag), is_in_out_(0), params_count_(params_count) {}
+
+ private:
+ IpcTag tag_;
+ uint32_t is_in_out_;
+ CrossCallReturn call_return;
+ const uint32_t params_count_;
+ DISALLOW_COPY_AND_ASSIGN(CrossCallParams);
+};
+
+// ActualCallParams models an specific IPC call parameters with respect to the
+// storage allocation that the packed parameters should need.
+// NUMBER_PARAMS: the number of parameters, valid from 1 to N
+// BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take,
+// typically the block size is defined by the channel size of the underlying
+// ipc mechanism.
+// In practice this class is used to levergage C++ capacity to properly
+// calculate sizes and displacements given the possibility of the packed params
+// blob to be complex.
+//
+// As is, this class assumes that the layout of the blob is as follows. Assume
+// that NUMBER_PARAMS = 2 and a 32-bit build:
+//
+// [ tag 4 bytes]
+// [ IsOnOut 4 bytes]
+// [ call return 52 bytes]
+// [ params count 4 bytes]
+// [ parameter 0 type 4 bytes]
+// [ parameter 0 offset 4 bytes] ---delta to ---\
+// [ parameter 0 size 4 bytes] |
+// [ parameter 1 type 4 bytes] |
+// [ parameter 1 offset 4 bytes] ---------------|--\
+// [ parameter 1 size 4 bytes] | |
+// [ parameter 2 type 4 bytes] | |
+// [ parameter 2 offset 4 bytes] ----------------------\
+// [ parameter 2 size 4 bytes] | | |
+// |---------------------------| | | |
+// | value 0 (x bytes) | <--------------/ | |
+// | value 1 (y bytes) | <-----------------/ |
+// | | |
+// | end of buffer | <---------------------/
+// |---------------------------|
+//
+// Note that the actual number of params is NUMBER_PARAMS + 1
+// so that the size of each actual param can be computed from the difference
+// between one parameter and the next down. The offset of the last param
+// points to the end of the buffer and the type and size are undefined.
+//
+template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE>
+class ActualCallParams : public CrossCallParams {
+ public:
+ // constructor. Pass the ipc unique tag as input
+ explicit ActualCallParams(IpcTag tag) : CrossCallParams(tag, NUMBER_PARAMS) {
+ param_info_[0].offset_ =
+ static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
+ }
+
+ // Testing-only constructor. Allows setting the |number_params| to a
+ // wrong value.
+ ActualCallParams(IpcTag tag, uint32_t number_params)
+ : CrossCallParams(tag, number_params) {
+ param_info_[0].offset_ =
+ static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
+ }
+
+ static constexpr size_t MaxParamsSize() {
+ return sizeof(
+ ActualCallParams<NUMBER_PARAMS, kIPCChannelSize>::parameters_);
+ }
+
+ // Testing-only method. Allows setting the apparent size to a wrong value.
+ // returns the previous size.
+ 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_t index,
+ const void* parameter_address,
+ uint32_t size,
+ bool is_in_out,
+ ArgType type) {
+ if (index >= NUMBER_PARAMS) {
+ return false;
+ }
+
+ if (UINT32_MAX == size) {
+ // Memory error while getting the size.
+ return false;
+ }
+
+ if (size && !parameter_address) {
+ return false;
+ }
+
+ if ((size > sizeof(*this)) ||
+ (param_info_[index].offset_ > (sizeof(*this) - size))) {
+ // It does not fit, abort copy.
+ return false;
+ }
+
+ char* dest = reinterpret_cast<char*>(this) + param_info_[index].offset_;
+
+ // We might be touching user memory, this has to be done from inside a try
+ // except.
+ __try {
+ memcpy_wrapper(dest, parameter_address, size);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+
+ // Set the flag to tell the broker to update the buffer once the call is
+ // made.
+ if (is_in_out)
+ SetIsInOut(true);
+
+ param_info_[index + 1].offset_ = Align(param_info_[index].offset_ + size);
+ param_info_[index].size_ = size;
+ param_info_[index].type_ = type;
+ return true;
+ }
+
+ // Returns a pointer to a parameter in the memory section.
+ void* GetParamPtr(size_t index) {
+ return reinterpret_cast<char*>(this) + param_info_[index].offset_;
+ }
+
+ // Returns the total size of the buffer. Only valid once all the paramters
+ // have been copied in with CopyParamIn.
+ uint32_t GetSize() const { return param_info_[NUMBER_PARAMS].offset_; }
+
+ protected:
+ ActualCallParams() : CrossCallParams(IpcTag::UNUSED, NUMBER_PARAMS) {}
+
+ private:
+ ParamInfo param_info_[NUMBER_PARAMS + 1];
+ char parameters_[BLOCK_SIZE - sizeof(CrossCallParams) -
+ sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
+ DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
+
+ friend uint32_t GetMinDeclaredActualCallParamsSize(uint32_t param_count);
+};
+
+static_assert(sizeof(ActualCallParams<1, 1024>) == 1024, "bad size buffer");
+static_assert(sizeof(ActualCallParams<2, 1024>) == 1024, "bad size buffer");
+static_assert(sizeof(ActualCallParams<3, 1024>) == 1024, "bad size buffer");
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
new file mode 100644
index 0000000000..27bfdfbd65
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/crosscall_server.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_params.h"
+
+// See comment in atomicops.h. This is needed any time windows.h is included
+// after atomicops.h.
+#undef MemoryBarrier
+
+// This code performs the ipc message validation. Potential security flaws
+// on the ipc are likelier to be found in this code than in the rest of
+// the ipc code.
+
+namespace {
+
+// The buffer for a message must match the max channel size.
+const size_t kMaxBufferSize = sandbox::kIPCChannelSize;
+
+} // namespace
+
+namespace sandbox {
+
+// The template types are used to calculate the maximum expected size.
+typedef ActualCallParams<0, kMaxBufferSize> ActualCP0;
+typedef ActualCallParams<1, kMaxBufferSize> ActualCP1;
+typedef ActualCallParams<2, kMaxBufferSize> ActualCP2;
+typedef ActualCallParams<3, kMaxBufferSize> ActualCP3;
+typedef ActualCallParams<4, kMaxBufferSize> ActualCP4;
+typedef ActualCallParams<5, kMaxBufferSize> ActualCP5;
+typedef ActualCallParams<6, kMaxBufferSize> ActualCP6;
+typedef ActualCallParams<7, kMaxBufferSize> ActualCP7;
+typedef ActualCallParams<8, kMaxBufferSize> ActualCP8;
+typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;
+
+// Returns the actual size for the parameters in an IPC buffer. Returns
+// zero if the |param_count| is zero or too big.
+uint32_t GetActualBufferSize(uint32_t param_count, void* buffer_base) {
+ // Retrieve the actual size and the maximum size of the params buffer.
+ switch (param_count) {
+ case 0:
+ return 0;
+ case 1:
+ return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize();
+ case 2:
+ return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize();
+ case 3:
+ return reinterpret_cast<ActualCP3*>(buffer_base)->GetSize();
+ case 4:
+ return reinterpret_cast<ActualCP4*>(buffer_base)->GetSize();
+ case 5:
+ return reinterpret_cast<ActualCP5*>(buffer_base)->GetSize();
+ case 6:
+ return reinterpret_cast<ActualCP6*>(buffer_base)->GetSize();
+ case 7:
+ return reinterpret_cast<ActualCP7*>(buffer_base)->GetSize();
+ case 8:
+ return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize();
+ case 9:
+ return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize();
+ default:
+ return 0;
+ }
+}
+
+// Returns the minimum size for the parameters in an IPC buffer. Returns
+// zero if the |param_count| is less than zero or too big.
+uint32_t GetMinDeclaredActualCallParamsSize(uint32_t param_count) {
+ switch (param_count) {
+ case 0:
+ return offsetof(ActualCP0, parameters_);
+ case 1:
+ return offsetof(ActualCP1, parameters_);
+ case 2:
+ return offsetof(ActualCP2, parameters_);
+ case 3:
+ return offsetof(ActualCP3, parameters_);
+ case 4:
+ return offsetof(ActualCP4, parameters_);
+ case 5:
+ return offsetof(ActualCP5, parameters_);
+ case 6:
+ return offsetof(ActualCP6, parameters_);
+ case 7:
+ return offsetof(ActualCP7, parameters_);
+ case 8:
+ return offsetof(ActualCP8, parameters_);
+ case 9:
+ return offsetof(ActualCP9, parameters_);
+ default:
+ return 0;
+ }
+}
+
+// Verifies that the declared sizes of an IPC buffer are within range.
+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
+ // integer overflow.
+ return false;
+ }
+
+ if ((declared_size > buffer_size) || (declared_size < min_declared_size)) {
+ // Declared size is bigger than buffer or smaller than computed size
+ // or param_count is equal to 0 or bigger than 9.
+ return false;
+ }
+
+ return true;
+}
+
+CrossCallParamsEx::CrossCallParamsEx() : CrossCallParams(IpcTag::UNUSED, 0) {}
+
+// We override the delete operator because the object's backing memory
+// is hand allocated in CreateFromBuffer. We don't override the new operator
+// because the constructors are private so there is no way to mismatch
+// new & delete.
+void CrossCallParamsEx::operator delete(void* raw_memory) throw() {
+ if (!raw_memory) {
+ // C++ standard allows 'delete 0' behavior.
+ return;
+ }
+ delete[] reinterpret_cast<char*>(raw_memory);
+}
+
+// This function uses a SEH try block so cannot use C++ objects that
+// have destructors or else you get Compiler Error C2712. So no DCHECKs
+// inside this function.
+CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base,
+ uint32_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 (!buffer_base)
+ return nullptr;
+ if (buffer_size < sizeof(CrossCallParams))
+ return nullptr;
+ if (buffer_size > kMaxBufferSize)
+ return nullptr;
+
+ char* backing_mem = nullptr;
+ uint32_t param_count = 0;
+ uint32_t declared_size;
+ uint32_t min_declared_size;
+ CrossCallParamsEx* copied_params = nullptr;
+
+ // Touching the untrusted buffer is done under a SEH try block. This
+ // will catch memory access violations so we don't crash.
+ __try {
+ CrossCallParams* call_params =
+ reinterpret_cast<CrossCallParams*>(buffer_base);
+
+ // Check against the minimum size given the number of stated params
+ // if too small we bail out.
+ param_count = call_params->GetParamsCount();
+ min_declared_size = GetMinDeclaredActualCallParamsSize(param_count);
+
+ // Initial check for the buffer being big enough to determine the actual
+ // buffer size.
+ if (buffer_size < min_declared_size)
+ return nullptr;
+
+ // Retrieve the declared size which if it fails returns 0.
+ declared_size = GetActualBufferSize(param_count, buffer_base);
+
+ if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size))
+ return nullptr;
+
+ // Now we copy the actual amount of the message.
+ *output_size = declared_size;
+ backing_mem = new char[declared_size];
+ copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem);
+ memcpy(backing_mem, call_params, declared_size);
+
+ // Avoid compiler optimizations across this point. Any value stored in
+ // memory should be stored for real, and values previously read from memory
+ // should be actually read.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+
+ min_declared_size = GetMinDeclaredActualCallParamsSize(param_count);
+
+ // Check that the copied buffer is still valid.
+ if (copied_params->GetParamsCount() != param_count ||
+ GetActualBufferSize(param_count, backing_mem) != declared_size ||
+ !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) {
+ delete[] backing_mem;
+ return nullptr;
+ }
+
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ // In case of a windows exception we know it occurred while touching the
+ // untrusted buffer so we bail out as is.
+ delete[] backing_mem;
+ return nullptr;
+ }
+
+ // Here and below we're making use of uintptr_t to have well-defined integer
+ // overflow when doing pointer arithmetic.
+ auto backing_mem_ptr = reinterpret_cast<uintptr_t>(backing_mem);
+ auto last_byte = reinterpret_cast<uintptr_t>(&backing_mem[declared_size]);
+ auto first_byte =
+ reinterpret_cast<uintptr_t>(&backing_mem[min_declared_size]);
+
+ // Verify here that all and each parameters make sense. This is done in the
+ // local copy.
+ for (uint32_t ix = 0; ix != param_count; ++ix) {
+ uint32_t size = 0;
+ ArgType type;
+ auto address = reinterpret_cast<uintptr_t>(
+ copied_params->GetRawParameter(ix, &size, &type));
+ if ((!address) || // No null params.
+ (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type.
+ (address < backing_mem_ptr) || // Start cannot point before buffer.
+ (address < first_byte) || // Start cannot point too low.
+ (address > last_byte) || // Start cannot point past buffer.
+ ((address + size) < address) || // Invalid size.
+ ((address + size) > last_byte)) { // End cannot point past buffer.
+ // Malformed.
+ delete[] backing_mem;
+ return nullptr;
+ }
+ }
+ // The parameter buffer looks good.
+ return copied_params;
+}
+
+// Accessors to the parameters in the raw buffer.
+void* CrossCallParamsEx::GetRawParameter(uint32_t index,
+ uint32_t* size,
+ ArgType* type) {
+ if (index >= GetParamsCount())
+ return nullptr;
+ // The size is always computed from the parameter minus the next
+ // parameter, this works because the message has an extra parameter slot
+ *size = param_info_[index].size_;
+ *type = param_info_[index].type_;
+
+ return param_info_[index].offset_ + reinterpret_cast<char*>(this);
+}
+
+// Covers common case for 32 bit integers.
+bool CrossCallParamsEx::GetParameter32(uint32_t index, uint32_t* param) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (!start || (4 != size) || (UINT32_TYPE != type))
+ return false;
+ // Copy the 4 bytes.
+ *(reinterpret_cast<uint32_t*>(param)) = *(reinterpret_cast<uint32_t*>(start));
+ return true;
+}
+
+bool CrossCallParamsEx::GetParameterVoidPtr(uint32_t index, void** param) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (!start || (sizeof(void*) != size) || (VOIDPTR_TYPE != type))
+ return false;
+ *param = *(reinterpret_cast<void**>(start));
+ return true;
+}
+
+// Covers the common case of reading a string. Note that the string is not
+// scanned for invalid characters.
+bool CrossCallParamsEx::GetParameterStr(uint32_t index, std::wstring* string) {
+ DCHECK(string->empty());
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (WCHAR_TYPE != type)
+ return false;
+
+ // Check if this is an empty string.
+ if (size == 0) {
+ *string = std::wstring();
+ return true;
+ }
+
+ if (!start || ((size % sizeof(wchar_t)) != 0))
+ return false;
+
+ string->assign(reinterpret_cast<const wchar_t*>(start),
+ size / sizeof(wchar_t));
+ return true;
+}
+
+bool CrossCallParamsEx::GetParameterPtr(uint32_t index,
+ uint32_t expected_size,
+ void** pointer) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+
+ if ((size != expected_size) || (INOUTPTR_TYPE != type && INPTR_TYPE != type))
+ return false;
+
+ if (!start)
+ return false;
+
+ *pointer = start;
+ return true;
+}
+
+void SetCallError(ResultCode error, CrossCallReturn* call_return) {
+ call_return->call_outcome = error;
+ call_return->extended_count = 0;
+}
+
+void SetCallSuccess(CrossCallReturn* call_return) {
+ call_return->call_outcome = SBOX_ALL_OK;
+}
+
+Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) {
+ DCHECK(callback);
+ std::vector<IPCCall>::iterator it = ipc_calls_.begin();
+ for (; it != ipc_calls_.end(); ++it) {
+ if (it->params.Matches(ipc)) {
+ *callback = it->callback;
+ return this;
+ }
+ }
+ return nullptr;
+}
+
+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
new file mode 100644
index 0000000000..aed7f99aae
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.h
@@ -0,0 +1,261 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_SERVER_H_
+#define SANDBOX_SRC_CROSSCALL_SERVER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/ipc_tags.h"
+
+// This is the IPC server interface for CrossCall: The IPC for the Sandbox
+// On the server, CrossCall needs two things:
+// 1) threads: Or better said, someone to provide them, that is what the
+// ThreadProvider interface is defined for. These thread(s) are
+// the ones that will actually execute the IPC data retrieval.
+//
+// 2) a dispatcher: This interface represents the way to route and process
+// an IPC call given the IPC tag.
+//
+// The other class included here CrossCallParamsEx is the server side version
+// of the CrossCallParams class of /sandbox/crosscall_params.h The difference
+// is that the sever version is paranoid about the correctness of the IPC
+// message and will do all sorts of verifications.
+//
+// A general diagram of the interaction is as follows:
+//
+// ------------
+// | |
+// ThreadProvider <--(1)Register--| IPC |
+// | | Implemen |
+// | | -tation |
+// (2) | | OnMessage
+// IPC fired --callback ------>| |--(3)---> Dispatcher
+// | |
+// ------------
+//
+// The IPC implementation sits as a middleman between the handling of the
+// specifics of scheduling a thread to service the IPC and the multiple
+// entities that can potentially serve each particular IPC.
+namespace sandbox {
+
+class InterceptionManager;
+
+// This function signature is required as the callback when an IPC call fires.
+// context: a user-defined pointer that was set using ThreadProvider
+// reason: 0 if the callback was fired because of a timeout.
+// 1 if the callback was fired because of an event.
+typedef void(__stdcall* CrossCallIPCCallback)(void* context,
+ unsigned char reason);
+
+// ThreadProvider models a thread factory. The idea is to decouple thread
+// creation and lifetime from the inner guts of the IPC. The contract is
+// simple:
+// - the IPC implementation calls RegisterWait with a waitable object that
+// becomes signaled when an IPC arrives and needs to be serviced.
+// - when the waitable object becomes signaled, the thread provider conjures
+// a thread that calls the callback (CrossCallIPCCallback) function
+// - the callback function tries its best not to block and return quickly
+// and should not assume that the next callback will use the same thread
+// - when the callback returns the ThreadProvider owns again the thread
+// and can destroy it or keep it around.
+class ThreadProvider {
+ public:
+ // Registers a waitable object with the thread provider.
+ // client: A number to associate with all the RegisterWait calls, typically
+ // this is the address of the caller object. This parameter cannot
+ // be zero.
+ // waitable_object : a kernel object that can be waited on
+ // callback: a function pointer which is the function that will be called
+ // when the waitable object fires
+ // context: a user-provider pointer that is passed back to the callback
+ // when its called
+ virtual bool RegisterWait(const void* client,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) = 0;
+
+ // Removes all the registrations done with the same cookie parameter.
+ // This frees internal thread pool resources.
+ virtual bool UnRegisterWaits(void* cookie) = 0;
+ virtual ~ThreadProvider() {}
+};
+
+// Models the server-side of the original input parameters.
+// Provides IPC buffer validation and it is capable of reading the parameters
+// out of the IPC buffer.
+class CrossCallParamsEx : public CrossCallParams {
+ public:
+ // Factory constructor. Pass an IPCbuffer (and buffer size) that contains a
+ // pending IPCcall. This constructor will:
+ // 1) validate the IPC buffer. returns nullptr is the IPCbuffer is malformed.
+ // 2) make a copy of the IPCbuffer (parameter capture)
+ static CrossCallParamsEx* CreateFromBuffer(void* buffer_base,
+ uint32_t buffer_size,
+ uint32_t* output_size);
+
+ // Provides IPCinput parameter raw access:
+ // index : the parameter to read; 0 is the first parameter
+ // returns nullptr if the parameter is non-existent. If it exists it also
+ // returns the size in *size
+ 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_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_t index, void** param);
+
+ // Gets a parameter that is a string. Returns false if the parameter does not
+ // exist.
+ bool GetParameterStr(uint32_t index, std::wstring* string);
+
+ // Gets a parameter that is an in/out buffer. Returns false is the parameter
+ // does not exist or if the size of the actual parameter is not equal to the
+ // expected size.
+ bool GetParameterPtr(uint32_t index, uint32_t expected_size, void** pointer);
+
+ // Frees the memory associated with the IPC parameters.
+ static void operator delete(void* raw_memory) throw();
+
+ private:
+ // Only the factory method CreateFromBuffer can construct these objects.
+ CrossCallParamsEx();
+
+ ParamInfo param_info_[1];
+ DISALLOW_COPY_AND_ASSIGN(CrossCallParamsEx);
+};
+
+// Simple helper function that sets the members of CrossCallReturn
+// to the proper state to signal a basic error.
+void SetCallError(ResultCode error, CrossCallReturn* call_return);
+
+// Sets the internal status of call_return to signify the that IPC call
+// completed successfully.
+void SetCallSuccess(CrossCallReturn* call_return);
+
+// Represents the client process that initiated the IPC which boils down to the
+// process handle and the job object handle that contains the client process.
+struct ClientInfo {
+ HANDLE process;
+ DWORD process_id;
+};
+
+// All IPC-related information to be passed to the IPC handler.
+struct IPCInfo {
+ IpcTag ipc_tag;
+ const ClientInfo* client_info;
+ CrossCallReturn return_info;
+};
+
+// This structure identifies IPC signatures.
+struct IPCParams {
+ IpcTag ipc_tag;
+ ArgType args[kMaxIpcParams];
+
+ bool Matches(IPCParams* other) const {
+ return !memcmp(this, other, sizeof(*other));
+ }
+};
+
+// Models an entity that can process an IPC message or it can route to another
+// one that could handle it. When an IPC arrives the IPC implementation will:
+// 1) call OnMessageReady() with the tag of the pending IPC. If the dispatcher
+// returns nullptr it means that it cannot handle this IPC but if it returns
+// non-null, it must be the pointer to a dispatcher that can handle it.
+// 2) When the IPC finally obtains a valid Dispatcher the IPC
+// implementation creates a CrossCallParamsEx from the raw IPC buffer.
+// 3) It calls the returned callback, with the IPC info and arguments.
+class Dispatcher {
+ public:
+ // Called from the IPC implementation to handle a specific IPC message.
+ typedef bool (Dispatcher::*CallbackGeneric)();
+ typedef bool (Dispatcher::*Callback0)(IPCInfo* ipc);
+ typedef bool (Dispatcher::*Callback1)(IPCInfo* ipc, void* p1);
+ typedef bool (Dispatcher::*Callback2)(IPCInfo* ipc, void* p1, void* p2);
+ typedef bool (Dispatcher::*Callback3)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3);
+ typedef bool (Dispatcher::*Callback4)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4);
+ typedef bool (Dispatcher::*Callback5)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5);
+ typedef bool (Dispatcher::*Callback6)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6);
+ typedef bool (Dispatcher::*Callback7)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6,
+ void* p7);
+ typedef bool (Dispatcher::*Callback8)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6,
+ void* p7,
+ void* p8);
+ typedef bool (Dispatcher::*Callback9)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6,
+ void* p7,
+ void* p8,
+ void* p9);
+
+ // Called from the IPC implementation when an IPC message is ready override
+ // on a derived class to handle a set of IPC messages. Return nullptr if your
+ // subclass does not handle the message or return the pointer to the subclass
+ // that can handle it.
+ virtual Dispatcher* OnMessageReady(IPCParams* ipc, CallbackGeneric* callback);
+
+ // Called when a target proces is created, to setup the interceptions related
+ // with the given service (IPC).
+ virtual bool SetupService(InterceptionManager* manager, IpcTag service) = 0;
+
+ Dispatcher();
+ virtual ~Dispatcher();
+
+ protected:
+ // Structure that defines an IPC Call with all the parameters and the handler.
+ struct IPCCall {
+ IPCParams params;
+ CallbackGeneric callback;
+ };
+
+ // List of IPC Calls supported by the class.
+ std::vector<IPCCall> ipc_calls_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_SERVER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc b/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc
new file mode 100644
index 0000000000..f9551ed4b2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/eat_resolver.h"
+
+#include <stddef.h>
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS EatResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret =
+ Init(target_module, interceptor_module, target_name, interceptor_name,
+ interceptor_entry_point, thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ if (!eat_entry_)
+ return STATUS_INVALID_PARAMETER;
+
+#if defined(_WIN64)
+ // We have two thunks, in order: the return path and the forward path.
+ if (!SetInternalThunk(thunk_storage, storage_bytes, nullptr, target_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ size_t thunk_bytes = GetInternalThunkSize();
+ storage_bytes -= thunk_bytes;
+ thunk_storage = reinterpret_cast<char*>(thunk_storage) + thunk_bytes;
+#endif
+
+ if (!SetInternalThunk(thunk_storage, storage_bytes, target_, interceptor_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ AutoProtectMemory memory;
+ ret = memory.ChangeProtection(eat_entry_, sizeof(DWORD), PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ // Perform the patch.
+ *eat_entry_ = static_cast<DWORD>(reinterpret_cast<uintptr_t>(thunk_storage)) -
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(target_module));
+
+ if (storage_used)
+ *storage_used = GetThunkSize();
+
+ return ret;
+}
+
+NTSTATUS EatResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ DCHECK_NT(address);
+ if (!module)
+ return STATUS_INVALID_PARAMETER;
+
+ base::win::PEImage pe(module);
+ if (!pe.VerifyMagic())
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ eat_entry_ = pe.GetExportEntry(function_name);
+
+ if (!eat_entry_)
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ *address = pe.RVAToAddr(*eat_entry_);
+
+ return STATUS_SUCCESS;
+}
+
+size_t EatResolverThunk::GetThunkSize() const {
+#if defined(_WIN64)
+ return GetInternalThunkSize() * 2;
+#else
+ return GetInternalThunkSize();
+#endif
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/eat_resolver.h b/security/sandbox/chromium/sandbox/win/src/eat_resolver.h
new file mode 100644
index 0000000000..cbc5516f8c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/eat_resolver.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_EAT_RESOLVER_H__
+#define SANDBOX_SRC_EAT_RESOLVER_H__
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform exports table interceptions.
+class EatResolverThunk : public ResolverThunk {
+ public:
+ EatResolverThunk() : eat_entry_(nullptr) {}
+ ~EatResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) override;
+
+ // Implementation of Resolver::ResolveTarget.
+ NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ private:
+ // The entry to patch.
+ DWORD* eat_entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(EatResolverThunk);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_EAT_RESOLVER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc
new file mode 100644
index 0000000000..4989ab308f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc
@@ -0,0 +1,705 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cctype>
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+namespace sandbox {
+
+const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+
+// Creates a file using different desired access. Returns if the call succeeded
+// or not. The first argument in argv is the filename. The second argument
+// determines the type of access and the dispositino of the file.
+SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring operation(argv[0]);
+
+ if (operation == L"Read") {
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_READ, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
+ base::win::ScopedHandle file2(CreateFile(
+ argv[1], FILE_EXECUTE, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+
+ } else if (operation == L"Write") {
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_ALL, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
+ base::win::ScopedHandle file2(
+ CreateFile(argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, nullptr,
+ OPEN_EXISTING, 0, nullptr));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+
+ } else if (operation == L"ReadCreate") {
+ base::win::ScopedHandle file2(CreateFile(argv[1], GENERIC_READ, kSharing,
+ nullptr, CREATE_NEW, 0, nullptr));
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_READ, kSharing, nullptr, CREATE_ALWAYS, 0, nullptr));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+ }
+
+ return SBOX_TEST_INVALID_PARAMETER;
+}
+
+SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t** argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ std::wstring full_path = MakePathToSys(argv[0], false);
+ if (full_path.empty()) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ HANDLE file =
+ ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (INVALID_HANDLE_VALUE != file) {
+ ::CloseHandle(file);
+ return SBOX_TEST_SUCCEEDED;
+ } else {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ } else {
+ return SBOX_TEST_FAILED;
+ }
+ }
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Creates the file in parameter using the NtCreateFile api and returns if the
+// call succeeded or not.
+SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t** argv) {
+ BINDNTDLL(NtCreateFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtCreateFile || !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring file(argv[0]);
+ if (0 != _wcsnicmp(file.c_str(), kNTDevicePrefix, kNTDevicePrefixLen))
+ file = MakePathToSys(argv[0], true);
+
+ UNICODE_STRING object_name;
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ HANDLE handle;
+ IO_STATUS_BLOCK io_block = {};
+ NTSTATUS status =
+ NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, &io_block, nullptr,
+ 0, kSharing, FILE_OPEN, 0, nullptr, 0);
+ if (NT_SUCCESS(status)) {
+ ::CloseHandle(handle);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+ return SBOX_TEST_FAILED;
+}
+
+// Opens the file in parameter using the NtOpenFile api and returns if the
+// call succeeded or not.
+SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t** argv) {
+ BINDNTDLL(NtOpenFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtOpenFile || !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring file = MakePathToSys(argv[0], true);
+ UNICODE_STRING object_name;
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ HANDLE handle;
+ IO_STATUS_BLOCK io_block = {};
+ NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes,
+ &io_block, kSharing, 0);
+ if (NT_SUCCESS(status)) {
+ ::CloseHandle(handle);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t** argv) {
+ std::wstring sys_path = MakePathToSys(L"", false);
+ if (sys_path.empty()) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ ULARGE_INTEGER free_user = {};
+ 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)) {
+ return SBOX_TEST_SUCCEEDED;
+ }
+ } else {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ } else {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ }
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Move a file using the MoveFileEx api and returns if the call succeeded or
+// not.
+SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (::MoveFileEx(argv[0], argv[1], 0))
+ return SBOX_TEST_SUCCEEDED;
+
+ if (::GetLastError() != ERROR_ACCESS_DENIED)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_DENIED;
+}
+
+// Query the attributes of file in parameter using the NtQueryAttributesFile api
+// and NtQueryFullAttributesFile and returns if the call succeeded or not. The
+// second argument in argv is "d" or "f" telling if we expect the attributes to
+// specify a file or a directory. The expected attribute has to match the real
+// attributes for the call to be successful.
+SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t** argv) {
+ BINDNTDLL(NtQueryAttributesFile);
+ BINDNTDLL(NtQueryFullAttributesFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtQueryAttributesFile || !NtQueryFullAttributesFile ||
+ !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ bool expect_directory = (L'd' == argv[1][0]);
+
+ UNICODE_STRING object_name;
+ std::wstring file = MakePathToSys(argv[0], true);
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ FILE_BASIC_INFORMATION info = {};
+ FILE_NETWORK_OPEN_INFORMATION full_info = {};
+ NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info);
+ NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info);
+
+ if (status1 != status2)
+ return SBOX_TEST_FAILED;
+
+ if (NT_SUCCESS(status1)) {
+ if (info.FileAttributes != full_info.FileAttributes)
+ return SBOX_TEST_FAILED;
+
+ bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ if (expect_directory == is_directory1)
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status1) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+// Tries to create a backup of calc.exe in system32 folder. This should fail
+// with ERROR_ACCESS_DENIED if everything is working as expected.
+SBOX_TESTS_COMMAND int File_CopyFile(int argc, wchar_t** argv) {
+ std::wstring calc_path = MakePathToSys(L"calc.exe", false);
+ std::wstring calc_backup_path = MakePathToSys(L"calc.exe.bak", false);
+
+ if (::CopyFile(calc_path.c_str(), calc_backup_path.c_str(), FALSE))
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (::GetLastError() != ERROR_ACCESS_DENIED)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(FilePolicyTest, DenyNtCreateCalc) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+}
+
+TEST(FilePolicyTest, AllowNtCreateCalc) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+}
+
+TEST(FilePolicyTest, AllowNtCreateWithNativePath) {
+ std::wstring calc = MakePathToSys(L"calc.exe", false);
+ std::wstring nt_path;
+ ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path));
+ TestRunner runner;
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str());
+
+ wchar_t buff[MAX_PATH];
+ ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
+
+ for (wchar_t& c : nt_path)
+ c = std::tolower(c);
+ ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
+}
+
+TEST(FilePolicyTest, AllowReadOnly) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ EXPECT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name));
+
+ wchar_t command_read[MAX_PATH + 20] = {};
+ wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name);
+ 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] = {};
+ wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
+
+ // Verify that we cannot create the file after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_read_create));
+
+ // Verify that we don't have write access after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write));
+
+ // Verify that we have read access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read));
+
+ // Verify that we really have write access to the file.
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
+
+ DeleteFile(temp_file_name);
+}
+
+// Tests support of "\\\\.\\DeviceName" kind of paths.
+TEST(FilePolicyTest, AllowImplicitDeviceName) {
+ TestRunner runner;
+
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ std::wstring path(temp_file_name);
+ EXPECT_TRUE(ConvertToLongPath(&path));
+ EXPECT_TRUE(GetNtPathFromWin32Path(path, &path));
+ path = path.substr(sandbox::kNTDevicePrefixLen);
+
+ wchar_t command[MAX_PATH + 20] = {};
+ wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str());
+ path = std::wstring(kNTPrefix) + path;
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, path.c_str()));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+
+ DeleteFile(temp_file_name);
+}
+
+TEST(FilePolicyTest, AllowWildcard) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ wcscat_s(temp_directory, MAX_PATH, L"*");
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory));
+
+ wchar_t command_write[MAX_PATH + 20] = {};
+ wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
+
+ // Verify that we have write access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
+
+ DeleteFile(temp_file_name);
+}
+
+TEST(FilePolicyTest, AllowNtCreatePatternRule) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_OpenSys32 apphelp.dll"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_OpenSys32 apphelp.dll"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
+}
+
+TEST(FilePolicyTest, CheckNotFound) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll"));
+
+ EXPECT_EQ(SBOX_TEST_NOT_FOUND,
+ runner.RunTest(L"File_OpenSys32 notfound.dll"));
+}
+
+TEST(FilePolicyTest, CheckNoLeak) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe"));
+}
+
+TEST(FilePolicyTest, TestQueryAttributesFile) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"apphelp.dll"));
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notfound.exe"));
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers"));
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, L"ipconfig.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes drivers d"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes apphelp.dll f"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes ipconfig.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes ftp.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_NOT_FOUND,
+ runner.RunTest(L"File_QueryAttributes notfound.exe f"));
+}
+
+// Makes sure that we don't leak information when there is not policy to allow
+// a path.
+TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes ftp.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes notfound.exe f"));
+}
+
+TEST(FilePolicyTest, TestRename) {
+ TestRunner runner;
+
+ // Give access to the temp directory.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name1[MAX_PATH];
+ wchar_t temp_file_name2[MAX_PATH];
+ wchar_t temp_file_name3[MAX_PATH];
+ wchar_t temp_file_name4[MAX_PATH];
+ wchar_t temp_file_name5[MAX_PATH];
+ wchar_t temp_file_name6[MAX_PATH];
+ wchar_t temp_file_name7[MAX_PATH];
+ wchar_t temp_file_name8[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u);
+
+ // Add rules to make file1->file2 succeed.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2));
+
+ // Add rules to make file3->file4 fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3));
+ ASSERT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name4));
+
+ // Add rules to make file5->file6 fail.
+ ASSERT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name5));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6));
+
+ // Add rules to make file7->no_pol_file fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7));
+
+ // Delete the files where the files are going to be renamed to.
+ ::DeleteFile(temp_file_name2);
+ ::DeleteFile(temp_file_name4);
+ ::DeleteFile(temp_file_name6);
+ ::DeleteFile(temp_file_name8);
+
+ wchar_t command[MAX_PATH * 2 + 20] = {};
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1,
+ temp_file_name2);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3,
+ temp_file_name4);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5,
+ temp_file_name6);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7,
+ temp_file_name8);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ // Delete all the files in case they are still there.
+ ::DeleteFile(temp_file_name1);
+ ::DeleteFile(temp_file_name2);
+ ::DeleteFile(temp_file_name3);
+ ::DeleteFile(temp_file_name4);
+ ::DeleteFile(temp_file_name5);
+ ::DeleteFile(temp_file_name6);
+ ::DeleteFile(temp_file_name7);
+ ::DeleteFile(temp_file_name8);
+}
+
+TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+}
+
+TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe"));
+}
+
+TEST(FilePolicyTest, FileGetDiskSpace) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace"));
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+
+ // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx
+ // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is
+ // denied since there is no wild card in the rule.
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L""));
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
+}
+
+TEST(FilePolicyTest, TestReparsePoint) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(temp_file_name));
+ ASSERT_TRUE(::CreateDirectory(temp_file_name, nullptr));
+
+ // Create a temporary file in the subfolder.
+ std::wstring subfolder = temp_file_name;
+ std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1);
+ std::wstring temp_file = subfolder + L"\\file_" + temp_file_title;
+
+ HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ CREATE_ALWAYS, 0, nullptr);
+ ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Create a temporary file in the temp directory.
+ std::wstring temp_dir = temp_directory;
+ std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title;
+ file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ CREATE_ALWAYS, 0, nullptr);
+ ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Give write access to the temp directory.
+ std::wstring temp_dir_wildcard = temp_dir + L"*";
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY,
+ temp_dir_wildcard.c_str()));
+
+ // Prepare the command to execute.
+ std::wstring command_write;
+ command_write += L"File_Create Write \"";
+ command_write += temp_file;
+ command_write += L"\"";
+
+ // Verify that we have write access to the original file
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str()));
+
+ // Replace the subfolder by a reparse point to %temp%.
+ ::DeleteFile(temp_file.c_str());
+ HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
+
+ std::wstring temp_dir_nt;
+ temp_dir_nt += L"\\??\\";
+ temp_dir_nt += temp_dir;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+ EXPECT_TRUE(::CloseHandle(dir));
+
+ // Try to open the file again.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str()));
+
+ // Remove the reparse point.
+ dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ nullptr);
+ EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
+ EXPECT_TRUE(DeleteReparsePoint(dir));
+ EXPECT_TRUE(::CloseHandle(dir));
+
+ // Cleanup.
+ EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str()));
+ EXPECT_TRUE(::RemoveDirectory(subfolder.c_str()));
+}
+
+TEST(FilePolicyTest, CheckExistingNTPrefixEscape) {
+ std::wstring name = L"\\??\\NAME";
+
+ std::wstring result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), L"\\/?/?\\NAME");
+}
+
+TEST(FilePolicyTest, CheckEscapedNTPrefixNoEscape) {
+ std::wstring name = L"\\/?/?\\NAME";
+
+ std::wstring result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), name.c_str());
+}
+
+TEST(FilePolicyTest, CheckMissingNTPrefixEscape) {
+ std::wstring name = L"C:\\NAME";
+
+ std::wstring result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), L"\\/?/?\\C:\\NAME");
+}
+
+TEST(FilePolicyTest, TestCopyFile) {
+ // Check if the test is running Win8 or newer since
+ // MITIGATION_STRICT_HANDLE_CHECKS is not supported on older systems.
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+
+ TestRunner runner;
+ runner.SetTimeout(2000);
+
+ // Allow read access to calc.exe, this should be on all Windows versions.
+ ASSERT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_READONLY, L"calc.exe"));
+
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ // Set proper mitigation.
+ EXPECT_EQ(
+ policy->SetDelayedProcessMitigations(MITIGATION_STRICT_HANDLE_CHECKS),
+ SBOX_ALL_OK);
+
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CopyFile"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
new file mode 100644
index 0000000000..c0f1de07fb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/filesystem_dispatcher.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/filesystem_interception.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+FilesystemDispatcher::FilesystemDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::NTCREATEFILE,
+ {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtCreateFile)};
+
+ static const IPCCall open_file = {
+ {IpcTag::NTOPENFILE,
+ {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtOpenFile)};
+
+ static const IPCCall attribs = {
+ {IpcTag::NTQUERYATTRIBUTESFILE, {WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryAttributesFile)};
+
+ static const IPCCall full_attribs = {
+ {IpcTag::NTQUERYFULLATTRIBUTESFILE,
+ {WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryFullAttributesFile)};
+
+ static const IPCCall set_info = {
+ {IpcTag::NTSETINFO_RENAME,
+ {VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtSetInformationFile)};
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_file);
+ ipc_calls_.push_back(attribs);
+ ipc_calls_.push_back(full_attribs);
+ ipc_calls_.push_back(set_info);
+}
+
+bool FilesystemDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ switch (service) {
+ case IpcTag::NTCREATEFILE:
+ return INTERCEPT_NT(manager, NtCreateFile, CREATE_FILE_ID, 48);
+
+ case IpcTag::NTOPENFILE:
+ return INTERCEPT_NT(manager, NtOpenFile, OPEN_FILE_ID, 28);
+
+ case IpcTag::NTQUERYATTRIBUTESFILE:
+ return INTERCEPT_NT(manager, NtQueryAttributesFile, QUERY_ATTRIB_FILE_ID,
+ 12);
+
+ case IpcTag::NTQUERYFULLATTRIBUTESFILE:
+ return INTERCEPT_NT(manager, NtQueryFullAttributesFile,
+ QUERY_FULL_ATTRIB_FILE_ID, 12);
+
+ case IpcTag::NTSETINFO_RENAME:
+ return INTERCEPT_NT(manager, NtSetInformationFile, SET_INFO_FILE_ID, 24);
+
+ default:
+ return false;
+ }
+}
+
+bool FilesystemDispatcher::NtCreateFile(IPCInfo* ipc,
+ std::wstring* 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)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ uint32_t broker = BROKER_TRUE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition);
+ params[OpenFile::OPTIONS] = ParamPickerMake(create_options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTCREATEFILE, params.GetBase());
+ HANDLE handle;
+ ULONG_PTR io_information = 0;
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::CreateFileAction(
+ result, *ipc->client_info, *name, attributes, desired_access,
+ file_attributes, share_access, create_disposition, create_options,
+ &handle, &nt_status, &io_information)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].ulong_ptr = io_information;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t open_options) {
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ uint32_t broker = BROKER_TRUE;
+ uint32_t create_disposition = FILE_OPEN;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition);
+ params[OpenFile::OPTIONS] = ParamPickerMake(open_options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTOPENFILE, params.GetBase());
+ HANDLE handle;
+ ULONG_PTR io_information = 0;
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::OpenFileAction(
+ result, *ipc->client_info, *name, attributes, desired_access,
+ share_access, open_options, &handle, &nt_status, &io_information)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].ulong_ptr = io_information;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool FilesystemDispatcher::NtQueryAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info) {
+ if (sizeof(FILE_BASIC_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTQUERYATTRIBUTESFILE, params.GetBase());
+
+ FILE_BASIC_INFORMATION* information =
+ reinterpret_cast<FILE_BASIC_INFORMATION*>(info->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::QueryAttributesFileAction(result, *ipc->client_info,
+ *name, attributes,
+ information, &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+bool FilesystemDispatcher::NtQueryFullAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info) {
+ if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(
+ IpcTag::NTQUERYFULLATTRIBUTESFILE, params.GetBase());
+
+ FILE_NETWORK_OPEN_INFORMATION* information =
+ reinterpret_cast<FILE_NETWORK_OPEN_INFORMATION*>(info->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::QueryFullAttributesFileAction(
+ result, *ipc->client_info, *name, attributes, information,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+bool FilesystemDispatcher::NtSetInformationFile(IPCInfo* ipc,
+ HANDLE handle,
+ CountedBuffer* status,
+ CountedBuffer* info,
+ uint32_t length,
+ uint32_t info_class) {
+ if (sizeof(IO_STATUS_BLOCK) != status->Size())
+ return false;
+ if (length != info->Size())
+ return false;
+
+ FILE_RENAME_INFORMATION* rename_info =
+ reinterpret_cast<FILE_RENAME_INFORMATION*>(info->Buffer());
+
+ if (!IsSupportedRenameCall(rename_info, length, info_class))
+ return false;
+
+ std::wstring name;
+ name.assign(rename_info->FileName,
+ rename_info->FileNameLength / sizeof(rename_info->FileName[0]));
+ if (!PreProcessName(&name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name.c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTSETINFO_RENAME, params.GetBase());
+
+ IO_STATUS_BLOCK* io_status =
+ reinterpret_cast<IO_STATUS_BLOCK*>(status->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::SetInformationFileAction(
+ result, *ipc->client_info, handle, rename_info, length, info_class,
+ io_status, &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h
new file mode 100644
index 0000000000..5551656e62
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
+#define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles file system-related IPC calls.
+class FilesystemDispatcher : public Dispatcher {
+ public:
+ explicit FilesystemDispatcher(PolicyBase* policy_base);
+ ~FilesystemDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateFile in the target.
+ bool NtCreateFile(IPCInfo* ipc,
+ std::wstring* name,
+ 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,
+ std::wstring* name,
+ 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,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtQueryFullAttributesFile in
+ // the target.
+ bool NtQueryFullAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtSetInformationFile with the
+ // rename information class.
+ bool NtSetInformationFile(IPCInfo* ipc,
+ HANDLE handle,
+ CountedBuffer* status,
+ CountedBuffer* info,
+ uint32_t length,
+ uint32_t info_class);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
new file mode 100644
index 0000000000..200dd1f63d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
@@ -0,0 +1,412 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/filesystem_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/filesystem_policy.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"
+
+// This status occurs when trying to access a network share on the machine from
+// which it is shared.
+#define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS)0xC0000201L)
+
+namespace sandbox {
+
+NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_CreateFile(
+ file, desired_access, object_attributes, io_status, allocation_size,
+ file_attributes, sharing, disposition, options, ea_buffer, ea_length);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ uint32_t options_uint32 = options;
+ uint32_t disposition_uint32 = disposition;
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<OpenFile> params;
+ const wchar_t* name_ptr = name.get();
+ params[OpenFile::NAME] = ParamPickerMake(name_ptr);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access_uint32);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(disposition_uint32);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options_uint32);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTCREATEFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ // The following call must match in the parameters with
+ // FilesystemDispatcher::ProcessNtCreateFile.
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTCREATEFILE, name.get(), attributes,
+ desired_access_uint32, file_attributes, sharing, disposition,
+ options_uint32, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtCreateFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes,
+ io_status, sharing, options);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ uint32_t options_uint32 = options;
+ uint32_t disposition_uint32 = FILE_OPEN;
+ uint32_t broker = BROKER_FALSE;
+ const wchar_t* name_ptr = name.get();
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(name_ptr);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access_uint32);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(disposition_uint32);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options_uint32);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTOPENFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTOPENFILE, name.get(), attributes,
+ desired_access_uint32, sharing, options_uint32, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtOpenFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ InOutCountedBuffer file_info(file_attributes,
+ sizeof(FILE_BASIC_INFORMATION));
+
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<FileName> params;
+ const wchar_t* name_ptr = name.get();
+ params[FileName::NAME] = ParamPickerMake(name_ptr);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTQUERYATTRIBUTESFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTQUERYATTRIBUTESFILE, name.get(),
+ attributes, file_info, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ mozilla::sandboxing::LogAllowed("NtQueryAttributesFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryFullAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status =
+ orig_QueryFullAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != 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;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ InOutCountedBuffer file_info(file_attributes,
+ sizeof(FILE_NETWORK_OPEN_INFORMATION));
+
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<FileName> params;
+ const wchar_t* name_ptr = name.get();
+ params[FileName::NAME] = ParamPickerMake(name_ptr);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTQUERYFULLATTRIBUTESFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTQUERYFULLATTRIBUTESFILE,
+ name.get(), attributes, file_info, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ mozilla::sandboxing::LogAllowed("NtQueryFullAttributesFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtSetInformationFile(NtSetInformationFileFunction orig_SetInformationFile,
+ HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_info,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_info_class) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_SetInformationFile(file, io_status, file_info, length,
+ file_info_class);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtSetInformationFile");
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ if (!ValidParameter(file_info, length, READ))
+ break;
+
+ FILE_RENAME_INFORMATION* file_rename_info =
+ reinterpret_cast<FILE_RENAME_INFORMATION*>(file_info);
+ OBJECT_ATTRIBUTES object_attributes;
+ UNICODE_STRING object_name;
+ InitializeObjectAttributes(&object_attributes, &object_name, 0, nullptr,
+ nullptr);
+
+ __try {
+ if (!IsSupportedRenameCall(file_rename_info, length, file_info_class))
+ break;
+
+ object_attributes.RootDirectory = file_rename_info->RootDirectory;
+ object_name.Buffer = file_rename_info->FileName;
+ object_name.Length = object_name.MaximumLength =
+ static_cast<USHORT>(file_rename_info->FileNameLength);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ NTSTATUS ret =
+ AllocAndCopyName(&object_attributes, &name, nullptr, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<FileName> params;
+ const wchar_t* name_ptr = name.get();
+ params[FileName::NAME] = ParamPickerMake(name_ptr);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTSETINFO_RENAME, params.GetBase()))
+ break;
+
+ InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK));
+ // This is actually not an InOut buffer, only In, but using InOut facility
+ // really helps to simplify the code.
+ InOutCountedBuffer file_info_buffer(file_info, length);
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTSETINFO_RENAME, file, io_status_buffer,
+ file_info_buffer, length, file_info_class, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+ mozilla::sandboxing::LogAllowed("NtSetInformationFile");
+ } while (false);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.h b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.h
new file mode 100644
index 0000000000..d9d92f7fa4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_FILESYSTEM_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_FILESYSTEM_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtCreateFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length);
+
+// Interception of NtOpenFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenFile(NtOpenFileFunction orig_OpenFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options);
+
+// Interception of NtQueryAtttributesFile on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes);
+
+// Interception of NtQueryFullAtttributesFile on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes);
+
+// Interception of NtSetInformationFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationFile(NtSetInformationFileFunction orig_SetInformationFile,
+ HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_information,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_information_class);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_FILESYSTEM_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
new file mode 100644
index 0000000000..3018cad0cb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
@@ -0,0 +1,443 @@
+// 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/filesystem_policy.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "base/stl_util.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_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ IO_STATUS_BLOCK* io_status_block,
+ ULONG file_attributes,
+ ULONG share_access,
+ ULONG create_disposition,
+ ULONG create_options,
+ PVOID ea_buffer,
+ ULONG ea_length,
+ HANDLE target_process) {
+ NtCreateFileFunction NtCreateFile = nullptr;
+ ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile);
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status =
+ NtCreateFile(&local_handle, desired_access, obj_attributes,
+ io_status_block, nullptr, file_attributes, share_access,
+ create_disposition, create_options, ea_buffer, ea_length);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_file_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+// Get an initialized anonymous level Security QOS.
+SECURITY_QUALITY_OF_SERVICE GetAnonymousQOS() {
+ SECURITY_QUALITY_OF_SERVICE security_qos = {0};
+ security_qos.Length = sizeof(security_qos);
+ security_qos.ImpersonationLevel = SecurityAnonymous;
+ // Set dynamic tracking so that a pipe doesn't capture the broker's token
+ security_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ security_qos.EffectiveOnly = true;
+
+ return security_qos;
+}
+
+} // namespace.
+
+namespace sandbox {
+
+bool FileSystemPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ // Don't pre-process the path name and check for reparse points if it is the
+ // special case of allowing read access to all paths.
+ if (!(semantics == TargetPolicy::FILES_ALLOW_READONLY
+ && mod_name.compare(L"*") == 0)
+ && !PreProcessName(&mod_name)) {
+ // The path to be added might contain a reparse point.
+ NOTREACHED();
+ return false;
+ }
+
+ // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
+ // infrastructure to normalize names. In any case we need to escape the
+ // question marks.
+ if (_wcsnicmp(mod_name.c_str(), kNTDevicePrefix, kNTDevicePrefixLen)) {
+ mod_name = FixNTPrefixForMatch(mod_name);
+ name = mod_name.c_str();
+ }
+
+ EvalResult result = ASK_BROKER;
+
+ // List of supported calls for the filesystem.
+ const unsigned kCallNtCreateFile = 0x1;
+ const unsigned kCallNtOpenFile = 0x2;
+ const unsigned kCallNtQueryAttributesFile = 0x4;
+ const unsigned kCallNtQueryFullAttributesFile = 0x8;
+ const unsigned kCallNtSetInfoRename = 0x10;
+
+ DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile |
+ kCallNtQueryAttributesFile |
+ kCallNtQueryFullAttributesFile | kCallNtSetInfoRename;
+
+ PolicyRule create(result);
+ PolicyRule open(result);
+ PolicyRule query(result);
+ PolicyRule query_full(result);
+ PolicyRule rename(result);
+
+ switch (semantics) {
+ case TargetPolicy::FILES_ALLOW_DIR_ANY: {
+ open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
+ create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_READONLY: {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write.
+ DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES |
+ FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE |
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
+ DWORD restricted_flags = ~allowed_flags;
+ open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+ open.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
+ create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+ create.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
+
+ // Read only access don't work for rename.
+ rule_to_add &= ~kCallNtSetInfoRename;
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_QUERY: {
+ // Here we don't want to add policy for the open or the create.
+ rule_to_add &=
+ ~(kCallNtOpenFile | kCallNtCreateFile | kCallNtSetInfoRename);
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_ANY: {
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if ((rule_to_add & kCallNtCreateFile) &&
+ (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTCREATEFILE, &create))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtOpenFile) &&
+ (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTOPENFILE, &open))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtQueryAttributesFile) &&
+ (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTQUERYATTRIBUTESFILE, &query))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtQueryFullAttributesFile) &&
+ (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTQUERYFULLATTRIBUTESFILE, &query_full))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtSetInfoRename) &&
+ (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTSETINFO_RENAME, &rename))) {
+ return false;
+ }
+
+ return true;
+}
+
+// Right now we insert two rules, to be evaluated before any user supplied rule:
+// - go to the broker if the path doesn't look like the paths that we push on
+// the policy (namely \??\something).
+// - go to the broker if it looks like this is a short-name path.
+//
+// It is possible to add a rule to go to the broker in any case; it would look
+// something like:
+// rule = new PolicyRule(ASK_BROKER);
+// rule->AddNumberMatch(IF_NOT, FileName::BROKER, true, AND);
+// policy->AddRule(service, rule);
+bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) {
+ PolicyRule format(ASK_BROKER);
+ PolicyRule short_name(ASK_BROKER);
+
+ bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, BROKER_TRUE, AND);
+ rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*",
+ CASE_SENSITIVE);
+
+ rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, BROKER_TRUE, AND);
+ rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE);
+
+ if (!rv || !policy->AddRule(IpcTag::NTCREATEFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTCREATEFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTOPENFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTOPENFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYATTRIBUTESFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYATTRIBUTESFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYFULLATTRIBUTESFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYFULLATTRIBUTESFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTSETINFO_RENAME, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTSETINFO_RENAME, &short_name))
+ return false;
+
+ return true;
+}
+
+bool FileSystemPolicy::CreateFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& 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) {
+ *handle = nullptr;
+ // 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 = {};
+ UNICODE_STRING uni_name = {};
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status =
+ NtCreateFileInTarget(handle, desired_access, &obj_attributes, &io_block,
+ file_attributes, share_access, create_disposition,
+ create_options, nullptr, 0, client_info.process);
+
+ *io_information = io_block.Information;
+ return true;
+}
+
+bool FileSystemPolicy::OpenFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t open_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information) {
+ *handle = nullptr;
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+ // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
+ // CreateDisposition = FILE_OPEN.
+ IO_STATUS_BLOCK io_block = {};
+ UNICODE_STRING uni_name = {};
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status = NtCreateFileInTarget(
+ handle, desired_access, &obj_attributes, &io_block, 0, share_access,
+ FILE_OPEN, open_options, nullptr, 0, client_info.process);
+
+ *io_information = io_block.Information;
+ return true;
+}
+
+bool FileSystemPolicy::QueryAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_BASIC_INFORMATION* file_info,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means query the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ NtQueryAttributesFileFunction NtQueryAttributesFile = nullptr;
+ ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status = NtQueryAttributesFile(&obj_attributes, file_info);
+
+ return true;
+}
+
+bool FileSystemPolicy::QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& 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
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = nullptr;
+ ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile);
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *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_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) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ NtSetInformationFileFunction NtSetInformationFile = nullptr;
+ ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile);
+
+ HANDLE local_handle = nullptr;
+ if (!::DuplicateHandle(client_info.process, target_file_handle,
+ ::GetCurrentProcess(), &local_handle, 0, false,
+ DUPLICATE_SAME_ACCESS)) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ base::win::ScopedHandle handle(local_handle);
+
+ FILE_INFORMATION_CLASS file_info_class =
+ static_cast<FILE_INFORMATION_CLASS>(info_class);
+ *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length,
+ file_info_class);
+
+ return true;
+}
+
+bool PreProcessName(std::wstring* path) {
+ // We now allow symbolic links to be opened via the broker, so we can no
+ // longer rely on the same object check where we checked the path of the
+ // opened file against the original. We don't specify a root when creating
+ // OBJECT_ATTRIBUTES from file names for brokering so they must be fully
+ // qualified and we can just check for the parent directory double dot between
+ // two backslashes. NtCreateFile doesn't seem to allow it anyway, but this is
+ // just an extra precaution. It also doesn't seem to allow the forward slash,
+ // but this is also used for checking policy rules, so we just replace forward
+ // slashes with backslashes.
+ std::replace(path->begin(), path->end(), L'/', L'\\');
+ if (path->find(L"\\..\\") != std::wstring::npos) {
+ return false;
+ }
+
+ ConvertToLongPath(path);
+ return true;
+}
+
+std::wstring FixNTPrefixForMatch(const std::wstring& name) {
+ std::wstring mod_name = name;
+
+ // NT prefix escaped for rule matcher
+ const wchar_t kNTPrefixEscaped[] = L"\\/?/?\\";
+ const int kNTPrefixEscapedLen = base::size(kNTPrefixEscaped) - 1;
+
+ if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) {
+ if (0 != mod_name.compare(0, kNTPrefixEscapedLen, kNTPrefixEscaped)) {
+ // TODO(nsylvain): Find a better way to do name resolution. Right now we
+ // take the name and we expand it.
+ mod_name.insert(0, kNTPrefixEscaped);
+ }
+ } else {
+ // Start of name matches NT prefix, replace with escaped format
+ // Fixes bug: 334882
+ mod_name.replace(0, kNTPrefixLen, kNTPrefixEscaped);
+ }
+
+ return mod_name;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h
new file mode 100644
index 0000000000..81dd1d0d1d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_FILESYSTEM_POLICY_H__
+#define SANDBOX_SRC_FILESYSTEM_POLICY_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum IsBroker { BROKER_FALSE, BROKER_TRUE };
+
+// This class centralizes most of the knowledge related to file system policy
+class FileSystemPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for File IO, in particular open or create actions.
+ // 'name' is the file or directory name.
+ // 'semantics' is the desired semantics for the open or create.
+ // 'policy' is the policy generator to which the rules are going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Add basic file system rules.
+ static bool SetInitialRules(LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a create request with an
+ // API that is compatible with the IPC-received parameters.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'file' : The target file or directory.
+ static bool CreateFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_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);
+
+ // Performs the desired policy action on an open request with an
+ // API that is compatible with the IPC-received parameters.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'file' : The target file or directory.
+ static bool OpenFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t open_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information);
+
+ // Performs the desired policy action on a query request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool QueryAttributesFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_BASIC_INFORMATION* file_info,
+ NTSTATUS* nt_status);
+
+ // Performs the desired policy action on a query request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_NETWORK_OPEN_INFORMATION* file_info,
+ NTSTATUS* nt_status);
+
+ // Performs the desired policy action on a set_info request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool SetInformationFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ HANDLE target_file_handle,
+ void* file_info,
+ uint32_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 the path
+// cannot be trusted.
+bool PreProcessName(std::wstring* path);
+
+// Corrects global paths to have a correctly escaped NT prefix at the
+// beginning. If the name has no NT prefix (either normal or escaped)
+// add the escaped form to the string
+std::wstring FixNTPrefixForMatch(const std::wstring& name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer.cc
new file mode 100644
index 0000000000..6751151dcb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_closer.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+template <typename T>
+T RoundUpToWordSize(T v) {
+ if (size_t mod = v % sizeof(size_t))
+ v += sizeof(size_t) - mod;
+ return v;
+}
+
+template <typename T>
+T* RoundUpToWordSize(T* v) {
+ return reinterpret_cast<T*>(RoundUpToWordSize(reinterpret_cast<size_t>(v)));
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
+
+HandleCloser::HandleCloser() {}
+
+HandleCloser::~HandleCloser() {}
+
+ResultCode HandleCloser::AddHandle(const wchar_t* handle_type,
+ const wchar_t* handle_name) {
+ if (!handle_type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ std::wstring resolved_name;
+ if (handle_name) {
+ resolved_name = handle_name;
+ if (handle_type == std::wstring(L"Key"))
+ if (!ResolveRegistryName(resolved_name, &resolved_name))
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ HandleMap::iterator names = handles_to_close_.find(handle_type);
+ if (names == handles_to_close_.end()) { // We have no entries for this type.
+ std::pair<HandleMap::iterator, bool> result = handles_to_close_.insert(
+ HandleMap::value_type(handle_type, HandleMap::mapped_type()));
+ names = result.first;
+ if (handle_name)
+ names->second.insert(resolved_name);
+ } else if (!handle_name) { // Now we need to close all handles of this type.
+ names->second.clear();
+ } else if (!names->second.empty()) { // Add another name for this type.
+ names->second.insert(resolved_name);
+ } // If we're already closing all handles of type then we're done.
+
+ return SBOX_ALL_OK;
+}
+
+size_t HandleCloser::GetBufferSize() {
+ size_t bytes_total = offsetof(HandleCloserInfo, handle_entries);
+
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ size_t bytes_entry = offsetof(HandleListEntry, handle_type) +
+ (i->first.size() + 1) * sizeof(wchar_t);
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ bytes_entry += ((*j).size() + 1) * sizeof(wchar_t);
+ }
+
+ // Round up to the nearest multiple of word size.
+ bytes_entry = RoundUpToWordSize(bytes_entry);
+ bytes_total += bytes_entry;
+ }
+
+ return bytes_total;
+}
+
+bool HandleCloser::InitializeTargetHandles(TargetProcess* target) {
+ // Do nothing on an empty list (global pointer already initialized to
+ // nullptr).
+ if (handles_to_close_.empty())
+ return true;
+
+ size_t bytes_needed = GetBufferSize();
+ std::unique_ptr<size_t[]> local_buffer(
+ new size_t[bytes_needed / sizeof(size_t)]);
+
+ if (!SetupHandleList(local_buffer.get(), bytes_needed))
+ return false;
+
+ void* remote_data;
+ if (!CopyToChildMemory(target->Process(), local_buffer.get(), bytes_needed,
+ &remote_data))
+ return false;
+
+ g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data);
+
+ ResultCode rc = target->TransferVariable(
+ "g_handles_to_close", &g_handles_to_close, sizeof(g_handles_to_close));
+
+ return (SBOX_ALL_OK == rc);
+}
+
+bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
+ ::ZeroMemory(buffer, buffer_bytes);
+ HandleCloserInfo* handle_info = reinterpret_cast<HandleCloserInfo*>(buffer);
+ handle_info->record_bytes = buffer_bytes;
+ handle_info->num_handle_types = handles_to_close_.size();
+
+ wchar_t* output = reinterpret_cast<wchar_t*>(&handle_info->handle_entries[0]);
+ wchar_t* end = reinterpret_cast<wchar_t*>(reinterpret_cast<char*>(buffer) +
+ buffer_bytes);
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ if (output >= end)
+ return false;
+ HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output);
+ output = &list_entry->handle_type[0];
+
+ // Copy the typename and set the offset and count.
+ i->first.copy(output, i->first.size());
+ *(output += i->first.size()) = L'\0';
+ output++;
+ list_entry->offset_to_names =
+ reinterpret_cast<char*>(output) - reinterpret_cast<char*>(list_entry);
+ list_entry->name_count = i->second.size();
+
+ // Copy the handle names.
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ output = std::copy((*j).begin(), (*j).end(), output) + 1;
+ }
+
+ // Round up to the nearest multiple of sizeof(size_t).
+ output = RoundUpToWordSize(output);
+ list_entry->record_bytes =
+ reinterpret_cast<char*>(output) - reinterpret_cast<char*>(list_entry);
+ }
+
+ DCHECK_EQ(reinterpret_cast<size_t>(output), reinterpret_cast<size_t>(end));
+ return output <= end;
+}
+
+bool GetHandleName(HANDLE handle, std::wstring* handle_name) {
+ static NtQueryObject QueryObject = nullptr;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ ULONG size = MAX_PATH;
+ std::unique_ptr<UNICODE_STRING, base::FreeDeleter> name;
+ NTSTATUS result;
+
+ do {
+ name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
+ DCHECK(name.get());
+ result =
+ QueryObject(handle, ObjectNameInformation, name.get(), size, &size);
+ } while (result == STATUS_INFO_LENGTH_MISMATCH ||
+ result == STATUS_BUFFER_OVERFLOW);
+
+ if (NT_SUCCESS(result) && name->Buffer && name->Length)
+ handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
+ else
+ handle_name->clear();
+
+ return NT_SUCCESS(result);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer.h b/security/sandbox/chromium/sandbox/win/src/handle_closer.h
new file mode 100644
index 0000000000..4f023b27b6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_CLOSER_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <set>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_process.h"
+
+namespace sandbox {
+
+// This is a map of handle-types to names that we need to close in the
+// target process. A null set means we need to close all handles of the
+// given type.
+typedef std::map<const std::wstring, std::set<std::wstring>> HandleMap;
+
+// Type and set of corresponding handle names to close.
+struct HandleListEntry {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t offset_to_names; // Nul terminated strings of name_count names.
+ size_t name_count;
+ wchar_t handle_type[1];
+};
+
+// Global parameters and a pointer to the list of entries.
+struct HandleCloserInfo {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t num_handle_types;
+ struct HandleListEntry handle_entries[1];
+};
+
+SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info;
+
+// Adds handles to close after lockdown.
+class HandleCloser {
+ public:
+ HandleCloser();
+ ~HandleCloser();
+
+ // Adds a handle that will be closed in the target process after lockdown.
+ // A nullptr value for handle_name indicates all handles of the specified
+ // type. An empty string for handle_name indicates the handle is unnamed.
+ ResultCode AddHandle(const wchar_t* handle_type, const wchar_t* handle_name);
+
+ // Serializes and copies the closer table into the target process.
+ bool InitializeTargetHandles(TargetProcess* target);
+
+ private:
+ // Calculates the memory needed to copy the serialized handles list (rounded
+ // to the nearest machine-word size).
+ size_t GetBufferSize();
+
+ // Serializes the handle list into the target process.
+ bool SetupHandleList(void* buffer, size_t buffer_bytes);
+
+ HandleMap handles_to_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloser);
+};
+
+// Returns the object manager's name associated with a handle
+bool GetHandleName(HANDLE handle, std::wstring* handle_name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc
new file mode 100644
index 0000000000..55fe2d4689
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_closer_agent.h"
+
+#include <limits.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/win/static_constants.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Returns type infomation for an NT object. This routine is expected to be
+// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
+// that can be generated when handle tracing is enabled.
+NTSTATUS QueryObjectTypeInformation(HANDLE handle, void* buffer, ULONG* size) {
+ static NtQueryObject QueryObject = nullptr;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ __try {
+ status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
+ } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
+ ? EXCEPTION_EXECUTE_HANDLER
+ : EXCEPTION_CONTINUE_SEARCH) {
+ status = STATUS_INVALID_HANDLE;
+ }
+ return status;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = nullptr;
+
+bool HandleCloserAgent::NeedsHandlesClosed() {
+ return !!g_handles_to_close;
+}
+
+HandleCloserAgent::HandleCloserAgent()
+ : dummy_handle_(::CreateEvent(nullptr, false, false, nullptr)) {}
+
+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 std::wstring& type) {
+ // Only attempt to stuff Files and Events at the moment.
+ if (type != L"Event" && type != L"File") {
+ return true;
+ }
+
+ if (!dummy_handle_.IsValid())
+ return false;
+
+ // This should never happen, as g_dummy is created before closing to_stuff.
+ DCHECK(dummy_handle_.Get() != closed_handle);
+
+ std::vector<HANDLE> to_close;
+
+ const DWORD original_proc_num = GetCurrentProcessorNumber();
+ DWORD proc_num = original_proc_num;
+ DWORD_PTR original_affinity_mask =
+ SetThreadAffinityMask(GetCurrentThread(), DWORD_PTR{1} << proc_num);
+ bool found_handle = false;
+ BOOL result = FALSE;
+
+ // There is per-processor based free list of handles entries. The free handle
+ // from current processor's freelist is preferred for reusing, so cycling
+ // through all possible processors to find closed_handle.
+ // Start searching from current processor which covers usual cases.
+
+ do {
+ DWORD_PTR current_mask = DWORD_PTR{1} << proc_num;
+
+ if (original_affinity_mask & current_mask) {
+ if (proc_num != original_proc_num) {
+ SetThreadAffinityMask(GetCurrentThread(), current_mask);
+ }
+
+ HANDLE dup_dummy = nullptr;
+ size_t count = 16;
+
+ do {
+ result =
+ ::DuplicateHandle(::GetCurrentProcess(), dummy_handle_.Get(),
+ ::GetCurrentProcess(), &dup_dummy, 0, false, 0);
+ if (!result) {
+ break;
+ }
+ if (dup_dummy != closed_handle) {
+ to_close.push_back(dup_dummy);
+ } else {
+ found_handle = true;
+ }
+ } while (count-- && reinterpret_cast<uintptr_t>(dup_dummy) <
+ reinterpret_cast<uintptr_t>(closed_handle));
+ }
+
+ proc_num++;
+ if (proc_num == sizeof(DWORD_PTR) * 8) {
+ proc_num = 0;
+ }
+ if (proc_num == original_proc_num) {
+ break;
+ }
+ } while (result && !found_handle);
+
+ SetThreadAffinityMask(GetCurrentThread(), original_affinity_mask);
+
+ for (HANDLE h : to_close)
+ ::CloseHandle(h);
+
+ return found_handle;
+}
+
+// Reads g_handles_to_close and creates the lookup map.
+void HandleCloserAgent::InitializeHandlesToClose(bool* is_csrss_connected) {
+ CHECK(g_handles_to_close);
+
+ // 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.
+ wchar_t* 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<wchar_t*>(reinterpret_cast<char*>(entry) +
+ entry->offset_to_names);
+ // Grab all the handle names.
+ for (size_t j = 0; j < entry->name_count; ++j) {
+ std::pair<HandleMap::mapped_type::iterator, bool> name =
+ handle_names.insert(input);
+ CHECK(name.second);
+ input += name.first->size() + 1;
+ }
+
+ // Move on to the next entry.
+ entry = reinterpret_cast<HandleListEntry*>(reinterpret_cast<char*>(entry) +
+ entry->record_bytes);
+
+ DCHECK(reinterpret_cast<wchar_t*>(entry) >= input);
+ DCHECK(reinterpret_cast<wchar_t*>(entry) - input <
+ static_cast<ptrdiff_t>(sizeof(size_t) / sizeof(wchar_t)));
+ }
+
+ // Clean up the memory we copied over.
+ ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE);
+ g_handles_to_close = nullptr;
+}
+
+bool HandleCloserAgent::CloseHandles() {
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = 4; // Handles are always a multiple of 4.
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return false;
+
+ // Skip closing these handles when Application Verifier is in use in order to
+ // avoid invalid-handle exceptions.
+ if (GetModuleHandleA(base::win::kApplicationVerifierDllName))
+ return true;
+
+ // Set up buffers for the type info and the name.
+ std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+ 32 * sizeof(wchar_t));
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ std::wstring handle_name;
+ HANDLE handle = nullptr;
+ int invalid_count = 0;
+
+ // Keep incrementing until we hit the number of handles reported by
+ // GetProcessHandleCount(). If we hit a very long sequence of invalid
+ // handles we assume that we've run past the end of the table.
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ NTSTATUS rc;
+
+ // Get the type name, reusing the buffer.
+ ULONG size = static_cast<ULONG>(type_info_buffer.size());
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ while (rc == STATUS_INFO_LENGTH_MISMATCH || rc == STATUS_BUFFER_OVERFLOW) {
+ type_info_buffer.resize(size + sizeof(wchar_t));
+ type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ // Leave padding for the nul terminator.
+ if (NT_SUCCESS(rc) && size == type_info_buffer.size())
+ rc = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) {
+ ++invalid_count;
+ continue;
+ }
+
+ --handle_count;
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ // Check if we're looking for this type of handle.
+ HandleMap::iterator result = handles_to_close_.find(type_info->Name.Buffer);
+ if (result != handles_to_close_.end()) {
+ HandleMap::mapped_type& names = result->second;
+ // Empty set means close all handles of this type; otherwise check name.
+ if (!names.empty()) {
+ // Move on to the next handle if this name doesn't match.
+ if (!GetHandleName(handle, &handle_name) || !names.count(handle_name))
+ continue;
+ }
+
+ if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0))
+ return false;
+ if (!::CloseHandle(handle))
+ return false;
+ // Attempt to stuff this handle with a new dummy Event.
+ AttemptToStuffHandleSlot(handle, result->first);
+ }
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h
new file mode 100644
index 0000000000..91f8e74c7e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
+
+#include <string>
+
+#include "base/macros.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();
+
+ // Reads the serialized list from the broker and creates the lookup map.
+ // 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.
+ static bool NeedsHandlesClosed();
+
+ private:
+ // Attempt to stuff a closed handle with a dummy Event.
+ bool AttemptToStuffHandleSlot(HANDLE closed_handle, const std::wstring& type);
+
+ HandleMap handles_to_close_;
+ base::win::ScopedHandle dummy_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc
new file mode 100644
index 0000000000..7406f77592
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits.h>
+#include <stddef.h>
+
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const wchar_t* kFileExtensions[] = {L".1", L".2", L".3", L".4"};
+
+// Returns a handle to a unique marker file that can be retrieved between runs.
+HANDLE GetMarkerFile(const wchar_t* extension) {
+ wchar_t path_buffer[MAX_PATH + 1];
+ CHECK(::GetTempPath(MAX_PATH, path_buffer));
+ std::wstring marker_path = path_buffer;
+ marker_path += L"\\sbox_marker_";
+
+ // Generate a unique value from the exe's size and timestamp.
+ CHECK(::GetModuleFileName(nullptr, path_buffer, MAX_PATH));
+ base::win::ScopedHandle module(
+ ::CreateFile(path_buffer, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+ CHECK(module.IsValid());
+ FILETIME timestamp;
+ CHECK(::GetFileTime(module.Get(), &timestamp, nullptr, nullptr));
+ marker_path +=
+ base::StringPrintf(L"%08x%08x%08x", ::GetFileSize(module.Get(), nullptr),
+ timestamp.dwLowDateTime, timestamp.dwHighDateTime);
+ marker_path += extension;
+
+ // Make the file delete-on-close so cleanup is automatic.
+ return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
+}
+
+// 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 = nullptr;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ __try {
+ status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
+ } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
+ ? EXCEPTION_EXECUTE_HANDLER
+ : EXCEPTION_CONTINUE_SEARCH) {
+ status = STATUS_INVALID_HANDLE;
+ }
+ return status;
+}
+
+// Used by the thread pool tests.
+HANDLE finish_event;
+const int kWaitCount = 20;
+
+} // namespace
+
+namespace sandbox {
+
+// Checks for the presence of a list of files (in object path form).
+// Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...]
+// - Y or N depending if the file should exist or not.
+SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t** argv) {
+ if (argc < 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ bool should_find = argv[0][0] == L'Y';
+ if (argv[0][1] != L'\0' || (!should_find && argv[0][0] != L'N'))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ static int state = BEFORE_INIT;
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ // The handles leak, but it will be closed by the test or on exit.
+ for (const wchar_t* kExtension : kFileExtensions)
+ CHECK_NE(GetMarkerFile(kExtension), INVALID_HANDLE_VALUE);
+ return SBOX_TEST_SUCCEEDED;
+
+ case AFTER_REVERT: {
+ // Brute force the handle table to find what we're looking for.
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = 4; // Handles are always a multiple of 4.
+ HANDLE handle = nullptr;
+ int invalid_count = 0;
+ std::wstring handle_name;
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ if (GetHandleName(handle, &handle_name)) {
+ for (int i = 1; i < argc; ++i) {
+ if (handle_name == argv[i])
+ return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ }
+ --handle_count;
+ } else {
+ ++invalid_count;
+ }
+ }
+
+ return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+
+ default: // Do nothing.
+ break;
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Checks that supplied handle is an Event and it's not waitable.
+// Format: CheckForEventHandles
+SBOX_TESTS_COMMAND int CheckForEventHandles(int argc, wchar_t** argv) {
+ static int state = BEFORE_INIT;
+ static std::vector<HANDLE> to_check;
+
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ for (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 (HANDLE handle : to_check) {
+ // Set up buffers for the type info and the name.
+ std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+ 32 * sizeof(wchar_t));
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ NTSTATUS rc;
+
+ // Get the type name, reusing the buffer.
+ ULONG size = static_cast<ULONG>(type_info_buffer.size());
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ while (rc == STATUS_INFO_LENGTH_MISMATCH ||
+ rc == STATUS_BUFFER_OVERFLOW) {
+ type_info_buffer.resize(size + sizeof(wchar_t));
+ type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
+ &(type_info_buffer[0]));
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ // Leave padding for the nul terminator.
+ if (NT_SUCCESS(rc) && size == type_info_buffer.size())
+ rc = STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ CHECK(NT_SUCCESS(rc));
+ CHECK(type_info->Name.Buffer);
+
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] =
+ L'\0';
+
+ // Should be an Event now.
+ CHECK_EQ(wcslen(type_info->Name.Buffer), 5U);
+ CHECK_EQ(wcscmp(L"Event", type_info->Name.Buffer), 0);
+
+ // Should not be able to wait.
+ CHECK_EQ(WaitForSingleObject(handle, INFINITE), WAIT_FAILED);
+
+ // Should be able to close.
+ CHECK(::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);
+
+ std::wstring command = std::wstring(L"CheckForFileHandles Y");
+ for (const wchar_t* kExtension : kFileExtensions) {
+ std::wstring handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kExtension));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+ command += (L" ");
+ command += handle_name;
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str()))
+ << "Failed: " << command;
+}
+
+TEST(HandleCloserTest, CloseMarkerFiles) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ std::wstring command = std::wstring(L"CheckForFileHandles N");
+ for (const wchar_t* kExtension : kFileExtensions) {
+ std::wstring 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);
+ command += (L" ");
+ command += handle_name;
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str()))
+ << "Failed: " << command;
+}
+
+TEST(HandleCloserTest, CheckStuffedHandle) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ for (const wchar_t* kExtension : kFileExtensions) {
+ std::wstring 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);
+ CHECK(::CloseHandle(event));
+ if (::InterlockedDecrement(&waiters_remaining) == 0)
+ CHECK(::SetEvent(finish_event));
+}
+
+// Run a thread pool inside a sandbox without a CSRSS connection.
+SBOX_TESTS_COMMAND int RunThreadPool(int argc, wchar_t** argv) {
+ HANDLE wait_list[20];
+ finish_event = ::CreateEvent(nullptr, true, false, nullptr);
+ CHECK(finish_event);
+
+ // Set up a bunch of waiters.
+ HANDLE pool = nullptr;
+ for (int i = 0; i < kWaitCount; ++i) {
+ HANDLE event = ::CreateEvent(nullptr, true, false, nullptr);
+ CHECK(event);
+ CHECK(::RegisterWaitForSingleObject(&pool, event, ThreadPoolTask, event,
+ INFINITE, WT_EXECUTEONLYONCE));
+ wait_list[i] = event;
+ }
+
+ // Signal all the waiters.
+ for (int i = 0; i < kWaitCount; ++i)
+ CHECK(::SetEvent(wait_list[i]));
+
+ CHECK_EQ(::WaitForSingleObject(finish_event, INFINITE), WAIT_OBJECT_0);
+ CHECK(::CloseHandle(finish_event));
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, RunThreadPool) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(AFTER_REVERT);
+
+ // Sandbox policy will determine which platforms to disconnect CSRSS and when
+ // to close the CSRSS handle.
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"RunThreadPool"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc
new file mode 100644
index 0000000000..611e33d2a6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_interception.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+HandleDispatcher::HandleDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall duplicate_handle_proxy = {
+ {IpcTag::DUPLICATEHANDLEPROXY,
+ {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &HandleDispatcher::DuplicateHandleProxy)};
+
+ ipc_calls_.push_back(duplicate_handle_proxy);
+}
+
+bool HandleDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ // We perform no interceptions for handles right now.
+ switch (service) {
+ case IpcTag::DUPLICATEHANDLEPROXY:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc,
+ HANDLE source_handle,
+ uint32_t target_process_id,
+ uint32_t desired_access,
+ uint32_t options) {
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ // Get a copy of the handle for use in the broker process.
+ HANDLE handle_temp;
+ if (!::DuplicateHandle(ipc->client_info->process, source_handle,
+ ::GetCurrentProcess(), &handle_temp,
+ 0, FALSE, DUPLICATE_SAME_ACCESS | options)) {
+ ipc->return_info.win32_result = ::GetLastError();
+ return false;
+ }
+ options &= ~DUPLICATE_CLOSE_SOURCE;
+ base::win::ScopedHandle handle(handle_temp);
+
+ // Get the object type (32 characters is safe; current max is 14).
+ BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)];
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer);
+ ULONG size = sizeof(buffer) - sizeof(wchar_t);
+ NTSTATUS error =
+ QueryObject(handle.Get(), ObjectTypeInformation, type_info, size, &size);
+ if (!NT_SUCCESS(error)) {
+ ipc->return_info.nt_status = error;
+ return false;
+ }
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ CountedParameterSet<HandleTarget> params;
+ params[HandleTarget::NAME] = ParamPickerMake(type_info->Name.Buffer);
+ params[HandleTarget::TARGET] = ParamPickerMake(target_process_id);
+
+ EvalResult eval = policy_base_->EvalPolicy(IpcTag::DUPLICATEHANDLEPROXY,
+ params.GetBase());
+ ipc->return_info.win32_result =
+ HandlePolicy::DuplicateHandleProxyAction(eval, handle.Get(),
+ target_process_id,
+ &ipc->return_info.handle,
+ desired_access, options);
+ return true;
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h
new file mode 100644
index 0000000000..6f9adbc10b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_
+#define SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles handle-related IPC calls.
+class HandleDispatcher : public Dispatcher {
+ public:
+ explicit HandleDispatcher(PolicyBase* policy_base);
+ ~HandleDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to
+ // TargetServices::DuplicateHandle() in the target.
+ bool DuplicateHandleProxy(IPCInfo* ipc,
+ HANDLE source_handle,
+ uint32_t target_process_id,
+ uint32_t desired_access,
+ uint32_t options);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(HandleDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc
new file mode 100644
index 0000000000..c91903c809
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int HandleInheritanceTests_PrintToStdout(int argc,
+ wchar_t** argv) {
+ printf("Example output to stdout\n");
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleInheritanceTests, TestStdoutInheritance) {
+ base::ScopedTempDir temp_directory;
+ base::FilePath temp_file_name;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ ASSERT_TRUE(
+ CreateTemporaryFileInDir(temp_directory.GetPath(), &temp_file_name));
+
+ SECURITY_ATTRIBUTES attrs = {};
+ attrs.nLength = sizeof(attrs);
+ attrs.bInheritHandle = true;
+ base::win::ScopedHandle tmp_handle(
+ CreateFile(temp_file_name.value().c_str(), GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, &attrs,
+ OPEN_EXISTING, 0, nullptr));
+ ASSERT_TRUE(tmp_handle.IsValid());
+
+ TestRunner runner;
+ ASSERT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStdoutHandle(tmp_handle.Get()));
+ int result = runner.RunTest(L"HandleInheritanceTests_PrintToStdout");
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED, result);
+
+ std::string data;
+ ASSERT_TRUE(base::ReadFileToString(base::FilePath(temp_file_name), &data));
+ // Redirection uses a feature that was added in Windows Vista.
+ ASSERT_EQ("Example output to stdout\r\n", data);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
new file mode 100644
index 0000000000..53db4a8b27
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ *target_handle = NULL;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ return SBOX_ERROR_NO_SPACE;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::DUPLICATEHANDLEPROXY,
+ source_handle, target_process_id,
+ desired_access, options, &answer);
+ if (SBOX_ALL_OK != code)
+ return code;
+
+ 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;
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.h b/security/sandbox/chromium/sandbox/win/src/handle_interception.h
new file mode 100644
index 0000000000..6f60811f17
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+#define SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+
+namespace sandbox {
+
+// TODO(jschuh) Add an interception to catch dangerous DuplicateHandle calls.
+
+ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.cc b/security/sandbox/chromium/sandbox/win/src/handle_policy.cc
new file mode 100644
index 0000000000..fa3295ae3f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_policy.h"
+
+#include <string>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/broker_services.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+bool HandlePolicy::GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule duplicate_rule(ASK_BROKER);
+
+ switch (semantics) {
+ case TargetPolicy::HANDLES_DUP_ANY: {
+ if (!duplicate_rule.AddNumberMatch(IF_NOT, HandleTarget::TARGET,
+ ::GetCurrentProcessId(), EQUAL)) {
+ return false;
+ }
+ break;
+ }
+
+ case TargetPolicy::HANDLES_DUP_BROKER: {
+ if (!duplicate_rule.AddNumberMatch(IF, HandleTarget::TARGET,
+ ::GetCurrentProcessId(), EQUAL)) {
+ return false;
+ }
+ break;
+ }
+
+ default:
+ return false;
+ }
+ if (!duplicate_rule.AddStringMatch(IF, HandleTarget::NAME, type_name,
+ CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::DUPLICATEHANDLEPROXY, &duplicate_rule)) {
+ return false;
+ }
+ return true;
+}
+
+DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result,
+ HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ // The only action supported is ASK_BROKER which means duplicate the handle.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ base::win::ScopedHandle remote_target_process;
+ if (target_process_id != ::GetCurrentProcessId()) {
+ // Sandboxed children are dynamic, so we check that manually.
+ if (!BrokerServicesBase::GetInstance()->IsSafeDuplicationTarget(
+ target_process_id)) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ remote_target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE,
+ target_process_id));
+ if (!remote_target_process.IsValid())
+ return ::GetLastError();
+ }
+
+ // If the policy didn't block us and we have no valid target, then the broker
+ // (this process) is the valid target.
+ HANDLE target_process = remote_target_process.IsValid() ?
+ remote_target_process.Get() : ::GetCurrentProcess();
+ if (!::DuplicateHandle(::GetCurrentProcess(), source_handle, target_process,
+ target_handle, desired_access, FALSE,
+ options)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.h b/security/sandbox/chromium/sandbox/win/src/handle_policy.h
new file mode 100644
index 0000000000..29ce5ab666
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_POLICY_H_
+#define SANDBOX_SRC_HANDLE_POLICY_H_
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum EvalResult;
+
+// This class centralizes most of the knowledge related to handle policy.
+class HandlePolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for handles, in particular duplicate action.
+ static bool GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Processes a 'TargetPolicy::DuplicateHandle()' request from the target.
+ static DWORD DuplicateHandleProxyAction(EvalResult eval_result,
+ HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_POLICY_H_
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc
new file mode 100644
index 0000000000..11382da811
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/stringprintf.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Just waits for the supplied number of milliseconds.
+SBOX_TESTS_COMMAND int Handle_WaitProcess(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ ::Sleep(::wcstoul(argv[0], NULL, 10));
+ return SBOX_TEST_TIMED_OUT;
+}
+
+// Attempts to duplicate an event handle into the target process.
+SBOX_TESTS_COMMAND int Handle_DuplicateEvent(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // Create a test event to use as a handle.
+ base::win::ScopedHandle test_event;
+ test_event.Set(::CreateEvent(NULL, TRUE, TRUE, NULL));
+ if (!test_event.IsValid())
+ return SBOX_TEST_FIRST_ERROR;
+
+ // Get the target process ID.
+ DWORD target_process_id = ::wcstoul(argv[0], NULL, 10);
+
+ HANDLE handle = NULL;
+ ResultCode result = SandboxFactory::GetTargetServices()->DuplicateHandle(
+ test_event.Get(), target_process_id, &handle, 0, DUPLICATE_SAME_ACCESS);
+
+ return (result == SBOX_ALL_OK) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicateHandle) {
+ TestRunner target;
+ TestRunner runner;
+
+ // Kick off an asynchronous target process for testing.
+ target.SetAsynchronous(true);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
+
+ // First test that we fail to open the event.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicatePeerHandle) {
+ TestRunner target;
+ TestRunner runner;
+
+ // Kick off an asynchronous target process for testing.
+ target.SetAsynchronous(true);
+ target.SetUnsandboxed(true);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
+
+ // First test that we fail to open the event.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicateBrokerHandle) {
+ TestRunner runner;
+
+ // First test that we fail to open the event.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ ::GetCurrentProcessId());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Add the peer rule and make sure we fail again.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+
+ // Now successfully open the event after adding a broker handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_BROKER,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/heap_helper.cc b/security/sandbox/chromium/sandbox/win/src/heap_helper.cc
new file mode 100644
index 0000000000..b0f4498fea
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/heap_helper.cc
@@ -0,0 +1,124 @@
+// Copyright 2017 The Chromium Authors. All 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/heap_helper.h"
+
+#include <windows.h>
+
+#include "base/memory/ref_counted.h"
+#include "base/win/windows_version.h"
+
+namespace sandbox {
+namespace {
+#pragma pack(1)
+
+// These are undocumented, but readily found on the internet.
+constexpr DWORD kHeapClass8 = 0x00008000; // CSR port heap
+constexpr DWORD kHeapClassMask = 0x0000f000;
+
+constexpr DWORD kHeapSegmentSignature = 0xffeeffee;
+constexpr DWORD kHeapSignature = 0xeeffeeff;
+
+typedef struct _HEAP_ENTRY {
+ PVOID Data1;
+ PVOID Data2;
+} HEAP_ENTRY, *PHEAP_ENTRY;
+
+// The _HEAP struct is not documented, so char arrays are used to space out the
+// struct of the fields that are not relevant. However, this spacing is
+// different because of the different pointer widths between 32 and 64-bit.
+// So 32 and 64 bit structs are defined.
+struct _HEAP_32 {
+ HEAP_ENTRY HeapEntry;
+ DWORD SegmentSignature;
+ DWORD SegmentFlags;
+ LIST_ENTRY SegmentListEntry;
+ struct _HEAP_32* Heap;
+ char Unknown0[0x24];
+ // Offset 0x40
+ DWORD Flags;
+ // Offset 0x60
+ char Unknown1[0x1c];
+ DWORD Signature;
+ // Other stuff that is not relevant.
+};
+
+struct _HEAP_64 {
+ HEAP_ENTRY HeapEntry;
+ DWORD SegmentSignature;
+ DWORD SegmentFlags;
+ LIST_ENTRY SegmentListEntry;
+ struct _HEAP_64* Heap;
+ char Unknown0[0x40];
+ // Offset 0x70
+ DWORD Flags;
+ // Offset 0x98
+ char Unknown1[0x24];
+ DWORD Signature;
+ // Other stuff that is not relevant.
+};
+
+#if defined(_WIN64)
+using _HEAP = _HEAP_64;
+#else // defined(_WIN64)
+using _HEAP = _HEAP_32;
+#endif // defined(_WIN64)
+
+bool ValidateHeap(_HEAP* heap) {
+ if (heap->SegmentSignature != kHeapSegmentSignature)
+ return false;
+ if (heap->Heap != heap)
+ return false;
+ if (heap->Signature != kHeapSignature)
+ return false;
+ return true;
+}
+
+} // namespace
+
+bool HeapFlags(HANDLE handle, DWORD* flags) {
+ if (!handle || !flags) {
+ // This is an error.
+ return false;
+ }
+ _HEAP* heap = reinterpret_cast<_HEAP*>(handle);
+ if (!ValidateHeap(heap)) {
+ DLOG(ERROR) << "unable to validate heap";
+ return false;
+ }
+ *flags = heap->Flags;
+ return true;
+}
+
+HANDLE FindCsrPortHeap() {
+ if (base::win::GetVersion() < base::win::Version::WIN10) {
+ // This functionality has not been verified on versions before Win10.
+ return nullptr;
+ }
+ DWORD number_of_heaps = ::GetProcessHeaps(0, nullptr);
+ std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]);
+ if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps)
+ return nullptr;
+
+ // Search for the CSR port heap handle, identified purely based on flags.
+ HANDLE csr_port_heap = nullptr;
+ for (size_t i = 0; i < number_of_heaps; ++i) {
+ HANDLE handle = all_heaps[i];
+ DWORD flags = 0;
+ if (!HeapFlags(handle, &flags)) {
+ DLOG(ERROR) << "Unable to get flags for this heap";
+ continue;
+ }
+ if ((flags & kHeapClassMask) == kHeapClass8) {
+ if (csr_port_heap) {
+ DLOG(ERROR) << "Found multiple suitable CSR Port heaps";
+ return nullptr;
+ }
+ csr_port_heap = handle;
+ }
+ }
+ return csr_port_heap;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/heap_helper.h b/security/sandbox/chromium/sandbox/win/src/heap_helper.h
new file mode 100644
index 0000000000..302a3c4e29
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/heap_helper.h
@@ -0,0 +1,26 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_HEAP_HELPER_H_
+#define SANDBOX_WIN_SRC_HEAP_HELPER_H_
+
+#include <windows.h>
+
+#include "base/win/windows_version.h"
+
+namespace sandbox {
+// These helper functions are not expected to be used generally, but are exposed
+// only to allow direct testing of this functionality.
+
+// Return the flags for this heap handle. Limited verification of the handle is
+// performed. No verification of the flags is performed.
+bool HeapFlags(HANDLE handle, DWORD* flags);
+
+// Return the handle to the CSR Port Heap, return nullptr if none or more than
+// one candidate was found.
+HANDLE FindCsrPortHeap();
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_HEAP_HELPER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc b/security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc
new file mode 100644
index 0000000000..a4788af028
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include <atlsecurity.h>
+
+#include "base/process/process_info.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int CheckUntrustedIntegrityLevel(int argc, wchar_t** argv) {
+ return (base::GetCurrentProcessIntegrityLevel() == base::UNTRUSTED_INTEGRITY)
+ ? SBOX_TEST_SUCCEEDED
+ : SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int CheckLowIntegrityLevel(int argc, wchar_t** argv) {
+ return (base::GetCurrentProcessIntegrityLevel() == base::LOW_INTEGRITY)
+ ? SBOX_TEST_SUCCEEDED
+ : SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int CheckIntegrityLevel(int argc, wchar_t** argv) {
+ ATL::CAccessToken token;
+ if (!token.GetEffectiveToken(TOKEN_READ))
+ return SBOX_TEST_FAILED;
+
+ char* buffer[100];
+ DWORD buf_size = 100;
+ if (!::GetTokenInformation(token.GetHandle(), TokenIntegrityLevel,
+ reinterpret_cast<void*>(buffer), buf_size,
+ &buf_size))
+ return SBOX_TEST_FAILED;
+
+ TOKEN_MANDATORY_LABEL* label =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buffer);
+
+ PSID sid_low = nullptr;
+ if (!::ConvertStringSidToSid(L"S-1-16-4096", &sid_low))
+ return SBOX_TEST_FAILED;
+
+ bool is_low_sid = ::EqualSid(label->Label.Sid, sid_low);
+
+ ::LocalFree(sid_low);
+
+ if (is_low_sid)
+ return SBOX_TEST_SUCCEEDED;
+
+ return SBOX_TEST_DENIED;
+}
+
+TEST(IntegrityLevelTest, TestLowILReal) {
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ runner.GetPolicy()->SetAlternateDesktop(true);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(DelayedIntegrityLevelTest, TestLowILDelayed) {
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestNoILChange) {
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestUntrustedIL) {
+ TestRunner runner(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED);
+ runner.GetPolicy()->SetLockdownDefaultDacl();
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"CheckUntrustedIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestLowIL) {
+ TestRunner runner(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetLockdownDefaultDacl();
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckLowIntegrityLevel"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interception.cc b/security/sandbox/chromium/sandbox/win/src/interception.cc
new file mode 100644
index 0000000000..b2c0bc47ea
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception.cc
@@ -0,0 +1,512 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "sandbox/win/src/interception.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_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/win_utils.h"
+
+namespace sandbox {
+
+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 {
+ GetRandom(&offset);
+ offset &= (kAllocGranularity - 1);
+ } while (offset > (kAllocGranularity - size));
+
+ // Find an alignment between 64 and the page size (4096).
+ size_t align_size = kPageSize;
+ for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) {
+ align_size = new_size;
+ }
+ return offset & ~(align_size - 1);
+}
+
+} // namespace internal
+
+SANDBOX_INTERCEPT SharedMemory* g_interceptions;
+
+// Table of the unpatched functions that we intercept. Mapped from the parent.
+SANDBOX_INTERCEPT OriginalFunctions g_originals = {nullptr};
+
+// Magic constant that identifies that this function is not to be patched.
+const char kUnloadDLLDummyFunction[] = "@";
+
+InterceptionManager::InterceptionData::InterceptionData() {}
+
+InterceptionManager::InterceptionData::InterceptionData(
+ const InterceptionData& other) = default;
+
+InterceptionManager::InterceptionData::~InterceptionData() {}
+
+InterceptionManager::InterceptionManager(TargetProcess* child_process,
+ bool relaxed)
+ : child_(child_process), names_used_(false), relaxed_(relaxed) {
+ child_->AddRef();
+}
+InterceptionManager::~InterceptionManager() {
+ child_->Release();
+}
+
+bool InterceptionManager::AddToPatchedFunctions(
+ const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const void* replacement_code_address,
+ InterceptorId id) {
+ InterceptionData function;
+ function.type = interception_type;
+ function.id = id;
+ function.dll = dll_name;
+ function.function = function_name;
+ function.interceptor_address = replacement_code_address;
+
+ interceptions_.push_back(function);
+ return true;
+}
+
+bool InterceptionManager::AddToPatchedFunctions(
+ const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const char* replacement_function_name,
+ InterceptorId id) {
+ InterceptionData function;
+ function.type = interception_type;
+ function.id = id;
+ function.dll = dll_name;
+ function.function = function_name;
+ function.interceptor = replacement_function_name;
+ function.interceptor_address = nullptr;
+
+ interceptions_.push_back(function);
+ names_used_ = true;
+ return true;
+}
+
+bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) {
+ InterceptionData module_to_unload;
+ module_to_unload.type = INTERCEPTION_UNLOAD_MODULE;
+ module_to_unload.dll = dll_name;
+ // The next two are dummy values that make the structures regular, instead
+ // of having special cases. They should not be used.
+ module_to_unload.function = kUnloadDLLDummyFunction;
+ module_to_unload.interceptor_address = reinterpret_cast<void*>(1);
+
+ interceptions_.push_back(module_to_unload);
+ return true;
+}
+
+ResultCode InterceptionManager::InitializeInterceptions() {
+ if (interceptions_.empty())
+ return SBOX_ALL_OK; // Nothing to do here
+
+ size_t buffer_bytes = GetBufferSize();
+ std::unique_ptr<char[]> local_buffer(new char[buffer_bytes]);
+
+ if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
+ return SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_CONFIG_BUFFER;
+
+ void* remote_buffer;
+ if (!CopyToChildMemory(child_->Process(), local_buffer.get(), buffer_bytes,
+ &remote_buffer))
+ return SBOX_ERROR_CANNOT_COPY_DATA_TO_CHILD;
+
+ bool hot_patch_needed = (0 != buffer_bytes);
+ ResultCode rc = PatchNtdll(hot_patch_needed);
+
+ if (rc != SBOX_ALL_OK)
+ return rc;
+
+ g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
+ rc = child_->TransferVariable("g_interceptions", &g_interceptions,
+ sizeof(g_interceptions));
+ return rc;
+}
+
+size_t InterceptionManager::GetBufferSize() const {
+ std::set<std::wstring> dlls;
+ size_t buffer_bytes = 0;
+
+ for (const auto& interception : interceptions_) {
+ // skip interceptions that are performed from the parent
+ if (!IsInterceptionPerformedByChild(interception))
+ continue;
+
+ if (!dlls.count(interception.dll)) {
+ // NULL terminate the dll name on the structure
+ size_t dll_name_bytes = (interception.dll.size() + 1) * sizeof(wchar_t);
+
+ // include the dll related size
+ buffer_bytes += RoundUpToMultiple(
+ offsetof(DllPatchInfo, dll_name) + dll_name_bytes, sizeof(size_t));
+ dlls.insert(interception.dll);
+ }
+
+ // we have to NULL terminate the strings on the structure
+ size_t strings_chars =
+ interception.function.size() + interception.interceptor.size() + 2;
+
+ // a new FunctionInfo is required per function
+ size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
+ record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
+ buffer_bytes += record_bytes;
+ }
+
+ if (0 != buffer_bytes)
+ // add the part of SharedMemory that we have not counted yet
+ buffer_bytes += offsetof(SharedMemory, dll_list);
+
+ return buffer_bytes;
+}
+
+// Basically, walk the list of interceptions moving them to the config buffer,
+// but keeping together all interceptions that belong to the same dll.
+// The config buffer is a local buffer, not the one allocated on the child.
+bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
+ if (0 == buffer_bytes)
+ return true;
+
+ DCHECK(buffer_bytes > sizeof(SharedMemory));
+
+ SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
+ DllPatchInfo* dll_info = shared_memory->dll_list;
+ int num_dlls = 0;
+
+ shared_memory->interceptor_base =
+ names_used_ ? child_->MainModule() : nullptr;
+
+ buffer_bytes -= offsetof(SharedMemory, dll_list);
+ buffer = dll_info;
+
+ std::list<InterceptionData>::iterator it = interceptions_.begin();
+ for (; it != interceptions_.end();) {
+ // skip interceptions that are performed from the parent
+ if (!IsInterceptionPerformedByChild(*it)) {
+ ++it;
+ continue;
+ }
+
+ const std::wstring dll = it->dll;
+ if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
+ return false;
+
+ // walk the interceptions from this point, saving the ones that are
+ // performed on this dll, and removing the entry from the list.
+ // advance the iterator before removing the element from the list
+ std::list<InterceptionData>::iterator rest = it;
+ for (; rest != interceptions_.end();) {
+ if (rest->dll == dll) {
+ if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
+ return false;
+ if (it == rest)
+ ++it;
+ rest = interceptions_.erase(rest);
+ } else {
+ ++rest;
+ }
+ }
+ dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
+ ++num_dlls;
+ }
+
+ shared_memory->num_intercepted_dlls = num_dlls;
+ return true;
+}
+
+// Fills up just the part that depends on the dll, not the info that depends on
+// the actual interception.
+bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes) const {
+ DCHECK(buffer_bytes);
+ DCHECK(buffer);
+ DCHECK(*buffer);
+
+ DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);
+
+ // the strings have to be zero terminated
+ size_t required = offsetof(DllPatchInfo, dll_name) +
+ (data.dll.size() + 1) * sizeof(wchar_t);
+ required = RoundUpToMultiple(required, sizeof(size_t));
+ if (*buffer_bytes < required)
+ return false;
+
+ *buffer_bytes -= required;
+ *buffer = reinterpret_cast<char*>(*buffer) + required;
+
+ // set up the dll info to be what we know about it at this time
+ dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE);
+ dll_info->record_bytes = required;
+ dll_info->offset_to_functions = required;
+ dll_info->num_functions = 0;
+ data.dll.copy(dll_info->dll_name, data.dll.size());
+ dll_info->dll_name[data.dll.size()] = L'\0';
+
+ return true;
+}
+
+bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes,
+ DllPatchInfo* dll_info) const {
+ DCHECK(buffer_bytes);
+ DCHECK(buffer);
+ DCHECK(*buffer);
+
+ if ((dll_info->unload_module) && (data.function != kUnloadDLLDummyFunction)) {
+ // Can't specify a dll for both patch and unload.
+ NOTREACHED();
+ }
+
+ FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);
+
+ size_t name_bytes = data.function.size();
+ size_t interceptor_bytes = data.interceptor.size();
+
+ // the strings at the end of the structure are zero terminated
+ size_t required =
+ offsetof(FunctionInfo, function) + name_bytes + interceptor_bytes + 2;
+ required = RoundUpToMultiple(required, sizeof(size_t));
+ if (*buffer_bytes < required)
+ return false;
+
+ // update the caller's values
+ *buffer_bytes -= required;
+ *buffer = reinterpret_cast<char*>(*buffer) + required;
+
+ function->record_bytes = required;
+ function->type = data.type;
+ function->id = data.id;
+ function->interceptor_address = data.interceptor_address;
+ char* names = function->function;
+
+ data.function.copy(names, name_bytes);
+ names += name_bytes;
+ *names++ = '\0';
+
+ // interceptor follows the function_name
+ data.interceptor.copy(names, interceptor_bytes);
+ names += interceptor_bytes;
+ *names++ = '\0';
+
+ // update the dll table
+ dll_info->num_functions++;
+ dll_info->record_bytes += required;
+
+ return true;
+}
+
+// Only return true if the child should be able to perform this interception.
+bool InterceptionManager::IsInterceptionPerformedByChild(
+ const InterceptionData& data) const {
+ if (INTERCEPTION_INVALID == data.type)
+ return false;
+
+ if (INTERCEPTION_SERVICE_CALL == data.type)
+ return false;
+
+ if (data.type >= INTERCEPTION_LAST)
+ return false;
+
+ std::wstring ntdll(kNtdllName);
+ if (ntdll == data.dll)
+ return false; // ntdll has to be intercepted from the parent
+
+ return true;
+}
+
+ResultCode InterceptionManager::PatchNtdll(bool hot_patch_needed) {
+ // Maybe there is nothing to do
+ if (!hot_patch_needed && interceptions_.empty())
+ return SBOX_ALL_OK;
+
+ if (hot_patch_needed) {
+#if defined(SANDBOX_EXPORTS)
+// Make sure the functions are not excluded by the linker.
+#if defined(_WIN64)
+#pragma comment(linker, "/include:TargetNtMapViewOfSection64")
+#pragma comment(linker, "/include:TargetNtUnmapViewOfSection64")
+#else
+#pragma comment(linker, "/include:_TargetNtMapViewOfSection@44")
+#pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12")
+#endif
+#endif // defined(SANDBOX_EXPORTS)
+ ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44);
+ ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12);
+ }
+
+ // Reserve a full 64k memory range in the child process.
+ HANDLE child = child_->Process();
+ BYTE* thunk_base = reinterpret_cast<BYTE*>(::VirtualAllocEx(
+ child, nullptr, kAllocGranularity, MEM_RESERVE, PAGE_NOACCESS));
+
+ // Find an aligned, random location within the reserved range.
+ size_t thunk_bytes =
+ interceptions_.size() * sizeof(ThunkData) + sizeof(DllInterceptionData);
+ size_t thunk_offset = internal::GetGranularAlignedRandomOffset(thunk_bytes);
+
+ // Split the base and offset along page boundaries.
+ thunk_base += thunk_offset & ~(kPageSize - 1);
+ thunk_offset &= kPageSize - 1;
+
+ // Make an aligned, padded allocation, and move the pointer to our chunk.
+ size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1);
+ thunk_base = reinterpret_cast<BYTE*>(
+ ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE));
+ CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access.
+ DllInterceptionData* thunks =
+ reinterpret_cast<DllInterceptionData*>(thunk_base + thunk_offset);
+
+ DllInterceptionData dll_data;
+ dll_data.data_bytes = thunk_bytes;
+ dll_data.num_thunks = 0;
+ dll_data.used_bytes = offsetof(DllInterceptionData, thunks);
+
+ // Reset all helpers for a new child.
+ memset(g_originals, 0, sizeof(g_originals));
+
+ // this should write all the individual thunks to the child's memory
+ ResultCode rc = PatchClientFunctions(thunks, thunk_bytes, &dll_data);
+
+ if (rc != SBOX_ALL_OK)
+ return rc;
+
+ // and now write the first part of the table to the child's memory
+ SIZE_T written;
+ bool ok =
+ !!::WriteProcessMemory(child, thunks, &dll_data,
+ offsetof(DllInterceptionData, thunks), &written);
+
+ if (!ok || (offsetof(DllInterceptionData, thunks) != written))
+ return SBOX_ERROR_CANNOT_WRITE_INTERCEPTION_THUNK;
+
+ // Attempt to protect all the thunks, but ignore failure
+ DWORD old_protection;
+ ::VirtualProtectEx(child, thunks, thunk_bytes, PAGE_EXECUTE_READ,
+ &old_protection);
+
+ ResultCode ret =
+ child_->TransferVariable("g_originals", g_originals, sizeof(g_originals));
+ return ret;
+}
+
+ResultCode InterceptionManager::PatchClientFunctions(
+ DllInterceptionData* thunks,
+ size_t thunk_bytes,
+ DllInterceptionData* dll_data) {
+ DCHECK(thunks);
+ DCHECK(dll_data);
+
+ HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
+ if (!ntdll_base)
+ return SBOX_ERROR_NO_HANDLE;
+
+ char* interceptor_base = nullptr;
+
+#if defined(SANDBOX_EXPORTS)
+ interceptor_base = reinterpret_cast<char*>(child_->MainModule());
+ base::ScopedNativeLibrary local_interceptor(::LoadLibrary(child_->Name()));
+#endif // defined(SANDBOX_EXPORTS)
+
+ std::unique_ptr<ServiceResolverThunk> thunk;
+#if defined(_WIN64)
+ thunk.reset(new ServiceResolverThunk(child_->Process(), relaxed_));
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::Version::WIN10)
+ thunk.reset(new Wow64W10ResolverThunk(child_->Process(), relaxed_));
+ else if (os_info->version() >= base::win::Version::WIN8)
+ thunk.reset(new Wow64W8ResolverThunk(child_->Process(), relaxed_));
+ else
+ thunk.reset(new Wow64ResolverThunk(child_->Process(), relaxed_));
+ } else if (os_info->version() >= base::win::Version::WIN8) {
+ thunk.reset(new Win8ResolverThunk(child_->Process(), relaxed_));
+ } else {
+ thunk.reset(new ServiceResolverThunk(child_->Process(), relaxed_));
+ }
+#endif
+
+ for (auto interception : interceptions_) {
+ const std::wstring ntdll(kNtdllName);
+ if (interception.dll != ntdll)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (INTERCEPTION_SERVICE_CALL != interception.type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+#if defined(SANDBOX_EXPORTS)
+ // We may be trying to patch by function name.
+ if (!interception.interceptor_address) {
+ const char* address;
+ NTSTATUS ret = thunk->ResolveInterceptor(
+ local_interceptor.get(), interception.interceptor.c_str(),
+ reinterpret_cast<const void**>(&address));
+ if (!NT_SUCCESS(ret)) {
+ ::SetLastError(GetLastErrorFromNtStatus(ret));
+ return SBOX_ERROR_CANNOT_RESOLVE_INTERCEPTION_THUNK;
+ }
+
+ // Translate the local address to an address on the child.
+ interception.interceptor_address =
+ interceptor_base +
+ (address - reinterpret_cast<char*>(local_interceptor.get()));
+ }
+#endif // defined(SANDBOX_EXPORTS)
+ NTSTATUS ret = thunk->Setup(
+ ntdll_base, interceptor_base, interception.function.c_str(),
+ interception.interceptor.c_str(), interception.interceptor_address,
+ &thunks->thunks[dll_data->num_thunks],
+ thunk_bytes - dll_data->used_bytes, nullptr);
+ if (!NT_SUCCESS(ret)) {
+ ::SetLastError(GetLastErrorFromNtStatus(ret));
+ return SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_THUNK;
+ }
+
+ DCHECK(!g_originals[interception.id]);
+ g_originals[interception.id] = &thunks->thunks[dll_data->num_thunks];
+
+ dll_data->num_thunks++;
+ dll_data->used_bytes += sizeof(ThunkData);
+ }
+
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interception.h b/security/sandbox/chromium/sandbox/win/src/interception.h
new file mode 100644
index 0000000000..6b4612fb6c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception.h
@@ -0,0 +1,290 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines InterceptionManager, the class in charge of setting up interceptions
+// for the sandboxed process. For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_H_
+#define SANDBOX_SRC_INTERCEPTION_H_
+
+#include <stddef.h>
+
+#include <list>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+class TargetProcess;
+
+// Internal structures used for communication between the broker and the target.
+struct DllPatchInfo;
+struct DllInterceptionData;
+
+// The InterceptionManager executes on the parent application, and it is in
+// charge of setting up the desired interceptions, and placing the Interception
+// Agent into the child application.
+//
+// The exposed API consists of two methods: AddToPatchedFunctions to set up a
+// particular interception, and InitializeInterceptions to actually go ahead and
+// perform all interceptions and transfer data to the child application.
+//
+// The typical usage is something like this:
+//
+// InterceptionManager interception_manager(child);
+// if (!interception_manager.AddToPatchedFunctions(
+// L"ntdll.dll", "NtCreateFile",
+// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1))
+// return false;
+//
+// if (!interception_manager.AddToPatchedFunctions(
+// L"kernel32.dll", "CreateDirectoryW",
+// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2))
+// return false;
+//
+// sandbox::ResultCode rc = interception_manager.InitializeInterceptions();
+// if (rc != sandbox::SBOX_ALL_OK) {
+// DWORD error = ::GetLastError();
+// return rc;
+// }
+//
+// Any required syncronization must be performed outside this class. Also, it is
+// not possible to perform further interceptions after InitializeInterceptions
+// is called.
+//
+class InterceptionManager {
+ // The unit test will access private members.
+ // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
+ // do not work with sandbox tests.
+ FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1);
+ FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2);
+
+ public:
+ // An interception manager performs interceptions on a given child process.
+ // If we are allowed to intercept functions that have been patched by somebody
+ // else, relaxed should be set to true.
+ // Note: We increase the child's reference count internally.
+ InterceptionManager(TargetProcess* child_process, bool relaxed);
+ ~InterceptionManager();
+
+ // Patches function_name inside dll_name to point to replacement_code_address.
+ // function_name has to be an exported symbol of dll_name.
+ // Returns true on success.
+ //
+ // The new function should match the prototype and calling convention of the
+ // function to intercept except for one extra argument (the first one) that
+ // contains a pointer to the original function, to simplify the development
+ // of interceptors (for IA32). In x64, there is no extra argument to the
+ // interceptor, so the provided InterceptorId is used to keep a table of
+ // intercepted functions so that the interceptor can index that table to get
+ // the pointer that would have been the first argument (g_originals[id]).
+ //
+ // For example, to intercept NtClose, the following code could be used:
+ //
+ // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
+ // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose,
+ // IN HANDLE Handle) {
+ // // do something
+ // // call the original function
+ // return OriginalClose(Handle);
+ // }
+ //
+ // And in x64:
+ //
+ // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
+ // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) {
+ // // do something
+ // // call the original function
+ // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID];
+ // return OriginalClose(Handle);
+ // }
+ bool AddToPatchedFunctions(const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const void* replacement_code_address,
+ InterceptorId id);
+
+ // Patches function_name inside dll_name to point to
+ // replacement_function_name.
+ bool AddToPatchedFunctions(const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const char* replacement_function_name,
+ InterceptorId id);
+
+ // The interception agent will unload the dll with dll_name.
+ bool AddToUnloadModules(const wchar_t* dll_name);
+
+ // Initializes all interceptions on the client.
+ // Returns SBOX_ALL_OK on success, or an appropriate error code.
+ //
+ // The child process must be created suspended, and cannot be resumed until
+ // after this method returns. In addition, no action should be performed on
+ // the child that may cause it to resume momentarily, such as injecting
+ // threads or APCs.
+ //
+ // This function must be called only once, after all interceptions have been
+ // set up using AddToPatchedFunctions.
+ ResultCode InitializeInterceptions();
+
+ private:
+ // Used to store the interception information until the actual set-up.
+ struct InterceptionData {
+ InterceptionData();
+ InterceptionData(const InterceptionData& other);
+ ~InterceptionData();
+
+ InterceptionType type; // Interception type.
+ InterceptorId id; // Interceptor id.
+ std::wstring dll; // Name of dll to intercept.
+ std::string function; // Name of function to intercept.
+ std::string interceptor; // Name of interceptor function.
+ const void* interceptor_address; // Interceptor's entry point.
+ };
+
+ // Calculates the size of the required configuration buffer.
+ size_t GetBufferSize() const;
+
+ // Rounds up the size of a given buffer, considering alignment (padding).
+ // value is the current size of the buffer, and alignment is specified in
+ // bytes.
+ static inline size_t RoundUpToMultiple(size_t value, size_t alignment) {
+ return ((value + alignment - 1) / alignment) * alignment;
+ }
+
+ // Sets up a given buffer with all the information that has to be transfered
+ // to the child.
+ // Returns true on success.
+ //
+ // The buffer size should be at least the value returned by GetBufferSize
+ bool SetupConfigBuffer(void* buffer, size_t buffer_bytes);
+
+ // Fills up the part of the transfer buffer that corresponds to information
+ // about one dll to patch.
+ // data is the first recorded interception for this dll.
+ // Returns true on success.
+ //
+ // On successful return, buffer will be advanced from it's current position
+ // to the point where the next block of configuration data should be written
+ // (the actual interception info), and the current size of the buffer will
+ // decrease to account the space used by this method.
+ bool SetupDllInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes) const;
+
+ // Fills up the part of the transfer buffer that corresponds to a single
+ // function to patch.
+ // dll_info points to the dll being updated with the interception stored on
+ // data. The buffer pointer and remaining size are updated by this call.
+ // Returns true on success.
+ bool SetupInterceptionInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes,
+ DllPatchInfo* dll_info) const;
+
+ // Returns true if this interception is to be performed by the child
+ // as opposed to from the parent.
+ bool IsInterceptionPerformedByChild(const InterceptionData& data) const;
+
+ // Allocates a buffer on the child's address space (returned on
+ // remote_buffer), and fills it with the contents of a local buffer.
+ // Returns SBOX_ALL_OK on success.
+ ResultCode CopyDataToChild(const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer) const;
+
+ // Performs the cold patch (from the parent) of ntdll.
+ // Returns SBOX_ALL_OK on success.
+ //
+ // This method will insert additional interceptions to launch the interceptor
+ // agent on the child process, if there are additional interceptions to do.
+ ResultCode PatchNtdll(bool hot_patch_needed);
+
+ // Peforms the actual interceptions on ntdll.
+ // thunks is the memory to store all the thunks for this dll (on the child),
+ // and dll_data is a local buffer to hold global dll interception info.
+ // Returns SBOX_ALL_OK on success.
+ ResultCode PatchClientFunctions(DllInterceptionData* thunks,
+ size_t thunk_bytes,
+ DllInterceptionData* dll_data);
+
+ // The process to intercept.
+ TargetProcess* child_;
+ // Holds all interception info until the call to initialize (perform the
+ // actual patch).
+ std::list<InterceptionData> interceptions_;
+
+ // Keep track of patches added by name.
+ bool names_used_;
+
+ // true if we are allowed to patch already-patched functions.
+ bool relaxed_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
+};
+
+// This macro simply calls interception_manager.AddToPatchedFunctions with
+// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that
+// the interceptor is called "TargetXXX", where XXX is the name of the service.
+// Note that num_params is the number of bytes to pop out of the stack for
+// the exported interceptor, following the calling convention of a service call
+// (WINAPI = with the "C" underscore).
+#if SANDBOX_EXPORTS
+#if defined(_WIN64)
+#define MAKE_SERVICE_NAME(service, params) "Target" #service "64"
+#else
+#define MAKE_SERVICE_NAME(service, params) "_Target" #service "@" #params
+#endif
+
+#define ADD_NT_INTERCEPTION(service, id, num_params) \
+ AddToPatchedFunctions(kNtdllName, #service, \
+ sandbox::INTERCEPTION_SERVICE_CALL, \
+ MAKE_SERVICE_NAME(service, num_params), id)
+
+#define INTERCEPT_NT(manager, service, id, num_params) \
+ ((&Target##service) ? manager->ADD_NT_INTERCEPTION(service, id, num_params) \
+ : false)
+
+// When intercepting the EAT it is important that the patched version of the
+// function not call any functions imported from system libraries unless
+// |TargetServices::InitCalled()| returns true, because it is only then that
+// we are guaranteed that our IAT has been initialized.
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ ((&Target##function) ? manager->AddToPatchedFunctions( \
+ dll, #function, sandbox::INTERCEPTION_EAT, \
+ MAKE_SERVICE_NAME(function, num_params), id) \
+ : false)
+#else // SANDBOX_EXPORTS
+#if defined(_WIN64)
+#define MAKE_SERVICE_NAME(service) &Target##service##64
+#else
+#define MAKE_SERVICE_NAME(service) &Target##service
+#endif
+
+#define ADD_NT_INTERCEPTION(service, id, num_params) \
+ AddToPatchedFunctions( \
+ kNtdllName, #service, sandbox::INTERCEPTION_SERVICE_CALL, \
+ reinterpret_cast<void*>(MAKE_SERVICE_NAME(service)), id)
+
+#define INTERCEPT_NT(manager, service, id, num_params) \
+ manager->ADD_NT_INTERCEPTION(service, id, num_params)
+
+// When intercepting the EAT it is important that the patched version of the
+// function not call any functions imported from system libraries unless
+// |TargetServices::InitCalled()| returns true, because it is only then that
+// we are guaranteed that our IAT has been initialized.
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ manager->AddToPatchedFunctions( \
+ dll, #function, sandbox::INTERCEPTION_EAT, \
+ reinterpret_cast<void*>(MAKE_SERVICE_NAME(function)), id)
+#endif // SANDBOX_EXPORTS
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.cc b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
new file mode 100644
index 0000000000..e095328a71
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "sandbox/win/src/interception_agent.h"
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include "sandbox/win/src/eat_resolver.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sidestep_resolver.h"
+
+namespace {
+
+// Returns true if target lies between base and base + range.
+bool IsWithinRange(const void* base, size_t range, const void* target) {
+ const char* end = reinterpret_cast<const char*>(base) + range;
+ return reinterpret_cast<const char*>(target) < end;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// The list of intercepted functions back-pointers.
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+// Memory buffer mapped from the parent, with the list of interceptions.
+SANDBOX_INTERCEPT SharedMemory* g_interceptions = nullptr;
+
+InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
+ static InterceptionAgent* s_singleton = nullptr;
+ if (!s_singleton) {
+ if (!g_interceptions)
+ return nullptr;
+
+ size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
+ s_singleton = reinterpret_cast<InterceptionAgent*>(
+ new (NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]);
+
+ bool success = s_singleton->Init(g_interceptions);
+ if (!success) {
+ operator delete(s_singleton, NT_ALLOC);
+ s_singleton = nullptr;
+ }
+ }
+ return s_singleton;
+}
+
+bool InterceptionAgent::Init(SharedMemory* shared_memory) {
+ interceptions_ = shared_memory;
+ for (int i = 0; i < shared_memory->num_intercepted_dlls; i++)
+ dlls_[i] = nullptr;
+ return true;
+}
+
+bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info) {
+ UNICODE_STRING current_name;
+ current_name.Length =
+ static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) * sizeof(wchar_t));
+ current_name.MaximumLength = current_name.Length;
+ current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);
+
+ BOOLEAN case_insensitive = TRUE;
+ if (full_path &&
+ !g_nt.RtlCompareUnicodeString(&current_name, full_path, case_insensitive))
+ return true;
+
+ if (name &&
+ !g_nt.RtlCompareUnicodeString(&current_name, name, case_insensitive))
+ return true;
+
+ return false;
+}
+
+bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ void* base_address) {
+ DllPatchInfo* dll_info = interceptions_->dll_list;
+ int i = 0;
+ for (; i < interceptions_->num_intercepted_dlls; i++) {
+ if (DllMatch(full_path, name, dll_info))
+ break;
+
+ dll_info = reinterpret_cast<DllPatchInfo*>(
+ reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
+ }
+
+ // Return now if the dll is not in our list of interest.
+ if (i == interceptions_->num_intercepted_dlls)
+ return true;
+
+ // The dll must be unloaded.
+ if (dll_info->unload_module)
+ return false;
+
+ // Purify causes this condition to trigger.
+ if (dlls_[i])
+ return true;
+
+ size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
+ dll_info->num_functions * sizeof(ThunkData);
+ dlls_[i] = reinterpret_cast<DllInterceptionData*>(
+ new (NT_PAGE, base_address) char[buffer_bytes]);
+
+ DCHECK_NT(dlls_[i]);
+ if (!dlls_[i])
+ return true;
+
+ dlls_[i]->data_bytes = buffer_bytes;
+ dlls_[i]->num_thunks = 0;
+ dlls_[i]->base = base_address;
+ dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);
+
+ VERIFY(PatchDll(dll_info, dlls_[i]));
+
+ ULONG old_protect;
+ SIZE_T real_size = buffer_bytes;
+ void* to_protect = dlls_[i];
+ VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect,
+ &real_size, PAGE_EXECUTE_READ,
+ &old_protect));
+ return true;
+}
+
+void InterceptionAgent::OnDllUnload(void* base_address) {
+ for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) {
+ if (dlls_[i] && dlls_[i]->base == base_address) {
+ operator delete(dlls_[i], NT_PAGE);
+ dlls_[i] = nullptr;
+ break;
+ }
+ }
+}
+
+// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
+// the timestamp of the patched dll, or modify the info on the prebinded dll.
+// the first approach messes matching of debug symbols, the second one is more
+// complicated.
+bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
+ DllInterceptionData* thunks) {
+ DCHECK_NT(thunks);
+ DCHECK_NT(dll_info);
+
+ const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);
+
+ for (int i = 0; i < dll_info->num_functions; i++) {
+ if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ ResolverThunk* resolver = GetResolver(function->type);
+ if (!resolver)
+ return false;
+
+ const char* interceptor =
+ function->function + g_nt.strlen(function->function) + 1;
+
+ if (!IsWithinRange(function, function->record_bytes, interceptor) ||
+ !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ NTSTATUS ret = resolver->Setup(
+ thunks->base, interceptions_->interceptor_base, function->function,
+ interceptor, function->interceptor_address, &thunks->thunks[i],
+ sizeof(ThunkData), nullptr);
+ if (!NT_SUCCESS(ret)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ DCHECK_NT(!g_originals[function->id] ||
+ g_originals[function->id] == &thunks->thunks[i]);
+ g_originals[function->id] = &thunks->thunks[i];
+
+ thunks->num_thunks++;
+ thunks->used_bytes += sizeof(ThunkData);
+
+ function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(function) + function->record_bytes);
+ }
+
+ return true;
+}
+
+// This method is called from within the loader lock
+ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
+ static EatResolverThunk* eat_resolver = nullptr;
+ static SidestepResolverThunk* sidestep_resolver = nullptr;
+ static SmartSidestepResolverThunk* smart_sidestep_resolver = nullptr;
+
+ if (!eat_resolver)
+ eat_resolver = new (NT_ALLOC) EatResolverThunk;
+
+#if !defined(_WIN64)
+ // Sidestep is not supported for x64.
+ if (!sidestep_resolver)
+ sidestep_resolver = new (NT_ALLOC) SidestepResolverThunk;
+
+ if (!smart_sidestep_resolver)
+ smart_sidestep_resolver = new (NT_ALLOC) SmartSidestepResolverThunk;
+#endif
+
+ switch (type) {
+ case INTERCEPTION_EAT:
+ return eat_resolver;
+ case INTERCEPTION_SIDESTEP:
+ return sidestep_resolver;
+ case INTERCEPTION_SMART_SIDESTEP:
+ return smart_sidestep_resolver;
+ default:
+ NOTREACHED_NT();
+ }
+
+ return nullptr;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.h b/security/sandbox/chromium/sandbox/win/src/interception_agent.h
new file mode 100644
index 0000000000..b2bce08b09
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines InterceptionAgent, the class in charge of setting up interceptions
+// from the inside of the sandboxed process. For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_AGENT_H__
+#define SANDBOX_SRC_INTERCEPTION_AGENT_H__
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Internal structures used for communication between the broker and the target.
+struct DllInterceptionData;
+struct SharedMemory;
+struct DllPatchInfo;
+
+class ResolverThunk;
+
+// The InterceptionAgent executes on the target application, and it is in charge
+// of setting up the desired interceptions or indicating what module needs to
+// be unloaded.
+//
+// The exposed API consists of three methods: GetInterceptionAgent to retrieve
+// the single class instance, OnDllLoad and OnDllUnload to process a dll being
+// loaded and unloaded respectively.
+//
+// This class assumes that it will get called for every dll being loaded,
+// starting with kernel32, so the singleton will be instantiated from within the
+// loader lock.
+class InterceptionAgent {
+ public:
+ // Returns the single InterceptionAgent object for this process.
+ static InterceptionAgent* GetInterceptionAgent();
+
+ // This method should be invoked whenever a new dll is loaded to perform the
+ // required patches. If the return value is false, this dll should not be
+ // allowed to load.
+ //
+ // full_path is the (optional) full name of the module being loaded and name
+ // is the internal module name. If full_path is provided, it will be used
+ // before the internal name to determine if we care about this dll.
+ bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+ void* base_address);
+
+ // Performs cleanup when a dll is unloaded.
+ void OnDllUnload(void* base_address);
+
+ private:
+ ~InterceptionAgent() {}
+
+ // Performs initialization of the singleton.
+ bool Init(SharedMemory* shared_memory);
+
+ // Returns true if we are interested on this dll. dll_info is an entry of the
+ // list of intercepted dlls.
+ bool DllMatch(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info);
+
+ // Performs the patching of the dll loaded at base_address.
+ // The patches to perform are described on dll_info, and thunks is the thunk
+ // storage for the whole dll.
+ // Returns true on success.
+ bool PatchDll(const DllPatchInfo* dll_info, DllInterceptionData* thunks);
+
+ // Returns a resolver for a given interception type.
+ ResolverThunk* GetResolver(InterceptionType type);
+
+ // Shared memory containing the list of functions to intercept.
+ SharedMemory* interceptions_;
+
+ // Array of thunk data buffers for the intercepted dlls. This object singleton
+ // is allocated with a placement new with enough space to hold the complete
+ // array of pointers, not just the first element.
+ DllInterceptionData* dlls_[1];
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptionAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_AGENT_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_internal.h b/security/sandbox/chromium/sandbox/win/src/interception_internal.h
new file mode 100644
index 0000000000..bf452e049e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_internal.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Defines InterceptionManager, the class in charge of setting up interceptions
+// for the sandboxed process. For more details see:
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
+#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
+
+#include <stddef.h>
+
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+const int kMaxThunkDataBytes = 64;
+
+// The following structures contain variable size fields at the end, and will be
+// used to transfer information between two processes. In order to guarantee
+// our ability to follow the chain of structures, the alignment should be fixed,
+// hence this pragma.
+#pragma pack(push, 4)
+
+// Structures for the shared memory that contains patching information
+// for the InterceptionAgent.
+// A single interception:
+struct FunctionInfo {
+ size_t record_bytes; // rounded to sizeof(size_t) bytes
+ InterceptionType type;
+ InterceptorId id;
+ const void* interceptor_address;
+ char function[1]; // placeholder for null terminated name
+ // char interceptor[] // followed by the interceptor function
+};
+
+// A single dll:
+struct DllPatchInfo {
+ size_t record_bytes; // rounded to sizeof(size_t) bytes
+ size_t offset_to_functions;
+ int num_functions;
+ bool unload_module;
+ wchar_t dll_name[1]; // placeholder for null terminated name
+ // FunctionInfo function_info[] // followed by the functions to intercept
+};
+
+// All interceptions:
+struct SharedMemory {
+ int num_intercepted_dlls;
+ void* interceptor_base;
+ DllPatchInfo dll_list[1]; // placeholder for the list of dlls
+};
+
+// Dummy single thunk:
+struct ThunkData {
+ char data[kMaxThunkDataBytes];
+};
+
+// In-memory representation of the interceptions for a given dll:
+struct DllInterceptionData {
+ size_t data_bytes;
+ size_t used_bytes;
+ void* base;
+ int num_thunks;
+#if defined(_WIN64)
+ int dummy; // Improve alignment.
+#endif
+ ThunkData thunks[1];
+};
+
+#pragma pack(pop)
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc b/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc
new file mode 100644
index 0000000000..0120682bcb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for InterceptionManager.
+// The tests require private information so the whole interception.cc file is
+// included from this file.
+
+#include "sandbox/win/src/interception.h"
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+
+#include "base/bits.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:
+// buffer (in): the buffer to walk.
+// size (in): buffer size
+// num_dlls (out): count of the dlls on the buffer.
+// num_function (out): count of intercepted functions.
+// num_names (out): count of named interceptor functions.
+void WalkBuffer(void* buffer,
+ size_t size,
+ int* num_dlls,
+ int* num_functions,
+ int* num_names) {
+ ASSERT_TRUE(buffer);
+ ASSERT_TRUE(num_functions);
+ ASSERT_TRUE(num_names);
+ *num_dlls = *num_functions = *num_names = 0;
+ SharedMemory* memory = reinterpret_cast<SharedMemory*>(buffer);
+
+ ASSERT_GT(size, sizeof(SharedMemory));
+ DllPatchInfo* dll = &memory->dll_list[0];
+
+ for (int i = 0; i < memory->num_intercepted_dlls; i++) {
+ ASSERT_NE(0u, wcslen(dll->dll_name));
+ ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t));
+ ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t));
+ ASSERT_NE(0, dll->num_functions);
+
+ FunctionInfo* function = reinterpret_cast<FunctionInfo*>(
+ reinterpret_cast<char*>(dll) + dll->offset_to_functions);
+
+ for (int j = 0; j < dll->num_functions; j++) {
+ ASSERT_EQ(0u, function->record_bytes % sizeof(size_t));
+
+ char* name = function->function;
+ size_t length = strlen(name);
+ ASSERT_NE(0u, length);
+ name += length + 1;
+
+ // look for overflows
+ ASSERT_GT(reinterpret_cast<char*>(buffer) + size, name + strlen(name));
+
+ // look for a named interceptor
+ if (strlen(name)) {
+ (*num_names)++;
+ EXPECT_TRUE(!function->interceptor_address);
+ } else {
+ EXPECT_TRUE(function->interceptor_address);
+ }
+
+ (*num_functions)++;
+ function = reinterpret_cast<FunctionInfo*>(
+ reinterpret_cast<char*>(function) + function->record_bytes);
+ }
+
+ (*num_dlls)++;
+ dll = reinterpret_cast<DllPatchInfo*>(reinterpret_cast<char*>(dll) +
+ dll->record_bytes);
+ }
+}
+
+TEST(InterceptionManagerTest, GetGranularAlignedRandomOffset) {
+ std::set<size_t> 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<size_t>(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(nullptr, exe_name, MAX_PATH - 1));
+
+ TargetProcess* target =
+ MakeTestTargetProcess(::GetCurrentProcess(), ::GetModuleHandle(exe_name));
+
+ InterceptionManager interceptions(target, true);
+
+ // Any pointer will do for a function pointer.
+ void* function = &interceptions;
+
+ // We don't care about the interceptor id.
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
+ INTERCEPTION_SMART_SIDESTEP, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "FindWindow",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateMutex",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+ INTERCEPTION_EAT, "replacement",
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtOpenFile",
+ INTERCEPTION_SIDESTEP, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"some.dll", "Superfn", INTERCEPTION_EAT,
+ function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, "a", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_SIDESTEP, "ab", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, "abc", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "p", INTERCEPTION_EAT, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"b.dll",
+ "TheIncredibleCallToSaveTheWorld",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame", INTERCEPTION_EAT,
+ function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "ARules", INTERCEPTION_EAT,
+ function, OPEN_KEY_ID);
+
+ // Verify that all interceptions were added
+ ASSERT_EQ(18u, interceptions.interceptions_.size());
+
+ size_t buffer_size = interceptions.GetBufferSize();
+ std::unique_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]);
+
+ ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), buffer_size));
+
+ // At this point, the interceptions should have been separated into two
+ // groups: one group with the local ("cold") interceptions, consisting of
+ // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and
+ // another group with the interceptions belonging to dlls that will be "hot"
+ // patched on the client. The second group lives on local_buffer, and the
+ // first group remains on the list of interceptions (inside the object
+ // "interceptions"). There are 3 local interceptions (of ntdll); the
+ // other 15 have to be sent to the child to be performed "hot".
+ EXPECT_EQ(3u, interceptions.interceptions_.size());
+
+ int num_dlls, num_functions, num_names;
+ WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions,
+ &num_names);
+
+ // The 15 interceptions on the buffer (to the child) should be grouped on 6
+ // dlls. Only four interceptions are using an explicit name for the
+ // interceptor function.
+ EXPECT_EQ(6, num_dlls);
+ EXPECT_EQ(15, num_functions);
+ EXPECT_EQ(4, num_names);
+}
+
+TEST(InterceptionManagerTest, BufferLayout2) {
+ wchar_t exe_name[MAX_PATH];
+ ASSERT_NE(0u, GetModuleFileName(nullptr, exe_name, MAX_PATH - 1));
+
+ TargetProcess* target =
+ MakeTestTargetProcess(::GetCurrentProcess(), ::GetModuleHandle(exe_name));
+
+ InterceptionManager interceptions(target, true);
+
+ // Any pointer will do for a function pointer.
+ void* function = &interceptions;
+ interceptions.AddToUnloadModules(L"some01.dll");
+ // We don't care about the interceptor id.
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_FILE_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx",
+ INTERCEPTION_EAT, function, OPEN_FILE_ID);
+ interceptions.AddToUnloadModules(L"some02.dll");
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
+ INTERCEPTION_SMART_SIDESTEP, function,
+ OPEN_FILE_ID);
+ // Verify that all interceptions were added
+ ASSERT_EQ(5u, interceptions.interceptions_.size());
+
+ size_t buffer_size = interceptions.GetBufferSize();
+ std::unique_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]);
+
+ ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), buffer_size));
+
+ // At this point, the interceptions should have been separated into two
+ // groups: one group with the local ("cold") interceptions, and another
+ // group with the interceptions belonging to dlls that will be "hot"
+ // patched on the client. The second group lives on local_buffer, and the
+ // first group remains on the list of interceptions, in this case just one.
+ EXPECT_EQ(1u, interceptions.interceptions_.size());
+
+ int num_dlls, num_functions, num_names;
+ WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions,
+ &num_names);
+
+ EXPECT_EQ(3, num_dlls);
+ EXPECT_EQ(4, num_functions);
+ EXPECT_EQ(0, num_names);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interceptors.h b/security/sandbox/chromium/sandbox/win/src/interceptors.h
new file mode 100644
index 0000000000..5788614707
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interceptors.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_INTERCEPTORS_H_
+#define SANDBOX_SRC_INTERCEPTORS_H_
+
+#if defined(_WIN64)
+#include "sandbox/win/src/interceptors_64.h"
+#endif
+
+namespace sandbox {
+
+enum InterceptorId {
+ // Internal use:
+ MAP_VIEW_OF_SECTION_ID = 0,
+ UNMAP_VIEW_OF_SECTION_ID,
+ // Policy broker:
+ SET_INFORMATION_THREAD_ID,
+ OPEN_THREAD_TOKEN_ID,
+ OPEN_THREAD_TOKEN_EX_ID,
+ OPEN_THREAD_ID,
+ OPEN_PROCESS_ID,
+ OPEN_PROCESS_TOKEN_ID,
+ OPEN_PROCESS_TOKEN_EX_ID,
+ // Filesystem dispatcher:
+ CREATE_FILE_ID,
+ OPEN_FILE_ID,
+ QUERY_ATTRIB_FILE_ID,
+ QUERY_FULL_ATTRIB_FILE_ID,
+ SET_INFO_FILE_ID,
+ // Named pipe dispatcher:
+ CREATE_NAMED_PIPE_ID,
+ // Process-thread dispatcher:
+ CREATE_PROCESSW_ID,
+ CREATE_PROCESSA_ID,
+ CREATE_THREAD_ID,
+ // Registry dispatcher:
+ CREATE_KEY_ID,
+ OPEN_KEY_ID,
+ OPEN_KEY_EX_ID,
+ // Sync dispatcher:
+ CREATE_EVENT_ID,
+ OPEN_EVENT_ID,
+ // Process mitigations Win32k dispatcher:
+ GDIINITIALIZE_ID,
+ GETSTOCKOBJECT_ID,
+ REGISTERCLASSW_ID,
+ ENUMDISPLAYMONITORS_ID,
+ ENUMDISPLAYDEVICESA_ID,
+ GETMONITORINFOA_ID,
+ GETMONITORINFOW_ID,
+ CREATEOPMPROTECTEDOUTPUTS_ID,
+ GETCERTIFICATE_ID,
+ GETCERTIFICATESIZE_ID,
+ GETCERTIFICATEBYHANDLE_ID,
+ GETCERTIFICATESIZEBYHANDLE_ID,
+ DESTROYOPMPROTECTEDOUTPUT_ID,
+ CONFIGUREOPMPROTECTEDOUTPUT_ID,
+ GETOPMINFORMATION_ID,
+ GETOPMRANDOMNUMBER_ID,
+ GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_ID,
+ SETOPMSIGNINGKEYANDSEQUENCENUMBERS_ID,
+ // Signed dispatcher:
+ CREATE_SECTION_ID,
+ INTERCEPTOR_MAX_ID
+};
+
+typedef void* OriginalFunctions[INTERCEPTOR_MAX_ID];
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTORS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/interceptors_64.cc b/security/sandbox/chromium/sandbox/win/src/interceptors_64.cc
new file mode 100644
index 0000000000..0ac2671fcc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interceptors_64.cc
@@ -0,0 +1,531 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/interceptors_64.h"
+
+#include "sandbox/win/src/filesystem_interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/named_pipe_interception.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/registry_interception.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/signed_interception.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/target_interceptions.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+NTSTATUS WINAPI TargetNtMapViewOfSection64(HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect) {
+ NtMapViewOfSectionFunction orig_fn =
+ reinterpret_cast<NtMapViewOfSectionFunction>(
+ g_originals[MAP_VIEW_OF_SECTION_ID]);
+
+ return TargetNtMapViewOfSection(orig_fn, section, process, base, zero_bits,
+ commit_size, offset, view_size, inherit,
+ allocation_type, protect);
+}
+
+NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, PVOID base) {
+ NtUnmapViewOfSectionFunction orig_fn =
+ reinterpret_cast<NtUnmapViewOfSectionFunction>(
+ g_originals[UNMAP_VIEW_OF_SECTION_ID]);
+ return TargetNtUnmapViewOfSection(orig_fn, process, base);
+}
+
+// -----------------------------------------------------------------------
+
+NTSTATUS WINAPI
+TargetNtSetInformationThread64(HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes) {
+ NtSetInformationThreadFunction orig_fn =
+ reinterpret_cast<NtSetInformationThreadFunction>(
+ g_originals[SET_INFORMATION_THREAD_ID]);
+ return TargetNtSetInformationThread(orig_fn, thread, thread_info_class,
+ thread_information,
+ thread_information_bytes);
+}
+
+NTSTATUS WINAPI TargetNtOpenThreadToken64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ PHANDLE token) {
+ NtOpenThreadTokenFunction orig_fn =
+ reinterpret_cast<NtOpenThreadTokenFunction>(
+ g_originals[OPEN_THREAD_TOKEN_ID]);
+ return TargetNtOpenThreadToken(orig_fn, thread, desired_access, open_as_self,
+ token);
+}
+
+NTSTATUS WINAPI TargetNtOpenThreadTokenEx64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NtOpenThreadTokenExFunction orig_fn =
+ reinterpret_cast<NtOpenThreadTokenExFunction>(
+ g_originals[OPEN_THREAD_TOKEN_EX_ID]);
+ return TargetNtOpenThreadTokenEx(orig_fn, thread, desired_access,
+ open_as_self, handle_attributes, token);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length) {
+ NtCreateFileFunction orig_fn =
+ reinterpret_cast<NtCreateFileFunction>(g_originals[CREATE_FILE_ID]);
+ return TargetNtCreateFile(orig_fn, file, desired_access, object_attributes,
+ io_status, allocation_size, file_attributes,
+ sharing, disposition, options, ea_buffer,
+ ea_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options) {
+ NtOpenFileFunction orig_fn =
+ reinterpret_cast<NtOpenFileFunction>(g_originals[OPEN_FILE_ID]);
+ return TargetNtOpenFile(orig_fn, file, desired_access, object_attributes,
+ io_status, sharing, options);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtQueryAttributesFile64(POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ NtQueryAttributesFileFunction orig_fn =
+ reinterpret_cast<NtQueryAttributesFileFunction>(
+ g_originals[QUERY_ATTRIB_FILE_ID]);
+ return TargetNtQueryAttributesFile(orig_fn, object_attributes,
+ file_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ NtQueryFullAttributesFileFunction orig_fn =
+ reinterpret_cast<NtQueryFullAttributesFileFunction>(
+ g_originals[QUERY_FULL_ATTRIB_FILE_ID]);
+ return TargetNtQueryFullAttributesFile(orig_fn, object_attributes,
+ file_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationFile64(HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_information,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_information_class) {
+ NtSetInformationFileFunction orig_fn =
+ reinterpret_cast<NtSetInformationFileFunction>(
+ g_originals[SET_INFO_FILE_ID]);
+ return TargetNtSetInformationFile(orig_fn, file, io_status, file_information,
+ length, file_information_class);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateNamedPipeW64(LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ CreateNamedPipeWFunction orig_fn = reinterpret_cast<CreateNamedPipeWFunction>(
+ g_originals[CREATE_NAMED_PIPE_ID]);
+ return TargetCreateNamedPipeW(orig_fn, pipe_name, open_mode, pipe_mode,
+ max_instance, out_buffer_size, in_buffer_size,
+ default_timeout, security_attributes);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThread64(PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NtOpenThreadFunction orig_fn =
+ reinterpret_cast<NtOpenThreadFunction>(g_originals[OPEN_THREAD_ID]);
+ return TargetNtOpenThread(orig_fn, thread, desired_access, object_attributes,
+ client_id);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcess64(PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NtOpenProcessFunction orig_fn =
+ reinterpret_cast<NtOpenProcessFunction>(g_originals[OPEN_PROCESS_ID]);
+ return TargetNtOpenProcess(orig_fn, process, desired_access,
+ object_attributes, client_id);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessToken64(HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token) {
+ NtOpenProcessTokenFunction orig_fn =
+ reinterpret_cast<NtOpenProcessTokenFunction>(
+ g_originals[OPEN_PROCESS_TOKEN_ID]);
+ return TargetNtOpenProcessToken(orig_fn, process, desired_access, token);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx64(HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NtOpenProcessTokenExFunction orig_fn =
+ reinterpret_cast<NtOpenProcessTokenExFunction>(
+ g_originals[OPEN_PROCESS_TOKEN_EX_ID]);
+ return TargetNtOpenProcessTokenEx(orig_fn, process, desired_access,
+ handle_attributes, token);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessW64(LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ CreateProcessWFunction orig_fn =
+ reinterpret_cast<CreateProcessWFunction>(g_originals[CREATE_PROCESSW_ID]);
+ return TargetCreateProcessW(
+ orig_fn, application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags, environment, current_directory,
+ startup_info, process_information);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessA64(LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ CreateProcessAFunction orig_fn =
+ reinterpret_cast<CreateProcessAFunction>(g_originals[CREATE_PROCESSA_ID]);
+ return TargetCreateProcessA(
+ orig_fn, application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags, environment, current_directory,
+ startup_info, process_information);
+}
+
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateThread64(LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ PVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id) {
+ CreateThreadFunction orig_fn =
+ reinterpret_cast<CreateThreadFunction>(g_originals[CREATE_THREAD_ID]);
+ return TargetCreateThread(orig_fn, thread_attributes, stack_size,
+ start_address, parameter, creation_flags,
+ thread_id);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+ PUNICODE_STRING class_name,
+ ULONG create_options,
+ PULONG disposition) {
+ NtCreateKeyFunction orig_fn =
+ reinterpret_cast<NtCreateKeyFunction>(g_originals[CREATE_KEY_ID]);
+ return TargetNtCreateKey(orig_fn, key, desired_access, object_attributes,
+ title_index, class_name, create_options,
+ disposition);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NtOpenKeyFunction orig_fn =
+ reinterpret_cast<NtOpenKeyFunction>(g_originals[OPEN_KEY_ID]);
+ return TargetNtOpenKey(orig_fn, key, desired_access, object_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKeyEx64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options) {
+ NtOpenKeyExFunction orig_fn =
+ reinterpret_cast<NtOpenKeyExFunction>(g_originals[OPEN_KEY_EX_ID]);
+ return TargetNtOpenKeyEx(orig_fn, key, desired_access, object_attributes,
+ open_options);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NtCreateEventFunction orig_fn =
+ reinterpret_cast<NtCreateEventFunction>(g_originals[CREATE_EVENT_ID]);
+ return TargetNtCreateEvent(orig_fn, event_handle, desired_access,
+ object_attributes, event_type, initial_state);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NtOpenEventFunction orig_fn =
+ reinterpret_cast<NtOpenEventFunction>(g_originals[OPEN_EVENT_ID]);
+ return TargetNtOpenEvent(orig_fn, event_handle, desired_access,
+ object_attributes);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(HANDLE dll,
+ DWORD reason) {
+ GdiDllInitializeFunction orig_fn =
+ reinterpret_cast<GdiDllInitializeFunction>(g_originals[GDIINITIALIZE_ID]);
+ return TargetGdiDllInitialize(orig_fn, dll, reason);
+}
+
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object) {
+ GetStockObjectFunction orig_fn =
+ reinterpret_cast<GetStockObjectFunction>(g_originals[GETSTOCKOBJECT_ID]);
+ return TargetGetStockObject(orig_fn, object);
+}
+
+SANDBOX_INTERCEPT ATOM WINAPI
+TargetRegisterClassW64(const WNDCLASS* wnd_class) {
+ RegisterClassWFunction orig_fn =
+ reinterpret_cast<RegisterClassWFunction>(g_originals[REGISTERCLASSW_ID]);
+ return TargetRegisterClassW(orig_fn, wnd_class);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayMonitors64(HDC hdc,
+ LPCRECT clip_rect,
+ MONITORENUMPROC enum_function,
+ LPARAM data_pointer) {
+ EnumDisplayMonitorsFunction orig_fn =
+ reinterpret_cast<EnumDisplayMonitorsFunction>(
+ g_originals[ENUMDISPLAYMONITORS_ID]);
+ return TargetEnumDisplayMonitors(orig_fn, hdc, clip_rect, enum_function,
+ data_pointer);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayDevicesA64(LPCSTR device,
+ DWORD device_num,
+ PDISPLAY_DEVICEA display_device,
+ DWORD flags) {
+ EnumDisplayDevicesAFunction orig_fn =
+ reinterpret_cast<EnumDisplayDevicesAFunction>(
+ g_originals[ENUMDISPLAYDEVICESA_ID]);
+ return TargetEnumDisplayDevicesA(orig_fn, device, device_num, display_device,
+ flags);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoA64(HMONITOR monitor, LPMONITORINFO monitor_info) {
+ GetMonitorInfoAFunction orig_fn = reinterpret_cast<GetMonitorInfoAFunction>(
+ g_originals[GETMONITORINFOA_ID]);
+ return TargetGetMonitorInfoA(orig_fn, monitor, monitor_info);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoW64(HMONITOR monitor, LPMONITORINFO monitor_info) {
+ GetMonitorInfoWFunction orig_fn = reinterpret_cast<GetMonitorInfoWFunction>(
+ g_originals[GETMONITORINFOW_ID]);
+ return TargetGetMonitorInfoW(orig_fn, monitor, monitor_info);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetSuggestedOPMProtectedOutputArraySize64(
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size) {
+ GetSuggestedOPMProtectedOutputArraySizeFunction orig_fn =
+ reinterpret_cast<GetSuggestedOPMProtectedOutputArraySizeFunction>(
+ g_originals[GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_ID]);
+ return TargetGetSuggestedOPMProtectedOutputArraySize(
+ orig_fn, device_name, suggested_output_array_size);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetCreateOPMProtectedOutputs64(
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD protected_output_array_size,
+ DWORD* num_output_handles,
+ OPM_PROTECTED_OUTPUT_HANDLE* protected_output_array) {
+ CreateOPMProtectedOutputsFunction orig_fn =
+ reinterpret_cast<CreateOPMProtectedOutputsFunction>(
+ g_originals[CREATEOPMPROTECTEDOUTPUTS_ID]);
+ return TargetCreateOPMProtectedOutputs(
+ orig_fn, device_name, vos, protected_output_array_size,
+ num_output_handles, protected_output_array);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificate64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length) {
+ GetCertificateFunction orig_fn =
+ reinterpret_cast<GetCertificateFunction>(g_originals[GETCERTIFICATE_ID]);
+ return TargetGetCertificate(orig_fn, device_name, certificate_type,
+ certificate, certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSize64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ GetCertificateSizeFunction orig_fn =
+ reinterpret_cast<GetCertificateSizeFunction>(
+ g_originals[GETCERTIFICATESIZE_ID]);
+ return TargetGetCertificateSize(orig_fn, device_name, certificate_type,
+ certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length) {
+ GetCertificateByHandleFunction orig_fn =
+ reinterpret_cast<GetCertificateByHandleFunction>(
+ g_originals[GETCERTIFICATE_ID]);
+ return TargetGetCertificateByHandle(orig_fn, protected_output,
+ certificate_type, certificate,
+ certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSizeByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ GetCertificateSizeByHandleFunction orig_fn =
+ reinterpret_cast<GetCertificateSizeByHandleFunction>(
+ g_originals[GETCERTIFICATESIZE_ID]);
+ return TargetGetCertificateSizeByHandle(orig_fn, protected_output,
+ certificate_type, certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetDestroyOPMProtectedOutput64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output) {
+ DestroyOPMProtectedOutputFunction orig_fn =
+ reinterpret_cast<DestroyOPMProtectedOutputFunction>(
+ g_originals[DESTROYOPMPROTECTEDOUTPUT_ID]);
+ return TargetDestroyOPMProtectedOutput(orig_fn, protected_output);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMInformation64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_info) {
+ GetOPMInformationFunction orig_fn =
+ reinterpret_cast<GetOPMInformationFunction>(
+ g_originals[GETOPMINFORMATION_ID]);
+ return TargetGetOPMInformation(orig_fn, protected_output, parameters,
+ requested_info);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMRandomNumber64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number) {
+ GetOPMRandomNumberFunction orig_fn =
+ reinterpret_cast<GetOPMRandomNumberFunction>(
+ g_originals[GETOPMRANDOMNUMBER_ID]);
+ return TargetGetOPMRandomNumber(orig_fn, protected_output, random_number);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters) {
+ SetOPMSigningKeyAndSequenceNumbersFunction orig_fn =
+ reinterpret_cast<SetOPMSigningKeyAndSequenceNumbersFunction>(
+ g_originals[SETOPMSIGNINGKEYANDSEQUENCENUMBERS_ID]);
+ return TargetSetOPMSigningKeyAndSequenceNumbers(orig_fn, protected_output,
+ parameters);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetConfigureOPMProtectedOutput64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters) {
+ ConfigureOPMProtectedOutputFunction orig_fn =
+ reinterpret_cast<ConfigureOPMProtectedOutputFunction>(
+ g_originals[CONFIGUREOPMPROTECTEDOUTPUT_ID]);
+ return TargetConfigureOPMProtectedOutput(
+ orig_fn, protected_output, parameters, additional_parameters_size,
+ additional_parameters);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateSection64(PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle) {
+ NtCreateSectionFunction orig_fn =
+ reinterpret_cast<NtCreateSectionFunction>(g_originals[CREATE_SECTION_ID]);
+ return TargetNtCreateSection(
+ orig_fn, section_handle, desired_access, object_attributes, maximum_size,
+ section_page_protection, allocation_attributes, file_handle);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interceptors_64.h b/security/sandbox/chromium/sandbox/win/src/interceptors_64.h
new file mode 100644
index 0000000000..f34627dcd2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interceptors_64.h
@@ -0,0 +1,330 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_INTERCEPTORS_64_H_
+#define SANDBOX_WIN_SRC_INTERCEPTORS_64_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtMapViewOfSection64(HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect);
+
+// Interception of NtUnmapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being unloaded, so we can clean up our interceptions.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process,
+ PVOID base);
+
+// -----------------------------------------------------------------------
+// Interceptors without IPC.
+
+// Interception of NtSetInformationThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationThread64(HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes);
+
+// Interception of NtOpenThreadToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThreadToken64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ PHANDLE token);
+
+// Interception of NtOpenThreadTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThreadTokenEx64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ ULONG handle_attributes,
+ PHANDLE token);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the file system dispatcher.
+
+// Interception of NtCreateFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length);
+
+// Interception of NtOpenFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options);
+
+// Interception of NtQueryAtttributesFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtQueryAttributesFile64(POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes);
+
+// Interception of NtQueryFullAtttributesFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes);
+
+// Interception of NtSetInformationFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationFile64(HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_information,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_information_class);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the named pipe dispatcher.
+
+// Interception of CreateNamedPipeW in kernel32.dll
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateNamedPipeW64(LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the process-thread dispatcher.
+
+// Interception of NtOpenThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThread64(PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcess on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcess64(PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcessToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessToken64(HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token);
+
+// Interception of NtOpenProcessTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx64(HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token);
+
+// Interception of CreateProcessW in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessW64(LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateProcessA in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessA64(LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateThread in kernel32.dll.
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateThread64(LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ PVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the registry dispatcher.
+
+// Interception of NtCreateKey on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+ PUNICODE_STRING class_name,
+ ULONG create_options,
+ PULONG disposition);
+
+// Interception of NtOpenKey on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// Interception of NtOpenKeyEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKeyEx64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the sync dispatcher.
+
+// Interception of NtCreateEvent/NtOpenEvent on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the process mitigations win32k lockdown code.
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW64(const WNDCLASS* wnd_class);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayMonitors64(HDC hdc,
+ LPCRECT lprcClip,
+ MONITORENUMPROC lpfnEnum,
+ LPARAM dwData);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayDevicesA64(LPCSTR lpDevice,
+ DWORD iDevNum,
+ PDISPLAY_DEVICEA lpDisplayDevice,
+ DWORD dwFlags);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGetMonitorInfoA64(HMONITOR hMonitor,
+ LPMONITORINFO lpmi);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGetMonitorInfoW64(HMONITOR hMonitor,
+ LPMONITORINFO lpmi);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetSuggestedOPMProtectedOutputArraySize64(
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetCreateOPMProtectedOutputs64(
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD protected_output_array_size,
+ DWORD* num_output_handles,
+ OPM_PROTECTED_OUTPUT_HANDLE* protected_output_array);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificate64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSize64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSizeByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetDestroyOPMProtectedOutput64(OPM_PROTECTED_OUTPUT_HANDLE protected_output);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMInformation64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_info);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMRandomNumber64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetConfigureOPMProtectedOutput64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the signed process code.
+
+// Interception of NtCreateSection on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateSection64(PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_INTERCEPTORS_64_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/internal_types.h b/security/sandbox/chromium/sandbox/win/src/internal_types.h
new file mode 100644
index 0000000000..9971da81d0
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/internal_types.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
+#define SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
+
+#include <stdint.h>
+
+namespace sandbox {
+
+const wchar_t kNtdllName[] = L"ntdll.dll";
+const wchar_t kKerneldllName[] = L"kernel32.dll";
+const wchar_t kKernelBasedllName[] = L"kernelbase.dll";
+
+// Defines the supported C++ types encoding to numeric id. Like a simplified
+// RTTI. Note that true C++ RTTI will not work because the types are not
+// polymorphic anyway.
+enum ArgType {
+ INVALID_TYPE = 0,
+ WCHAR_TYPE,
+ UINT32_TYPE,
+ UNISTR_TYPE,
+ VOIDPTR_TYPE,
+ INPTR_TYPE,
+ INOUTPTR_TYPE,
+ LAST_TYPE
+};
+
+// Encapsulates a pointer to a buffer and the size of the buffer.
+class CountedBuffer {
+ public:
+ CountedBuffer(void* buffer, uint32_t size) : size_(size), buffer_(buffer) {}
+
+ uint32_t Size() const { return size_; }
+
+ void* Buffer() const { return buffer_; }
+
+ private:
+ uint32_t size_;
+ void* buffer_;
+};
+
+// Helper class to convert void-pointer packed ints for both
+// 32 and 64 bit builds. This construct is non-portable.
+class IPCInt {
+ public:
+ explicit IPCInt(void* buffer) { buffer_.vp = buffer; }
+
+ explicit IPCInt(uint32_t i32) {
+ buffer_.vp = nullptr;
+ buffer_.i32 = i32;
+ }
+
+ uint32_t As32Bit() const { return buffer_.i32; }
+
+ void* AsVoidPtr() const { return buffer_.vp; }
+
+ private:
+ union U {
+ void* vp;
+ uint32_t i32;
+ } buffer_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_args.cc b/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
new file mode 100644
index 0000000000..c0c64f1864
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
@@ -0,0 +1,96 @@
+// Copyright 2017 The Chromium Authors. All 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/ipc_args.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Releases memory allocated for IPC arguments, if needed.
+void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) {
+ for (size_t i = 0; i < kMaxIpcParams; i++) {
+ switch (ipc_params->args[i]) {
+ case WCHAR_TYPE: {
+ delete reinterpret_cast<std::wstring*>(args[i]);
+ args[i] = nullptr;
+ break;
+ }
+ case INPTR_TYPE:
+ case INOUTPTR_TYPE: {
+ delete reinterpret_cast<CountedBuffer*>(args[i]);
+ args[i] = nullptr;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+// Fills up the list of arguments (args and ipc_params) for an IPC call.
+bool GetArgs(CrossCallParamsEx* params,
+ IPCParams* ipc_params,
+ void* args[kMaxIpcParams]) {
+ if (kMaxIpcParams < params->GetParamsCount())
+ return false;
+
+ for (uint32_t i = 0; i < params->GetParamsCount(); i++) {
+ uint32_t size;
+ ArgType type;
+ args[i] = params->GetRawParameter(i, &size, &type);
+ if (args[i]) {
+ ipc_params->args[i] = type;
+ switch (type) {
+ case WCHAR_TYPE: {
+ std::unique_ptr<std::wstring> data(new std::wstring);
+ if (!params->GetParameterStr(i, data.get())) {
+ args[i] = 0;
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data.release();
+ break;
+ }
+ case UINT32_TYPE: {
+ uint32_t data;
+ if (!params->GetParameter32(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ IPCInt ipc_int(data);
+ args[i] = ipc_int.AsVoidPtr();
+ break;
+ }
+ case VOIDPTR_TYPE: {
+ void* data;
+ if (!params->GetParameterVoidPtr(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data;
+ break;
+ }
+ case INPTR_TYPE:
+ case INOUTPTR_TYPE: {
+ if (!args[i]) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ CountedBuffer* buffer = new CountedBuffer(args[i], size);
+ args[i] = buffer;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_args.h b/security/sandbox/chromium/sandbox/win/src/ipc_args.h
new file mode 100644
index 0000000000..58b7507dee
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_args.h
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_IPC_ARGS_H_
+#define SANDBOX_WIN_SRC_IPC_ARGS_H_
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Releases memory allocated for IPC arguments.
+void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]);
+
+// Fills up the list of arguments (args and ipc_params) for an IPC call.
+// Call ReleaseArgs on |ipc_params| and |args| after calling this.
+bool GetArgs(CrossCallParamsEx* params,
+ IPCParams* ipc_params,
+ void* args[kMaxIpcParams]);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_IPC_ARGS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc b/security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc
new file mode 100644
index 0000000000..44f6be433d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests that the IPC is working by issuing a special IPC that is not exposed
+// in the public API.
+SBOX_TESTS_COMMAND int IPC_Ping(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED;
+
+ TargetServices* ts = SandboxFactory::GetTargetServices();
+ if (!ts)
+ return SBOX_TEST_FAILED;
+
+ // Downcast because we have internal knowledge of the object returned.
+ TargetServicesBase* ts_base = reinterpret_cast<TargetServicesBase*>(ts);
+
+ int version = 0;
+ if (L'1' == argv[0][0])
+ version = 1;
+ else
+ version = 2;
+
+ if (!ts_base->TestIPCPing(version))
+ return SBOX_TEST_FAILED;
+
+ ::Sleep(1);
+ if (!ts_base->TestIPCPing(version))
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// The IPC ping test should work before and after the token drop.
+TEST(IPCTest, IPCPingTestSimple) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 1"));
+}
+
+TEST(IPCTest, IPCPingTestWithOutput) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_tags.h b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
new file mode 100644
index 0000000000..ec6de4a66a
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_IPC_TAGS_H__
+#define SANDBOX_SRC_IPC_TAGS_H__
+
+namespace sandbox {
+
+enum class IpcTag {
+ UNUSED = 0,
+ PING1, // Takes a cookie in parameters and returns the cookie
+ // multiplied by 2 and the tick_count. Used for testing only.
+ PING2, // Takes an in/out cookie in parameters and modify the cookie
+ // to be multiplied by 3. Used for testing only.
+ NTCREATEFILE,
+ NTOPENFILE,
+ NTQUERYATTRIBUTESFILE,
+ NTQUERYFULLATTRIBUTESFILE,
+ NTSETINFO_RENAME,
+ CREATENAMEDPIPEW,
+ NTOPENTHREAD,
+ NTOPENPROCESS,
+ NTOPENPROCESSTOKEN,
+ NTOPENPROCESSTOKENEX,
+ CREATEPROCESSW,
+ CREATEEVENT,
+ OPENEVENT,
+ NTCREATEKEY,
+ NTOPENKEY,
+ DUPLICATEHANDLEPROXY,
+ GDI_GDIDLLINITIALIZE,
+ GDI_GETSTOCKOBJECT,
+ USER_REGISTERCLASSW,
+ CREATETHREAD,
+ USER_ENUMDISPLAYMONITORS,
+ USER_ENUMDISPLAYDEVICES,
+ USER_GETMONITORINFO,
+ GDI_CREATEOPMPROTECTEDOUTPUTS,
+ GDI_GETCERTIFICATE,
+ GDI_GETCERTIFICATESIZE,
+ GDI_DESTROYOPMPROTECTEDOUTPUT,
+ GDI_CONFIGUREOPMPROTECTEDOUTPUT,
+ GDI_GETOPMINFORMATION,
+ GDI_GETOPMRANDOMNUMBER,
+ GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ NTCREATESECTION,
+ GETCOMPLEXLINEBREAKS,
+ LAST
+};
+
+constexpr size_t kMaxServiceCount = 64;
+constexpr size_t kMaxIpcTag = static_cast<size_t>(IpcTag::LAST);
+static_assert(kMaxIpcTag <= kMaxServiceCount, "kMaxServiceCount is too low");
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_IPC_TAGS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc b/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc
new file mode 100644
index 0000000000..71f7aff4cc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc
@@ -0,0 +1,632 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Helper function to make the fake shared memory with some
+// basic elements initialized.
+IPCControl* MakeChannels(size_t channel_size,
+ size_t total_shared_size,
+ size_t* base_start) {
+ // Allocate memory
+ char* mem = new char[total_shared_size];
+ memset(mem, 0, total_shared_size);
+ // Calculate how many channels we can fit in the shared memory.
+ total_shared_size -= offsetof(IPCControl, channels);
+ size_t channel_count =
+ total_shared_size / (sizeof(ChannelControl) + channel_size);
+ // Calculate the start of the first channel.
+ *base_start =
+ (sizeof(ChannelControl) * channel_count) + offsetof(IPCControl, channels);
+ // Setup client structure.
+ IPCControl* client_control = reinterpret_cast<IPCControl*>(mem);
+ client_control->channels_count = channel_count;
+ return client_control;
+}
+
+enum TestFixMode { FIX_NO_EVENTS, FIX_PONG_READY, FIX_PONG_NOT_READY };
+
+void FixChannels(IPCControl* client_control,
+ size_t base_start,
+ size_t channel_size,
+ TestFixMode mode) {
+ for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
+ ChannelControl& channel = client_control->channels[ix];
+ channel.channel_base = base_start;
+ channel.state = kFreeChannel;
+ if (mode != FIX_NO_EVENTS) {
+ bool signaled = (FIX_PONG_READY == mode) ? true : false;
+ channel.ping_event = ::CreateEventW(nullptr, false, false, nullptr);
+ channel.pong_event = ::CreateEventW(nullptr, false, signaled, nullptr);
+ }
+ base_start += channel_size;
+ }
+}
+
+void CloseChannelEvents(IPCControl* client_control) {
+ for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
+ ChannelControl& channel = client_control->channels[ix];
+ ::CloseHandle(channel.ping_event);
+ ::CloseHandle(channel.pong_event);
+ }
+}
+
+TEST(IPCTest, ChannelMaker) {
+ // Test that our testing rig is computing offsets properly. We should have
+ // 5 channnels and the offset to the first channel is 108 bytes in 32 bits
+ // and 216 in 64 bits.
+ size_t channel_start = 0;
+ IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start);
+ ASSERT_TRUE(client_control);
+ EXPECT_EQ(5u, client_control->channels_count);
+#if defined(_WIN64)
+ EXPECT_EQ(216u, channel_start);
+#else
+ EXPECT_EQ(108u, channel_start);
+#endif
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, ClientLockUnlock) {
+ // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and
+ // unlock channels properly.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ // Test that we lock the first 3 channels in sequence.
+ void* buff0 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff1 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff2 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ // Test that we unlock and re-lock the right channel.
+ client.FreeBuffer(buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff2b = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ client.FreeBuffer(buff0);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallStrPacking) {
+ // This test tries the CrossCall object with null and non-null string
+ // combination of parameters, integer types and verifies that the unpacker
+ // can read them properly.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ IpcTag tag1 = IpcTag::PING1;
+ const wchar_t* text = L"98765 - 43210";
+ std::wstring copied_text;
+ CrossCallParamsEx* actual_params;
+
+ CrossCall(client, tag1, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(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());
+ copied_text.clear();
+
+ // Check with an empty string.
+ IpcTag tag2 = IpcTag::PING2;
+ const wchar_t* null_text = nullptr;
+ CrossCall(client, tag2, null_text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ uint32_t param_size = 1;
+ ArgType type = INVALID_TYPE;
+ void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ EXPECT_TRUE(param_addr);
+ EXPECT_EQ(0u, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_TRUE(copied_text.empty());
+
+ IpcTag tag3 = IpcTag::PING1;
+ param_size = 1;
+ copied_text.clear();
+
+ // Check with an empty string and a non-empty string.
+ CrossCall(client, tag3, null_text, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(2u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag3, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ EXPECT_TRUE(param_addr);
+ EXPECT_EQ(0u, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_TRUE(copied_text.empty());
+ EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text));
+ EXPECT_STREQ(text, copied_text.c_str());
+
+ param_size = 1;
+ std::wstring copied_text_p0, copied_text_p2;
+
+ const wchar_t* text2 = L"AeFG";
+ CrossCall(client, tag1, text2, null_text, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(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());
+ EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2));
+ EXPECT_STREQ(text, copied_text_p2.c_str());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ EXPECT_TRUE(param_addr);
+ EXPECT_EQ(0u, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallIntPacking) {
+ // Check handling for regular 32 bit integers used in Windows.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ IpcTag tag1 = IpcTag::PING1;
+ IpcTag tag2 = IpcTag::PING2;
+ const wchar_t* text = L"godzilla";
+ CrossCallParamsEx* actual_params;
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ DWORD dw = 0xE6578;
+ CrossCall(client, tag2, dw, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ ArgType type = INVALID_TYPE;
+ uint32_t param_size = 1;
+ void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ ASSERT_EQ(sizeof(dw), param_size);
+ EXPECT_EQ(UINT32_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
+
+ // Check handling for windows HANDLES.
+ HANDLE h = HANDLE(0x70000500);
+ CrossCall(client, tag1, text, h, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(2u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+
+ // Check combination of 32 and 64 bits.
+ CrossCall(client, tag2, h, dw, h, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(3u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ ASSERT_EQ(sizeof(dw), param_size);
+ EXPECT_EQ(UINT32_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(2, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallValidation) {
+ // First a sanity test with a well formed parameter object.
+ unsigned long value = 124816;
+ IpcTag kTag = IpcTag::PING1;
+ 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<void*>(params_1.GetBuffer());
+
+ uint32_t out_size = 0;
+ CrossCallParamsEx* ccp = 0;
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
+ &out_size);
+ ASSERT_TRUE(ccp);
+ EXPECT_TRUE(ccp->GetBuffer() != buffer);
+ EXPECT_EQ(kTag, ccp->GetTag());
+ EXPECT_EQ(1u, ccp->GetParamsCount());
+ delete[](reinterpret_cast<char*>(ccp));
+
+ // Test that we handle integer overflow on the number of params
+ // correctly. We use a test-only ctor for ActualCallParams that
+ // allows to create malformed cross-call buffers.
+ const int32_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<void*>(params_2.GetBuffer());
+
+ EXPECT_TRUE(buffer);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
+ &out_size);
+ // If the buffer is malformed the return is nullptr.
+ EXPECT_TRUE(!ccp);
+ }
+
+ ActualCallParams<1, kBufferSize> params_3(kTag, 1);
+ params_3.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE);
+ buffer = const_cast<void*>(params_3.GetBuffer());
+ EXPECT_TRUE(buffer);
+
+ uint32_t correct_size = params_3.OverrideSize(1);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(!ccp);
+
+ // The correct_size is 8 bytes aligned.
+ params_3.OverrideSize(correct_size - 7);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(!ccp);
+
+ params_3.OverrideSize(correct_size);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(ccp);
+
+ // Make sure that two parameters work as expected.
+ ActualCallParams<2, kBufferSize> params_4(kTag, 2);
+ params_4.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE);
+ params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE);
+ buffer = const_cast<void*>(params_4.GetBuffer());
+ EXPECT_TRUE(buffer);
+
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(ccp);
+
+#if defined(_WIN64)
+ correct_size = params_4.OverrideSize(1);
+ params_4.OverrideSize(correct_size - 1);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(!ccp);
+#endif
+}
+
+// This structure is passed to the mock server threads to simulate
+// the server side IPC so it has the required kernel objects.
+struct ServerEvents {
+ HANDLE ping;
+ HANDLE pong;
+ volatile LONG* state;
+ HANDLE mutex;
+};
+
+// This is the server thread that quicky answers an IPC and exits.
+DWORD WINAPI QuickResponseServer(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->ping, INFINITE);
+ ::InterlockedExchange(events->state, kAckChannel);
+ ::SetEvent(events->pong);
+ return wait_result;
+}
+
+class CrossCallParamsMock : public CrossCallParams {
+ public:
+ CrossCallParamsMock(IpcTag tag, uint32_t params_count)
+ : CrossCallParams(tag, params_count) {}
+};
+
+void FakeOkAnswerInChannel(void* channel) {
+ CrossCallReturn* answer = reinterpret_cast<CrossCallReturn*>(channel);
+ answer->call_outcome = SBOX_ALL_OK;
+}
+
+// Create two threads that will quickly answer IPCs; the first one
+// using channel 1 (channel 0 is busy) and one using channel 0. No time-out
+// should occur.
+TEST(IPCTest, ClientFastServer) {
+ const size_t channel_size = kIPCChannelSize;
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(channel_size, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
+ client_control->server_alive = ::CreateMutex(nullptr, false, nullptr);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ ServerEvents events = {0};
+ events.ping = client_control->channels[1].ping_event;
+ events.pong = client_control->channels[1].pong_event;
+ events.state = &client_control->channels[1].state;
+
+ HANDLE t1 =
+ ::CreateThread(nullptr, 0, QuickResponseServer, &events, 0, nullptr);
+ ASSERT_TRUE(t1);
+ ::CloseHandle(t1);
+
+ void* buff0 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ void* buff1 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ EXPECT_EQ(IpcTag::UNUSED, client_control->channels[1].ipc_tag);
+
+ IpcTag tag = IpcTag::PING1;
+ CrossCallReturn answer;
+ CrossCallParamsMock* params1 = new (buff1) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff1);
+
+ ResultCode result = client.DoCall(params1, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff1);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[1].ipc_tag);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ HANDLE t2 =
+ ::CreateThread(nullptr, 0, QuickResponseServer, &events, 0, nullptr);
+ ASSERT_TRUE(t2);
+ ::CloseHandle(t2);
+
+ client.FreeBuffer(buff0);
+ events.ping = client_control->channels[0].ping_event;
+ events.pong = client_control->channels[0].pong_event;
+ events.state = &client_control->channels[0].state;
+
+ tag = IpcTag::PING2;
+ CrossCallParamsMock* params2 = new (buff0) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff0);
+
+ result = client.DoCall(params2, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff0);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ CloseChannelEvents(client_control);
+ ::CloseHandle(client_control->server_alive);
+
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+// This is the server thread that very slowly answers an IPC and exits. Note
+// that the pong event needs to be signaled twice.
+DWORD WINAPI SlowResponseServer(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->ping, INFINITE);
+ ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200);
+ ::InterlockedExchange(events->state, kAckChannel);
+ ::SetEvent(events->pong);
+ return wait_result;
+}
+
+// This thread's job is to keep the mutex locked.
+DWORD WINAPI MainServerThread(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->mutex, INFINITE);
+ Sleep(kIPCWaitTimeOut1 * 20);
+ return wait_result;
+}
+
+// Creates a server thread that answers the IPC so slow that is guaranteed to
+// trigger the time-out code path in the client. A second thread is created
+// to hold locked the server_alive mutex: this signals the client that the
+// server is not dead and it retries the wait.
+TEST(IPCTest, ClientSlowServer) {
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
+ client_control->server_alive = ::CreateMutex(nullptr, false, nullptr);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ ServerEvents events = {0};
+ events.ping = client_control->channels[0].ping_event;
+ events.pong = client_control->channels[0].pong_event;
+ events.state = &client_control->channels[0].state;
+
+ HANDLE t1 =
+ ::CreateThread(nullptr, 0, SlowResponseServer, &events, 0, nullptr);
+ ASSERT_TRUE(t1);
+ ::CloseHandle(t1);
+
+ ServerEvents events2 = {0};
+ events2.pong = events.pong;
+ events2.mutex = client_control->server_alive;
+
+ HANDLE t2 =
+ ::CreateThread(nullptr, 0, MainServerThread, &events2, 0, nullptr);
+ ASSERT_TRUE(t2);
+ ::CloseHandle(t2);
+
+ ::Sleep(1);
+
+ void* buff0 = client.GetBuffer();
+ IpcTag tag = IpcTag::PING1;
+ CrossCallReturn answer;
+ CrossCallParamsMock* params1 = new (buff0) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff0);
+
+ ResultCode result = client.DoCall(params1, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff0);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+
+ CloseChannelEvents(client_control);
+ ::CloseHandle(client_control->server_alive);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+// This test-only IPC dispatcher has two handlers with the same signature
+// but only CallOneHandler should be used.
+class UnitTestIPCDispatcher : public Dispatcher {
+ public:
+ UnitTestIPCDispatcher();
+ ~UnitTestIPCDispatcher() override {}
+
+ bool SetupService(InterceptionManager* manager, IpcTag service) override {
+ return true;
+ }
+
+ private:
+ 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_t p2) { return true; }
+};
+
+UnitTestIPCDispatcher::UnitTestIPCDispatcher() {
+ static const IPCCall call_one = {{IpcTag::PING1, {VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallOneHandler)};
+ static const IPCCall call_two = {{IpcTag::PING2, {VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallTwoHandler)};
+ ipc_calls_.push_back(call_one);
+ ipc_calls_.push_back(call_two);
+}
+
+// This test does most of the shared memory IPC client-server roundtrip
+// and tests the packing, unpacking and call dispatching.
+TEST(IPCTest, SharedMemServerTests) {
+ size_t base_start = 0;
+ IPCControl* client_control = MakeChannels(kIPCChannelSize, 4096, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ HANDLE bar = HANDLE(191919);
+ DWORD foo = 6767676;
+ CrossCall(client, IpcTag::PING1, bar, foo, &answer);
+ void* buff = client.GetBuffer();
+ ASSERT_TRUE(buff);
+
+ UnitTestIPCDispatcher dispatcher;
+ // Since we are directly calling InvokeCallback, most of this structure
+ // can be set to nullptr.
+ sandbox::SharedMemIPCServer::ServerControl srv_control = {};
+ srv_control.channel_size = kIPCChannelSize;
+ srv_control.shared_base = reinterpret_cast<char*>(client_control);
+ srv_control.dispatcher = &dispatcher;
+
+ sandbox::CrossCallReturn call_return = {0};
+ EXPECT_TRUE(
+ SharedMemIPCServer::InvokeCallback(&srv_control, buff, &call_return));
+ EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome);
+ EXPECT_TRUE(bar == call_return.extended[0].handle);
+ EXPECT_EQ(foo, call_return.extended[1].unsigned_int);
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/job.cc b/security/sandbox/chromium/sandbox/win/src/job.cc
new file mode 100644
index 0000000000..39baffb193
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/job.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/job.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/restricted_token.h"
+
+namespace sandbox {
+
+Job::Job() : job_handle_(nullptr) {}
+
+Job::~Job() {}
+
+DWORD Job::Init(JobLevel security_level,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit) {
+ if (job_handle_.IsValid())
+ return ERROR_ALREADY_INITIALIZED;
+
+ job_handle_.Set(::CreateJobObject(nullptr, // No security attribute
+ job_name));
+ if (!job_handle_.IsValid())
+ return ::GetLastError();
+
+ 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.
+ switch (security_level) {
+ case JOB_LOCKDOWN: {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
+ FALLTHROUGH;
+ }
+ case JOB_RESTRICTED: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS;
+ FALLTHROUGH;
+ }
+ case JOB_LIMITED_USER: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS;
+ jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
+ jeli.BasicLimitInformation.ActiveProcessLimit = 1;
+ FALLTHROUGH;
+ }
+ case JOB_INTERACTIVE: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
+ FALLTHROUGH;
+ }
+ case JOB_UNPROTECTED: {
+ if (memory_limit) {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY;
+ jeli.ProcessMemoryLimit = memory_limit;
+ }
+
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ break;
+ }
+ default: { return ERROR_BAD_ARGUMENTS; }
+ }
+
+ if (!::SetInformationJobObject(job_handle_.Get(),
+ JobObjectExtendedLimitInformation, &jeli,
+ sizeof(jeli))) {
+ return ::GetLastError();
+ }
+
+ jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions);
+ if (!::SetInformationJobObject(job_handle_.Get(),
+ JobObjectBasicUIRestrictions, &jbur,
+ sizeof(jbur))) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD Job::UserHandleGrantAccess(HANDLE handle) {
+ if (!job_handle_.IsValid())
+ return ERROR_NO_DATA;
+
+ if (!::UserHandleGrantAccess(handle, job_handle_.Get(),
+ true)) { // Access allowed.
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+base::win::ScopedHandle Job::Take() {
+ return std::move(job_handle_);
+}
+
+DWORD Job::AssignProcessToJob(HANDLE process_handle) {
+ if (!job_handle_.IsValid())
+ return ERROR_NO_DATA;
+
+ if (!::AssignProcessToJobObject(job_handle_.Get(), process_handle))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/job.h b/security/sandbox/chromium/sandbox/win/src/job.h
new file mode 100644
index 0000000000..153080bf6b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/job.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_JOB_H_
+#define SANDBOX_SRC_JOB_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+
+namespace sandbox {
+
+// Handles the creation of job objects based on a security profile.
+// Sample usage:
+// Job job;
+// job.Init(JOB_LOCKDOWN, nullptr); //no job name
+// job.AssignProcessToJob(process_handle);
+class Job {
+ public:
+ Job();
+
+ ~Job();
+
+ // Initializes and creates the job object. The security of the job is based
+ // on the security_level parameter.
+ // job_name can be nullptr if the job is unnamed.
+ // If the chosen profile has too many ui restrictions, you can disable some
+ // by specifying them in the ui_exceptions parameters.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD Init(JobLevel security_level,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit);
+
+ // Assigns the process referenced by process_handle to the job.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AssignProcessToJob(HANDLE process_handle);
+
+ // Grants access to "handle" to the job. All processes in the job can
+ // subsequently recognize and use the handle.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD UserHandleGrantAccess(HANDLE handle);
+
+ // Revokes ownership to the job handle and returns it.
+ // 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.
+ base::win::ScopedHandle job_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_JOB_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/job_unittest.cc b/security/sandbox/chromium/sandbox/win/src/job_unittest.cc
new file mode 100644
index 0000000000..536900dc40
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/job_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the job object.
+
+#include "sandbox/win/src/job.h"
+
+#include "base/win/scoped_process_information.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests the creation and destruction of the job.
+TEST(JobTest, TestCreation) {
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+
+ // check if the job exists.
+ HANDLE job_handle =
+ ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(job_handle);
+
+ if (job_handle)
+ CloseHandle(job_handle);
+ }
+
+ // Check if the job is destroyed when the object goes out of scope.
+ HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(!job_handle);
+ ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError());
+}
+
+// Tests the method "Take".
+TEST(JobTest, Take) {
+ base::win::ScopedHandle job_handle;
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+
+ 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
+ // out of scope.
+ HANDLE job_handle_dup =
+ ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(job_handle_dup);
+
+ // Remove all references.
+ if (job_handle_dup)
+ ::CloseHandle(job_handle_dup);
+
+ job_handle.Close();
+
+ // Check if the jbo is really dead.
+ job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(!job_handle_dup);
+ ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError());
+}
+
+// Tests the ui exceptions
+TEST(JobTest, TestExceptions) {
+ base::win::ScopedHandle job_handle;
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name",
+ JOB_OBJECT_UILIMIT_READCLIPBOARD, 0));
+
+ job_handle = job.Take();
+ ASSERT_TRUE(job_handle.IsValid());
+
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+ DWORD size = sizeof(jbur);
+ ASSERT_TRUE(::QueryInformationJobObject(
+ job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size));
+
+ ASSERT_EQ(0u, jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD);
+ job_handle.Close();
+ }
+
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+
+ job_handle = job.Take();
+ ASSERT_TRUE(job_handle.IsValid());
+
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+ DWORD size = sizeof(jbur);
+ ASSERT_TRUE(::QueryInformationJobObject(
+ job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size));
+
+ ASSERT_EQ(static_cast<DWORD>(JOB_OBJECT_UILIMIT_READCLIPBOARD),
+ jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD);
+ }
+}
+
+// Tests the error case when the job is initialized twice.
+TEST(JobTest, DoubleInit) {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+ ASSERT_EQ(static_cast<DWORD>(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(static_cast<DWORD>(ERROR_NO_DATA),
+ job.UserHandleGrantAccess(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA), job.AssignProcessToJob(nullptr));
+ ASSERT_FALSE(job.Take().IsValid());
+}
+
+// Tests the initialization of the job with different security level.
+TEST(JobTest, SecurityLevel) {
+ Job job1;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job1.Init(JOB_LOCKDOWN, L"job1", 0, 0));
+
+ Job job2;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job2.Init(JOB_RESTRICTED, L"job2", 0, 0));
+
+ Job job3;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job3.Init(JOB_LIMITED_USER, L"job3", 0, 0));
+
+ Job job4;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job4.Init(JOB_INTERACTIVE, L"job4", 0, 0));
+
+ Job job5;
+ ASSERT_EQ(static_cast<DWORD>(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(static_cast<DWORD>(ERROR_BAD_ARGUMENTS),
+ job6.Init(JOB_NONE, L"job6", 0, 0));
+
+ Job job7;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS),
+ job7.Init(static_cast<JobLevel>(JOB_NONE + 1), L"job7", 0, 0));
+}
+
+// Tests the method "AssignProcessToJob".
+TEST(JobTest, ProcessInJob) {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_UNPROTECTED, L"job_test_process", 0, 0));
+
+ wchar_t notepad[] = L"notepad";
+ STARTUPINFO si = {sizeof(si)};
+ PROCESS_INFORMATION temp_process_info = {};
+ ASSERT_TRUE(::CreateProcess(nullptr, notepad, nullptr, nullptr, false, 0,
+ nullptr, nullptr, &si, &temp_process_info));
+ base::win::ScopedProcessInformation pi(temp_process_info);
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.AssignProcessToJob(pi.process_handle()));
+
+ // Get the job handle.
+ 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);
+ EXPECT_TRUE(::QueryInformationJobObject(
+ job_handle.Get(), JobObjectBasicProcessIdList, &jbpidl, size, &size));
+
+ 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));
+}
+
+} // 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
new file mode 100644
index 0000000000..85ffebe6e6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/named_pipe_interception.h"
+#include "sandbox/win/src/named_pipe_policy.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+namespace sandbox {
+
+NamedPipeDispatcher::NamedPipeDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::CREATENAMEDPIPEW,
+ {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&NamedPipeDispatcher::CreateNamedPipe)};
+
+ ipc_calls_.push_back(create_params);
+}
+
+bool NamedPipeDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (IpcTag::CREATENAMEDPIPEW == service)
+ return INTERCEPT_EAT(manager, kKerneldllName, CreateNamedPipeW,
+ CREATE_NAMED_PIPE_ID, 36);
+
+ return false;
+}
+
+bool NamedPipeDispatcher::CreateNamedPipe(IPCInfo* ipc,
+ std::wstring* name,
+ 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;
+
+ base::StringPiece16 dotdot(STRING16_LITERAL(".."));
+
+ for (const base::StringPiece16& path : base::SplitStringPiece(
+ base::AsStringPiece16(*name), STRING16_LITERAL("/"),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ for (const base::StringPiece16& inner :
+ base::SplitStringPiece(path, STRING16_LITERAL("\\"),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ if (inner == dotdot)
+ return true;
+ }
+ }
+
+ const wchar_t* pipe_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ EvalResult eval =
+ policy_base_->EvalPolicy(IpcTag::CREATENAMEDPIPEW, params.GetBase());
+
+ // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to
+ // disable all string parsing and to send the string that follows it straight
+ // to the file system."
+ // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ // This ensures even if there is a path traversal in the pipe name, and it is
+ // able to get past the checks above, it will still not be allowed to escape
+ // our allowed namespace.
+ if (name->compare(0, 4, L"\\\\.\\") == 0)
+ name->replace(0, 4, L"\\\\\?\\");
+
+ HANDLE pipe;
+ DWORD ret = NamedPipePolicy::CreateNamedPipeAction(
+ eval, *ipc->client_info, *name, open_mode, pipe_mode, max_instances,
+ out_buffer_size, in_buffer_size, default_timeout, &pipe);
+
+ ipc->return_info.win32_result = ret;
+ ipc->return_info.handle = pipe;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h
new file mode 100644
index 0000000000..a14f658506
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
+#define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles named pipe related IPC calls.
+class NamedPipeDispatcher : public Dispatcher {
+ public:
+ explicit NamedPipeDispatcher(PolicyBase* policy_base);
+ ~NamedPipeDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateNamedPipeW() in the
+ // target.
+ bool CreateNamedPipe(IPCInfo* ipc,
+ std::wstring* name,
+ 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);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
new file mode 100644
index 0000000000..aa3d5dc358
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+HANDLE WINAPI
+TargetCreateNamedPipeW(CreateNamedPipeWFunction orig_CreateNamedPipeW,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = orig_CreateNamedPipeW(
+ pipe_name, open_mode, pipe_mode, max_instance, out_buffer_size,
+ in_buffer_size, default_timeout, security_attributes);
+ if (INVALID_HANDLE_VALUE != pipe)
+ return pipe;
+
+ 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;
+
+ DWORD original_error = ::GetLastError();
+
+ // We don't support specific Security Attributes.
+ if (security_attributes)
+ return INVALID_HANDLE_VALUE;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ if (!QueryBroker(IpcTag::CREATENAMEDPIPEW, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code =
+ CrossCall(ipc, IpcTag::CREATENAMEDPIPEW, pipe_name, open_mode,
+ pipe_mode, max_instance, out_buffer_size, in_buffer_size,
+ default_timeout, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+
+ if (ERROR_SUCCESS != answer.win32_result)
+ return INVALID_HANDLE_VALUE;
+
+ mozilla::sandboxing::LogAllowed("CreateNamedPipeW", pipe_name);
+ return answer.handle;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return INVALID_HANDLE_VALUE;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h
new file mode 100644
index 0000000000..8eedd14b31
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_NAMED_PIPE_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_NAMED_PIPE_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+typedef HANDLE(WINAPI* CreateNamedPipeWFunction)(
+ LPCWSTR lpName,
+ DWORD dwOpenMode,
+ DWORD dwPipeMode,
+ DWORD nMaxInstances,
+ DWORD nOutBufferSize,
+ DWORD nInBufferSize,
+ DWORD nDefaultTimeOut,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes);
+
+// Interception of CreateNamedPipeW in kernel32.dll
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateNamedPipeW(CreateNamedPipeWFunction orig_CreateNamedPipeW,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_NAMED_PIPE_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc
new file mode 100644
index 0000000000..bfad75852d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_policy.h"
+
+#include <string>
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+
+// Creates a named pipe and duplicates the handle to 'target_process'. The
+// remaining parameters are the same as CreateNamedPipeW().
+HANDLE CreateNamedPipeHelper(HANDLE target_process,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = ::CreateNamedPipeW(
+ pipe_name, open_mode, pipe_mode, max_instances, out_buffer_size,
+ in_buffer_size, default_timeout, security_attributes);
+ if (INVALID_HANDLE_VALUE == pipe)
+ return pipe;
+
+ HANDLE new_pipe;
+ if (!::DuplicateHandle(::GetCurrentProcess(), pipe, target_process, &new_pipe,
+ 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ return new_pipe;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool NamedPipePolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ if (TargetPolicy::NAMEDPIPES_ALLOW_ANY != semantics) {
+ return false;
+ }
+ PolicyRule pipe(ASK_BROKER);
+ if (!pipe.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::CREATENAMEDPIPEW, &pipe)) {
+ return false;
+ }
+ return true;
+}
+
+DWORD NamedPipePolicy::CreateNamedPipeAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ HANDLE* pipe) {
+ *pipe = INVALID_HANDLE_VALUE;
+ // The only action supported is ASK_BROKER which means create the pipe.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ *pipe = CreateNamedPipeHelper(client_info.process, name.c_str(), open_mode,
+ pipe_mode, max_instances, out_buffer_size,
+ in_buffer_size, default_timeout, nullptr);
+
+ if (INVALID_HANDLE_VALUE == *pipe)
+ return ERROR_ACCESS_DENIED;
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h
new file mode 100644
index 0000000000..4ad272f16f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_NAMED_PIPE_POLICY_H__
+#define SANDBOX_SRC_NAMED_PIPE_POLICY_H__
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to named pipe creation.
+class NamedPipePolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level.
+ // policy rule for named pipe creation
+ // 'name' is the named pipe to be created
+ // 'semantics' is the desired semantics.
+ // 'policy' is the policy generator to which the rules are going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Processes a 'CreateNamedPipeW()' request from the target.
+ static DWORD CreateNamedPipeAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ HANDLE* pipe);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc
new file mode 100644
index 0000000000..db532d618d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t** argv) {
+ if (argc < 1 || argc > 2 || !argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE pipe = ::CreateNamedPipeW(
+ argv[0], PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096, 4096, 2000, nullptr);
+ if (INVALID_HANDLE_VALUE == pipe)
+ return SBOX_TEST_DENIED;
+
+ // The second parameter allows us to enforce an allowlist for where the
+ // pipe should be in the object namespace after creation.
+ if (argc == 2) {
+ std::wstring handle_name;
+ if (GetHandleName(pipe, &handle_name)) {
+ if (handle_name.compare(0, wcslen(argv[1]), argv[1]) != 0)
+ return SBOX_TEST_FAILED;
+ } else {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ OVERLAPPED overlapped = {0};
+ overlapped.hEvent = ::CreateEvent(nullptr, true, true, nullptr);
+ bool result = ::ConnectNamedPipe(pipe, &overlapped);
+
+ if (!result) {
+ DWORD error = ::GetLastError();
+ if (ERROR_PIPE_CONNECTED != error && ERROR_IO_PENDING != error) {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ if (!::CloseHandle(pipe))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(overlapped.hEvent);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests if we can create a pipe in the sandbox.
+TEST(NamedPipePolicyTest, CreatePipe) {
+ TestRunner runner;
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh"));
+}
+
+// Tests if we can create a pipe with a path traversal in the sandbox.
+TEST(NamedPipePolicyTest, CreatePipeTraversal) {
+ TestRunner runner;
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\..\\bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/../bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\../bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/..\\bleh"));
+}
+
+// This tests that path canonicalization is actually disabled if we use \\?\
+// syntax.
+TEST(NamedPipePolicyTest, CreatePipeCanonicalization) {
+ // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to
+ // disable all string parsing and to send the string that follows it straight
+ // to the file system."
+ // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ const wchar_t* argv[2] = {L"\\\\?\\pipe\\test\\..\\bleh",
+ L"\\Device\\NamedPipe\\test"};
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ NamedPipe_Create(2, const_cast<wchar_t**>(argv)));
+}
+
+// The same test as CreatePipe but this time using strict interceptions.
+TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) {
+ TestRunner runner;
+ runner.GetPolicy()->SetStrictInterceptions();
+
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/nt_internals.h b/security/sandbox/chromium/sandbox/win/src/nt_internals.h
new file mode 100644
index 0000000000..6041566436
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/nt_internals.h
@@ -0,0 +1,983 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file holds definitions related to the ntdll API.
+
+#ifndef SANDBOX_WIN_SRC_NT_INTERNALS_H__
+#define SANDBOX_WIN_SRC_NT_INTERNALS_H__
+
+#include <windows.h>
+
+#include <stddef.h>
+
+typedef LONG NTSTATUS;
+#define NT_SUCCESS(st) (st >= 0)
+#define NT_ERROR(st) ((((ULONG)(st)) >> 30) == 3)
+
+// clang-format off
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
+#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
+#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L)
+#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003L)
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+#ifndef STATUS_INVALID_PARAMETER
+// It is now defined in Windows 2008 SDK.
+#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
+#endif
+#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
+#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
+#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
+#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
+#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL)
+#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
+#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
+#define STATUS_INVALID_IMAGE_HASH ((NTSTATUS)0xC0000428L)
+// clang-format on
+
+#define CURRENT_PROCESS ((HANDLE)-1)
+#define CURRENT_THREAD ((HANDLE)-2)
+#define NtCurrentProcess CURRENT_PROCESS
+
+typedef struct _UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING;
+typedef UNICODE_STRING* PUNICODE_STRING;
+typedef const UNICODE_STRING* PCUNICODE_STRING;
+
+typedef struct _STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PCHAR Buffer;
+} STRING;
+typedef STRING* PSTRING;
+
+typedef STRING ANSI_STRING;
+typedef PSTRING PANSI_STRING;
+typedef CONST PSTRING PCANSI_STRING;
+
+typedef STRING OEM_STRING;
+typedef PSTRING POEM_STRING;
+typedef CONST STRING* PCOEM_STRING;
+
+#define OBJ_CASE_INSENSITIVE 0x00000040L
+#define OBJ_OPENIF 0x00000080L
+
+typedef struct _OBJECT_ATTRIBUTES {
+ ULONG Length;
+ HANDLE RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+} OBJECT_ATTRIBUTES;
+typedef OBJECT_ATTRIBUTES* POBJECT_ATTRIBUTES;
+
+#define InitializeObjectAttributes(p, n, a, r, s) \
+ { \
+ (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
+ (p)->RootDirectory = r; \
+ (p)->Attributes = a; \
+ (p)->ObjectName = n; \
+ (p)->SecurityDescriptor = s; \
+ (p)->SecurityQualityOfService = nullptr; \
+ }
+
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+// -----------------------------------------------------------------------
+// File IO
+
+// Create disposition values.
+
+#define FILE_SUPERSEDE 0x00000000
+#define FILE_OPEN 0x00000001
+#define FILE_CREATE 0x00000002
+#define FILE_OPEN_IF 0x00000003
+#define FILE_OVERWRITE 0x00000004
+#define FILE_OVERWRITE_IF 0x00000005
+#define FILE_MAXIMUM_DISPOSITION 0x00000005
+
+// Create/open option flags.
+
+#define FILE_DIRECTORY_FILE 0x00000001
+#define FILE_WRITE_THROUGH 0x00000002
+#define FILE_SEQUENTIAL_ONLY 0x00000004
+#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
+
+#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
+#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
+#define FILE_NON_DIRECTORY_FILE 0x00000040
+#define FILE_CREATE_TREE_CONNECTION 0x00000080
+
+#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
+#define FILE_NO_EA_KNOWLEDGE 0x00000200
+#define FILE_OPEN_REMOTE_INSTANCE 0x00000400
+#define FILE_RANDOM_ACCESS 0x00000800
+
+#define FILE_DELETE_ON_CLOSE 0x00001000
+#define FILE_OPEN_BY_FILE_ID 0x00002000
+#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
+#define FILE_NO_COMPRESSION 0x00008000
+
+#define FILE_RESERVE_OPFILTER 0x00100000
+#define FILE_OPEN_REPARSE_POINT 0x00200000
+#define FILE_OPEN_NO_RECALL 0x00400000
+#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
+
+// Create/open result values. These are the disposition values returned on the
+// io status information.
+#define FILE_SUPERSEDED 0x00000000
+#define FILE_OPENED 0x00000001
+#define FILE_CREATED 0x00000002
+#define FILE_OVERWRITTEN 0x00000003
+#define FILE_EXISTS 0x00000004
+#define FILE_DOES_NOT_EXIST 0x00000005
+
+typedef NTSTATUS(WINAPI* NtCreateFileFunction)(
+ OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PLARGE_INTEGER AllocationSize OPTIONAL,
+ IN ULONG FileAttributes,
+ IN ULONG ShareAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength);
+
+typedef NTSTATUS(WINAPI* NtOpenFileFunction)(OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG ShareAccess,
+ IN ULONG OpenOptions);
+
+typedef NTSTATUS(WINAPI* NtCloseFunction)(IN HANDLE Handle);
+
+typedef enum _FILE_INFORMATION_CLASS {
+ FileRenameInformation = 10
+} FILE_INFORMATION_CLASS,
+ *PFILE_INFORMATION_CLASS;
+
+typedef struct _FILE_RENAME_INFORMATION {
+ BOOLEAN ReplaceIfExists;
+ HANDLE RootDirectory;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtSetInformationFileFunction)(
+ IN HANDLE FileHandle,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PVOID FileInformation,
+ IN ULONG Length,
+ IN FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef struct FILE_BASIC_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ ULONG FileAttributes;
+} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryAttributesFileFunction)(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PFILE_BASIC_INFORMATION FileAttributes);
+
+typedef struct _FILE_NETWORK_OPEN_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ ULONG FileAttributes;
+} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryFullAttributesFileFunction)(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PFILE_NETWORK_OPEN_INFORMATION FileAttributes);
+
+// -----------------------------------------------------------------------
+// Sections
+
+typedef NTSTATUS(WINAPI* NtCreateSectionFunction)(
+ OUT PHANDLE SectionHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN PLARGE_INTEGER MaximumSize OPTIONAL,
+ IN ULONG SectionPageProtection,
+ IN ULONG AllocationAttributes,
+ IN HANDLE FileHandle OPTIONAL);
+
+typedef ULONG SECTION_INHERIT;
+#define ViewShare 1
+#define ViewUnmap 2
+
+typedef NTSTATUS(WINAPI* NtMapViewOfSectionFunction)(
+ IN HANDLE SectionHandle,
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN ULONG_PTR ZeroBits,
+ IN SIZE_T CommitSize,
+ IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
+ IN OUT PSIZE_T ViewSize,
+ IN SECTION_INHERIT InheritDisposition,
+ IN ULONG AllocationType,
+ IN ULONG Win32Protect);
+
+typedef NTSTATUS(WINAPI* NtUnmapViewOfSectionFunction)(IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress);
+
+typedef enum _SECTION_INFORMATION_CLASS {
+ SectionBasicInformation = 0,
+ SectionImageInformation
+} SECTION_INFORMATION_CLASS;
+
+typedef struct _SECTION_BASIC_INFORMATION {
+ PVOID BaseAddress;
+ ULONG Attributes;
+ LARGE_INTEGER Size;
+} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQuerySectionFunction)(
+ IN HANDLE SectionHandle,
+ IN SECTION_INFORMATION_CLASS SectionInformationClass,
+ OUT PVOID SectionInformation,
+ IN SIZE_T SectionInformationLength,
+ OUT PSIZE_T ReturnLength OPTIONAL);
+
+// -----------------------------------------------------------------------
+// Process and Thread
+
+typedef struct _CLIENT_ID {
+ PVOID UniqueProcess;
+ PVOID UniqueThread;
+} CLIENT_ID, *PCLIENT_ID;
+
+typedef NTSTATUS(WINAPI* NtOpenThreadFunction)(OUT PHANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN PCLIENT_ID ClientId);
+
+typedef NTSTATUS(WINAPI* NtOpenProcessFunction)(OUT PHANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN PCLIENT_ID ClientId);
+
+typedef enum _NT_THREAD_INFORMATION_CLASS {
+ ThreadBasicInformation,
+ ThreadTimes,
+ ThreadPriority,
+ ThreadBasePriority,
+ ThreadAffinityMask,
+ ThreadImpersonationToken,
+ ThreadDescriptorTableEntry,
+ ThreadEnableAlignmentFaultFixup,
+ ThreadEventPair,
+ ThreadQuerySetWin32StartAddress,
+ ThreadZeroTlsCell,
+ ThreadPerformanceCount,
+ ThreadAmILastThread,
+ ThreadIdealProcessor,
+ ThreadPriorityBoost,
+ ThreadSetTlsArrayAddress,
+ ThreadIsIoPending,
+ ThreadHideFromDebugger
+} NT_THREAD_INFORMATION_CLASS,
+ *PNT_THREAD_INFORMATION_CLASS;
+
+typedef NTSTATUS(WINAPI* NtSetInformationThreadFunction)(
+ IN HANDLE ThreadHandle,
+ IN NT_THREAD_INFORMATION_CLASS ThreadInformationClass,
+ IN PVOID ThreadInformation,
+ IN ULONG ThreadInformationLength);
+
+// Partial definition only:
+typedef enum _PROCESSINFOCLASS {
+ ProcessBasicInformation = 0,
+ ProcessExecuteFlags = 0x22
+} PROCESSINFOCLASS;
+
+// For the structure documentation, see
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813741(v=vs.85).aspx
+typedef struct _RTL_USER_PROCESS_PARAMETERS {
+ BYTE Reserved1[16];
+ PVOID Reserved2[10];
+ UNICODE_STRING ImagePathName;
+ UNICODE_STRING CommandLine;
+} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
+
+// Partial definition only, from
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx
+typedef struct _PEB {
+ BYTE InheritedAddressSpace;
+ BYTE ReadImageFileExecOptions;
+ BYTE BeingDebugged;
+ BYTE SpareBool;
+ PVOID Mutant;
+ PVOID ImageBaseAddress;
+ PVOID Ldr;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+} PEB, *PPEB;
+
+typedef LONG KPRIORITY;
+
+typedef struct _PROCESS_BASIC_INFORMATION {
+ union {
+ NTSTATUS ExitStatus;
+ PVOID padding_for_x64_0;
+ };
+ PPEB PebBaseAddress;
+ KAFFINITY AffinityMask;
+ 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)(
+ IN HANDLE ProcessHandle,
+ IN PROCESSINFOCLASS ProcessInformationClass,
+ OUT PVOID ProcessInformation,
+ IN ULONG ProcessInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtSetInformationProcessFunction)(
+ HANDLE ProcessHandle,
+ IN PROCESSINFOCLASS ProcessInformationClass,
+ IN PVOID ProcessInformation,
+ IN ULONG ProcessInformationLength);
+
+typedef NTSTATUS(WINAPI* NtOpenThreadTokenFunction)(IN HANDLE ThreadHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtOpenThreadTokenExFunction)(IN HANDLE ThreadHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ IN ULONG HandleAttributes,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtOpenProcessTokenFunction)(IN HANDLE ProcessHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtOpenProcessTokenExFunction)(
+ IN HANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG HandleAttributes,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtQueryInformationTokenFunction)(
+ IN HANDLE TokenHandle,
+ IN TOKEN_INFORMATION_CLASS TokenInformationClass,
+ OUT PVOID TokenInformation,
+ IN ULONG TokenInformationLength,
+ OUT PULONG ReturnLength);
+
+typedef NTSTATUS(WINAPI* RtlCreateUserThreadFunction)(
+ IN HANDLE Process,
+ IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
+ IN BOOLEAN CreateSuspended,
+ IN ULONG ZeroBits,
+ IN SIZE_T MaximumStackSize,
+ IN SIZE_T CommittedStackSize,
+ IN LPTHREAD_START_ROUTINE StartAddress,
+ IN PVOID Parameter,
+ OUT PHANDLE Thread,
+ OUT PCLIENT_ID ClientId);
+
+typedef NTSTATUS(WINAPI* RtlConvertSidToUnicodeStringFunction)(
+ OUT PUNICODE_STRING UnicodeString,
+ IN PSID Sid,
+ IN BOOLEAN AllocateDestinationString);
+
+typedef VOID(WINAPI* RtlFreeUnicodeStringFunction)(
+ IN OUT PUNICODE_STRING UnicodeString);
+
+// -----------------------------------------------------------------------
+// Registry
+
+typedef enum _KEY_INFORMATION_CLASS {
+ KeyBasicInformation = 0,
+ KeyFullInformation = 2
+} KEY_INFORMATION_CLASS,
+ *PKEY_INFORMATION_CLASS;
+
+typedef struct _KEY_BASIC_INFORMATION {
+ LARGE_INTEGER LastWriteTime;
+ ULONG TitleIndex;
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;
+
+typedef struct _KEY_FULL_INFORMATION {
+ LARGE_INTEGER LastWriteTime;
+ ULONG TitleIndex;
+ ULONG ClassOffset;
+ ULONG ClassLength;
+ ULONG SubKeys;
+ ULONG MaxNameLen;
+ ULONG MaxClassLen;
+ ULONG Values;
+ ULONG MaxValueNameLen;
+ ULONG MaxValueDataLen;
+ WCHAR Class[1];
+} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION;
+
+typedef enum _KEY_VALUE_INFORMATION_CLASS {
+ KeyValueFullInformation = 1
+} KEY_VALUE_INFORMATION_CLASS,
+ *PKEY_VALUE_INFORMATION_CLASS;
+
+typedef struct _KEY_VALUE_FULL_INFORMATION {
+ ULONG TitleIndex;
+ ULONG Type;
+ ULONG DataOffset;
+ ULONG DataLength;
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtCreateKeyFunction)(OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN ULONG TitleIndex,
+ IN PUNICODE_STRING Class OPTIONAL,
+ IN ULONG CreateOptions,
+ OUT PULONG Disposition OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtOpenKeyFunction)(OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes);
+
+typedef NTSTATUS(WINAPI* NtOpenKeyExFunction)(OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN DWORD open_options);
+
+typedef NTSTATUS(WINAPI* NtDeleteKeyFunction)(IN HANDLE KeyHandle);
+
+typedef NTSTATUS(WINAPI* RtlFormatCurrentUserKeyPathFunction)(
+ OUT PUNICODE_STRING RegistryPath);
+
+typedef NTSTATUS(WINAPI* NtQueryKeyFunction)(IN HANDLE KeyHandle,
+ IN KEY_INFORMATION_CLASS
+ KeyInformationClass,
+ OUT PVOID KeyInformation,
+ IN ULONG Length,
+ OUT PULONG ResultLength);
+
+typedef NTSTATUS(WINAPI* NtEnumerateKeyFunction)(IN HANDLE KeyHandle,
+ IN ULONG Index,
+ IN KEY_INFORMATION_CLASS
+ KeyInformationClass,
+ OUT PVOID KeyInformation,
+ IN ULONG Length,
+ OUT PULONG ResultLength);
+
+typedef NTSTATUS(WINAPI* NtQueryValueKeyFunction)(IN HANDLE KeyHandle,
+ IN PUNICODE_STRING ValueName,
+ IN KEY_VALUE_INFORMATION_CLASS
+ KeyValueInformationClass,
+ OUT PVOID KeyValueInformation,
+ IN ULONG Length,
+ OUT PULONG ResultLength);
+
+typedef NTSTATUS(WINAPI* NtSetValueKeyFunction)(IN HANDLE KeyHandle,
+ IN PUNICODE_STRING ValueName,
+ IN ULONG TitleIndex OPTIONAL,
+ IN ULONG Type,
+ IN PVOID Data,
+ IN ULONG DataSize);
+
+// -----------------------------------------------------------------------
+// Memory
+
+// Don't really need this structure right now.
+typedef PVOID PRTL_HEAP_PARAMETERS;
+
+typedef PVOID(WINAPI* RtlCreateHeapFunction)(IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN SIZE_T ReserveSize OPTIONAL,
+ IN SIZE_T CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters
+ OPTIONAL);
+
+typedef PVOID(WINAPI* RtlDestroyHeapFunction)(IN PVOID HeapHandle);
+
+typedef PVOID(WINAPI* RtlAllocateHeapFunction)(IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN SIZE_T Size);
+
+typedef BOOLEAN(WINAPI* RtlFreeHeapFunction)(IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID HeapBase);
+
+typedef NTSTATUS(WINAPI* NtAllocateVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN ULONG_PTR ZeroBits,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG AllocationType,
+ IN ULONG Protect);
+
+typedef NTSTATUS(WINAPI* NtFreeVirtualMemoryFunction)(IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG FreeType);
+
+typedef enum _MEMORY_INFORMATION_CLASS {
+ MemoryBasicInformation = 0,
+ MemoryWorkingSetList,
+ MemorySectionName,
+ MemoryBasicVlmInformation
+} MEMORY_INFORMATION_CLASS;
+
+typedef struct _MEMORY_SECTION_NAME { // Information Class 2
+ UNICODE_STRING SectionFileName;
+} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
+
+typedef NTSTATUS(WINAPI* NtQueryVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress,
+ IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
+ OUT PVOID MemoryInformation,
+ IN SIZE_T MemoryInformationLength,
+ OUT PSIZE_T ReturnLength OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtProtectVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN OUT PSIZE_T ProtectSize,
+ IN ULONG NewProtect,
+ OUT PULONG OldProtect);
+
+// -----------------------------------------------------------------------
+// Objects
+
+typedef enum _OBJECT_INFORMATION_CLASS {
+ ObjectBasicInformation,
+ ObjectNameInformation,
+ ObjectTypeInformation,
+ ObjectAllInformation,
+ ObjectDataInformation
+} OBJECT_INFORMATION_CLASS,
+ *POBJECT_INFORMATION_CLASS;
+
+typedef struct _OBJDIR_INFORMATION {
+ UNICODE_STRING ObjectName;
+ UNICODE_STRING ObjectTypeName;
+ BYTE Data[1];
+} OBJDIR_INFORMATION;
+
+typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG Reserved[10]; // reserved for internal use
+} PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION;
+
+typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING TypeName;
+ ULONG Reserved[22]; // reserved for internal use
+} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;
+
+typedef enum _POOL_TYPE {
+ NonPagedPool,
+ PagedPool,
+ NonPagedPoolMustSucceed,
+ ReservedType,
+ NonPagedPoolCacheAligned,
+ PagedPoolCacheAligned,
+ NonPagedPoolCacheAlignedMustS
+} POOL_TYPE;
+
+typedef struct _OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+ ULONG Reserved[3];
+ ULONG NameInformationLength;
+ ULONG TypeInformationLength;
+ ULONG SecurityDescriptorLength;
+ LARGE_INTEGER CreateTime;
+} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
+
+typedef struct _OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING Name;
+ ULONG TotalNumberOfObjects;
+ ULONG TotalNumberOfHandles;
+ ULONG TotalPagedPoolUsage;
+ ULONG TotalNonPagedPoolUsage;
+ ULONG TotalNamePoolUsage;
+ ULONG TotalHandleTableUsage;
+ ULONG HighWaterNumberOfObjects;
+ ULONG HighWaterNumberOfHandles;
+ ULONG HighWaterPagedPoolUsage;
+ ULONG HighWaterNonPagedPoolUsage;
+ ULONG HighWaterNamePoolUsage;
+ ULONG HighWaterHandleTableUsage;
+ ULONG InvalidAttributes;
+ GENERIC_MAPPING GenericMapping;
+ ULONG ValidAccess;
+ BOOLEAN SecurityRequired;
+ BOOLEAN MaintainHandleCount;
+ USHORT MaintainTypeList;
+ POOL_TYPE PoolType;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemHandleInformation = 16
+} SYSTEM_INFORMATION_CLASS;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION {
+ USHORT ProcessId;
+ USHORT CreatorBackTraceIndex;
+ UCHAR ObjectTypeNumber;
+ UCHAR Flags;
+ USHORT Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
+ ULONG NumberOfHandles;
+ SYSTEM_HANDLE_INFORMATION Information[1];
+} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
+
+typedef struct _OBJECT_NAME_INFORMATION {
+ UNICODE_STRING ObjectName;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryObjectFunction)(
+ IN HANDLE Handle,
+ IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ OUT PVOID ObjectInformation OPTIONAL,
+ IN ULONG ObjectInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtDuplicateObjectFunction)(IN HANDLE SourceProcess,
+ IN HANDLE SourceHandle,
+ IN HANDLE TargetProcess,
+ OUT PHANDLE TargetHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ IN ULONG Attributes,
+ IN ULONG Options);
+
+typedef NTSTATUS(WINAPI* NtSignalAndWaitForSingleObjectFunction)(
+ IN HANDLE HandleToSignal,
+ IN HANDLE HandleToWait,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtWaitForSingleObjectFunction)(
+ IN HANDLE ObjectHandle,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER TimeOut OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtQuerySystemInformation)(
+ IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ OUT PVOID SystemInformation,
+ IN ULONG SystemInformationLength,
+ OUT PULONG ReturnLength);
+
+typedef NTSTATUS(WINAPI* NtQueryObject)(IN HANDLE Handle,
+ IN OBJECT_INFORMATION_CLASS
+ ObjectInformationClass,
+ OUT PVOID ObjectInformation,
+ IN ULONG ObjectInformationLength,
+ OUT PULONG ReturnLength);
+
+// -----------------------------------------------------------------------
+// Strings
+
+typedef int(__cdecl* _strnicmpFunction)(IN const char* _Str1,
+ IN const char* _Str2,
+ IN size_t _MaxCount);
+
+typedef size_t(__cdecl* strlenFunction)(IN const char* _Str);
+
+typedef size_t(__cdecl* wcslenFunction)(IN const wchar_t* _Str);
+
+typedef void*(__cdecl* memcpyFunction)(IN void* dest,
+ IN const void* src,
+ IN size_t count);
+
+typedef NTSTATUS(WINAPI* RtlAnsiStringToUnicodeStringFunction)(
+ IN OUT PUNICODE_STRING DestinationString,
+ IN PANSI_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString);
+
+typedef LONG(WINAPI* RtlCompareUnicodeStringFunction)(
+ IN PCUNICODE_STRING String1,
+ IN PCUNICODE_STRING String2,
+ IN BOOLEAN CaseInSensitive);
+
+typedef VOID(WINAPI* RtlInitUnicodeStringFunction)(IN OUT PUNICODE_STRING
+ DestinationString,
+ IN PCWSTR SourceString);
+
+typedef ULONG(WINAPI* RtlNtStatusToDosErrorFunction)(NTSTATUS status);
+
+typedef enum _EVENT_TYPE {
+ NotificationEvent,
+ SynchronizationEvent
+} EVENT_TYPE,
+ *PEVENT_TYPE;
+
+typedef NTSTATUS(WINAPI* NtCreateDirectoryObjectFunction)(
+ PHANDLE DirectoryHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS(WINAPI* NtOpenDirectoryObjectFunction)(
+ PHANDLE DirectoryHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS(WINAPI* NtQuerySymbolicLinkObjectFunction)(
+ HANDLE LinkHandle,
+ PUNICODE_STRING LinkTarget,
+ PULONG ReturnedLength);
+
+typedef NTSTATUS(WINAPI* NtOpenSymbolicLinkObjectFunction)(
+ PHANDLE LinkHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+#define DIRECTORY_QUERY 0x0001
+#define DIRECTORY_TRAVERSE 0x0002
+#define DIRECTORY_CREATE_OBJECT 0x0004
+#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008
+#define DIRECTORY_ALL_ACCESS 0x000F
+
+typedef NTSTATUS(WINAPI* NtCreateLowBoxToken)(
+ OUT PHANDLE token,
+ IN HANDLE original_handle,
+ IN ACCESS_MASK access,
+ IN POBJECT_ATTRIBUTES object_attribute,
+ IN PSID appcontainer_sid,
+ IN DWORD capabilityCount,
+ IN PSID_AND_ATTRIBUTES capabilities,
+ IN DWORD handle_count,
+ IN PHANDLE handles);
+
+typedef NTSTATUS(WINAPI* NtSetInformationProcess)(IN HANDLE process_handle,
+ IN ULONG info_class,
+ IN PVOID process_information,
+ IN ULONG information_length);
+
+struct PROCESS_ACCESS_TOKEN {
+ HANDLE token;
+ HANDLE thread;
+};
+
+const unsigned int NtProcessInformationAccessToken = 9;
+
+typedef NTSTATUS(WINAPI* RtlDeriveCapabilitySidsFromNameFunction)(
+ PCUNICODE_STRING SourceString,
+ PSID CapabilityGroupSid,
+ PSID CapabilitySid);
+
+// -----------------------------------------------------------------------
+// GDI OPM API and Supported Calls
+
+#define DXGKMDT_OPM_OMAC_SIZE 16
+#define DXGKMDT_OPM_128_BIT_RANDOM_NUMBER_SIZE 16
+#define DXGKMDT_OPM_ENCRYPTED_PARAMETERS_SIZE 256
+#define DXGKMDT_OPM_CONFIGURE_SETTING_DATA_SIZE 4056
+#define DXGKMDT_OPM_GET_INFORMATION_PARAMETERS_SIZE 4056
+#define DXGKMDT_OPM_REQUESTED_INFORMATION_SIZE 4076
+#define DXGKMDT_OPM_HDCP_KEY_SELECTION_VECTOR_SIZE 5
+#define DXGKMDT_OPM_PROTECTION_TYPE_SIZE 4
+
+enum DXGKMDT_CERTIFICATE_TYPE {
+ DXGKMDT_OPM_CERTIFICATE = 0,
+ DXGKMDT_COPP_CERTIFICATE = 1,
+ DXGKMDT_UAB_CERTIFICATE = 2,
+ DXGKMDT_FORCE_ULONG = 0xFFFFFFFF
+};
+
+enum DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS {
+ DXGKMDT_OPM_VOS_COPP_SEMANTICS = 0,
+ DXGKMDT_OPM_VOS_OPM_SEMANTICS = 1
+};
+
+enum DXGKMDT_DPCP_PROTECTION_LEVEL {
+ DXGKMDT_OPM_DPCP_OFF = 0,
+ DXGKMDT_OPM_DPCP_ON = 1,
+ DXGKMDT_OPM_DPCP_FORCE_ULONG = 0x7fffffff
+};
+
+enum DXGKMDT_OPM_HDCP_PROTECTION_LEVEL {
+ DXGKMDT_OPM_HDCP_OFF = 0,
+ DXGKMDT_OPM_HDCP_ON = 1,
+ DXGKMDT_OPM_HDCP_FORCE_ULONG = 0x7fffffff
+};
+
+enum DXGKMDT_OPM_HDCP_FLAG {
+ DXGKMDT_OPM_HDCP_FLAG_NONE = 0x00,
+ DXGKMDT_OPM_HDCP_FLAG_REPEATER = 0x01
+};
+
+enum DXGKMDT_OPM_PROTECTION_TYPE {
+ DXGKMDT_OPM_PROTECTION_TYPE_OTHER = 0x80000000,
+ DXGKMDT_OPM_PROTECTION_TYPE_NONE = 0x00000000,
+ DXGKMDT_OPM_PROTECTION_TYPE_COPP_COMPATIBLE_HDCP = 0x00000001,
+ DXGKMDT_OPM_PROTECTION_TYPE_ACP = 0x00000002,
+ DXGKMDT_OPM_PROTECTION_TYPE_CGMSA = 0x00000004,
+ DXGKMDT_OPM_PROTECTION_TYPE_HDCP = 0x00000008,
+ DXGKMDT_OPM_PROTECTION_TYPE_DPCP = 0x00000010,
+ DXGKMDT_OPM_PROTECTION_TYPE_MASK = 0x8000001F
+};
+
+typedef void* OPM_PROTECTED_OUTPUT_HANDLE;
+
+struct DXGKMDT_OPM_ENCRYPTED_PARAMETERS {
+ BYTE abEncryptedParameters[DXGKMDT_OPM_ENCRYPTED_PARAMETERS_SIZE];
+};
+
+struct DXGKMDT_OPM_OMAC {
+ BYTE abOMAC[DXGKMDT_OPM_OMAC_SIZE];
+};
+
+struct DXGKMDT_OPM_CONFIGURE_PARAMETERS {
+ DXGKMDT_OPM_OMAC omac;
+ GUID guidSetting;
+ ULONG ulSequenceNumber;
+ ULONG cbParametersSize;
+ BYTE abParameters[DXGKMDT_OPM_CONFIGURE_SETTING_DATA_SIZE];
+};
+
+struct DXGKMDT_OPM_RANDOM_NUMBER {
+ BYTE abRandomNumber[DXGKMDT_OPM_128_BIT_RANDOM_NUMBER_SIZE];
+};
+
+struct DXGKMDT_OPM_GET_INFO_PARAMETERS {
+ DXGKMDT_OPM_OMAC omac;
+ DXGKMDT_OPM_RANDOM_NUMBER rnRandomNumber;
+ GUID guidInformation;
+ ULONG ulSequenceNumber;
+ ULONG cbParametersSize;
+ BYTE abParameters[DXGKMDT_OPM_GET_INFORMATION_PARAMETERS_SIZE];
+};
+
+struct DXGKMDT_OPM_REQUESTED_INFORMATION {
+ DXGKMDT_OPM_OMAC omac;
+ ULONG cbRequestedInformationSize;
+ BYTE abRequestedInformation[DXGKMDT_OPM_REQUESTED_INFORMATION_SIZE];
+};
+
+struct DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS {
+ ULONG ulProtectionType;
+ ULONG ulProtectionLevel;
+ ULONG Reserved;
+ ULONG Reserved2;
+};
+
+struct DXGKMDT_OPM_STANDARD_INFORMATION {
+ DXGKMDT_OPM_RANDOM_NUMBER rnRandomNumber;
+ ULONG ulStatusFlags;
+ ULONG ulInformation;
+ ULONG ulReserved;
+ ULONG ulReserved2;
+};
+
+typedef NTSTATUS(WINAPI* GetSuggestedOPMProtectedOutputArraySizeFunction)(
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size);
+
+typedef NTSTATUS(WINAPI* CreateOPMProtectedOutputsFunction)(
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD output_array_size,
+ DWORD* num_in_output_array,
+ OPM_PROTECTED_OUTPUT_HANDLE* output_array);
+
+typedef NTSTATUS(WINAPI* GetCertificateFunction)(
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+typedef NTSTATUS(WINAPI* GetCertificateSizeFunction)(
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+typedef NTSTATUS(WINAPI* GetCertificateByHandleFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+typedef NTSTATUS(WINAPI* GetCertificateSizeByHandleFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+typedef NTSTATUS(WINAPI* DestroyOPMProtectedOutputFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output);
+
+typedef NTSTATUS(WINAPI* ConfigureOPMProtectedOutputFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters);
+
+typedef NTSTATUS(WINAPI* GetOPMInformationFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information);
+
+typedef NTSTATUS(WINAPI* GetOPMRandomNumberFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number);
+
+typedef NTSTATUS(WINAPI* SetOPMSigningKeyAndSequenceNumbersFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters);
+
+#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
new file mode 100644
index 0000000000..406057acbb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_broker.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_broker.h"
+
+#include <stddef.h>
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_process.h"
+
+// This code executes on the broker side, as a callback from the policy on the
+// target side (the child).
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+#define INIT_GLOBAL_NT(member) \
+ g_nt.member = reinterpret_cast<Nt##member##Function>( \
+ ntdll_image.GetProcAddress("Nt" #member)); \
+ if (!g_nt.member) \
+ return false
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.member = \
+ reinterpret_cast<member##Function>(ntdll_image.GetProcAddress(#member)); \
+ if (!g_nt.member) \
+ return false
+
+bool InitGlobalNt() {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ base::win::PEImage ntdll_image(ntdll);
+
+ INIT_GLOBAL_NT(AllocateVirtualMemory);
+ INIT_GLOBAL_NT(Close);
+ INIT_GLOBAL_NT(DuplicateObject);
+ INIT_GLOBAL_NT(FreeVirtualMemory);
+ INIT_GLOBAL_NT(MapViewOfSection);
+ INIT_GLOBAL_NT(ProtectVirtualMemory);
+ INIT_GLOBAL_NT(QueryInformationProcess);
+ INIT_GLOBAL_NT(QueryObject);
+ INIT_GLOBAL_NT(QuerySection);
+ INIT_GLOBAL_NT(QueryVirtualMemory);
+ INIT_GLOBAL_NT(UnmapViewOfSection);
+ INIT_GLOBAL_NT(SignalAndWaitForSingleObject);
+ INIT_GLOBAL_NT(WaitForSingleObject);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlCreateUserThread);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+ INIT_GLOBAL_RTL(memcpy);
+
+ 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++)
+ DCHECK(reinterpret_cast<char**>(&g_nt)[i]);
+#endif
+ return (SBOX_ALL_OK == child->TransferVariable("g_nt", &g_nt, sizeof(g_nt)));
+}
+
+#undef INIT_GLOBAL_NT
+#undef INIT_GLOBAL_RTL
+
+bool SetupBasicInterceptions(InterceptionManager* manager,
+ bool is_csrss_connected) {
+ // Interceptions provided by process_thread_policy, without actual policy.
+ if (!INTERCEPT_NT(manager, NtOpenThread, OPEN_THREAD_ID, 20) ||
+ !INTERCEPT_NT(manager, NtOpenProcess, OPEN_PROCESS_ID, 20) ||
+ !INTERCEPT_NT(manager, NtOpenProcessToken, OPEN_PROCESS_TOKEN_ID, 16))
+ return false;
+
+ // Interceptions with neither policy nor IPC.
+ if (!INTERCEPT_NT(manager, NtSetInformationThread, SET_INFORMATION_THREAD_ID,
+ 20) ||
+ !INTERCEPT_NT(manager, NtOpenThreadToken, OPEN_THREAD_TOKEN_ID, 20))
+ return false;
+
+ // This one is also provided by process_thread_policy.
+ if (!INTERCEPT_NT(manager, NtOpenProcessTokenEx, OPEN_PROCESS_TOKEN_EX_ID,
+ 20))
+ return false;
+
+ if (!INTERCEPT_NT(manager, NtOpenThreadTokenEx, OPEN_THREAD_TOKEN_EX_ID, 24))
+ return false;
+
+ if (!is_csrss_connected) {
+ if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread, CREATE_THREAD_ID,
+ 28))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_broker.h b/security/sandbox/chromium/sandbox/win/src/policy_broker.h
new file mode 100644
index 0000000000..95b123bd0d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_broker.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_POLICY_BROKER_H_
+#define SANDBOX_SRC_POLICY_BROKER_H_
+
+#include "sandbox/win/src/interception.h"
+
+namespace sandbox {
+
+class TargetProcess;
+
+// Initializes global imported symbols from ntdll.
+bool InitGlobalNt();
+
+// Sets up interceptions not controlled by explicit policies.
+bool SetupBasicInterceptions(InterceptionManager* manager,
+ bool is_csrss_connected);
+
+// Sets up imports from NTDLL for the given target process so the interceptions
+// can work.
+bool SetupNtdllImports(TargetProcess* child);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_BROKER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
new file mode 100644
index 0000000000..02c636f4aa
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
@@ -0,0 +1,450 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_engine_opcodes.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+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<wchar_t*>(source);
+ ustring->Length = static_cast<USHORT>(length) * sizeof(wchar_t);
+ ustring->MaximumLength = source ? ustring->Length + sizeof(wchar_t) : 0;
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Note: The opcodes are implemented as functions (as opposed to classes derived
+// from PolicyOpcode) because you should not add more member variables to the
+// PolicyOpcode class since it would cause object slicing on the target. So to
+// enforce that (instead of just trusting the developer) the opcodes became
+// just functions.
+//
+// In the code that follows I have keep the evaluation function and the factory
+// function together to stress the close relationship between both. For example,
+// only the factory method and the evaluation function know the stored argument
+// order and meaning.
+
+template <int>
+EvalResult OpcodeEval(PolicyOpcode* opcode,
+ const ParameterSet* pp,
+ MatchContext* match);
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAlwaysFalse:
+// Does not require input parameter.
+
+PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32_t options) {
+ return MakeBase(OP_ALWAYS_FALSE, options, -1);
+}
+
+template <>
+EvalResult OpcodeEval<OP_ALWAYS_FALSE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ return EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAlwaysTrue:
+// Does not require input parameter.
+
+PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32_t options) {
+ return MakeBase(OP_ALWAYS_TRUE, options, -1);
+}
+
+template <>
+EvalResult OpcodeEval<OP_ALWAYS_TRUE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ return EVAL_TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAction:
+// Does not require input parameter.
+// Argument 0 contains the actual action to return.
+
+PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_ACTION, options, -1);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, action);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_ACTION>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ int action = 0;
+ opcode->GetArgument(0, &action);
+ return static_cast<EvalResult>(action);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberMatch:
+// 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_t selected_param,
+ uint32_t match,
+ uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, UINT32_TYPE);
+ return opcode;
+}
+
+PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16_t selected_param,
+ const void* match,
+ uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, VOIDPTR_TYPE);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ uint32_t value_uint32 = 0;
+ if (param->Get(&value_uint32)) {
+ uint32_t match_uint32 = 0;
+ opcode->GetArgument(0, &match_uint32);
+ return (match_uint32 != value_uint32) ? EVAL_FALSE : EVAL_TRUE;
+ } else {
+ const void* value_ptr = nullptr;
+ if (param->Get(&value_ptr)) {
+ const void* match_ptr = nullptr;
+ opcode->GetArgument(0, &match_ptr);
+ return (match_ptr != value_ptr) ? EVAL_FALSE : EVAL_TRUE;
+ }
+ }
+ return EVAL_ERROR;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberMatchRange
+// 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_t selected_param,
+ uint32_t lower_bound,
+ uint32_t upper_bound,
+ uint32_t options) {
+ if (lower_bound > upper_bound) {
+ return nullptr;
+ }
+ PolicyOpcode* opcode =
+ MakeBase(OP_NUMBER_MATCH_RANGE, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, lower_bound);
+ opcode->SetArgument(1, upper_bound);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_MATCH_RANGE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ uint32_t value = 0;
+ if (!param->Get(&value))
+ return EVAL_ERROR;
+
+ 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)) ? EVAL_TRUE
+ : EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberAndMatch:
+// Requires a uint32_t in selected_param.
+// Argument 0 is the stored number to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberAndMatch(int16_t selected_param,
+ uint32_t match,
+ uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_AND_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, match);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_AND_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ uint32_t value = 0;
+ if (!param->Get(&value))
+ return EVAL_ERROR;
+
+ uint32_t number = 0;
+ opcode->GetArgument(0, &number);
+ return (number & value) ? EVAL_TRUE : EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpWStringMatch:
+// Requires a wchar_t* in selected_param.
+// Argument 0 is the byte displacement of the stored string.
+// Argument 1 is the length in chars of the stored string.
+// Argument 2 is the offset to apply on the input string. It has special values.
+// as noted in the header file.
+// Argument 3 is the string matching options.
+
+PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16_t selected_param,
+ const wchar_t* match_str,
+ int start_position,
+ StringMatchOptions match_opts,
+ uint32_t options) {
+ if (!match_str)
+ return nullptr;
+ if ('\0' == match_str[0])
+ return nullptr;
+
+ int length = lstrlenW(match_str);
+
+ PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str) + 1);
+ if (0 == delta_str)
+ return nullptr;
+ opcode->SetArgument(0, delta_str);
+ opcode->SetArgument(1, length);
+ opcode->SetArgument(2, start_position);
+ opcode->SetArgument(3, match_opts);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_WSTRING_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ if (!context) {
+ return EVAL_ERROR;
+ }
+ const wchar_t* source_str = nullptr;
+ if (!param->Get(&source_str))
+ return EVAL_ERROR;
+
+ int start_position = 0;
+ int match_len = 0;
+ unsigned int match_opts = 0;
+ opcode->GetArgument(1, &match_len);
+ opcode->GetArgument(2, &start_position);
+ opcode->GetArgument(3, &match_opts);
+
+ const wchar_t* match_str = opcode->GetRelativeString(0);
+ // Advance the source string to the last successfully evaluated position
+ // according to the match context.
+ source_str = &source_str[context->position];
+ int source_len = static_cast<int>(g_nt.wcslen(source_str));
+
+ if (0 == source_len) {
+ // If we reached the end of the source string there is nothing we can
+ // match against.
+ return EVAL_FALSE;
+ }
+ if (match_len > source_len) {
+ // There can't be a positive match when the target string is bigger than
+ // the source string
+ return EVAL_FALSE;
+ }
+
+ BOOLEAN case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE;
+
+ // We have three cases, depending on the value of start_pos:
+ // Case 1. We skip N characters and compare once.
+ // Case 2: We skip to the end and compare once.
+ // Case 3: We match the first substring (if we find any).
+ if (start_position >= 0) {
+ if (kSeekToEnd == start_position) {
+ start_position = source_len - match_len;
+ } else if (match_opts & EXACT_LENGTH) {
+ // A sub-case of case 3 is when the EXACT_LENGTH flag is on
+ // the match needs to be not just substring but full match.
+ if ((match_len + start_position) != source_len) {
+ return EVAL_FALSE;
+ }
+ }
+
+ // Advance start_pos characters. Warning! this does not consider
+ // utf16 encodings (surrogate pairs) or other Unicode 'features'.
+ source_str += start_position;
+
+ // Since we skipped, lets reevaluate just the lengths again.
+ if ((match_len + start_position) > source_len) {
+ return EVAL_FALSE;
+ }
+
+ UNICODE_STRING match_ustr;
+ UNICODE_STRING 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)) {
+ // Match! update the match context.
+ context->position += start_position + match_len;
+ return EVAL_TRUE;
+ } else {
+ return EVAL_FALSE;
+ }
+ } else if (start_position < 0) {
+ UNICODE_STRING match_ustr;
+ UNICODE_STRING 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,
+ case_sensitive)) {
+ // Match! update the match context.
+ context->position += (source_ustr.Buffer - source_str) + match_len;
+ return EVAL_TRUE;
+ }
+ ++source_ustr.Buffer;
+ --source_len;
+ } while (source_len >= match_len);
+ }
+ return EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// OpcodeMaker (other member functions).
+
+PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id,
+ uint32_t options,
+ int16_t selected_param) {
+ if (memory_size() < sizeof(PolicyOpcode))
+ return nullptr;
+
+ // Create opcode using placement-new on the buffer memory.
+ PolicyOpcode* opcode = new (memory_top_) PolicyOpcode();
+
+ // Fill in the standard fields, that every opcode has.
+ memory_top_ += sizeof(PolicyOpcode);
+ opcode->opcode_id_ = opcode_id;
+ opcode->SetOptions(options);
+ opcode->parameter_ = selected_param;
+ return opcode;
+}
+
+ptrdiff_t OpcodeFactory::AllocRelative(void* start,
+ const wchar_t* str,
+ size_t length) {
+ size_t bytes = length * sizeof(wchar_t);
+ if (memory_size() < bytes)
+ return 0;
+ memory_bottom_ -= bytes;
+ if (reinterpret_cast<UINT_PTR>(memory_bottom_) & 1) {
+ // TODO(cpu) replace this for something better.
+ ::DebugBreak();
+ }
+ memcpy(memory_bottom_, str, bytes);
+ ptrdiff_t delta = memory_bottom_ - reinterpret_cast<char*>(start);
+ return delta;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode evaluation dispatchers.
+
+// This function is the one and only entry for evaluating any opcode. It is
+// in charge of applying any relevant opcode options and calling EvaluateInner
+// were the actual dispatch-by-id is made. It would seem at first glance that
+// the dispatch should be done by virtual function (vtable) calls but you have
+// to remember that the opcodes are made in the broker process and copied as
+// raw memory to the target process.
+
+EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params,
+ size_t param_count,
+ MatchContext* match) {
+ if (!call_params)
+ return EVAL_ERROR;
+ const ParameterSet* selected_param = nullptr;
+ if (parameter_ >= 0) {
+ if (static_cast<size_t>(parameter_) >= param_count) {
+ return EVAL_ERROR;
+ }
+ selected_param = &call_params[parameter_];
+ }
+ EvalResult result = EvaluateHelper(selected_param, match);
+
+ // Apply the general options regardless of the particular type of opcode.
+ if (kPolNone == options_) {
+ return result;
+ }
+
+ if (options_ & kPolNegateEval) {
+ if (EVAL_TRUE == result) {
+ result = EVAL_FALSE;
+ } else if (EVAL_FALSE == result) {
+ result = EVAL_TRUE;
+ } else if (EVAL_ERROR != result) {
+ result = EVAL_ERROR;
+ }
+ }
+ if (match) {
+ if (options_ & kPolClearContext)
+ match->Clear();
+ if (options_ & kPolUseOREval)
+ match->options = kPolUseOREval;
+ }
+ return result;
+}
+
+#define OPCODE_EVAL(op, x, y, z) \
+ case op: \
+ return OpcodeEval<op>(x, y, z)
+
+EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters,
+ MatchContext* match) {
+ switch (opcode_id_) {
+ OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match);
+ OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_MATCH_RANGE, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_AND_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_ACTION, this, parameters, match);
+ default:
+ return EVAL_ERROR;
+ }
+}
+
+#undef OPCODE_EVAL
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h
new file mode 100644
index 0000000000..8e8816df1c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h
@@ -0,0 +1,379 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
+#define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "sandbox/win/src/policy_engine_params.h"
+
+// The low-level policy is implemented using the concept of policy 'opcodes'.
+// An opcode is a structure that contains enough information to perform one
+// comparison against one single input parameter. For example, an opcode can
+// encode just one of the following comparison:
+//
+// - Is input parameter 3 not equal to nullptr?
+// - Does input parameter 2 start with L"c:\\"?
+// - Is input parameter 5, bit 3 is equal 1?
+//
+// Each opcode is in fact equivalent to a function invocation where all
+// the parameters are known by the opcode except one. So say you have a
+// function of this form:
+// bool fn(a, b, c, d) with 4 arguments
+//
+// Then an opcode is:
+// op(fn, b, c, d)
+// Which stores the function to call and its 3 last arguments
+//
+// Then and opcode evaluation is:
+// op.eval(a) ------------------------> fn(a,b,c,d)
+// internally calls
+//
+// The idea is that complex policy rules can be split into streams of
+// opcodes which are evaluated in sequence. The evaluation is done in
+// groups of opcodes that have N comparison opcodes plus 1 action opcode:
+//
+// [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
+// ----- evaluation order----------->
+//
+// Each opcode group encodes one high-level policy rule. The rule applies
+// only if all the conditions on the group evaluate to true. The action
+// opcode contains the policy outcome for that particular rule.
+//
+// Note that this header contains the main building blocks of low-level policy
+// but not the low level policy class.
+namespace sandbox {
+
+// These are the possible policy outcomes. Note that some of them might
+// not apply and can be removed. Also note that The following values only
+// specify what to do, not how to do it and it is acceptable given specific
+// cases to ignore the policy outcome.
+enum EvalResult {
+ // Comparison opcode values:
+ EVAL_TRUE, // Opcode condition evaluated true.
+ EVAL_FALSE, // Opcode condition evaluated false.
+ EVAL_ERROR, // Opcode condition generated an error while evaluating.
+ // Action opcode values:
+ ASK_BROKER, // The target must generate an IPC to the broker. On the broker
+ // side, this means grant access to the resource.
+ DENY_ACCESS, // No access granted to the resource.
+ GIVE_READONLY, // Give readonly access to the resource.
+ GIVE_ALLACCESS, // Give full access to the resource.
+ GIVE_CACHED, // IPC is not required. Target can return a cached handle.
+ GIVE_FIRST, // TODO(cpu)
+ SIGNAL_ALARM, // Unusual activity. Generate an alarm.
+ FAKE_SUCCESS, // Do not call original function. Just return 'success'.
+ FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied'
+ // and do not do IPC.
+ TERMINATE_PROCESS, // Destroy target process. Do IPC as well.
+};
+
+// The following are the implemented opcodes.
+enum OpcodeID {
+ OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE).
+ OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE).
+ OP_NUMBER_MATCH, // Match a 32-bit integer as n == a.
+ OP_NUMBER_MATCH_RANGE, // Match a 32-bit integer as a <= n <= b.
+ OP_NUMBER_AND_MATCH, // Match using bitwise AND; as in: n & a != 0.
+ OP_WSTRING_MATCH, // Match a string for equality.
+ OP_ACTION // Evaluates to an action opcode.
+};
+
+// Options that apply to every opcode. They are specified when creating
+// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
+// Do nothing special.
+const uint32_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_t kPolNegateEval = 1;
+
+// Zero the MatchContext context structure. This happens after the opcode
+// is evaluated.
+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_t kPolUseOREval = 4;
+
+// Keeps the evaluation state between opcode evaluations. This is used
+// for string matching where the next opcode needs to continue matching
+// from the last character position from the current opcode. The match
+// context is preserved across opcode evaluation unless an opcode specifies
+// as an option kPolClearContext.
+struct MatchContext {
+ size_t position;
+ uint32_t options;
+
+ MatchContext() { Clear(); }
+
+ void Clear() {
+ position = 0;
+ options = 0;
+ }
+};
+
+// Models a policy opcode; that is a condition evaluation were all the
+// arguments but one are stored in objects of this class. Use OpcodeFactory
+// to create objects of this type.
+// This class is just an implementation artifact and not exposed to the
+// API clients or visible in the intercepted service. Internally, an
+// opcode is just:
+// - An integer that identifies the actual opcode.
+// - An index to indicate which one is the input argument
+// - An array of arguments.
+// While an OO hierarchy of objects would have been a natural choice, the fact
+// that 1) this code can execute before the CRT is loaded, presents serious
+// problems in terms of guarantees about the actual state of the vtables and
+// 2) because the opcode objects are generated in the broker process, we need to
+// use plain objects. To preserve some minimal type safety templates are used
+// when possible.
+class PolicyOpcode {
+ friend class OpcodeFactory;
+
+ public:
+ // Evaluates the opcode. For a typical comparison opcode the return value
+ // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the
+ // the return is EVAL_ERROR. If the opcode is an action opcode then the
+ // return can take other values such as ASK_BROKER.
+ // parameters: An array of all input parameters. This argument is normally
+ // created by the macros POLPARAMS_BEGIN() POLPARAMS_END.
+ // count: The number of parameters passed as first argument.
+ // match: The match context that is persisted across the opcode evaluation
+ // sequence.
+ EvalResult Evaluate(const ParameterSet* parameters,
+ size_t count,
+ MatchContext* match);
+
+ // Retrieves a stored argument by index. Valid index values are
+ // from 0 to < kArgumentCount.
+ template <typename T>
+ void GetArgument(size_t index, T* argument) const {
+ static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size");
+ *argument = *reinterpret_cast<const T*>(&arguments_[index].mem);
+ }
+
+ // Sets a stored argument by index. Valid index values are
+ // from 0 to < kArgumentCount.
+ template <typename T>
+ void SetArgument(size_t index, const T& argument) {
+ static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size");
+ *reinterpret_cast<T*>(&arguments_[index].mem) = argument;
+ }
+
+ // Retrieves the actual address of a string argument. When using
+ // GetArgument() to retrieve an index that contains a string, the returned
+ // value is just an offset to the actual string.
+ // index: the stored string index. Valid values are from 0
+ // to < kArgumentCount.
+ const wchar_t* GetRelativeString(size_t index) const {
+ ptrdiff_t str_delta = 0;
+ GetArgument(index, &str_delta);
+ const char* delta = reinterpret_cast<const char*>(this) + str_delta;
+ return reinterpret_cast<const wchar_t*>(delta);
+ }
+
+ // Returns true if this opcode is an action opcode without actually
+ // evaluating it. Used to do a quick scan forward to the next opcode group.
+ bool IsAction() const { return (OP_ACTION == opcode_id_); }
+
+ // Returns the opcode type.
+ OpcodeID GetID() const { return opcode_id_; }
+
+ // Returns the stored options such as kPolNegateEval and others.
+ uint32_t GetOptions() const { return options_; }
+
+ // Sets the stored options such as kPolNegateEval.
+ void SetOptions(uint32_t options) { options_ = options; }
+
+ // Returns the parameter of the function the opcode concerns.
+ uint16_t GetParameter() const { return parameter_; }
+
+ private:
+ static const size_t kArgumentCount = 4; // The number of supported argument.
+
+ struct OpcodeArgument {
+ UINT_PTR mem;
+ };
+
+ // Better define placement new in the class instead of relying on the
+ // global definition which seems to be fubared.
+ void* operator new(size_t, void* location) { return location; }
+
+ // Helper function to evaluate the opcode. The parameters have the same
+ // meaning that in Evaluate().
+ EvalResult EvaluateHelper(const ParameterSet* parameters,
+ MatchContext* match);
+ OpcodeID opcode_id_;
+ int16_t parameter_;
+ uint32_t options_;
+ OpcodeArgument arguments_[PolicyOpcode::kArgumentCount];
+};
+
+enum StringMatchOptions {
+ CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
+ CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
+ EXACT_LENGTH = 2 // Don't do substring match. Do full string match.
+};
+
+// Opcodes that do string comparisons take a parameter that is the starting
+// position to perform the comparison so we can do substring matching. There
+// are two special values:
+//
+// Start from the current position and compare strings advancing forward until
+// a match is found if any. Similar to CRT strstr().
+const int kSeekForward = -1;
+// Perform a match with the end of the string. It only does a single comparison.
+const int kSeekToEnd = 0xfffff;
+
+// A PolicyBuffer is a variable size structure that contains all the opcodes
+// that are to be created or evaluated in sequence.
+struct PolicyBuffer {
+ size_t opcode_count;
+ PolicyOpcode opcodes[1];
+};
+
+// Helper class to create any opcode sequence. This class is normally invoked
+// only by the high level policy module or when you need to handcraft a special
+// policy.
+// The factory works by creating the opcodes using a chunk of memory given
+// in the constructor. The opcodes themselves are allocated from the beginning
+// (top) of the memory, while any string that an opcode needs is allocated from
+// the end (bottom) of the memory.
+//
+// In essence:
+//
+// low address ---> [opcode 1]
+// [opcode 2]
+// [opcode 3]
+// | | <--- memory_top_
+// | free |
+// | |
+// | | <--- memory_bottom_
+// [string 1]
+// high address --> [string 2]
+//
+// Note that this class does not keep track of the number of opcodes made and
+// it is designed to be a building block for low-level policy.
+//
+// Note that any of the MakeOpXXXXX member functions below can return nullptr on
+// failure. When that happens opcode sequence creation must be aborted.
+class OpcodeFactory {
+ public:
+ // memory: base pointer to a chunk of memory where the opcodes are created.
+ // memory_size: the size in bytes of the memory chunk.
+ OpcodeFactory(char* memory, size_t memory_size) : memory_top_(memory) {
+ memory_bottom_ = &memory_top_[memory_size];
+ }
+
+ // policy: contains the raw memory where the opcodes are created.
+ // memory_size: contains the actual size of the policy argument.
+ OpcodeFactory(PolicyBuffer* policy, size_t memory_size) {
+ memory_top_ = reinterpret_cast<char*>(&policy->opcodes[0]);
+ memory_bottom_ = &memory_top_[memory_size];
+ }
+
+ // Returns the available memory to make opcodes.
+ size_t memory_size() const {
+ DCHECK_GE(memory_bottom_, memory_top_);
+ return memory_bottom_ - memory_top_;
+ }
+
+ // Creates an OpAlwaysFalse opcode.
+ PolicyOpcode* MakeOpAlwaysFalse(uint32_t options);
+
+ // Creates an OpAlwaysFalse opcode.
+ PolicyOpcode* MakeOpAlwaysTrue(uint32_t options);
+
+ // Creates an OpAction opcode.
+ // action: The action to return when Evaluate() is called.
+ PolicyOpcode* MakeOpAction(EvalResult action, uint32_t options);
+
+ // Creates an OpNumberMatch opcode.
+ // 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_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_t selected_param,
+ const void* match,
+ 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_t or the
+ // evaluation result will generate a EVAL_ERROR.
+ // lower_bound, upper_bound: the range to compare against selected_param.
+ 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
+ // pointer or the evaluation result will generate a EVAL_ERROR.
+ // match_str: string to compare against selected_param.
+ // start_position: when its value is from 0 to < 0x7fff it indicates an
+ // offset from the selected_param string where to perform the comparison. If
+ // the value is SeekForward then a substring search is performed. If the
+ // value is SeekToEnd the comparison is performed against the last part of
+ // the selected_param string.
+ // Note that the range in the position (0 to 0x7fff) is dictated by the
+ // current implementation.
+ // match_opts: Indicates additional matching flags. Currently CaseInsensitive
+ // is supported.
+ PolicyOpcode* MakeOpWStringMatch(int16_t selected_param,
+ const wchar_t* match_str,
+ int start_position,
+ StringMatchOptions match_opts,
+ 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_t or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the value to bitwise AND against selected_param.
+ 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_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.
+ ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t length);
+
+ // Points to the lowest currently available address of the memory
+ // used to make the opcodes. This pointer increments as opcodes are made.
+ char* memory_top_;
+
+ // Points to the highest currently available address of the memory
+ // used to make the opcodes. This pointer decrements as opcode strings are
+ // allocated.
+ char* memory_bottom_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpcodeFactory);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h
new file mode 100644
index 0000000000..07fd7eac82
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h
@@ -0,0 +1,190 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
+#define SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
+
+#include <stdint.h>
+
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+// This header defines the classes that allow the low level policy to select
+// the input parameters. In order to better make sense of this header is
+// recommended that you check policy_engine_opcodes.h first.
+
+namespace sandbox {
+
+// Models the set of interesting parameters of an intercepted system call
+// normally you don't create objects of this class directly, instead you
+// use the POLPARAMS_XXX macros.
+// For example, if an intercepted function has the following signature:
+//
+// NTSTATUS NtOpenFileFunction (PHANDLE FileHandle,
+// ACCESS_MASK DesiredAccess,
+// POBJECT_ATTRIBUTES ObjectAttributes,
+// PIO_STATUS_BLOCK IoStatusBlock,
+// ULONG ShareAccess,
+// ULONG OpenOptions);
+//
+// You could say that the following parameters are of interest to policy:
+//
+// POLPARAMS_BEGIN(open_params)
+// POLPARAM(DESIRED_ACCESS)
+// POLPARAM(OBJECT_NAME)
+// POLPARAM(SECURITY_DESCRIPTOR)
+// POLPARAM(IO_STATUS)
+// POLPARAM(OPEN_OPTIONS)
+// POLPARAMS_END;
+//
+// and the actual code will use this for defining the parameters:
+//
+// CountedParameterSet<open_params> p;
+// p[open_params::DESIRED_ACCESS] = ParamPickerMake(DesiredAccess);
+// p[open_params::OBJECT_NAME] =
+// ParamPickerMake(ObjectAttributes->ObjectName);
+// p[open_params::SECURITY_DESCRIPTOR] =
+// ParamPickerMake(ObjectAttributes->SecurityDescriptor);
+// p[open_params::IO_STATUS] = ParamPickerMake(IoStatusBlock);
+// p[open_params::OPEN_OPTIONS] = ParamPickerMake(OpenOptions);
+//
+// These will create an stack-allocated array of ParameterSet objects which
+// have each 1) the address of the parameter 2) a numeric id that encodes the
+// original C++ type. This allows the policy to treat any set of supported
+// argument types uniformily and with some type safety.
+//
+// TODO(cpu): support not fully implemented yet for unicode string and will
+// probably add other types as well.
+class ParameterSet {
+ public:
+ ParameterSet() : real_type_(INVALID_TYPE), address_(nullptr) {}
+
+ // Retrieve the stored parameter. If the type does not match ulong fail.
+ bool Get(uint32_t* destination) const {
+ if (real_type_ != UINT32_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<uint32_t>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match void* fail.
+ bool Get(const void** destination) const {
+ if (real_type_ != VOIDPTR_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<void*>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match wchar_t* fail.
+ bool Get(const wchar_t** destination) const {
+ if (real_type_ != WCHAR_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<const wchar_t*>();
+ return true;
+ }
+
+ // False if the parameter is not properly initialized.
+ bool IsValid() const { return real_type_ != INVALID_TYPE; }
+
+ protected:
+ // The constructor can only be called by derived types, which should
+ // safely provide the real_type and the address of the argument.
+ ParameterSet(ArgType real_type, const void* address)
+ : real_type_(real_type), address_(address) {}
+
+ private:
+ // This template provides the same functionality as bits_cast but
+ // it works with pointer while the former works only with references.
+ template <typename T>
+ T Void2TypePointerCopy() const {
+ return *(reinterpret_cast<const T*>(address_));
+ }
+
+ ArgType real_type_;
+ const void* address_;
+};
+
+// To safely infer the type, we use a set of template specializations
+// in ParameterSetEx with a template function ParamPickerMake to do the
+// parameter type deduction.
+
+// Base template class. Not implemented so using unsupported types should
+// fail to compile.
+template <typename T>
+class ParameterSetEx : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address);
+};
+
+template <>
+class ParameterSetEx<void const*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(VOIDPTR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<void*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(VOIDPTR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<wchar_t*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(WCHAR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<wchar_t const*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(WCHAR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<uint32_t> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(UINT32_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<UNICODE_STRING> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(UNISTR_TYPE, address) {}
+};
+
+template <typename T>
+ParameterSet ParamPickerMake(T& parameter) {
+ return ParameterSetEx<T>(&parameter);
+}
+
+struct CountedParameterSetBase {
+ size_t count;
+ ParameterSet parameters[1];
+};
+
+// This template defines the actual list of policy parameters for a given
+// interception.
+// Warning: This template stores the address to the actual variables, in
+// other words, the values are not copied.
+template <typename T>
+struct CountedParameterSet {
+ CountedParameterSet() : count(T::PolParamLast) {}
+
+ ParameterSet& operator[](typename T::Args n) { return parameters[n]; }
+
+ CountedParameterSetBase* GetBase() {
+ return reinterpret_cast<CountedParameterSetBase*>(this);
+ }
+
+ size_t count;
+ ParameterSet parameters[T::PolParamLast];
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc
new file mode 100644
index 0000000000..07969f31a8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_engine_processor.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace sandbox {
+
+void PolicyProcessor::SetInternalState(size_t index, EvalResult result) {
+ state_.current_index_ = index;
+ state_.current_result_ = result;
+}
+
+EvalResult PolicyProcessor::GetAction() const {
+ return state_.current_result_;
+}
+
+// Decides if an opcode can be skipped (not evaluated) or not. The function
+// takes as inputs the opcode and the current evaluation context and returns
+// true if the opcode should be skipped or not and also can set keep_skipping
+// to false to signal that the current instruction should be skipped but not
+// the next after the current one.
+bool SkipOpcode(const PolicyOpcode& opcode,
+ MatchContext* context,
+ bool* keep_skipping) {
+ if (opcode.IsAction()) {
+ uint32_t options = context->options;
+ context->Clear();
+ *keep_skipping = false;
+ return (kPolUseOREval != options);
+ }
+ *keep_skipping = true;
+ return true;
+}
+
+PolicyResult PolicyProcessor::Evaluate(uint32_t options,
+ ParameterSet* parameters,
+ size_t param_count) {
+ if (!policy_)
+ return NO_POLICY_MATCH;
+ if (0 == policy_->opcode_count)
+ return NO_POLICY_MATCH;
+ if (!(kShortEval & options))
+ return POLICY_ERROR;
+
+ MatchContext context;
+ bool evaluation = false;
+ bool skip_group = false;
+ SetInternalState(0, EVAL_FALSE);
+ size_t count = policy_->opcode_count;
+
+ // Loop over all the opcodes Evaluating in sequence. Since we only support
+ // short circuit evaluation, we stop as soon as we find an 'action' opcode
+ // and the current evaluation is true.
+ //
+ // Skipping opcodes can happen when we are in AND mode (!kPolUseOREval) and
+ // have got EVAL_FALSE or when we are in OR mode (kPolUseOREval) and got
+ // EVAL_TRUE. Skipping will stop at the next action opcode or at the opcode
+ // after the action depending on kPolUseOREval.
+
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode& opcode = policy_->opcodes[ix];
+ // Skipping block.
+ if (skip_group) {
+ if (SkipOpcode(opcode, &context, &skip_group))
+ continue;
+ }
+ // Evaluation block.
+ EvalResult result = opcode.Evaluate(parameters, param_count, &context);
+ switch (result) {
+ case EVAL_FALSE:
+ evaluation = false;
+ if (kPolUseOREval != context.options)
+ skip_group = true;
+ break;
+ case EVAL_ERROR:
+ if (kStopOnErrors & options)
+ return POLICY_ERROR;
+ break;
+ case EVAL_TRUE:
+ evaluation = true;
+ if (kPolUseOREval == context.options)
+ skip_group = true;
+ break;
+ default:
+ // We have evaluated an action.
+ SetInternalState(ix, result);
+ return POLICY_MATCH;
+ }
+ }
+
+ if (evaluation) {
+ // Reaching the end of the policy with a positive evaluation is probably
+ // an error: we did not find a final action opcode?
+ return POLICY_ERROR;
+ }
+ return NO_POLICY_MATCH;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h
new file mode 100644
index 0000000000..b62973b14a
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
+#define SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+
+namespace sandbox {
+
+// This header contains the core policy evaluator. In its simplest form
+// it evaluates a stream of opcodes assuming that they are laid out in
+// memory as opcode groups.
+//
+// An opcode group has N comparison opcodes plus 1 action opcode. For
+// example here we have 3 opcode groups (A, B,C):
+//
+// [comparison 1] <-- group A start
+// [comparison 2]
+// [comparison 3]
+// [action A ]
+// [comparison 1] <-- group B start
+// [action B ]
+// [comparison 1] <-- group C start
+// [comparison 2]
+// [action C ]
+//
+// The opcode evaluator proceeds from the top, evaluating each opcode in
+// sequence. An opcode group is evaluated until the first comparison that
+// returns false. At that point the rest of the group is skipped and evaluation
+// resumes with the first comparison of the next group. When all the comparisons
+// in a group have evaluated to true and the action is reached. The group is
+// considered a matching group.
+//
+// In the 'ShortEval' mode evaluation stops when it reaches the end or the first
+// matching group. The action opcode from this group is the resulting policy
+// action.
+//
+// In the 'RankedEval' mode evaluation stops only when it reaches the end of the
+// the opcode stream. In the process all matching groups are saved and at the
+// end the 'best' group is selected (what makes the best is TBD) and the action
+// from this group is the resulting policy action.
+//
+// As explained above, the policy evaluation of a group is a logical AND of
+// the evaluation of each opcode. However an opcode can request kPolUseOREval
+// which makes the evaluation to use logical OR. Given that each opcode can
+// request its evaluation result to be negated with kPolNegateEval you can
+// achieve the negation of the total group evaluation. This means that if you
+// need to express:
+// if (!(c1 && c2 && c3))
+// You can do it by:
+// if ((!c1) || (!c2) || (!c3))
+//
+
+// Possible outcomes of policy evaluation.
+enum PolicyResult { NO_POLICY_MATCH, POLICY_MATCH, POLICY_ERROR };
+
+// Policy evaluation flags
+// TODO(cpu): implement the options kStopOnErrors & kRankedEval.
+//
+// Stop evaluating as soon as an error is encountered.
+const uint32_t kStopOnErrors = 1;
+// Ignore all non fatal opcode evaluation errors.
+const uint32_t kIgnoreErrors = 2;
+// Short-circuit evaluation: Only evaluate until opcode group that
+// evaluated to true has been found.
+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_t kRankedEval = 8;
+
+// This class evaluates a policy-opcode stream given the memory where the
+// opcodes are and an input 'parameter set'.
+//
+// This class is designed to be callable from interception points
+// as low as the NtXXXX service level (it is not currently safe, but
+// it is designed to be made safe).
+//
+// Its usage in an interception is:
+//
+// POLPARAMS_BEGIN(eval_params)
+// POLPARAM(param1)
+// POLPARAM(param2)
+// POLPARAM(param3)
+// POLPARAM(param4)
+// POLPARAM(param5)
+// POLPARAMS_END;
+//
+// PolicyProcessor pol_evaluator(policy_memory);
+// PolicyResult pr = pol_evaluator.Evaluate(ShortEval, eval_params,
+// _countof(eval_params));
+// if (NO_POLICY_MATCH == pr) {
+// EvalResult policy_action = pol_evaluator.GetAction();
+// // apply policy here...
+// }
+//
+// Where the POLPARAM() arguments are derived from the intercepted function
+// arguments, and represent all the 'interesting' policy inputs, and
+// policy_memory is a memory buffer containing the opcode stream that is the
+// relevant policy for this intercept.
+class PolicyProcessor {
+ public:
+ // policy_buffer contains opcodes made with OpcodeFactory. They are usually
+ // created in the broker process and evaluated in the target process.
+
+ // This constructor is just a variant of the previous constructor.
+ explicit PolicyProcessor(PolicyBuffer* policy) : policy_(policy) {
+ SetInternalState(0, EVAL_FALSE);
+ }
+
+ // Evaluates a policy-opcode stream. See the comments at the top of this
+ // class for more info. Returns POLICY_MATCH if a rule set was found that
+ // matches an active policy.
+ PolicyResult Evaluate(uint32_t options,
+ ParameterSet* parameters,
+ size_t parameter_count);
+
+ // If the result of Evaluate() was POLICY_MATCH, calling this function returns
+ // the recommended policy action.
+ EvalResult GetAction() const;
+
+ private:
+ struct {
+ size_t current_index_;
+ EvalResult current_result_;
+ } state_;
+
+ // Sets the currently matching action result.
+ void SetInternalState(size_t index, EvalResult result);
+
+ PolicyBuffer* policy_;
+ DISALLOW_COPY_AND_ASSIGN(PolicyProcessor);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc
new file mode 100644
index 0000000000..b8b2002108
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc
@@ -0,0 +1,103 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = {
+#define POLPARAM(p) sandbox::ParamPickerMake(p),
+#define POLPARAMS_END }
+
+namespace sandbox {
+
+bool SetupNtdllImports();
+
+TEST(PolicyEngineTest, Rules1) {
+ SetupNtdllImports();
+
+ // Construct two policy rules that say:
+ //
+ // #1
+ // If the path is c:\\documents and settings\\* AND
+ // If the creation mode is 'open existing' AND
+ // If the security descriptor is null THEN
+ // Ask the broker.
+ //
+ // #2
+ // If the security descriptor is null AND
+ // If the path ends with *.txt AND
+ // If the creation mode is not 'create new' THEN
+ // return Access Denied.
+
+ enum FileCreateArgs {
+ FileNameArg,
+ CreationDispositionArg,
+ FlagsAndAttributesArg,
+ SecurityAttributes
+ };
+
+ const size_t policy_sz = 1024;
+ PolicyBuffer* policy = reinterpret_cast<PolicyBuffer*>(new char[policy_sz]);
+ OpcodeFactory opcode_maker(policy, policy_sz - 0x40);
+
+ // Add rule set #1
+ opcode_maker.MakeOpWStringMatch(FileNameArg, L"c:\\documents and settings\\",
+ 0, CASE_INSENSITIVE, kPolNone);
+ opcode_maker.MakeOpNumberMatch(CreationDispositionArg, OPEN_EXISTING,
+ kPolNone);
+ opcode_maker.MakeOpVoidPtrMatch(SecurityAttributes, nullptr, kPolNone);
+ opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+
+ // Add rule set #2
+ opcode_maker.MakeOpWStringMatch(FileNameArg, L".TXT", kSeekToEnd,
+ CASE_INSENSITIVE, kPolNone);
+ opcode_maker.MakeOpNumberMatch(CreationDispositionArg, CREATE_NEW,
+ kPolNegateEval);
+ opcode_maker.MakeOpAction(FAKE_ACCESS_DENIED, kPolNone);
+ policy->opcode_count = 7;
+
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ uint32_t creation_mode = OPEN_EXISTING;
+ uint32_t flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = nullptr;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename)
+ POLPARAM(creation_mode)
+ POLPARAM(flags)
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult pr;
+ PolicyProcessor pol_ev(policy);
+
+ // Test should match the first rule set.
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ // Test should still match the first rule set.
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ // Changing creation_mode such that evaluation should not match any rule.
+ creation_mode = CREATE_NEW;
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, pr);
+
+ // Changing creation_mode such that evaluation should match rule #2.
+ creation_mode = OPEN_ALWAYS;
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(FAKE_ACCESS_DENIED, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc
new file mode 100644
index 0000000000..d987211c8b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc
@@ -0,0 +1,355 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_low_level.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+namespace {
+
+// A single rule can use at most this amount of memory.
+const size_t kRuleBufferSize = 1024 * 4;
+
+// The possible states of the string matching opcode generator.
+enum {
+ PENDING_NONE,
+ PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode.
+ PENDING_QMARK, // Have seen an '?' but have not generated an opcode.
+};
+
+// The category of the last character seen by the string matching opcode
+// generator.
+const uint32_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
+
+namespace sandbox {
+
+LowLevelPolicy::LowLevelPolicy(PolicyGlobal* policy_store)
+ : policy_store_(policy_store) {}
+
+// Adding a rule is nothing more than pushing it into an stl container. Done()
+// is called for the rule in case the code that made the rule in the first
+// place has not done it.
+bool LowLevelPolicy::AddRule(IpcTag service, PolicyRule* rule) {
+ if (!rule->Done()) {
+ return false;
+ }
+
+ PolicyRule* local_rule = new PolicyRule(*rule);
+ RuleNode node = {local_rule, service};
+ rules_.push_back(node);
+ return true;
+}
+
+LowLevelPolicy::~LowLevelPolicy() {
+ // Delete all the rules.
+ typedef std::list<RuleNode> RuleNodes;
+ for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
+ delete it->rule;
+ }
+}
+
+// Here is where the heavy byte shuffling is done. We take all the rules and
+// 'compile' them into a single memory region. Now, the rules are in random
+// order so the first step is to reorganize them into a stl map that is keyed
+// by the service id and as a value contains a list with all the rules that
+// belong to that service. Then we enter the big for-loop where we carve a
+// memory zone for the opcodes and the data and call RebindCopy on each rule
+// so they all end up nicely packed in the policy_store_.
+bool LowLevelPolicy::Done() {
+ typedef std::list<RuleNode> RuleNodes;
+ typedef std::list<const PolicyRule*> RuleList;
+ typedef std::map<IpcTag, RuleList> Mmap;
+ Mmap mmap;
+
+ for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
+ mmap[it->service].push_back(it->rule);
+ }
+
+ PolicyBuffer* current_buffer = &policy_store_->data[0];
+ char* buffer_end =
+ reinterpret_cast<char*>(current_buffer) + policy_store_->data_size;
+ size_t avail_size = policy_store_->data_size;
+
+ for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) {
+ IpcTag service = (*it).first;
+ if (static_cast<size_t>(service) >= kMaxServiceCount) {
+ return false;
+ }
+ policy_store_->entry[static_cast<size_t>(service)] = current_buffer;
+
+ RuleList::iterator rules_it = (*it).second.begin();
+ RuleList::iterator rules_it_end = (*it).second.end();
+
+ size_t svc_opcode_count = 0;
+
+ for (; rules_it != rules_it_end; ++rules_it) {
+ const PolicyRule* rule = (*rules_it);
+ size_t op_count = rule->GetOpcodeCount();
+
+ size_t opcodes_size = op_count * sizeof(PolicyOpcode);
+ if (avail_size < opcodes_size) {
+ return false;
+ }
+ size_t data_size = avail_size - opcodes_size;
+ PolicyOpcode* opcodes_start = &current_buffer->opcodes[svc_opcode_count];
+ if (!rule->RebindCopy(opcodes_start, opcodes_size, buffer_end,
+ &data_size)) {
+ return false;
+ }
+ size_t used = avail_size - data_size;
+ buffer_end -= used;
+ avail_size -= used;
+ svc_opcode_count += op_count;
+ }
+
+ current_buffer->opcode_count = svc_opcode_count;
+ size_t policy_buffers_occupied =
+ (svc_opcode_count * sizeof(PolicyOpcode)) / sizeof(current_buffer[0]);
+ current_buffer = &current_buffer[policy_buffers_occupied + 1];
+ }
+
+ return true;
+}
+
+PolicyRule::PolicyRule(EvalResult action) : action_(action), done_(false) {
+ char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize];
+ buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
+ buffer_->opcode_count = 0;
+ opcode_factory_ =
+ new OpcodeFactory(buffer_, kRuleBufferSize + sizeof(PolicyOpcode));
+}
+
+PolicyRule::PolicyRule(const PolicyRule& other) {
+ if (this == &other)
+ return;
+ action_ = other.action_;
+ done_ = other.done_;
+ size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize;
+ char* memory = new char[buffer_size];
+ buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
+ memcpy(buffer_, other.buffer_, buffer_size);
+
+ char* opcode_buffer = reinterpret_cast<char*>(&buffer_->opcodes[0]);
+ char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)];
+ opcode_factory_ =
+ new OpcodeFactory(next_opcode, other.opcode_factory_->memory_size());
+}
+
+// This function get called from a simple state machine implemented in
+// AddStringMatch() which passes the current state (in state) and it passes
+// true in last_call if AddStringMatch() has finished processing the input
+// pattern string and this would be the last call to generate any pending
+// opcode. The skip_count is the currently accumulated number of '?' seen so
+// far and once the associated opcode is generated this function sets it back
+// to zero.
+bool PolicyRule::GenStringOpcode(RuleType rule_type,
+ StringMatchOptions match_opts,
+ uint16_t parameter,
+ int state,
+ bool last_call,
+ int* skip_count,
+ std::wstring* fragment) {
+ // The last opcode must:
+ // 1) Always clear the context.
+ // 2) Preserve the negation.
+ // 3) Remove the 'OR' mode flag.
+ uint32_t options = kPolNone;
+ if (last_call) {
+ if (IF_NOT == rule_type) {
+ options = kPolClearContext | kPolNegateEval;
+ } else {
+ options = kPolClearContext;
+ }
+ } else if (IF_NOT == rule_type) {
+ options = kPolUseOREval | kPolNegateEval;
+ }
+
+ PolicyOpcode* op = nullptr;
+
+ // The fragment string contains the accumulated characters to match with, it
+ // never contains wildcards (unless they have been escaped) and while there
+ // is no fragment there is no new string match opcode to generate.
+ if (fragment->empty()) {
+ // There is no new opcode to generate but in the last call we have to fix
+ // the previous opcode because it was really the last but we did not know
+ // it at that time.
+ if (last_call && (buffer_->opcode_count > 0)) {
+ op = &buffer_->opcodes[buffer_->opcode_count - 1];
+ op->SetOptions(options);
+ }
+ return true;
+ }
+
+ if (PENDING_ASTERISK == state) {
+ if (last_call) {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ kSeekToEnd, match_opts, options);
+ } else {
+ op = opcode_factory_->MakeOpWStringMatch(
+ parameter, fragment->c_str(), kSeekForward, match_opts, options);
+ }
+
+ } else if (PENDING_QMARK == state) {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ *skip_count, match_opts, options);
+ *skip_count = 0;
+ } else {
+ if (last_call) {
+ match_opts = static_cast<StringMatchOptions>(EXACT_LENGTH | match_opts);
+ }
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0,
+ match_opts, options);
+ }
+ if (!op)
+ return false;
+ ++buffer_->opcode_count;
+ fragment->clear();
+ return true;
+}
+
+bool PolicyRule::AddStringMatch(RuleType rule_type,
+ int16_t parameter,
+ const wchar_t* string,
+ StringMatchOptions match_opts) {
+ if (done_) {
+ // Do not allow to add more rules after generating the action opcode.
+ return false;
+ }
+
+ const wchar_t* current_char = string;
+ uint32_t last_char = kLastCharIsNone;
+ int state = PENDING_NONE;
+ int skip_count = 0; // counts how many '?' we have seen in a row.
+ std::wstring fragment; // accumulates the non-wildcard part.
+
+ while (L'\0' != *current_char) {
+ switch (*current_char) {
+ case L'*':
+ if (kLastCharIsWild & last_char) {
+ // '**' and '&*' is an error.
+ return false;
+ }
+ if (!GenStringOpcode(rule_type, match_opts, parameter, state, false,
+ &skip_count, &fragment)) {
+ return false;
+ }
+ last_char = kLastCharIsAsterisk;
+ state = PENDING_ASTERISK;
+ break;
+ case L'?':
+ if (kLastCharIsAsterisk == last_char) {
+ // '*?' is an error.
+ return false;
+ }
+ if (!GenStringOpcode(rule_type, match_opts, parameter, state, false,
+ &skip_count, &fragment)) {
+ return false;
+ }
+ ++skip_count;
+ last_char = kLastCharIsQuestionM;
+ state = PENDING_QMARK;
+ break;
+ case L'/':
+ // Note: "/?" is an escaped '?'. Eat the slash and fall through.
+ if (L'?' == current_char[1]) {
+ ++current_char;
+ }
+ FALLTHROUGH;
+ default:
+ fragment += *current_char;
+ last_char = kLastCharIsAlpha;
+ }
+ ++current_char;
+ }
+
+ if (!GenStringOpcode(rule_type, match_opts, parameter, state, true,
+ &skip_count, &fragment)) {
+ return false;
+ }
+ return true;
+}
+
+bool PolicyRule::AddNumberMatch(RuleType rule_type,
+ int16_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_t opts = (rule_type == IF_NOT) ? kPolNegateEval : kPolNone;
+
+ if (EQUAL == comparison_op) {
+ if (!opcode_factory_->MakeOpNumberMatch(parameter, number, opts))
+ return false;
+ } else if (AND == comparison_op) {
+ if (!opcode_factory_->MakeOpNumberAndMatch(parameter, number, opts))
+ return false;
+ }
+ ++buffer_->opcode_count;
+ return true;
+}
+
+bool PolicyRule::Done() {
+ if (done_) {
+ return true;
+ }
+ if (!opcode_factory_->MakeOpAction(action_, kPolNone))
+ return false;
+ ++buffer_->opcode_count;
+ done_ = true;
+ return true;
+}
+
+bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start,
+ size_t opcode_size,
+ char* data_start,
+ size_t* data_size) const {
+ size_t count = buffer_->opcode_count;
+ for (size_t ix = 0; ix != count; ++ix) {
+ if (opcode_size < sizeof(PolicyOpcode)) {
+ return false;
+ }
+ PolicyOpcode& opcode = buffer_->opcodes[ix];
+ *opcode_start = opcode;
+ if (OP_WSTRING_MATCH == opcode.GetID()) {
+ // For this opcode argument 0 is a delta to the string and argument 1
+ // is the length (in chars) of the string.
+ const wchar_t* str = opcode.GetRelativeString(0);
+ size_t str_len;
+ opcode.GetArgument(1, &str_len);
+ str_len = str_len * sizeof(wchar_t);
+ if ((*data_size) < str_len) {
+ return false;
+ }
+ *data_size -= str_len;
+ data_start -= str_len;
+ memcpy(data_start, str, str_len);
+ // Recompute the string displacement
+ ptrdiff_t delta = data_start - reinterpret_cast<char*>(opcode_start);
+ opcode_start->SetArgument(0, delta);
+ }
+ ++opcode_start;
+ opcode_size -= sizeof(PolicyOpcode);
+ }
+
+ return true;
+}
+
+PolicyRule::~PolicyRule() {
+ delete[] reinterpret_cast<char*>(buffer_);
+ delete opcode_factory_;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.h b/security/sandbox/chromium/sandbox/win/src/policy_low_level.h
new file mode 100644
index 0000000000..1586f96af9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_POLICY_LOW_LEVEL_H__
+#define SANDBOX_SRC_POLICY_LOW_LEVEL_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <list>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/ipc_tags.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 OpcodeFactory, the low level policy
+// provides a way to define rules on strings and numbers but it is unaware
+// of Windows specific details or how the Interceptions must be set up.
+// To use these classes you construct one or more rules and add them to the
+// LowLevelPolicy object like this:
+//
+// PolicyRule rule1(ASK_BROKER);
+// rule1.AddStringMatch(IF, 0, L"\\\\/?/?\\c:\\*Microsoft*\\*.exe", true);
+// rule1.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL);
+// rule1.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL);
+//
+// PolicyRule rule2(FAKE_SUCCESS);
+// rule2.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", false));
+// rule2.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL));
+//
+// LowLevelPolicy policyGen(*policy_memory);
+// policyGen.AddRule(kNtCreateFileSvc, &rule1);
+// policyGen.AddRule(kNtCreateFileSvc, &rule2);
+// policyGen.Done();
+//
+// At this point (error checking omitted) the policy_memory can be copied
+// to the target process where it can be evaluated.
+
+namespace sandbox {
+
+// Defines the memory layout of the policy. This memory is filled by
+// LowLevelPolicy object.
+// For example:
+//
+// [Service 0] --points to---\
+// [Service 1] --------------|-----\
+// ...... | |
+// [Service N] | |
+// [data_size] | |
+// [Policy Buffer 0] <-------/ |
+// [opcodes of] |
+// ....... |
+// [Policy Buffer 1] <-------------/
+// [opcodes]
+// .......
+// .......
+// [Policy Buffer N]
+// [opcodes]
+// .......
+// <possibly unused space here>
+// .......
+// [opcode string ]
+// [opcode string ]
+// .......
+// [opcode string ]
+struct PolicyGlobal {
+ PolicyBuffer* entry[kMaxServiceCount];
+ size_t data_size;
+ PolicyBuffer data[1];
+};
+
+class PolicyRule;
+
+// Provides the means to collect rules into a policy store (memory)
+class LowLevelPolicy {
+ public:
+ // policy_store: must contain allocated memory and the internal
+ // size fields set to correct values.
+ explicit LowLevelPolicy(PolicyGlobal* policy_store);
+
+ // Destroys all the policy rules.
+ ~LowLevelPolicy();
+
+ // Adds a rule to be generated when Done() is called.
+ // service: The id of the service that this rule is associated with,
+ // for example the 'Open Thread' service or the "Create File" service.
+ // returns false on error.
+ bool AddRule(IpcTag service, PolicyRule* rule);
+
+ // Generates all the rules added with AddRule() into the memory area
+ // passed on the constructor. Returns false on error.
+ bool Done();
+
+ private:
+ struct RuleNode {
+ const PolicyRule* rule;
+ IpcTag service;
+ };
+ std::list<RuleNode> rules_;
+ PolicyGlobal* policy_store_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LowLevelPolicy);
+};
+
+// There are 'if' rules and 'if not' comparisons
+enum RuleType {
+ IF = 0,
+ IF_NOT = 1,
+};
+
+// Possible comparisons for numbers
+enum RuleOp {
+ EQUAL,
+ AND,
+ RANGE // TODO(cpu): Implement this option.
+};
+
+// Provides the means to collect a set of comparisons into a single
+// rule and its associated action.
+class PolicyRule {
+ friend class LowLevelPolicy;
+
+ public:
+ explicit PolicyRule(EvalResult action);
+ PolicyRule(const PolicyRule& other);
+ ~PolicyRule();
+
+ // Adds a string comparison to the rule.
+ // rule_type: possible values are IF and IF_NOT.
+ // parameter: the expected index of the argument for this rule. For example
+ // in a 'create file' service the file name argument can be at index 0.
+ // string: is the desired matching pattern.
+ // match_opts: if the pattern matching is case sensitive or not.
+ bool AddStringMatch(RuleType rule_type,
+ int16_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.
+ // parameter: the expected index of the argument for this rule.
+ // number: the value to compare the input to.
+ // comparison_op: the comparison kind (equal, logical and, etc).
+ bool AddNumberMatch(RuleType rule_type,
+ int16_t parameter,
+ uint32_t number,
+ RuleOp comparison_op);
+
+ // Returns the number of opcodes generated so far.
+ size_t GetOpcodeCount() const { return buffer_->opcode_count; }
+
+ // Called when there is no more comparisons to add. Internally it generates
+ // the last opcode (the action opcode). Returns false if this operation fails.
+ bool Done();
+
+ private:
+ void operator=(const PolicyRule&);
+ // Called in a loop from AddStringMatch to generate the required string
+ // match opcodes. rule_type, match_opts and parameter are the same as
+ // in AddStringMatch.
+ bool GenStringOpcode(RuleType rule_type,
+ StringMatchOptions match_opts,
+ uint16_t parameter,
+ int state,
+ bool last_call,
+ int* skip_count,
+ std::wstring* fragment);
+
+ // Loop over all generated opcodes and copy them to increasing memory
+ // addresses from opcode_start and copy the extra data (strings usually) into
+ // decreasing addresses from data_start. Extra data is only present in the
+ // string evaluation opcodes.
+ bool RebindCopy(PolicyOpcode* opcode_start,
+ size_t opcode_size,
+ char* data_start,
+ size_t* data_size) const;
+ PolicyBuffer* buffer_;
+ OpcodeFactory* opcode_factory_;
+ EvalResult action_;
+ bool done_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_LOW_LEVEL_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc
new file mode 100644
index 0000000000..8484660096
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc
@@ -0,0 +1,684 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_low_level.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = {
+#define POLPARAM(p) sandbox::ParamPickerMake(p),
+#define POLPARAMS_END }
+
+namespace sandbox {
+
+bool SetupNtdllImports();
+
+// Testing that we allow opcode generation on valid string patterns.
+TEST(PolicyEngineTest, StringPatternsOK) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\adobe\\ver??\\", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"*.tmp", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*.doc", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\windows\\*", CASE_SENSITIVE));
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF, 0, L"d:\\adobe\\acrobat.exe", CASE_SENSITIVE));
+}
+
+// Testing that we signal invalid string patterns.
+TEST(PolicyEngineTest, StringPatternsBAD) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"one**two", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"**three", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"five?six*?seven", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"eight?*nine", CASE_SENSITIVE));
+}
+
+// Helper function to allocate space (on the heap) for policy.
+PolicyGlobal* MakePolicyMemory() {
+ const size_t kTotalPolicySz = 4096 * 8;
+ char* mem = new char[kTotalPolicySz];
+ memset(mem, 0, kTotalPolicySz);
+ PolicyGlobal* policy = reinterpret_cast<PolicyGlobal*>(mem);
+ policy->data_size = kTotalPolicySz - sizeof(PolicyGlobal);
+ return policy;
+}
+
+// The simplest test using LowLevelPolicy it should test a single opcode which
+// does a exact string comparison.
+TEST(PolicyEngineTest, SimpleStrMatch) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF, 0, L"z:\\Directory\\domo.txt", CASE_INSENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::PING2;
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = L"Z:\\Directory\\domo.txt";
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"Z:\\Directory\\domo.txt.tmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatch) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::PING2;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\domo.bmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ uint32_t access = 0;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(access) // Argument 1
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Micronesia\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL));
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\GoogleV?\\*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 2, 66, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ uint32_t access = 0;
+ uint32_t sharing = 66;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(access) // Argument 1
+ POLPARAM(sharing) // Argument 2
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\GoogleV2\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\GoogleV2\\domo.bmp";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\GoogleV23\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\GoogleV2\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Google\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Micronesia\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\GoogleV2\\domo.bmp";
+ access = 24;
+ sharing = 0;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+// Testing one single rule in one single service. The service is made to
+// resemble NtCreateFile.
+TEST(PolicyEngineTest, OneRuleTest) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF, 0, L"c:\\*Microsoft*\\*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+
+ const IpcTag kNtFakeCreateFile = IpcTag::NTCREATEFILE;
+
+ 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_t creation_mode = OPEN_EXISTING;
+ uint32_t flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = nullptr;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(creation_mode) // Argument 1
+ POLPARAM(flags) // Argument 2
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kNtFakeCreateFile)]);
+
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ creation_mode = CREATE_ALWAYS;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ creation_mode = OPEN_EXISTING;
+ filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt.tmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags = FILE_ATTRIBUTE_DEVICE;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Other\\Macrosoft\\Another file.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Microsoft\\1.txt";
+ flags = FILE_ATTRIBUTE_NORMAL;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\1.ttt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+// Testing 3 rules in 3 services. Two of the services resemble File services.
+TEST(PolicyEngineTest, ThreeRulesTest) {
+ SetupNtdllImports();
+ PolicyRule pr_pipe(FAKE_SUCCESS);
+ EXPECT_TRUE(pr_pipe.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL));
+ EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc1 = pr_pipe.GetOpcodeCount();
+ EXPECT_EQ(3u, opc1);
+
+ PolicyRule pr_dump(ASK_BROKER);
+ EXPECT_TRUE(pr_dump.AddStringMatch(IF, 0, L"\\\\/?/?\\*\\Crash Reports\\*",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 1, CREATE_ALWAYS, EQUAL));
+ EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc2 = pr_dump.GetOpcodeCount();
+ EXPECT_EQ(4u, opc2);
+
+ PolicyRule pr_winexe(SIGNAL_ALARM);
+ EXPECT_TRUE(pr_winexe.AddStringMatch(IF, 0, L"\\\\/?/?\\C:\\Windows\\*.exe",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_winexe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc3 = pr_winexe.GetOpcodeCount();
+ EXPECT_EQ(3u, opc3);
+
+ PolicyRule pr_adobe(GIVE_CACHED);
+ EXPECT_TRUE(
+ pr_adobe.AddStringMatch(IF, 0, L"c:\\adobe\\ver?.?\\", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_adobe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc4 = pr_adobe.GetOpcodeCount();
+ EXPECT_EQ(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(2u, opc5);
+
+ PolicyGlobal* policy = MakePolicyMemory();
+
+ // These do not match the real tag values.
+ const IpcTag kNtFakeNone = static_cast<IpcTag>(4);
+ const IpcTag kNtFakeCreateFile = static_cast<IpcTag>(5);
+ const IpcTag kNtFakeOpenFile = static_cast<IpcTag>(6);
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_pipe));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_dump));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_winexe));
+
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_adobe));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_pipe));
+
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeNone, &pr_none));
+
+ EXPECT_TRUE(policyGen.Done());
+
+ // Inspect the policy structure manually.
+ EXPECT_FALSE(policy->entry[0]);
+ EXPECT_FALSE(policy->entry[1]);
+ EXPECT_FALSE(policy->entry[2]);
+ EXPECT_FALSE(policy->entry[3]);
+ EXPECT_TRUE(policy->entry[4]); // kNtFakeNone.
+ EXPECT_TRUE(policy->entry[5]); // kNtFakeCreateFile.
+ EXPECT_TRUE(policy->entry[6]); // kNtFakeOpenFile.
+ EXPECT_FALSE(policy->entry[7]);
+
+ // The total per service opcode counts now must take in account one
+ // extra opcode (action opcode) per rule.
+ ++opc1;
+ ++opc2;
+ ++opc3;
+ ++opc4;
+ ++opc5;
+
+ size_t tc1 = policy->entry[static_cast<size_t>(kNtFakeNone)]->opcode_count;
+ size_t tc2 =
+ policy->entry[static_cast<size_t>(kNtFakeCreateFile)]->opcode_count;
+ size_t tc3 =
+ policy->entry[static_cast<size_t>(kNtFakeOpenFile)]->opcode_count;
+
+ EXPECT_EQ(opc5, tc1);
+ EXPECT_EQ((opc1 + opc2 + opc3), tc2);
+ EXPECT_EQ((opc1 + opc4), tc3);
+
+ // Check the type of the first and last opcode of each service.
+
+ EXPECT_EQ(
+ OP_NUMBER_AND_MATCH,
+ policy->entry[static_cast<size_t>(kNtFakeNone)]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[static_cast<size_t>(kNtFakeNone)]
+ ->opcodes[tc1 - 1]
+ .GetID());
+ EXPECT_EQ(OP_WSTRING_MATCH,
+ policy->entry[static_cast<size_t>(kNtFakeCreateFile)]
+ ->opcodes[0]
+ .GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[static_cast<size_t>(kNtFakeCreateFile)]
+ ->opcodes[tc2 - 1]
+ .GetID());
+ EXPECT_EQ(
+ OP_WSTRING_MATCH,
+ policy->entry[static_cast<size_t>(kNtFakeOpenFile)]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[static_cast<size_t>(kNtFakeOpenFile)]
+ ->opcodes[tc3 - 1]
+ .GetID());
+
+ // Test the policy evaluation.
+
+ const wchar_t* filename = L"";
+ uint32_t creation_mode = OPEN_EXISTING;
+ uint32_t flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = nullptr;
+
+ POLPARAMS_BEGIN(params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(creation_mode) // Argument 1
+ POLPARAM(flags) // Argument 2
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor eval_CreateFile(
+ policy->entry[static_cast<size_t>(kNtFakeCreateFile)]);
+ PolicyProcessor eval_OpenFile(
+ policy->entry[static_cast<size_t>(kNtFakeOpenFile)]);
+ PolicyProcessor eval_None(policy->entry[static_cast<size_t>(kNtFakeNone)]);
+
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\c:\\Windows\\System32\\calc.exe";
+ flags = FILE_ATTRIBUTE_SYSTEM;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags += FILE_ATTRIBUTE_READONLY;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(GIVE_FIRST, eval_None.GetAction());
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags = FILE_ATTRIBUTE_NORMAL;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(SIGNAL_ALARM, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\adobe\\ver3.2\\temp";
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(GIVE_CACHED, eval_OpenFile.GetAction());
+
+ filename = L"c:\\adobe\\ver3.22\\temp";
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\c:\\some path\\other path\\crash reports\\some path";
+ creation_mode = CREATE_ALWAYS;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\Pipe\\Chrome.12345";
+ creation_mode = OPEN_EXISTING;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(FAKE_SUCCESS, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(FAKE_SUCCESS, eval_OpenFile.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, PolicyRuleCopyConstructorTwoStrings) {
+ SetupNtdllImports();
+ // Both pr_orig and pr_copy should allow hello.* but not *.txt files.
+ PolicyRule pr_orig(ASK_BROKER);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF, 0, L"hello.*", CASE_SENSITIVE));
+
+ PolicyRule pr_copy(pr_orig);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_copy.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(IpcTag::PING1, &pr_orig));
+ EXPECT_TRUE(policyGen.AddRule(IpcTag::PING2, &pr_copy));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* name = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(name)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev_orig(policy->entry[1]);
+ name = L"domo.txt";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_orig.GetAction());
+
+ PolicyProcessor pol_ev_copy(policy->entry[2]);
+ name = L"domo.txt";
+ result = pol_ev_copy.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_copy.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_copy.GetAction());
+}
+
+TEST(PolicyEngineTest, PolicyGenDoneCalledTwice) {
+ SetupNtdllImports();
+ // The specific rules here are not important.
+ PolicyRule pr_orig(ASK_BROKER);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF, 0, L"hello.*", CASE_SENSITIVE));
+
+ PolicyRule pr_copy(pr_orig);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_copy.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ LowLevelPolicy policyGen(policy);
+ const IpcTag tag1 = IpcTag::PING1;
+ const IpcTag tag2 = IpcTag::PING2;
+ EXPECT_TRUE(policyGen.AddRule(tag1, &pr_orig));
+ EXPECT_TRUE(policyGen.AddRule(tag2, &pr_copy));
+ EXPECT_TRUE(policyGen.Done());
+
+ // Obtain opcode counts.
+ size_t tc1 = policy->entry[static_cast<size_t>(IpcTag::PING1)]->opcode_count;
+ size_t tc2 = policy->entry[static_cast<size_t>(IpcTag::PING2)]->opcode_count;
+
+ // Call Done() again.
+ EXPECT_TRUE(policyGen.Done());
+
+ // Expect same opcode counts.
+ EXPECT_EQ(tc1,
+ policy->entry[static_cast<size_t>(IpcTag::PING1)]->opcode_count);
+ EXPECT_EQ(tc2,
+ policy->entry[static_cast<size_t>(IpcTag::PING2)]->opcode_count);
+
+ // Confirm the rules work as before.
+ const wchar_t* name = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(name)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev_orig(policy->entry[1]);
+ name = L"domo.txt";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_orig.GetAction());
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc
new file mode 100644
index 0000000000..c83efcba18
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc
@@ -0,0 +1,364 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_engine_opcodes.h"
+
+#include <stddef.h>
+#include <stdint.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"
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.member = \
+ reinterpret_cast<member##Function>(::GetProcAddress(ntdll, #member)); \
+ if (!g_nt.member) \
+ return false
+
+namespace sandbox {
+
+const size_t kOpcodeMemory = 1024;
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+bool SetupNtdllImports() {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+
+ return true;
+}
+
+TEST(PolicyEngineTest, ParameterSetTest) {
+ void* pv1 = reinterpret_cast<void*>(0x477EAA5);
+ const void* pv2 = reinterpret_cast<void*>(0x987654);
+ ParameterSet pset1 = ParamPickerMake(pv1);
+ ParameterSet pset2 = ParamPickerMake(pv2);
+
+ // Test that we can store and retrieve a void pointer:
+ const void* result1 = 0;
+ uint32_t result2 = 0;
+ EXPECT_TRUE(pset1.Get(&result1));
+ EXPECT_TRUE(pv1 == result1);
+ EXPECT_FALSE(pset1.Get(&result2));
+ EXPECT_TRUE(pset2.Get(&result1));
+ EXPECT_TRUE(pv2 == result1);
+ EXPECT_FALSE(pset2.Get(&result2));
+
+ // Test that we can store and retrieve a uint32_t:
+ uint32_t number = 12747;
+ ParameterSet pset3 = ParamPickerMake(number);
+ EXPECT_FALSE(pset3.Get(&result1));
+ EXPECT_TRUE(pset3.Get(&result2));
+ EXPECT_EQ(number, result2);
+
+ // Test that we can store and retrieve a string:
+ const wchar_t* txt = L"S231L";
+ ParameterSet pset4 = ParamPickerMake(txt);
+ const wchar_t* result3 = nullptr;
+ EXPECT_TRUE(pset4.Get(&result3));
+ EXPECT_EQ(0, wcscmp(txt, result3));
+}
+
+TEST(PolicyEngineTest, OpcodeConstraints) {
+ // Test that PolicyOpcode has no virtual functions
+ // because these objects are copied over to other processes
+ // so they cannot have vtables.
+ EXPECT_FALSE(__is_polymorphic(PolicyOpcode));
+ // Keep developers from adding smarts to the opcodes which should
+ // be pretty much a bag of bytes with a OO interface.
+ EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_copy(PolicyOpcode));
+}
+
+TEST(PolicyEngineTest, TrueFalseOpcodes) {
+ void* dummy = nullptr;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // This opcode always evaluates to true.
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_NE(nullptr, op1);
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, nullptr));
+ EXPECT_FALSE(op1->IsAction());
+
+ // This opcode always evaluates to false.
+ PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone);
+ ASSERT_NE(nullptr, op2);
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, nullptr));
+
+ // Nulls not allowed on the params.
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(nullptr, 0, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(nullptr, 1, nullptr));
+
+ // True and False opcodes do not 'require' a number of parameters
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, nullptr));
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, nullptr));
+
+ // Test Inverting the logic. Note that inversion is done outside
+ // any particular opcode evaluation so no need to repeat for all
+ // opcodes.
+ PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval);
+ ASSERT_NE(nullptr, op3);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, nullptr));
+ PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval);
+ ASSERT_NE(nullptr, op4);
+ EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, nullptr));
+
+ // Test that we clear the match context
+ PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext);
+ ASSERT_NE(nullptr, op5);
+ MatchContext context;
+ context.position = 1;
+ context.options = kPolUseOREval;
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context));
+ EXPECT_EQ(0u, context.position);
+ MatchContext context2;
+ EXPECT_EQ(context2.options, context.options);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase1) {
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ void* dummy = nullptr;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / sizeof(PolicyOpcode);
+
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_NE(nullptr, op);
+ EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, nullptr));
+ }
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_EQ(nullptr, op1);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase2) {
+ SetupNtdllImports();
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ // The difference with the previous test is that this opcodes allocate
+ // the string 'txt2' inside the same buffer.
+ const wchar_t* txt1 = L"1234";
+ const wchar_t txt2[] = L"123";
+
+ ParameterSet ppb1 = ParamPickerMake(txt1);
+ MatchContext mc1;
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2));
+
+ // Test that it does not overrun the buffer.
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(
+ 0, txt2, 0, CASE_SENSITIVE, kPolClearContext);
+ ASSERT_NE(nullptr, op);
+ EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1));
+ }
+
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 =
+ opcode_maker.MakeOpWStringMatch(0, txt2, 0, CASE_SENSITIVE, kPolNone);
+ ASSERT_EQ(nullptr, op1);
+}
+
+TEST(PolicyEngineTest, IntegerOpcodes) {
+ const wchar_t* txt = L"abcdef";
+ uint32_t num1 = 42;
+ uint32_t num2 = 113377;
+
+ ParameterSet pp_wrong1 = ParamPickerMake(txt);
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+ ParameterSet pp_num2 = ParamPickerMake(num2);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // Test basic match for uint32s 42 == 42 and 42 != 113377.
+ PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, 42UL, kPolNone);
+ ASSERT_NE(nullptr, op_m42);
+ EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, nullptr));
+ EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, nullptr));
+
+ // Test basic match for void pointers.
+ const void* vp = nullptr;
+ ParameterSet pp_num3 = ParamPickerMake(vp);
+ PolicyOpcode* op_vp_null =
+ opcode_maker.MakeOpVoidPtrMatch(0, nullptr, kPolNone);
+ ASSERT_NE(nullptr, op_vp_null);
+ EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, nullptr));
+ EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, nullptr));
+
+ // Basic range test [41 43] (inclusive).
+ PolicyOpcode* op_range1 =
+ opcode_maker.MakeOpNumberMatchRange(0, 41, 43, kPolNone);
+ ASSERT_NE(nullptr, op_range1);
+ EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, nullptr));
+ EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, nullptr));
+}
+
+TEST(PolicyEngineTest, LogicalOpcodes) {
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ uint32_t num1 = 0x10100702;
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+
+ PolicyOpcode* op_and1 =
+ opcode_maker.MakeOpNumberAndMatch(0, 0x00100000, kPolNone);
+ ASSERT_NE(nullptr, op_and1);
+ EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, nullptr));
+ PolicyOpcode* op_and2 =
+ opcode_maker.MakeOpNumberAndMatch(0, 0x00000001, kPolNone);
+ ASSERT_NE(nullptr, op_and2);
+ EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, nullptr));
+}
+
+TEST(PolicyEngineTest, WCharOpcodes1) {
+ SetupNtdllImports();
+
+ const wchar_t* txt1 = L"the quick fox jumps over the lazy dog";
+ const wchar_t txt2[] = L"the quick";
+ const wchar_t txt3[] = L" fox jumps";
+ const wchar_t txt4[] = L"the lazy dog";
+ const wchar_t txt5[] = L"jumps over";
+ const wchar_t txt6[] = L"g";
+
+ ParameterSet pp_tc1 = ParamPickerMake(txt1);
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ PolicyOpcode* op1 =
+ opcode_maker.MakeOpWStringMatch(0, txt2, 0, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op1);
+
+ // Simplest substring match from pos 0. It should be a successful match
+ // and the match context should be updated.
+ MatchContext mc1;
+ EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Matching again should fail and the context should be unmodified.
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Using the same match context we should continue where we left
+ // in the previous successful match,
+ PolicyOpcode* op3 =
+ opcode_maker.MakeOpWStringMatch(0, txt3, 0, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op3);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2);
+
+ // We now keep on matching but now we skip 6 characters which means
+ // we skip the string ' over '. And we zero the match context. This is
+ // the primitive that we use to build '??'.
+ PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(
+ 0, txt4, 6, CASE_SENSITIVE, kPolClearContext);
+ ASSERT_NE(nullptr, op4);
+ EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0u, mc1.position);
+
+ // Test that we can properly match the last part of the string
+ PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(
+ 0, txt4, kSeekToEnd, CASE_SENSITIVE, kPolClearContext);
+ ASSERT_NE(nullptr, op4b);
+ EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0u, mc1.position);
+
+ // Test matching 'jumps over' over the entire string. This is the
+ // primitive we build '*' from.
+ PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op5);
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(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, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op5b);
+ EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1));
+ 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'.
+ PolicyOpcode* op6 =
+ opcode_maker.MakeOpWStringMatch(0, txt4, 2, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op6);
+ EXPECT_EQ(EVAL_FALSE, op6->Evaluate(&pp_tc1, 1, &mc1));
+
+ // Testing matching against 'g' which should be the last char.
+ MatchContext mc2;
+ PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op7);
+ EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2));
+ EXPECT_EQ(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(37u, mc2.position);
+}
+
+TEST(PolicyEngineTest, WCharOpcodes2) {
+ SetupNtdllImports();
+
+ const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt";
+ const wchar_t txt1[] = L"Settings\\microsoft";
+ ParameterSet pp_tc1 = ParamPickerMake(path1);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+
+ // Testing case-insensitive does not buy us much since it this option
+ // is just passed to the Microsoft API that we use normally, but just for
+ // coverage, here it is:
+ PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(
+ 0, txt1, kSeekForward, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op1s);
+ PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(
+ 0, txt1, kSeekForward, CASE_INSENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op1i);
+ EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(35u, mc1.position);
+}
+
+TEST(PolicyEngineTest, ActionOpcodes) {
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+ void* dummy = nullptr;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+ ASSERT_NE(nullptr, op1);
+ EXPECT_TRUE(op1->IsAction());
+ EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_params.h b/security/sandbox/chromium/sandbox/win/src/policy_params.h
new file mode 100644
index 0000000000..aeec6d7edc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_params.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_POLICY_PARAMS_H__
+#define SANDBOX_SRC_POLICY_PARAMS_H__
+
+#include "sandbox/win/src/policy_engine_params.h"
+
+namespace sandbox {
+
+class ParameterSet;
+
+// Warning: The following macros store the address to the actual variables, in
+// other words, the values are not copied.
+#define POLPARAMS_BEGIN(type) class type { public: enum Args {
+#define POLPARAM(arg) arg,
+#define POLPARAMS_END(type) PolParamLast }; }; \
+ typedef sandbox::ParameterSet type##Array [type::PolParamLast];
+
+// Policy parameters for file open / create.
+POLPARAMS_BEGIN(OpenFile)
+ POLPARAM(NAME)
+ POLPARAM(BROKER) // true if called from the broker.
+ POLPARAM(ACCESS)
+ POLPARAM(DISPOSITION)
+ POLPARAM(OPTIONS)
+POLPARAMS_END(OpenFile)
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(FileName)
+ POLPARAM(NAME)
+ POLPARAM(BROKER) // true if called from the broker.
+POLPARAMS_END(FileName)
+
+static_assert(OpenFile::NAME == static_cast<int>(FileName::NAME),
+ "to simplify fs policies");
+static_assert(OpenFile::BROKER == static_cast<int>(FileName::BROKER),
+ "to simplify fs policies");
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(NameBased)
+ POLPARAM(NAME)
+POLPARAMS_END(NameBased)
+
+// Policy parameters for open event.
+POLPARAMS_BEGIN(OpenEventParams)
+ POLPARAM(NAME)
+ POLPARAM(ACCESS)
+POLPARAMS_END(OpenEventParams)
+
+// Policy Parameters for reg open / create.
+POLPARAMS_BEGIN(OpenKey)
+ POLPARAM(NAME)
+ POLPARAM(ACCESS)
+POLPARAMS_END(OpenKey)
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(HandleTarget)
+ POLPARAM(NAME)
+ POLPARAM(TARGET)
+POLPARAMS_END(HandleTarget)
+
+// Policy parameters where no parameter based checks are done.
+POLPARAMS_BEGIN(EmptyParams)
+POLPARAMS_END(EmptyParams)
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_PARAMS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target.cc b/security/sandbox/chromium/sandbox/win/src/policy_target.cc
new file mode 100644
index 0000000000..1727d622f8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_target.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// Handle for our private heap.
+extern void* g_heap;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Policy data.
+extern void* volatile g_shared_policy_memory;
+SANDBOX_INTERCEPT size_t g_shared_policy_size;
+
+bool QueryBroker(IpcTag ipc_id, CountedParameterSetBase* params) {
+ DCHECK_NT(static_cast<size_t>(ipc_id) < kMaxServiceCount);
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+
+ if (static_cast<size_t>(ipc_id) >= kMaxServiceCount)
+ return false;
+
+ PolicyGlobal* global_policy =
+ reinterpret_cast<PolicyGlobal*>(g_shared_policy_memory);
+
+ if (!global_policy->entry[static_cast<size_t>(ipc_id)])
+ return false;
+
+ PolicyBuffer* policy = reinterpret_cast<PolicyBuffer*>(
+ reinterpret_cast<char*>(g_shared_policy_memory) +
+ reinterpret_cast<size_t>(
+ global_policy->entry[static_cast<size_t>(ipc_id)]));
+
+ if ((reinterpret_cast<size_t>(
+ global_policy->entry[static_cast<size_t>(ipc_id)]) >
+ global_policy->data_size) ||
+ (g_shared_policy_size < global_policy->data_size)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ for (size_t i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED_NT();
+ return false;
+ }
+ }
+
+ PolicyProcessor processor(policy);
+ PolicyResult result =
+ processor.Evaluate(kShortEval, params->parameters, params->count);
+ DCHECK_NT(POLICY_ERROR != result);
+
+ return POLICY_MATCH == result && ASK_BROKER == processor.GetAction();
+}
+
+// -----------------------------------------------------------------------
+
+// Hooks NtSetInformationThread to block RevertToSelf from being
+// called before the actual call to LowerToken.
+NTSTATUS WINAPI TargetNtSetInformationThread(
+ NtSetInformationThreadFunction orig_SetInformationThread,
+ HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes) {
+ do {
+ if (SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ break;
+ if (ThreadImpersonationToken != thread_info_class)
+ break;
+ if (!thread_information)
+ break;
+ HANDLE token;
+ if (sizeof(token) > thread_information_bytes)
+ break;
+
+ NTSTATUS ret = CopyData(&token, thread_information, sizeof(token));
+ if (!NT_SUCCESS(ret) || NULL != token)
+ break;
+
+ // This is a revert to self.
+ return STATUS_SUCCESS;
+ } while (false);
+
+ return orig_SetInformationThread(
+ thread, thread_info_class, thread_information, thread_information_bytes);
+}
+
+// Hooks NtOpenThreadToken to force the open_as_self parameter to be set to
+// false if we are still running with the impersonation token. open_as_self set
+// to true means that the token will be open using the process token instead of
+// the impersonation token. This is bad because the process token does not have
+// access to open the thread token.
+NTSTATUS WINAPI
+TargetNtOpenThreadToken(NtOpenThreadTokenFunction orig_OpenThreadToken,
+ HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ PHANDLE token) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ open_as_self = false;
+
+ return orig_OpenThreadToken(thread, desired_access, open_as_self, token);
+}
+
+// See comment for TargetNtOpenThreadToken
+NTSTATUS WINAPI
+TargetNtOpenThreadTokenEx(NtOpenThreadTokenExFunction orig_OpenThreadTokenEx,
+ HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ open_as_self = false;
+
+ return orig_OpenThreadTokenEx(thread, desired_access, open_as_self,
+ handle_attributes, token);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target.h b/security/sandbox/chromium/sandbox/win/src/policy_target.h
new file mode 100644
index 0000000000..62686aaab2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_POLICY_TARGET_H_
+#define SANDBOX_WIN_SRC_POLICY_TARGET_H_
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+struct CountedParameterSetBase;
+
+// Performs a policy lookup and returns true if the request should be passed to
+// the broker process.
+bool QueryBroker(IpcTag ipc_id, CountedParameterSetBase* params);
+
+extern "C" {
+
+// Interception of NtSetInformationThread on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread(
+ NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information,
+ ULONG thread_information_bytes);
+
+// Interception of NtOpenThreadToken on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken(
+ NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token);
+
+// Interception of NtOpenThreadTokenEx on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx(
+ NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes,
+ PHANDLE token);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_POLICY_TARGET_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
new file mode 100644
index 0000000000..4ee03e19f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/writable_shared_memory_region.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
+namespace sandbox {
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+// Reverts to self and verify that SetInformationToken was faked. Returns
+// SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t** argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+
+ ::RevertToSelf();
+ ::CloseHandle(thread_token);
+
+ int ret = SBOX_TEST_FAILED;
+ if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ false, &thread_token)) {
+ ret = SBOX_TEST_SUCCEEDED;
+ ::CloseHandle(thread_token);
+ }
+ return ret;
+}
+
+// Stores the high privilege token on a static variable, change impersonation
+// again to that one and verify that we are not interfering anymore with
+// RevertToSelf.
+SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t** argv) {
+ static HANDLE thread_token;
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+ } else {
+ if (!::SetThreadToken(nullptr, thread_token))
+ return ::GetLastError();
+
+ // See if we fake the call again.
+ int ret = PolicyTargetTest_token(argc, argv);
+ ::CloseHandle(thread_token);
+ return ret;
+ }
+ return 0;
+}
+
+// Opens the thread token with and without impersonation.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t** argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ true, &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Opens the thread token with and without impersonation, using
+// NtOpenThreadTokenEX.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t** argv) {
+ BINDNTDLL(NtOpenThreadTokenEx);
+ if (!NtOpenThreadTokenEx)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ false, 0, &thread_token);
+ if (status == STATUS_NO_TOKEN)
+ return ERROR_NO_TOKEN;
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, true, 0,
+ &thread_token);
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can open the current thread.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t** argv) {
+ DWORD thread_id = ::GetCurrentThreadId();
+ HANDLE thread = ::OpenThread(SYNCHRONIZE, false, thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// New thread entry point: do nothing.
+DWORD WINAPI PolicyTargetTest_thread_main(void* param) {
+ ::Sleep(INFINITE);
+ return 0;
+}
+
+// Tests that we can create a new thread, and open it.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t** argv) {
+ // Use default values to create a new thread.
+ DWORD thread_id;
+ HANDLE thread = ::CreateThread(nullptr, 0, &PolicyTargetTest_thread_main, 0,
+ 0, &thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ thread = ::OpenThread(SYNCHRONIZE, false, thread_id);
+ if (!thread)
+ return ::GetLastError();
+
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can call CreateProcess.
+SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t** argv) {
+ // Use default values to create a new process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION temp_process_info = {};
+ // Note: CreateProcessW() can write to its lpCommandLine, don't pass a
+ // raw string literal.
+ std::wstring writable_cmdline_str(L"foo.exe");
+ if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], nullptr, nullptr,
+ false, 0, nullptr, nullptr, &startup_info,
+ &temp_process_info))
+ return SBOX_TEST_SUCCEEDED;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+ return SBOX_TEST_FAILED;
+}
+
+TEST(PolicyTargetTest, SetInformationThread) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal"));
+}
+
+TEST(PolicyTargetTest, OpenThreadToken) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2"));
+}
+
+TEST(PolicyTargetTest, OpenThreadTokenEx) {
+ TestRunner runner;
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3"));
+}
+
+TEST(PolicyTargetTest, OpenThread) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread"))
+ << "Opens the current thread";
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2"))
+ << "Creates a new thread and opens it";
+}
+
+TEST(PolicyTargetTest, OpenProcess) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process"))
+ << "Opens a process";
+}
+
+TEST(PolicyTargetTest, PolicyBaseNoJobLifetime) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.SetReleasePolicyInRun(true);
+ // TargetPolicy and its SharedMemIPCServer should continue to exist until
+ // the child process dies.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread"))
+ << "Opens the current thread";
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// desktop associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, DesktopPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ scoped_refptr<TargetPolicy> temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(false);
+ temp_policy = nullptr;
+
+ ASSERT_TRUE(broker);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ DWORD last_error = ERROR_SUCCESS;
+ base::win::ScopedProcessInformation target;
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(false);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ policy = nullptr;
+
+ 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<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ EXPECT_TRUE(desk);
+ EXPECT_TRUE(::CloseDesktop(desk));
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy = nullptr;
+
+ // Make sure the desktop does not exist anymore.
+ desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ EXPECT_FALSE(desk);
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// winstation associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, WinstaPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ scoped_refptr<TargetPolicy> temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(true);
+ temp_policy = nullptr;
+
+ ASSERT_TRUE(broker);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(true);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ DWORD last_error = ERROR_SUCCESS;
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ policy = nullptr;
+
+ 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<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ ASSERT_FALSE(desktop_name.empty());
+
+ // Make sure there is a backslash, for the window station name.
+ EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos);
+
+ // Isolate the desktop name.
+ desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ // This should fail if the desktop is really on another window station.
+ EXPECT_FALSE(desk);
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy = nullptr;
+}
+
+// Creates multiple policies, with alternate desktops on both local and
+// alternate winstations.
+TEST(PolicyTargetTest, BothLocalAndAlternateWinstationDesktop) {
+ BrokerServices* broker = GetBroker();
+
+ scoped_refptr<TargetPolicy> policy1 = broker->CreatePolicy();
+ scoped_refptr<TargetPolicy> policy2 = broker->CreatePolicy();
+ scoped_refptr<TargetPolicy> policy3 = broker->CreatePolicy();
+
+ ResultCode result;
+ result = policy1->SetAlternateDesktop(false);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ result = policy2->SetAlternateDesktop(true);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ result = policy3->SetAlternateDesktop(false);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+
+ std::wstring policy1_desktop_name = policy1->GetAlternateDesktop();
+ std::wstring policy2_desktop_name = policy2->GetAlternateDesktop();
+
+ // Extract only the "desktop name" portion of
+ // "{winstation name}\\{desktop name}"
+ EXPECT_NE(policy1_desktop_name.substr(
+ policy1_desktop_name.find_first_of(L'\\') + 1),
+ policy2_desktop_name.substr(
+ policy2_desktop_name.find_first_of(L'\\') + 1));
+
+ policy1->DestroyAlternateDesktop();
+ policy2->DestroyAlternateDesktop();
+ policy3->DestroyAlternateDesktop();
+}
+
+// Launches the app in the sandbox and share a handle with it. The app should
+// be able to use the handle.
+TEST(PolicyTargetTest, ShareHandleTest) {
+ BrokerServices* broker = GetBroker();
+ ASSERT_TRUE(broker);
+
+ base::StringPiece contents = "Hello World";
+ base::WritableSharedMemoryRegion writable_region =
+ base::WritableSharedMemoryRegion::Create(contents.size());
+ ASSERT_TRUE(writable_region.IsValid());
+ base::WritableSharedMemoryMapping writable_mapping = writable_region.Map();
+ ASSERT_TRUE(writable_mapping.IsValid());
+ memcpy(writable_mapping.memory(), contents.data(), contents.size());
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ base::ReadOnlySharedMemoryRegion read_only_region =
+ base::WritableSharedMemoryRegion::ConvertToReadOnly(
+ std::move(writable_region));
+ ASSERT_TRUE(read_only_region.IsValid());
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->AddHandleToShare(read_only_region.GetPlatformHandle());
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 shared_memory_handle ";
+ arguments += base::AsWString(base::NumberToString16(
+ base::win::HandleToUint32(read_only_region.GetPlatformHandle())));
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ DWORD last_error = ERROR_SUCCESS;
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ policy = nullptr;
+
+ 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<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+}
+
+// Dummy target that just reports that's it spawned correctly.
+SBOX_TESTS_COMMAND int PolicyTargetTest_SetEffectiveToken(int argc,
+ wchar_t** argv) {
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Test whether after using SetEffectiveToken spawning a target works as
+// expected.
+TEST(PolicyTargetTest, SetEffectiveToken) {
+ TestRunner runner;
+ HANDLE token;
+
+ // Get current process token.
+ EXPECT_TRUE(
+ ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
+
+ // Setup token guard.
+ base::win::ScopedHandle token_guard(token);
+
+ // Set token and run target.
+ runner.GetPolicy()->SetEffectiveToken(token_guard.Get());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"PolicyTargetTest_SetEffectiveToken"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
new file mode 100644
index 0000000000..557296c93f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
@@ -0,0 +1,622 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations.h"
+
+#include <stddef.h>
+#include <windows.h>
+#include <wow64apiset.h>
+
+#include <algorithm>
+
+#include "base/win/windows_version.h"
+#include "build/build_config.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 {
+
+// API defined in libloaderapi.h >= Win8.
+using SetDefaultDllDirectoriesFunction = decltype(&SetDefaultDllDirectories);
+
+// APIs defined in processthreadsapi.h >= Win8.
+using SetProcessMitigationPolicyFunction =
+ decltype(&SetProcessMitigationPolicy);
+using GetProcessMitigationPolicyFunction =
+ decltype(&GetProcessMitigationPolicy);
+using SetThreadInformationFunction = decltype(&SetThreadInformation);
+
+// Returns a two-element array of mitigation flags supported on this machine.
+// - This function is only useful on >= base::win::Version::WIN8.
+const ULONG64* GetSupportedMitigations() {
+ static ULONG64 mitigations[2] = {};
+
+ // This static variable will only be initialized once.
+ if (!mitigations[0] && !mitigations[1]) {
+ GetProcessMitigationPolicyFunction get_process_mitigation_policy =
+ reinterpret_cast<GetProcessMitigationPolicyFunction>(::GetProcAddress(
+ ::GetModuleHandleA("kernel32.dll"), "GetProcessMitigationPolicy"));
+ if (get_process_mitigation_policy) {
+ // NOTE: the two-element-sized input array is only supported on >= Win10
+ // RS2.
+ // If an earlier version, the second element will be left 0.
+ size_t mits_size =
+ (base::win::GetVersion() >= base::win::Version::WIN10_RS2)
+ ? (sizeof(mitigations[0]) * 2)
+ : sizeof(mitigations[0]);
+ if (!get_process_mitigation_policy(::GetCurrentProcess(),
+ ProcessMitigationOptionsMask,
+ &mitigations, mits_size)) {
+ NOTREACHED();
+ }
+ }
+ }
+
+ return &mitigations[0];
+}
+
+// Returns true if this is 32-bit Chrome running on ARM64 with emulation.
+// Needed because ACG does not work with emulated code.
+// See
+// https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm-troubleshooting-x86.
+// See https://crbug.com/977723.
+// TODO(wfh): Move this code into base. See https://crbug.com/978257.
+bool IsRunning32bitEmulatedOnArm64() {
+#if defined(ARCH_CPU_X86)
+ using IsWow64Process2Function = decltype(&IsWow64Process2);
+
+ IsWow64Process2Function is_wow64_process2 =
+ reinterpret_cast<IsWow64Process2Function>(::GetProcAddress(
+ ::GetModuleHandleA("kernel32.dll"), "IsWow64Process2"));
+ if (!is_wow64_process2)
+ return false;
+ USHORT process_machine;
+ USHORT native_machine;
+ bool retval = is_wow64_process2(::GetCurrentProcess(), &process_machine,
+ &native_machine);
+ if (!retval)
+ return false;
+ if (native_machine == IMAGE_FILE_MACHINE_ARM64)
+ return true;
+#endif // defined(ARCH_CPU_X86)
+ return false;
+}
+
+// Returns true if user-mode Hardware-enforced Stack Protection is available for
+// the Win32 environment.
+bool IsUserCetWin32Available() {
+ static bool cetAvailable = []() -> bool {
+ using IsUserCetAvailableInEnvironmentFunction =
+ decltype(&IsUserCetAvailableInEnvironment);
+
+ IsUserCetAvailableInEnvironmentFunction is_user_cet_available =
+ reinterpret_cast<IsUserCetAvailableInEnvironmentFunction>(
+ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
+ "IsUserCetAvailableInEnvironment"));
+ if (!is_user_cet_available) {
+ return false;
+ }
+
+ return is_user_cet_available(USER_CET_ENVIRONMENT_WIN32_PROCESS);
+ }();
+
+ return cetAvailable;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPostStartup(flags))
+ return false;
+
+ base::win::Version version = base::win::GetVersion();
+ HMODULE module = ::GetModuleHandleA("kernel32.dll");
+
+ if (flags & MITIGATION_DLL_SEARCH_ORDER) {
+ SetDefaultDllDirectoriesFunction set_default_dll_directories =
+ reinterpret_cast<SetDefaultDllDirectoriesFunction>(
+ ::GetProcAddress(module, "SetDefaultDllDirectories"));
+
+ // Check for SetDefaultDllDirectories since it requires KB2533623.
+ if (set_default_dll_directories) {
+ if (!set_default_dll_directories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+ }
+
+ // Set the heap to terminate on corruption
+ if (flags & MITIGATION_HEAP_TERMINATE) {
+ if (!::HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption,
+ nullptr, 0) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (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;
+
+ if (flags & MITIGATION_DEP_NO_ATL_THUNK)
+ dep_flags |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION;
+
+ if (!::SetProcessDEPPolicy(dep_flags) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+#endif
+
+ // This is all we can do in Win7 and below.
+ if (version < base::win::Version::WIN8)
+ return true;
+
+ SetProcessMitigationPolicyFunction set_process_mitigation_policy =
+ reinterpret_cast<SetProcessMitigationPolicyFunction>(
+ ::GetProcAddress(module, "SetProcessMitigationPolicy"));
+ if (!set_process_mitigation_policy)
+ return false;
+
+ // Enable ASLR policies.
+ if (flags & MITIGATION_RELOCATE_IMAGE) {
+ PROCESS_MITIGATION_ASLR_POLICY policy = {};
+ policy.EnableForceRelocateImages = true;
+ policy.DisallowStrippedImages =
+ (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) ==
+ MITIGATION_RELOCATE_IMAGE_REQUIRED;
+
+ if (!set_process_mitigation_policy(ProcessASLRPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable strict handle policies.
+ if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
+ PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = {};
+ policy.HandleExceptionsPermanentlyEnabled =
+ policy.RaiseExceptionOnInvalidHandleReference = true;
+
+ if (!set_process_mitigation_policy(ProcessStrictHandleCheckPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable system call policies.
+ if (flags & MITIGATION_WIN32K_DISABLE) {
+ PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
+ policy.DisallowWin32kSystemCalls = true;
+
+ if (!set_process_mitigation_policy(ProcessSystemCallDisablePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable extension point policies.
+ if (flags & MITIGATION_EXTENSION_POINT_DISABLE) {
+ PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = {};
+ policy.DisableExtensionPoints = true;
+
+ if (!set_process_mitigation_policy(ProcessExtensionPointDisablePolicy,
+ &policy, sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN8_1)
+ return true;
+
+ // Enable dynamic code policies.
+ if (!IsRunning32bitEmulatedOnArm64() &&
+ (flags & MITIGATION_DYNAMIC_CODE_DISABLE)) {
+ // Verify caller is not accidentally setting both mutually exclusive
+ // policies.
+ DCHECK(!(flags & MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT));
+ PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy = {};
+ policy.ProhibitDynamicCode = true;
+
+ if (!set_process_mitigation_policy(ProcessDynamicCodePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN10)
+ return true;
+
+ // Enable font policies.
+ if (flags & MITIGATION_NONSYSTEM_FONT_DISABLE) {
+ PROCESS_MITIGATION_FONT_DISABLE_POLICY policy = {};
+ policy.DisableNonSystemFonts = true;
+
+ if (!set_process_mitigation_policy(ProcessFontDisablePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN10_TH2)
+ return true;
+
+ // Enable binary signing policies.
+ if (flags & MITIGATION_FORCE_MS_SIGNED_BINS) {
+ PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY policy = {};
+ // Allow only MS signed binaries.
+ policy.MicrosoftSignedOnly = true;
+ // NOTE: there are two other flags available to allow
+ // 1) Only Windows Store signed.
+ // 2) MS-signed, Win Store signed, and WHQL signed binaries.
+ // Support not added at the moment.
+ if (!set_process_mitigation_policy(ProcessSignaturePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable image load policies.
+ if (flags & MITIGATION_IMAGE_LOAD_NO_REMOTE ||
+ flags & MITIGATION_IMAGE_LOAD_NO_LOW_LABEL ||
+ flags & MITIGATION_IMAGE_LOAD_PREFER_SYS32) {
+ PROCESS_MITIGATION_IMAGE_LOAD_POLICY policy = {};
+ if (flags & MITIGATION_IMAGE_LOAD_NO_REMOTE)
+ policy.NoRemoteImages = true;
+ if (flags & MITIGATION_IMAGE_LOAD_NO_LOW_LABEL)
+ policy.NoLowMandatoryLabelImages = true;
+ // PreferSystem32 is only supported on >= Anniversary.
+ if (version >= base::win::Version::WIN10_RS1 &&
+ flags & MITIGATION_IMAGE_LOAD_PREFER_SYS32) {
+ policy.PreferSystem32Images = true;
+ }
+
+ if (!set_process_mitigation_policy(ProcessImageLoadPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN10_RS1)
+ return true;
+
+ // Enable dynamic code policies.
+ // Per-thread opt-out is only supported on >= Anniversary (RS1).
+ if (!IsRunning32bitEmulatedOnArm64() &&
+ (flags & MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT)) {
+ // Verify caller is not accidentally setting both mutually exclusive
+ // policies.
+ DCHECK(!(flags & MITIGATION_DYNAMIC_CODE_DISABLE));
+ PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy = {};
+ policy.ProhibitDynamicCode = true;
+ policy.AllowThreadOptOut = true;
+
+ if (!set_process_mitigation_policy(ProcessDynamicCodePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ApplyMitigationsToCurrentThread(MitigationFlags flags) {
+ if (!CanSetMitigationsPerThread(flags))
+ return false;
+
+ base::win::Version version = base::win::GetVersion();
+
+ if (version < base::win::Version::WIN10_RS1)
+ return true;
+
+ // Enable dynamic code per-thread policies.
+ if (flags & MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD) {
+ DWORD thread_policy = THREAD_DYNAMIC_CODE_ALLOW;
+
+ // NOTE: SetThreadInformation API only exists on >= Win8. Dynamically
+ // get function handle.
+ SetThreadInformationFunction set_thread_info_function =
+ reinterpret_cast<SetThreadInformationFunction>(::GetProcAddress(
+ ::GetModuleHandleA("kernel32.dll"), "SetThreadInformation"));
+ if (!set_thread_info_function)
+ return false;
+
+ // NOTE: Must use the pseudo-handle here, a thread HANDLE won't work.
+ if (!set_thread_info_function(::GetCurrentThread(), ThreadDynamicCodePolicy,
+ &thread_policy, sizeof(thread_policy))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
+ DWORD64* policy_flags,
+ size_t* size) {
+ base::win::Version version = base::win::GetVersion();
+
+ // |policy_flags| is a two-element array of DWORD64s. Ensure mitigation flags
+ // from PROCESS_CREATION_MITIGATION_POLICY2_* go into the second value. If
+ // any flags are set in value 2, update |size| to include both elements.
+ DWORD64* policy_value_1 = &policy_flags[0];
+ DWORD64* policy_value_2 = &policy_flags[1];
+ *policy_value_1 = 0;
+ *policy_value_2 = 0;
+
+#if defined(_WIN64)
+ *size = sizeof(*policy_flags);
+#elif defined(_M_IX86)
+ // A 64-bit flags attribute is illegal on 32-bit Win 7.
+ if (version < base::win::Version::WIN8)
+ *size = sizeof(DWORD);
+ else
+ *size = sizeof(*policy_flags);
+#else
+#error This platform is not supported.
+#endif
+
+// DEP and SEHOP are not valid for 64-bit Windows
+#if !defined(_WIN64)
+ if (flags & MITIGATION_DEP) {
+ *policy_value_1 |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE;
+ if (!(flags & MITIGATION_DEP_NO_ATL_THUNK))
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE;
+ }
+
+ if (flags & MITIGATION_SEHOP)
+ *policy_value_1 |= PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE;
+#endif
+
+ // Win 7
+ if (version < base::win::Version::WIN8)
+ return;
+
+ // Everything >= Win8, do not return before the end of the function where
+ // the final policy bitmap is sanity checked against what is supported on this
+ // machine. The API required to do so is only available since Win8.
+
+ // Mitigations >= Win8:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN8) {
+ if (flags & MITIGATION_RELOCATE_IMAGE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON;
+ if (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS;
+ }
+ }
+
+ if (flags & MITIGATION_HEAP_TERMINATE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_BOTTOM_UP_ASLR) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_HIGH_ENTROPY_ASLR) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_WIN32K_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_EXTENSION_POINT_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win8.1:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN8_1) {
+ if (flags & MITIGATION_DYNAMIC_CODE_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_CONTROL_FLOW_GUARD_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF;
+ }
+ }
+
+ // Mitigations >= Win10:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10) {
+ if (flags & MITIGATION_NONSYSTEM_FONT_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 TH2:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_TH2) {
+ if (flags & MITIGATION_FORCE_MS_SIGNED_BINS) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_IMAGE_LOAD_NO_REMOTE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_IMAGE_LOAD_NO_LOW_LABEL) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 RS1 ("Anniversary"):
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_RS1) {
+ if (flags & MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON_ALLOW_OPT_OUT;
+ }
+
+ if (flags & MITIGATION_IMAGE_LOAD_PREFER_SYS32) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 RS3 ("Fall Creator's"):
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_RS3) {
+ // Note: This mitigation requires not only Win10 1709, but also the January
+ // 2018 security updates and any applicable firmware updates from the
+ // OEM device manufacturer.
+ // Note: Applying this mitigation attribute on creation will succeed, even
+ // if
+ // the underlying hardware does not support the implementation.
+ // Windows just does its best under the hood for the given hardware.
+ if (flags & MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION) {
+ *policy_value_2 |=
+ PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 20H1
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_20H1) {
+ if (flags & MITIGATION_CET_COMPAT_MODE && IsUserCetWin32Available()) {
+ *policy_value_2 |=
+ PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON;
+ }
+ }
+
+ // When done setting policy flags, sanity check supported policies on this
+ // machine, and then update |size|.
+
+ const ULONG64* supported = GetSupportedMitigations();
+
+ *policy_value_1 = *policy_value_1 & supported[0];
+ *policy_value_2 = *policy_value_2 & supported[1];
+
+ // Only include the second element in |size| if it is non-zero. Else,
+ // UpdateProcThreadAttribute() will return a failure when setting policies.
+ if (*policy_value_2 && version >= base::win::Version::WIN10_RS2) {
+ *size = sizeof(*policy_flags) * 2;
+ }
+
+ return;
+}
+
+MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags) {
+ base::win::Version version = base::win::GetVersion();
+
+ // Windows 7.
+ if (version < base::win::Version::WIN8) {
+ return flags & (MITIGATION_BOTTOM_UP_ASLR | MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HEAP_TERMINATE);
+ }
+
+ // Windows 8 and above.
+ return flags & (MITIGATION_BOTTOM_UP_ASLR | MITIGATION_DLL_SEARCH_ORDER);
+}
+
+bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
+ MitigationFlags flags) {
+// This is a hack to fake a weak bottom-up ASLR on 32-bit Windows.
+#if !defined(_WIN64)
+ if (flags & MITIGATION_BOTTOM_UP_ASLR) {
+ unsigned int limit;
+ GetRandom(&limit);
+ char* ptr = 0;
+ const size_t kMask64k = 0xFFFF;
+ // Random range (512k-16.5mb) in 64k steps.
+ const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k);
+ while (ptr < end) {
+ MEMORY_BASIC_INFORMATION memory_info;
+ if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info)))
+ break;
+ size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k,
+ static_cast<SIZE_T>(end - ptr));
+ if (ptr && memory_info.State == MEM_FREE)
+ ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS);
+ ptr += size;
+ }
+ }
+#endif
+
+ return true;
+}
+
+MitigationFlags GetAllowedPostStartupProcessMitigations() {
+ return MITIGATION_HEAP_TERMINATE |
+ MITIGATION_DEP |
+ MITIGATION_DEP_NO_ATL_THUNK |
+ MITIGATION_RELOCATE_IMAGE |
+ MITIGATION_RELOCATE_IMAGE_REQUIRED |
+ MITIGATION_BOTTOM_UP_ASLR |
+ MITIGATION_STRICT_HANDLE_CHECKS |
+ MITIGATION_EXTENSION_POINT_DISABLE |
+ MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HARDEN_TOKEN_IL_POLICY |
+ MITIGATION_WIN32K_DISABLE |
+ MITIGATION_DYNAMIC_CODE_DISABLE |
+ MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT |
+ MITIGATION_FORCE_MS_SIGNED_BINS |
+ MITIGATION_NONSYSTEM_FONT_DISABLE |
+ MITIGATION_IMAGE_LOAD_NO_REMOTE |
+ MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
+ MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+}
+
+bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) {
+ // All of these mitigations can be enabled after startup.
+ return !(flags & ~GetAllowedPostStartupProcessMitigations());
+}
+
+bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) {
+ // These mitigations cannot be enabled prior to startup.
+ return !(flags &
+ (MITIGATION_STRICT_HANDLE_CHECKS | MITIGATION_DLL_SEARCH_ORDER));
+}
+
+bool CanSetMitigationsPerThread(MitigationFlags flags) {
+ // If any flags EXCEPT these are set, fail.
+ if (flags & ~(MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD))
+ return false;
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations.h
new file mode 100644
index 0000000000..26dc42e358
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+#define SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include "sandbox/win/src/security_level.h"
+
+namespace sandbox {
+
+// Sets the mitigation policy for the current process, ignoring any settings
+// that are invalid for the current version of Windows.
+bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags);
+
+// Sets the mitigation policy for the current thread, ignoring any settings
+// that are invalid for the current version of Windows.
+bool ApplyMitigationsToCurrentThread(MitigationFlags flags);
+
+// Returns the flags that must be enforced after startup for the current OS
+// version.
+MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags);
+
+// Converts sandbox flags to the PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES
+// policy flags used by UpdateProcThreadAttribute().
+// - |policy_flags| must be a two-element DWORD64 array.
+// - |size| is a size_t so that it can be passed directly into
+// UpdateProcThreadAttribute().
+void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
+ DWORD64* policy_flags,
+ size_t* size);
+
+// Adds mitigations that need to be performed on the suspended target process
+// before execution begins.
+bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
+ MitigationFlags flags);
+
+// Returns the list of process mitigations which can be enabled post startup.
+MitigationFlags GetAllowedPostStartupProcessMitigations();
+
+// Returns true if all the supplied flags can be set after a process starts.
+bool CanSetProcessMitigationsPostStartup(MitigationFlags flags);
+
+// Returns true if all the supplied flags can be set before a process starts.
+bool CanSetProcessMitigationsPreStartup(MitigationFlags flags);
+
+// Returns true if all the supplied flags can be set on the current thread.
+bool CanSetMitigationsPerThread(MitigationFlags flags);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
new file mode 100644
index 0000000000..4d2d94b865
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
@@ -0,0 +1,592 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_dispatcher.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/unguessable_token.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+
+namespace sandbox {
+
+namespace {
+
+base::UnsafeSharedMemoryRegion GetSharedMemoryRegion(
+ const ClientInfo& client_info,
+ HANDLE handle,
+ size_t size) {
+ HANDLE dup_handle = nullptr;
+ intptr_t handle_int = reinterpret_cast<intptr_t>(handle);
+ if (handle_int <= 0 ||
+ !::DuplicateHandle(client_info.process, handle, ::GetCurrentProcess(),
+ &dup_handle, 0, false, DUPLICATE_SAME_ACCESS)) {
+ return {};
+ }
+ // The raw handle returned from ::DuplicateHandle() must be wrapped in a
+ // base::PlatformSharedMemoryRegion.
+ base::subtle::PlatformSharedMemoryRegion platform_region =
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ base::win::ScopedHandle(dup_handle),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe, size,
+ base::UnguessableToken::Create());
+ // |platform_region| can now be wrapped in a base::UnsafeSharedMemoryRegion.
+ return base::UnsafeSharedMemoryRegion::Deserialize(
+ std::move(platform_region));
+}
+
+} // namespace
+
+ProtectedVideoOutput::~ProtectedVideoOutput() {
+ ProcessMitigationsWin32KLockdownPolicy::DestroyOPMProtectedOutputAction(
+ handle_);
+}
+
+scoped_refptr<ProtectedVideoOutput>
+ProcessMitigationsWin32KDispatcher::GetProtectedVideoOutput(
+ HANDLE handle,
+ bool destroy_output) {
+ base::AutoLock lock(protected_outputs_lock_);
+ scoped_refptr<ProtectedVideoOutput> result;
+ auto it = protected_outputs_.find(handle);
+ if (it != protected_outputs_.end()) {
+ result = it->second;
+ if (destroy_output)
+ protected_outputs_.erase(it);
+ }
+ return result;
+}
+
+ProcessMitigationsWin32KDispatcher::ProcessMitigationsWin32KDispatcher(
+ PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall enum_display_monitors_params = {
+ {IpcTag::USER_ENUMDISPLAYMONITORS, {INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::EnumDisplayMonitors)};
+ static const IPCCall get_monitor_info_params = {
+ {IpcTag::USER_GETMONITORINFO, {VOIDPTR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetMonitorInfo)};
+ static const IPCCall get_suggested_output_size_params = {
+ {IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE, {WCHAR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::
+ GetSuggestedOPMProtectedOutputArraySize)};
+ static const IPCCall create_protected_outputs_params = {
+ {IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS, {WCHAR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::CreateOPMProtectedOutputs)};
+ static const IPCCall get_cert_size_params = {
+ {IpcTag::GDI_GETCERTIFICATESIZE, {WCHAR_TYPE, VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetCertificateSize)};
+ static const IPCCall get_cert_params = {
+ {IpcTag::GDI_GETCERTIFICATE,
+ {WCHAR_TYPE, VOIDPTR_TYPE, VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetCertificate)};
+ static const IPCCall destroy_protected_output_params = {
+ {IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT, {VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::DestroyOPMProtectedOutput)};
+ static const IPCCall get_random_number_params = {
+ {IpcTag::GDI_GETOPMRANDOMNUMBER, {VOIDPTR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetOPMRandomNumber)};
+ static const IPCCall set_signing_key_params = {
+ {IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ {VOIDPTR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::
+ SetOPMSigningKeyAndSequenceNumbers)};
+ static const IPCCall configure_protected_output_params = {
+ {IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT, {VOIDPTR_TYPE, VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::ConfigureOPMProtectedOutput)};
+ static const IPCCall get_information_params = {
+ {IpcTag::GDI_GETOPMINFORMATION, {VOIDPTR_TYPE, VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetOPMInformation)};
+
+ ipc_calls_.push_back(enum_display_monitors_params);
+ ipc_calls_.push_back(get_monitor_info_params);
+ ipc_calls_.push_back(get_suggested_output_size_params);
+ ipc_calls_.push_back(create_protected_outputs_params);
+ ipc_calls_.push_back(get_cert_size_params);
+ ipc_calls_.push_back(get_cert_params);
+ ipc_calls_.push_back(destroy_protected_output_params);
+ ipc_calls_.push_back(get_random_number_params);
+ ipc_calls_.push_back(set_signing_key_params);
+ ipc_calls_.push_back(configure_protected_output_params);
+ ipc_calls_.push_back(get_information_params);
+}
+
+ProcessMitigationsWin32KDispatcher::~ProcessMitigationsWin32KDispatcher() {}
+
+bool ProcessMitigationsWin32KDispatcher::SetupService(
+ InterceptionManager* manager,
+ IpcTag service) {
+ if (!(policy_base_->GetProcessMitigations() &
+ sandbox::MITIGATION_WIN32K_DISABLE)) {
+ return false;
+ }
+
+ switch (service) {
+ case IpcTag::GDI_GDIDLLINITIALIZE: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GdiDllInitialize,
+ GDIINITIALIZE_ID, 12)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::GDI_GETSTOCKOBJECT: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetStockObject,
+ GETSTOCKOBJECT_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_REGISTERCLASSW: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", RegisterClassW,
+ REGISTERCLASSW_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_ENUMDISPLAYMONITORS: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", EnumDisplayMonitors,
+ ENUMDISPLAYMONITORS_ID, 20)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_ENUMDISPLAYDEVICES: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", EnumDisplayDevicesA,
+ ENUMDISPLAYDEVICESA_ID, 20)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_GETMONITORINFO: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", GetMonitorInfoA,
+ GETMONITORINFOA_ID, 12)) {
+ return false;
+ }
+
+ if (!INTERCEPT_EAT(manager, L"user32.dll", GetMonitorInfoW,
+ GETMONITORINFOW_ID, 12)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", CreateOPMProtectedOutputs,
+ CREATEOPMPROTECTEDOUTPUTS_ID, 24)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETCERTIFICATE:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificate,
+ GETCERTIFICATE_ID, 20)) {
+ return false;
+ }
+ if (base::win::GetVersion() < base::win::Version::WIN10_TH2)
+ return true;
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificateByHandle,
+ GETCERTIFICATEBYHANDLE_ID, 20)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETCERTIFICATESIZE:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificateSize,
+ GETCERTIFICATESIZE_ID, 16)) {
+ return false;
+ }
+ if (base::win::GetVersion() < base::win::Version::WIN10_TH2)
+ return true;
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificateSizeByHandle,
+ GETCERTIFICATESIZEBYHANDLE_ID, 16)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", DestroyOPMProtectedOutput,
+ DESTROYOPMPROTECTEDOUTPUT_ID, 8)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", ConfigureOPMProtectedOutput,
+ CONFIGUREOPMPROTECTEDOUTPUT_ID, 20)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETOPMINFORMATION:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetOPMInformation,
+ GETOPMINFORMATION_ID, 16)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETOPMRANDOMNUMBER:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetOPMRandomNumber,
+ GETOPMRANDOMNUMBER_ID, 12)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll",
+ GetSuggestedOPMProtectedOutputArraySize,
+ GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_ID, 12)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll",
+ SetOPMSigningKeyAndSequenceNumbers,
+ SETOPMSIGNINGKEYANDSEQUENCENUMBERS_ID, 12)) {
+ return false;
+ }
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+bool ProcessMitigationsWin32KDispatcher::EnumDisplayMonitors(
+ IPCInfo* ipc,
+ CountedBuffer* buffer) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.win32_result = ERROR_ACCESS_DENIED;
+ return true;
+ }
+
+ if (buffer->Size() != sizeof(EnumMonitorsResult)) {
+ ipc->return_info.win32_result = ERROR_INVALID_PARAMETER;
+ return true;
+ }
+ HMONITOR monitor_list[kMaxEnumMonitors] = {};
+
+ uint32_t monitor_list_count =
+ ProcessMitigationsWin32KLockdownPolicy::EnumDisplayMonitorsAction(
+ *ipc->client_info, monitor_list, kMaxEnumMonitors);
+ DCHECK(monitor_list_count <= kMaxEnumMonitors);
+
+ EnumMonitorsResult* result =
+ static_cast<EnumMonitorsResult*>(buffer->Buffer());
+ for (uint32_t monitor_pos = 0; monitor_pos < monitor_list_count;
+ ++monitor_pos) {
+ result->monitors[monitor_pos] = monitor_list[monitor_pos];
+ }
+ result->monitor_count = monitor_list_count;
+ ipc->return_info.win32_result = 0;
+
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetMonitorInfo(IPCInfo* ipc,
+ void* monitor,
+ CountedBuffer* buffer) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.win32_result = ERROR_ACCESS_DENIED;
+ return true;
+ }
+ if (buffer->Size() != sizeof(MONITORINFOEXW)) {
+ ipc->return_info.win32_result = ERROR_INVALID_PARAMETER;
+ return true;
+ }
+ MONITORINFO* monitor_info = static_cast<MONITORINFO*>(buffer->Buffer());
+ // Ensure size is valid and represents what we've been passed.
+ monitor_info->cbSize = buffer->Size();
+ HMONITOR monitor_handle = static_cast<HMONITOR>(monitor);
+ bool success = ProcessMitigationsWin32KLockdownPolicy::GetMonitorInfoAction(
+ *ipc->client_info, monitor_handle, monitor_info);
+ ipc->return_info.win32_result =
+ success ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::
+ GetSuggestedOPMProtectedOutputArraySize(IPCInfo* ipc,
+ std::wstring* device_name) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status = ProcessMitigationsWin32KLockdownPolicy::
+ GetSuggestedOPMProtectedOutputArraySizeAction(
+ *ipc->client_info, *device_name,
+ &ipc->return_info.extended[0].unsigned_int);
+ if (!status) {
+ ipc->return_info.extended_count = 1;
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::CreateOPMProtectedOutputs(
+ IPCInfo* ipc,
+ std::wstring* device_name,
+ CountedBuffer* protected_outputs) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ uint32_t output_array_size = 0;
+ uint32_t input_array_size = protected_outputs->Size() / sizeof(HANDLE);
+ HANDLE* handles = static_cast<HANDLE*>(protected_outputs->Buffer());
+ NTSTATUS status =
+ ProcessMitigationsWin32KLockdownPolicy::CreateOPMProtectedOutputsAction(
+ *ipc->client_info, *device_name, handles, input_array_size,
+ &output_array_size);
+ if (!status && (output_array_size <= input_array_size)) {
+ base::AutoLock lock(protected_outputs_lock_);
+ ipc->return_info.extended_count = 1;
+ ipc->return_info.extended[0].unsigned_int = output_array_size;
+ for (uint32_t handle_pos = 0; handle_pos < output_array_size;
+ handle_pos++) {
+ HANDLE handle = handles[handle_pos];
+ protected_outputs_[handle] = new ProtectedVideoOutput(handle);
+ }
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetCertificateSize(
+ IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (device_name->size() > 0) {
+ status = ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeAction(
+ *ipc->client_info, *device_name,
+ &ipc->return_info.extended[0].unsigned_int);
+ } else {
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (output) {
+ status = ProcessMitigationsWin32KLockdownPolicy::
+ GetCertificateSizeByHandleAction(
+ *ipc->client_info, output.get()->handle(),
+ &ipc->return_info.extended[0].unsigned_int);
+ }
+ }
+ if (!status) {
+ ipc->return_info.extended_count = 1;
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetCertificate(
+ IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output,
+ void* shared_buffer_handle,
+ uint32_t shared_buffer_size) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Don't let caller map an arbitrarily large buffer into memory.
+ if (shared_buffer_size > kProtectedVideoOutputSectionSize) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ base::UnsafeSharedMemoryRegion region = GetSharedMemoryRegion(
+ *ipc->client_info, shared_buffer_handle, shared_buffer_size);
+ if (!region.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ base::WritableSharedMemoryMapping cert_data = region.Map();
+ if (!cert_data.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (device_name->size() > 0) {
+ status = ProcessMitigationsWin32KLockdownPolicy::GetCertificateAction(
+ *ipc->client_info, *device_name,
+ cert_data.GetMemoryAsSpan<BYTE>().data(), shared_buffer_size);
+ } else {
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (output) {
+ status =
+ ProcessMitigationsWin32KLockdownPolicy::GetCertificateByHandleAction(
+ *ipc->client_info, output.get()->handle(),
+ cert_data.GetMemoryAsSpan<BYTE>().data(), shared_buffer_size);
+ }
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::DestroyOPMProtectedOutput(
+ IPCInfo* ipc,
+ void* protected_output) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, true);
+ NTSTATUS status = STATUS_INVALID_HANDLE;
+ if (output)
+ status = STATUS_SUCCESS;
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetOPMRandomNumber(
+ IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* random_number) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (!output || random_number->Size() != sizeof(DXGKMDT_OPM_RANDOM_NUMBER)) {
+ status = STATUS_INVALID_PARAMETER;
+ } else {
+ status = ProcessMitigationsWin32KLockdownPolicy::GetOPMRandomNumberAction(
+ *ipc->client_info, output.get()->handle(), random_number->Buffer());
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::SetOPMSigningKeyAndSequenceNumbers(
+ IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* parameters) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (!output ||
+ parameters->Size() != sizeof(DXGKMDT_OPM_ENCRYPTED_PARAMETERS)) {
+ status = STATUS_INVALID_PARAMETER;
+ } else {
+ status = ProcessMitigationsWin32KLockdownPolicy::
+ SetOPMSigningKeyAndSequenceNumbersAction(
+ *ipc->client_info, output.get()->handle(), parameters->Buffer());
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::ConfigureOPMProtectedOutput(
+ IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (!output) {
+ ipc->return_info.nt_status = STATUS_INVALID_HANDLE;
+ return true;
+ };
+ base::UnsafeSharedMemoryRegion region =
+ GetSharedMemoryRegion(*ipc->client_info, shared_buffer_handle,
+ sizeof(DXGKMDT_OPM_CONFIGURE_PARAMETERS));
+ if (!region.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ base::WritableSharedMemoryMapping buffer = region.Map();
+ if (!buffer.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status =
+ ProcessMitigationsWin32KLockdownPolicy::ConfigureOPMProtectedOutputAction(
+ *ipc->client_info, output.get()->handle(), buffer.memory());
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetOPMInformation(
+ IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (!output) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ size_t shared_buffer_size =
+ std::max(sizeof(DXGKMDT_OPM_GET_INFO_PARAMETERS),
+ sizeof(DXGKMDT_OPM_REQUESTED_INFORMATION));
+
+ base::UnsafeSharedMemoryRegion region = GetSharedMemoryRegion(
+ *ipc->client_info, shared_buffer_handle, shared_buffer_size);
+ if (!region.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ base::WritableSharedMemoryMapping buffer = region.Map();
+ if (!buffer.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ DXGKMDT_OPM_REQUESTED_INFORMATION requested_info = {};
+ NTSTATUS status =
+ ProcessMitigationsWin32KLockdownPolicy::GetOPMInformationAction(
+ *ipc->client_info, output.get()->handle(), buffer.memory(),
+ &requested_info);
+ if (!status) {
+ memcpy(buffer.memory(), &requested_info,
+ sizeof(DXGKMDT_OPM_REQUESTED_INFORMATION));
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+} // namespace sandbox
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
new file mode 100644
index 0000000000..0714191fcf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// Class to maintain a reference to a OPM protected output handle.
+class ProtectedVideoOutput
+ : public base::RefCountedThreadSafe<ProtectedVideoOutput> {
+ public:
+ ProtectedVideoOutput(HANDLE handle) : handle_(handle) {}
+ HANDLE handle() { return handle_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<ProtectedVideoOutput>;
+ ~ProtectedVideoOutput();
+
+ HANDLE handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProtectedVideoOutput);
+};
+
+// This class sets up intercepts for the Win32K lockdown policy which is set
+// on Windows 8 and beyond.
+class ProcessMitigationsWin32KDispatcher : public Dispatcher {
+ public:
+ explicit ProcessMitigationsWin32KDispatcher(PolicyBase* policy_base);
+ ~ProcessMitigationsWin32KDispatcher() override;
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ bool EnumDisplayMonitors(IPCInfo* ipc, CountedBuffer* buffer);
+ bool GetMonitorInfo(IPCInfo* ipc, void* monitor, CountedBuffer* buffer);
+ bool GetSuggestedOPMProtectedOutputArraySize(IPCInfo* ipc,
+ std::wstring* device_name);
+ bool CreateOPMProtectedOutputs(IPCInfo* ipc,
+ std::wstring* device_name,
+ CountedBuffer* protected_outputs);
+ bool GetCertificateSize(IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output);
+ bool GetCertificate(IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output,
+ void* shared_buffer_handle,
+ uint32_t shared_buffer_size);
+ bool DestroyOPMProtectedOutput(IPCInfo* ipc, void* protected_output);
+ bool GetOPMRandomNumber(IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* random_number);
+ bool SetOPMSigningKeyAndSequenceNumbers(IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* parameters);
+ bool ConfigureOPMProtectedOutput(IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle);
+ bool GetOPMInformation(IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle);
+
+ private:
+ scoped_refptr<ProtectedVideoOutput> GetProtectedVideoOutput(
+ HANDLE handle,
+ bool destroy_output);
+
+ PolicyBase* policy_base_;
+ std::map<HANDLE, scoped_refptr<ProtectedVideoOutput>> protected_outputs_;
+ base::Lock protected_outputs_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMitigationsWin32KDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc
new file mode 100644
index 0000000000..1a16d85adf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc
@@ -0,0 +1,523 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+#include <algorithm>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+namespace {
+
+// Implement a simple shared memory class as we can't use the base one.
+class ScopedSharedMemory {
+ public:
+ ScopedSharedMemory(uint32_t size) : memory_(nullptr) {
+ handle_.Set(::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr,
+ PAGE_READWRITE | SEC_COMMIT, 0, size,
+ nullptr));
+ if (handle_.IsValid()) {
+ memory_ = ::MapViewOfFile(handle_.Get(), FILE_MAP_READ | FILE_MAP_WRITE,
+ 0, 0, size);
+ }
+ }
+ ~ScopedSharedMemory() {
+ if (memory_)
+ ::UnmapViewOfFile(memory_);
+ }
+
+ void* handle() { return handle_.Get(); }
+ void* memory() { return memory_; }
+ bool IsValid() { return handle_.IsValid() && memory_; }
+
+ private:
+ base::win::ScopedHandle handle_;
+ void* memory_;
+};
+
+void UnicodeStringToString(PUNICODE_STRING unicode_string,
+ std::wstring* result) {
+ *result = std::wstring(
+ unicode_string->Buffer,
+ unicode_string->Buffer +
+ (unicode_string->Length / sizeof(unicode_string->Buffer[0])));
+}
+
+bool CallMonitorInfo(HMONITOR monitor, MONITORINFOEXW* monitor_info_ptr) {
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return false;
+
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return false;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ InOutCountedBuffer buffer(monitor_info_ptr, sizeof(*monitor_info_ptr));
+ ResultCode code = CrossCall(ipc, IpcTag::USER_GETMONITORINFO,
+ static_cast<void*>(monitor), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return false;
+
+ if (answer.win32_result != ERROR_SUCCESS)
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+BOOL WINAPI
+TargetGdiDllInitialize(GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason) {
+ return true;
+}
+
+HGDIOBJ WINAPI
+TargetGetStockObject(GetStockObjectFunction orig_get_stock_object, int object) {
+ return nullptr;
+}
+
+ATOM WINAPI
+TargetRegisterClassW(RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class) {
+ return true;
+}
+
+BOOL WINAPI TargetEnumDisplayMonitors(EnumDisplayMonitorsFunction,
+ HDC hdc,
+ LPCRECT lprcClip,
+ MONITORENUMPROC lpfnEnum,
+ LPARAM dwData) {
+ if (!lpfnEnum || hdc || lprcClip) {
+ return false;
+ }
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return false;
+
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return false;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = 0;
+ EnumMonitorsResult result = {};
+ InOutCountedBuffer result_buffer(&result, sizeof(result));
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::USER_ENUMDISPLAYMONITORS, result_buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return false;
+
+ if (answer.win32_result)
+ return false;
+
+ if (result.monitor_count > kMaxEnumMonitors)
+ return false;
+
+ for (uint32_t monitor_pos = 0; monitor_pos < result.monitor_count;
+ ++monitor_pos) {
+ bool continue_enum =
+ lpfnEnum(result.monitors[monitor_pos], nullptr, nullptr, dwData);
+ if (!continue_enum)
+ return false;
+ }
+
+ return true;
+}
+
+BOOL WINAPI TargetEnumDisplayDevicesA(EnumDisplayDevicesAFunction,
+ LPCSTR lpDevice,
+ DWORD iDevNum,
+ PDISPLAY_DEVICEA lpDisplayDevice,
+ DWORD dwFlags) {
+ return false;
+}
+
+BOOL WINAPI TargetGetMonitorInfoA(GetMonitorInfoAFunction,
+ HMONITOR monitor,
+ MONITORINFO* monitor_info_ptr) {
+ if (!monitor_info_ptr)
+ return false;
+ DWORD size = monitor_info_ptr->cbSize;
+ if (size != sizeof(MONITORINFO) && size != sizeof(MONITORINFOEXA))
+ return false;
+ MONITORINFOEXW monitor_info_tmp = {};
+ monitor_info_tmp.cbSize = sizeof(monitor_info_tmp);
+ bool success = CallMonitorInfo(monitor, &monitor_info_tmp);
+ if (!success)
+ return false;
+ memcpy(monitor_info_ptr, &monitor_info_tmp, sizeof(*monitor_info_ptr));
+ if (size == sizeof(MONITORINFOEXA)) {
+ MONITORINFOEXA* monitor_info_exa =
+ reinterpret_cast<MONITORINFOEXA*>(monitor_info_ptr);
+ if (!::WideCharToMultiByte(CP_ACP, 0, monitor_info_tmp.szDevice, -1,
+ monitor_info_exa->szDevice,
+ sizeof(monitor_info_exa->szDevice), nullptr,
+ nullptr)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+BOOL WINAPI TargetGetMonitorInfoW(GetMonitorInfoWFunction,
+ HMONITOR monitor,
+ LPMONITORINFO monitor_info_ptr) {
+ if (!monitor_info_ptr)
+ return false;
+ DWORD size = monitor_info_ptr->cbSize;
+ if (size != sizeof(MONITORINFO) && size != sizeof(MONITORINFOEXW))
+ return false;
+ MONITORINFOEXW monitor_info_tmp = {};
+ monitor_info_tmp.cbSize = sizeof(monitor_info_tmp);
+ if (!CallMonitorInfo(monitor, &monitor_info_tmp))
+ return false;
+ memcpy(monitor_info_ptr, &monitor_info_tmp, size);
+ return true;
+}
+
+static NTSTATUS GetCertificateCommon(
+ PUNICODE_STRING device_name,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_size) {
+ // Don't support arbitrarily large certificate buffers.
+ if (certificate_size > kProtectedVideoOutputSectionSize)
+ return STATUS_INVALID_PARAMETER;
+ if (certificate_type != DXGKMDT_OPM_CERTIFICATE)
+ return STATUS_INVALID_PARAMETER;
+ if (device_name && device_name->Length == 0)
+ return STATUS_INVALID_PARAMETER;
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ ScopedSharedMemory buffer(certificate_size);
+ if (!buffer.IsValid())
+ return STATUS_INVALID_PARAMETER;
+ std::wstring device_name_str;
+ void* protected_output_handle = nullptr;
+ if (device_name) {
+ if (device_name->Length == 0)
+ return STATUS_INVALID_PARAMETER;
+ UnicodeStringToString(device_name, &device_name_str);
+ } else {
+ protected_output_handle = protected_output;
+ }
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETCERTIFICATE, device_name_str.c_str(),
+ protected_output_handle, buffer.handle(),
+ static_cast<uint32_t>(certificate_size), &answer);
+
+ if (code != SBOX_ALL_OK) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (!answer.nt_status)
+ memcpy(certificate, buffer.memory(), certificate_size);
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetGetCertificate(GetCertificateFunction,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_size) {
+ return GetCertificateCommon(device_name, nullptr, certificate_type,
+ certificate, certificate_size);
+}
+
+static NTSTATUS GetCertificateSizeCommon(
+ PUNICODE_STRING device_name,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ if (certificate_type != DXGKMDT_OPM_CERTIFICATE)
+ return STATUS_INVALID_PARAMETER;
+ if (device_name && device_name->Length == 0)
+ return STATUS_INVALID_PARAMETER;
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ std::wstring device_name_str;
+ void* protected_output_handle = nullptr;
+ if (device_name) {
+ UnicodeStringToString(device_name, &device_name_str);
+ } else {
+ protected_output_handle = protected_output;
+ }
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETCERTIFICATESIZE, device_name_str.c_str(),
+ protected_output_handle, &answer);
+
+ if (code != SBOX_ALL_OK) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (!answer.nt_status)
+ *certificate_length = answer.extended[0].unsigned_int;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI
+TargetGetCertificateSize(GetCertificateSizeFunction,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ return GetCertificateSizeCommon(device_name, nullptr, certificate_type,
+ certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateByHandle(
+ GetCertificateByHandleFunction orig_get_certificate_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length) {
+ return GetCertificateCommon(nullptr, protected_output, certificate_type,
+ certificate, certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSizeByHandle(
+ GetCertificateSizeByHandleFunction orig_get_certificate_size_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ return GetCertificateSizeCommon(nullptr, protected_output, certificate_type,
+ certificate_length);
+}
+
+NTSTATUS WINAPI
+TargetDestroyOPMProtectedOutput(DestroyOPMProtectedOutputFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code = CrossCall(ipc, IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT,
+ static_cast<void*>(protected_output), &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetConfigureOPMProtectedOutput(
+ ConfigureOPMProtectedOutputFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters) {
+ // Don't support additional parameters.
+ if (additional_parameters_size > 0)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ ScopedSharedMemory buffer(sizeof(*parameters));
+ if (!buffer.IsValid())
+ return STATUS_INVALID_PARAMETER;
+ memcpy(buffer.memory(), parameters, sizeof(*parameters));
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT,
+ static_cast<void*>(protected_output), buffer.handle(), &answer);
+
+ if (code != SBOX_ALL_OK) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetGetOPMInformation(
+ GetOPMInformationFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information) {
+ size_t max_size =
+ std::max(sizeof(*parameters), sizeof(*requested_information));
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ ScopedSharedMemory buffer(base::checked_cast<uint32_t>(max_size));
+ if (!buffer.IsValid())
+ return STATUS_INVALID_PARAMETER;
+ memcpy(buffer.memory(), parameters, sizeof(*parameters));
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETOPMINFORMATION,
+ static_cast<void*>(protected_output), buffer.handle(), &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ if (!answer.nt_status) {
+ memcpy(requested_information, buffer.memory(),
+ sizeof(*requested_information));
+ }
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI
+TargetGetOPMRandomNumber(GetOPMRandomNumberFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ InOutCountedBuffer buffer(random_number, sizeof(*random_number));
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETOPMRANDOMNUMBER,
+ static_cast<void*>(protected_output), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetGetSuggestedOPMProtectedOutputArraySize(
+ GetSuggestedOPMProtectedOutputArraySizeFunction,
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_size) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ std::wstring device_name_str;
+ UnicodeStringToString(device_name, &device_name_str);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ device_name_str.c_str(), &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ if (!answer.nt_status)
+ *suggested_output_size = answer.extended[0].unsigned_int;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers(
+ SetOPMSigningKeyAndSequenceNumbersFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ DXGKMDT_OPM_ENCRYPTED_PARAMETERS temp_parameters = *parameters;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ InOutCountedBuffer buffer(&temp_parameters, sizeof(temp_parameters));
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ static_cast<void*>(protected_output), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI
+TargetCreateOPMProtectedOutputs(CreateOPMProtectedOutputsFunction,
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD outputs_array_size,
+ DWORD* output_size,
+ OPM_PROTECTED_OUTPUT_HANDLE* outputs_array) {
+ if (vos != DXGKMDT_OPM_VOS_OPM_SEMANTICS)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ base::CheckedNumeric<uint32_t> array_size = outputs_array_size;
+ array_size *= sizeof(HANDLE);
+ if (!array_size.IsValid())
+ return STATUS_INVALID_PARAMETER;
+
+ InOutCountedBuffer buffer(outputs_array, array_size.ValueOrDie());
+ std::wstring device_name_str;
+ UnicodeStringToString(device_name, &device_name_str);
+ ResultCode code = CrossCall(ipc, IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS,
+ device_name_str.c_str(), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ if (!answer.nt_status)
+ *output_size = answer.extended[0].unsigned_int;
+
+ return answer.nt_status;
+}
+
+} // namespace sandbox
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
new file mode 100644
index 0000000000..befcda2767
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+
+#include <windows.h>
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+const DWORD kProtectedVideoOutputSectionSize = 16 * 1024;
+const DWORD kMaxEnumMonitors = 32;
+
+struct EnumMonitorsResult {
+ DWORD monitor_count;
+ HMONITOR monitors[kMaxEnumMonitors];
+};
+
+typedef BOOL(WINAPI* GdiDllInitializeFunction)(HANDLE dll,
+ DWORD reason,
+ LPVOID reserved);
+
+using GetStockObjectFunction = decltype(&::GetStockObject);
+
+using RegisterClassWFunction = decltype(&::RegisterClassW);
+
+using EnumDisplayMonitorsFunction = decltype(&::EnumDisplayMonitors);
+
+using EnumDisplayDevicesAFunction = decltype(&::EnumDisplayDevicesA);
+
+using GetMonitorInfoWFunction = decltype(&::GetMonitorInfoW);
+using GetMonitorInfoAFunction = decltype(&::GetMonitorInfoA);
+
+extern "C" {
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGdiDllInitialize(GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI
+TargetGetStockObject(GetStockObjectFunction orig_get_stock_object, int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI
+TargetRegisterClassW(RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetEnumDisplayMonitors(
+ EnumDisplayMonitorsFunction orig_enum_display_monitors_function,
+ HDC hdc,
+ LPCRECT clip_rect,
+ MONITORENUMPROC enum_function,
+ LPARAM data);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetEnumDisplayDevicesA(
+ EnumDisplayDevicesAFunction orig_enum_display_devices_function,
+ LPCSTR device,
+ DWORD device_number,
+ PDISPLAY_DEVICEA display_device,
+ DWORD flags);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoA(GetMonitorInfoAFunction orig_get_monitor_info_function,
+ HMONITOR monitor,
+ LPMONITORINFO monitor_info);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoW(GetMonitorInfoWFunction orig_get_monitor_info_function,
+ HMONITOR monitor,
+ LPMONITORINFO monitor_info);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetCreateOPMProtectedOutputs(
+ CreateOPMProtectedOutputsFunction orig_create_proceted_outputs_function,
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD protected_output_array_size,
+ DWORD* output_array_size,
+ OPM_PROTECTED_OUTPUT_HANDLE* protected_outputs);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificate(GetCertificateFunction orig_get_certificate_function,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSize(
+ GetCertificateSizeFunction orig_get_certificate_size_function,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateByHandle(
+ GetCertificateByHandleFunction orig_get_certificate_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSizeByHandle(
+ GetCertificateSizeByHandleFunction orig_get_certificate_size_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetDestroyOPMProtectedOutput(
+ DestroyOPMProtectedOutputFunction orig_destroy_protected_output_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetConfigureOPMProtectedOutput(
+ ConfigureOPMProtectedOutputFunction
+ origin_configure_protected_output_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetOPMInformation(
+ GetOPMInformationFunction origin_get_information_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetOPMRandomNumber(
+ GetOPMRandomNumberFunction orig_get_random_number_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetSuggestedOPMProtectedOutputArraySize(
+ GetSuggestedOPMProtectedOutputArraySizeFunction
+ orig_get_suggested_size_function,
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers(
+ SetOPMSigningKeyAndSequenceNumbersFunction orig_set_signing_keys_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc
new file mode 100644
index 0000000000..8ab915ec33
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc
@@ -0,0 +1,410 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+namespace sandbox {
+
+namespace {
+
+// Define GUIDs for OPM APIs
+const GUID DXGKMDT_OPM_GET_CONNECTOR_TYPE = {
+ 0x81d0bfd5,
+ 0x6afe,
+ 0x48c2,
+ {0x99, 0xc0, 0x95, 0xa0, 0x8f, 0x97, 0xc5, 0xda}};
+const GUID DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES = {
+ 0x38f2a801,
+ 0x9a6c,
+ 0x48bb,
+ {0x91, 0x07, 0xb6, 0x69, 0x6e, 0x6f, 0x17, 0x97}};
+const GUID DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL = {
+ 0xb2075857,
+ 0x3eda,
+ 0x4d5d,
+ {0x88, 0xdb, 0x74, 0x8f, 0x8c, 0x1a, 0x05, 0x49}};
+const GUID DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL = {
+ 0x1957210a,
+ 0x7766,
+ 0x452a,
+ {0xb9, 0x9a, 0xd2, 0x7a, 0xed, 0x54, 0xf0, 0x3a}};
+const GUID DXGKMDT_OPM_SET_PROTECTION_LEVEL = {
+ 0x9bb9327c,
+ 0x4eb5,
+ 0x4727,
+ {0x9f, 0x00, 0xb4, 0x2b, 0x09, 0x19, 0xc0, 0xda}};
+
+void StringToUnicodeString(PUNICODE_STRING unicode_string,
+ const std::wstring& device_name) {
+ static RtlInitUnicodeStringFunction RtlInitUnicodeString;
+ if (!RtlInitUnicodeString) {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
+ GetProcAddress(ntdll, "RtlInitUnicodeString"));
+ }
+ RtlInitUnicodeString(unicode_string, device_name.c_str());
+}
+
+struct MonitorListState {
+ HMONITOR* monitor_list;
+ uint32_t monitor_list_size;
+ uint32_t monitor_list_pos;
+};
+
+BOOL CALLBACK DisplayMonitorEnumProc(HMONITOR monitor,
+ HDC hdc_monitor,
+ LPRECT rect_monitor,
+ LPARAM data) {
+ MonitorListState* state = reinterpret_cast<MonitorListState*>(data);
+ if (state->monitor_list_pos >= state->monitor_list_size)
+ return false;
+ state->monitor_list[state->monitor_list_pos++] = monitor;
+ return true;
+}
+
+template <typename T>
+T GetExportedFunc(const wchar_t* libname, const char* name) {
+ OverrideForTestFunction test_override =
+ ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback();
+ if (test_override)
+ return reinterpret_cast<T>(test_override(name));
+
+ static T func = nullptr;
+ if (!func) {
+ func =
+ reinterpret_cast<T>(::GetProcAddress(::GetModuleHandle(libname), name));
+ DCHECK(!!func);
+ }
+ return func;
+}
+
+#define GDIFUNC(name) GetExportedFunc<name##Function>(L"gdi32.dll", #name)
+#define USERFUNC(name) GetExportedFunc<name##Function>(L"user32.dll", #name)
+
+struct ValidateMonitorParams {
+ HMONITOR monitor;
+ std::wstring device_name;
+ bool result;
+};
+
+bool GetMonitorDeviceName(HMONITOR monitor, std::wstring* device_name) {
+ MONITORINFOEXW monitor_info = {};
+ monitor_info.cbSize = sizeof(monitor_info);
+ if (!USERFUNC(GetMonitorInfoW)(monitor, &monitor_info))
+ return false;
+ if (monitor_info.szDevice[CCHDEVICENAME - 1] != 0)
+ return false;
+ *device_name = monitor_info.szDevice;
+ return true;
+}
+
+BOOL CALLBACK ValidateMonitorEnumProc(HMONITOR monitor,
+ HDC,
+ LPRECT,
+ LPARAM data) {
+ ValidateMonitorParams* valid_params =
+ reinterpret_cast<ValidateMonitorParams*>(data);
+ std::wstring device_name;
+ bool result = false;
+ if (valid_params->device_name.empty()) {
+ result = monitor == valid_params->monitor;
+ } else if (GetMonitorDeviceName(monitor, &device_name)) {
+ result = device_name == valid_params->device_name;
+ }
+ valid_params->result = result;
+ if (!result)
+ return true;
+ return false;
+}
+
+bool IsValidMonitorOrDeviceName(HMONITOR monitor, const wchar_t* device_name) {
+ ValidateMonitorParams params = {};
+ params.monitor = monitor;
+ if (device_name)
+ params.device_name = device_name;
+ USERFUNC(EnumDisplayMonitors)
+ (nullptr, nullptr, ValidateMonitorEnumProc,
+ reinterpret_cast<LPARAM>(&params));
+ return params.result;
+}
+
+} // namespace
+
+OverrideForTestFunction
+ ProcessMitigationsWin32KLockdownPolicy::override_callback_;
+
+bool ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule rule(FAKE_SUCCESS);
+ if (!policy->AddRule(IpcTag::GDI_GDIDLLINITIALIZE, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETSTOCKOBJECT, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::USER_REGISTERCLASSW, &rule))
+ return false;
+ if (semantics != TargetPolicy::IMPLEMENT_OPM_APIS)
+ return true;
+ if (!policy->AddRule(IpcTag::USER_ENUMDISPLAYMONITORS, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::USER_ENUMDISPLAYDEVICES, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::USER_GETMONITORINFO, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETCERTIFICATE, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETCERTIFICATESIZE, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETOPMINFORMATION, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETOPMRANDOMNUMBER, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS, &rule))
+ return false;
+ return true;
+}
+
+uint32_t ProcessMitigationsWin32KLockdownPolicy::EnumDisplayMonitorsAction(
+ const ClientInfo& client_info,
+ HMONITOR* monitor_list,
+ uint32_t monitor_list_size) {
+ MonitorListState state = {monitor_list, monitor_list_size, 0};
+ USERFUNC(EnumDisplayMonitors)
+ (nullptr, nullptr, DisplayMonitorEnumProc, reinterpret_cast<LPARAM>(&state));
+ return state.monitor_list_pos;
+}
+
+bool ProcessMitigationsWin32KLockdownPolicy::GetMonitorInfoAction(
+ const ClientInfo& client_info,
+ HMONITOR monitor,
+ MONITORINFO* monitor_info_ptr) {
+ if (!IsValidMonitorOrDeviceName(monitor, nullptr))
+ return false;
+ MONITORINFOEXW monitor_info = {};
+ monitor_info.cbSize = sizeof(MONITORINFOEXW);
+
+ bool success = USERFUNC(GetMonitorInfoW)(
+ monitor, reinterpret_cast<MONITORINFO*>(&monitor_info));
+ if (success)
+ memcpy(monitor_info_ptr, &monitor_info, sizeof(monitor_info));
+ return success;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
+ GetSuggestedOPMProtectedOutputArraySizeAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* suggested_array_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+ DWORD suggested_array_size_dword = 0;
+ NTSTATUS status = GDIFUNC(GetSuggestedOPMProtectedOutputArraySize)(
+ &unicode_device_name, &suggested_array_size_dword);
+ if (!status)
+ *suggested_array_size = suggested_array_size_dword;
+ return status;
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::CreateOPMProtectedOutputsAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ HANDLE* protected_outputs,
+ uint32_t array_input_size,
+ uint32_t* array_output_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+ DWORD output_size = 0;
+
+ NTSTATUS status = GDIFUNC(CreateOPMProtectedOutputs)(
+ &unicode_device_name, DXGKMDT_OPM_VOS_OPM_SEMANTICS, array_input_size,
+ &output_size,
+ reinterpret_cast<OPM_PROTECTED_OUTPUT_HANDLE*>(protected_outputs));
+ if (!status)
+ *array_output_size = output_size;
+ return status;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* cert_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+
+ return GDIFUNC(GetCertificateSize)(&unicode_device_name,
+ DXGKMDT_OPM_CERTIFICATE,
+ reinterpret_cast<DWORD*>(cert_size));
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ BYTE* cert_data,
+ uint32_t cert_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+
+ return GDIFUNC(GetCertificate)(&unicode_device_name, DXGKMDT_OPM_CERTIFICATE,
+ cert_data, cert_size);
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeByHandleAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ uint32_t* cert_size) {
+ auto get_certificate_size_func = GDIFUNC(GetCertificateSizeByHandle);
+ if (get_certificate_size_func) {
+ return get_certificate_size_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
+ reinterpret_cast<DWORD*>(cert_size));
+ }
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateByHandleAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ BYTE* cert_data,
+ uint32_t cert_size) {
+ auto get_certificate_func = GDIFUNC(GetCertificateByHandle);
+ if (get_certificate_func) {
+ return get_certificate_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
+ cert_data, cert_size);
+ }
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMRandomNumberAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* random_number) {
+ return GDIFUNC(GetOPMRandomNumber)(
+ protected_output, static_cast<DXGKMDT_OPM_RANDOM_NUMBER*>(random_number));
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
+ SetOPMSigningKeyAndSequenceNumbersAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters) {
+ return GDIFUNC(SetOPMSigningKeyAndSequenceNumbers)(
+ protected_output,
+ static_cast<DXGKMDT_OPM_ENCRYPTED_PARAMETERS*>(parameters));
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::ConfigureOPMProtectedOutputAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr) {
+ DXGKMDT_OPM_CONFIGURE_PARAMETERS parameters;
+ memcpy(&parameters, parameters_ptr, sizeof(parameters));
+ if (parameters.guidSetting != DXGKMDT_OPM_SET_PROTECTION_LEVEL ||
+ parameters.cbParametersSize !=
+ sizeof(DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS prot_level;
+ memcpy(&prot_level, parameters.abParameters, sizeof(prot_level));
+ if (prot_level.Reserved || prot_level.Reserved2)
+ return STATUS_INVALID_PARAMETER;
+
+ if (prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_HDCP &&
+ prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ // Protection levels are same for HDCP and DPCP.
+ if (prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_OFF &&
+ prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_ON) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ return GDIFUNC(ConfigureOPMProtectedOutput)(protected_output, &parameters, 0,
+ nullptr);
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMInformationAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr,
+ void* requested_info_ptr) {
+ DXGKMDT_OPM_GET_INFO_PARAMETERS parameters;
+ memcpy(&parameters, parameters_ptr, sizeof(parameters));
+
+ bool valid_parameters = false;
+ // Validate sizes based on the type being requested.
+ if ((parameters.guidInformation == DXGKMDT_OPM_GET_CONNECTOR_TYPE ||
+ parameters.guidInformation ==
+ DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES) &&
+ parameters.cbParametersSize == 0) {
+ valid_parameters = true;
+ } else if ((parameters.guidInformation ==
+ DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL ||
+ parameters.guidInformation ==
+ DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL) &&
+ parameters.cbParametersSize == sizeof(uint32_t)) {
+ uint32_t param_value;
+ memcpy(&param_value, parameters.abParameters, sizeof(param_value));
+ if (param_value == DXGKMDT_OPM_PROTECTION_TYPE_HDCP ||
+ param_value == DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
+ valid_parameters = true;
+ }
+ }
+ if (!valid_parameters)
+ return STATUS_INVALID_PARAMETER;
+ DXGKMDT_OPM_REQUESTED_INFORMATION requested_info = {};
+ NTSTATUS status = GDIFUNC(GetOPMInformation)(protected_output, &parameters,
+ &requested_info);
+ if (!status)
+ memcpy(requested_info_ptr, &requested_info, sizeof(requested_info));
+
+ return status;
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::DestroyOPMProtectedOutputAction(
+ HANDLE protected_output) {
+ return GDIFUNC(DestroyOPMProtectedOutput)(protected_output);
+}
+
+void ProcessMitigationsWin32KLockdownPolicy::SetOverrideForTestCallback(
+ OverrideForTestFunction callback) {
+ override_callback_ = callback;
+}
+
+OverrideForTestFunction
+ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback() {
+ return override_callback_;
+}
+
+} // 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
new file mode 100644
index 0000000000..0c2b2dff95
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h
@@ -0,0 +1,91 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// A callback function type to get a function for testing.
+typedef void* (*OverrideForTestFunction)(const char* name);
+
+// This class centralizes most of the knowledge related to the process
+// mitigations Win32K lockdown policy.
+class ProcessMitigationsWin32KLockdownPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for the Win32K process mitigation policy.
+ // name is the object name, semantics is the desired semantics for the
+ // open or create and policy is the policy generator to which the rules are
+ // going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ static uint32_t EnumDisplayMonitorsAction(const ClientInfo& client_info,
+ HMONITOR* monitor_list,
+ uint32_t monitor_list_size);
+ static bool GetMonitorInfoAction(const ClientInfo& client_info,
+ HMONITOR monitor,
+ MONITORINFO* monitor_info);
+
+ static NTSTATUS GetSuggestedOPMProtectedOutputArraySizeAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* suggested_array_size);
+
+ static NTSTATUS CreateOPMProtectedOutputsAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ HANDLE* protected_outputs,
+ uint32_t array_input_size,
+ uint32_t* array_output_size);
+
+ static NTSTATUS GetCertificateSizeAction(const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* cert_size);
+ static NTSTATUS GetCertificateAction(const ClientInfo& client_info,
+ const std::wstring& device_name,
+ BYTE* cert_data,
+ uint32_t cert_size);
+ static NTSTATUS GetCertificateSizeByHandleAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ uint32_t* cert_size);
+ static NTSTATUS GetCertificateByHandleAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ BYTE* cert_data,
+ uint32_t cert_size);
+ static NTSTATUS GetOPMRandomNumberAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* random_number);
+ static NTSTATUS SetOPMSigningKeyAndSequenceNumbersAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters);
+ static NTSTATUS ConfigureOPMProtectedOutputAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr);
+ static NTSTATUS GetOPMInformationAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr,
+ void* requested_information_ptr);
+ static NTSTATUS DestroyOPMProtectedOutputAction(HANDLE protected_output);
+ static void SetOverrideForTestCallback(OverrideForTestFunction callback);
+ static OverrideForTestFunction GetOverrideForTestCallback();
+
+ private:
+ static OverrideForTestFunction override_callback_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/process_policy_test.cc
new file mode 100644
index 0000000000..c321c3a088
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_policy_test.cc
@@ -0,0 +1,548 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/memory/free_deleter.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "build/build_config.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Creates a process with the |exe| and |command| parameter using the
+// unicode and ascii version of the api.
+sandbox::SboxTestResult CreateProcessHelper(const std::wstring& exe,
+ const std::wstring& command) {
+ base::win::ScopedProcessInformation pi;
+ STARTUPINFOW si = {sizeof(si)};
+ const wchar_t* exe_name = nullptr;
+ if (!exe.empty())
+ exe_name = exe.c_str();
+
+ std::unique_ptr<wchar_t, base::FreeDeleter> writable_command(
+ _wcsdup(command.c_str()));
+
+ // Create the process with the unicode version of the API.
+ sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED;
+ PROCESS_INFORMATION temp_process_info = {};
+ if (::CreateProcessW(
+ exe_name, command.empty() ? nullptr : writable_command.get(), nullptr,
+ nullptr, false, 0, nullptr, nullptr, &si, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret1 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret1 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret1 = sandbox::SBOX_TEST_FAILED;
+ }
+ }
+
+ pi.Close();
+
+ // Do the same with the ansi version of the api
+ STARTUPINFOA sia = {sizeof(sia)};
+ sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED;
+
+ std::string narrow_cmd_line =
+ base::SysWideToMultiByte(command.c_str(), CP_UTF8);
+ if (::CreateProcessA(
+ exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str()
+ : nullptr,
+ command.empty() ? nullptr : &narrow_cmd_line[0], nullptr, nullptr,
+ false, 0, nullptr, nullptr, &sia, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret2 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret2 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret2 = sandbox::SBOX_TEST_FAILED;
+ }
+ }
+
+ if (ret1 == ret2)
+ return ret1;
+
+ return sandbox::SBOX_TEST_FAILED;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Process_RunApp1(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ // TEST 1: Try with the path in the app_name.
+ return CreateProcessHelper(path, std::wstring());
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ // TEST 2: Try with the path in the cmd_line.
+ std::wstring cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\"";
+ return CreateProcessHelper(std::wstring(), cmd_line);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ // TEST 3: Try file name in the cmd_line.
+ return CreateProcessHelper(std::wstring(), argv[0]);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // TEST 4: Try file name in the app_name and current directory sets correctly.
+ std::wstring system32 = MakePathToSys(L"", false);
+ wchar_t current_directory[MAX_PATH + 1];
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ if (!ret)
+ return SBOX_TEST_FIRST_ERROR;
+ if (ret >= MAX_PATH)
+ return SBOX_TEST_FAILED;
+
+ current_directory[ret] = L'\\';
+ current_directory[ret + 1] = L'\0';
+ if (!::SetCurrentDirectory(system32.c_str()))
+ return SBOX_TEST_SECOND_ERROR;
+
+ const int result4 = CreateProcessHelper(argv[0], std::wstring());
+ return ::SetCurrentDirectory(current_directory) ? result4 : SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp5(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ // TEST 5: Try with the path in the cmd_line and arguments.
+ std::wstring cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\" /I";
+ return CreateProcessHelper(std::wstring(), cmd_line);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // TEST 6: Try with the file_name in the cmd_line and arguments.
+ std::wstring cmd_line = argv[0];
+ cmd_line += L" /I";
+ return CreateProcessHelper(std::wstring(), cmd_line);
+}
+
+// Creates a process and checks if it's possible to get a handle to it's token.
+SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ STARTUPINFOW si = {sizeof(si)};
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessW(path.c_str(), nullptr, nullptr, nullptr, false,
+ CREATE_SUSPENDED, nullptr, nullptr, &si,
+ &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+
+ HANDLE token = nullptr;
+ bool result =
+ ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token);
+ DWORD error = ::GetLastError();
+
+ base::win::ScopedHandle token_handle(token);
+
+ if (!::TerminateProcess(pi.process_handle(), 0))
+ return SBOX_TEST_FAILED;
+
+ if (result && token)
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+// Creates a suspended process using CreateProcessA then kill it.
+SBOX_TESTS_COMMAND int Process_CreateProcessA(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ STARTUPINFOA si = {sizeof(si)};
+
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ PROCESS_INFORMATION temp_process_info = {};
+ // Create suspended to avoid popping calc.
+ if (!::CreateProcessA(base::SysWideToMultiByte(path, CP_UTF8).c_str(),
+ nullptr, nullptr, nullptr, false, CREATE_SUSPENDED,
+ nullptr, nullptr, &si, &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+
+ if (!::TerminateProcess(pi.process_handle(), 0))
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t** argv) {
+ HANDLE token;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ }
+ } else {
+ ::CloseHandle(token);
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Process_Crash(int argc, wchar_t** argv) {
+ __debugbreak();
+ return SBOX_TEST_FAILED;
+}
+// Generate a event name, used to test thread creation.
+std::wstring GenerateEventName(DWORD pid) {
+ wchar_t buff[30] = {0};
+ int res = swprintf_s(buff, sizeof(buff) / sizeof(buff[0]),
+ L"ProcessPolicyTest_%08x", pid);
+ if (-1 != res) {
+ return std::wstring(buff);
+ }
+ return std::wstring();
+}
+
+// This is the function that is called when testing thread creation.
+// It is expected to set an event that the caller is waiting on.
+DWORD WINAPI TestThreadFunc(LPVOID lpdwThreadParam) {
+ std::wstring event_name = GenerateEventName(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(lpdwThreadParam)));
+ if (!event_name.length())
+ return 1;
+ HANDLE event = ::OpenEvent(EVENT_ALL_ACCESS | EVENT_MODIFY_STATE, false,
+ event_name.c_str());
+ if (!event)
+ return 1;
+ if (!SetEvent(event))
+ return 1;
+ return 0;
+}
+
+SBOX_TESTS_COMMAND int Process_CreateThread(int argc, wchar_t** argv) {
+ DWORD pid = ::GetCurrentProcessId();
+ std::wstring event_name = GenerateEventName(pid);
+ if (!event_name.length())
+ return SBOX_TEST_FIRST_ERROR;
+ HANDLE event = ::CreateEvent(nullptr, true, false, event_name.c_str());
+ if (!event)
+ return SBOX_TEST_SECOND_ERROR;
+
+ DWORD thread_id = 0;
+ HANDLE thread = nullptr;
+ thread = ::CreateThread(nullptr, 0, &TestThreadFunc,
+ reinterpret_cast<LPVOID>(static_cast<uintptr_t>(pid)),
+ 0, &thread_id);
+
+ if (!thread)
+ return SBOX_TEST_THIRD_ERROR;
+ if (!thread_id)
+ return SBOX_TEST_FOURTH_ERROR;
+ if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
+ return SBOX_TEST_FIFTH_ERROR;
+ DWORD exit_code = 0;
+ if (!GetExitCodeThread(thread, &exit_code))
+ return SBOX_TEST_SIXTH_ERROR;
+ if (exit_code)
+ return SBOX_TEST_SEVENTH_ERROR;
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0)
+ return SBOX_TEST_FAILED;
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Creates a process and checks its exit code. Succeeds on exit code 0.
+SBOX_TESTS_COMMAND int Process_CheckExitCode(int argc, wchar_t** argv) {
+ if (argc != 3)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (!argv || !argv[0] || !argv[1] || !argv[2])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring path = MakePathToSys(argv[0], false);
+ std::wstring cmdline = argv[1];
+ std::wstring cwd = argv[2];
+
+ STARTUPINFOW si = {sizeof(si)};
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessW(path.c_str(), &cmdline[0], nullptr, nullptr, false, 0,
+ nullptr, cwd.c_str(), &si, &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+ DWORD ret = WaitForSingleObject(pi.process_handle(), 1000);
+ if (ret != WAIT_OBJECT_0)
+ return SBOX_TEST_FAILED;
+
+ DWORD exit_code;
+ if (!GetExitCodeProcess(pi.process_handle(), &exit_code))
+ return SBOX_TEST_FAILED;
+
+ if (exit_code != 0)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(ProcessPolicyTest, TestAllAccess) {
+ // Check if the "all access" rule fails to be added when the token is too
+ // powerful.
+ TestRunner runner;
+
+ // Check the failing case.
+ runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ EXPECT_EQ(SBOX_ERROR_UNSUPPORTED,
+ runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ L"this is not important"));
+
+ // Check the working case.
+ runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE);
+
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ L"this is not important"));
+}
+
+TEST(ProcessPolicyTest, CreateProcessAW) {
+ TestRunner runner;
+ std::wstring maybe_virtual_exe_path = MakePathToSys(L"findstr.exe", false);
+ std::wstring non_virtual_exe_path = MakePathToSys32(L"findstr.exe", false);
+ ASSERT_TRUE(!maybe_virtual_exe_path.empty());
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ maybe_virtual_exe_path.c_str()));
+
+ if (non_virtual_exe_path != maybe_virtual_exe_path) {
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ non_virtual_exe_path.c_str()));
+ }
+
+ // Need to add directory rules for the directories that we use in
+ // SetCurrentDirectory.
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L""));
+
+ wchar_t current_directory[MAX_PATH];
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ ASSERT_TRUE(0 != ret && ret < MAX_PATH);
+
+ wcscat_s(current_directory, MAX_PATH, L"\\");
+ EXPECT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, current_directory));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp1 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp2 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp3 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp4 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp5 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp6 calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp1 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp2 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp3 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp4 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp5 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp6 findstr.exe"));
+}
+
+// Tests that the broker correctly handles a process crashing within the job.
+// Fails on Windows ARM64: https://crbug.com/905526
+#if defined(ARCH_CPU_ARM64)
+#define MAYBE_CreateProcessCrashy DISABLED_CreateProcessCrashy
+#else
+#define MAYBE_CreateProcessCrashy CreateProcessCrashy
+#endif
+TEST(ProcessPolicyTest, MAYBE_CreateProcessCrashy) {
+ TestRunner runner;
+ EXPECT_EQ(static_cast<int>(STATUS_BREAKPOINT),
+ runner.RunTest(L"Process_Crash"));
+}
+
+TEST(ProcessPolicyTest, CreateProcessWithCWD) {
+ TestRunner runner;
+ std::wstring sys_path = MakePathToSys(L"", false);
+ while (!sys_path.empty() && sys_path.back() == L'\\')
+ sys_path.erase(sys_path.length() - 1);
+
+ std::wstring exe_path = MakePathToSys(L"cmd.exe", false);
+ std::wstring cmd_line =
+ L"\"/c if \\\"%CD%\\\" NEQ \\\"" + sys_path + L"\\\" exit 1\"";
+
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC, exe_path.c_str()));
+
+ std::wstring command =
+ L"Process_CheckExitCode cmd.exe " + cmd_line + L" " + sys_path;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str()));
+}
+
+TEST(ProcessPolicyTest, OpenToken) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) {
+ TestRunner runner;
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) {
+ TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE);
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccessNoJob) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestCreateProcessA) {
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->SetJobLevel(JOB_NONE, 0);
+ policy->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ std::wstring exe_path = MakePathToSys(L"calc.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC, exe_path.c_str()));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_CreateProcessA calc.exe"));
+}
+
+// This tests that the CreateThread works with CSRSS not locked down.
+// In other words, that the interception passes through OK.
+TEST(ProcessPolicyTest, TestCreateThreadWithCsrss) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ runner.SetDisableCsrss(false);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_CreateThread"));
+}
+
+// This tests that the CreateThread works with CSRSS locked down.
+// In other words, that the interception correctly works.
+TEST(ProcessPolicyTest, TestCreateThreadWithoutCsrss) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_CreateThread"));
+}
+
+// This tests that our CreateThread interceptors works when called directly.
+TEST(ProcessPolicyTest, TestCreateThreadOutsideSandbox) {
+ DWORD pid = ::GetCurrentProcessId();
+ std::wstring event_name = GenerateEventName(pid);
+ ASSERT_STRNE(nullptr, event_name.c_str());
+ HANDLE event = ::CreateEvent(nullptr, true, false, event_name.c_str());
+ EXPECT_NE(static_cast<HANDLE>(nullptr), event);
+
+ DWORD thread_id = 0;
+ HANDLE thread = nullptr;
+ thread = TargetCreateThread(
+ ::CreateThread, nullptr, 0, &TestThreadFunc,
+ reinterpret_cast<LPVOID>(static_cast<uintptr_t>(pid)), 0, &thread_id);
+ EXPECT_NE(static_cast<HANDLE>(nullptr), thread);
+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(thread, INFINITE));
+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, INFINITE));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc
new file mode 100644
index 0000000000..7f0b0a6542
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc
@@ -0,0 +1,275 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_thread_dispatcher.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/sandbox.h"
+
+namespace {
+
+// Extracts the application name from a command line.
+//
+// The application name is the first element of the command line. If
+// there is no quotes, the first element is delimited by the first space.
+// If there are quotes, the first element is delimited by the quotes.
+//
+// The create process call is smarter than us. It tries really hard to launch
+// the process even if the command line is wrong. For example:
+// "c:\program files\test param" will first try to launch c:\program.exe then
+// c:\program files\test.exe. We don't do that, we stop after at the first
+// space when there is no quotes.
+std::wstring GetPathFromCmdLine(const std::wstring& cmd_line) {
+ std::wstring exe_name;
+ // Check if it starts with '"'.
+ if (cmd_line[0] == L'\"') {
+ // Find the position of the second '"', this terminates the path.
+ std::wstring::size_type pos = cmd_line.find(L'\"', 1);
+ if (std::wstring::npos == pos)
+ return cmd_line;
+ exe_name = cmd_line.substr(1, pos - 1);
+ } else {
+ // There is no '"', that means that the appname is terminated at the
+ // first space.
+ std::wstring::size_type pos = cmd_line.find(L' ');
+ if (std::wstring::npos == pos) {
+ // There is no space, the cmd_line contains only the app_name
+ exe_name = cmd_line;
+ } else {
+ exe_name = cmd_line.substr(0, pos);
+ }
+ }
+
+ return exe_name;
+}
+
+// Returns true is the path in parameter is relative. False if it's
+// absolute.
+bool IsPathRelative(const std::wstring& path) {
+ // A path is Relative if it's not a UNC path beginnning with \\ or a
+ // path beginning with a drive. (i.e. X:\)
+ if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
+ return false;
+ return true;
+}
+
+// Converts a relative path to an absolute path.
+bool ConvertToAbsolutePath(const std::wstring& child_current_directory,
+ bool use_env_path,
+ std::wstring* path) {
+ wchar_t file_buffer[MAX_PATH];
+ wchar_t* file_part = nullptr;
+
+ // Here we should start by looking at the path where the child application was
+ // started. We don't have this information yet.
+ DWORD result = 0;
+ if (use_env_path) {
+ // Try with the complete path
+ result = ::SearchPath(nullptr, path->c_str(), nullptr, MAX_PATH,
+ file_buffer, &file_part);
+ }
+
+ if (0 == result) {
+ // Try with the current directory of the child
+ result = ::SearchPath(child_current_directory.c_str(), path->c_str(),
+ nullptr, MAX_PATH, file_buffer, &file_part);
+ }
+
+ if (0 == result || result >= MAX_PATH)
+ return false;
+
+ *path = file_buffer;
+ return true;
+}
+
+} // namespace
+namespace sandbox {
+
+ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall open_thread = {
+ {IpcTag::NTOPENTHREAD, {UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenThread)};
+
+ static const IPCCall open_process = {
+ {IpcTag::NTOPENPROCESS, {UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcess)};
+
+ static const IPCCall process_token = {
+ {IpcTag::NTOPENPROCESSTOKEN, {VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessToken)};
+
+ static const IPCCall process_tokenex = {
+ {IpcTag::NTOPENPROCESSTOKENEX, {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessTokenEx)};
+
+ static const IPCCall create_params = {
+ {IpcTag::CREATEPROCESSW,
+ {WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::CreateProcessW)};
+
+ // NOTE(liamjm): 2nd param is size_t: Using VOIDPTR_TYPE as they are
+ // the same size on windows.
+ static_assert(sizeof(size_t) == sizeof(void*),
+ "VOIDPTR_TYPE not same size as size_t");
+ static const IPCCall create_thread_params = {
+ {IpcTag::CREATETHREAD,
+ {VOIDPTR_TYPE, VOIDPTR_TYPE, VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::CreateThread)};
+
+ ipc_calls_.push_back(open_thread);
+ ipc_calls_.push_back(open_process);
+ ipc_calls_.push_back(process_token);
+ ipc_calls_.push_back(process_tokenex);
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(create_thread_params);
+}
+
+bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ switch (service) {
+ case IpcTag::NTOPENTHREAD:
+ case IpcTag::NTOPENPROCESS:
+ case IpcTag::NTOPENPROCESSTOKEN:
+ case IpcTag::NTOPENPROCESSTOKENEX:
+ case IpcTag::CREATETHREAD:
+ // There is no explicit policy for these services.
+ NOTREACHED();
+ return false;
+
+ case IpcTag::CREATEPROCESSW:
+ return INTERCEPT_EAT(manager, kKerneldllName, CreateProcessW,
+ CREATE_PROCESSW_ID, 44) &&
+ INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
+ CREATE_PROCESSA_ID, 44);
+
+ default:
+ return false;
+ }
+}
+
+bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc,
+ uint32_t desired_access,
+ uint32_t thread_id) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenThreadAction(
+ *ipc->client_info, desired_access, thread_id, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc,
+ uint32_t desired_access,
+ uint32_t process_id) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessAction(
+ *ipc->client_info, desired_access, process_id, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc,
+ HANDLE process,
+ uint32_t desired_access) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(
+ *ipc->client_info, process, desired_access, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(
+ *ipc->client_info, process, desired_access, attributes, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc,
+ std::wstring* name,
+ std::wstring* cmd_line,
+ std::wstring* cur_dir,
+ std::wstring* target_cur_dir,
+ CountedBuffer* info) {
+ if (sizeof(PROCESS_INFORMATION) != info->Size())
+ return false;
+
+ // Check if there is an application name.
+ std::wstring exe_name;
+ if (!name->empty())
+ exe_name = *name;
+ else
+ exe_name = GetPathFromCmdLine(*cmd_line);
+
+ if (IsPathRelative(exe_name)) {
+ if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
+ // Cannot find the path. Maybe the file does not exist.
+ ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
+ return true;
+ }
+ }
+
+ const wchar_t* const_exe_name = exe_name.c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(const_exe_name);
+
+ EvalResult eval =
+ policy_base_->EvalPolicy(IpcTag::CREATEPROCESSW, params.GetBase());
+
+ PROCESS_INFORMATION* proc_info =
+ reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
+ // Here we force the app_name to be the one we used for the policy lookup.
+ // If our logic was wrong, at least we wont allow create a random process.
+ DWORD ret = ProcessPolicy::CreateProcessWAction(
+ eval, *ipc->client_info, exe_name, *cmd_line, *target_cur_dir, proc_info);
+
+ ipc->return_info.win32_result = ret;
+ return true;
+}
+
+bool ThreadProcessDispatcher::CreateThread(IPCInfo* ipc,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags) {
+ if (!start_address) {
+ return false;
+ }
+
+ HANDLE handle;
+ DWORD ret = ProcessPolicy::CreateThreadAction(
+ *ipc->client_info, stack_size, start_address, parameter, creation_flags,
+ nullptr, &handle);
+
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h
new file mode 100644
index 0000000000..07466c46e2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles process and thread-related IPC calls.
+class ThreadProcessDispatcher : public Dispatcher {
+ public:
+ explicit ThreadProcessDispatcher(PolicyBase* policy_base);
+ ~ThreadProcessDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtOpenThread() in the target.
+ 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_t desired_access,
+ uint32_t process_id);
+
+ // Processes IPC requests from calls to NtOpenProcessToken() in the target.
+ 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_t desired_access,
+ uint32_t attributes);
+
+ // Processes IPC requests coming from calls to CreateProcessW() in the target.
+ bool CreateProcessW(IPCInfo* ipc,
+ std::wstring* name,
+ std::wstring* cmd_line,
+ std::wstring* cur_dir,
+ std::wstring* target_cur_dir,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to CreateThread() in the target.
+ bool CreateThread(IPCInfo* ipc,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
new file mode 100644
index 0000000000..0bd3d5a8d2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
@@ -0,0 +1,520 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_thread_interception.h"
+
+#include <stdint.h>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Hooks NtOpenThread and proxy the call to the broker if it's trying to
+// open a thread in the same process.
+NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
+ PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status =
+ orig_OpenThread(thread, desired_access, object_attributes, client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenThread");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32_t thread_id = 0;
+ bool should_break = false;
+ __try {
+ // We support only the calls for the current process
+ if (client_id->UniqueProcess)
+ should_break = true;
+
+ // Object attributes should be nullptr or empty.
+ if (!should_break && object_attributes) {
+ if (object_attributes->Attributes || object_attributes->ObjectName ||
+ object_attributes->RootDirectory ||
+ object_attributes->SecurityDescriptor ||
+ object_attributes->SecurityQualityOfService) {
+ should_break = true;
+ }
+ }
+
+ thread_id = static_cast<uint32_t>(
+ reinterpret_cast<ULONG_PTR>(client_id->UniqueThread));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ if (should_break)
+ break;
+
+ if (!ValidParameter(thread, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENTHREAD, desired_access,
+ thread_id, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // The nt_status here is most likely STATUS_INVALID_CID because
+ // in the broker we set the process id in the CID (client ID) param
+ // to be the current process. If you try to open a thread from another
+ // process you will get this INVALID_CID error. On the other hand, if you
+ // try to open a thread in your own process, it should return success.
+ // We don't want to return STATUS_INVALID_CID here, so we return the
+ // return of the original open thread status, which is most likely
+ // STATUS_ACCESS_DENIED.
+ break;
+
+ __try {
+ // Write the output parameters.
+ *thread = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ mozilla::sandboxing::LogAllowed("NtOpenThread");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+// Hooks NtOpenProcess and proxy the call to the broker if it's trying to
+// open the current process.
+NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
+ PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status =
+ orig_OpenProcess(process, desired_access, object_attributes, client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32_t process_id = 0;
+ bool should_break = false;
+ __try {
+ // Object attributes should be nullptr or empty.
+ if (!should_break && object_attributes) {
+ if (object_attributes->Attributes || object_attributes->ObjectName ||
+ object_attributes->RootDirectory ||
+ object_attributes->SecurityDescriptor ||
+ object_attributes->SecurityQualityOfService) {
+ should_break = true;
+ }
+ }
+
+ process_id = static_cast<uint32_t>(
+ reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ if (should_break)
+ break;
+
+ if (!ValidParameter(process, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESS, desired_access,
+ process_id, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *process = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessToken(process, desired_access, token);
+ if (NT_SUCCESS(status))
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenProcessToken");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKEN, process,
+ desired_access, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ mozilla::sandboxing::LogAllowed("NtOpenProcessToken");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access,
+ handle_attributes, token);
+ if (NT_SUCCESS(status))
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenProcessTokenEx");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKENEX, process,
+ desired_access, handle_attributes, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ mozilla::sandboxing::LogAllowed("NtOpenProcessTokenEx");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
+ LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ if (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 {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ const wchar_t* cur_dir = nullptr;
+
+ wchar_t this_current_directory[MAX_PATH];
+ DWORD result = ::GetCurrentDirectory(MAX_PATH, this_current_directory);
+ if (0 != result && result < MAX_PATH)
+ cur_dir = this_current_directory;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ InOutCountedBuffer proc_info(process_information,
+ sizeof(PROCESS_INFORMATION));
+
+ ResultCode code =
+ CrossCall(ipc, IpcTag::CREATEPROCESSW, application_name, command_line,
+ cur_dir, current_directory, proc_info, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return false;
+
+ mozilla::sandboxing::LogAllowed("CreateProcessW", application_name);
+ return true;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return false;
+}
+
+BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
+ LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() &&
+ orig_CreateProcessA(application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags,
+ environment, current_directory, startup_info,
+ process_information)) {
+ 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 {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ // Convert the input params to unicode.
+ UNICODE_STRING* cmd_unicode = nullptr;
+ UNICODE_STRING* app_unicode = nullptr;
+ UNICODE_STRING* cwd_unicode = nullptr;
+ if (command_line) {
+ cmd_unicode = AnsiToUnicode(command_line);
+ if (!cmd_unicode)
+ break;
+ }
+
+ if (application_name) {
+ app_unicode = AnsiToUnicode(application_name);
+ if (!app_unicode) {
+ operator delete(cmd_unicode, NT_ALLOC);
+ break;
+ }
+ }
+
+ if (current_directory) {
+ cwd_unicode = AnsiToUnicode(current_directory);
+ if (!cwd_unicode) {
+ operator delete(cmd_unicode, NT_ALLOC);
+ operator delete(app_unicode, NT_ALLOC);
+ break;
+ }
+ }
+
+ const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : nullptr;
+ const wchar_t* app_name = app_unicode ? app_unicode->Buffer : nullptr;
+ const wchar_t* cwd = cwd_unicode ? cwd_unicode->Buffer : nullptr;
+ const wchar_t* cur_dir = nullptr;
+
+ wchar_t target_current_directory[MAX_PATH];
+ DWORD result = ::GetCurrentDirectory(MAX_PATH, target_current_directory);
+ if (0 != result && result < MAX_PATH)
+ cur_dir = target_current_directory;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ InOutCountedBuffer proc_info(process_information,
+ sizeof(PROCESS_INFORMATION));
+
+ ResultCode code = CrossCall(ipc, IpcTag::CREATEPROCESSW, app_name, cmd_line,
+ cur_dir, cwd, proc_info, &answer);
+
+ operator delete(cmd_unicode, NT_ALLOC);
+ operator delete(app_unicode, NT_ALLOC);
+ operator delete(cwd_unicode, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return false;
+
+ mozilla::sandboxing::LogAllowed("CreateProcessA", application_name);
+ return true;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return false;
+}
+
+HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id) {
+ HANDLE hThread = nullptr;
+
+ TargetServices* target_services = SandboxFactory::GetTargetServices();
+ if (!target_services || target_services->GetState()->IsCsrssConnected()) {
+ hThread = orig_CreateThread(thread_attributes, stack_size, start_address,
+ parameter, creation_flags, thread_id);
+ if (hThread)
+ return hThread;
+ }
+
+ DWORD original_error = ::GetLastError();
+ do {
+ if (!target_services)
+ break;
+
+ // We don't trust that the IPC can work this early.
+ if (!target_services->GetState()->InitCalled())
+ break;
+
+ __try {
+ if (thread_id && !ValidParameter(thread_id, sizeof(*thread_id), WRITE))
+ break;
+
+ if (!start_address)
+ break;
+ // We don't support thread_attributes not being null.
+ if (thread_attributes)
+ break;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ // NOTE: we don't pass the thread_attributes through. This matches the
+ // approach in CreateProcess and in CreateThreadInternal().
+ ResultCode code = CrossCall(ipc, IpcTag::CREATETHREAD,
+ reinterpret_cast<LPVOID>(stack_size),
+ reinterpret_cast<LPVOID>(start_address),
+ parameter, creation_flags, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return nullptr;
+
+ __try {
+ if (thread_id)
+ *thread_id = ::GetThreadId(answer.handle);
+ return answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ ::SetLastError(original_error);
+ return nullptr;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.h b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.h
new file mode 100644
index 0000000000..2608d7d31e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_PROCESS_THREAD_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_PROCESS_THREAD_INTERCEPTION_H_
+
+#include <windows.h>
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+namespace {
+
+using CreateProcessWFunction = decltype(&::CreateProcessW);
+
+using CreateProcessAFunction = decltype(&::CreateProcessA);
+
+using CreateThreadFunction = decltype(&::CreateThread);
+
+using GetUserDefaultLCIDFunction = decltype(&::GetUserDefaultLCID);
+
+} // namespace
+
+extern "C" {
+
+// Interception of NtOpenThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
+ PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcess on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
+ PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcessToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token);
+
+// Interception of NtOpenProcessTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token);
+
+// Interception of CreateProcessW and A in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
+ LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
+ LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateThread in kernel32.dll.
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateThread(CreateThreadFunction orig_CreateThread,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_PROCESS_THREAD_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc
new file mode 100644
index 0000000000..20146a83df
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc
@@ -0,0 +1,269 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_thread_policy.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/memory/free_deleter.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// These are the only safe rights that can be given to a sandboxed
+// process for the process created by the broker. All others are potential
+// vectors of privilege elevation.
+const DWORD kProcessRights = SYNCHRONIZE | PROCESS_QUERY_INFORMATION |
+ PROCESS_QUERY_LIMITED_INFORMATION |
+ PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME;
+
+const DWORD kThreadRights = SYNCHRONIZE | THREAD_TERMINATE |
+ THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION |
+ THREAD_QUERY_LIMITED_INFORMATION |
+ THREAD_SET_LIMITED_INFORMATION;
+
+// Creates a child process and duplicates the handles to 'target_process'. The
+// remaining parameters are the same as CreateProcess().
+bool CreateProcessExWHelper(HANDLE target_process,
+ bool give_full_access,
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ bool bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation) {
+ if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
+ lpThreadAttributes, bInheritHandles, dwCreationFlags,
+ lpEnvironment, lpCurrentDirectory, lpStartupInfo,
+ lpProcessInformation)) {
+ return false;
+ }
+
+ DWORD process_access = kProcessRights;
+ DWORD thread_access = kThreadRights;
+ if (give_full_access) {
+ process_access = PROCESS_ALL_ACCESS;
+ thread_access = THREAD_ALL_ACCESS;
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess,
+ target_process, &lpProcessInformation->hProcess,
+ process_access, false, DUPLICATE_CLOSE_SOURCE)) {
+ ::CloseHandle(lpProcessInformation->hThread);
+ return false;
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread,
+ target_process, &lpProcessInformation->hThread,
+ thread_access, false, DUPLICATE_CLOSE_SOURCE)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool ProcessPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::unique_ptr<PolicyRule> process;
+ switch (semantics) {
+ case TargetPolicy::PROCESS_MIN_EXEC: {
+ process.reset(new PolicyRule(GIVE_READONLY));
+ break;
+ };
+ case TargetPolicy::PROCESS_ALL_EXEC: {
+ process.reset(new PolicyRule(GIVE_ALLACCESS));
+ break;
+ };
+ default: { return false; };
+ }
+
+ if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::CREATEPROCESSW, process.get())) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info,
+ uint32_t desired_access,
+ uint32_t thread_id,
+ HANDLE* handle) {
+ *handle = nullptr;
+
+ NtOpenThreadFunction NtOpenThread = nullptr;
+ ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread);
+
+ OBJECT_ATTRIBUTES attributes = {0};
+ attributes.Length = sizeof(attributes);
+ CLIENT_ID client_id = {0};
+ client_id.UniqueProcess =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(client_info.process_id));
+ client_id.UniqueThread =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(thread_id));
+
+ HANDLE local_handle = nullptr;
+ NTSTATUS status =
+ NtOpenThread(&local_handle, desired_access, &attributes, &client_id);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info,
+ uint32_t desired_access,
+ uint32_t process_id,
+ HANDLE* handle) {
+ *handle = nullptr;
+
+ NtOpenProcessFunction NtOpenProcess = nullptr;
+ ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess);
+
+ if (client_info.process_id != process_id)
+ return STATUS_ACCESS_DENIED;
+
+ OBJECT_ATTRIBUTES attributes = {0};
+ attributes.Length = sizeof(attributes);
+ CLIENT_ID client_id = {0};
+ client_id.UniqueProcess =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(client_info.process_id));
+ HANDLE local_handle = nullptr;
+ NTSTATUS status =
+ NtOpenProcess(&local_handle, desired_access, &attributes, &client_id);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ HANDLE* handle) {
+ *handle = nullptr;
+ NtOpenProcessTokenFunction NtOpenProcessToken = nullptr;
+ ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken);
+
+ if (CURRENT_PROCESS != process)
+ return STATUS_ACCESS_DENIED;
+
+ HANDLE local_handle = nullptr;
+ NTSTATUS status =
+ NtOpenProcessToken(client_info.process, desired_access, &local_handle);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes,
+ HANDLE* handle) {
+ *handle = nullptr;
+ NtOpenProcessTokenExFunction NtOpenProcessTokenEx = nullptr;
+ ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx);
+
+ if (CURRENT_PROCESS != process)
+ return STATUS_ACCESS_DENIED;
+
+ HANDLE local_handle = nullptr;
+ NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access,
+ attributes, &local_handle);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& app_name,
+ const std::wstring& command_line,
+ const std::wstring& current_dir,
+ PROCESS_INFORMATION* process_info) {
+ // The only action supported is ASK_BROKER which means create the process.
+ if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(
+ _wcsdup(command_line.c_str()));
+
+ bool should_give_full_access = (GIVE_ALLACCESS == eval_result);
+
+ const wchar_t* cwd = current_dir.c_str();
+ if (current_dir.empty())
+ cwd = nullptr;
+
+ if (!CreateProcessExWHelper(client_info.process, should_give_full_access,
+ app_name.c_str(), cmd_line.get(), nullptr,
+ nullptr, false, 0, nullptr, cwd, &startup_info,
+ process_info)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+DWORD ProcessPolicy::CreateThreadAction(
+ const ClientInfo& client_info,
+ const SIZE_T stack_size,
+ const LPTHREAD_START_ROUTINE start_address,
+ const LPVOID parameter,
+ const DWORD creation_flags,
+ LPDWORD thread_id,
+ HANDLE* handle) {
+ *handle = nullptr;
+ HANDLE local_handle =
+ ::CreateRemoteThread(client_info.process, nullptr, stack_size,
+ start_address, parameter, creation_flags, thread_id);
+ if (!local_handle) {
+ return ::GetLastError();
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h
new file mode 100644
index 0000000000..f6f96dd0ff
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
+#define SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to process execution.
+class ProcessPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level.
+ // policy rule for process creation
+ // 'name' is the executable to be spawn.
+ // 'semantics' is the desired semantics.
+ // 'policy' is the policy generator to which the rules are going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Opens a thread from the child process and returns the handle.
+ // client_info contains the information about the child process,
+ // desired_access is the access requested by the child and thread_id
+ // is the thread_id to be opened.
+ // The function returns the return value of NtOpenThread.
+ static NTSTATUS OpenThreadAction(const ClientInfo& client_info,
+ uint32_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_t desired_access,
+ uint32_t process_id,
+ HANDLE* handle);
+
+ // Opens the token associated with the process and returns the duplicated
+ // handle to the child. We only allow the child processes to open its own
+ // token (using ::GetCurrentProcess()).
+ static NTSTATUS OpenProcessTokenAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ HANDLE* handle);
+
+ // Opens the token associated with the process and returns the duplicated
+ // handle to the child. We only allow the child processes to open its own
+ // token (using ::GetCurrentProcess()).
+ static NTSTATUS OpenProcessTokenExAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes,
+ HANDLE* handle);
+
+ // Processes a 'CreateProcessW()' request from the target.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'app_name' : The full path of the process to be created.
+ // 'command_line' : The command line passed to the created process.
+ // 'current_dir' : The CWD with which to spawn the child process.
+ static DWORD CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& app_name,
+ const std::wstring& command_line,
+ const std::wstring& current_dir,
+ PROCESS_INFORMATION* process_info);
+
+ // Processes a 'CreateThread()' request from the target.
+ // 'client_info' : the target process that is making the request.
+ static DWORD CreateThreadAction(const ClientInfo& client_info,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ PVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id,
+ HANDLE* handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc
new file mode 100644
index 0000000000..26d2bb3ffc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/registry_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.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 {
+
+// Builds a path using the root directory and the name.
+bool GetCompletePath(HANDLE root,
+ const std::wstring& name,
+ std::wstring* complete_name) {
+ if (root) {
+ if (!sandbox::GetPathFromHandle(root, complete_name))
+ return false;
+
+ *complete_name += L"\\";
+ *complete_name += name;
+ } else {
+ *complete_name = name;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+RegistryDispatcher::RegistryDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::NTCREATEKEY,
+ {WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtCreateKey)};
+
+ static const IPCCall open_params = {
+ {IpcTag::NTOPENKEY, {WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtOpenKey)};
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_params);
+}
+
+bool RegistryDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (IpcTag::NTCREATEKEY == service)
+ return INTERCEPT_NT(manager, NtCreateKey, CREATE_KEY_ID, 32);
+
+ if (IpcTag::NTOPENKEY == service) {
+ bool result = INTERCEPT_NT(manager, NtOpenKey, OPEN_KEY_ID, 16);
+ result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20);
+ return result;
+ }
+
+ return false;
+}
+
+bool RegistryDispatcher::NtCreateKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options) {
+ base::win::ScopedHandle root_handle;
+ std::wstring real_path = *name;
+
+ // If there is a root directory, we need to duplicate the handle to make
+ // it valid in this process.
+ if (root) {
+ if (!::DuplicateHandle(ipc->client_info->process, root,
+ ::GetCurrentProcess(), &root, 0, false,
+ DUPLICATE_SAME_ACCESS))
+ return false;
+
+ root_handle.Set(root);
+ }
+
+ if (!GetCompletePath(root, *name, &real_path))
+ return false;
+
+ const wchar_t* regname = real_path.c_str();
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::NAME] = ParamPickerMake(regname);
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTCREATEKEY, params.GetBase());
+
+ HANDLE handle;
+ NTSTATUS nt_status;
+ ULONG disposition = 0;
+ if (!RegistryPolicy::CreateKeyAction(
+ result, *ipc->client_info, *name, attributes, root, desired_access,
+ title_index, create_options, &handle, &nt_status, &disposition)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].unsigned_int = disposition;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access) {
+ base::win::ScopedHandle root_handle;
+ std::wstring real_path = *name;
+
+ // If there is a root directory, we need to duplicate the handle to make
+ // it valid in this process.
+ if (root) {
+ if (!::DuplicateHandle(ipc->client_info->process, root,
+ ::GetCurrentProcess(), &root, 0, false,
+ DUPLICATE_SAME_ACCESS))
+ return false;
+ root_handle.Set(root);
+ }
+
+ if (!GetCompletePath(root, *name, &real_path))
+ return false;
+
+ const wchar_t* regname = real_path.c_str();
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::NAME] = ParamPickerMake(regname);
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTOPENKEY, params.GetBase());
+ HANDLE handle;
+ NTSTATUS nt_status;
+ if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name,
+ attributes, root, desired_access, &handle,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h
new file mode 100644
index 0000000000..c23b95098b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_REGISTRY_DISPATCHER_H_
+#define SANDBOX_SRC_REGISTRY_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles registry-related IPC calls.
+class RegistryDispatcher : public Dispatcher {
+ public:
+ explicit RegistryDispatcher(PolicyBase* policy_base);
+ ~RegistryDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateKey in the target.
+ bool NtCreateKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ 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,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_interception.cc b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
new file mode 100644
index 0000000000..d8bb82949b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/registry_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+ PUNICODE_STRING class_name,
+ ULONG create_options,
+ PULONG disposition) {
+ // Check if the process can create it first.
+ NTSTATUS status =
+ orig_CreateKey(key, desired_access, object_attributes, title_index,
+ class_name, create_options, disposition);
+ if (NT_SUCCESS(status))
+ return status;
+
+ 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;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+ if (disposition && !ValidParameter(disposition, sizeof(ULONG), WRITE))
+ break;
+
+ // At this point we don't support class_name.
+ if (class_name && class_name->Buffer && class_name->Length)
+ break;
+
+ // We don't support creating link keys, volatile keys and backup/restore.
+ if (create_options)
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ HANDLE root_directory = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ &root_directory);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
+
+ bool query_broker = false;
+ {
+ std::unique_ptr<wchar_t, NtAllocDeleter> full_name;
+ const wchar_t* name_ptr = name.get();
+ const wchar_t* full_name_ptr = nullptr;
+
+ if (root_directory) {
+ ret = sandbox::AllocAndGetFullPath(root_directory, name.get(),
+ &full_name);
+ if (!NT_SUCCESS(ret) || !full_name)
+ break;
+ full_name_ptr = full_name.get();
+ params[OpenKey::NAME] = ParamPickerMake(full_name_ptr);
+ } else {
+ params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+ }
+
+ query_broker = QueryBroker(IpcTag::NTCREATEKEY, params.GetBase());
+ }
+
+ if (!query_broker)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ ResultCode code = CrossCall(ipc, IpcTag::NTCREATEKEY, name.get(),
+ attributes, root_directory, desired_access,
+ title_index, create_options, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // TODO(nsylvain): We should return answer.nt_status here instead
+ // of status. We can do this only after we checked the policy.
+ // otherwise we will returns ACCESS_DENIED for all paths
+ // that are not specified by a policy, even though your token allows
+ // access to that path, and the original call had a more meaningful
+ // error. Bug 4369
+ break;
+
+ __try {
+ *key = answer.handle;
+
+ if (disposition)
+ *disposition = answer.extended[0].unsigned_int;
+
+ status = answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtCreateKey",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes;
+ HANDLE root_directory;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ &root_directory);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
+
+ bool query_broker = false;
+ {
+ std::unique_ptr<wchar_t, NtAllocDeleter> full_name;
+ const wchar_t* name_ptr = name.get();
+ const wchar_t* full_name_ptr = nullptr;
+
+ if (root_directory) {
+ ret = sandbox::AllocAndGetFullPath(root_directory, name.get(),
+ &full_name);
+ if (!NT_SUCCESS(ret) || !full_name)
+ break;
+ full_name_ptr = full_name.get();
+ params[OpenKey::NAME] = ParamPickerMake(full_name_ptr);
+ } else {
+ params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+ }
+
+ query_broker = QueryBroker(IpcTag::NTOPENKEY, params.GetBase());
+ }
+
+ if (!query_broker)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENKEY, name.get(), attributes,
+ root_directory, desired_access, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // TODO(nsylvain): We should return answer.nt_status here instead
+ // of status. We can do this only after we checked the policy.
+ // otherwise we will returns ACCESS_DENIED for all paths
+ // that are not specified by a policy, even though your token allows
+ // access to that path, and the original call had a more meaningful
+ // error. Bug 4369
+ break;
+
+ __try {
+ *key = answer.handle;
+ status = answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtOpenKey[Ex]",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenKey(key, desired_access, object_attributes);
+ if (NT_SUCCESS(status))
+ return status;
+
+ 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);
+}
+
+NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options) {
+ // Check if the process can open it first.
+ NTSTATUS status =
+ orig_OpenKeyEx(key, desired_access, object_attributes, open_options);
+
+ // We do not support open_options at this time. The 2 current known values
+ // are REG_OPTION_CREATE_LINK, to open a symbolic link, and
+ // REG_OPTION_BACKUP_RESTORE to open the key with special privileges.
+ if (NT_SUCCESS(status) || open_options != 0)
+ return status;
+
+ 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);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_interception.h b/security/sandbox/chromium/sandbox/win/src/registry_interception.h
new file mode 100644
index 0000000000..f47e56ce39
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_interception.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_REGISTRY_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_REGISTRY_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtCreateKey on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey(
+ NtCreateKeyFunction orig_CreateKey, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG title_index,
+ PUNICODE_STRING class_name, ULONG create_options, PULONG disposition);
+
+// Interception of NtOpenKey on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey(
+ NtOpenKeyFunction orig_OpenKey, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// Interception of NtOpenKeyEx on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx(
+ NtOpenKeyExFunction orig_OpenKeyEx, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG open_options);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_REGISTRY_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.cc b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
new file mode 100644
index 0000000000..212ff713c4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/registry_policy.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+static const uint32_t kAllowedRegFlags =
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_READ |
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL | KEY_WOW64_64KEY |
+ KEY_WOW64_32KEY;
+
+// Opens the key referenced by |obj_attributes| with |access| and
+// checks what permission was given. Remove the WRITE flags and update
+// |access| with the new value.
+NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes,
+ DWORD* access) {
+ NtOpenKeyFunction NtOpenKey = nullptr;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
+
+ NtCloseFunction NtClose = nullptr;
+ ResolveNTFunctionPtr("NtClose", &NtClose);
+
+ NtQueryObjectFunction NtQueryObject = nullptr;
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ // Open the key.
+ HANDLE handle;
+ NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ OBJECT_BASIC_INFORMATION info = {0};
+ status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info),
+ nullptr);
+ CHECK(NT_SUCCESS(NtClose(handle)));
+ if (!NT_SUCCESS(status))
+ return status;
+
+ *access = info.GrantedAccess & kAllowedRegFlags;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ ULONG title_index,
+ UNICODE_STRING* class_name,
+ ULONG create_options,
+ ULONG* disposition,
+ HANDLE target_process) {
+ *target_key_handle = nullptr;
+ NtCreateKeyFunction NtCreateKey = nullptr;
+ ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey);
+
+ if (MAXIMUM_ALLOWED & desired_access) {
+ NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access);
+ if (!NT_SUCCESS(status))
+ return STATUS_ACCESS_DENIED;
+ }
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status =
+ NtCreateKey(&local_handle, desired_access, obj_attributes, title_index,
+ class_name, create_options, disposition);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_key_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ HANDLE target_process) {
+ *target_key_handle = nullptr;
+ NtOpenKeyFunction NtOpenKey = nullptr;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
+
+ if (MAXIMUM_ALLOWED & desired_access) {
+ NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access);
+ if (!NT_SUCCESS(status))
+ return STATUS_ACCESS_DENIED;
+ }
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_key_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool RegistryPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring resolved_name(name);
+ if (resolved_name.empty()) {
+ return false;
+ }
+
+ if (!ResolveRegistryName(resolved_name, &resolved_name))
+ return false;
+
+ name = resolved_name.c_str();
+
+ EvalResult result = ASK_BROKER;
+
+ PolicyRule open(result);
+ PolicyRule create(result);
+
+ switch (semantics) {
+ case TargetPolicy::REG_ALLOW_READONLY: {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write. Here we also support MAXIMUM_ALLOWED, but we are going
+ // to expand it to read-only before the call.
+ uint32_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;
+ }
+ case TargetPolicy::REG_ALLOW_ANY: {
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTCREATEKEY, &create)) {
+ return false;
+ }
+
+ if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTOPENKEY, &open)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool RegistryPolicy::CreateKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG* disposition) {
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ // We don't support creating link keys, volatile keys or backup/restore.
+ if (create_options) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(key, attributes, root_directory, &obj_attributes, &uni_name,
+ nullptr);
+ *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes,
+ title_index, nullptr, create_options,
+ disposition, client_info.process);
+ return true;
+}
+
+bool RegistryPolicy::OpenKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ HANDLE* handle,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(key, attributes, root_directory, &obj_attributes, &uni_name,
+ nullptr);
+ *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes,
+ client_info.process);
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.h b/security/sandbox/chromium/sandbox/win/src/registry_policy.h
new file mode 100644
index 0000000000..9a36932869
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_REGISTRY_POLICY_H__
+#define SANDBOX_SRC_REGISTRY_POLICY_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to registry policy
+class RegistryPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for registry IO, in particular open or create actions.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a create request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool CreateKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG* disposition);
+
+ // Performs the desired policy action on an open request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool OpenKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ HANDLE* handle,
+ NTSTATUS* nt_status);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc
new file mode 100644
index 0000000000..83ce586877
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <shlobj.h>
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/registry_policy.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS |
+ KEY_NOTIFY | KEY_READ | GENERIC_READ |
+ GENERIC_EXECUTE | READ_CONTROL;
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+bool IsKeyOpenForRead(HKEY handle) {
+ BINDNTDLL(NtQueryObject);
+
+ OBJECT_BASIC_INFORMATION info = {0};
+ NTSTATUS status = NtQueryObject(handle, ObjectBasicInformation, &info,
+ sizeof(info), nullptr);
+
+ if (!NT_SUCCESS(status))
+ return false;
+
+ if ((info.GrantedAccess & (~kAllowedRegFlags)) != 0)
+ return false;
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Reg_OpenKey(int argc, wchar_t** argv) {
+ if (argc != 4)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ REGSAM desired_access = 0;
+ ULONG options = 0;
+ if (wcscmp(argv[1], L"read") == 0) {
+ desired_access = KEY_READ;
+ } else if (wcscmp(argv[1], L"write") == 0) {
+ desired_access = KEY_ALL_ACCESS;
+ } else if (wcscmp(argv[1], L"link") == 0) {
+ options = REG_OPTION_CREATE_LINK;
+ desired_access = KEY_ALL_ACCESS;
+ } else {
+ desired_access = MAXIMUM_ALLOWED;
+ }
+
+ HKEY root = GetReservedKeyFromName(argv[2]);
+ HKEY key;
+ LRESULT result = 0;
+
+ if (wcscmp(argv[0], L"create") == 0)
+ result = ::RegCreateKeyEx(root, argv[3], 0, nullptr, options,
+ desired_access, nullptr, &key, nullptr);
+ else
+ result = ::RegOpenKeyEx(root, argv[3], 0, desired_access, &key);
+
+ if (ERROR_SUCCESS == result) {
+ if (MAXIMUM_ALLOWED == desired_access) {
+ if (!IsKeyOpenForRead(key)) {
+ ::RegCloseKey(key);
+ return SBOX_TEST_FAILED;
+ }
+ }
+ ::RegCloseKey(key);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (ERROR_ACCESS_DENIED == result) {
+ return SBOX_TEST_DENIED;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+TEST(RegistryPolicyTest, TestKeyAnyAccess) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
+
+ // Tests read access on key allowed for read-write.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ if (::IsUserAnAdmin()) {
+ // Tests write access on key allowed for read-write.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey create write HKEY_LOCAL_MACHINE "
+ L"software\\microsoft"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey open write HKEY_LOCAL_MACHINE software\\microsoft"));
+ }
+
+ // Tests subdirectory access on keys where we don't have subdirectory acess.
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey create read "
+ L"HKEY_LOCAL_MACHINE software\\microsoft\\Windows"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey open read "
+ L"HKEY_LOCAL_MACHINE software\\microsoft\\windows"));
+
+ // Tests to see if we can create keys where we dont have subdirectory access.
+ // This is denied.
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Microsoft\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Microsoft\\google_unit_tests");
+
+ // Tests if we need to handle differently the "\\" at the end.
+ // This is denied. We need to add both rules.
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft\\"));
+
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft\\"));
+}
+
+TEST(RegistryPolicyTest, TestKeyNoAccess) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ // Tests read access where we don't have access at all.
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software"));
+}
+
+TEST(RegistryPolicyTest, TestKeyReadOnlyAccess) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ // Tests subdirectory acess on keys where we have subdirectory acess.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey create read "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey open read "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
+
+ // Tests to see if we can create keys where we have subdirectory access.
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
+}
+
+TEST(RegistryPolicyTest, TestKeyAllAccessSubDir) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ if (::IsUserAnAdmin()) {
+ // Tests to see if we can create keys where we have subdirectory access.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
+ }
+}
+
+TEST(RegistryPolicyTest, TestKeyCreateLink) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ // Tests to see if we can create a registry link key.
+ // NOTE: In theory here we should make sure to check for SBOX_TEST_DENIED
+ // instead of !SBOX_TEST_SUCCEEDED, but unfortunately the result is not
+ // access denied. Internally RegCreateKeyEx (At least on Vista 64) tries to
+ // create the link, and we return successfully access denied, then, it
+ // decides to try to break the path in multiple chunks, and create the links
+ // one by one. In this scenario, it tries to create "HKLM\Software" as a
+ // link key, which obviously fail with STATUS_OBJECT_NAME_COLLISION, and
+ // this is what is returned to the user.
+ EXPECT_NE(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create link "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ // In case our code fails, and the call works, we need to delete the new
+ // link. There is no api for this, so we need to use the NT call.
+ HKEY key = nullptr;
+ LRESULT result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"software\\Policies\\google_unit_tests",
+ REG_OPTION_OPEN_LINK, MAXIMUM_ALLOWED, &key);
+
+ if (!result) {
+ HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
+ NtDeleteKeyFunction NtDeleteKey = reinterpret_cast<NtDeleteKeyFunction>(
+ GetProcAddress(ntdll, "NtDeleteKey"));
+ NtDeleteKey(key);
+ }
+}
+
+TEST(RegistryPolicyTest, TestKeyReadOnlyHKCU) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Software"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_USERS\\.default"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_USERS\\.default\\software"));
+
+ // Tests read access where we only have read-only access.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey create read HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey open read HKEY_CURRENT_USER software"));
+
+ // Tests write access where we only have read-only acess.
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey create write HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey open write HKEY_CURRENT_USER software"));
+
+ // Tests maximum allowed access where we only have read-only access.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create maximum_allowed HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey open maximum_allowed HKEY_CURRENT_USER software"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver.cc b/security/sandbox/chromium/sandbox/win/src/resolver.cc
new file mode 100644
index 0000000000..6ed20e9b51
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/resolver.h"
+
+#include <stddef.h>
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS ResolverThunk::Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ if (!thunk_storage || 0 == storage_bytes || !target_module || !target_name)
+ return STATUS_INVALID_PARAMETER;
+
+ if (storage_bytes < GetThunkSize())
+ return STATUS_BUFFER_TOO_SMALL;
+
+ NTSTATUS ret = STATUS_SUCCESS;
+ if (!interceptor_entry_point) {
+ ret = ResolveInterceptor(interceptor_module, interceptor_name,
+ &interceptor_entry_point);
+ if (!NT_SUCCESS(ret))
+ return ret;
+ }
+
+ ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ interceptor_ = interceptor_entry_point;
+
+ return ret;
+}
+
+NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ DCHECK_NT(address);
+ if (!interceptor_module)
+ return STATUS_INVALID_PARAMETER;
+
+ base::win::PEImage pe(interceptor_module);
+ if (!pe.VerifyMagic())
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ *address = reinterpret_cast<void*>(pe.GetProcAddress(interceptor_name));
+
+ if (!(*address))
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ return STATUS_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver.h b/security/sandbox/chromium/sandbox/win/src/resolver.h
new file mode 100644
index 0000000000..3ce427b74b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_RESOLVER_H_
+#define SANDBOX_WIN_SRC_RESOLVER_H_
+
+// Defines ResolverThunk, the interface for classes that perform interceptions.
+// For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+// A resolver is the object in charge of performing the actual interception of
+// a function. There should be a concrete implementation of a resolver roughly
+// per type of interception.
+class ResolverThunk {
+ public:
+ ResolverThunk() {}
+ virtual ~ResolverThunk() {}
+
+ // Performs the actual interception of a function.
+ // target_name is an exported function from the module loaded at
+ // target_module, and must be replaced by interceptor_name, exported from
+ // interceptor_module. interceptor_entry_point can be provided instead of
+ // interceptor_name / interceptor_module.
+ // thunk_storage must point to a buffer on the child's address space, to hold
+ // the patch thunk, and related data. If provided, storage_used will receive
+ // the number of bytes used from thunk_storage.
+ //
+ // Example: (without error checking)
+ //
+ // size_t size = resolver.GetThunkSize();
+ // char* buffer = ::VirtualAllocEx(child_process, nullptr, size,
+ // MEM_COMMIT, PAGE_READWRITE);
+ // resolver.Setup(ntdll_module, nullptr, L"NtCreateFile", nullptr,
+ // &MyReplacementFunction, buffer, size, nullptr);
+ //
+ // In general, the idea is to allocate a single big buffer for all
+ // interceptions on the same dll, and call Setup n times.
+ // WARNING: This means that any data member that is specific to a single
+ // interception must be reset within this method.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) = 0;
+
+ // Gets the address of function_name inside module (main exe).
+ virtual NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address);
+
+ // Gets the address of an exported function_name inside module.
+ virtual NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address);
+
+ // Gets the required buffer size for this type of thunk.
+ virtual size_t GetThunkSize() const = 0;
+
+ protected:
+ // Performs basic initialization on behalf of a concrete instance of a
+ // resolver. That is, parameter validation and resolution of the target
+ // and the interceptor into the member variables.
+ //
+ // target_name is an exported function from the module loaded at
+ // target_module, and must be replaced by interceptor_name, exported from
+ // interceptor_module. interceptor_entry_point can be provided instead of
+ // interceptor_name / interceptor_module.
+ // thunk_storage must point to a buffer on the child's address space, to hold
+ // the patch thunk, and related data.
+ virtual NTSTATUS Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes);
+
+ // Gets the required buffer size for the internal part of the thunk.
+ size_t GetInternalThunkSize() const;
+
+ // Initializes the internal part of the thunk.
+ // interceptor is the function to be called instead of original_function.
+ bool SetInternalThunk(void* storage, size_t storage_bytes,
+ const void* original_function, const void* interceptor);
+
+ // Holds the resolved interception target.
+ void* target_;
+ // Holds the resolved interception interceptor.
+ const void* interceptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunk);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_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
new file mode 100644
index 0000000000..ff78f53fb1
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver_32.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/resolver.h"
+
+#include <stddef.h>
+
+// For placement new. This file must not depend on the CRT at runtime, but
+// placement operator new is inline.
+#include <new>
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // sub esp, 8 // Create working space
+ // push edx // Save register
+ // mov edx, [esp + 0xc] // Get return adddress
+ // mov [esp + 8], edx // Store return address
+ // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument
+ // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to
+ // pop edx // Restore register
+ // ret // Jump to interceptor
+ //
+ // This code only modifies esp and eip so it must work with to normal calling
+ // convention. It is assembled as:
+ //
+ // 00 83ec08 sub esp,8
+ // 03 52 push edx
+ // 04 8b54240c mov edx,dword ptr [esp + 0Ch]
+ // 08 89542408 mov dword ptr [esp + 8], edx
+ // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h
+ // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h
+ // 1c 5a pop edx
+ // 1d c3 ret
+ InternalThunk() {
+ opcodes_1 = 0x5208ec83;
+ opcodes_2 = 0x0c24548b;
+ opcodes_3 = 0x08245489;
+ opcodes_4 = 0x0c2444c7;
+ opcodes_5 = 0x042444c7;
+ opcodes_6 = 0xc35a;
+ extra_argument = 0;
+ interceptor_function = 0;
+ }
+ ULONG opcodes_1; // = 0x5208ec83
+ ULONG opcodes_2; // = 0x0c24548b
+ ULONG opcodes_3; // = 0x08245489
+ ULONG opcodes_4; // = 0x0c2444c7
+ ULONG extra_argument;
+ ULONG opcodes_5; // = 0x042444c7
+ ULONG interceptor_function;
+ USHORT opcodes_6; // = 0xc35a
+};
+#pragma pack(pop)
+
+} // namespace
+
+namespace sandbox {
+
+bool ResolverThunk::SetInternalThunk(void* storage,
+ size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new (storage) InternalThunk;
+
+#pragma warning(push)
+#pragma warning(disable : 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ thunk->interceptor_function = reinterpret_cast<ULONG>(interceptor);
+ thunk->extra_argument = reinterpret_cast<ULONG>(original_function);
+#pragma warning(pop)
+
+ return true;
+}
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ const void** casted = const_cast<const void**>(address);
+ return ResolverThunk::ResolveInterceptor(module, function_name, casted);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver_64.cc b/security/sandbox/chromium/sandbox/win/src/resolver_64.cc
new file mode 100644
index 0000000000..573b2bc3a9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver_64.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/resolver.h"
+
+#include <stddef.h>
+
+// For placement new. This file must not depend on the CRT at runtime, but
+// placement operator new is inline.
+#include <new>
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+#if defined(_M_X64)
+
+const USHORT kMovRax = 0xB848;
+const USHORT kJmpRax = 0xe0ff;
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // 01 48b8f0debc9a78563412 mov rax,123456789ABCDEF0h
+ // ff e0 jmp rax
+ //
+ // The code modifies rax, but that's fine for x64 ABI.
+
+ InternalThunk() {
+ mov_rax = kMovRax;
+ jmp_rax = kJmpRax;
+ interceptor_function = 0;
+ }
+ USHORT mov_rax; // = 48 B8
+ ULONG_PTR interceptor_function;
+ USHORT jmp_rax; // = ff e0
+};
+#pragma pack(pop)
+
+#elif defined(_M_ARM64)
+
+const ULONG kLdrX16Pc4 = 0x58000050;
+const ULONG kBrX16 = 0xD61F0200;
+
+#pragma pack(push, 4)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // 00 58000050 ldr x16, pc+4
+ // 04 D61F0200 br x16
+ // 08 123456789ABCDEF0H
+
+ InternalThunk() {
+ ldr_x16_pc4 = kLdrX16Pc4;
+ br_x16 = kBrX16;
+ interceptor_function = 0;
+ }
+ ULONG ldr_x16_pc4;
+ ULONG br_x16;
+ ULONG_PTR interceptor_function;
+};
+#pragma pack(pop)
+#else
+#error "Unsupported Windows 64-bit Arch"
+#endif
+
+} // namespace.
+
+namespace sandbox {
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+bool ResolverThunk::SetInternalThunk(void* storage,
+ size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new (storage) InternalThunk;
+ thunk->interceptor_function = reinterpret_cast<ULONG_PTR>(interceptor);
+
+ return true;
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ // We don't support sidestep & co.
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
new file mode 100644
index 0000000000..5b20e88264
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
@@ -0,0 +1,432 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/restricted_token.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Wrapper for utility version to unwrap ScopedHandle.
+std::unique_ptr<BYTE[]> GetTokenInfo(const base::win::ScopedHandle& token,
+ TOKEN_INFORMATION_CLASS info_class,
+ DWORD* error) {
+ std::unique_ptr<BYTE[]> buffer;
+ *error = sandbox::GetTokenInformation(token.Get(), info_class, &buffer);
+ if (*error != ERROR_SUCCESS)
+ return nullptr;
+ return buffer;
+}
+
+} // namespace
+
+namespace sandbox {
+
+RestrictedToken::RestrictedToken()
+ : integrity_level_(INTEGRITY_LEVEL_LAST),
+ init_(false),
+ lockdown_default_dacl_(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.
+ if (!::DuplicateHandle(::GetCurrentProcess(), effective_token,
+ ::GetCurrentProcess(), &temp_token, 0, false,
+ DUPLICATE_SAME_ACCESS)) {
+ return ::GetLastError();
+ }
+ } else {
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &temp_token)) {
+ return ::GetLastError();
+ }
+ }
+ effective_token_.Set(temp_token);
+
+ init_ = true;
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::GetRestrictedToken(
+ base::win::ScopedHandle* token) const {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ size_t deny_size = sids_for_deny_only_.size();
+ size_t restrict_size = sids_to_restrict_.size();
+ size_t privileges_size = privileges_to_disable_.size();
+
+ SID_AND_ATTRIBUTES* deny_only_array = nullptr;
+ if (deny_size) {
+ deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
+
+ for (unsigned int i = 0; i < sids_for_deny_only_.size(); ++i) {
+ deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
+ deny_only_array[i].Sid = sids_for_deny_only_[i].GetPSID();
+ }
+ }
+
+ SID_AND_ATTRIBUTES* sids_to_restrict_array = nullptr;
+ if (restrict_size) {
+ sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size];
+
+ for (unsigned int i = 0; i < restrict_size; ++i) {
+ sids_to_restrict_array[i].Attributes = 0;
+ sids_to_restrict_array[i].Sid = sids_to_restrict_[i].GetPSID();
+ }
+ }
+
+ LUID_AND_ATTRIBUTES* privileges_to_disable_array = nullptr;
+ if (privileges_size) {
+ privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size];
+
+ for (unsigned int i = 0; i < privileges_size; ++i) {
+ privileges_to_disable_array[i].Attributes = 0;
+ privileges_to_disable_array[i].Luid = privileges_to_disable_[i];
+ }
+ }
+
+ bool result = true;
+ HANDLE new_token_handle = nullptr;
+ if (deny_size || restrict_size || privileges_size) {
+ result = ::CreateRestrictedToken(
+ effective_token_.Get(), 0, static_cast<DWORD>(deny_size),
+ deny_only_array, static_cast<DWORD>(privileges_size),
+ privileges_to_disable_array, static_cast<DWORD>(restrict_size),
+ sids_to_restrict_array, &new_token_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_.Get(), TOKEN_ALL_ACCESS,
+ nullptr, SecurityIdentification, TokenPrimary,
+ &new_token_handle);
+ }
+ auto last_error = ::GetLastError();
+
+ if (deny_only_array)
+ delete[] deny_only_array;
+
+ if (sids_to_restrict_array)
+ delete[] sids_to_restrict_array;
+
+ if (privileges_to_disable_array)
+ delete[] privileges_to_disable_array;
+
+ if (!result)
+ return last_error;
+
+ base::win::ScopedHandle new_token(new_token_handle);
+
+ if (lockdown_default_dacl_) {
+ // Don't add Restricted sid and also remove logon sid access.
+ if (!RevokeLogonSidFromDefaultDacl(new_token.Get()))
+ return ::GetLastError();
+ } else {
+ // Modify the default dacl on the token to contain Restricted.
+ if (!AddSidToDefaultDacl(new_token.Get(), WinRestrictedCodeSid,
+ GRANT_ACCESS, GENERIC_ALL)) {
+ return ::GetLastError();
+ }
+ }
+
+ for (const auto& default_dacl_sid : sids_for_default_dacl_) {
+ if (!AddSidToDefaultDacl(new_token.Get(), std::get<0>(default_dacl_sid),
+ std::get<1>(default_dacl_sid),
+ std::get<2>(default_dacl_sid))) {
+ return ::GetLastError();
+ }
+ }
+
+ // Add user to default dacl.
+ if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL))
+ return ::GetLastError();
+
+ DWORD error = SetTokenIntegrityLevel(new_token.Get(), integrity_level_);
+ if (ERROR_SUCCESS != error)
+ return error;
+
+ 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;
+}
+
+DWORD RestrictedToken::GetRestrictedTokenForImpersonation(
+ base::win::ScopedHandle* token) const {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ base::win::ScopedHandle restricted_token;
+ DWORD err_code = GetRestrictedToken(&restricted_token);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+
+ HANDLE impersonation_token_handle;
+ if (!::DuplicateToken(restricted_token.Get(), SecurityImpersonation,
+ &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();
+ }
+
+ token->Set(token_handle);
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid>* exceptions) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD error;
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenGroups, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
+
+ // Build the list of the deny only group SIDs
+ for (unsigned int i = 0; i < token_groups->GroupCount; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
+ (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) {
+ bool should_ignore = false;
+ if (exceptions) {
+ for (unsigned int j = 0; j < exceptions->size(); ++j) {
+ if (::EqualSid((*exceptions)[j].GetPSID(),
+ token_groups->Groups[i].Sid)) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+ if (!should_ignore) {
+ sids_for_deny_only_.push_back(
+ reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
+ }
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddSidForDenyOnly(const Sid& sid) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_for_deny_only_.push_back(sid);
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddUserSidForDenyOnly() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ std::unique_ptr<BYTE[]> buffer(new BYTE[size]);
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer.get());
+
+ bool result = ::GetTokenInformation(effective_token_.Get(), TokenUser,
+ token_user, size, &size);
+
+ if (!result)
+ return ::GetLastError();
+
+ Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
+ sids_for_deny_only_.push_back(user);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::DeleteAllPrivileges(
+ const std::vector<std::wstring>* exceptions) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD error;
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenPrivileges, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_PRIVILEGES* token_privileges =
+ reinterpret_cast<TOKEN_PRIVILEGES*>(buffer.get());
+
+ // Build the list of privileges to disable
+ for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) {
+ bool should_ignore = false;
+ if (exceptions) {
+ for (unsigned int j = 0; j < exceptions->size(); ++j) {
+ LUID luid = {0};
+ ::LookupPrivilegeValue(nullptr, (*exceptions)[j].c_str(), &luid);
+ if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart &&
+ token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+ if (!should_ignore) {
+ privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid);
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::DeletePrivilege(const wchar_t* privilege) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ LUID luid = {0};
+ if (LookupPrivilegeValue(nullptr, privilege, &luid))
+ privileges_to_disable_.push_back(luid);
+ else
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSid(const Sid& sid) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_to_restrict_.push_back(sid); // No attributes
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSidLogonSession() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD error;
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenGroups, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
+
+ SID* logon_sid = nullptr;
+ for (unsigned int i = 0; i < token_groups->GroupCount; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
+ logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
+ break;
+ }
+ }
+
+ if (logon_sid)
+ sids_to_restrict_.push_back(logon_sid);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSidCurrentUser() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ std::unique_ptr<BYTE[]> buffer(new BYTE[size]);
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer.get());
+
+ bool result = ::GetTokenInformation(effective_token_.Get(), TokenUser,
+ token_user, size, &size);
+
+ if (!result)
+ return ::GetLastError();
+
+ Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
+ sids_to_restrict_.push_back(user);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSidAllSids() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ // Add the current user to the list.
+ DWORD error = AddRestrictingSidCurrentUser();
+ if (ERROR_SUCCESS != error)
+ return error;
+
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenGroups, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
+
+ // Build the list of restricting sids from all groups.
+ for (unsigned int i = 0; i < token_groups->GroupCount; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0)
+ AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ integrity_level_ = integrity_level;
+ return ERROR_SUCCESS;
+}
+
+void RestrictedToken::SetLockdownDefaultDacl() {
+ lockdown_default_dacl_ = true;
+}
+
+DWORD RestrictedToken::AddDefaultDaclSid(const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_for_default_dacl_.push_back(std::make_tuple(sid, access_mode, access));
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.h b/security/sandbox/chromium/sandbox/win/src/restricted_token.h
new file mode 100644
index 0000000000..c89427af14
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_RESTRICTED_TOKEN_H_
+#define SANDBOX_SRC_RESTRICTED_TOKEN_H_
+
+#include <windows.h>
+
+#include <tuple>
+#include <vector>
+
+#include <string>
+
+#include "base/macros.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"
+
+// Flags present in the Group SID list. These 2 flags are new in Windows Vista
+#ifndef SE_GROUP_INTEGRITY
+#define SE_GROUP_INTEGRITY (0x00000020L)
+#endif
+#ifndef SE_GROUP_INTEGRITY_ENABLED
+#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L)
+#endif
+
+namespace sandbox {
+
+// Handles the creation of a restricted token using the effective token or
+// any token handle.
+// Sample usage:
+// RestrictedToken restricted_token;
+// DWORD err_code = restricted_token.Init(nullptr); // Use the current
+// // effective token
+// if (ERROR_SUCCESS != err_code) {
+// // handle error.
+// }
+//
+// restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID());
+// base::win::ScopedHandle token_handle;
+// err_code = restricted_token.GetRestrictedToken(&token_handle);
+// if (ERROR_SUCCESS != err_code) {
+// // handle error.
+// }
+// [...]
+class RestrictedToken {
+ public:
+ // Init() has to be called before calling any other method in the class.
+ RestrictedToken();
+ ~RestrictedToken();
+
+ // Initializes the RestrictedToken object with effective_token.
+ // If effective_token is nullptr, it initializes the RestrictedToken object
+ // with the effective token of the current process.
+ DWORD Init(HANDLE effective_token);
+
+ // 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.
+ DWORD GetRestrictedToken(base::win::ScopedHandle* token) const;
+
+ // Creates a restricted token and uses this new token to create a new token
+ // 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 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,
+ // the caller can pass an empty list or nullptr for the exceptions
+ // parameter.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // Sample usage:
+ // std::vector<Sid> sid_exceptions;
+ // sid_exceptions.push_back(ATL::Sids::Users().GetPSID());
+ // sid_exceptions.push_back(ATL::Sids::World().GetPSID());
+ // restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+ // Note: A Sid marked for Deny Only in a token cannot be used to grant
+ // access to any resource. It can only be used to deny access.
+ DWORD AddAllSidsForDenyOnly(std::vector<Sid>* exceptions);
+
+ // Adds a user or group SID for Deny Only in the restricted token.
+ // Parameter: sid is the SID to add in the Deny Only list.
+ // The return value is always ERROR_SUCCESS.
+ //
+ // Sample Usage:
+ // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID());
+ 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.
+ 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
+ // there is no exception needed, the caller can pass an empty list or nullptr
+ // for the exceptions parameter.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // Sample usage:
+ // std::vector<std::wstring> privilege_exceptions;
+ // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ // restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ DWORD DeleteAllPrivileges(const std::vector<std::wstring>* exceptions);
+
+ // Adds a privilege to the list of privileges to remove in the restricted
+ // token.
+ // Parameter: privilege is the privilege name to remove. This is the string
+ // representing the privilege. (e.g. "SeChangeNotifyPrivilege").
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // Sample usage:
+ // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME);
+ 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.
+ // The return value is always ERROR_SUCCESS.
+ //
+ // Sample usage:
+ // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID());
+ // Note: The list of restricting is used to force Windows to perform all
+ // access checks twice. The first time using your user SID and your groups,
+ // and the second time using your list of restricting sids. The access has
+ // to be granted in both places to get access to the resource requested.
+ DWORD AddRestrictingSid(const Sid& sid);
+
+ // Adds the logon sid of the token in the list of restricting sids for the
+ // restricted token.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AddRestrictingSidLogonSession();
+
+ // Adds the owner sid of the token in the list of restricting sids for the
+ // restricted token.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ 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.
+ DWORD AddRestrictingSidAllSids();
+
+ // Sets the token integrity level. This is only valid on Vista. The integrity
+ // level cannot be higher than your current integrity level.
+ DWORD SetIntegrityLevel(IntegrityLevel integrity_level);
+
+ // Set a flag which indicates the created token should have a locked down
+ // default DACL when created.
+ void SetLockdownDefaultDacl();
+
+ // Add a SID to the default DACL. These SIDs are added regardless of the
+ // SetLockdownDefaultDacl state.
+ DWORD AddDefaultDaclSid(const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+ private:
+ // The list of restricting sids in the restricted token.
+ std::vector<Sid> sids_to_restrict_;
+ // The list of privileges to remove in the restricted token.
+ std::vector<LUID> privileges_to_disable_;
+ // The list of sids to mark as Deny Only in the restricted token.
+ std::vector<Sid> sids_for_deny_only_;
+ // The list of sids to add to the default DACL of the restricted token.
+ std::vector<std::tuple<Sid, ACCESS_MODE, ACCESS_MASK>> sids_for_default_dacl_;
+ // The token to restrict. Can only be set in a constructor.
+ 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)
+ bool init_;
+ // Lockdown the default DACL when creating new tokens.
+ bool lockdown_default_dacl_;
+
+ DISALLOW_COPY_AND_ASSIGN(RestrictedToken);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESTRICTED_TOKEN_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
new file mode 100644
index 0000000000..1855054a7c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
@@ -0,0 +1,829 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the RestrictedToken.
+
+#include "sandbox/win/src/restricted_token.h"
+
+#include <vector>
+
+#include "base/win/atl.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void TestDefaultDalc(bool restricted_required) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ if (!restricted_required)
+ token.SetLockdownDefaultDacl();
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ base::win::ScopedHandle handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(handle.Take());
+
+ ATL::CDacl dacl;
+ ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl));
+
+ ATL::CSid logon_sid;
+ ASSERT_TRUE(restricted_token.GetLogonSid(&logon_sid));
+
+ bool restricted_found = false;
+ bool logon_sid_found = false;
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ dacl.GetAclEntry(i, &sid, &mask);
+ if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) {
+ restricted_found = true;
+ } else if (sid == logon_sid) {
+ logon_sid_found = true;
+ }
+ }
+
+ ASSERT_EQ(restricted_required, restricted_found);
+ if (!restricted_required)
+ ASSERT_FALSE(logon_sid_found);
+}
+
+bool GetVariableTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS information_class,
+ std::vector<char>* information) {
+ DWORD return_length;
+ if (!::GetTokenInformation(token, information_class, nullptr, 0,
+ &return_length)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+ }
+
+ information->resize(return_length);
+ return !!::GetTokenInformation(token, information_class, information->data(),
+ return_length, &return_length);
+}
+
+bool GetVariableTokenInformation(const base::win::ScopedHandle& token,
+ TOKEN_INFORMATION_CLASS information_class,
+ std::vector<char>* information) {
+ return GetVariableTokenInformation(token.Get(), information_class,
+ information);
+}
+
+void CheckDaclForPackageSid(const base::win::ScopedHandle& token,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool package_sid_required) {
+ DWORD length_needed = 0;
+ ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0,
+ &length_needed);
+ ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER});
+
+ std::vector<char> security_desc_buffer(length_needed);
+ SECURITY_DESCRIPTOR* security_desc =
+ reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data());
+
+ ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION,
+ security_desc, length_needed,
+ &length_needed));
+
+ ATL::CSecurityDesc token_sd(*security_desc);
+ ATL::CDacl dacl;
+ ASSERT_TRUE(token_sd.GetDacl(&dacl));
+
+ ATL::CSid package_sid(
+ static_cast<SID*>(security_capabilities->AppContainerSid));
+ ATL::CSid all_package_sid(
+ static_cast<SID*>(sandbox::Sid(::WinBuiltinAnyPackageSid).GetPSID()));
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ BYTE type = 0;
+ dacl.GetAclEntry(i, &sid, &mask, &type);
+ if (mask != TOKEN_ALL_ACCESS || type != ACCESS_ALLOWED_ACE_TYPE)
+ continue;
+ if (sid == package_sid)
+ EXPECT_TRUE(package_sid_required);
+ else if (sid == all_package_sid)
+ EXPECT_FALSE(package_sid_required);
+ }
+}
+
+void CheckLowBoxToken(const base::win::ScopedHandle& token,
+ TOKEN_TYPE token_type,
+ PSECURITY_CAPABILITIES security_capabilities) {
+ DWORD appcontainer;
+ DWORD return_length;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenIsAppContainer,
+ &appcontainer, sizeof(appcontainer),
+ &return_length));
+ ASSERT_TRUE(appcontainer);
+ TOKEN_TYPE token_type_real;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenType, &token_type_real,
+ sizeof(token_type_real), &return_length));
+ ASSERT_EQ(token_type_real, token_type);
+ if (token_type == ::TokenImpersonation) {
+ SECURITY_IMPERSONATION_LEVEL imp_level;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenImpersonationLevel,
+ &imp_level, sizeof(imp_level),
+ &return_length));
+ ASSERT_EQ(imp_level, ::SecurityImpersonation);
+ }
+
+ std::vector<char> package_sid_buf;
+ ASSERT_TRUE(GetVariableTokenInformation(token, ::TokenAppContainerSid,
+ &package_sid_buf));
+ PTOKEN_APPCONTAINER_INFORMATION package_sid =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(package_sid_buf.data());
+ EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
+ package_sid->TokenAppContainer));
+
+ std::vector<char> capabilities_buf;
+ ASSERT_TRUE(GetVariableTokenInformation(token, ::TokenCapabilities,
+ &capabilities_buf));
+ PTOKEN_GROUPS capabilities =
+ reinterpret_cast<PTOKEN_GROUPS>(capabilities_buf.data());
+ ASSERT_EQ(capabilities->GroupCount, security_capabilities->CapabilityCount);
+ for (DWORD index = 0; index < capabilities->GroupCount; ++index) {
+ EXPECT_EQ(capabilities->Groups[index].Attributes,
+ security_capabilities->Capabilities[index].Attributes);
+ EXPECT_TRUE(::EqualSid(capabilities->Groups[index].Sid,
+ security_capabilities->Capabilities[index].Sid));
+ }
+
+ CheckDaclForPackageSid(token, security_capabilities, true);
+}
+
+// Checks if a sid is in the restricting list of the restricted token.
+// Asserts if it's not the case. If count is a positive number, the number of
+// elements in the restricting sids list has to be equal.
+void CheckRestrictingSid(HANDLE restricted_token, ATL::CSid sid, int count) {
+ std::vector<char> memory;
+ ASSERT_TRUE(GetVariableTokenInformation(restricted_token,
+ ::TokenRestrictedSids, &memory));
+ PTOKEN_GROUPS groups = reinterpret_cast<PTOKEN_GROUPS>(memory.data());
+ ATL::CTokenGroups atl_groups(*groups);
+
+ if (count >= 0)
+ ASSERT_EQ(static_cast<unsigned>(count), atl_groups.GetCount());
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ atl_groups.GetSidsAndAttributes(&sids, &attributes);
+
+ bool present = false;
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (sids[i] == sid) {
+ present = true;
+ break;
+ }
+ }
+
+ ASSERT_TRUE(present);
+}
+
+void CheckRestrictingSid(const ATL::CAccessToken& restricted_token,
+ ATL::CSid sid,
+ int count) {
+ CheckRestrictingSid(restricted_token.GetHandle(), sid, count);
+}
+
+} // namespace
+
+// Tests the initializatioin with an invalid token handle.
+TEST(RestrictedTokenTest, InvalidHandle) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_INVALID_HANDLE),
+ token.Init(reinterpret_cast<HANDLE>(0x5555)));
+}
+
+// Tests the initialization with nullptr as parameter.
+TEST(RestrictedTokenTest, DefaultInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Create the token using the current token.
+ RestrictedToken token_default;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token_default.Init(nullptr));
+
+ // Get the handle to the restricted token.
+
+ base::win::ScopedHandle restricted_token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token_default.GetRestrictedToken(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle.Take());
+
+ ATL::CSid sid_user_restricted;
+ ATL::CSid sid_user_default;
+ ATL::CSid sid_owner_restricted;
+ ATL::CSid sid_owner_default;
+ ASSERT_TRUE(restricted_token.GetUser(&sid_user_restricted));
+ ASSERT_TRUE(access_token.GetUser(&sid_user_default));
+ ASSERT_TRUE(restricted_token.GetOwner(&sid_owner_restricted));
+ ASSERT_TRUE(access_token.GetOwner(&sid_owner_default));
+
+ // Check if both token have the same owner and user.
+ ASSERT_EQ(sid_user_restricted, sid_user_default);
+ ASSERT_EQ(sid_owner_restricted, sid_owner_default);
+}
+
+// Tests the initialization with a custom token as parameter.
+TEST(RestrictedTokenTest, CustomInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Change the primary group.
+ access_token.SetPrimaryGroup(ATL::Sids::World());
+
+ // Create the token using the current token.
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+
+ // Get the handle to the restricted token.
+
+ base::win::ScopedHandle restricted_token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle.Take());
+
+ ATL::CSid sid_restricted;
+ ATL::CSid sid_default;
+ ASSERT_TRUE(restricted_token.GetPrimaryGroup(&sid_restricted));
+ ASSERT_TRUE(access_token.GetPrimaryGroup(&sid_default));
+
+ // Check if both token have the same owner.
+ ASSERT_EQ(sid_restricted, sid_default);
+}
+
+// Verifies that the token created by the object are valid.
+TEST(RestrictedTokenTest, ResultToken) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ base::win::ScopedHandle restricted_token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&restricted_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(restricted_token.Get()));
+
+ DWORD length = 0;
+ TOKEN_TYPE type;
+ ASSERT_TRUE(::GetTokenInformation(restricted_token.Get(), ::TokenType, &type,
+ sizeof(type), &length));
+
+ ASSERT_EQ(type, TokenPrimary);
+
+ base::win::ScopedHandle impersonation_token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedTokenForImpersonation(&impersonation_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(impersonation_token.Get()));
+
+ ASSERT_TRUE(::GetTokenInformation(impersonation_token.Get(), ::TokenType,
+ &type, sizeof(type), &length));
+
+ ASSERT_EQ(type, TokenImpersonation);
+}
+
+// Verifies that the token created has "Restricted" in its default dacl.
+TEST(RestrictedTokenTest, DefaultDacl) {
+ TestDefaultDalc(true);
+}
+
+// Verifies that the token created does not have "Restricted" in its default
+// dacl.
+TEST(RestrictedTokenTest, DefaultDaclLockdown) {
+ TestDefaultDalc(false);
+}
+
+// Tests the method "AddSidForDenyOnly".
+TEST(RestrictedTokenTest, DenySid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddSidForDenyOnly(Sid(WinWorldSid)));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly".
+TEST(RestrictedTokenTest, DenySids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddAllSidsForDenyOnly(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly" using an exception list.
+TEST(RestrictedTokenTest, DenySidsException) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ std::vector<Sid> sids_exception;
+ sids_exception.push_back(Sid(WinWorldSid));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddAllSidsForDenyOnly(&sids_exception));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(0u, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ } else {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly.
+TEST(RestrictedTokenTest, DenyOwnerSid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.AddUserSidForDenyOnly());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly with a custom effective token.
+TEST(RestrictedTokenTest, DenyOwnerSidCustom) {
+ // Get the current process token.
+ HANDLE access_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &access_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, access_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(access_handle);
+
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.AddUserSidForDenyOnly());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method DeleteAllPrivileges.
+TEST(RestrictedTokenTest, DeleteAllPrivileges) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeleteAllPrivileges(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ASSERT_EQ(0u, privileges.GetCount());
+}
+
+// Tests the method DeleteAllPrivileges with an exception list.
+TEST(RestrictedTokenTest, DeleteAllPrivilegesException) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ std::vector<std::wstring> exceptions;
+ exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeleteAllPrivileges(&exceptions));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ ASSERT_EQ(1u, privileges.GetCount());
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method DeletePrivilege.
+TEST(RestrictedTokenTest, DeletePrivilege) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_NE(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method AddRestrictingSid.
+TEST(RestrictedTokenTest, AddRestrictingSid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser with a custom effective token.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUserCustom) {
+ // Get the current process token.
+ HANDLE access_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &access_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, access_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(access_handle);
+
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidLogonSession.
+TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ CheckRestrictingSid(restricted_token, session, 1);
+}
+
+// Tests adding a lot of restricting sids.
+TEST(RestrictedTokenTest, AddMultipleRestrictingSids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ std::vector<char> memory;
+ ASSERT_TRUE(GetVariableTokenInformation(restricted_token.GetHandle(),
+ ::TokenRestrictedSids, &memory));
+ PTOKEN_GROUPS groups = reinterpret_cast<PTOKEN_GROUPS>(memory.data());
+ ATL::CTokenGroups atl_groups(*groups);
+ ASSERT_EQ(3u, atl_groups.GetCount());
+}
+
+// Tests the method "AddRestrictingSidAllSids".
+TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidAllSids());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all group sids are in the restricting sid list.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ CheckRestrictingSid(restricted_token, sids[i], -1);
+ }
+ }
+
+ // Verify that the user is in the restricting sid list.
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+ CheckRestrictingSid(restricted_token, user, -1);
+}
+
+// Checks the error code when the object is initialized twice.
+TEST(RestrictedTokenTest, DoubleInit) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_ALREADY_INITIALIZED), token.Init(nullptr));
+}
+
+TEST(RestrictedTokenTest, LockdownDefaultDaclNoLogonSid) {
+ ATL::CAccessToken anonymous_token;
+ ASSERT_TRUE(::ImpersonateAnonymousToken(::GetCurrentThread()));
+ ASSERT_TRUE(anonymous_token.GetThreadToken(TOKEN_ALL_ACCESS));
+ ::RevertToSelf();
+ ATL::CSid logon_sid;
+ // Verify that the anonymous token doesn't have the logon sid.
+ ASSERT_FALSE(anonymous_token.GetLogonSid(&logon_sid));
+
+ RestrictedToken token;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, token.Init(anonymous_token.GetHandle()));
+ token.SetLockdownDefaultDacl();
+
+ base::win::ScopedHandle handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, token.GetRestrictedToken(&handle));
+}
+
+TEST(RestrictedTokenTest, LowBoxToken) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+ base::win::ScopedHandle token;
+
+ Sid package_sid = Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-7");
+ SecurityCapabilities caps_no_capabilities(package_sid);
+
+ ASSERT_EQ(DWORD{ERROR_INVALID_PARAMETER},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_no_capabilities, nullptr,
+ 0, nullptr));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_no_capabilities, nullptr,
+ 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_no_capabilities);
+
+ ASSERT_TRUE(ReplacePackageSidInDacl(token.Get(), SE_KERNEL_OBJECT,
+ Sid(caps_no_capabilities.AppContainerSid),
+ TOKEN_ALL_ACCESS));
+ CheckDaclForPackageSid(token, &caps_no_capabilities, false);
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, IMPERSONATION, &caps_no_capabilities,
+ nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenImpersonation, &caps_no_capabilities);
+
+ std::vector<Sid> capabilities;
+ capabilities.push_back(Sid::FromKnownCapability(kInternetClient));
+ capabilities.push_back(Sid::FromKnownCapability(kPrivateNetworkClientServer));
+ SecurityCapabilities caps_with_capabilities(package_sid, capabilities);
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_with_capabilities,
+ nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_with_capabilities);
+
+ RestrictedToken restricted_token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, restricted_token.Init(nullptr));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ restricted_token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ restricted_token.GetRestrictedToken(&token_handle));
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(token_handle.Get(), PRIMARY,
+ &caps_with_capabilities, nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_with_capabilities);
+ CheckRestrictingSid(token.Get(), ATL::Sids::World(), 1);
+
+ SecurityCapabilities caps_for_handles(
+ Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-8"));
+ base::win::ScopedHandle object_handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxObjectDirectory(caps_for_handles.AppContainerSid, true,
+ &object_handle));
+ HANDLE saved_handles[] = {object_handle.Get()};
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(token_handle.Get(), PRIMARY, &caps_for_handles,
+ saved_handles, 1, &token));
+ ASSERT_TRUE(token.IsValid());
+ object_handle.Close();
+ ASSERT_FALSE(object_handle.IsValid());
+ ASSERT_EQ(DWORD{ERROR_ALREADY_EXISTS},
+ CreateLowBoxObjectDirectory(caps_for_handles.AppContainerSid, false,
+ &object_handle));
+}
+
+} // 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
new file mode 100644
index 0000000000..863dee8d69
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/restricted_token_utils.h"
+
+#include <aclapi.h>
+#include <sddl.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/security_level.h"
+#include "sandbox/win/src/sid.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+DWORD GetObjectSecurityDescriptor(HANDLE handle,
+ SECURITY_INFORMATION security_info,
+ std::vector<char>* security_desc_buffer,
+ PSECURITY_DESCRIPTOR* security_desc) {
+ DWORD last_error = 0;
+ DWORD length_needed = 0;
+
+ ::GetKernelObjectSecurity(handle, security_info, nullptr, 0, &length_needed);
+ last_error = ::GetLastError();
+ if (last_error != ERROR_INSUFFICIENT_BUFFER)
+ return last_error;
+
+ security_desc_buffer->resize(length_needed);
+ *security_desc =
+ reinterpret_cast<PSECURITY_DESCRIPTOR>(security_desc_buffer->data());
+
+ if (!::GetKernelObjectSecurity(handle, security_info, *security_desc,
+ length_needed, &length_needed)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace
+
+DWORD CreateRestrictedToken(HANDLE effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
+ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token) {
+ RestrictedToken restricted_token;
+ restricted_token.Init(effective_token);
+ if (lockdown_default_dacl)
+ restricted_token.SetLockdownDefaultDacl();
+ if (unique_restricted_sid) {
+ restricted_token.AddDefaultDaclSid(Sid(unique_restricted_sid), GRANT_ACCESS,
+ GENERIC_ALL);
+ restricted_token.AddDefaultDaclSid(Sid(WinCreatorOwnerRightsSid),
+ GRANT_ACCESS, READ_CONTROL);
+ }
+
+ std::vector<std::wstring> privilege_exceptions;
+ std::vector<Sid> sid_exceptions;
+
+ bool deny_sids = true;
+ bool remove_privileges = true;
+
+ switch (security_level) {
+ case USER_UNPROTECTED: {
+ deny_sids = false;
+ remove_privileges = false;
+ break;
+ }
+ case USER_RESTRICTED_SAME_ACCESS: {
+ deny_sids = false;
+ remove_privileges = false;
+
+ if (use_restricting_sids) {
+ unsigned err_code = restricted_token.AddRestrictingSidAllSids();
+ if (ERROR_SUCCESS != err_code) {
+ return err_code;
+ }
+ }
+
+ break;
+ }
+ case USER_NON_ADMIN: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ break;
+ }
+ case USER_RESTRICTED_NON_ADMIN: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinInteractiveSid);
+ restricted_token.AddRestrictingSid(WinAuthenticatedUserSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ break;
+ }
+ case USER_INTERACTIVE: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LIMITED: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+
+ // This token has to be able to create objects in BNO.
+ // Unfortunately, on Vista+, it needs the current logon sid
+ // in the token to achieve this. You should also set the process to be
+ // low integrity level so it can't access object created by other
+ // processes.
+ restricted_token.AddRestrictingSidLogonSession();
+ } else {
+ restricted_token.AddUserSidForDenyOnly();
+ }
+ break;
+ }
+ case USER_RESTRICTED: {
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LOCKDOWN: {
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinNullSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ default: { return ERROR_BAD_ARGUMENTS; }
+ }
+
+ DWORD err_code = ERROR_SUCCESS;
+ if (deny_sids) {
+ err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ if (remove_privileges) {
+ err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ restricted_token.SetIntegrityLevel(integrity_level);
+
+ switch (token_type) {
+ case PRIMARY: {
+ err_code = restricted_token.GetRestrictedToken(token);
+ break;
+ }
+ case IMPERSONATION: {
+ err_code = restricted_token.GetRestrictedTokenForImpersonation(token);
+ break;
+ }
+ default: {
+ err_code = ERROR_BAD_ARGUMENTS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+DWORD SetObjectIntegrityLabel(HANDLE handle,
+ SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid) {
+ // Build the SDDL string for the label.
+ std::wstring sddl = L"S:("; // SDDL for a SACL.
+ sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
+ sddl += L";;"; // No Ace Flags.
+ sddl += ace_access; // Add the ACE access.
+ sddl += L";;;"; // No ObjectType and Inherited Object Type.
+ sddl += integrity_level_sid; // Trustee Sid.
+ sddl += L")";
+
+ DWORD error = ERROR_SUCCESS;
+ PSECURITY_DESCRIPTOR sec_desc = nullptr;
+
+ PACL sacl = nullptr;
+ BOOL sacl_present = false;
+ BOOL sacl_defaulted = false;
+
+ if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
+ sddl.c_str(), SDDL_REVISION, &sec_desc, nullptr)) {
+ if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ error = ::SetSecurityInfo(handle, type, LABEL_SECURITY_INFORMATION,
+ nullptr, nullptr, nullptr, sacl);
+ } else {
+ error = ::GetLastError();
+ }
+
+ ::LocalFree(sec_desc);
+ } else {
+ return ::GetLastError();
+ }
+
+ return error;
+}
+
+const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) {
+ switch (integrity_level) {
+ case INTEGRITY_LEVEL_SYSTEM:
+ return L"S-1-16-16384";
+ case INTEGRITY_LEVEL_HIGH:
+ return L"S-1-16-12288";
+ case INTEGRITY_LEVEL_MEDIUM:
+ return L"S-1-16-8192";
+ case INTEGRITY_LEVEL_MEDIUM_LOW:
+ return L"S-1-16-6144";
+ case INTEGRITY_LEVEL_LOW:
+ return L"S-1-16-4096";
+ case INTEGRITY_LEVEL_BELOW_LOW:
+ return L"S-1-16-2048";
+ case INTEGRITY_LEVEL_UNTRUSTED:
+ return L"S-1-16-0";
+ case INTEGRITY_LEVEL_LAST:
+ return nullptr;
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
+ const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level);
+ if (!integrity_level_str) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ PSID integrity_sid = nullptr;
+ if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
+ return ::GetLastError();
+
+ 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 : last_error;
+}
+
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
+ // We don't check for an invalid level here because we'll just let it
+ // fail on the SetTokenIntegrityLevel call later on.
+ if (integrity_level == INTEGRITY_LEVEL_LAST) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return SetTokenIntegrityLevel(token.Get(), integrity_level);
+}
+
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) {
+ std::vector<char> security_desc_buffer;
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ DWORD last_error = GetObjectSecurityDescriptor(
+ token, LABEL_SECURITY_INFORMATION, &security_desc_buffer, &security_desc);
+ if (last_error != ERROR_SUCCESS)
+ return last_error;
+
+ PACL sacl = nullptr;
+ BOOL sacl_present = false;
+ BOOL sacl_defaulted = false;
+
+ if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ return ::GetLastError();
+ }
+
+ for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) {
+ PSYSTEM_MANDATORY_LABEL_ACE ace;
+
+ if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace)) &&
+ ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) {
+ ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP |
+ SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP;
+ break;
+ }
+ }
+
+ if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
+ security_desc))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD HardenProcessIntegrityLevelPolicy() {
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return HardenTokenIntegrityLevelPolicy(token.Get());
+}
+
+DWORD CreateLowBoxToken(HANDLE base_token,
+ TokenType token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ PHANDLE saved_handles,
+ DWORD saved_handles_count,
+ base::win::ScopedHandle* token) {
+ NtCreateLowBoxToken CreateLowBoxToken = nullptr;
+ ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken);
+
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return ERROR_CALL_NOT_IMPLEMENTED;
+
+ if (token_type != PRIMARY && token_type != IMPERSONATION)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!token)
+ return ERROR_INVALID_PARAMETER;
+
+ base::win::ScopedHandle base_token_handle;
+ if (!base_token) {
+ HANDLE process_token = nullptr;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &process_token)) {
+ return ::GetLastError();
+ }
+ base_token_handle.Set(process_token);
+ base_token = process_token;
+ }
+ OBJECT_ATTRIBUTES obj_attr;
+ InitializeObjectAttributes(&obj_attr, nullptr, 0, nullptr, nullptr);
+ HANDLE token_lowbox = nullptr;
+
+ NTSTATUS status = CreateLowBoxToken(
+ &token_lowbox, base_token, TOKEN_ALL_ACCESS, &obj_attr,
+ security_capabilities->AppContainerSid,
+ security_capabilities->CapabilityCount,
+ security_capabilities->Capabilities, saved_handles_count,
+ saved_handles_count > 0 ? saved_handles : nullptr);
+ if (!NT_SUCCESS(status))
+ return GetLastErrorFromNtStatus(status);
+
+ base::win::ScopedHandle token_lowbox_handle(token_lowbox);
+ DCHECK(token_lowbox_handle.IsValid());
+
+ // Default from NtCreateLowBoxToken is a Primary token.
+ if (token_type == PRIMARY) {
+ *token = std::move(token_lowbox_handle);
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE dup_handle = nullptr;
+ if (!::DuplicateTokenEx(token_lowbox_handle.Get(), TOKEN_ALL_ACCESS, nullptr,
+ ::SecurityImpersonation, ::TokenImpersonation,
+ &dup_handle)) {
+ return ::GetLastError();
+ }
+
+ // Copy security descriptor from primary token as the new object will have
+ // the DACL from the current token's default DACL.
+ base::win::ScopedHandle token_for_sd(dup_handle);
+ std::vector<char> security_desc_buffer;
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ DWORD last_error = GetObjectSecurityDescriptor(
+ token_lowbox_handle.Get(), DACL_SECURITY_INFORMATION,
+ &security_desc_buffer, &security_desc);
+ if (last_error != ERROR_SUCCESS)
+ return last_error;
+
+ if (!::SetKernelObjectSecurity(token_for_sd.Get(), DACL_SECURITY_INFORMATION,
+ security_desc)) {
+ return ::GetLastError();
+ }
+
+ *token = std::move(token_for_sd);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD CreateLowBoxObjectDirectory(PSID lowbox_sid,
+ bool open_directory,
+ base::win::ScopedHandle* directory) {
+ DWORD session_id = 0;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id))
+ return ::GetLastError();
+
+ LPWSTR sid_string = nullptr;
+ if (!::ConvertSidToStringSid(lowbox_sid, &sid_string))
+ return ::GetLastError();
+
+ std::unique_ptr<wchar_t, LocalFreeDeleter> sid_string_ptr(sid_string);
+ std::wstring directory_path = base::StringPrintf(
+ L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls", session_id, sid_string);
+
+ NtCreateDirectoryObjectFunction CreateObjectDirectory = nullptr;
+ ResolveNTFunctionPtr("NtCreateDirectoryObject", &CreateObjectDirectory);
+
+ OBJECT_ATTRIBUTES obj_attr;
+ UNICODE_STRING obj_name;
+ DWORD attributes = OBJ_CASE_INSENSITIVE;
+ if (open_directory)
+ attributes |= OBJ_OPENIF;
+
+ sandbox::InitObjectAttribs(directory_path, attributes, nullptr, &obj_attr,
+ &obj_name, nullptr);
+
+ HANDLE handle = nullptr;
+ NTSTATUS status =
+ CreateObjectDirectory(&handle, DIRECTORY_ALL_ACCESS, &obj_attr);
+
+ if (!NT_SUCCESS(status))
+ return GetLastErrorFromNtStatus(status);
+ directory->Set(handle);
+
+ return ERROR_SUCCESS;
+}
+
+} // 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
new file mode 100644
index 0000000000..e1f75a89c9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
+#define SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
+
+#include <accctrl.h>
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/security_level.h"
+
+// Contains the utility functions to be able to create restricted tokens based
+// on a security profiles.
+
+namespace sandbox {
+
+// The type of the token returned by the CreateNakedToken.
+enum TokenType { IMPERSONATION = 0, PRIMARY };
+
+// Creates a restricted token from effective token. If it's nullptr then
+// effective token of process is used instead. The parameter security_level
+// determines how much the token isrestricted. The token_type determines if
+// the token will be used as a primarytoken or impersonation token. The
+// integrity level of the token is set to |integrity level| on Vista only.
+// |token| is the output value containing the handle of the newly created
+// restricted token.
+// |lockdown_default_dacl| indicates the token's default DACL should be locked
+// down to restrict what other process can open kernel resources created while
+// running under the 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 effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
+ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token);
+
+// Sets the integrity label on a object handle.
+DWORD SetObjectIntegrityLabel(HANDLE handle,
+ SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid);
+
+// Sets the integrity level on a token. This is only valid on Vista. It returns
+// without failing on XP. If the integrity level that you specify is greater
+// than the current integrity level, the function will fail.
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level);
+
+// Returns the integrity level SDDL string associated with a given
+// IntegrityLevel value.
+const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level);
+
+// Sets the integrity level on the current process on Vista. It returns without
+// failing on XP. If the integrity level that you specify is greater than the
+// current integrity level, the function will fail.
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level);
+
+// Hardens the integrity level policy on a token. This is only valid on Win 7
+// and above. Specifically it sets the policy to block read and execute so
+// that a lower privileged process cannot open the token for impersonate or
+// duplicate permissions. This should limit potential security holes.
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token);
+
+// Hardens the integrity level policy on the current process. This is only
+// valid on Win 7 and above. Specifically it sets the policy to block read
+// and execute so that a lower privileged process cannot open the token for
+// impersonate or duplicate permissions. This should limit potential security
+// holes.
+DWORD HardenProcessIntegrityLevelPolicy();
+
+// Create a lowbox token. This is not valid prior to Windows 8.
+// |base_token| a base token to derive the lowbox token from. Can be nullptr.
+// |security_capabilities| list of LowBox capabilities to use when creating the
+// token.
+// |token| is the output value containing the handle of the newly created
+// restricted token.
+// |lockdown_default_dacl| indicates the token's default DACL should be locked
+// down to restrict what other process can open kernel resources created while
+// running under the token.
+DWORD CreateLowBoxToken(HANDLE base_token,
+ TokenType token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ PHANDLE saved_handles,
+ DWORD saved_handles_count,
+ base::win::ScopedHandle* token);
+
+// Create a lowbox object directory token. This is not valid prior to Windows 8.
+// This returns the Win32 error code from the operation.
+// |lowbox_sid| the SID for the LowBox.
+// |open_directory| open the directory if it already exists.
+// |directory| is the output value for the directory object.
+DWORD CreateLowBoxObjectDirectory(PSID lowbox_sid,
+ bool open_directory,
+ base::win::ScopedHandle* directory);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.cc b/security/sandbox/chromium/sandbox/win/src/sandbox.cc
new file mode 100644
index 0000000000..f65e379683
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox.h"
+
+#include <windows.h>
+
+#include "sandbox/win/src/broker_services.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+// The section for IPC and policy.
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+static bool s_is_broker = false;
+
+// GetBrokerServices: the current implementation relies on a shared section
+// that is created by the broker and opened by the target.
+BrokerServices* SandboxFactory::GetBrokerServices() {
+ // Can't be the broker if the shared section is open.
+ if (g_shared_section)
+ return nullptr;
+ // If the shared section does not exist we are the broker, then create
+ // the broker object.
+ s_is_broker = true;
+ return BrokerServicesBase::GetInstance();
+}
+
+// GetTargetServices implementation must follow the same technique as the
+// GetBrokerServices, but in this case the logic is the opposite.
+TargetServices* SandboxFactory::GetTargetServices() {
+ // Can't be the target if the section handle is not valid.
+ if (!g_shared_section)
+ return nullptr;
+ // We are the target
+ s_is_broker = false;
+ // Creates and returns the target services implementation.
+ return TargetServicesBase::GetInstance();
+}
+
+} // namespace sandbox
+
+// Allows querying for whether the current process has been sandboxed.
+extern "C" bool __declspec(dllexport) IsSandboxedProcess() {
+ return !!sandbox::g_shared_section;
+}
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
new file mode 100644
index 0000000000..858c350558
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
@@ -0,0 +1,228 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Sandbox is a sandbox library for windows processes. Use when you want a
+// 'privileged' process and a 'locked down process' to interact with.
+// The privileged process is called the broker and it is started by external
+// means (such as the user starting it). The 'sandboxed' process is called the
+// target and it is started by the broker. There can be many target processes
+// started by a single broker process. This library provides facilities
+// for both the broker and the target.
+//
+// The design rationale and relevant documents can be found at http://go/sbox.
+//
+// Note: this header does not include the SandboxFactory definitions because
+// there are cases where the Sandbox library is linked against the main .exe
+// while its API needs to be used in a DLL.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_H_
+#define SANDBOX_WIN_SRC_SANDBOX_H_
+
+#if !defined(SANDBOX_FUZZ_TARGET)
+#include <windows.h>
+#else
+#include "sandbox/win/fuzzer/fuzzer_types.h"
+#endif
+
+#include <stddef.h>
+#include <memory>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+// sandbox: Google User-Land Application Sandbox
+namespace sandbox {
+
+class BrokerServices;
+class PolicyDiagnosticsReceiver;
+class ProcessState;
+class TargetPolicy;
+class TargetServices;
+
+// BrokerServices exposes all the broker API.
+// The basic use is to start the target(s) and wait for them to end.
+//
+// This API is intended to be called in the following order
+// (error checking omitted):
+// BrokerServices* broker = SandboxFactory::GetBrokerServices();
+// broker->Init();
+// PROCESS_INFORMATION target;
+// broker->SpawnTarget(target_exe_path, target_args, &target);
+// ::ResumeThread(target->hThread);
+// // -- later you can call:
+// broker->WaitForAllTargets(option);
+//
+class BrokerServices {
+ public:
+ // Initializes the broker. Must be called before any other on this class.
+ // returns ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode Init() = 0;
+
+ // Returns the interface pointer to a new, empty policy object. Use this
+ // interface to specify the sandbox policy for new processes created by
+ // SpawnTarget()
+ virtual scoped_refptr<TargetPolicy> CreatePolicy() = 0;
+
+ // Creates a new target (child process) in a suspended state.
+ // Parameters:
+ // exe_path: This is the full path to the target binary. This parameter
+ // can be null and in this case the exe path must be the first argument
+ // of the command_line.
+ // command_line: The arguments to be passed as command line to the new
+ // process. This can be null if the exe_path parameter is not null.
+ // policy: This is the pointer to the policy object for the sandbox to
+ // be created.
+ // last_warning: The argument will contain an indication on whether
+ // the process security was initialized completely, Only set if the
+ // process can be used without a serious compromise in security.
+ // last_error: If an error or warning is returned from this method this
+ // parameter will hold the last Win32 error value.
+ // target: returns the resulting target process information such as process
+ // handle and PID just as if CreateProcess() had been called. The caller is
+ // responsible for closing the handles returned in this structure.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ virtual ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) = 0;
+
+ // This call blocks (waits) for all the targets to terminate.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode WaitForAllTargets() = 0;
+
+ // Adds an unsandboxed process as a peer for policy decisions (e.g.
+ // HANDLES_DUP_ANY policy).
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode AddTargetPeer(HANDLE peer_process) = 0;
+
+ // This call creates a snapshot of policies managed by the sandbox and
+ // returns them via a helper class.
+ // Parameters:
+ // receiver: The |PolicyDiagnosticsReceiver| implementation will be
+ // called to accept the results of the call.
+ // Returns:
+ // ALL_OK if the request was dispatched. All other return values
+ // imply failure, and the responder will not receive its completion
+ // callback.
+ virtual ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) = 0;
+
+ // Derive a capability PSID from the given string.
+ virtual bool DeriveCapabilitySidFromName(const wchar_t* name,
+ PSID derived_sid,
+ DWORD sid_buffer_length) = 0;
+
+ protected:
+ ~BrokerServices() {}
+};
+
+// TargetServices models the current process from the perspective
+// of a target process. To obtain a pointer to it use
+// Sandbox::GetTargetServices(). Note that this call returns a non-null
+// pointer only if this process is in fact a target. A process is a target
+// only if the process was spawned by a call to BrokerServices::SpawnTarget().
+//
+// This API allows the target to gain access to resources with a high
+// privilege token and then when it is ready to perform dangerous activities
+// (such as download content from the web) it can lower its token and
+// enter into locked-down (sandbox) mode.
+// The typical usage is as follows:
+//
+// TargetServices* target_services = Sandbox::GetTargetServices();
+// if (target_services) {
+// // We are the target.
+// target_services->Init();
+// // Do work that requires high privileges here.
+// // ....
+// // When ready to enter lock-down mode call LowerToken:
+// target_services->LowerToken();
+// }
+//
+// For more information see the BrokerServices API documentation.
+class TargetServices {
+ public:
+ // Initializes the target. Must call this function before any other.
+ // returns ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode Init() = 0;
+
+ // Discards the impersonation token and uses the lower token, call before
+ // processing any untrusted data or running third-party code. If this call
+ // fails the current process could be terminated immediately.
+ virtual void LowerToken() = 0;
+
+ // Returns the ProcessState object. Through that object it's possible to have
+ // information about the current state of the process, such as whether
+ // LowerToken has been called or not.
+ virtual ProcessState* GetState() = 0;
+
+ // Requests the broker to duplicate the supplied handle into the target
+ // process. The target process must be an active sandbox child process
+ // and the source process must have a corresponding policy allowing
+ // handle duplication for this object type.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) = 0;
+
+ virtual ResultCode GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before) = 0;
+
+ protected:
+ ~TargetServices() {}
+};
+
+class PolicyInfo {
+ public:
+ // Returns a JSON representation of the policy snapshot.
+ // This pointer has the same lifetime as this PolicyInfo object.
+ virtual const char* JsonString() = 0;
+ virtual ~PolicyInfo() {}
+};
+
+// This is returned by BrokerServices::GetPolicyDiagnostics().
+// PolicyInfo entries need not be ordered.
+class PolicyList {
+ public:
+ virtual std::vector<std::unique_ptr<PolicyInfo>>::iterator begin() = 0;
+ virtual std::vector<std::unique_ptr<PolicyInfo>>::iterator end() = 0;
+ virtual size_t size() const = 0;
+ virtual ~PolicyList() {}
+};
+
+// This class mediates calls to BrokerServices::GetPolicyDiagnostics().
+class PolicyDiagnosticsReceiver {
+ public:
+ // ReceiveDiagnostics() should return quickly and should not block the
+ // thread on which it is called.
+ virtual void ReceiveDiagnostics(std::unique_ptr<PolicyList> policies) = 0;
+ // OnError() is passed any errors encountered and |ReceiveDiagnostics|
+ // will not be called.
+ virtual void OnError(ResultCode code) = 0;
+ virtual ~PolicyDiagnosticsReceiver() {}
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.vcproj b/security/sandbox/chromium/sandbox/win/src/sandbox.vcproj
new file mode 100644
index 0000000000..229441cbd5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox.vcproj
@@ -0,0 +1,648 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sandbox"
+ ProjectGUID="{881F6A97-D539-4C48-B401-DF04385B2343}"
+ RootNamespace="sandbox"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="security"
+ >
+ <File
+ RelativePath=".\acl.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\acl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\dep.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dep.h"
+ >
+ </File>
+ <File
+ RelativePath=".\job.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\job.h"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token.h"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\security_level.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sid.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\window.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\window.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Interception"
+ >
+ <File
+ RelativePath=".\eat_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\eat_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_agent.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_agent.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_internal.h"
+ >
+ </File>
+ <File
+ RelativePath=".\pe_image.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\pe_image.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\service_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\service_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_interceptions.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_interceptions.h"
+ >
+ </File>
+ <File
+ RelativePath=".\Wow64.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\Wow64.h"
+ >
+ </File>
+ <Filter
+ Name="sidestep"
+ >
+ <File
+ RelativePath=".\sidestep\ia32_modrm_map.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\ia32_opcode_map.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\preamble_patcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\preamble_patcher_with_stub.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="nt_level"
+ >
+ <File
+ RelativePath=".\nt_internals.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_target.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_target.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_util.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_util.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy_handlers"
+ >
+ <File
+ RelativePath=".\filesystem_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_policy.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="IPC"
+ >
+ <File
+ RelativePath=".\crosscall_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_server.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_server.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_tags.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_client.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_server.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_server.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy_base"
+ >
+ <File
+ RelativePath=".\policy_engine_opcodes.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_opcodes.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_processor.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_processor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_low_level.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_low_level.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy_base.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy_base.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\broker_services.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\broker_services.h"
+ >
+ </File>
+ <File
+ RelativePath=".\internal_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_broker.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_broker.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_factory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\shared_handles.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\shared_handles.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_process.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_process.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_services.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_services.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win2k_threadpool.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\win2k_threadpool.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\win_utils.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h b/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h
new file mode 100644
index 0000000000..07402521dc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SANDBOX_FACTORY_H__
+#define SANDBOX_SRC_SANDBOX_FACTORY_H__
+
+#include "base/macros.h"
+#include "sandbox/win/src/sandbox.h"
+
+// SandboxFactory is a set of static methods to get access to the broker
+// or target services object. Only one of the two methods (GetBrokerServices,
+// GetTargetServices) will return a non-null pointer and that should be used
+// as the indication that the process is the broker or the target:
+//
+// BrokerServices* broker_services = SandboxFactory::GetBrokerServices();
+// if (broker_services) {
+// //we are the broker, call broker api here
+// broker_services->Init();
+// } else {
+// TargetServices* target_services = SandboxFactory::GetTargetServices();
+// if (target_services) {
+// //we are the target, call target api here
+// target_services->Init();
+// }
+//
+// The methods in this class are expected to be called from a single thread
+//
+// The Sandbox library needs to be linked against the main executable, but
+// sometimes the API calls are issued from a DLL that loads into the exe
+// process. These factory methods then need to be called from the main
+// exe and the interface pointers then can be safely passed to the DLL where
+// the Sandbox API calls are made.
+namespace sandbox {
+
+class SandboxFactory {
+ public:
+ // Returns the Broker API interface, returns nullptr if this process is the
+ // target.
+ static BrokerServices* GetBrokerServices();
+
+ // Returns the Target API interface, returns nullptr if this process is the
+ // broker.
+ static TargetServices* GetTargetServices();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxFactory);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_FACTORY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc
new file mode 100644
index 0000000000..3387e3f604
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc
@@ -0,0 +1,18 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// The section for IPC and policy.
+SANDBOX_INTERCEPT HANDLE g_shared_section = nullptr;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt = {};
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h
new file mode 100644
index 0000000000..eff0f019d3
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SANDBOX_NT_TYPES_H__
+#define SANDBOX_SRC_SANDBOX_NT_TYPES_H__
+
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+struct NtExports {
+ NtAllocateVirtualMemoryFunction AllocateVirtualMemory;
+ NtCloseFunction Close;
+ NtDuplicateObjectFunction DuplicateObject;
+ NtFreeVirtualMemoryFunction FreeVirtualMemory;
+ NtMapViewOfSectionFunction MapViewOfSection;
+ NtProtectVirtualMemoryFunction ProtectVirtualMemory;
+ NtQueryInformationProcessFunction QueryInformationProcess;
+ NtQueryObjectFunction QueryObject;
+ NtQuerySectionFunction QuerySection;
+ NtQueryVirtualMemoryFunction QueryVirtualMemory;
+ NtUnmapViewOfSectionFunction UnmapViewOfSection;
+ NtSignalAndWaitForSingleObjectFunction SignalAndWaitForSingleObject;
+ NtWaitForSingleObjectFunction WaitForSingleObject;
+ RtlAllocateHeapFunction RtlAllocateHeap;
+ RtlAnsiStringToUnicodeStringFunction RtlAnsiStringToUnicodeString;
+ RtlCompareUnicodeStringFunction RtlCompareUnicodeString;
+ RtlCreateHeapFunction RtlCreateHeap;
+ RtlCreateUserThreadFunction RtlCreateUserThread;
+ RtlDestroyHeapFunction RtlDestroyHeap;
+ RtlFreeHeapFunction RtlFreeHeap;
+ _strnicmpFunction _strnicmp;
+ strlenFunction strlen;
+ wcslenFunction wcslen;
+ memcpyFunction memcpy;
+};
+
+// This is the value used for the ntdll level allocator.
+enum AllocationType {
+ NT_ALLOC,
+ NT_PAGE
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_NT_TYPES_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
new file mode 100644
index 0000000000..2641a13d04
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
@@ -0,0 +1,755 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+} // namespace sandbox
+
+namespace {
+
+#if defined(_WIN64)
+// Align a pointer to the next allocation granularity boundary.
+inline char* AlignToBoundary(void* ptr, size_t increment) {
+ const size_t kAllocationGranularity = (64 * 1024) - 1;
+ uintptr_t ptr_int = reinterpret_cast<uintptr_t>(ptr);
+ uintptr_t ret_ptr =
+ (ptr_int + increment + kAllocationGranularity) & ~kAllocationGranularity;
+ // Check for overflow.
+ if (ret_ptr < ptr_int)
+ return nullptr;
+ return reinterpret_cast<char*>(ret_ptr);
+}
+
+// Allocate a memory block somewhere within 2GiB of a specified base address.
+// This is used for the DLL hooking code to get a valid trampoline location
+// which must be within +/- 2GiB of the base. We only consider +2GiB for now.
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+ // 2GiB, maximum upper bound the allocation address must be within.
+ const size_t kMaxSize = 0x80000000ULL;
+ // We don't support null as a base as this would just pick an arbitrary
+ // address when passed to NtAllocateVirtualMemory.
+ if (!source)
+ return nullptr;
+ // Ignore an allocation which is larger than the maximum.
+ if (size > kMaxSize)
+ return nullptr;
+
+ // Ensure base address is aligned to the allocation granularity boundary.
+ char* base = AlignToBoundary(source, 0);
+ if (!base)
+ return nullptr;
+ // Set top address to be base + 2GiB.
+ const char* top_address = base + kMaxSize;
+
+ while (base < top_address) {
+ // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
+ STACK_UNINITIALIZED MEMORY_BASIC_INFORMATION mem_info;
+ NTSTATUS status =
+ g_nt.QueryVirtualMemory(NtCurrentProcess, base, MemoryBasicInformation,
+ &mem_info, sizeof(mem_info), nullptr);
+ if (!NT_SUCCESS(status))
+ break;
+
+ if ((mem_info.State == MEM_FREE) && (mem_info.RegionSize >= size)) {
+ // We've found a valid free block, try and allocate it for use.
+ // Note that we need to both commit and reserve the block for the
+ // allocation to succeed as per Windows virtual memory requirements.
+ void* ret_base = mem_info.BaseAddress;
+ status =
+ g_nt.AllocateVirtualMemory(NtCurrentProcess, &ret_base, 0, &size,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ // Shouldn't fail, but if it does we'll just continue and try next block.
+ if (NT_SUCCESS(status))
+ return ret_base;
+ }
+
+ // Update base past current allocation region.
+ base = AlignToBoundary(mem_info.BaseAddress, mem_info.RegionSize);
+ if (!base)
+ break;
+ }
+ return nullptr;
+}
+#else // defined(_WIN64).
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+
+ // In 32-bit processes allocations below 512k are predictable, so mark
+ // anything in that range as reserved and retry until we get a good address.
+ const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024);
+ NTSTATUS ret;
+ SIZE_T actual_size;
+ void* base;
+ do {
+ base = nullptr;
+ actual_size = 64 * 1024;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_RESERVE, PAGE_NOACCESS);
+ if (!NT_SUCCESS(ret))
+ return nullptr;
+ } while (base < kMinAddress);
+
+ actual_size = size;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return nullptr;
+ return base;
+}
+#endif // defined(_WIN64).
+
+} // namespace.
+
+namespace sandbox {
+
+// Handle for our private heap.
+void* g_heap = nullptr;
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0;
+SANDBOX_INTERCEPT size_t g_shared_policy_size = 0;
+
+void* volatile g_shared_policy_memory = nullptr;
+void* volatile g_shared_IPC_memory = nullptr;
+
+// Both the IPC and the policy share a single region of memory in which the IPC
+// memory is first and the policy memory is last.
+bool MapGlobalMemory() {
+ if (!g_shared_IPC_memory) {
+ void* memory = nullptr;
+ SIZE_T size = 0;
+ // Map the entire shared section from the start.
+ NTSTATUS ret =
+ g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, &memory, 0, 0,
+ nullptr, &size, ViewUnmap, 0, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(ret) || !memory) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ if (_InterlockedCompareExchangePointer(&g_shared_IPC_memory, memory,
+ nullptr)) {
+ // Somebody beat us to the memory setup.
+ VERIFY_SUCCESS(g_nt.UnmapViewOfSection(NtCurrentProcess, memory));
+ }
+ DCHECK_NT(g_shared_IPC_size > 0);
+ g_shared_policy_memory =
+ reinterpret_cast<char*>(g_shared_IPC_memory) + g_shared_IPC_size;
+ }
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+ return true;
+}
+
+void* GetGlobalIPCMemory() {
+ if (!MapGlobalMemory())
+ return nullptr;
+ return g_shared_IPC_memory;
+}
+
+void* GetGlobalPolicyMemory() {
+ if (!MapGlobalMemory())
+ return nullptr;
+ return g_shared_policy_memory;
+}
+
+bool InitHeap() {
+ if (!g_heap) {
+ // Create a new heap using default values for everything.
+ void* heap =
+ g_nt.RtlCreateHeap(HEAP_GROWABLE, nullptr, 0, 0, nullptr, nullptr);
+ if (!heap)
+ return false;
+
+ if (_InterlockedCompareExchangePointer(&g_heap, heap, nullptr)) {
+ // Somebody beat us to the memory setup.
+ g_nt.RtlDestroyHeap(heap);
+ }
+ }
+ return !!g_heap;
+}
+
+// Physically reads or writes from memory to verify that (at this time), it is
+// valid. Returns a dummy value.
+int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
+ const int kPageSize = 4096;
+ int dummy = 0;
+ volatile char* start = reinterpret_cast<char*>(buffer);
+ volatile char* end = start + size_bytes - 1;
+
+ if (WRITE == intent) {
+ for (; start < end; start += kPageSize) {
+ *start = *start;
+ }
+ *end = *end;
+ } else {
+ for (; start < end; start += kPageSize) {
+ dummy += *start;
+ }
+ dummy += *end;
+ }
+
+ return dummy;
+}
+
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
+ DCHECK_NT(size);
+ __try {
+ TouchMemory(buffer, size, intent);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ __try {
+ g_nt.memcpy(destination, source, bytes);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+ return ret;
+}
+
+NTSTATUS AllocAndGetFullPath(
+ HANDLE root,
+ const wchar_t* path,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* full_path) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(full_path);
+ DCHECK_NT(path);
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ static NtQueryObjectFunction NtQueryObject = nullptr;
+ 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, nullptr, 0, &size);
+
+ std::unique_ptr<OBJECT_NAME_INFORMATION, NtAllocDeleter> handle_name;
+ if (size) {
+ handle_name.reset(reinterpret_cast<OBJECT_NAME_INFORMATION*>(
+ new (NT_ALLOC) BYTE[size]));
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ ret = NtQueryObject(root, ObjectNameInformation, handle_name.get(),
+ 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->reset(new (NT_ALLOC) wchar_t[name_length / sizeof(wchar_t)]);
+ if (!*full_path)
+ break;
+ wchar_t* off = full_path->get();
+ 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) && *full_path)
+ full_path->reset(nullptr);
+
+ return ret;
+}
+
+// Hacky code... replace with AllocAndCopyObjectAttributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* out_name,
+ uint32_t* attributes,
+ HANDLE* root) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(out_name);
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
+ break;
+ if (!in_object->ObjectName)
+ break;
+ if (!in_object->ObjectName->Buffer)
+ break;
+
+ size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
+ out_name->reset(new (NT_ALLOC) wchar_t[size / sizeof(wchar_t)]);
+ if (!*out_name)
+ break;
+
+ ret = CopyData(out_name->get(), in_object->ObjectName->Buffer,
+ size - sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+
+ out_name->get()[size / sizeof(wchar_t) - 1] = L'\0';
+
+ if (attributes)
+ *attributes = in_object->Attributes;
+
+ if (root)
+ *root = in_object->RootDirectory;
+ ret = STATUS_SUCCESS;
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret) && *out_name)
+ out_name->reset(nullptr);
+
+ return ret;
+}
+
+NTSTATUS GetProcessId(HANDLE process, DWORD* process_id) {
+ PROCESS_BASIC_INFORMATION proc_info;
+ ULONG bytes_returned;
+
+ NTSTATUS ret =
+ g_nt.QueryInformationProcess(process, ProcessBasicInformation, &proc_info,
+ sizeof(proc_info), &bytes_returned);
+ if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
+ return ret;
+
+ *process_id = proc_info.UniqueProcessId;
+ return STATUS_SUCCESS;
+}
+
+bool IsSameProcess(HANDLE process) {
+ if (NtCurrentProcess == process)
+ return true;
+
+ static DWORD s_process_id = 0;
+
+ if (!s_process_id) {
+ NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+ }
+
+ DWORD process_id;
+ NTSTATUS ret = GetProcessId(process, &process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ return (process_id == s_process_id);
+}
+
+bool IsValidImageSection(HANDLE section,
+ PVOID* base,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size) {
+ if (!section || !base || !view_size || offset)
+ return false;
+
+ HANDLE query_section;
+
+ NTSTATUS ret =
+ g_nt.DuplicateObject(NtCurrentProcess, section, NtCurrentProcess,
+ &query_section, SECTION_QUERY, 0, 0);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ SECTION_BASIC_INFORMATION basic_info;
+ SIZE_T bytes_returned;
+ ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
+ sizeof(basic_info), &bytes_returned);
+
+ VERIFY_SUCCESS(g_nt.Close(query_section));
+
+ if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
+ return false;
+
+ if (!(basic_info.Attributes & SEC_IMAGE))
+ return false;
+
+ // Windows 10 2009+ may open PEs as SEC_IMAGE_NO_EXECUTE in non-dll-loading
+ // paths which looks identical to dll-loading unless we check if the section
+ // handle has execute rights.
+ // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
+ STACK_UNINITIALIZED OBJECT_BASIC_INFORMATION obj_info;
+ ULONG obj_size_returned;
+ ret = g_nt.QueryObject(section, ObjectBasicInformation, &obj_info,
+ sizeof(obj_info), &obj_size_returned);
+
+ if (!NT_SUCCESS(ret) || sizeof(obj_info) != obj_size_returned)
+ return false;
+
+ if (!(obj_info.GrantedAccess & SECTION_MAP_EXECUTE))
+ return false;
+
+ return true;
+}
+
+UNICODE_STRING* AnsiToUnicode(const char* string) {
+ ANSI_STRING ansi_string;
+ ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
+ ansi_string.MaximumLength = ansi_string.Length + 1;
+ ansi_string.Buffer = const_cast<char*>(string);
+
+ if (ansi_string.Length > ansi_string.MaximumLength)
+ return nullptr;
+
+ size_t name_bytes =
+ ansi_string.MaximumLength * sizeof(wchar_t) + sizeof(UNICODE_STRING);
+
+ UNICODE_STRING* out_string =
+ reinterpret_cast<UNICODE_STRING*>(new (NT_ALLOC) char[name_bytes]);
+ if (!out_string)
+ return nullptr;
+
+ out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+
+ BOOLEAN alloc_destination = false;
+ NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
+ alloc_destination);
+ DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return nullptr;
+ }
+
+ return out_string;
+}
+
+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)
+ UNICODE_STRING* out_name = nullptr;
+ __try {
+ do {
+ *flags = 0;
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+ *flags |= MODULE_IS_PE_IMAGE;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports) {
+ char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
+ out_name = AnsiToUnicode(name);
+ }
+
+ PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
+ if (headers) {
+ if (headers->OptionalHeader.AddressOfEntryPoint)
+ *flags |= MODULE_HAS_ENTRY_POINT;
+ if (headers->OptionalHeader.SizeOfCode)
+ *flags |= MODULE_HAS_CODE;
+ }
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
+const char* GetAnsiImageInfoFromModule(HMODULE module) {
+// PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable : 4509)
+ const char* out_name = nullptr;
+ __try {
+ do {
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports)
+ out_name = static_cast<const char*>(pe.RVAToAddr(exports->Name));
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
+UNICODE_STRING* GetBackingFilePath(PVOID address) {
+ // We'll start with something close to max_path charactes for the name.
+ SIZE_T buffer_bytes = MAX_PATH * 2;
+
+ for (;;) {
+ MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
+ new (NT_ALLOC) char[buffer_bytes]);
+
+ if (!section_name)
+ return nullptr;
+
+ SIZE_T returned_bytes;
+ NTSTATUS ret =
+ g_nt.QueryVirtualMemory(NtCurrentProcess, address, MemorySectionName,
+ section_name, buffer_bytes, &returned_bytes);
+
+ if (STATUS_BUFFER_OVERFLOW == ret) {
+ // Retry the call with the given buffer size.
+ operator delete(section_name, NT_ALLOC);
+ section_name = nullptr;
+ buffer_bytes = returned_bytes;
+ continue;
+ }
+ if (!NT_SUCCESS(ret)) {
+ operator delete(section_name, NT_ALLOC);
+ return nullptr;
+ }
+
+ return reinterpret_cast<UNICODE_STRING*>(section_name);
+ }
+}
+
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
+ if ((!module_path) || (!module_path->Buffer))
+ return nullptr;
+
+ wchar_t* sep = nullptr;
+ int start_pos = module_path->Length / sizeof(wchar_t) - 1;
+ int ix = start_pos;
+
+ for (; ix >= 0; --ix) {
+ if (module_path->Buffer[ix] == L'\\') {
+ sep = &module_path->Buffer[ix];
+ break;
+ }
+ }
+
+ // Ends with path separator. Not a valid module name.
+ if ((ix == start_pos) && sep)
+ return nullptr;
+
+ // No path separator found. Use the entire name.
+ if (!sep) {
+ sep = &module_path->Buffer[-1];
+ }
+
+ // Add one to the size so we can null terminate the string.
+ size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
+
+ // Based on the code above, size_bytes should always be small enough
+ // to make the static_cast below safe.
+ DCHECK_NT(UINT16_MAX > size_bytes);
+ char* str_buffer = new (NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
+ if (!str_buffer)
+ return nullptr;
+
+ UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+ out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
+ out_string->MaximumLength = static_cast<USHORT>(size_bytes);
+
+ NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return nullptr;
+ }
+
+ out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
+ return out_string;
+}
+
+NTSTATUS AutoProtectMemory::ChangeProtection(void* address,
+ size_t bytes,
+ ULONG protect) {
+ DCHECK_NT(!changed_);
+ SIZE_T new_bytes = bytes;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
+ &new_bytes, protect, &old_protect_);
+ if (NT_SUCCESS(ret)) {
+ changed_ = true;
+ address_ = address;
+ bytes_ = new_bytes;
+ }
+
+ return ret;
+}
+
+NTSTATUS AutoProtectMemory::RevertProtection() {
+ if (!changed_)
+ return STATUS_SUCCESS;
+
+ DCHECK_NT(address_);
+ DCHECK_NT(bytes_);
+
+ SIZE_T new_bytes = bytes_;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(
+ NtCurrentProcess, &address_, &new_bytes, old_protect_, &old_protect_);
+ DCHECK_NT(NT_SUCCESS(ret));
+
+ changed_ = false;
+ address_ = nullptr;
+ bytes_ = 0;
+ old_protect_ = 0;
+
+ return ret;
+}
+
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info,
+ DWORD length,
+ uint32_t file_info_class) {
+ if (FileRenameInformation != file_info_class)
+ return false;
+
+ if (length < sizeof(FILE_RENAME_INFORMATION))
+ return false;
+
+ // Make sure file name length doesn't exceed the message length
+ if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
+ file_info->FileNameLength)
+ return false;
+
+ // We don't support a root directory.
+ if (file_info->RootDirectory)
+ return false;
+
+ static const wchar_t kPathPrefix[] = {L'\\', L'?', L'?', L'\\'};
+
+ // Check if it starts with \\??\\. We don't support relative paths.
+ if (file_info->FileNameLength < sizeof(kPathPrefix) ||
+ file_info->FileNameLength > UINT16_MAX)
+ return false;
+
+ if (file_info->FileName[0] != kPathPrefix[0] ||
+ file_info->FileName[1] != kPathPrefix[1] ||
+ file_info->FileName[2] != kPathPrefix[2] ||
+ file_info->FileName[3] != kPathPrefix[3])
+ return false;
+
+ return true;
+}
+
+bool NtGetPathFromHandle(HANDLE handle,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* path) {
+ OBJECT_NAME_INFORMATION initial_buffer;
+ OBJECT_NAME_INFORMATION* name;
+ ULONG size = 0;
+ // Query the name information a first time to get the size of the name.
+ NTSTATUS status = g_nt.QueryObject(handle, ObjectNameInformation,
+ &initial_buffer, size, &size);
+
+ if (!NT_SUCCESS(status) && status != STATUS_INFO_LENGTH_MISMATCH)
+ return false;
+
+ std::unique_ptr<BYTE[], NtAllocDeleter> name_ptr;
+ if (!size)
+ return false;
+ name_ptr.reset(new (NT_ALLOC) BYTE[size]);
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = g_nt.QueryObject(handle, ObjectNameInformation, name, size, &size);
+
+ if (STATUS_SUCCESS != status)
+ return false;
+ size_t num_path_wchars = (name->ObjectName.Length / sizeof(wchar_t)) + 1;
+ path->reset(new (NT_ALLOC) wchar_t[num_path_wchars]);
+ status =
+ CopyData(path->get(), name->ObjectName.Buffer, name->ObjectName.Length);
+ path->get()[num_path_wchars - 1] = L'\0';
+ if (STATUS_SUCCESS != status)
+ return false;
+ return true;
+}
+
+} // namespace sandbox
+
+void* operator new(size_t size, sandbox::AllocationType type, void* near_to) {
+ void* result = nullptr;
+ if (type == sandbox::NT_ALLOC) {
+ if (sandbox::InitHeap()) {
+ // Use default flags for the allocation.
+ result = sandbox::g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
+ }
+ } else if (type == sandbox::NT_PAGE) {
+ result = AllocateNearTo(near_to, size);
+ } else {
+ NOTREACHED_NT();
+ }
+
+ // TODO: Returning nullptr from operator new has undefined behavior, but
+ // the Allocate() functions called above can return nullptr. Consider checking
+ // for nullptr here and crashing or throwing.
+
+ return result;
+}
+
+void* operator new [](size_t size, sandbox::AllocationType type,
+ void* near_to) {
+ return operator new(size, type, near_to);
+}
+
+void operator delete(void* memory, sandbox::AllocationType type) {
+ if (type == sandbox::NT_ALLOC) {
+ // Use default flags.
+ 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(sandbox::g_nt.FreeVirtualMemory(NtCurrentProcess, &base,
+ &size, MEM_RELEASE));
+ } else {
+ NOTREACHED_NT();
+ }
+}
+
+void operator delete(void* memory,
+ sandbox::AllocationType type,
+ void* near_to) {
+ operator delete(memory, type);
+}
+
+void* __cdecl operator new(size_t size,
+ void* buffer,
+ sandbox::AllocationType type) {
+ return buffer;
+}
+
+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
new file mode 100644
index 0000000000..70d011dfb4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SANDBOX_NT_UTIL_H_
+#define SANDBOX_SRC_SANDBOX_NT_UTIL_H_
+
+#include <intrin.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+
+// Placement new and delete to be used from ntdll interception code.
+void* __cdecl operator new(size_t size,
+ sandbox::AllocationType type,
+ void* near_to = nullptr);
+void* __cdecl operator new[](size_t size,
+ sandbox::AllocationType type,
+ void* near_to = nullptr);
+void __cdecl operator delete(void* memory, sandbox::AllocationType type);
+// Add operator delete that matches the placement form of the operator new
+// above. This is required by compiler to generate code to call operator delete
+// in case the object's constructor throws an exception.
+// See http://msdn.microsoft.com/en-us/library/cxdxz3x6.aspx
+void __cdecl operator delete(void* memory,
+ sandbox::AllocationType type,
+ void* near_to);
+
+// Regular placement new and delete
+void* __cdecl operator new(size_t size,
+ void* buffer,
+ sandbox::AllocationType type);
+void __cdecl operator delete(void* memory,
+ void* buffer,
+ sandbox::AllocationType type);
+
+// DCHECK_NT is defined to be pretty much an assert at this time because we
+// don't have logging from the ntdll layer on the child.
+//
+// VERIFY_NT and VERIFY_SUCCESS 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 expects an action returning
+// NTSTATUS.
+#ifndef NDEBUG
+#define DCHECK_NT(condition) \
+ { (condition) ? (void)0 : __debugbreak(); }
+#define VERIFY(action) DCHECK_NT(action)
+#define VERIFY_SUCCESS(action) DCHECK_NT(NT_SUCCESS(action))
+#else
+#define DCHECK_NT(condition)
+#define VERIFY(action) (action)
+#define VERIFY_SUCCESS(action) (action)
+#endif
+
+#define CHECK_NT(condition) \
+ { (condition) ? (void)0 : __debugbreak(); }
+
+#define NOTREACHED_NT() DCHECK_NT(false)
+
+namespace sandbox {
+
+#if defined(_M_X64) || defined(_M_ARM64)
+#pragma intrinsic(_InterlockedCompareExchange)
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+
+#elif defined(_M_IX86)
+extern "C" long _InterlockedCompareExchange(long volatile* destination,
+ long exchange,
+ long comperand);
+
+#pragma intrinsic(_InterlockedCompareExchange)
+
+// We want to make sure that we use an intrinsic version of the function, not
+// the one provided by kernel32.
+__forceinline void* _InterlockedCompareExchangePointer(
+ void* volatile* destination,
+ void* exchange,
+ void* comperand) {
+ long ret = _InterlockedCompareExchange(
+ reinterpret_cast<long volatile*>(destination),
+ static_cast<long>(reinterpret_cast<size_t>(exchange)),
+ static_cast<long>(reinterpret_cast<size_t>(comperand)));
+
+ return reinterpret_cast<void*>(static_cast<size_t>(ret));
+}
+
+#else
+#error Architecture not supported.
+
+#endif
+
+struct NtAllocDeleter {
+ inline void operator()(void* ptr) const {
+ operator delete(ptr, AllocationType::NT_ALLOC);
+ }
+};
+
+// Returns a pointer to the IPC shared memory.
+void* GetGlobalIPCMemory();
+
+// Returns a pointer to the Policy shared memory.
+void* GetGlobalPolicyMemory();
+
+enum RequiredAccess { READ, WRITE };
+
+// Performs basic user mode buffer validation. In any case, buffers access must
+// be protected by SEH. intent specifies if the buffer should be tested for read
+// or write.
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent);
+
+// Copies data from a user buffer to our buffer. Returns the operation status.
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes);
+
+// Copies the name from an object attributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* out_name,
+ uint32_t* attributes,
+ HANDLE* root);
+
+// Determine full path name from object root and path.
+NTSTATUS AllocAndGetFullPath(
+ HANDLE root,
+ const wchar_t* path,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* full_path);
+
+// Initializes our ntdll level heap
+bool InitHeap();
+
+// Returns true if the provided handle refers to the current process.
+bool IsSameProcess(HANDLE process);
+
+enum MappedModuleFlags {
+ MODULE_IS_PE_IMAGE = 1, // Module is an executable.
+ MODULE_HAS_ENTRY_POINT = 2, // Execution entry point found.
+ MODULE_HAS_CODE = 4 // Non zero size of executable sections.
+};
+
+// Returns the name and characteristics for a given PE module. The return
+// value is the name as defined by the export table and the flags is any
+// combination of the MappedModuleFlags enumeration.
+//
+// The returned buffer must be freed with a placement delete from the ntdll
+// level allocator:
+//
+// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags);
+// if (!name) {
+// // probably not a valid dll
+// return;
+// }
+// InsertYourLogicHere(name);
+// operator delete(name, NT_ALLOC);
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags);
+
+// Returns the name and characteristics for a given PE module. The return
+// value is the name as defined by the export table.
+//
+// The returned buffer is within the PE module and must not be freed.
+const char* GetAnsiImageInfoFromModule(HMODULE module);
+
+// Returns the full path and filename for a given dll.
+// May return nullptr if the provided address is not backed by a named section,
+// or if the current OS version doesn't support the call. The returned buffer
+// must be freed with a placement delete (see GetImageNameFromModule example).
+UNICODE_STRING* GetBackingFilePath(PVOID address);
+
+// Returns the last component of a path that contains the module name.
+// It will return nullptr if the path ends with the path separator. The returned
+// buffer must be freed with a placement delete (see GetImageNameFromModule
+// example).
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path);
+
+// Returns true if the parameters correspond to a dll mapped as code.
+bool IsValidImageSection(HANDLE section,
+ PVOID* base,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size);
+
+// Converts an ansi string to an UNICODE_STRING.
+UNICODE_STRING* AnsiToUnicode(const char* string);
+
+// Resolves a handle to an nt path. Returns true if the handle can be resolved.
+bool NtGetPathFromHandle(HANDLE handle,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* path);
+
+// Provides a simple way to temporarily change the protection of a memory page.
+class AutoProtectMemory {
+ public:
+ AutoProtectMemory()
+ : changed_(false), address_(nullptr), bytes_(0), old_protect_(0) {}
+
+ ~AutoProtectMemory() { RevertProtection(); }
+
+ // Sets the desired protection of a given memory range.
+ NTSTATUS ChangeProtection(void* address, size_t bytes, ULONG protect);
+
+ // Restores the original page protection.
+ NTSTATUS RevertProtection();
+
+ private:
+ bool changed_;
+ void* address_;
+ size_t bytes_;
+ ULONG old_protect_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
+};
+
+// Returns true if the file_rename_information structure is supported by our
+// rename handler.
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info,
+ DWORD length,
+ uint32_t file_info_class);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
new file mode 100644
index 0000000000..75514ef595
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
@@ -0,0 +1,296 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/memory/scoped_refptr.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/security_level.h"
+
+namespace sandbox {
+
+class AppContainerProfile;
+
+class TargetPolicy {
+ public:
+ // Windows subsystems that can have specific rules.
+ // Note: The process subsystem(SUBSYS_PROCESS) does not evaluate the request
+ // exactly like the CreateProcess API does. See the comment at the top of
+ // process_thread_dispatcher.cc for more details.
+ enum SubSystem {
+ SUBSYS_FILES, // Creation and opening of files and pipes.
+ SUBSYS_NAMED_PIPES, // Creation of named pipes.
+ SUBSYS_PROCESS, // Creation of child processes.
+ SUBSYS_REGISTRY, // Creation and opening of registry keys.
+ SUBSYS_SYNC, // Creation of named sync objects.
+ SUBSYS_HANDLES, // Duplication of handles to other processes.
+ SUBSYS_WIN32K_LOCKDOWN, // Win32K Lockdown related policy.
+ SUBSYS_SIGNED_BINARY, // Signed binary policy.
+ SUBSYS_LINE_BREAK // Complex line break policy.
+ };
+
+ // Allowable semantics when a rule is matched.
+ enum Semantics {
+ FILES_ALLOW_ANY, // Allows open or create for any kind of access that
+ // the file system supports.
+ FILES_ALLOW_READONLY, // Allows open or create with read access only.
+ FILES_ALLOW_QUERY, // Allows access to query the attributes of a file.
+ FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics
+ // only.
+ HANDLES_DUP_ANY, // Allows duplicating handles opened with any
+ // access permissions.
+ HANDLES_DUP_BROKER, // Allows duplicating handles to the broker process.
+ NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe.
+ PROCESS_MIN_EXEC, // Allows to create a process with minimal rights
+ // over the resulting process and thread handles.
+ // No other parameters besides the command line are
+ // passed to the child process.
+ PROCESS_ALL_EXEC, // Allows the creation of a process and return full
+ // access on the returned handles.
+ // This flag can be used only when the main token of
+ // the sandboxed application is at least INTERACTIVE.
+ EVENTS_ALLOW_ANY, // Allows the creation of an event with full access.
+ EVENTS_ALLOW_READONLY, // Allows opening an even with synchronize access.
+ REG_ALLOW_READONLY, // Allows readonly access to a registry key.
+ REG_ALLOW_ANY, // Allows read and write access to a registry key.
+ FAKE_USER_GDI_INIT, // Fakes user32 and gdi32 initialization. This can
+ // be used to allow the DLLs to load and initialize
+ // even if the process cannot access that subsystem.
+ IMPLEMENT_OPM_APIS, // Implements FAKE_USER_GDI_INIT and also exposes
+ // IPC calls to handle Output Protection Manager
+ // APIs.
+ SIGNED_ALLOW_LOAD, // Allows loading the module when CIG is enabled.
+ LINE_BREAK_ALLOW // Allow complex line break brokering.
+ };
+
+ // Increments the reference count of this object. The reference count must
+ // be incremented if this interface is given to another component.
+ virtual void AddRef() = 0;
+
+ // Decrements the reference count of this object. When the reference count
+ // is zero the object is automatically destroyed.
+ // Indicates that the caller is done with this interface. After calling
+ // release no other method should be called.
+ virtual void Release() = 0;
+
+ // Sets the security level for the target process' two tokens.
+ // This setting is permanent and cannot be changed once the target process is
+ // spawned.
+ // initial: the security level for the initial token. This is the token that
+ // is used by the process from the creation of the process until the moment
+ // the process calls TargetServices::LowerToken() or the process calls
+ // win32's 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
+ // 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
+ // value.
+ //
+ // Important: most of the sandbox-provided security relies on this single
+ // setting. The caller should strive to set the lockdown level as restricted
+ // as possible.
+ virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0;
+
+ // Returns the initial token level.
+ virtual TokenLevel GetInitialTokenLevel() const = 0;
+
+ // Returns the lockdown token level.
+ virtual TokenLevel GetLockdownTokenLevel() const = 0;
+
+ // Sets that we should not use restricting SIDs in the access tokens. We need
+ // to do this in some circumstances even though it weakens the sandbox.
+ // The default is to use them.
+ virtual void SetDoNotUseRestrictingSIDs() = 0;
+
+ // Sets the security level of the Job Object to which the target process will
+ // belong. This setting is permanent and cannot be changed once the target
+ // process is spawned. The job controls the global security settings which
+ // can not be specified in the token security profile.
+ // job_level: the security level for the job. See the explanation of each
+ // level in the JobLevel definition.
+ // ui_exceptions: specify what specific rights that are disabled in the
+ // chosen job_level that need to be granted. Use this parameter to avoid
+ // selecting the next permissive job level unless you need all the rights
+ // that are granted in such level.
+ // The exceptions can be specified as a combination of the following
+ // constants:
+ // JOB_OBJECT_UILIMIT_HANDLES : grant access to all user-mode handles. These
+ // include windows, icons, menus and various GDI objects. In addition the
+ // target process can set hooks, and broadcast messages to other processes
+ // that belong to the same desktop.
+ // JOB_OBJECT_UILIMIT_READCLIPBOARD : grant read-only access to the clipboard.
+ // JOB_OBJECT_UILIMIT_WRITECLIPBOARD : grant write access to the clipboard.
+ // JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS : allow changes to the system-wide
+ // parameters as defined by the Win32 call SystemParametersInfo().
+ // JOB_OBJECT_UILIMIT_DISPLAYSETTINGS : allow programmatic changes to the
+ // display settings.
+ // JOB_OBJECT_UILIMIT_GLOBALATOMS : allow access to the global atoms table.
+ // JOB_OBJECT_UILIMIT_DESKTOP : allow the creation of new desktops.
+ // JOB_OBJECT_UILIMIT_EXITWINDOWS : allow the call to ExitWindows().
+ //
+ // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise.
+ //
+ // Note: JOB_OBJECT_XXXX constants are defined in winnt.h and documented at
+ // length in:
+ // http://msdn2.microsoft.com/en-us/library/ms684152.aspx
+ //
+ // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN.
+ virtual ResultCode SetJobLevel(JobLevel job_level,
+ uint32_t ui_exceptions) = 0;
+
+ // Returns the job level.
+ virtual JobLevel GetJobLevel() const = 0;
+
+ // Sets a hard limit on the size of the commit set for the sandboxed process.
+ // If the limit is reached, the process will be terminated with
+ // SBOX_FATAL_MEMORY_EXCEEDED (7012).
+ virtual ResultCode SetJobMemoryLimit(size_t memory_limit) = 0;
+
+ // Specifies the desktop on which the application is going to run. If the
+ // desktop does not exist, it will be created. If alternate_winstation is
+ // set to true, the desktop will be created on an alternate window station.
+ virtual ResultCode SetAlternateDesktop(bool alternate_winstation) = 0;
+
+ // Returns the name of the alternate desktop used. If an alternate window
+ // station is specified, the name is prepended by the window station name,
+ // followed by a backslash.
+ virtual std::wstring GetAlternateDesktop() const = 0;
+
+ // Precreates the desktop and window station, if any.
+ virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) = 0;
+
+ // Destroys the desktop and windows station.
+ virtual void DestroyAlternateDesktop() = 0;
+
+ // Sets the integrity level of the process in the sandbox. Both the initial
+ // token and the main token will be affected by this. If the integrity level
+ // is set to a level higher than the current level, the sandbox will fail
+ // to start.
+ virtual ResultCode SetIntegrityLevel(IntegrityLevel level) = 0;
+
+ // Returns the initial integrity level used.
+ virtual IntegrityLevel GetIntegrityLevel() const = 0;
+
+ // Sets the integrity level of the process in the sandbox. The integrity level
+ // will not take effect before you call LowerToken. User Interface Privilege
+ // Isolation is not affected by this setting and will remain off for the
+ // process in the sandbox. If the integrity level is set to a level higher
+ // than the current level, the sandbox will fail to start.
+ virtual ResultCode SetDelayedIntegrityLevel(IntegrityLevel level) = 0;
+
+ // Sets the LowBox token for sandboxed process. This is mutually exclusive
+ // with SetAppContainer method.
+ virtual ResultCode SetLowBox(const wchar_t* sid) = 0;
+
+ // Sets the mitigations enabled when the process is created. Most of these
+ // are implemented as attributes passed via STARTUPINFOEX. So they take
+ // effect before any thread in the target executes. The declaration of
+ // MitigationFlags is followed by a detailed description of each flag.
+ virtual ResultCode SetProcessMitigations(MitigationFlags flags) = 0;
+
+ // Returns the currently set mitigation flags.
+ virtual MitigationFlags GetProcessMitigations() = 0;
+
+ // Sets process mitigation flags that don't take effect before the call to
+ // LowerToken().
+ virtual ResultCode SetDelayedProcessMitigations(MitigationFlags flags) = 0;
+
+ // Returns the currently set delayed mitigation flags.
+ virtual MitigationFlags GetDelayedProcessMitigations() const = 0;
+
+ // Disconnect the target from CSRSS when TargetServices::LowerToken() is
+ // called inside the target.
+ virtual ResultCode SetDisconnectCsrss() = 0;
+
+ // Sets the interceptions to operate in strict mode. By default, interceptions
+ // are performed in "relaxed" mode, where if something inside NTDLL.DLL is
+ // already patched we attempt to intercept it anyway. Setting interceptions
+ // to strict mode means that when we detect that the function is patched we'll
+ // refuse to perform the interception.
+ virtual void SetStrictInterceptions() = 0;
+
+ // Set the handles the target process should inherit for stdout and
+ // stderr. The handles the caller passes must remain valid for the
+ // lifetime of the policy object. This only has an effect on
+ // Windows Vista and later versions. These methods accept pipe and
+ // file handles, but not console handles.
+ virtual ResultCode SetStdoutHandle(HANDLE handle) = 0;
+ virtual ResultCode SetStderrHandle(HANDLE handle) = 0;
+
+ // Adds a policy rule effective for processes spawned using this policy.
+ // subsystem: One of the above enumerated windows subsystems.
+ // semantics: One of the above enumerated FileSemantics.
+ // pattern: A specific full path or a full path with wildcard patterns.
+ // The valid wildcards are:
+ // '*' : Matches zero or more character. Only one in series allowed.
+ // '?' : Matches a single character. One or more in series are allowed.
+ // Examples:
+ // "c:\\documents and settings\\vince\\*.dmp"
+ // "c:\\documents and settings\\*\\crashdumps\\*.dmp"
+ // "c:\\temp\\app_log_?????_chrome.txt"
+ virtual ResultCode AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) = 0;
+
+ // Adds a dll that will be unloaded in the target process before it gets
+ // a chance to initialize itself. Typically, dlls that cause the target
+ // to crash go here.
+ virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0;
+
+ // Adds a handle that will be closed in the target process after lockdown.
+ // A nullptr value for handle_name indicates all handles of the specified
+ // type. An empty string for handle_name indicates the handle is unnamed.
+ virtual ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) = 0;
+
+ // Adds a handle that will be shared with the target process. Does not take
+ // ownership of the handle.
+ virtual void AddHandleToShare(HANDLE handle) = 0;
+
+ // Locks down the default DACL of the created lockdown and initial tokens
+ // to restrict what other processes are allowed to access a process' kernel
+ // resources.
+ virtual void SetLockdownDefaultDacl() = 0;
+
+ // Adds a restricting random SID to the restricted SIDs list as well as
+ // the default DACL.
+ virtual void AddRestrictingRandomSid() = 0;
+
+ // Enable OPM API redirection when in Win32k lockdown.
+ virtual void SetEnableOPMRedirection() = 0;
+ // Enable OPM API emulation when in Win32k lockdown.
+ virtual bool GetEnableOPMRedirection() = 0;
+
+ // Configure policy to use an AppContainer profile. |package_name| is the
+ // name of the profile to use. Specifying True for |create_profile| ensures
+ // the profile exists, if set to False process creation will fail if the
+ // profile has not already been created.
+ virtual ResultCode AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) = 0;
+
+ // Get the configured AppContainerProfile.
+ virtual scoped_refptr<AppContainerProfile> GetAppContainerProfile() = 0;
+
+ // Set effective token that will be used for creating the initial and
+ // lockdown tokens. The token the caller passes must remain valid for the
+ // lifetime of the policy object.
+ virtual void SetEffectiveToken(HANDLE token) = 0;
+
+ protected:
+ ~TargetPolicy() {}
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
new file mode 100644
index 0000000000..f228dbbc31
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
@@ -0,0 +1,832 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+#include <sddl.h>
+#include <stddef.h>
+#include <stdint.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/win_util.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/line_break_policy.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.h"
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/registry_policy.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/signed_policy.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 {
+
+// The standard windows size for one memory page.
+constexpr size_t kOneMemPage = 4096;
+// The IPC and Policy shared memory sizes.
+constexpr size_t kIPCMemSize = kOneMemPage * 2;
+constexpr size_t kPolMemSize = kOneMemPage * 14;
+
+// Helper function to allocate space (on the heap) for policy.
+sandbox::PolicyGlobal* MakeBrokerPolicyMemory() {
+ const size_t kTotalPolicySz = kPolMemSize;
+ sandbox::PolicyGlobal* policy =
+ static_cast<sandbox::PolicyGlobal*>(::operator new(kTotalPolicySz));
+ DCHECK(policy);
+ memset(policy, 0, kTotalPolicySz);
+ policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal);
+ return policy;
+}
+
+bool IsInheritableHandle(HANDLE handle) {
+ if (!handle)
+ return false;
+ if (handle == INVALID_HANDLE_VALUE)
+ return false;
+ // File handles (FILE_TYPE_DISK) and pipe handles are known to be
+ // inheritable. Console handles (FILE_TYPE_CHAR) are not
+ // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ DWORD handle_type = GetFileType(handle);
+ return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level;
+SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations;
+
+// Initializes static members. alternate_desktop_handle_ is a desktop on
+// alternate_winstation_handle_, alternate_desktop_local_winstation_handle_ is a
+// desktop on the same winstation as the parent process.
+HWINSTA PolicyBase::alternate_winstation_handle_ = nullptr;
+HDESK PolicyBase::alternate_desktop_handle_ = nullptr;
+HDESK PolicyBase::alternate_desktop_local_winstation_handle_ = nullptr;
+IntegrityLevel PolicyBase::alternate_desktop_integrity_level_label_ =
+ INTEGRITY_LEVEL_SYSTEM;
+IntegrityLevel
+ PolicyBase::alternate_desktop_local_winstation_integrity_level_label_ =
+ INTEGRITY_LEVEL_SYSTEM;
+
+PolicyBase::PolicyBase()
+ : ref_count(1),
+ lockdown_level_(USER_LOCKDOWN),
+ initial_level_(USER_LOCKDOWN),
+ job_level_(JOB_LOCKDOWN),
+ ui_exceptions_(0),
+ memory_limit_(0),
+ use_alternate_desktop_(false),
+ use_alternate_winstation_(false),
+ file_system_init_(false),
+ relaxed_interceptions_(true),
+ stdout_handle_(INVALID_HANDLE_VALUE),
+ stderr_handle_(INVALID_HANDLE_VALUE),
+ integrity_level_(INTEGRITY_LEVEL_LAST),
+ delayed_integrity_level_(INTEGRITY_LEVEL_LAST),
+ mitigations_(0),
+ delayed_mitigations_(0),
+ is_csrss_connected_(true),
+ policy_maker_(nullptr),
+ policy_(nullptr),
+ lowbox_sid_(nullptr),
+ lockdown_default_dacl_(false),
+ add_restricting_random_sid_(false),
+ enable_opm_redirection_(false),
+ effective_token_(nullptr) {
+ ::InitializeCriticalSection(&lock_);
+ dispatcher_.reset(new TopLevelDispatcher(this));
+}
+
+PolicyBase::~PolicyBase() {
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ TargetProcess* target = (*it);
+ delete target;
+ }
+ delete policy_maker_;
+ delete policy_;
+
+ if (lowbox_sid_)
+ ::LocalFree(lowbox_sid_);
+
+ ::DeleteCriticalSection(&lock_);
+}
+
+void PolicyBase::AddRef() {
+ ::InterlockedIncrement(&ref_count);
+}
+
+void PolicyBase::Release() {
+ if (0 == ::InterlockedDecrement(&ref_count))
+ delete this;
+}
+
+ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
+ if (initial < lockdown) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ initial_level_ = initial;
+ lockdown_level_ = lockdown;
+ return SBOX_ALL_OK;
+}
+
+TokenLevel PolicyBase::GetInitialTokenLevel() const {
+ return initial_level_;
+}
+
+TokenLevel PolicyBase::GetLockdownTokenLevel() const {
+ return lockdown_level_;
+}
+
+void PolicyBase::SetDoNotUseRestrictingSIDs() {
+ use_restricting_sids_ = false;
+}
+
+ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) {
+ if (memory_limit_ && job_level == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ job_level_ = job_level;
+ ui_exceptions_ = ui_exceptions;
+ return SBOX_ALL_OK;
+}
+
+JobLevel PolicyBase::GetJobLevel() const {
+ return job_level_;
+}
+
+ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) {
+ memory_limit_ = memory_limit;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
+ use_alternate_desktop_ = true;
+ use_alternate_winstation_ = alternate_winstation;
+ return CreateAlternateDesktop(alternate_winstation);
+}
+
+std::wstring PolicyBase::GetAlternateDesktop() const {
+ // No alternate desktop or winstation. Return an empty string.
+ if (!use_alternate_desktop_ && !use_alternate_winstation_) {
+ return std::wstring();
+ }
+
+ if (use_alternate_winstation_) {
+ // The desktop and winstation should have been created by now.
+ // If we hit this scenario, it means that the user ignored the failure
+ // during SetAlternateDesktop, so we ignore it here too.
+ if (!alternate_desktop_handle_ || !alternate_winstation_handle_)
+ return std::wstring();
+
+ return GetFullDesktopName(alternate_winstation_handle_,
+ alternate_desktop_handle_);
+ }
+
+ if (!alternate_desktop_local_winstation_handle_)
+ return std::wstring();
+
+ return GetFullDesktopName(nullptr,
+ alternate_desktop_local_winstation_handle_);
+}
+
+ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
+ if (alternate_winstation) {
+ // Check if it's already created.
+ if (alternate_winstation_handle_ && alternate_desktop_handle_)
+ return SBOX_ALL_OK;
+
+ DCHECK(!alternate_winstation_handle_);
+ // Create the window station.
+ ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_winstation_handle_ ||
+ base::win::GetWindowObjectName(alternate_winstation_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+
+ // Create the destkop.
+ result = CreateAltDesktop(alternate_winstation_handle_,
+ &alternate_desktop_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_handle_ ||
+ base::win::GetWindowObjectName(alternate_desktop_handle_).empty()) {
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ } else {
+ // Check if it already exists.
+ if (alternate_desktop_local_winstation_handle_)
+ return SBOX_ALL_OK;
+
+ // Create the destkop.
+ ResultCode result =
+ CreateAltDesktop(nullptr, &alternate_desktop_local_winstation_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_local_winstation_handle_ ||
+ base::win::GetWindowObjectName(
+ alternate_desktop_local_winstation_handle_)
+ .empty()) {
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+void PolicyBase::DestroyAlternateDesktop() {
+ if (use_alternate_winstation_) {
+ if (alternate_desktop_handle_) {
+ ::CloseDesktop(alternate_desktop_handle_);
+ alternate_desktop_handle_ = nullptr;
+ }
+
+ if (alternate_winstation_handle_) {
+ ::CloseWindowStation(alternate_winstation_handle_);
+ alternate_winstation_handle_ = nullptr;
+ }
+ } else {
+ if (alternate_desktop_local_winstation_handle_) {
+ ::CloseDesktop(alternate_desktop_local_winstation_handle_);
+ alternate_desktop_local_winstation_handle_ = nullptr;
+ }
+ }
+}
+
+ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ if (app_container_profile_)
+ return SBOX_ERROR_BAD_PARAMS;
+ integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+IntegrityLevel PolicyBase::GetIntegrityLevel() const {
+ return integrity_level_;
+}
+
+ResultCode PolicyBase::SetDelayedIntegrityLevel(
+ IntegrityLevel integrity_level) {
+ delayed_integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetLowBox(const wchar_t* sid) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ DCHECK(sid);
+ if (lowbox_sid_ || app_container_profile_)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!ConvertStringSidToSid(sid, &lowbox_sid_))
+ return SBOX_ERROR_INVALID_LOWBOX_SID;
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetProcessMitigations(MitigationFlags flags) {
+ // Prior to Win10 RS5 CreateProcess fails when AppContainer and mitigation
+ // flags are enabled. Return an error on downlevel platforms if trying to
+ // set new mitigations.
+ if (app_container_profile_ &&
+ base::win::GetVersion() < base::win::Version::WIN10_RS5) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ if (!CanSetProcessMitigationsPreStartup(flags))
+ return SBOX_ERROR_BAD_PARAMS;
+ mitigations_ = flags;
+ return SBOX_ALL_OK;
+}
+
+MitigationFlags PolicyBase::GetProcessMitigations() {
+ return mitigations_;
+}
+
+ResultCode PolicyBase::SetDelayedProcessMitigations(MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPostStartup(flags))
+ return SBOX_ERROR_BAD_PARAMS;
+ delayed_mitigations_ = flags;
+ return SBOX_ALL_OK;
+}
+
+MitigationFlags PolicyBase::GetDelayedProcessMitigations() const {
+ return delayed_mitigations_;
+}
+
+void PolicyBase::SetStrictInterceptions() {
+ relaxed_interceptions_ = false;
+}
+
+ResultCode PolicyBase::SetStdoutHandle(HANDLE handle) {
+ if (!IsInheritableHandle(handle))
+ return SBOX_ERROR_BAD_PARAMS;
+ stdout_handle_ = handle;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetStderrHandle(HANDLE handle) {
+ if (!IsInheritableHandle(handle))
+ return SBOX_ERROR_BAD_PARAMS;
+ stderr_handle_ = handle;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) {
+ ResultCode result = AddRuleInternal(subsystem, semantics, pattern);
+ LOG_IF(ERROR, result != SBOX_ALL_OK)
+ << "Failed to add sandbox rule."
+ << " error = " << result << ", subsystem = " << subsystem
+ << ", semantics = " << semantics << ", pattern = '" << pattern << "'";
+ return result;
+}
+
+ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
+ blocklisted_dlls_.push_back(dll_name);
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) {
+ return handle_closer_.AddHandle(handle_type, handle_name);
+}
+
+void PolicyBase::AddHandleToShare(HANDLE handle) {
+ CHECK(handle);
+ CHECK_NE(handle, INVALID_HANDLE_VALUE);
+
+ // Ensure the handle can be inherited.
+ bool result =
+ SetHandleInformation(handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+ PCHECK(result);
+
+ handles_to_share_.push_back(handle);
+}
+
+void PolicyBase::SetLockdownDefaultDacl() {
+ lockdown_default_dacl_ = true;
+}
+
+void PolicyBase::AddRestrictingRandomSid() {
+ add_restricting_random_sid_ = true;
+}
+
+const base::HandlesToInheritVector& PolicyBase::GetHandlesBeingShared() {
+ return handles_to_share_;
+}
+
+ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) {
+ if (job_level_ == JOB_NONE) {
+ job->Close();
+ return SBOX_ALL_OK;
+ }
+
+ // Create the windows job object.
+ Job job_obj;
+ DWORD result =
+ job_obj.Init(job_level_, nullptr, ui_exceptions_, memory_limit_);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_INIT_JOB;
+
+ *job = job_obj.Take();
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
+ base::win::ScopedHandle* lockdown,
+ base::win::ScopedHandle* lowbox) {
+ Sid random_sid = Sid::GenerateRandomSid();
+ PSID random_sid_ptr = nullptr;
+ if (add_restricting_random_sid_)
+ random_sid_ptr = random_sid.GetPSID();
+
+ // 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(
+ effective_token_, lockdown_level_, integrity_level_, PRIMARY,
+ lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, lockdown);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN;
+
+ // If we're launching on the alternate desktop we need to make sure the
+ // integrity label on the object is no higher than the sandboxed process's
+ // integrity level. So, we lower the label on the desktop process if it's
+ // not already low enough for our process.
+ if (use_alternate_desktop_ && integrity_level_ != INTEGRITY_LEVEL_LAST) {
+ // Integrity label enum is reversed (higher level is a lower value).
+ static_assert(INTEGRITY_LEVEL_SYSTEM < INTEGRITY_LEVEL_UNTRUSTED,
+ "Integrity level ordering reversed.");
+ HDESK desktop_handle = nullptr;
+ IntegrityLevel desktop_integrity_level_label;
+ if (use_alternate_winstation_) {
+ desktop_handle = alternate_desktop_handle_;
+ desktop_integrity_level_label = alternate_desktop_integrity_level_label_;
+ } else {
+ desktop_handle = alternate_desktop_local_winstation_handle_;
+ desktop_integrity_level_label =
+ alternate_desktop_local_winstation_integrity_level_label_;
+ }
+ // If the desktop_handle hasn't been created for any reason, skip this.
+ if (desktop_handle && desktop_integrity_level_label < integrity_level_) {
+ result =
+ SetObjectIntegrityLabel(desktop_handle, SE_WINDOW_OBJECT, L"",
+ GetIntegrityLevelString(integrity_level_));
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY;
+
+ if (use_alternate_winstation_) {
+ alternate_desktop_integrity_level_label_ = integrity_level_;
+ } else {
+ alternate_desktop_local_winstation_integrity_level_label_ =
+ integrity_level_;
+ }
+ }
+ }
+
+ if (lowbox_sid_) {
+ if (!lowbox_directory_.IsValid()) {
+ result =
+ CreateLowBoxObjectDirectory(lowbox_sid_, true, &lowbox_directory_);
+ DCHECK(result == ERROR_SUCCESS);
+ }
+
+ // 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;
+
+ Sid package_sid(lowbox_sid_);
+ SecurityCapabilities caps(package_sid);
+ if (CreateLowBoxToken(lockdown->Get(), PRIMARY, &caps, saved_handles,
+ saved_handles_count, lowbox) != ERROR_SUCCESS) {
+ return SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN;
+ }
+
+ if (!ReplacePackageSidInDacl(lowbox->Get(), SE_KERNEL_OBJECT, package_sid,
+ TOKEN_ALL_ACCESS)) {
+ return SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL;
+ }
+ }
+
+ // 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(
+ effective_token_, initial_level_, integrity_level_, IMPERSONATION,
+ lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, initial);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN;
+
+ return SBOX_ALL_OK;
+}
+
+PSID PolicyBase::GetLowBoxSid() const {
+ return lowbox_sid_;
+}
+
+ResultCode PolicyBase::AddTarget(TargetProcess* target) {
+ if (policy_)
+ policy_maker_->Done();
+
+ if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(),
+ mitigations_)) {
+ return SBOX_ERROR_APPLY_ASLR_MITIGATIONS;
+ }
+
+ ResultCode ret = SetupAllInterceptions(target);
+
+ if (ret != SBOX_ALL_OK)
+ return ret;
+
+ if (!SetupHandleCloser(target))
+ return SBOX_ERROR_SETUP_HANDLE_CLOSER;
+
+ DWORD win_error = ERROR_SUCCESS;
+ // Initialize the sandbox infrastructure for the target.
+ // TODO(wfh) do something with win_error code here.
+ ret = target->Init(dispatcher_.get(), policy_, kIPCMemSize, kPolMemSize,
+ &win_error);
+
+ if (ret != SBOX_ALL_OK)
+ return ret;
+
+ g_shared_delayed_integrity_level = delayed_integrity_level_;
+ ret = target->TransferVariable("g_shared_delayed_integrity_level",
+ &g_shared_delayed_integrity_level,
+ sizeof(g_shared_delayed_integrity_level));
+ g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST;
+ if (SBOX_ALL_OK != ret)
+ return ret;
+
+ // Add in delayed mitigations and pseudo-mitigations enforced at startup.
+ g_shared_delayed_mitigations =
+ delayed_mitigations_ | FilterPostStartupProcessMitigations(mitigations_);
+ if (!CanSetProcessMitigationsPostStartup(g_shared_delayed_mitigations))
+ return SBOX_ERROR_BAD_PARAMS;
+
+ ret = target->TransferVariable("g_shared_delayed_mitigations",
+ &g_shared_delayed_mitigations,
+ sizeof(g_shared_delayed_mitigations));
+ g_shared_delayed_mitigations = 0;
+ if (SBOX_ALL_OK != ret)
+ return ret;
+
+ AutoLock lock(&lock_);
+ targets_.push_back(target);
+ return SBOX_ALL_OK;
+}
+
+bool PolicyBase::OnJobEmpty(HANDLE job) {
+ AutoLock lock(&lock_);
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ if ((*it)->Job() == job)
+ break;
+ }
+ if (it == targets_.end()) {
+ return false;
+ }
+ TargetProcess* target = *it;
+ targets_.erase(it);
+ delete target;
+ return true;
+}
+
+ResultCode PolicyBase::SetDisconnectCsrss() {
+// Does not work on 32-bit, and the ASAN runtime falls over with the
+// CreateThread EAT patch used when this is enabled.
+// See https://crbug.com/783296#c27.
+#if defined(_WIN64) && !defined(ADDRESS_SANITIZER)
+ if (base::win::GetVersion() >= base::win::Version::WIN10) {
+ is_csrss_connected_ = false;
+ return AddKernelObjectToClose(L"ALPC Port", nullptr);
+ }
+#endif // !defined(_WIN64)
+ return SBOX_ALL_OK;
+}
+
+EvalResult PolicyBase::EvalPolicy(IpcTag service,
+ CountedParameterSetBase* params) {
+ if (policy_) {
+ if (!policy_->entry[static_cast<size_t>(service)]) {
+ // There is no policy for this particular service. This is not a big
+ // deal.
+ return DENY_ACCESS;
+ }
+ for (size_t i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED();
+ return SIGNAL_ALARM;
+ }
+ }
+ PolicyProcessor pol_evaluator(policy_->entry[static_cast<size_t>(service)]);
+ PolicyResult result =
+ pol_evaluator.Evaluate(kShortEval, params->parameters, params->count);
+ if (POLICY_MATCH == result)
+ return pol_evaluator.GetAction();
+
+ DCHECK(POLICY_ERROR != result);
+ }
+
+ return DENY_ACCESS;
+}
+
+HANDLE PolicyBase::GetStdoutHandle() {
+ return stdout_handle_;
+}
+
+HANDLE PolicyBase::GetStderrHandle() {
+ return stderr_handle_;
+}
+
+void PolicyBase::SetEnableOPMRedirection() {
+ enable_opm_redirection_ = true;
+}
+
+bool PolicyBase::GetEnableOPMRedirection() {
+ return enable_opm_redirection_;
+}
+
+ResultCode PolicyBase::AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ DCHECK(package_name);
+ if (lowbox_sid_ || app_container_profile_ ||
+ integrity_level_ != INTEGRITY_LEVEL_LAST) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ if (create_profile) {
+ app_container_profile_ = AppContainerProfileBase::Create(
+ package_name, L"Chrome Sandbox", L"Profile for Chrome Sandbox");
+ } else {
+ app_container_profile_ = AppContainerProfileBase::Open(package_name);
+ }
+ if (!app_container_profile_)
+ return SBOX_ERROR_CREATE_APPCONTAINER_PROFILE;
+
+ // A bug exists in CreateProcess where enabling an AppContainer profile and
+ // passing a set of mitigation flags will generate ERROR_INVALID_PARAMETER.
+ // Apply best efforts here and convert set mitigations to delayed mitigations.
+ // This bug looks to have been fixed in Win10 RS5, so exit early if possible.
+ if (base::win::GetVersion() >= base::win::Version::WIN10_RS5)
+ return SBOX_ALL_OK;
+
+ delayed_mitigations_ =
+ mitigations_ & GetAllowedPostStartupProcessMitigations();
+ DCHECK(delayed_mitigations_ ==
+ (mitigations_ & ~(MITIGATION_SEHOP |
+ MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION)));
+ mitigations_ = 0;
+ return SBOX_ALL_OK;
+}
+
+scoped_refptr<AppContainerProfile> PolicyBase::GetAppContainerProfile() {
+ return GetAppContainerProfileBase();
+}
+
+void PolicyBase::SetEffectiveToken(HANDLE token) {
+ CHECK(token);
+ effective_token_ = token;
+}
+
+scoped_refptr<AppContainerProfileBase>
+PolicyBase::GetAppContainerProfileBase() {
+ return app_container_profile_;
+}
+
+ResultCode PolicyBase::SetupAllInterceptions(TargetProcess* target) {
+ InterceptionManager manager(target, relaxed_interceptions_);
+
+ if (policy_) {
+ for (size_t i = 0; i < kMaxIpcTag; i++) {
+ if (policy_->entry[i] &&
+ !dispatcher_->SetupService(&manager, static_cast<IpcTag>(i)))
+ return SBOX_ERROR_SETUP_INTERCEPTION_SERVICE;
+ }
+ }
+
+ for (const std::wstring& dll : blocklisted_dlls_)
+ manager.AddToUnloadModules(dll.c_str());
+
+ if (!SetupBasicInterceptions(&manager, is_csrss_connected_))
+ return SBOX_ERROR_SETUP_BASIC_INTERCEPTIONS;
+
+ ResultCode rc = manager.InitializeInterceptions();
+ if (rc != SBOX_ALL_OK)
+ return rc;
+
+ // Finally, setup imports on the target so the interceptions can work.
+ if (!SetupNtdllImports(target))
+ return SBOX_ERROR_SETUP_NTDLL_IMPORTS;
+
+ return SBOX_ALL_OK;
+}
+
+bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
+ return handle_closer_.InitializeTargetHandles(target);
+}
+
+ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) {
+ if (!policy_) {
+ policy_ = MakeBrokerPolicyMemory();
+ DCHECK(policy_);
+ policy_maker_ = new LowLevelPolicy(policy_);
+ DCHECK(policy_maker_);
+ }
+
+ switch (subsystem) {
+ case SUBSYS_FILES: {
+ if (!file_system_init_) {
+ if (!FileSystemPolicy::SetInitialRules(policy_maker_))
+ return SBOX_ERROR_BAD_PARAMS;
+ file_system_init_ = true;
+ }
+ if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_SYNC: {
+ if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_PROCESS: {
+ if (lockdown_level_ < USER_INTERACTIVE &&
+ TargetPolicy::PROCESS_ALL_EXEC == semantics) {
+ // This is unsupported. This is a huge security risk to give full access
+ // to a process handle.
+ return SBOX_ERROR_UNSUPPORTED;
+ }
+ if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_NAMED_PIPES: {
+ if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_REGISTRY: {
+ if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_HANDLES: {
+ if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+
+ case SUBSYS_WIN32K_LOCKDOWN: {
+ // Win32k intercept rules only supported on Windows 8 and above. This must
+ // match the version checks in process_mitigations.cc for consistency.
+ if (base::win::GetVersion() >= base::win::Version::WIN8) {
+ DCHECK_EQ(MITIGATION_WIN32K_DISABLE,
+ mitigations_ & MITIGATION_WIN32K_DISABLE)
+ << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
+ "rules.";
+ if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ }
+ break;
+ }
+ case SUBSYS_SIGNED_BINARY: {
+ // Signed intercept rules only supported on Windows 10 TH2 and above. This
+ // must match the version checks in process_mitigations.cc for
+ // consistency.
+ if (base::win::GetVersion() >= base::win::Version::WIN10_TH2) {
+ DCHECK_EQ(MITIGATION_FORCE_MS_SIGNED_BINS,
+ (mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS) | (delayed_mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS))
+ << "Enable MITIGATION_FORCE_MS_SIGNED_BINS before adding signed "
+ "policy rules.";
+ if (!SignedPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ }
+ break;
+ }
+ case SUBSYS_LINE_BREAK: {
+ if (!LineBreakPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+
+ default: { return SBOX_ERROR_UNSUPPORTED; }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
new file mode 100644
index 0000000000..18268e230d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/process/launch.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile_base.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+class LowLevelPolicy;
+class PolicyDiagnostic;
+class TargetProcess;
+struct PolicyGlobal;
+
+class PolicyBase final : public TargetPolicy {
+ public:
+ PolicyBase();
+
+ // TargetPolicy:
+ void AddRef() override;
+ void Release() override;
+ ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) override;
+ TokenLevel GetInitialTokenLevel() const override;
+ TokenLevel GetLockdownTokenLevel() const override;
+ void SetDoNotUseRestrictingSIDs() final;
+ ResultCode SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) override;
+ JobLevel GetJobLevel() const override;
+ ResultCode SetJobMemoryLimit(size_t memory_limit) override;
+ ResultCode SetAlternateDesktop(bool alternate_winstation) override;
+ std::wstring 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 SetLowBox(const wchar_t* sid) override;
+ ResultCode SetProcessMitigations(MitigationFlags flags) override;
+ MitigationFlags GetProcessMitigations() override;
+ ResultCode SetDelayedProcessMitigations(MitigationFlags flags) override;
+ MitigationFlags GetDelayedProcessMitigations() const override;
+ ResultCode SetDisconnectCsrss() 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 wchar_t* handle_type,
+ const wchar_t* handle_name) override;
+ void AddHandleToShare(HANDLE handle) override;
+ void SetLockdownDefaultDacl() override;
+ void AddRestrictingRandomSid() override;
+ void SetEnableOPMRedirection() override;
+ bool GetEnableOPMRedirection() override;
+ ResultCode AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) override;
+ scoped_refptr<AppContainerProfile> GetAppContainerProfile() override;
+ void SetEffectiveToken(HANDLE token) override;
+
+ // Get the AppContainer profile as its internal type.
+ scoped_refptr<AppContainerProfileBase> GetAppContainerProfileBase();
+
+ // Creates a Job object with the level specified in a previous call to
+ // SetJobLevel().
+ ResultCode MakeJobObject(base::win::ScopedHandle* job);
+
+ // Creates the two tokens with the levels specified in a previous call to
+ // 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);
+
+ PSID GetLowBoxSid() const;
+
+ // Adds a target process to the internal list of targets. Internally a
+ // call to TargetProcess::Init() is issued.
+ ResultCode AddTarget(TargetProcess* target);
+
+ // Called when there are no more active processes in a Job.
+ // Removes a Job object associated with this policy and the target associated
+ // with the job.
+ bool OnJobEmpty(HANDLE job);
+
+ EvalResult EvalPolicy(IpcTag service, CountedParameterSetBase* params);
+
+ HANDLE GetStdoutHandle();
+ HANDLE GetStderrHandle();
+
+ // Returns the list of handles being shared with the target process.
+ const base::HandlesToInheritVector& GetHandlesBeingShared();
+
+ private:
+ // Allow PolicyInfo to snapshot PolicyBase for diagnostics.
+ friend class PolicyDiagnostic;
+ ~PolicyBase();
+
+ // Sets up interceptions for a new target.
+ ResultCode SetupAllInterceptions(TargetProcess* target);
+
+ // Sets up the handle closer for a new target.
+ bool SetupHandleCloser(TargetProcess* target);
+
+ ResultCode AddRuleInternal(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern);
+
+ // This lock synchronizes operations on the targets_ collection.
+ CRITICAL_SECTION lock_;
+ // Maintains the list of target process associated with this policy.
+ // The policy takes ownership of them.
+ typedef std::list<TargetProcess*> TargetSet;
+ TargetSet targets_;
+ // Standard object-lifetime reference counter.
+ volatile LONG ref_count;
+ // The user-defined global policy settings.
+ TokenLevel lockdown_level_;
+ TokenLevel initial_level_;
+ bool use_restricting_sids_ = true;
+ JobLevel job_level_;
+ uint32_t ui_exceptions_;
+ size_t memory_limit_;
+ bool use_alternate_desktop_;
+ bool use_alternate_winstation_;
+ // Helps the file system policy initialization.
+ bool file_system_init_;
+ bool relaxed_interceptions_;
+ HANDLE stdout_handle_;
+ HANDLE stderr_handle_;
+ IntegrityLevel integrity_level_;
+ IntegrityLevel delayed_integrity_level_;
+ MitigationFlags mitigations_;
+ MitigationFlags delayed_mitigations_;
+ bool is_csrss_connected_;
+ // Object in charge of generating the low level policy.
+ LowLevelPolicy* policy_maker_;
+ // Memory structure that stores the low level policy.
+ PolicyGlobal* policy_;
+ // The list of dlls to unload in the target process.
+ std::vector<std::wstring> blocklisted_dlls_;
+ // This is a map of handle-types to names that we need to close in the
+ // target process. A null set means we need to close all handles of the
+ // given type.
+ HandleCloser handle_closer_;
+ PSID lowbox_sid_;
+ base::win::ScopedHandle lowbox_directory_;
+ std::unique_ptr<Dispatcher> dispatcher_;
+ bool lockdown_default_dacl_;
+ bool add_restricting_random_sid_;
+
+ static HDESK alternate_desktop_handle_;
+ static HWINSTA alternate_winstation_handle_;
+ static HDESK alternate_desktop_local_winstation_handle_;
+ static IntegrityLevel alternate_desktop_integrity_level_label_;
+ static IntegrityLevel
+ alternate_desktop_local_winstation_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.
+ base::HandlesToInheritVector handles_to_share_;
+ bool enable_opm_redirection_;
+
+ scoped_refptr<AppContainerProfileBase> app_container_profile_;
+
+ HANDLE effective_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
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 0000000000..9bc8634ec6
--- /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 <windows.h>
+
+// #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 <ntsecapi.h>
+#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 0000000000..f4662f9a84
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h
@@ -0,0 +1,15 @@
+// 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
new file mode 100644
index 0000000000..782541863d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h
@@ -0,0 +1,199 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+#define SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+
+namespace sandbox {
+
+#ifdef __MINGW32__
+// Map Microsoft's proprietary more-safe version of copy() back to
+// the std::basic_string method
+#define _Copy_s copy
+#endif
+
+// Operation result codes returned by the sandbox API.
+//
+// Note: These codes are listed in a histogram and any new codes should be added
+// at the end. If the underlying type is changed then the forward declaration in
+// sandbox_init.h must be updated.
+//
+enum ResultCode : int {
+ SBOX_ALL_OK = 0,
+ // Error is originating on the win32 layer. Call GetlastError() for more
+ // information.
+ SBOX_ERROR_GENERIC = 1,
+ // An invalid combination of parameters was given to the API.
+ SBOX_ERROR_BAD_PARAMS = 2,
+ // The desired operation is not supported at this time.
+ SBOX_ERROR_UNSUPPORTED = 3,
+ // The request requires more memory that allocated or available.
+ SBOX_ERROR_NO_SPACE = 4,
+ // The ipc service requested does not exist.
+ SBOX_ERROR_INVALID_IPC = 5,
+ // The ipc service did not complete.
+ SBOX_ERROR_FAILED_IPC = 6,
+ // The requested handle was not found.
+ SBOX_ERROR_NO_HANDLE = 7,
+ // This function was not expected to be called at this time.
+ SBOX_ERROR_UNEXPECTED_CALL = 8,
+ // WaitForAllTargets is already called.
+ SBOX_ERROR_WAIT_ALREADY_CALLED = 9,
+ // A channel error prevented DoCall from executing.
+ SBOX_ERROR_CHANNEL_ERROR = 10,
+ // Failed to create the alternate desktop.
+ SBOX_ERROR_CANNOT_CREATE_DESKTOP = 11,
+ // Failed to create the alternate window station.
+ SBOX_ERROR_CANNOT_CREATE_WINSTATION = 12,
+ // Failed to switch back to the interactive window station.
+ SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION = 13,
+ // The supplied AppContainer is not valid.
+ SBOX_ERROR_INVALID_APP_CONTAINER = 14,
+ // The supplied capability is not valid.
+ SBOX_ERROR_INVALID_CAPABILITY = 15,
+ // There is a failure initializing the AppContainer.
+ SBOX_ERROR_CANNOT_INIT_APPCONTAINER = 16,
+ // Initializing or updating ProcThreadAttributes failed.
+ SBOX_ERROR_PROC_THREAD_ATTRIBUTES = 17,
+ // Error in creating process.
+ SBOX_ERROR_CREATE_PROCESS = 18,
+ // Failure calling delegate PreSpawnTarget.
+ SBOX_ERROR_DELEGATE_PRE_SPAWN = 19,
+ // Could not assign process to job object.
+ SBOX_ERROR_ASSIGN_PROCESS_TO_JOB_OBJECT = 20,
+ // Could not assign process to job object.
+ SBOX_ERROR_SET_THREAD_TOKEN = 21,
+ // Could not get thread context of new process.
+ SBOX_ERROR_GET_THREAD_CONTEXT = 22,
+ // Could not duplicate target info of new process.
+ SBOX_ERROR_DUPLICATE_TARGET_INFO = 23,
+ // Could not set low box token.
+ SBOX_ERROR_SET_LOW_BOX_TOKEN = 24,
+ // Could not create file mapping for IPC dispatcher.
+ SBOX_ERROR_CREATE_FILE_MAPPING = 25,
+ // Could not duplicate shared section into target process for IPC dispatcher.
+ SBOX_ERROR_DUPLICATE_SHARED_SECTION = 26,
+ // Could not map view of shared memory in broker.
+ SBOX_ERROR_MAP_VIEW_OF_SHARED_SECTION = 27,
+ // Could not apply ASLR mitigations to target process.
+ SBOX_ERROR_APPLY_ASLR_MITIGATIONS = 28,
+ // Could not setup one of the required interception services.
+ SBOX_ERROR_SETUP_BASIC_INTERCEPTIONS = 29,
+ // Could not setup basic interceptions.
+ SBOX_ERROR_SETUP_INTERCEPTION_SERVICE = 30,
+ // Could not initialize interceptions. This usually means 3rd party software
+ // is stomping on our hooks, or can sometimes mean the syscall format has
+ // changed.
+ SBOX_ERROR_INITIALIZE_INTERCEPTIONS = 31,
+ // Could not setup the imports for ntdll in target process.
+ SBOX_ERROR_SETUP_NTDLL_IMPORTS = 32,
+ // Could not setup the handle closer in target process.
+ SBOX_ERROR_SETUP_HANDLE_CLOSER = 33,
+ // Cannot get the current Window Station.
+ SBOX_ERROR_CANNOT_GET_WINSTATION = 34,
+ // Cannot query the security attributes of the current Window Station.
+ SBOX_ERROR_CANNOT_QUERY_WINSTATION_SECURITY = 35,
+ // Cannot get the current Desktop.
+ SBOX_ERROR_CANNOT_GET_DESKTOP = 36,
+ // Cannot query the security attributes of the current Desktop.
+ SBOX_ERROR_CANNOT_QUERY_DESKTOP_SECURITY = 37,
+ // Cannot setup the interception manager config buffer.
+ SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_CONFIG_BUFFER = 38,
+ // Cannot copy data to the child process.
+ SBOX_ERROR_CANNOT_COPY_DATA_TO_CHILD = 39,
+ // Cannot setup the interception thunk.
+ SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_THUNK = 40,
+ // Cannot resolve the interception thunk.
+ SBOX_ERROR_CANNOT_RESOLVE_INTERCEPTION_THUNK = 41,
+ // Cannot write interception thunk to child process.
+ SBOX_ERROR_CANNOT_WRITE_INTERCEPTION_THUNK = 42,
+ // Cannot find the base address of the new process.
+ SBOX_ERROR_CANNOT_FIND_BASE_ADDRESS = 43,
+ // Cannot create the AppContainer profile.
+ SBOX_ERROR_CREATE_APPCONTAINER_PROFILE = 44,
+ // Cannot create the AppContainer as the main executable can't be accessed.
+ SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_ACCESS_CHECK = 45,
+ // Cannot create the AppContainer as adding a capability failed.
+ SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY = 46,
+ // Cannot initialize a job object.
+ SBOX_ERROR_CANNOT_INIT_JOB = 47,
+ // Invalid LowBox SID string.
+ SBOX_ERROR_INVALID_LOWBOX_SID = 48,
+ // Cannot create restricted token.
+ SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN = 49,
+ // Cannot set the integrity level on a desktop object.
+ SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY = 50,
+ // Cannot create a LowBox token.
+ SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN = 51,
+ // Cannot modify LowBox token's DACL.
+ SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL = 52,
+ // Cannot create restricted impersonation token.
+ SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN = 53,
+ // Cannot duplicate target process handle.
+ SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE = 54,
+ // Cannot load executable for variable transfer.
+ SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE = 55,
+ // Cannot find variable address for transfer.
+ SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS = 56,
+ // Cannot write variable value.
+ SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE = 57,
+ // Short write to variable.
+ SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE = 58,
+ // Cannot initialize BrokerServices.
+ SBOX_ERROR_CANNOT_INIT_BROKERSERVICES = 59,
+ // Placeholder for last item of the enum.
+ SBOX_ERROR_LAST
+};
+
+// If the sandbox cannot create a secure environment for the target, the
+// target will be forcibly terminated. These are the process exit codes.
+enum TerminationCodes {
+ SBOX_FATAL_INTEGRITY = 7006, // Could not set the integrity level.
+ SBOX_FATAL_DROPTOKEN = 7007, // Could not lower the token.
+ SBOX_FATAL_FLUSHANDLES = 7008, // Failed to flush registry handles.
+ SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching.
+ SBOX_FATAL_CLOSEHANDLES = 7010, // Failed to close pending handles.
+ SBOX_FATAL_MITIGATION = 7011, // Could not set the mitigation policy.
+ SBOX_FATAL_MEMORY_EXCEEDED = 7012, // Exceeded the job memory limit.
+ SBOX_FATAL_WARMUP = 7013, // Failed to warmup.
+ SBOX_FATAL_LAST
+};
+
+#if !defined(SANDBOX_FUZZ_TARGET)
+static_assert(SBOX_FATAL_MEMORY_EXCEEDED ==
+ base::win::kSandboxFatalMemoryExceeded,
+ "Value for SBOX_FATAL_MEMORY_EXCEEDED must match base.");
+#endif // !defined(SANDBOX_FUZZ_TARGET)
+
+class BrokerServices;
+class TargetServices;
+
+// Contains the pointer to a target or broker service.
+struct SandboxInterfaceInfo {
+ BrokerServices* broker_services;
+ TargetServices* target_services;
+};
+
+#if SANDBOX_EXPORTS
+#define SANDBOX_INTERCEPT extern "C" __declspec(dllexport)
+#else
+#define SANDBOX_INTERCEPT extern "C"
+#endif
+
+enum InterceptionType {
+ INTERCEPTION_INVALID = 0,
+ INTERCEPTION_SERVICE_CALL, // Trampoline of an NT native call
+ INTERCEPTION_EAT,
+ INTERCEPTION_SIDESTEP, // Preamble patch
+ INTERCEPTION_SMART_SIDESTEP, // Preamble patch but bypass internal calls
+ INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch)
+ INTERCEPTION_LAST // Placeholder for last item in the enumeration
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc
new file mode 100644
index 0000000000..f34daa7d3e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_utils.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/internal_types.h"
+
+namespace sandbox {
+
+void InitObjectAttribs(const std::wstring& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name,
+ SECURITY_QUALITY_OF_SERVICE* security_qos) {
+ static RtlInitUnicodeStringFunction RtlInitUnicodeString;
+ if (!RtlInitUnicodeString) {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
+ GetProcAddress(ntdll, "RtlInitUnicodeString"));
+ DCHECK(RtlInitUnicodeString);
+ }
+ RtlInitUnicodeString(uni_name, name.c_str());
+ InitializeObjectAttributes(obj_attr, uni_name, attributes, root, nullptr);
+ 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
new file mode 100644
index 0000000000..580d1298f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SANDBOX_UTILS_H_
+#define SANDBOX_SRC_SANDBOX_UTILS_H_
+
+#include <windows.h>
+#include <string>
+
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+void InitObjectAttribs(const std::wstring& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name,
+ SECURITY_QUALITY_OF_SERVICE* security_qos);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_UTILS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/security_capabilities.cc b/security/sandbox/chromium/sandbox/win/src/security_capabilities.cc
new file mode 100644
index 0000000000..0df446b5be
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/security_capabilities.cc
@@ -0,0 +1,33 @@
+// Copyright 2017 The Chromium Authors. All 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/security_capabilities.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace sandbox {
+
+SecurityCapabilities::SecurityCapabilities(const Sid& package_sid,
+ const std::vector<Sid>& capabilities)
+ : SECURITY_CAPABILITIES(),
+ capabilities_(capabilities),
+ package_sid_(package_sid) {
+ AppContainerSid = package_sid_.GetPSID();
+ if (capabilities_.empty())
+ return;
+
+ capability_sids_.resize(capabilities_.size());
+ for (size_t index = 0; index < capabilities_.size(); ++index) {
+ capability_sids_[index].Sid = capabilities_[index].GetPSID();
+ capability_sids_[index].Attributes = SE_GROUP_ENABLED;
+ }
+ CapabilityCount = base::checked_cast<DWORD>(capability_sids_.size());
+ Capabilities = capability_sids_.data();
+}
+
+SecurityCapabilities::SecurityCapabilities(const Sid& package_sid)
+ : SecurityCapabilities(package_sid, std::vector<Sid>()) {}
+
+SecurityCapabilities::~SecurityCapabilities() {}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/security_capabilities.h b/security/sandbox/chromium/sandbox/win/src/security_capabilities.h
new file mode 100644
index 0000000000..7a66e59743
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/security_capabilities.h
@@ -0,0 +1,34 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SECURITY_CAPABILITIES_H_
+#define SANDBOX_SRC_SECURITY_CAPABILITIES_H_
+
+#include <windows.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+class SecurityCapabilities final : public SECURITY_CAPABILITIES {
+ public:
+ explicit SecurityCapabilities(const Sid& package_sid);
+ SecurityCapabilities(const Sid& package_sid,
+ const std::vector<Sid>& capabilities);
+ ~SecurityCapabilities();
+
+ private:
+ std::vector<Sid> capabilities_;
+ std::vector<SID_AND_ATTRIBUTES> capability_sids_;
+ Sid package_sid_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecurityCapabilities);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SECURITY_CAPABILITIES_H_ \ No newline at end of file
diff --git a/security/sandbox/chromium/sandbox/win/src/security_level.h b/security/sandbox/chromium/sandbox/win/src/security_level.h
new file mode 100644
index 0000000000..8e81cf21c9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/security_level.h
@@ -0,0 +1,300 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SECURITY_LEVEL_H_
+#define SANDBOX_SRC_SECURITY_LEVEL_H_
+
+#include <stdint.h>
+
+namespace sandbox {
+
+// List of all the integrity levels supported in the sandbox.
+// The integrity level of the sandboxed process can't be set to a level higher
+// than the broker process.
+//
+// Note: These levels map to SIDs under the hood.
+// INTEGRITY_LEVEL_SYSTEM: "S-1-16-16384" System Mandatory Level
+// INTEGRITY_LEVEL_HIGH: "S-1-16-12288" High Mandatory Level
+// INTEGRITY_LEVEL_MEDIUM: "S-1-16-8192" Medium Mandatory Level
+// INTEGRITY_LEVEL_MEDIUM_LOW: "S-1-16-6144"
+// INTEGRITY_LEVEL_LOW: "S-1-16-4096" Low Mandatory Level
+// INTEGRITY_LEVEL_BELOW_LOW: "S-1-16-2048"
+// INTEGRITY_LEVEL_UNTRUSTED: "S-1-16-0" Untrusted Mandatory Level
+//
+// Not defined: "S-1-16-20480" Protected Process Mandatory Level
+// Not defined: "S-1-16-28672" Secure Process Mandatory Level
+enum IntegrityLevel {
+ INTEGRITY_LEVEL_SYSTEM,
+ INTEGRITY_LEVEL_HIGH,
+ INTEGRITY_LEVEL_MEDIUM,
+ INTEGRITY_LEVEL_MEDIUM_LOW,
+ INTEGRITY_LEVEL_LOW,
+ INTEGRITY_LEVEL_BELOW_LOW,
+ INTEGRITY_LEVEL_UNTRUSTED,
+ INTEGRITY_LEVEL_LAST
+};
+
+// The Token level specifies a set of security profiles designed to
+// provide the bulk of the security of sandbox.
+//
+// TokenLevel |Restricting |Deny Only |Privileges|
+// |Sids |Sids | |
+// ----------------------------|--------------|----------------|----------|
+// USER_LOCKDOWN | Null Sid | All | None |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED | RESTRICTED | All | Traverse |
+// ----------------------------|--------------|----------------|----------|
+// USER_LIMITED | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | RESTRICTED | Everyone | |
+// | | Interactive | |
+// ----------------------------|--------------|----------------|----------|
+// USER_INTERACTIVE | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | RESTRICTED | Everyone | |
+// | Owner | Interactive | |
+// | | Local | |
+// | | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED_NON_ADMIN | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | Interactive | Everyone | |
+// | Local | Interactive | |
+// | Authent-users| Local | |
+// | User | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_NON_ADMIN | None | All except: | Traverse |
+// | | Users | |
+// | | Everyone | |
+// | | Interactive | |
+// | | Local | |
+// | | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED_SAME_ACCESS | All | None | All |
+// ----------------------------|--------------|----------------|----------|
+// USER_UNPROTECTED | None | None | All |
+// ----------------------------|--------------|----------------|----------|
+//
+// The above restrictions are actually a transformation that is applied to
+// the existing broker process token. The resulting token that will be
+// applied to the target process depends both on the token level selected
+// and on the broker token itself.
+//
+// The LOCKDOWN and RESTRICTED are designed to allow access to almost
+// nothing that has security associated with and they are the recommended
+// levels to run sandboxed code specially if there is a chance that the
+// broker is process might be started by a user that belongs to the Admins
+// or power users groups.
+enum TokenLevel {
+ USER_LOCKDOWN = 0,
+ USER_RESTRICTED,
+ USER_LIMITED,
+ USER_INTERACTIVE,
+ USER_RESTRICTED_NON_ADMIN,
+ USER_NON_ADMIN,
+ USER_RESTRICTED_SAME_ACCESS,
+ USER_UNPROTECTED,
+ USER_LAST
+};
+
+// The Job level specifies a set of decreasing security profiles for the
+// Job object that the target process will be placed into.
+// This table summarizes the security associated with each level:
+//
+// JobLevel |General |Quota |
+// |restrictions |restrictions |
+// -----------------|---------------------------------- |--------------------|
+// JOB_NONE | No job is assigned to the | None |
+// | sandboxed process. | |
+// -----------------|---------------------------------- |--------------------|
+// JOB_UNPROTECTED | None | *Kill on Job close.|
+// -----------------|---------------------------------- |--------------------|
+// JOB_INTERACTIVE | *Forbid system-wide changes using | |
+// | SystemParametersInfo(). | *Kill on Job close.|
+// | *Forbid the creation/switch of | |
+// | Desktops. | |
+// | *Forbids calls to ExitWindows(). | |
+// -----------------|---------------------------------- |--------------------|
+// JOB_LIMITED_USER | Same as INTERACTIVE_USER plus: | *One active process|
+// | *Forbid changes to the display | limit. |
+// | settings. | *Kill on Job close.|
+// -----------------|---------------------------------- |--------------------|
+// JOB_RESTRICTED | Same as LIMITED_USER plus: | *One active process|
+// | * No read/write to the clipboard. | limit. |
+// | * No access to User Handles that | *Kill on Job close.|
+// | belong to other processes. | |
+// | * Forbid message broadcasts. | |
+// | * Forbid setting global hooks. | |
+// | * No access to the global atoms | |
+// | table. | |
+// -----------------|-----------------------------------|--------------------|
+// JOB_LOCKDOWN | Same as RESTRICTED | *One active process|
+// | | limit. |
+// | | *Kill on Job close.|
+// | | *Kill on unhandled |
+// | | exception. |
+// | | |
+// In the context of the above table, 'user handles' refers to the handles of
+// windows, bitmaps, menus, etc. Files, treads and registry handles are kernel
+// handles and are not affected by the job level settings.
+enum JobLevel {
+ JOB_LOCKDOWN = 0,
+ JOB_RESTRICTED,
+ JOB_LIMITED_USER,
+ JOB_INTERACTIVE,
+ JOB_UNPROTECTED,
+ JOB_NONE
+};
+
+// These flags correspond to various process-level mitigations (eg. ASLR and
+// DEP). Most are implemented via UpdateProcThreadAttribute() plus flags for
+// the PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY attribute argument; documented
+// here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686880
+// Some mitigations are implemented directly by the sandbox or emulated to
+// the greatest extent possible when not directly supported by the OS.
+// Flags that are unsupported for the target OS will be silently ignored.
+// Flags that are invalid for their application (pre or post startup) will
+// return SBOX_ERROR_BAD_PARAMS.
+typedef uint64_t MitigationFlags;
+
+// Permanently enables DEP for the target process. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE.
+const MitigationFlags MITIGATION_DEP = 0x00000001;
+
+// Permanently Disables ATL thunk emulation when DEP is enabled. Valid
+// only when MITIGATION_DEP is passed. Corresponds to not passing
+// PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE.
+const MitigationFlags MITIGATION_DEP_NO_ATL_THUNK = 0x00000002;
+
+// Enables Structured exception handling override prevention. Must be
+// enabled prior to process start. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE.
+const MitigationFlags MITIGATION_SEHOP = 0x00000004;
+
+// Forces ASLR on all images in the child process. In debug builds, must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON .
+const MitigationFlags MITIGATION_RELOCATE_IMAGE = 0x00000008;
+
+// Refuses to load DLLs that cannot support ASLR. In debug builds, must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS.
+const MitigationFlags MITIGATION_RELOCATE_IMAGE_REQUIRED = 0x00000010;
+
+// Terminates the process on Windows heap corruption. Coresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON.
+const MitigationFlags MITIGATION_HEAP_TERMINATE = 0x00000020;
+
+// Sets a random lower bound as the minimum user address. Must be
+// enabled prior to process start. On 32-bit processes this is
+// emulated to a much smaller degree. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON.
+const MitigationFlags MITIGATION_BOTTOM_UP_ASLR = 0x00000040;
+
+// Increases the randomness range of bottom-up ASLR to up to 1TB. Must be
+// enabled prior to process start and with MITIGATION_BOTTOM_UP_ASLR.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON
+const MitigationFlags MITIGATION_HIGH_ENTROPY_ASLR = 0x00000080;
+
+// Immediately raises an exception on a bad handle reference. Must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON.
+const MitigationFlags MITIGATION_STRICT_HANDLE_CHECKS = 0x00000100;
+
+// Sets the DLL search order to LOAD_LIBRARY_SEARCH_DEFAULT_DIRS. Additional
+// directories can be added via the Windows AddDllDirectory() function.
+// http://msdn.microsoft.com/en-us/library/windows/desktop/hh310515
+// Must be enabled after startup.
+const MitigationFlags MITIGATION_DLL_SEARCH_ORDER = 0x00000200;
+
+// 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 = 0x00000400;
+
+// Prevents the process from making Win32k calls. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON.
+//
+// Applications linked to user32.dll or gdi32.dll make Win32k calls during
+// setup, even if Win32k is not otherwise used. So they also need to add a rule
+// with SUBSYS_WIN32K_LOCKDOWN and semantics FAKE_USER_GDI_INIT to allow the
+// initialization to succeed.
+const MitigationFlags MITIGATION_WIN32K_DISABLE = 0x00000800;
+
+// Prevents certain built-in third party extension points from being used.
+// - App_Init DLLs
+// - Winsock Layered Service Providers (LSPs)
+// - Global Windows Hooks (NOT thread-targeted hooks)
+// - Legacy Input Method Editors (IMEs)
+// I.e.: Disable legacy hooking mechanisms. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON.
+const MitigationFlags MITIGATION_EXTENSION_POINT_DISABLE = 0x00001000;
+
+// Prevents the process from generating dynamic code or modifying executable
+// code. Second option to allow thread-specific opt-out.
+// - VirtualAlloc with PAGE_EXECUTE_*
+// - VirtualProtect with PAGE_EXECUTE_*
+// - MapViewOfFile with FILE_MAP_EXECUTE | FILE_MAP_WRITE
+// - SetProcessValidCallTargets for CFG
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON and
+// PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON_ALLOW_OPT_OUT.
+const MitigationFlags MITIGATION_DYNAMIC_CODE_DISABLE = 0x00002000;
+const MitigationFlags MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT = 0x00004000;
+// The following per-thread flag can be used with the
+// ApplyMitigationsToCurrentThread API. Requires the above process mitigation
+// to be set on the current process.
+const MitigationFlags MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD = 0x00008000;
+
+// Prevents the process from loading non-system fonts into GDI.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON
+const MitigationFlags MITIGATION_NONSYSTEM_FONT_DISABLE = 0x00010000;
+
+// Prevents the process from loading binaries NOT signed by MS.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
+const MitigationFlags MITIGATION_FORCE_MS_SIGNED_BINS = 0x00020000;
+
+// Blocks mapping of images from remote devices. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON.
+const MitigationFlags MITIGATION_IMAGE_LOAD_NO_REMOTE = 0x00040000;
+
+// Blocks mapping of images that have the low manditory label. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON.
+const MitigationFlags MITIGATION_IMAGE_LOAD_NO_LOW_LABEL = 0x00080000;
+
+// Forces image load preference to prioritize the Windows install System32
+// folder before dll load dir, application dir and any user dirs set.
+// - Affects IAT resolution standard search path only, NOT direct LoadLibrary or
+// executable search path.
+// PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON.
+const MitigationFlags MITIGATION_IMAGE_LOAD_PREFER_SYS32 = 0x00100000;
+
+// Prevents hyperthreads from interfering with indirect branch predictions.
+// (SPECTRE Variant 2 mitigation.) Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON.
+const MitigationFlags MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION =
+ 0x00200000;
+
+// Begin Mozilla-added flags.
+// Working down from the high bit to avoid conflict with new upstream flags.
+
+// Disable Control Flow Guard. This may seem more like an anti-mitigation, but
+// this flag allows code to make targeted changes to CFG to avoid bugs, while
+// leaving it enabled in the common case. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON.
+const MitigationFlags MITIGATION_CONTROL_FLOW_GUARD_DISABLE = 0x80000000;
+
+// This enables CET User Shadow Stack for compatible modules and corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON.
+const MitigationFlags MITIGATION_CET_COMPAT_MODE = 0x40000000;
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SECURITY_LEVEL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver.cc
new file mode 100644
index 0000000000..f26322dd63
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver.cc
@@ -0,0 +1,47 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::ResolveInterceptor(
+ const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ // After all, we are using a locally mapped version of the exe, so the
+ // action is the same as for a target function.
+ return ResolveTarget(interceptor_module, interceptor_name,
+ const_cast<void**>(address));
+}
+
+// In this case all the work is done from the parent, so resolve is
+// just a simple GetProcAddress.
+NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ if (!module)
+ return STATUS_UNSUCCESSFUL;
+
+ base::win::PEImage module_image(module);
+ *address =
+ reinterpret_cast<void*>(module_image.GetProcAddress(function_name));
+
+ if (!*address) {
+ NOTREACHED_NT();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void ServiceResolverThunk::AllowLocalPatches() {
+ ntdll_base_ = ::GetModuleHandle(kNtdllName);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver.h b/security/sandbox/chromium/sandbox/win/src/service_resolver.h
new file mode 100644
index 0000000000..ee292f6a37
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SERVICE_RESOLVER_H__
+#define SANDBOX_SRC_SERVICE_RESOLVER_H__
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+class ServiceResolverThunk : public ResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ ServiceResolverThunk(HANDLE process, bool relaxed)
+ : ntdll_base_(nullptr),
+ process_(process),
+ relaxed_(relaxed),
+ relative_jump_(0) {}
+ ~ServiceResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) override;
+
+ // Implementation of Resolver::ResolveInterceptor.
+ NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address) override;
+
+ // Implementation of Resolver::ResolveTarget.
+ NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ // Call this to set up ntdll_base_ which will allow for local patches.
+ virtual void AllowLocalPatches();
+
+ // Verifies that the function specified by |target_name| in |target_module| is
+ // a service and copies the data from that function into |thunk_storage|. If
+ // |storage_bytes| is too small, then the method fails.
+ virtual NTSTATUS CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ protected:
+ // The unit test will use this member to allow local patch on a buffer.
+ HMODULE ntdll_base_;
+
+ // Handle of the child process.
+ HANDLE process_;
+
+ private:
+ // Returns true if the code pointer by target_ corresponds to the expected
+ // type of function. Saves that code on the first part of the thunk pointed
+ // by local_thunk (should be directly accessible from the parent).
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ // Performs the actual patch of target_.
+ // local_thunk must be already fully initialized, and the first part must
+ // contain the original code. The real type of this buffer is ServiceFullThunk
+ // (yes, private). remote_thunk (real type ServiceFullThunk), must be
+ // allocated on the child, and will contain the thunk data, after this call.
+ // Returns the apropriate status code.
+ virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk);
+
+ // Provides basically the same functionality as IsFunctionAService but it
+ // continues even if it does not recognize the function code. remote_thunk
+ // is the address of our memory on the child.
+ bool SaveOriginalFunction(void* local_thunk, void* remote_thunk);
+
+ // true if we are allowed to patch already-patched functions.
+ bool relaxed_;
+ ULONG relative_jump_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 (32 bit ntdll on 64 bit Vista).
+class Wow64ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Wow64ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64ResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 for Windows 8.
+class Wow64W8ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64W8ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Wow64W8ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on Windows 8.
+class Win8ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Win8ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Win8ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk);
+};
+
+// 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
+
+#endif // SANDBOX_SRC_SERVICE_RESOLVER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc
new file mode 100644
index 0000000000..d95ce0086b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc
@@ -0,0 +1,476 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/bit_cast.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+#pragma pack(push, 1)
+
+const BYTE kMovEax = 0xB8;
+const BYTE kMovEdx = 0xBA;
+const USHORT kMovEdxEsp = 0xD48B;
+const USHORT kCallPtrEdx = 0x12FF;
+const USHORT kCallEdx = 0xD2FF;
+const BYTE kCallEip = 0xE8;
+const BYTE kRet = 0xC2;
+const BYTE kRet2 = 0xC3;
+const USHORT kJmpEdx = 0xE2FF;
+const USHORT kXorEcx = 0xC933;
+const ULONG kLeaEdx = 0x0424548D;
+const ULONG kCallFs1 = 0xC015FF64;
+const USHORT kCallFs2 = 0;
+const BYTE kCallFs3 = 0;
+const BYTE kAddEsp1 = 0x83;
+const USHORT kAddEsp2 = 0x4C4;
+const BYTE kJmp32 = 0xE9;
+const USHORT kSysenter = 0x340F;
+
+// Service code for 32 bit systems.
+// NOTE: on win2003 "call dword ptr [edx]" is "call edx".
+struct ServiceEntry {
+ // This struct contains roughly the following code:
+ // 00 mov eax,25h
+ // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
+ // 0a call dword ptr [edx]
+ // 0c ret 2Ch
+ // 0f nop
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE mov_edx; // = BA
+ ULONG stub;
+ USHORT call_ptr_edx; // = FF 12
+ BYTE ret; // = C2
+ USHORT num_params;
+ BYTE nop;
+};
+
+// Service code for 32 bit Windows 8.
+struct ServiceEntryW8 {
+ // This struct contains the following code:
+ // 00 b825000000 mov eax,25h
+ // 05 e803000000 call eip+3
+ // 0a c22c00 ret 2Ch
+ // 0d 8bd4 mov edx,esp
+ // 0f 0f34 sysenter
+ // 11 c3 ret
+ // 12 8bff mov edi,edi
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE call_eip; // = E8
+ ULONG call_offset;
+ BYTE ret_p; // = C2
+ USHORT num_params;
+ USHORT mov_edx_esp; // = BD D4
+ USHORT sysenter; // = 0F 34
+ BYTE ret; // = C3
+ USHORT nop;
+};
+
+// Service code for a 32 bit process running on a 64 bit os.
+struct Wow64Entry {
+ // This struct may contain one of two versions of code:
+ // 1. For XP, Vista and 2K3:
+ // 00 b825000000 mov eax, 25h
+ // 05 33c9 xor ecx, ecx
+ // 07 8d542404 lea edx, [esp + 4]
+ // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 12 c22c00 ret 2Ch
+ //
+ // 2. For Windows 7:
+ // 00 b825000000 mov eax, 25h
+ // 05 33c9 xor ecx, ecx
+ // 07 8d542404 lea edx, [esp + 4]
+ // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 12 83c404 add esp, 4
+ // 15 c22c00 ret 2Ch
+ //
+ // So we base the structure on the bigger one:
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ USHORT xor_ecx; // = 33 C9
+ ULONG lea_edx; // = 8D 54 24 04
+ ULONG call_fs1; // = 64 FF 15 C0
+ USHORT call_fs2; // = 00 00
+ BYTE call_fs3; // = 00
+ BYTE add_esp1; // = 83 or ret
+ USHORT add_esp2; // = C4 04 or num_params
+ BYTE ret; // = C2
+ USHORT num_params;
+};
+
+// Service code for a 32 bit process running on 64 bit Windows 8.
+struct Wow64EntryW8 {
+ // 00 b825000000 mov eax, 25h
+ // 05 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 0b c22c00 ret 2Ch
+ // 0f 90 nop
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ ULONG call_fs1; // = 64 FF 15 C0
+ USHORT call_fs2; // = 00 00
+ BYTE call_fs3; // = 00
+ BYTE ret; // = C2
+ USHORT num_params;
+ BYTE nop;
+};
+
+// 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);
+static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
+ "wrong service length");
+static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
+static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
+
+struct ServiceFullThunk {
+ union {
+ ServiceEntry original;
+ ServiceEntryW8 original_w8;
+ Wow64Entry wow_64;
+ Wow64EntryW8 wow_64_w8;
+ };
+ int internal_thunk; // Dummy member to the beginning of the internal thunk.
+};
+
+#pragma pack(pop)
+
+} // namespace
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret =
+ Init(target_module, interceptor_module, target_name, interceptor_name,
+ interceptor_entry_point, thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ relative_jump_ = 0;
+ size_t thunk_bytes = GetThunkSize();
+ std::unique_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk =
+ reinterpret_cast<ServiceFullThunk*>(thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+size_t ServiceResolverThunk::GetThunkSize() const {
+ return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
+}
+
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceEntry function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kMovEdx != function_code.mov_edx ||
+ (kCallPtrEdx != function_code.call_ptr_edx &&
+ kCallEdx != function_code.call_ptr_edx) ||
+ kRet != function_code.ret) {
+ return false;
+ }
+
+ // Find the system call pointer if we don't already have it.
+ if (kCallEdx != function_code.call_ptr_edx) {
+ DWORD ki_system_call;
+ if (!::ReadProcessMemory(process_,
+ bit_cast<const void*>(function_code.stub),
+ &ki_system_call, sizeof(ki_system_call), &read)) {
+ return false;
+ }
+
+ if (sizeof(ki_system_call) != read)
+ return false;
+
+ HMODULE module_1, module_2;
+ // last check, call_stub should point to a KiXXSystemCall function on ntdll
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ bit_cast<const wchar_t*>(ki_system_call),
+ &module_1)) {
+ return false;
+ }
+
+ if (ntdll_base_) {
+ // This path is only taken when running the unit tests. We want to be
+ // able to patch a buffer in memory, so target_ is not inside ntdll.
+ module_2 = ntdll_base_;
+ } else {
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<const wchar_t*>(target_),
+ &module_2))
+ return false;
+ }
+
+ if (module_1 != module_2)
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ ServiceEntry intercepted_code;
+ size_t bytes_to_write = sizeof(intercepted_code);
+ ServiceFullThunk* full_local_thunk =
+ reinterpret_cast<ServiceFullThunk*>(local_thunk);
+ ServiceFullThunk* full_remote_thunk =
+ reinterpret_cast<ServiceFullThunk*>(remote_thunk);
+
+ // patch the original code
+ memcpy(&intercepted_code, &full_local_thunk->original,
+ sizeof(intercepted_code));
+ intercepted_code.mov_eax = kMovEax;
+ intercepted_code.service_id = full_local_thunk->original.service_id;
+ intercepted_code.mov_edx = kMovEdx;
+ intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
+ intercepted_code.call_ptr_edx = kJmpEdx;
+ bytes_to_write = kMinServiceSize;
+
+ if (relative_jump_) {
+ intercepted_code.mov_eax = kJmp32;
+ intercepted_code.service_id = relative_jump_;
+ bytes_to_write = offsetof(ServiceEntry, mov_edx);
+ }
+
+ // setup the thunk
+ SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
+ remote_thunk, interceptor_);
+
+ size_t thunk_size = GetThunkSize();
+
+ // copy the local thunk buffer to the child
+ SIZE_T written;
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, thunk_size,
+ &written)) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (thunk_size != written)
+ return STATUS_UNSUCCESSFUL;
+
+ // and now change the function to intercept, on the child
+ if (ntdll_base_) {
+ // running a unit test
+ if (!::WriteProcessMemory(process_, target_, &intercepted_code,
+ bytes_to_write, &written))
+ return STATUS_UNSUCCESSFUL;
+ } else {
+ if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
+ bytes_to_write))
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
+ void* remote_thunk) {
+ ServiceEntry function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kJmp32 == function_code.mov_eax) {
+ // Plain old entry point patch. The relative jump address follows it.
+ ULONG relative = function_code.service_id;
+
+ // First, fix our copy of their patch.
+ relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
+
+ function_code.service_id = relative;
+
+ // And now, remember how to re-patch it.
+ ServiceFullThunk* full_thunk =
+ reinterpret_cast<ServiceFullThunk*>(remote_thunk);
+
+ const ULONG kJmp32Size = 5;
+
+ relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
+ bit_cast<ULONG>(target_) - kJmp32Size;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64Entry function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
+ kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
+ kCallFs2 != function_code.call_fs2 ||
+ kCallFs3 != function_code.call_fs3) {
+ return false;
+ }
+
+ if ((kAddEsp1 == function_code.add_esp1 &&
+ kAddEsp2 == function_code.add_esp2 && kRet == function_code.ret) ||
+ kRet == function_code.add_esp1) {
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ return true;
+ }
+
+ return false;
+}
+
+bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64EntryW8 function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
+ kCallFs2 != function_code.call_fs2 ||
+ kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ return true;
+}
+
+bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceEntryW8 function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
+ function_code.call_offset != 3 || kRet != function_code.ret_p ||
+ kMovEdxEsp != function_code.mov_edx_esp ||
+ kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+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
new file mode 100644
index 0000000000..f692d0ea4f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+#if defined(_M_X64)
+#pragma pack(push, 1)
+
+const ULONG kMmovR10EcxMovEax = 0xB8D18B4C;
+const USHORT kSyscall = 0x050F;
+const BYTE kRetNp = 0xC3;
+const ULONG64 kMov1 = 0x54894808244C8948;
+const ULONG64 kMov2 = 0x4C182444894C1024;
+const ULONG kMov3 = 0x20244C89;
+const USHORT kTestByte = 0x04F6;
+const BYTE kPtr = 0x25;
+const BYTE kRet = 0xC3;
+const USHORT kJne = 0x0375;
+
+// Service code for 64 bit systems.
+struct ServiceEntry {
+ // This struct contains roughly the following code:
+ // 00 mov r10,rcx
+ // 03 mov eax,52h
+ // 08 syscall
+ // 0a ret
+ // 0b xchg ax,ax
+ // 0e xchg ax,ax
+
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ BYTE pad; // = 66
+ USHORT xchg_ax_ax1; // = 66 90
+ USHORT xchg_ax_ax2; // = 66 90
+};
+
+// Service code for 64 bit Windows 8.
+struct ServiceEntryW8 {
+ // This struct contains the following code:
+ // 00 48894c2408 mov [rsp+8], rcx
+ // 05 4889542410 mov [rsp+10], rdx
+ // 0a 4c89442418 mov [rsp+18], r8
+ // 0f 4c894c2420 mov [rsp+20], r9
+ // 14 4c8bd1 mov r10,rcx
+ // 17 b825000000 mov eax,25h
+ // 1c 0f05 syscall
+ // 1e c3 ret
+ // 1f 90 nop
+
+ ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54
+ ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C
+ ULONG mov_3; // = 89 4C 24 20
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ BYTE nop; // = 90
+};
+
+// Service code for 64 bit systems with int 2e fallback.
+struct ServiceEntryWithInt2E {
+ // This struct contains roughly the following code:
+ // 00 4c8bd1 mov r10,rcx
+ // 03 b855000000 mov eax,52h
+ // 08 f604250803fe7f01 test byte ptr SharedUserData!308, 1
+ // 10 7503 jne [over syscall]
+ // 12 0f05 syscall
+ // 14 c3 ret
+ // 15 cd2e int 2e
+ // 17 c3 ret
+
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT test_byte; // = F6 04
+ BYTE ptr; // = 25
+ ULONG user_shared_data_ptr;
+ BYTE one; // = 01
+ USHORT jne_over_syscall; // = 75 03
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ USHORT int2e; // = CD 2E
+ BYTE ret2; // = C3
+};
+
+// We don't have an internal thunk for x64.
+struct ServiceFullThunk {
+ union {
+ ServiceEntry original;
+ ServiceEntryW8 original_w8;
+ ServiceEntryWithInt2E original_int2e_fallback;
+ };
+};
+
+#pragma pack(pop)
+
+bool IsService(const void* source) {
+ const ServiceEntry* service = reinterpret_cast<const ServiceEntry*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kSyscall == service->syscall && kRetNp == service->ret);
+}
+
+bool IsServiceW8(const void* source) {
+ const ServiceEntryW8* service =
+ reinterpret_cast<const ServiceEntryW8*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kMov1 == service->mov_1 && kMov2 == service->mov_2 &&
+ kMov3 == service->mov_3);
+}
+
+bool IsServiceWithInt2E(const void* source) {
+ const ServiceEntryWithInt2E* service =
+ reinterpret_cast<const ServiceEntryWithInt2E*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kTestByte == service->test_byte && kPtr == service->ptr &&
+ kJne == service->jne_over_syscall && kSyscall == service->syscall &&
+ kRet == service->ret && kRet == service->ret2);
+}
+
+bool IsAnyService(const void* source) {
+ return IsService(source) || IsServiceW8(source) || IsServiceWithInt2E(source);
+}
+
+#elif defined(_M_ARM64)
+#pragma pack(push, 4)
+
+const ULONG kSvc = 0xD4000001;
+const ULONG kRetNp = 0xD65F03C0;
+const ULONG kServiceIdMask = 0x001FFFE0;
+
+struct ServiceEntry {
+ ULONG svc;
+ ULONG ret;
+ ULONG64 unused;
+};
+
+struct ServiceFullThunk {
+ ServiceEntry original;
+};
+
+#pragma pack(pop)
+
+bool IsService(const void* source) {
+ const ServiceEntry* service = reinterpret_cast<const ServiceEntry*>(source);
+
+ return (kSvc == (service->svc & ~kServiceIdMask) && kRetNp == service->ret &&
+ 0 == service->unused);
+}
+
+bool IsAnyService(const void* source) {
+ return IsService(source);
+}
+
+#else
+#error "Unsupported Windows 64-bit Arch"
+#endif
+
+} // namespace
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret =
+ Init(target_module, interceptor_module, target_name, interceptor_name,
+ interceptor_entry_point, thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ std::unique_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk =
+ reinterpret_cast<ServiceFullThunk*>(thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_OBJECT_NAME_COLLISION;
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+size_t ServiceResolverThunk::GetThunkSize() const {
+ return sizeof(ServiceFullThunk);
+}
+
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_OBJECT_NAME_COLLISION;
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceFullThunk function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read))
+ return false;
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (!IsAnyService(&function_code))
+ return false;
+
+ // Save the verified code.
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ // Patch the original code.
+ ServiceEntry local_service;
+ DCHECK_NT(GetInternalThunkSize() <= sizeof(local_service));
+ if (!SetInternalThunk(&local_service, sizeof(local_service), nullptr,
+ interceptor_))
+ return STATUS_UNSUCCESSFUL;
+
+ // Copy the local thunk buffer to the child.
+ SIZE_T actual;
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
+ sizeof(ServiceFullThunk), &actual))
+ return STATUS_UNSUCCESSFUL;
+
+ if (sizeof(ServiceFullThunk) != actual)
+ return STATUS_UNSUCCESSFUL;
+
+ // And now change the function to intercept, on the child.
+ if (ntdll_base_) {
+ // Running a unit test.
+ if (!::WriteProcessMemory(process_, target_, &local_service,
+ sizeof(local_service), &actual))
+ return STATUS_UNSUCCESSFUL;
+ } else {
+ if (!WriteProtectedChildMemory(process_, target_, &local_service,
+ sizeof(local_service)))
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ NOTREACHED_NT();
+ return false;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
new file mode 100644
index 0000000000..63ea26dcbf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for ServiceResolverThunk.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/bit_cast.h"
+#include "base/macros.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/resolver.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ResolverThunkTest {
+ public:
+ virtual ~ResolverThunkTest() {}
+
+ virtual sandbox::ServiceResolverThunk* resolver() = 0;
+
+ // Sets the interception target to the desired address.
+ void set_target(void* target) { fake_target_ = target; }
+
+ protected:
+ // Holds the address of the fake target.
+ void* fake_target_;
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+template <typename T>
+class ResolverThunkTestImpl : public T, public ResolverThunkTest {
+ public:
+ // The service resolver needs a child process to write to.
+ explicit ResolverThunkTestImpl(bool relaxed)
+ : T(::GetCurrentProcess(), relaxed) {}
+
+ sandbox::ServiceResolverThunk* resolver() { return this; }
+
+ protected:
+ // Overrides Resolver::Init
+ virtual NTSTATUS Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ ret = T::Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point, thunk_storage,
+ storage_bytes);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+
+ this->target_ = fake_target_;
+
+ return ret;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunkTestImpl);
+};
+
+typedef ResolverThunkTestImpl<sandbox::ServiceResolverThunk> WinXpResolverTest;
+
+#if !defined(_WIN64)
+typedef ResolverThunkTestImpl<sandbox::Win8ResolverThunk> Win8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W8ResolverThunk>
+ Wow64W8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W10ResolverThunk>
+ Wow64W10ResolverTest;
+#endif
+
+const BYTE kJump32 = 0xE9;
+
+void CheckJump(void* source, void* target) {
+#pragma pack(push)
+#pragma pack(1)
+ struct Code {
+ BYTE jump;
+ ULONG delta;
+ };
+#pragma pack(pop)
+
+#if defined(_WIN64)
+ FAIL() << "Running 32-bit codepath";
+#else
+ Code* patched = reinterpret_cast<Code*>(source);
+ EXPECT_EQ(kJump32, patched->jump);
+
+ ULONG source_addr = bit_cast<ULONG>(source);
+ ULONG target_addr = bit_cast<ULONG>(target);
+ EXPECT_EQ(target_addr + 19 - source_addr, patched->delta);
+#endif
+}
+
+NTSTATUS PatchNtdllWithResolver(const char* function,
+ bool relaxed,
+ ResolverThunkTest* thunk_test) {
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ EXPECT_TRUE(ntdll_base);
+
+ void* target =
+ reinterpret_cast<void*>(::GetProcAddress(ntdll_base, function));
+ EXPECT_TRUE(target);
+ if (!target)
+ return STATUS_UNSUCCESSFUL;
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+
+ thunk_test->set_target(service);
+
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ std::unique_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ resolver->AllowLocalPatches();
+
+ NTSTATUS ret =
+ resolver->Setup(ntdll_base, nullptr, function, nullptr, function_entry,
+ thunk.get(), thunk_size, &used);
+ if (NT_SUCCESS(ret)) {
+ EXPECT_EQ(thunk_size, used);
+ EXPECT_NE(0, memcmp(service, target, sizeof(service)));
+ EXPECT_NE(kJump32, service[0]);
+
+ if (relaxed) {
+ // It's already patched, let's patch again, and simulate a direct patch.
+ service[0] = kJump32;
+ ret = resolver->Setup(ntdll_base, nullptr, function, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ CheckJump(service, thunk.get());
+ }
+ }
+
+ return ret;
+}
+
+std::unique_ptr<ResolverThunkTest> GetTestResolver(bool relaxed) {
+#if defined(_WIN64)
+ return std::make_unique<WinXpResolverTest>(relaxed);
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::Version::WIN10)
+ return std::make_unique<Wow64W10ResolverTest>(relaxed);
+ if (os_info->version() >= base::win::Version::WIN8)
+ return std::make_unique<Wow64W8ResolverTest>(relaxed);
+ return std::make_unique<Wow64ResolverTest>(relaxed);
+ }
+
+ if (os_info->version() >= base::win::Version::WIN8)
+ return std::make_unique<Win8ResolverTest>(relaxed);
+
+ return std::make_unique<WinXpResolverTest>(relaxed);
+#endif
+}
+
+NTSTATUS PatchNtdll(const char* function, bool relaxed) {
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(relaxed);
+ return PatchNtdllWithResolver(function, relaxed, thunk_test.get());
+}
+
+TEST(ServiceResolverTest, PatchesServices) {
+ NTSTATUS ret = PatchNtdll("NtClose", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+}
+
+TEST(ServiceResolverTest, FailsIfNotService) {
+#if !defined(_WIN64)
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false));
+#endif
+
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false));
+}
+
+TEST(ServiceResolverTest, PatchesPatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ NTSTATUS ret = PatchNtdll("NtClose", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, MultiplePatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+ NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateFile", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateMutant", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtMapViewOfSection", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, LocalPatchesAllowed) {
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ ASSERT_TRUE(ntdll_base);
+
+ const char kFunctionName[] = "NtClose";
+
+ void* target =
+ reinterpret_cast<void*>(::GetProcAddress(ntdll_base, kFunctionName));
+ ASSERT_TRUE(target);
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+ thunk_test->set_target(service);
+
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ std::unique_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+
+ // First try patching without having allowed local patches.
+ ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ EXPECT_FALSE(NT_SUCCESS(ret));
+
+ // Now allow local patches and check that things work.
+ resolver->AllowLocalPatches();
+ ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+}
+
+} // namespace
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc
new file mode 100644
index 0000000000..3ddea76350
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc
@@ -0,0 +1,193 @@
+// 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/sharedmem_ipc_client.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+namespace {
+
+DWORD SignalObjectAndWaitWrapper(HANDLE object_to_signal,
+ HANDLE object_to_wait_on,
+ DWORD millis,
+ BOOL alertable) {
+ // Not running in a sandboxed process so can call directly.
+ if (!g_nt.SignalAndWaitForSingleObject)
+ return SignalObjectAndWait(object_to_signal, object_to_wait_on, millis,
+ alertable);
+ // Don't support alertable.
+ CHECK_NT(!alertable);
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = millis * -10000LL;
+ NTSTATUS status = g_nt.SignalAndWaitForSingleObject(
+ object_to_signal, object_to_wait_on, alertable,
+ millis == INFINITE ? nullptr : &timeout);
+ if (!NT_SUCCESS(status))
+ return WAIT_FAILED;
+ return status;
+}
+
+DWORD WaitForSingleObjectWrapper(HANDLE handle, DWORD millis) {
+ // Not running in a sandboxed process so can call directly.
+ if (!g_nt.WaitForSingleObject)
+ return WaitForSingleObject(handle, millis);
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = millis * -10000LL;
+ NTSTATUS status = g_nt.WaitForSingleObject(
+ handle, FALSE, millis == INFINITE ? nullptr : &timeout);
+ if (!NT_SUCCESS(status))
+ return WAIT_FAILED;
+ return status;
+}
+
+} // namespace
+
+// Get the base of the data buffer of the channel; this is where the input
+// parameters get serialized. Since they get serialized directly into the
+// channel we avoid one copy.
+void* SharedMemIPCClient::GetBuffer() {
+ bool failure = false;
+ size_t ix = LockFreeChannel(&failure);
+ if (failure)
+ return nullptr;
+ return reinterpret_cast<char*>(control_) +
+ control_->channels[ix].channel_base;
+}
+
+// If we need to cancel an IPC before issuing DoCall
+// our client should call FreeBuffer with the same pointer
+// returned by GetBuffer.
+void SharedMemIPCClient::FreeBuffer(void* buffer) {
+ size_t num = ChannelIndexFromBuffer(buffer);
+ ChannelControl* channel = control_->channels;
+ LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
+ DCHECK_NE(kFreeChannel, static_cast<ChannelState>(result));
+}
+
+// The constructor simply casts the shared memory to the internal
+// structures. This is a cheap step that is why this IPC object can
+// and should be constructed per call.
+SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
+ : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
+ first_base_ =
+ reinterpret_cast<char*>(shared_mem) + control_->channels[0].channel_base;
+ // There must be at least one channel.
+ DCHECK(0 != control_->channels_count);
+}
+
+// Do the IPC. At this point the channel should have already been
+// filled with the serialized input parameters.
+// We follow the pattern explained in the header file.
+ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
+ CrossCallReturn* answer) {
+ if (!control_->server_alive)
+ return SBOX_ERROR_CHANNEL_ERROR;
+
+ size_t num = ChannelIndexFromBuffer(params->GetBuffer());
+ ChannelControl* channel = control_->channels;
+ // Note that the IPC tag goes outside the buffer as well inside
+ // the buffer. This should enable the server to prioritize based on
+ // IPC tags without having to de-serialize the entire message.
+ channel[num].ipc_tag = params->GetTag();
+
+ // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
+ // we check if the server_alive mutex was abandoned which will indicate
+ // that the server has died.
+
+ // While the atomic signaling and waiting is not a requirement, it
+ // is nice because we save a trip to kernel.
+ DWORD wait = SignalObjectAndWaitWrapper(channel[num].ping_event,
+ channel[num].pong_event,
+ kIPCWaitTimeOut1, false);
+ if (WAIT_TIMEOUT == wait) {
+ // The server is taking too long. Enter a loop were we check if the
+ // server_alive mutex has been abandoned which would signal a server crash
+ // or else we keep waiting for a response.
+ while (true) {
+ wait = WaitForSingleObjectWrapper(control_->server_alive, 0);
+ if (WAIT_TIMEOUT == wait) {
+ // Server seems still alive. We already signaled so here we just wait.
+ wait = WaitForSingleObjectWrapper(channel[num].pong_event,
+ kIPCWaitTimeOut1);
+ if (WAIT_OBJECT_0 == wait) {
+ // The server took a long time but responded.
+ break;
+ } else if (WAIT_TIMEOUT == wait) {
+ continue;
+ } else {
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+ } else {
+ // The server has crashed and windows has signaled the mutex as
+ // abandoned.
+ ::InterlockedExchange(&channel[num].state, kAbandonedChannel);
+ control_->server_alive = 0;
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+ }
+ } else if (WAIT_OBJECT_0 != wait) {
+ // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+
+ // The server has returned an answer, copy it and free the channel.
+ memcpy_wrapper(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
+
+ // Return the IPC state It can indicate that while the IPC has
+ // completed some error in the Broker has caused to not return valid
+ // results.
+ return answer->call_outcome;
+}
+
+// Locking a channel is a simple as looping over all the channels
+// looking for one that is has state = kFreeChannel and atomically
+// swapping it to kBusyChannel.
+// If there is no free channel, then we must back off so some other
+// thread makes progress and frees a channel. To back off we sleep.
+size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
+ if (0 == control_->channels_count) {
+ *severe_failure = true;
+ return 0;
+ }
+ ChannelControl* channel = control_->channels;
+ do {
+ for (size_t ix = 0; ix != control_->channels_count; ++ix) {
+ if (kFreeChannel == ::InterlockedCompareExchange(
+ &channel[ix].state, kBusyChannel, kFreeChannel)) {
+ *severe_failure = false;
+ return ix;
+ }
+ }
+ // We did not find any available channel, maybe the server is dead.
+ DWORD wait =
+ WaitForSingleObjectWrapper(control_->server_alive, kIPCWaitTimeOut2);
+ if (WAIT_TIMEOUT != wait) {
+ // The server is dead and we outlive it enough to get in trouble.
+ *severe_failure = true;
+ return 0;
+ }
+ } while (true);
+}
+
+// Find out which channel we are from the pointer returned by GetBuffer.
+size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
+ ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
+ size_t num = d / kIPCChannelSize;
+ DCHECK_LT(num, control_->channels_count);
+ return (num);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h
new file mode 100644
index 0000000000..73f739dece
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
+#define SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox.h"
+
+// IPC transport implementation that uses shared memory.
+// This is the client side
+//
+// The shared memory is divided on blocks called channels, and potentially
+// it can perform as many concurrent IPC calls as channels. The IPC over
+// each channel is strictly synchronous for the client.
+//
+// Each channel as a channel control section associated with. Each control
+// section has two kernel events (known as ping and pong) and a integer
+// variable that maintains a state
+//
+// this is the state diagram of a channel:
+//
+// locked in service
+// kFreeChannel---------->BusyChannel-------------->kAckChannel
+// ^ |
+// |_________________________________________________|
+// answer ready
+//
+// The protocol is as follows:
+// 1) client finds a free channel: state = kFreeChannel
+// 2) does an atomic compare-and-swap, now state = BusyChannel
+// 3) client writes the data into the channel buffer
+// 4) client signals the ping event and waits (blocks) on the pong event
+// 5) eventually the server signals the pong event
+// 6) the client awakes and reads the answer from the same channel
+// 7) the client updates its InOut parameters with the new data from the
+// shared memory section.
+// 8) the client atomically sets the state = kFreeChannel
+//
+// In the shared memory the layout is as follows:
+//
+// [ channel count ]
+// [ channel control 0]
+// [ channel control 1]
+// [ channel control N]
+// [ channel buffer 0 ] 1024 bytes
+// [ channel buffer 1 ] 1024 bytes
+// [ channel buffer N ] 1024 bytes
+//
+// By default each channel buffer is 1024 bytes
+namespace sandbox {
+
+// the possible channel states as described above
+enum ChannelState {
+ // channel is free
+ kFreeChannel = 1,
+ // IPC in progress client side
+ kBusyChannel,
+ // IPC in progress server side
+ kAckChannel,
+ // not used right now
+ kReadyChannel,
+ // IPC abandoned by client side
+ kAbandonedChannel
+};
+
+// The next two constants control the time outs for the IPC.
+const DWORD kIPCWaitTimeOut1 = 1000; // Milliseconds.
+const DWORD kIPCWaitTimeOut2 = 50; // Milliseconds.
+
+// the channel control structure
+struct ChannelControl {
+ // points to be beginning of the channel buffer, where data goes
+ size_t channel_base;
+ // maintains the state from the ChannelState enumeration
+ volatile LONG state;
+ // the ping event is signaled by the client when the IPC data is ready on
+ // the buffer
+ HANDLE ping_event;
+ // the client waits on the pong event for the IPC answer back
+ HANDLE pong_event;
+ // the IPC unique identifier
+ IpcTag ipc_tag;
+};
+
+struct IPCControl {
+ // total number of channels available, some might be busy at a given time
+ size_t channels_count;
+ // handle to a shared mutex to detect when the server is dead
+ HANDLE server_alive;
+ // array of channel control structures
+ ChannelControl channels[1];
+};
+
+// the actual shared memory IPC implementation class. This object is designed
+// to be lightweight so it can be constructed on-site (at the calling place)
+// wherever an IPC call is needed.
+class SharedMemIPCClient {
+ public:
+ // Creates the IPC client.
+ // as parameter it takes the base address of the shared memory
+ explicit SharedMemIPCClient(void* shared_mem);
+
+ // locks a free channel and returns the channel buffer memory base. This call
+ // blocks until there is a free channel
+ void* GetBuffer();
+
+ // releases the lock on the channel, for other to use. call this if you have
+ // called GetBuffer and you want to abort but have not called yet DoCall()
+ void FreeBuffer(void* buffer);
+
+ // Performs the actual IPC call.
+ // params: The blob of packed input parameters.
+ // answer: upon IPC completion, it contains the server answer to the IPC.
+ // If the return value is not SBOX_ERROR_CHANNEL_ERROR, the caller has to free
+ // the channel.
+ // returns ALL_OK if the IPC mechanism successfully delivered. You still need
+ // to check on the answer structure to see the actual IPC result.
+ ResultCode DoCall(CrossCallParams* params, CrossCallReturn* answer);
+
+ private:
+ // Returns the index of the first free channel. It sets 'severe_failure'
+ // to true if there is an unrecoverable error that does not allow to
+ // find a channel.
+ size_t LockFreeChannel(bool* severe_failure);
+ // Return the channel index given the address of the buffer.
+ size_t ChannelIndexFromBuffer(const void* buffer);
+ IPCControl* control_;
+ // point to the first channel base
+ char* first_base_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc
new file mode 100644
index 0000000000..74e5c171e0
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_args.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+namespace {
+// This handle must not be closed.
+volatile HANDLE g_alive_mutex = nullptr;
+} // namespace
+
+namespace sandbox {
+
+SharedMemIPCServer::ServerControl::ServerControl() {}
+
+SharedMemIPCServer::ServerControl::~ServerControl() {}
+
+SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process,
+ DWORD target_process_id,
+ ThreadProvider* thread_provider,
+ Dispatcher* dispatcher)
+ : client_control_(nullptr),
+ thread_provider_(thread_provider),
+ target_process_(target_process),
+ target_process_id_(target_process_id),
+ call_dispatcher_(dispatcher) {
+ // We create a initially owned mutex. If the server dies unexpectedly,
+ // the thread that owns it will fail to release the lock and windows will
+ // report to the target (when it tries to acquire it) that the wait was
+ // abandoned. Note: We purposely leak the local handle because we want it to
+ // be closed by Windows itself so it is properly marked as abandoned if the
+ // server dies.
+ if (!g_alive_mutex) {
+ HANDLE mutex = ::CreateMutexW(nullptr, true, nullptr);
+ if (::InterlockedCompareExchangePointer(&g_alive_mutex, mutex, nullptr)) {
+ // We lost the race to create the mutex.
+ ::CloseHandle(mutex);
+ }
+ }
+}
+
+SharedMemIPCServer::~SharedMemIPCServer() {
+ // Free the wait handles associated with the thread pool.
+ if (!thread_provider_->UnRegisterWaits(this)) {
+ // Better to leak than to crash.
+ return;
+ }
+ server_contexts_.clear();
+
+ if (client_control_)
+ ::UnmapViewOfFile(client_control_);
+}
+
+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;
+ }
+ // The channel size should be aligned.
+ if (0 != (channel_size % 32)) {
+ return false;
+ }
+
+ // Calculate how many channels we can fit in the shared memory.
+ shared_size -= offsetof(IPCControl, channels);
+ size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size);
+
+ // If we cannot fit even one channel we bail out.
+ if (0 == channel_count) {
+ return false;
+ }
+ // Calculate the start of the first channel.
+ size_t base_start =
+ (sizeof(ChannelControl) * channel_count) + offsetof(IPCControl, channels);
+
+ client_control_ = reinterpret_cast<IPCControl*>(shared_mem);
+ client_control_->channels_count = 0;
+
+ // This is the initialization that we do per-channel. Basically:
+ // 1) make two events (ping & pong)
+ // 2) create handles to the events for the client and the server.
+ // 3) initialize the channel (client_context) with the state.
+ // 4) initialize the server side of the channel (service_context).
+ // 5) call the thread provider RegisterWait to register the ping events.
+ for (size_t ix = 0; ix != channel_count; ++ix) {
+ ChannelControl* client_context = &client_control_->channels[ix];
+ ServerControl* service_context = new ServerControl;
+ server_contexts_.push_back(base::WrapUnique(service_context));
+
+ if (!MakeEvents(&service_context->ping_event, &service_context->pong_event,
+ &client_context->ping_event, &client_context->pong_event)) {
+ return false;
+ }
+
+ client_context->channel_base = base_start;
+ client_context->state = kFreeChannel;
+
+ // Note that some of these values are available as members of this object
+ // but we put them again into the service_context because we will be called
+ // on a static method (ThreadPingEventReady). 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<char*>(shared_mem);
+ service_context->channel_size = channel_size;
+ service_context->channel = client_context;
+ service_context->channel_buffer =
+ service_context->shared_base + client_context->channel_base;
+ service_context->dispatcher = call_dispatcher_;
+ service_context->target_info.process = target_process_;
+ service_context->target_info.process_id = target_process_id_;
+ // Advance to the next channel.
+ base_start += channel_size;
+ // Register the ping event with the threadpool.
+ thread_provider_->RegisterWait(this, service_context->ping_event.Get(),
+ ThreadPingEventReady, service_context);
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), g_alive_mutex, target_process_,
+ &client_control_->server_alive,
+ SYNCHRONIZE | EVENT_MODIFY_STATE, false, 0)) {
+ return false;
+ }
+ // This last setting indicates to the client all is setup.
+ client_control_->channels_count = channel_count;
+ return true;
+}
+
+bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context,
+ void* ipc_buffer,
+ CrossCallReturn* call_result) {
+ // Set the default error code;
+ SetCallError(SBOX_ERROR_INVALID_IPC, call_result);
+ uint32_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.
+ std::unique_ptr<CrossCallParamsEx> params(CrossCallParamsEx::CreateFromBuffer(
+ ipc_buffer, service_context->channel_size, &output_size));
+ if (!params.get())
+ return false;
+
+ IpcTag tag = params->GetTag();
+ static_assert(0 == INVALID_TYPE, "incorrect type enum");
+ IPCParams ipc_params = {tag};
+
+ void* args[kMaxIpcParams];
+ if (!GetArgs(params.get(), &ipc_params, args))
+ return false;
+
+ IPCInfo ipc_info = {tag};
+ ipc_info.client_info = &service_context->target_info;
+ Dispatcher* dispatcher = service_context->dispatcher;
+ DCHECK(dispatcher);
+ bool error = true;
+ Dispatcher* handler = nullptr;
+
+ Dispatcher::CallbackGeneric callback_generic;
+ handler = dispatcher->OnMessageReady(&ipc_params, &callback_generic);
+ if (handler) {
+ switch (params->GetParamsCount()) {
+ case 0: {
+ // Ask the IPC dispatcher if it can service this IPC.
+ Dispatcher::Callback0 callback =
+ reinterpret_cast<Dispatcher::Callback0>(callback_generic);
+ if (!(handler->*callback)(&ipc_info))
+ break;
+ error = false;
+ break;
+ }
+ case 1: {
+ Dispatcher::Callback1 callback =
+ reinterpret_cast<Dispatcher::Callback1>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0]))
+ break;
+ error = false;
+ break;
+ }
+ case 2: {
+ Dispatcher::Callback2 callback =
+ reinterpret_cast<Dispatcher::Callback2>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1]))
+ break;
+ error = false;
+ break;
+ }
+ case 3: {
+ Dispatcher::Callback3 callback =
+ reinterpret_cast<Dispatcher::Callback3>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2]))
+ break;
+ error = false;
+ break;
+ }
+ case 4: {
+ Dispatcher::Callback4 callback =
+ reinterpret_cast<Dispatcher::Callback4>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2],
+ args[3]))
+ break;
+ error = false;
+ break;
+ }
+ case 5: {
+ Dispatcher::Callback5 callback =
+ reinterpret_cast<Dispatcher::Callback5>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4]))
+ break;
+ error = false;
+ break;
+ }
+ case 6: {
+ Dispatcher::Callback6 callback =
+ reinterpret_cast<Dispatcher::Callback6>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5]))
+ break;
+ error = false;
+ break;
+ }
+ case 7: {
+ Dispatcher::Callback7 callback =
+ reinterpret_cast<Dispatcher::Callback7>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6]))
+ break;
+ error = false;
+ break;
+ }
+ case 8: {
+ Dispatcher::Callback8 callback =
+ reinterpret_cast<Dispatcher::Callback8>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7]))
+ break;
+ error = false;
+ break;
+ }
+ case 9: {
+ Dispatcher::Callback9 callback =
+ reinterpret_cast<Dispatcher::Callback9>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7], args[8]))
+ break;
+ error = false;
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ }
+
+ if (error) {
+ if (handler)
+ SetCallError(SBOX_ERROR_FAILED_IPC, call_result);
+ } else {
+ memcpy(call_result, &ipc_info.return_info, sizeof(*call_result));
+ SetCallSuccess(call_result);
+ if (params->IsInOut()) {
+ // Maybe the params got changed by the broker. We need to upadte the
+ // memory section.
+ memcpy(ipc_buffer, params.get(), output_size);
+ }
+ }
+
+ ReleaseArgs(&ipc_params, args);
+
+ return !error;
+}
+
+// This function gets called by a thread from the thread pool when a
+// ping event fires. The context is the same as passed in the RegisterWait()
+// call above.
+void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context,
+ unsigned char) {
+ if (!context) {
+ DCHECK(false);
+ return;
+ }
+ ServerControl* service_context = reinterpret_cast<ServerControl*>(context);
+ // Since the event fired, the channel *must* be busy. Change to kAckChannel
+ // while we service it.
+ LONG last_state = ::InterlockedCompareExchange(
+ &service_context->channel->state, kAckChannel, kBusyChannel);
+ if (kBusyChannel != last_state) {
+ DCHECK(false);
+ return;
+ }
+
+ // Prepare the result structure. At this point we will return some result
+ // even if the IPC is invalid, malformed or has no handler.
+ CrossCallReturn call_result = {0};
+ void* buffer = service_context->channel_buffer;
+
+ InvokeCallback(service_context, buffer, &call_result);
+
+ // Copy the answer back into the channel and signal the pong event. This
+ // should wake up the client so it can finish the ipc cycle.
+ CrossCallParams* call_params = reinterpret_cast<CrossCallParams*>(buffer);
+ memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result));
+ ::InterlockedExchange(&service_context->channel->state, kAckChannel);
+ ::SetEvent(service_context->pong_event.Get());
+}
+
+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->Set(::CreateEventW(nullptr, false, false, nullptr));
+ if (!::DuplicateHandle(::GetCurrentProcess(), server_ping->Get(),
+ target_process_, client_ping, kDesiredAccess, false,
+ 0)) {
+ return false;
+ }
+
+ server_pong->Set(::CreateEventW(nullptr, false, false, nullptr));
+ if (!::DuplicateHandle(::GetCurrentProcess(), server_pong->Get(),
+ target_process_, client_pong, kDesiredAccess, false,
+ 0)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h
new file mode 100644
index 0000000000..fce52c6ad0
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
+#define SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
+
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+
+#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"
+
+// IPC transport implementation that uses shared memory.
+// This is the server side
+//
+// The server side has knowledge about the layout of the shared memory
+// and the state transitions. Both are explained in sharedmem_ipc_client.h
+//
+// As opposed to SharedMemIPClient, the Server object should be one for the
+// entire lifetime of the target process. The server is in charge of creating
+// the events (ping, pong) both for the client and for the target that are used
+// to signal the IPC and also in charge of setting the initial state of the
+// channels.
+//
+// When an IPC is ready, the server relies on being called by on the
+// ThreadPingEventReady callback. The IPC server then retrieves the buffer,
+// marshals it into a CrossCallParam object and calls the Dispatcher, who is in
+// charge of fulfilling the IPC request.
+namespace sandbox {
+
+// the shared memory implementation of the IPC server. There should be one
+// of these objects per target (IPC client) process
+class SharedMemIPCServer {
+ public:
+ // Creates the IPC server.
+ // target_process: handle to the target process. It must be suspended. 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.
+ // thread_provider: a thread provider object.
+ // dispatcher: an object that can service IPC calls.
+ SharedMemIPCServer(HANDLE target_process,
+ DWORD target_process_id,
+ 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_t shared_size, uint32_t channel_size);
+
+ private:
+ // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
+ // do not work with sandbox tests.
+ FRIEND_TEST_ALL_PREFIXES(IPCTest, SharedMemServerTests);
+ // When an event fires (IPC request). A thread from the ThreadProvider
+ // will call this function. The context parameter should be the same as
+ // provided when ThreadProvider::RegisterWait was called.
+ static void __stdcall ThreadPingEventReady(void* context, unsigned char);
+
+ // Makes the client and server events. This function is called once
+ // per channel.
+ bool MakeEvents(base::win::ScopedHandle* server_ping,
+ base::win::ScopedHandle* server_pong,
+ HANDLE* client_ping,
+ HANDLE* client_pong);
+
+ // A copy this structure is maintained per channel.
+ // Note that a lot of the fields are just the same of what we have in the IPC
+ // object itself. It is better to have the copies since we can dispatch in the
+ // static method without worrying about converting back to a member function
+ // call or about threading issues.
+ struct ServerControl {
+ ServerControl();
+ ~ServerControl();
+
+ // This channel server ping event.
+ base::win::ScopedHandle ping_event;
+ // This channel server pong event.
+ base::win::ScopedHandle pong_event;
+ // The size of this channel.
+ uint32_t channel_size;
+ // The pointer to the actual channel data.
+ char* channel_buffer;
+ // The pointer to the base of the shared memory.
+ char* shared_base;
+ // A pointer to this channel's client-side control structure this structure
+ // lives in the shared memory.
+ ChannelControl* channel;
+ // the IPC dispatcher associated with this channel.
+ Dispatcher* dispatcher;
+ // The target process information associated with this channel.
+ ClientInfo target_info;
+ };
+
+ // Looks for the appropriate handler for this IPC and invokes it.
+ static bool InvokeCallback(const ServerControl* service_context,
+ void* ipc_buffer,
+ CrossCallReturn* call_result);
+
+ // Points to the shared memory channel control which lives at
+ // the start of the shared section.
+ IPCControl* client_control_;
+
+ // Keeps track of the server side objects that are used to answer an IPC.
+ std::list<std::unique_ptr<ServerControl>> server_contexts_;
+
+ // The thread provider provides the threads that call back into this object
+ // when the IPC events fire.
+ ThreadProvider* thread_provider_;
+
+ // The IPC object is associated with a target process.
+ HANDLE target_process_;
+
+ // The target process id associated with the IPC object.
+ DWORD target_process_id_;
+
+ // The dispatcher handles 'ready' IPC calls.
+ Dispatcher* call_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemIPCServer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sid.cc b/security/sandbox/chromium/sandbox/win/src/sid.cc
new file mode 100644
index 0000000000..a97ca340ed
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sid.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sid.h"
+
+#include <memory>
+
+#include <sddl.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+DWORD WellKnownCapabilityToRid(WellKnownCapabilities capability) {
+ switch (capability) {
+ case kInternetClient:
+ return SECURITY_CAPABILITY_INTERNET_CLIENT;
+ case kInternetClientServer:
+ return SECURITY_CAPABILITY_INTERNET_CLIENT_SERVER;
+ case kPrivateNetworkClientServer:
+ return SECURITY_CAPABILITY_PRIVATE_NETWORK_CLIENT_SERVER;
+ case kPicturesLibrary:
+ return SECURITY_CAPABILITY_PICTURES_LIBRARY;
+ case kVideosLibrary:
+ return SECURITY_CAPABILITY_VIDEOS_LIBRARY;
+ case kMusicLibrary:
+ return SECURITY_CAPABILITY_MUSIC_LIBRARY;
+ case kDocumentsLibrary:
+ return SECURITY_CAPABILITY_DOCUMENTS_LIBRARY;
+ case kEnterpriseAuthentication:
+ return SECURITY_CAPABILITY_ENTERPRISE_AUTHENTICATION;
+ case kSharedUserCertificates:
+ return SECURITY_CAPABILITY_SHARED_USER_CERTIFICATES;
+ case kRemovableStorage:
+ return SECURITY_CAPABILITY_REMOVABLE_STORAGE;
+ case kAppointments:
+ return SECURITY_CAPABILITY_APPOINTMENTS;
+ case kContacts:
+ return SECURITY_CAPABILITY_CONTACTS;
+ default:
+ break;
+ }
+ return 0;
+}
+
+} // namespace
+
+Sid::Sid() : sid_() {}
+
+Sid::Sid(PSID sid) : sid_() {
+ ::CopySid(SECURITY_MAX_SID_SIZE, sid_, sid);
+}
+
+Sid::Sid(const SID* sid) : sid_() {
+ ::CopySid(SECURITY_MAX_SID_SIZE, sid_, const_cast<SID*>(sid));
+}
+
+Sid::Sid(WELL_KNOWN_SID_TYPE type) {
+ DWORD size_sid = SECURITY_MAX_SID_SIZE;
+ bool result = ::CreateWellKnownSid(type, nullptr, sid_, &size_sid);
+ DCHECK(result);
+ (void)result;
+}
+
+Sid Sid::FromKnownCapability(WellKnownCapabilities capability) {
+ DWORD capability_rid = WellKnownCapabilityToRid(capability);
+ if (!capability_rid)
+ return Sid();
+ SID_IDENTIFIER_AUTHORITY capability_authority = {
+ SECURITY_APP_PACKAGE_AUTHORITY};
+ DWORD sub_authorities[] = {SECURITY_CAPABILITY_BASE_RID, capability_rid};
+ return FromSubAuthorities(&capability_authority, 2, sub_authorities);
+}
+
+Sid Sid::FromNamedCapability(const wchar_t* capability_name) {
+ RtlDeriveCapabilitySidsFromNameFunction derive_capability_sids = nullptr;
+ ResolveNTFunctionPtr("RtlDeriveCapabilitySidsFromName",
+ &derive_capability_sids);
+ RtlInitUnicodeStringFunction init_unicode_string = nullptr;
+ ResolveNTFunctionPtr("RtlInitUnicodeString", &init_unicode_string);
+
+ if (!derive_capability_sids || !init_unicode_string)
+ return Sid();
+
+ if (!capability_name || ::wcslen(capability_name) == 0)
+ return Sid();
+
+ UNICODE_STRING name = {};
+ init_unicode_string(&name, capability_name);
+ Sid capability_sid;
+ Sid group_sid;
+
+ NTSTATUS status =
+ derive_capability_sids(&name, group_sid.sid_, capability_sid.sid_);
+ if (!NT_SUCCESS(status))
+ return Sid();
+
+ return capability_sid;
+}
+
+Sid Sid::FromSddlString(const wchar_t* sddl_sid) {
+ PSID converted_sid;
+ if (!::ConvertStringSidToSid(sddl_sid, &converted_sid))
+ return Sid();
+
+ return Sid(converted_sid);
+}
+
+Sid Sid::FromSubAuthorities(PSID_IDENTIFIER_AUTHORITY identifier_authority,
+ BYTE sub_authority_count,
+ PDWORD sub_authorities) {
+ Sid sid;
+ if (!::InitializeSid(sid.sid_, identifier_authority, sub_authority_count))
+ return Sid();
+
+ for (DWORD index = 0; index < sub_authority_count; ++index) {
+ PDWORD sub_authority = GetSidSubAuthority(sid.sid_, index);
+ *sub_authority = sub_authorities[index];
+ }
+ return sid;
+}
+
+Sid Sid::AllRestrictedApplicationPackages() {
+ SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_APP_PACKAGE_AUTHORITY};
+ DWORD sub_authorities[] = {SECURITY_APP_PACKAGE_BASE_RID,
+ SECURITY_BUILTIN_PACKAGE_ANY_RESTRICTED_PACKAGE};
+ return FromSubAuthorities(&package_authority, 2, sub_authorities);
+}
+
+Sid Sid::GenerateRandomSid() {
+ SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_NULL_SID_AUTHORITY};
+ DWORD sub_authorities[4] = {};
+ base::RandBytes(&sub_authorities, sizeof(sub_authorities));
+ return FromSubAuthorities(&package_authority, _countof(sub_authorities),
+ sub_authorities);
+}
+
+PSID Sid::GetPSID() const {
+ return const_cast<BYTE*>(sid_);
+}
+
+bool Sid::IsValid() const {
+ return !!::IsValidSid(GetPSID());
+}
+
+// Converts the SID to an SDDL format string.
+bool Sid::ToSddlString(std::wstring* sddl_string) const {
+ LPWSTR sid = nullptr;
+ if (!::ConvertSidToStringSid(GetPSID(), &sid))
+ return false;
+ std::unique_ptr<void, LocalFreeDeleter> sid_ptr(sid);
+ *sddl_string = sid;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sid.h b/security/sandbox/chromium/sandbox/win/src/sid.h
new file mode 100644
index 0000000000..745f471054
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sid.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SID_H_
+#define SANDBOX_SRC_SID_H_
+
+#include <windows.h>
+
+#include <string>
+
+namespace sandbox {
+
+// Known capabilities defined in Windows 8.
+enum WellKnownCapabilities {
+ kInternetClient,
+ kInternetClientServer,
+ kPrivateNetworkClientServer,
+ kPicturesLibrary,
+ kVideosLibrary,
+ kMusicLibrary,
+ kDocumentsLibrary,
+ kEnterpriseAuthentication,
+ kSharedUserCertificates,
+ kRemovableStorage,
+ kAppointments,
+ kContacts,
+ kMaxWellKnownCapability
+};
+
+// This class is used to hold and generate SIDS.
+class Sid {
+ public:
+ // As PSID is just a void* make it explicit.
+ explicit Sid(PSID sid);
+ // Constructors initializing the object with the SID passed.
+ // This is a converting constructor. It is not explicit.
+ Sid(const SID* sid);
+ Sid(WELL_KNOWN_SID_TYPE type);
+
+ // Create a Sid from an AppContainer capability name. The name can be
+ // completely arbitrary.
+ static Sid FromNamedCapability(const wchar_t* capability_name);
+ // Create a Sid from a known capability enumeration value. The Sids
+ // match with the list defined in Windows 8.
+ static Sid FromKnownCapability(WellKnownCapabilities capability);
+ // Create a Sid from a SDDL format string, such as S-1-1-0.
+ static Sid FromSddlString(const wchar_t* sddl_sid);
+ // Create a Sid from a set of sub authorities.
+ static Sid FromSubAuthorities(PSID_IDENTIFIER_AUTHORITY identifier_authority,
+ BYTE sub_authority_count,
+ PDWORD sub_authorities);
+ // Create the restricted all application packages sid.
+ static Sid AllRestrictedApplicationPackages();
+ // Generate a random SID value.
+ static Sid GenerateRandomSid();
+
+ // Returns sid_.
+ PSID GetPSID() const;
+
+ // Gets whether the sid is valid.
+ bool IsValid() const;
+
+ // Converts the SID to a SDDL format string.
+ bool ToSddlString(std::wstring* sddl_string) const;
+
+ private:
+ Sid();
+ BYTE sid_[SECURITY_MAX_SID_SIZE];
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SID_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sid_unittest.cc b/security/sandbox/chromium/sandbox/win/src/sid_unittest.cc
new file mode 100644
index 0000000000..81186cd0be
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sid_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the sid class.
+
+#include "sandbox/win/src/sid.h"
+
+#include <sddl.h>
+
+#include "base/win/atl.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool EqualSid(const Sid& sid, const ATL::CSid& compare_sid) {
+ if (!sid.IsValid())
+ return false;
+ return !!::EqualSid(sid.GetPSID(), const_cast<SID*>(compare_sid.GetPSID()));
+}
+
+bool EqualSid(const Sid& sid, const wchar_t* sddl_sid) {
+ PSID compare_sid;
+ if (!sid.IsValid())
+ return false;
+ if (!::ConvertStringSidToSid(sddl_sid, &compare_sid))
+ return false;
+ bool equal = !!::EqualSid(sid.GetPSID(), compare_sid);
+ ::LocalFree(compare_sid);
+ return equal;
+}
+
+struct KnownCapabilityTestEntry {
+ WellKnownCapabilities capability;
+ const wchar_t* sddl_sid;
+};
+
+struct NamedCapabilityTestEntry {
+ const wchar_t* capability_name;
+ const wchar_t* sddl_sid;
+};
+
+} // namespace
+
+// Tests the creation of a Sid.
+TEST(SidTest, Constructors) {
+ ATL::CSid sid_world = ATL::Sids::World();
+ PSID sid_world_pointer = const_cast<SID*>(sid_world.GetPSID());
+
+ // Check the SID* constructor.
+ Sid sid_sid_star(sid_world_pointer);
+ ASSERT_TRUE(EqualSid(sid_sid_star, sid_world));
+
+ // Check the copy constructor.
+ Sid sid_copy(sid_sid_star);
+ ASSERT_TRUE(EqualSid(sid_copy, sid_world));
+
+ Sid sid_sddl = Sid::FromSddlString(L"S-1-1-0");
+ ASSERT_TRUE(sid_sddl.IsValid());
+ ASSERT_TRUE(EqualSid(sid_sddl, sid_world));
+
+ Sid sid_sddl_invalid = Sid::FromSddlString(L"X-1-1-0");
+ ASSERT_FALSE(sid_sddl_invalid.IsValid());
+
+ Sid sid_sddl_empty = Sid::FromSddlString(L"");
+ ASSERT_FALSE(sid_sddl_empty.IsValid());
+
+ // Note that the WELL_KNOWN_SID_TYPE constructor is tested in the GetPSID
+ // test. AppContainer related constructors are tested in AppContainer.
+}
+
+// Tests the method GetPSID
+TEST(SidTest, GetPSID) {
+ // Check for non-null result;
+ ASSERT_NE(nullptr, Sid(::WinLocalSid).GetPSID());
+ ASSERT_NE(nullptr, Sid(::WinCreatorOwnerSid).GetPSID());
+ ASSERT_NE(nullptr, Sid(::WinBatchSid).GetPSID());
+
+ ASSERT_TRUE(EqualSid(Sid(::WinNullSid), ATL::Sids::Null()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinWorldSid), ATL::Sids::World()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinDialupSid), ATL::Sids::Dialup()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinNetworkSid), ATL::Sids::Network()));
+
+ ASSERT_TRUE(
+ EqualSid(Sid(::WinBuiltinAdministratorsSid), ATL::Sids::Admins()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinUsersSid), ATL::Sids::Users()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinGuestsSid), ATL::Sids::Guests()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinProxySid), ATL::Sids::Proxy()));
+}
+
+TEST(SidTest, KnownCapability) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+
+ Sid sid_invalid_well_known =
+ Sid::FromKnownCapability(kMaxWellKnownCapability);
+ EXPECT_FALSE(sid_invalid_well_known.IsValid());
+
+ const KnownCapabilityTestEntry capabilities[] = {
+ {kInternetClient, L"S-1-15-3-1"},
+ {kInternetClientServer, L"S-1-15-3-2"},
+ {kPrivateNetworkClientServer, L"S-1-15-3-3"},
+ {kPicturesLibrary, L"S-1-15-3-4"},
+ {kVideosLibrary, L"S-1-15-3-5"},
+ {kMusicLibrary, L"S-1-15-3-6"},
+ {kDocumentsLibrary, L"S-1-15-3-7"},
+ {kEnterpriseAuthentication, L"S-1-15-3-8"},
+ {kSharedUserCertificates, L"S-1-15-3-9"},
+ {kRemovableStorage, L"S-1-15-3-10"},
+ {kAppointments, L"S-1-15-3-11"},
+ {kContacts, L"S-1-15-3-12"},
+ };
+
+ for (auto capability : capabilities) {
+ EXPECT_TRUE(EqualSid(Sid::FromKnownCapability(capability.capability),
+ capability.sddl_sid))
+ << "Known Capability: " << capability.sddl_sid;
+ }
+}
+
+TEST(SidTest, NamedCapability) {
+ if (base::win::GetVersion() < base::win::Version::WIN10)
+ return;
+
+ Sid sid_nullptr = Sid::FromNamedCapability(nullptr);
+ EXPECT_FALSE(sid_nullptr.IsValid());
+
+ Sid sid_empty = Sid::FromNamedCapability(L"");
+ EXPECT_FALSE(sid_empty.IsValid());
+
+ const NamedCapabilityTestEntry capabilities[] = {
+ {L"internetClient", L"S-1-15-3-1"},
+ {L"internetClientServer", L"S-1-15-3-2"},
+ {L"registryRead",
+ L"S-1-15-3-1024-1065365936-1281604716-3511738428-"
+ "1654721687-432734479-3232135806-4053264122-3456934681"},
+ {L"lpacCryptoServices",
+ L"S-1-15-3-1024-3203351429-2120443784-2872670797-"
+ "1918958302-2829055647-4275794519-765664414-2751773334"},
+ {L"enterpriseAuthentication", L"S-1-15-3-8"},
+ {L"privateNetworkClientServer", L"S-1-15-3-3"}};
+
+ for (auto capability : capabilities) {
+ EXPECT_TRUE(EqualSid(Sid::FromNamedCapability(capability.capability_name),
+ capability.sddl_sid))
+ << "Named Capability: " << capability.sddl_sid;
+ }
+}
+
+TEST(SidTest, Sddl) {
+ Sid sid_sddl = Sid::FromSddlString(L"S-1-1-0");
+ ASSERT_TRUE(sid_sddl.IsValid());
+ std::wstring sddl_str;
+ ASSERT_TRUE(sid_sddl.ToSddlString(&sddl_str));
+ ASSERT_EQ(L"S-1-1-0", sddl_str);
+}
+
+TEST(SidTest, SubAuthorities) {
+ DWORD world_subauthorities[] = {0};
+ SID_IDENTIFIER_AUTHORITY world_authority = {SECURITY_WORLD_SID_AUTHORITY};
+ Sid sid_world =
+ Sid::FromSubAuthorities(&world_authority, 1, world_subauthorities);
+ ASSERT_TRUE(EqualSid(sid_world, ATL::Sids::World()));
+ ASSERT_TRUE(Sid::FromSubAuthorities(&world_authority, 0, nullptr).IsValid());
+
+ DWORD admin_subauthorities[] = {32, 544};
+ SID_IDENTIFIER_AUTHORITY nt_authority = {SECURITY_NT_AUTHORITY};
+ Sid sid_admin =
+ Sid::FromSubAuthorities(&nt_authority, 2, admin_subauthorities);
+ ASSERT_TRUE(EqualSid(sid_admin, ATL::Sids::Admins()));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc
new file mode 100644
index 0000000000..49e7cbe946
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All 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/signed_dispatcher.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/signed_interception.h"
+#include "sandbox/win/src/signed_policy.h"
+
+namespace sandbox {
+
+SignedDispatcher::SignedDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::NTCREATESECTION, {VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&SignedDispatcher::CreateSection)};
+
+ ipc_calls_.push_back(create_params);
+}
+
+bool SignedDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (service == IpcTag::NTCREATESECTION)
+ return INTERCEPT_NT(manager, NtCreateSection, CREATE_SECTION_ID, 32);
+ return false;
+}
+
+bool SignedDispatcher::CreateSection(IPCInfo* ipc, HANDLE file_handle) {
+ // Duplicate input handle from target to broker.
+ HANDLE local_file_handle = nullptr;
+ if (!::DuplicateHandle((*ipc->client_info).process, file_handle,
+ ::GetCurrentProcess(), &local_file_handle,
+ FILE_MAP_EXECUTE, false, 0)) {
+ return false;
+ }
+
+ base::win::ScopedHandle local_handle(local_file_handle);
+ std::wstring path;
+ if (!GetPathFromHandle(local_handle.Get(), &path))
+ return false;
+ const wchar_t* module_name = path.c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(module_name);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTCREATESECTION, params.GetBase());
+
+ // Return operation status on the IPC.
+ HANDLE section_handle = nullptr;
+ ipc->return_info.nt_status = SignedPolicy::CreateSectionAction(
+ result, *ipc->client_info, local_handle, &section_handle);
+ ipc->return_info.handle = section_handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h
new file mode 100644
index 0000000000..38435481f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SIGNED_DISPATCHER_H_
+#define SANDBOX_WIN_SRC_SIGNED_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include "base/macros.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 {
+
+// This class handles signed-binary related IPC calls.
+class SignedDispatcher : public Dispatcher {
+ public:
+ explicit SignedDispatcher(PolicyBase* policy_base);
+ ~SignedDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateSection in the target.
+ bool CreateSection(IPCInfo* ipc, HANDLE file_handle);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(SignedDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SIGNED_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_interception.cc b/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
new file mode 100644
index 0000000000..4f4310d605
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
@@ -0,0 +1,97 @@
+// Copyright 2019 The Chromium Authors. All 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/signed_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+NTSTATUS WINAPI
+TargetNtCreateSection(NtCreateSectionFunction orig_CreateSection,
+ PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle) {
+ do {
+ // The section only needs to have SECTION_MAP_EXECUTE, but the permissions
+ // vary depending on the OS. Windows 1903 and higher requests (SECTION_QUERY
+ // | SECTION_MAP_READ | SECTION_MAP_EXECUTE) while previous OS versions also
+ // request SECTION_MAP_WRITE. Just check for EXECUTE.
+ if (!(desired_access & SECTION_MAP_EXECUTE))
+ break;
+ if (object_attributes)
+ break;
+ if (maximum_size)
+ break;
+ if (section_page_protection != PAGE_EXECUTE)
+ break;
+ if (allocation_attributes != SEC_IMAGE)
+ break;
+
+ mozilla::sandboxing::LogBlocked("NtCreateSection");
+
+ // IPC must be fully started.
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> path;
+
+ if (!NtGetPathFromHandle(file_handle, &path))
+ break;
+
+ const wchar_t* const_name = path.get();
+
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(const_name);
+
+ // Check if this will be sent to the broker.
+ if (!QueryBroker(IpcTag::NTCREATESECTION, params.GetBase()))
+ break;
+
+ if (!ValidParameter(section_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = STATUS_INVALID_IMAGE_HASH;
+ SharedMemIPCClient ipc(memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTCREATESECTION, file_handle, &answer);
+
+ if (code != SBOX_ALL_OK)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *section_handle = answer.handle;
+ mozilla::sandboxing::LogAllowed("NtCreateSection");
+ return answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ // Fall back to the original API in all failure cases.
+ return orig_CreateSection(section_handle, desired_access, object_attributes,
+ maximum_size, section_page_protection,
+ allocation_attributes, file_handle);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_interception.h b/security/sandbox/chromium/sandbox/win/src/signed_interception.h
new file mode 100644
index 0000000000..a50ec38222
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_interception.h
@@ -0,0 +1,30 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SIGNED_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_SIGNED_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interceptor for NtCreateSection
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateSection(NtCreateSectionFunction orig_CreateSection,
+ PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SIGNED_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_policy.cc b/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
new file mode 100644
index 0000000000..ccf11defe7
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
@@ -0,0 +1,102 @@
+// Copyright 2019 The Chromium Authors. All 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/signed_policy.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+bool IsValidNtPath(const base::FilePath& name) {
+ UNICODE_STRING uni_name;
+ OBJECT_ATTRIBUTES obj_attr;
+ sandbox::InitObjectAttribs(name.value(), OBJ_CASE_INSENSITIVE, nullptr,
+ &obj_attr, &uni_name, nullptr);
+
+ NtQueryAttributesFileFunction NtQueryAttributesFile = nullptr;
+ ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
+ FILE_BASIC_INFORMATION file_info;
+ return NtQueryAttributesFile &&
+ NT_SUCCESS(NtQueryAttributesFile(&obj_attr, &file_info));
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool SignedPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ // Only support one semantic.
+ if (TargetPolicy::SIGNED_ALLOW_LOAD != semantics) {
+ return false;
+ }
+
+ base::FilePath file_path(name);
+ base::FilePath nt_filename;
+ std::wstring nt_path_name;
+ if (GetNtPathFromWin32Path(file_path.DirName().value().c_str(),
+ &nt_path_name)) {
+ base::FilePath nt_path(nt_path_name);
+ nt_filename = nt_path.Append(file_path.BaseName());
+ } else if (IsValidNtPath(file_path)) {
+ nt_filename = std::move(file_path);
+ } else {
+ return false;
+ }
+
+ // Create a rule to ASK_BROKER if name matches.
+ PolicyRule signed_policy(ASK_BROKER);
+ if (!signed_policy.AddStringMatch(
+ IF, NameBased::NAME, nt_filename.value().c_str(), CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::NTCREATESECTION, &signed_policy)) {
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS SignedPolicy::CreateSectionAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::win::ScopedHandle& local_file_handle,
+ HANDLE* target_section_handle) {
+ NtCreateSectionFunction NtCreateSection = nullptr;
+ ResolveNTFunctionPtr("NtCreateSection", &NtCreateSection);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // section as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE local_section_handle = nullptr;
+ NTSTATUS status = NtCreateSection(&local_section_handle,
+ SECTION_QUERY | SECTION_MAP_WRITE |
+ SECTION_MAP_READ | SECTION_MAP_EXECUTE,
+ nullptr, 0, PAGE_EXECUTE, SEC_IMAGE,
+ local_file_handle.Get());
+ if (!local_section_handle)
+ return status;
+
+ // Duplicate section handle back to the target.
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_section_handle,
+ client_info.process, target_section_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_policy.h b/security/sandbox/chromium/sandbox/win/src/signed_policy.h
new file mode 100644
index 0000000000..d22af4d4dc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_policy.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SIGNED_POLICY_H_
+#define SANDBOX_WIN_SRC_SIGNED_POLICY_H_
+
+#include <stdint.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to signed policy
+class SignedPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a request.
+ // client_info is the target process that is making the request and
+ // eval_result is the desired policy action to accomplish.
+ static NTSTATUS CreateSectionAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::win::ScopedHandle& local_file_handle,
+ HANDLE* section_handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SIGNED_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc
new file mode 100644
index 0000000000..d728e1d831
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/sync_policy.h"
+
+namespace sandbox {
+
+SyncDispatcher::SyncDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::CREATEEVENT, {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::CreateEvent)};
+
+ static const IPCCall open_params = {
+ {IpcTag::OPENEVENT, {WCHAR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::OpenEvent)};
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_params);
+}
+
+bool SyncDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (service == IpcTag::CREATEEVENT) {
+ return INTERCEPT_NT(manager, NtCreateEvent, CREATE_EVENT_ID, 24);
+ }
+ return (service == IpcTag::OPENEVENT) &&
+ INTERCEPT_NT(manager, NtOpenEvent, OPEN_EVENT_ID, 16);
+}
+
+bool SyncDispatcher::CreateEvent(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t event_type,
+ uint32_t initial_state) {
+ const wchar_t* event_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(event_name);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::CREATEEVENT, params.GetBase());
+ HANDLE handle = nullptr;
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = SyncPolicy::CreateEventAction(
+ result, *ipc->client_info, *name, event_type, initial_state, &handle);
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool SyncDispatcher::OpenEvent(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t desired_access) {
+ const wchar_t* event_name = name->c_str();
+
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(event_name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::OPENEVENT, params.GetBase());
+ HANDLE handle = nullptr;
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = SyncPolicy::OpenEventAction(
+ result, *ipc->client_info, *name, desired_access, &handle);
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h
new file mode 100644
index 0000000000..d4bc025a0b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SYNC_DISPATCHER_H_
+#define SANDBOX_SRC_SYNC_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles sync-related IPC calls.
+class SyncDispatcher : public Dispatcher {
+ public:
+ explicit SyncDispatcher(PolicyBase* policy_base);
+ ~SyncDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateEvent in the target.
+ bool CreateEvent(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t event_type,
+ uint32_t initial_state);
+
+ // Processes IPC requests coming from calls to OpenEvent in the target.
+ bool OpenEvent(IPCInfo* ipc, std::wstring* name, uint32_t desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(SyncDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_interception.cc b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
new file mode 100644
index 0000000000..9ec680e2fd
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+ResultCode ProxyCreateEvent(LPCWSTR name,
+ uint32_t initial_state,
+ EVENT_TYPE event_type,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(name);
+
+ if (!QueryBroker(IpcTag::CREATEEVENT, params.GetBase()))
+ return SBOX_ERROR_GENERIC;
+
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code = CrossCall(ipc, IpcTag::CREATEEVENT, name, event_type,
+ initial_state, answer);
+ return code;
+}
+
+ResultCode ProxyOpenEvent(LPCWSTR name,
+ uint32_t desired_access,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ if (!QueryBroker(IpcTag::OPENEVENT, params.GetBase()))
+ return SBOX_ERROR_GENERIC;
+
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::OPENEVENT, name, desired_access, answer);
+
+ return code;
+}
+
+NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NTSTATUS status =
+ orig_CreateEvent(event_handle, desired_access, object_attributes,
+ event_type, initial_state);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
+ 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;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes;
+ // The RootDirectory points to BaseNamedObjects. We can ignore it.
+ object_attribs_copy.RootDirectory = nullptr;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(&object_attribs_copy, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = status;
+ ResultCode code = ProxyCreateEvent(name.get(), initial_state, event_type,
+ memory, &answer);
+
+ if (code != SBOX_ALL_OK) {
+ status = answer.nt_status;
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtCreateEvent",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NTSTATUS status =
+ orig_OpenEvent(event_handle, desired_access, object_attributes);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
+ 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;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes;
+ // The RootDirectory points to BaseNamedObjects. We can ignore it.
+ object_attribs_copy.RootDirectory = nullptr;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(&object_attribs_copy, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = status;
+ ResultCode code =
+ ProxyOpenEvent(name.get(), desired_access, memory, &answer);
+
+ if (code != SBOX_ALL_OK) {
+ status = answer.nt_status;
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtOpenEvent",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_interception.h b/security/sandbox/chromium/sandbox/win/src/sync_interception.h
new file mode 100644
index 0000000000..c21e7ee7b2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_interception.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SYNC_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_SYNC_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+typedef NTSTATUS(WINAPI* NtCreateEventFunction)(
+ PHANDLE EventHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ EVENT_TYPE EventType,
+ BOOLEAN InitialState);
+
+typedef NTSTATUS(WINAPI* NtOpenEventFunction)(
+ PHANDLE EventHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+// Interceptors for NtCreateEvent/NtOpenEvent
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SYNC_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy.cc b/security/sandbox/chromium/sandbox/win/src/sync_policy.cc
new file mode 100644
index 0000000000..cdc34dd241
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_policy.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+// Provides functionality to resolve a symbolic link within the object
+// directory passed in.
+NTSTATUS ResolveSymbolicLink(const std::wstring& directory_name,
+ const std::wstring& name,
+ std::wstring* target) {
+ NtOpenDirectoryObjectFunction NtOpenDirectoryObject = nullptr;
+ ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject);
+
+ NtQuerySymbolicLinkObjectFunction NtQuerySymbolicLinkObject = nullptr;
+ ResolveNTFunctionPtr("NtQuerySymbolicLinkObject", &NtQuerySymbolicLinkObject);
+
+ NtOpenSymbolicLinkObjectFunction NtOpenSymbolicLinkObject = nullptr;
+ ResolveNTFunctionPtr("NtOpenSymbolicLinkObject", &NtOpenSymbolicLinkObject);
+
+ NtCloseFunction NtClose = nullptr;
+ ResolveNTFunctionPtr("NtClose", &NtClose);
+
+ OBJECT_ATTRIBUTES symbolic_link_directory_attributes = {};
+ UNICODE_STRING symbolic_link_directory_string = {};
+ InitObjectAttribs(directory_name, OBJ_CASE_INSENSITIVE, nullptr,
+ &symbolic_link_directory_attributes,
+ &symbolic_link_directory_string, nullptr);
+
+ HANDLE symbolic_link_directory = nullptr;
+ NTSTATUS status =
+ NtOpenDirectoryObject(&symbolic_link_directory, DIRECTORY_QUERY,
+ &symbolic_link_directory_attributes);
+ 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, nullptr);
+
+ HANDLE symbolic_link = nullptr;
+ status = NtOpenSymbolicLinkObject(&symbolic_link, GENERIC_READ,
+ &symbolic_link_attributes);
+ 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) {
+ CHECK(NT_SUCCESS(NtClose(symbolic_link)));
+ return status;
+ }
+
+ target_path.Length = 0;
+ target_path.MaximumLength = static_cast<USHORT>(target_length);
+ target_path.Buffer = new wchar_t[target_path.MaximumLength + 1];
+ status =
+ NtQuerySymbolicLinkObject(symbolic_link, &target_path, &target_length);
+ if (NT_SUCCESS(status))
+ target->assign(target_path.Buffer, target_length);
+
+ CHECK(NT_SUCCESS(NtClose(symbolic_link)));
+ delete[] target_path.Buffer;
+ return status;
+}
+
+NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) {
+ static HANDLE base_named_objects_handle = nullptr;
+ if (base_named_objects_handle) {
+ *directory = base_named_objects_handle;
+ return STATUS_SUCCESS;
+ }
+
+ NtOpenDirectoryObjectFunction NtOpenDirectoryObject = nullptr;
+ ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject);
+
+ DWORD session_id = 0;
+ ProcessIdToSessionId(::GetCurrentProcessId(), &session_id);
+
+ std::wstring base_named_objects_path;
+
+ NTSTATUS status = ResolveSymbolicLink(L"\\Sessions\\BNOLINKS",
+ base::StringPrintf(L"%d", session_id),
+ &base_named_objects_path);
+ if (!NT_SUCCESS(status)) {
+ DLOG(ERROR) << "Failed to resolve BaseNamedObjects path. Error: " << status;
+ return status;
+ }
+
+ UNICODE_STRING directory_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(base_named_objects_path, OBJ_CASE_INSENSITIVE, nullptr,
+ &object_attributes, &directory_name, nullptr);
+ status = NtOpenDirectoryObject(&base_named_objects_handle,
+ DIRECTORY_ALL_ACCESS, &object_attributes);
+ if (NT_SUCCESS(status))
+ *directory = base_named_objects_handle;
+ return status;
+}
+
+bool SyncPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ if (TargetPolicy::EVENTS_ALLOW_ANY != semantics &&
+ TargetPolicy::EVENTS_ALLOW_READONLY != semantics) {
+ // Other flags are not valid for sync policy yet.
+ NOTREACHED();
+ return false;
+ }
+
+ // Add the open rule.
+ EvalResult result = ASK_BROKER;
+ PolicyRule open(result);
+
+ if (!open.AddStringMatch(IF, OpenEventParams::NAME, name, CASE_INSENSITIVE))
+ return false;
+
+ if (TargetPolicy::EVENTS_ALLOW_READONLY == semantics) {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write.
+ uint32_t allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL;
+ uint32_t restricted_flags = ~allowed_flags;
+ open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND);
+ }
+
+ if (!policy->AddRule(IpcTag::OPENEVENT, &open))
+ return false;
+
+ // If it's not a read only, add the create rule.
+ if (TargetPolicy::EVENTS_ALLOW_READONLY != semantics) {
+ PolicyRule create(result);
+ if (!create.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE))
+ return false;
+
+ if (!policy->AddRule(IpcTag::CREATEEVENT, &create))
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t event_type,
+ uint32_t initial_state,
+ HANDLE* handle) {
+ NtCreateEventFunction NtCreateEvent = nullptr;
+ ResolveNTFunctionPtr("NtCreateEvent", &NtCreateEvent);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE object_directory = nullptr;
+ NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory);
+ if (status != STATUS_SUCCESS)
+ return status;
+
+ UNICODE_STRING unicode_event_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory,
+ &object_attributes, &unicode_event_name, nullptr);
+
+ HANDLE local_handle = nullptr;
+ status = NtCreateEvent(&local_handle, EVENT_ALL_ACCESS, &object_attributes,
+ static_cast<EVENT_TYPE>(event_type),
+ static_cast<BOOLEAN>(initial_state != 0));
+ if (!local_handle)
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+NTSTATUS SyncPolicy::OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t desired_access,
+ HANDLE* handle) {
+ NtOpenEventFunction NtOpenEvent = nullptr;
+ ResolveNTFunctionPtr("NtOpenEvent", &NtOpenEvent);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // event as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE object_directory = nullptr;
+ NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory);
+ if (status != STATUS_SUCCESS)
+ return status;
+
+ UNICODE_STRING unicode_event_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory,
+ &object_attributes, &unicode_event_name, nullptr);
+
+ HANDLE local_handle = nullptr;
+ status = NtOpenEvent(&local_handle, desired_access, &object_attributes);
+ if (!local_handle)
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy.h b/security/sandbox/chromium/sandbox/win/src/sync_policy.h
new file mode 100644
index 0000000000..2eb4124a82
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SYNC_POLICY_H__
+#define SANDBOX_SRC_SYNC_POLICY_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to sync policy
+class SyncPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for sync calls, in particular open or create actions.
+ // name is the sync object name, semantics is the desired semantics for the
+ // open or create and policy is the policy generator to which the rules are
+ // going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a request.
+ // client_info is the target process that is making the request and
+ // eval_result is the desired policy action to accomplish.
+ static NTSTATUS CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t event_type,
+ uint32_t initial_state,
+ HANDLE* handle);
+ static NTSTATUS OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t desired_access,
+ HANDLE* handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc
new file mode 100644
index 0000000000..97f60d359f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_policy_test.h"
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ DWORD desired_access = SYNCHRONIZE;
+ if (L'f' == argv[0][0])
+ desired_access = EVENT_ALL_ACCESS;
+
+ base::win::ScopedHandle event_open(
+ ::OpenEvent(desired_access, false, argv[1]));
+ DWORD error_open = ::GetLastError();
+
+ if (event_open.IsValid())
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error_open || ERROR_BAD_PATHNAME == error_open ||
+ ERROR_FILE_NOT_FOUND == error_open)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t** argv) {
+ if (argc < 2 || argc > 3)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ wchar_t* event_name = nullptr;
+ if (3 == argc)
+ event_name = argv[2];
+
+ bool manual_reset = false;
+ bool initial_state = false;
+ if (L't' == argv[0][0])
+ manual_reset = true;
+ if (L't' == argv[1][0])
+ initial_state = true;
+
+ base::win::ScopedHandle event_create(
+ ::CreateEvent(nullptr, manual_reset, initial_state, event_name));
+ DWORD error_create = ::GetLastError();
+ base::win::ScopedHandle event_open;
+ if (event_name)
+ event_open.Set(::OpenEvent(EVENT_ALL_ACCESS, false, event_name));
+
+ if (event_create.IsValid()) {
+ DWORD wait = ::WaitForSingleObject(event_create.Get(), 0);
+ if (initial_state && WAIT_OBJECT_0 != wait)
+ return SBOX_TEST_FAILED;
+
+ if (!initial_state && WAIT_TIMEOUT != wait)
+ return SBOX_TEST_FAILED;
+ }
+
+ if (event_name) {
+ // Both event_open and event_create have to be valid.
+ if (event_open.IsValid() && event_create.IsValid())
+ return SBOX_TEST_SUCCEEDED;
+
+ if ((event_open.IsValid() && !event_create.IsValid()) ||
+ (!event_open.IsValid() && event_create.IsValid())) {
+ return SBOX_TEST_FAILED;
+ }
+ } else {
+ // Only event_create has to be valid.
+ if (event_create.Get())
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ if (ERROR_ACCESS_DENIED == error_create || ERROR_BAD_PATHNAME == error_create)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+// Tests the creation of events using all the possible combinations.
+TEST(SyncPolicyTest, TestEvent) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"test1"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"test2"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f test2"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t test2"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test4"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test4"));
+}
+
+// Tests opening events with read only access.
+TEST(SyncPolicyTest, TestEventReadOnly) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test1"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test2"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test5"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test6"));
+
+ base::win::ScopedHandle handle1(
+ ::CreateEvent(nullptr, false, false, L"test1"));
+ base::win::ScopedHandle handle2(
+ ::CreateEvent(nullptr, false, false, L"test2"));
+ base::win::ScopedHandle handle3(
+ ::CreateEvent(nullptr, false, false, L"test3"));
+ base::win::ScopedHandle handle4(
+ ::CreateEvent(nullptr, false, false, L"test4"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open s test2"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open s test4"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test5"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test6"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test5"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test6"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy_test.h b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.h
new file mode 100644
index 0000000000..4f354b35be
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
+#define SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
+
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+// Opens the named event received on argv[1]. The requested access is
+// EVENT_ALL_ACCESS if argv[0] starts with 'f', or SYNCHRONIZE otherwise.
+SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/target_interceptions.cc b/security/sandbox/chromium/sandbox/win/src/target_interceptions.cc
new file mode 100644
index 0000000000..1b467814c6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_interceptions.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_interceptions.h"
+
+#include "base/win/static_constants.h"
+#include "sandbox/win/src/interception_agent.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+const char KERNEL32_DLL_NAME[] = "kernel32.dll";
+
+enum SectionLoadState {
+ kBeforeKernel32,
+ kAfterKernel32,
+};
+
+// Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is
+// required for this dll, this functions patches it.
+NTSTATUS WINAPI
+TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
+ HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect) {
+ NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits,
+ commit_size, offset, view_size, inherit,
+ allocation_type, protect);
+ static SectionLoadState s_state = kBeforeKernel32;
+
+ do {
+ if (!NT_SUCCESS(ret))
+ break;
+
+ if (!IsSameProcess(process))
+ break;
+
+ // Only check for verifier.dll or kernel32.dll loading if we haven't moved
+ // past that state yet.
+ if (s_state == kBeforeKernel32) {
+ const char* ansi_module_name =
+ GetAnsiImageInfoFromModule(reinterpret_cast<HMODULE>(*base));
+
+ // _strnicmp below may hit read access violations for some sections. We
+ // find what looks like a valid export directory for a PE module but the
+ // pointer to the module name will be pointing to invalid memory.
+ __try {
+ // Don't initialize the heap if verifier.dll is being loaded. This
+ // indicates Application Verifier is enabled and we should wait until
+ // the next module is loaded.
+ if (ansi_module_name &&
+ (g_nt._strnicmp(
+ ansi_module_name, base::win::kApplicationVerifierDllName,
+ g_nt.strlen(base::win::kApplicationVerifierDllName) + 1) == 0))
+ break;
+
+ if (ansi_module_name &&
+ (g_nt._strnicmp(ansi_module_name, KERNEL32_DLL_NAME,
+ sizeof(KERNEL32_DLL_NAME)) == 0)) {
+ s_state = kAfterKernel32;
+ }
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+ }
+
+ if (!InitHeap())
+ break;
+
+ if (!IsValidImageSection(section, base, offset, view_size))
+ break;
+
+ UINT image_flags;
+ UNICODE_STRING* module_name =
+ GetImageInfoFromModule(reinterpret_cast<HMODULE>(*base), &image_flags);
+ UNICODE_STRING* file_name = GetBackingFilePath(*base);
+
+ if ((!module_name) && (image_flags & MODULE_HAS_CODE)) {
+ // If the module has no exports we retrieve the module name from the
+ // full path of the mapped section.
+ module_name = ExtractModuleName(file_name);
+ }
+
+ InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
+
+ if (agent) {
+ if (!agent->OnDllLoad(file_name, module_name, *base)) {
+ // Interception agent is demanding to un-map the module.
+ g_nt.UnmapViewOfSection(process, *base);
+ *base = nullptr;
+ ret = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (module_name)
+ operator delete(module_name, NT_ALLOC);
+
+ if (file_name)
+ operator delete(file_name, NT_ALLOC);
+
+ } while (false);
+
+ return ret;
+}
+
+NTSTATUS WINAPI
+TargetNtUnmapViewOfSection(NtUnmapViewOfSectionFunction orig_UnmapViewOfSection,
+ HANDLE process,
+ PVOID base) {
+ NTSTATUS ret = orig_UnmapViewOfSection(process, base);
+
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ if (!IsSameProcess(process))
+ return ret;
+
+ InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
+
+ if (agent)
+ agent->OnDllUnload(base);
+
+ return ret;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/target_interceptions.h b/security/sandbox/chromium/sandbox/win/src/target_interceptions.h
new file mode 100644
index 0000000000..fac1c65c27
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_interceptions.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_TARGET_INTERCEPTIONS_H_
+#define SANDBOX_WIN_SRC_TARGET_INTERCEPTIONS_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
+ HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect);
+
+// Interception of NtUnmapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being unloaded, so we can clean up our interceptions.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtUnmapViewOfSection(NtUnmapViewOfSectionFunction orig_UnmapViewOfSection,
+ HANDLE process,
+ PVOID base);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_TARGET_INTERCEPTIONS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.cc b/security/sandbox/chromium/sandbox/win/src/target_process.cc
new file mode 100644
index 0000000000..4661267d1e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_process.cc
@@ -0,0 +1,393 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_process.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/free_deleter.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/process/environment_internal.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.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/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/sid.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
+ if (!source || !size)
+ return;
+ memcpy(dest, source, size);
+ sandbox::PolicyGlobal* policy =
+ reinterpret_cast<sandbox::PolicyGlobal*>(dest);
+
+ size_t offset = reinterpret_cast<size_t>(source);
+
+ for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
+ size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
+ if (buffer) {
+ buffer -= offset;
+ policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
+ }
+ }
+}
+
+bool GetTokenAppContainerSid(HANDLE token_handle,
+ std::unique_ptr<Sid>* app_container_sid) {
+ std::vector<char> app_container_info(sizeof(TOKEN_APPCONTAINER_INFORMATION) +
+ SECURITY_MAX_SID_SIZE);
+ DWORD return_length;
+
+ if (!::GetTokenInformation(
+ token_handle, TokenAppContainerSid, app_container_info.data(),
+ base::checked_cast<DWORD>(app_container_info.size()),
+ &return_length)) {
+ return false;
+ }
+
+ PTOKEN_APPCONTAINER_INFORMATION info =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(
+ app_container_info.data());
+ if (!info->TokenAppContainer)
+ return false;
+ *app_container_sid = std::unique_ptr<Sid>(new Sid(info->TokenAppContainer));
+ return true;
+}
+
+bool GetProcessAppContainerSid(HANDLE process,
+ std::unique_ptr<Sid>* app_container_sid) {
+ HANDLE token_handle;
+ if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle))
+ return false;
+ base::win::ScopedHandle process_token(token_handle);
+
+ return GetTokenAppContainerSid(process_token.Get(), app_container_sid);
+}
+
+bool GetAppContainerImpersonationToken(
+ HANDLE process,
+ HANDLE initial_token,
+ const std::vector<Sid>& capabilities,
+ base::win::ScopedHandle* impersonation_token) {
+ std::unique_ptr<Sid> app_container_sid;
+ if (!GetProcessAppContainerSid(process, &app_container_sid)) {
+ return false;
+ }
+ SecurityCapabilities security_caps(*app_container_sid, capabilities);
+ return CreateLowBoxToken(initial_token, IMPERSONATION, &security_caps,
+ nullptr, 0, impersonation_token) == ERROR_SUCCESS;
+}
+
+} // namespace
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size;
+SANDBOX_INTERCEPT size_t g_shared_policy_size;
+
+TargetProcess::TargetProcess(base::win::ScopedHandle initial_token,
+ base::win::ScopedHandle lockdown_token,
+ HANDLE job,
+ ThreadProvider* thread_pool,
+ const std::vector<Sid>& impersonation_capabilities)
+ // 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_(std::move(lockdown_token)),
+ initial_token_(std::move(initial_token)),
+ job_(job),
+ thread_pool_(thread_pool),
+ base_address_(nullptr),
+ impersonation_capabilities_(impersonation_capabilities) {}
+
+TargetProcess::~TargetProcess() {
+ // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
+ // will take effect only when the context changes. As far as the testing went,
+ // this wait was enough to switch context and kill the processes in the job.
+ // If this process is already dead, the function will return without waiting.
+ // For now, this wait is there only to do a best effort to prevent some leaks
+ // from showing up in purify.
+ if (sandbox_process_info_.IsValid()) {
+ ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
+ // Terminate the process if it's still alive, as its IPC server is going
+ // away. 1 is RESULT_CODE_KILLED.
+ ::TerminateProcess(sandbox_process_info_.process_handle(), 1);
+ }
+
+ // ipc_server_ references our process handle, so make sure the former is shut
+ // down before the latter is closed (by ScopedProcessInformation).
+ ipc_server_.reset();
+}
+
+// Creates the target (child) process suspended and assigns it to the job
+// object.
+ResultCode TargetProcess::Create(
+ const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info,
+ base::EnvironmentMap& env_changes,
+ DWORD* win_error) {
+ exe_name_.reset(_wcsdup(exe_path));
+
+ // the command line needs to be writable by CreateProcess().
+ std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
+
+ // Start the target process suspended.
+ DWORD flags =
+ CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
+
+ if (startup_info.has_extended_startup_info())
+ flags |= EXTENDED_STARTUPINFO_PRESENT;
+
+ if (job_ && base::win::GetVersion() < base::win::Version::WIN8) {
+ // Windows 8 implements nested jobs, but for older systems we need to
+ // break out of any job we're in to enforce our restrictions.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ LPTCH original_environment = GetEnvironmentStrings();
+ base::NativeEnvironmentString new_environment =
+ base::internal::AlterEnvironment(original_environment, env_changes);
+ // Ignore return value? What can we do?
+ FreeEnvironmentStrings(original_environment);
+ LPVOID new_env_ptr = (void*)new_environment.data();
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(),
+ nullptr, // No security attribute.
+ nullptr, // No thread attribute.
+ inherit_handles, flags,
+ new_env_ptr,
+ nullptr, // Use current directory of the caller.
+ startup_info.startup_info(),
+ &temp_process_info)) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_CREATE_PROCESS;
+ }
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
+ if (job_) {
+ // Assign the suspended target to the windows job object.
+ if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
+ *win_error = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_ASSIGN_PROCESS_TO_JOB_OBJECT;
+ }
+ }
+
+ if (initial_token_.IsValid()) {
+ HANDLE impersonation_token = initial_token_.Get();
+ base::win::ScopedHandle app_container_token;
+ if (GetAppContainerImpersonationToken(
+ process_info.process_handle(), impersonation_token,
+ impersonation_capabilities_, &app_container_token)) {
+ impersonation_token = app_container_token.Get();
+ }
+
+ // Change the token of the main thread of the new process for the
+ // impersonation token with more rights. This allows the target to start;
+ // otherwise it will crash too early for us to help.
+ HANDLE temp_thread = process_info.thread_handle();
+ if (!::SetThreadToken(&temp_thread, impersonation_token)) {
+ *win_error = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_SET_THREAD_TOKEN;
+ }
+ initial_token_.Close();
+ }
+
+ if (!target_info->DuplicateFrom(process_info)) {
+ *win_error = ::GetLastError(); // This may or may not be correct.
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_DUPLICATE_TARGET_INFO;
+ }
+
+ base_address_ = GetProcessBaseAddress(process_info.process_handle());
+ DCHECK(base_address_);
+ if (!base_address_) {
+ *win_error = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_CANNOT_FIND_BASE_ADDRESS;
+ }
+
+ sandbox_process_info_.Set(process_info.Take());
+ return SBOX_ALL_OK;
+}
+
+ResultCode TargetProcess::TransferVariable(const char* name,
+ void* address,
+ size_t size) {
+ if (!sandbox_process_info_.IsValid())
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ void* child_var = address;
+
+#if SANDBOX_EXPORTS
+ HMODULE module = ::LoadLibrary(exe_name_.get());
+ if (!module)
+ return SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE;
+
+ child_var = reinterpret_cast<void*>(::GetProcAddress(module, name));
+ ::FreeLibrary(module);
+
+ if (!child_var)
+ return SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS;
+
+ size_t offset =
+ reinterpret_cast<char*>(child_var) - reinterpret_cast<char*>(module);
+ child_var = reinterpret_cast<char*>(MainModule()) + offset;
+#endif
+
+ SIZE_T written;
+ if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), child_var,
+ address, size, &written))
+ return SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE;
+
+ if (written != size)
+ return SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE;
+
+ return SBOX_ALL_OK;
+}
+
+// Construct the IPC server and the IPC dispatcher. When the target does
+// an IPC it will eventually call the dispatcher.
+ResultCode TargetProcess::Init(Dispatcher* ipc_dispatcher,
+ void* policy,
+ uint32_t shared_IPC_size,
+ uint32_t shared_policy_size,
+ DWORD* win_error) {
+ // We need to map the shared memory on the target. This is necessary for
+ // any IPC that needs to take place, even if the target has not yet hit
+ // the main( ) function or even has initialized the CRT. So here we set
+ // the handle to the shared section. The target on the first IPC must do
+ // the rest, which boils down to calling MapViewofFile()
+
+ // We use this single memory pool for IPC and for policy.
+ DWORD shared_mem_size =
+ static_cast<DWORD>(shared_IPC_size + shared_policy_size);
+ shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
+ PAGE_READWRITE | SEC_COMMIT, 0,
+ shared_mem_size, nullptr));
+ if (!shared_section_.IsValid()) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_CREATE_FILE_MAPPING;
+ }
+
+ void* shared_memory = ::MapViewOfFile(
+ shared_section_.Get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
+ if (!shared_memory) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_MAP_VIEW_OF_SHARED_SECTION;
+ }
+
+ CopyPolicyToTarget(policy, shared_policy_size,
+ reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
+
+ ResultCode ret;
+ // Set the global variables in the target. These are not used on the broker.
+ g_shared_IPC_size = shared_IPC_size;
+ ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
+ sizeof(g_shared_IPC_size));
+ g_shared_IPC_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+ g_shared_policy_size = shared_policy_size;
+ ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
+ sizeof(g_shared_policy_size));
+ g_shared_policy_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+
+ ipc_server_.reset(new SharedMemIPCServer(
+ sandbox_process_info_.process_handle(),
+ sandbox_process_info_.process_id(), thread_pool_, ipc_dispatcher));
+
+ if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
+ return SBOX_ERROR_NO_SPACE;
+
+ 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(),
+ &target_shared_section, access, false, 0)) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_DUPLICATE_SHARED_SECTION;
+ }
+
+ g_shared_section = target_shared_section;
+ ret = TransferVariable("g_shared_section", &g_shared_section,
+ sizeof(g_shared_section));
+ g_shared_section = nullptr;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+
+ // After this point we cannot use this handle anymore.
+ ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
+
+ return SBOX_ALL_OK;
+}
+
+void TargetProcess::Terminate() {
+ if (!sandbox_process_info_.IsValid())
+ return;
+
+ ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
+}
+
+ResultCode TargetProcess::AssignLowBoxToken(
+ const base::win::ScopedHandle& token) {
+ if (!token.IsValid())
+ return SBOX_ALL_OK;
+ PROCESS_ACCESS_TOKEN process_access_token = {};
+ process_access_token.token = token.Get();
+
+ NtSetInformationProcess SetInformationProcess = nullptr;
+ ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess);
+
+ NTSTATUS status = SetInformationProcess(
+ sandbox_process_info_.process_handle(),
+ static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken),
+ &process_access_token, sizeof(process_access_token));
+ if (!NT_SUCCESS(status)) {
+ ::SetLastError(GetLastErrorFromNtStatus(status));
+ return SBOX_ERROR_SET_LOW_BOX_TOKEN;
+ }
+ return SBOX_ALL_OK;
+}
+
+TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
+ TargetProcess* target =
+ new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(),
+ nullptr, nullptr, std::vector<Sid>());
+ PROCESS_INFORMATION process_info = {};
+ process_info.hProcess = process;
+ target->sandbox_process_info_.Set(process_info);
+ target->base_address_ = base_address;
+ return target;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.h b/security/sandbox/chromium/sandbox/win/src/target_process.h
new file mode 100644
index 0000000000..c489ed7bc1
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_process.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_TARGET_PROCESS_H_
+#define SANDBOX_WIN_SRC_TARGET_PROCESS_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/environment.h"
+#include "base/memory/free_deleter.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace base {
+namespace win {
+
+class StartupInformation;
+
+} // namespace win
+} // namespace base
+
+namespace sandbox {
+
+class SharedMemIPCServer;
+class Sid;
+class ThreadProvider;
+
+// TargetProcess models a target instance (child process). Objects of this
+// class are owned by the Policy used to create them.
+class TargetProcess {
+ public:
+ // The constructor takes ownership of |initial_token| and |lockdown_token|
+ TargetProcess(base::win::ScopedHandle initial_token,
+ base::win::ScopedHandle lockdown_token,
+ HANDLE job,
+ ThreadProvider* thread_pool,
+ const std::vector<Sid>& impersonation_capabilities);
+ ~TargetProcess();
+
+ // TODO(cpu): Currently there does not seem to be a reason to implement
+ // reference counting for this class since is internal, but kept the
+ // the same interface so the interception framework does not need to be
+ // touched at this point.
+ void AddRef() {}
+ void Release() {}
+
+ // Creates the new target process. The process is created suspended.
+ ResultCode Create(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info,
+ base::EnvironmentMap& env_map,
+ DWORD* win_error);
+
+ // Assign a new lowbox token to the process post creation. The process
+ // must still be in its initial suspended state, however this still
+ // might fail in the presence of third-party software.
+ ResultCode AssignLowBoxToken(const base::win::ScopedHandle& token);
+
+ // Destroys the target process.
+ void Terminate();
+
+ // Creates the IPC objects such as the BrokerDispatcher and the
+ // IPC server. The IPC server uses the services of the thread_pool.
+ ResultCode Init(Dispatcher* ipc_dispatcher,
+ void* policy,
+ uint32_t shared_IPC_size,
+ uint32_t shared_policy_size,
+ DWORD* win_error);
+
+ // Returns the handle to the target process.
+ HANDLE Process() const { return sandbox_process_info_.process_handle(); }
+
+ // Returns the handle to the job object that the target process belongs to.
+ HANDLE Job() const { return job_; }
+
+ // Returns the address of the target main exe. This is used by the
+ // interceptions framework.
+ HMODULE MainModule() const {
+ return reinterpret_cast<HMODULE>(base_address_);
+ }
+
+ // Returns the name of the executable.
+ const wchar_t* Name() const { return exe_name_.get(); }
+
+ // Returns the process id.
+ DWORD ProcessId() const { return sandbox_process_info_.process_id(); }
+
+ // Returns the handle to the main thread.
+ HANDLE MainThread() const { return sandbox_process_info_.thread_handle(); }
+
+ // Transfers variable at |address| of |size| bytes from broker to target.
+ ResultCode TransferVariable(const char* name, void* address, size_t size);
+
+ private:
+ // Details of the target process.
+ base::win::ScopedProcessInformation sandbox_process_info_;
+ // The token associated with the process. It provides the core of the
+ // sbox security.
+ base::win::ScopedHandle lockdown_token_;
+ // The token given to the initial thread so that the target process can
+ // start. It has more powers than the lockdown_token.
+ base::win::ScopedHandle initial_token_;
+ // Kernel handle to the shared memory used by the IPC server.
+ base::win::ScopedHandle shared_section_;
+ // Job object containing the target process.
+ HANDLE job_;
+ // Reference to the IPC subsystem.
+ std::unique_ptr<SharedMemIPCServer> ipc_server_;
+ // Provides the threads used by the IPC. This class does not own this pointer.
+ ThreadProvider* thread_pool_;
+ // Base address of the main executable
+ void* base_address_;
+ // Full name of the target executable.
+ std::unique_ptr<wchar_t, base::FreeDeleter> exe_name_;
+ /// List of capability sids for use when impersonating in an AC process.
+ std::vector<Sid> impersonation_capabilities_;
+
+ // Function used for testing.
+ friend TargetProcess* MakeTestTargetProcess(HANDLE process,
+ HMODULE base_address);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TargetProcess);
+};
+
+// Creates a mock TargetProcess used for testing interceptions.
+// TODO(cpu): It seems that this method is not going to be used anymore.
+TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_TARGET_PROCESS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc
new file mode 100644
index 0000000000..a80e0106ef
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_services.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_services.h"
+
+#include <new>
+
+#include <process.h>
+#include <stdint.h>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/handle_interception.h"
+#include "sandbox/win/src/heap_helper.h"
+#include "sandbox/win/src/line_break_interception.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+namespace sandbox {
+namespace {
+
+// Flushing a cached key is triggered by just opening the key and closing the
+// resulting handle. RegDisablePredefinedCache() is the documented way to flush
+// HKCU so do not use it with this function.
+bool FlushRegKey(HKEY root) {
+ HKEY key;
+ if (ERROR_SUCCESS ==
+ ::RegOpenKeyExW(root, nullptr, 0, MAXIMUM_ALLOWED, &key)) {
+ if (ERROR_SUCCESS != ::RegCloseKey(key))
+ return false;
+ }
+ return true;
+}
+
+// This function forces advapi32.dll to release some internally cached handles
+// that were made during calls to RegOpenkey and RegOpenKeyEx if it is called
+// with a more restrictive token. Returns true if the flushing is succesful
+// although this behavior is undocumented and there is no guarantee that in
+// fact this will happen in future versions of windows.
+bool FlushCachedRegHandles() {
+ return (FlushRegKey(HKEY_LOCAL_MACHINE) && FlushRegKey(HKEY_CLASSES_ROOT) &&
+ FlushRegKey(HKEY_USERS));
+}
+
+// Cleans up this process if CSRSS will be disconnected, as this disconnection
+// is not supported Windows behavior.
+// Currently, this step requires closing a heap that this shared with csrss.exe.
+// Closing the ALPC Port handle to csrss.exe leaves this heap in an invalid
+// state. This causes problems if anyone enumerates the heap.
+bool CsrssDisconnectCleanup() {
+ HANDLE csr_port_heap = FindCsrPortHeap();
+ if (!csr_port_heap) {
+ DLOG(ERROR) << "Failed to find CSR Port heap handle";
+ return false;
+ }
+ HeapDestroy(csr_port_heap);
+ return true;
+}
+
+// Used by EnumSystemLocales for warming up.
+static BOOL CALLBACK EnumLocalesProcEx(LPWSTR lpLocaleString,
+ DWORD dwFlags,
+ LPARAM lParam) {
+ return TRUE;
+}
+
+// Additional warmup done just when CSRSS is being disconnected.
+bool CsrssDisconnectWarmup() {
+ return ::EnumSystemLocalesEx(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
+}
+
+// Checks if we have handle entries pending and runs the closer.
+// Updates is_csrss_connected based on which handle types are closed.
+bool CloseOpenHandles(bool* is_csrss_connected) {
+ if (HandleCloserAgent::NeedsHandlesClosed()) {
+ HandleCloserAgent handle_closer;
+ handle_closer.InitializeHandlesToClose(is_csrss_connected);
+ if (!*is_csrss_connected) {
+ if (!CsrssDisconnectWarmup() || !CsrssDisconnectCleanup()) {
+ return false;
+ }
+ }
+ if (!handle_closer.CloseHandles())
+ return false;
+ }
+ return true;
+}
+
+// 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();
+ wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0};
+ return (0 != ::GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH));
+}
+
+// Used as storage for g_target_services, because other allocation facilities
+// are not available early. We can't use a regular function static because on
+// VS2015, because the CRT tries to acquire a lock to guard initialization, but
+// this code runs before the CRT is initialized.
+char g_target_services_memory[sizeof(TargetServicesBase)];
+TargetServicesBase* g_target_services = nullptr;
+
+} // namespace
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level =
+ INTEGRITY_LEVEL_LAST;
+SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations = 0;
+
+TargetServicesBase::TargetServicesBase() {}
+
+ResultCode TargetServicesBase::Init() {
+ process_state_.SetInitCalled();
+ return SBOX_ALL_OK;
+}
+
+// Failure here is a breach of security so the process is terminated.
+void TargetServicesBase::LowerToken() {
+ if (ERROR_SUCCESS !=
+ SetProcessIntegrityLevel(g_shared_delayed_integrity_level))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY);
+ process_state_.SetRevertedToSelf();
+ // If the client code as called RegOpenKey, advapi32.dll has cached some
+ // handles. The following code gets rid of them.
+ if (!::RevertToSelf())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN);
+ if (!FlushCachedRegHandles())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES);
+ if (ERROR_SUCCESS != ::RegDisablePredefinedCache())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE);
+ if (!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))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION);
+}
+
+ProcessState* TargetServicesBase::GetState() {
+ return &process_state_;
+}
+
+TargetServicesBase* TargetServicesBase::GetInstance() {
+ // Leak on purpose TargetServicesBase.
+ if (!g_target_services)
+ g_target_services = new (g_target_services_memory) TargetServicesBase;
+ return g_target_services;
+}
+
+// The broker services a 'test' IPC service with the PING tag.
+bool TargetServicesBase::TestIPCPing(int version) {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ return false;
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ if (1 == version) {
+ uint32_t tick1 = ::GetTickCount();
+ uint32_t cookie = 717115;
+ ResultCode code = CrossCall(ipc, IpcTag::PING1, cookie, &answer);
+
+ if (SBOX_ALL_OK != code) {
+ return false;
+ }
+ // We should get two extended returns values from the IPC, one is the
+ // tick count on the broker and the other is the cookie times two.
+ if ((answer.extended_count != 2)) {
+ return false;
+ }
+ // We test the first extended answer to be within the bounds of the tick
+ // count only if there was no tick count wraparound.
+ uint32_t tick2 = ::GetTickCount();
+ if (tick2 >= tick1) {
+ if ((answer.extended[0].unsigned_int < tick1) ||
+ (answer.extended[0].unsigned_int > tick2)) {
+ return false;
+ }
+ }
+
+ if (answer.extended[1].unsigned_int != cookie * 2) {
+ return false;
+ }
+ } else if (2 == version) {
+ uint32_t cookie = 717111;
+ InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie));
+ ResultCode code = CrossCall(ipc, IpcTag::PING2, counted_buffer, &answer);
+
+ if (SBOX_ALL_OK != code) {
+ return false;
+ }
+ if (cookie != 717111 * 3) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+ProcessState::ProcessState()
+ : process_state_(ProcessStateInternal::NONE), csrss_connected_(true) {}
+
+bool ProcessState::InitCalled() const {
+ return process_state_ >= ProcessStateInternal::INIT_CALLED;
+}
+
+bool ProcessState::RevertedToSelf() const {
+ return process_state_ >= ProcessStateInternal::REVERTED_TO_SELF;
+}
+
+bool ProcessState::IsCsrssConnected() const {
+ return csrss_connected_;
+}
+
+void ProcessState::SetInitCalled() {
+ if (process_state_ < ProcessStateInternal::INIT_CALLED)
+ process_state_ = ProcessStateInternal::INIT_CALLED;
+}
+
+void ProcessState::SetRevertedToSelf() {
+ if (process_state_ < ProcessStateInternal::REVERTED_TO_SELF)
+ process_state_ = ProcessStateInternal::REVERTED_TO_SELF;
+}
+
+void ProcessState::SetCsrssConnected(bool csrss_connected) {
+ csrss_connected_ = csrss_connected;
+}
+
+ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ return sandbox::DuplicateHandleProxy(source_handle, target_process_id,
+ target_handle, desired_access, options);
+}
+
+ResultCode TargetServicesBase::GetComplexLineBreaks(const WCHAR* text,
+ uint32_t length,
+ uint8_t* break_before) {
+ return sandbox::GetComplexLineBreaksProxy(text, length, break_before);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.h b/security/sandbox/chromium/sandbox/win/src/target_services.h
new file mode 100644
index 0000000000..1d70d4cd34
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_services.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_TARGET_SERVICES_H__
+#define SANDBOX_SRC_TARGET_SERVICES_H__
+
+#include "base/macros.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+class ProcessState {
+ public:
+ ProcessState();
+ // Returns true if main has been called.
+ 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 SetInitCalled();
+ void SetRevertedToSelf();
+ void SetCsrssConnected(bool csrss_connected);
+
+ private:
+ enum class ProcessStateInternal { NONE = 0, INIT_CALLED, REVERTED_TO_SELF };
+
+ ProcessStateInternal process_state_;
+ bool csrss_connected_;
+ DISALLOW_COPY_AND_ASSIGN(ProcessState);
+};
+
+// This class is an implementation of the TargetServices.
+// Look in the documentation of sandbox::TargetServices for more info.
+// Do NOT add a destructor to this class without changing the implementation of
+// the factory method.
+class TargetServicesBase : public TargetServices {
+ public:
+ TargetServicesBase();
+
+ // Public interface of TargetServices.
+ ResultCode Init() override;
+ void LowerToken() override;
+ ProcessState* GetState() override;
+ ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) override;
+ ResultCode GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before) final;
+
+ // Factory method.
+ static TargetServicesBase* GetInstance();
+
+ // Sends a simple IPC Message that has a well-known answer. Returns true
+ // if the IPC was successful and false otherwise. There are 2 versions of
+ // this test: 1 and 2. The first one send a simple message while the
+ // second one send a message with an in/out param.
+ bool TestIPCPing(int version);
+
+ private:
+ ~TargetServicesBase() {}
+ ProcessState process_state_;
+ DISALLOW_COPY_AND_ASSIGN(TargetServicesBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_TARGET_SERVICES_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc b/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc
new file mode 100644
index 0000000000..3f951b761b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/win2k_threadpool.h"
+
+#include <stdint.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+void __stdcall EmptyCallBack(void*, unsigned char) {}
+
+void __stdcall TestCallBack(void* context, unsigned char) {
+ HANDLE event = reinterpret_cast<HANDLE>(context);
+ ::SetEvent(event);
+}
+
+namespace sandbox {
+
+// Test that register and unregister work, part 1.
+TEST(IPCTest, ThreadPoolRegisterTest1) {
+ Win2kThreadPool thread_pool;
+
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ HANDLE event1 = ::CreateEventW(nullptr, false, false, nullptr);
+ HANDLE event2 = ::CreateEventW(nullptr, false, false, nullptr);
+
+ uint32_t context = 0;
+ EXPECT_FALSE(thread_pool.RegisterWait(0, event1, EmptyCallBack, &context));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event1, EmptyCallBack, &context));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event2, EmptyCallBack, &context));
+ EXPECT_EQ(2u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(this));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(::CloseHandle(event1));
+ EXPECT_TRUE(::CloseHandle(event2));
+}
+
+// Test that register and unregister work, part 2.
+TEST(IPCTest, ThreadPoolRegisterTest2) {
+ Win2kThreadPool thread_pool;
+
+ HANDLE event1 = ::CreateEventW(nullptr, false, false, nullptr);
+ HANDLE event2 = ::CreateEventW(nullptr, false, false, nullptr);
+
+ uint32_t context = 0;
+ uint32_t c1 = 0;
+ uint32_t c2 = 0;
+
+ EXPECT_TRUE(thread_pool.RegisterWait(&c1, event1, EmptyCallBack, &context));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.RegisterWait(&c2, event2, EmptyCallBack, &context));
+ EXPECT_EQ(2u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c1));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(::CloseHandle(event1));
+ EXPECT_TRUE(::CloseHandle(event2));
+}
+
+// Test that the thread pool has at least a thread that services an event.
+// Test that when the event is un-registered is no longer serviced.
+TEST(IPCTest, ThreadPoolSignalAndWaitTest) {
+ Win2kThreadPool thread_pool;
+
+ // The events are auto reset and start not signaled.
+ HANDLE event1 = ::CreateEventW(nullptr, false, false, nullptr);
+ HANDLE event2 = ::CreateEventW(nullptr, false, false, nullptr);
+
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event1, TestCallBack, event2));
+
+ EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, false));
+ EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, false));
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(this));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::SignalObjectAndWait(event1, event2, 1000, false));
+
+ EXPECT_TRUE(::CloseHandle(event1));
+ EXPECT_TRUE(::CloseHandle(event2));
+}
+
+} // namespace sandbox
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 0000000000..3c8f8e25e5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
@@ -0,0 +1,178 @@
+// 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 <stdint.h>
+#include <string.h>
+
+#include "base/logging.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/line_break_dispatcher.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/signed_dispatcher.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_[static_cast<size_t>(IpcTag::NTCREATEFILE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENFILE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTSETINFO_RENAME)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTQUERYATTRIBUTESFILE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTQUERYFULLATTRIBUTESFILE)] =
+ dispatcher;
+ filesystem_dispatcher_.reset(dispatcher);
+
+ dispatcher = new NamedPipeDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATENAMEDPIPEW)] = dispatcher;
+ named_pipe_dispatcher_.reset(dispatcher);
+
+ dispatcher = new ThreadProcessDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENTHREAD)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENPROCESS)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATEPROCESSW)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENPROCESSTOKEN)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENPROCESSTOKENEX)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATETHREAD)] = dispatcher;
+ thread_process_dispatcher_.reset(dispatcher);
+
+ dispatcher = new SyncDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATEEVENT)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::OPENEVENT)] = dispatcher;
+ sync_dispatcher_.reset(dispatcher);
+
+ dispatcher = new RegistryDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATEKEY)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENKEY)] = dispatcher;
+ registry_dispatcher_.reset(dispatcher);
+
+ dispatcher = new HandleDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::DUPLICATEHANDLEPROXY)] = dispatcher;
+ handle_dispatcher_.reset(dispatcher);
+
+ dispatcher = new ProcessMitigationsWin32KDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GDIDLLINITIALIZE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETSTOCKOBJECT)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_REGISTERCLASSW)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_ENUMDISPLAYMONITORS)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_ENUMDISPLAYDEVICES)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_GETMONITORINFO)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETCERTIFICATE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETCERTIFICATESIZE)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETOPMINFORMATION)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETOPMRANDOMNUMBER)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(
+ IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(
+ IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS)] = dispatcher;
+ process_mitigations_win32k_dispatcher_.reset(dispatcher);
+
+ dispatcher = new SignedDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATESECTION)] = dispatcher;
+ signed_dispatcher_.reset(dispatcher);
+
+ dispatcher = new LineBreakDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::GETCOMPLEXLINEBREAKS)] = dispatcher;
+ line_break_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 = {IpcTag::PING1, {UINT32_TYPE}};
+ static const IPCParams ping2 = {IpcTag::PING2, {INOUTPTR_TYPE}};
+
+ if (ping1.Matches(ipc) || ping2.Matches(ipc)) {
+ *callback = reinterpret_cast<CallbackGeneric>(
+ static_cast<Callback1>(&TopLevelDispatcher::Ping));
+ return this;
+ }
+
+ Dispatcher* dispatcher = GetDispatcher(ipc->ipc_tag);
+ if (!dispatcher) {
+ NOTREACHED();
+ return nullptr;
+ }
+ return dispatcher->OnMessageReady(ipc, callback);
+}
+
+// Delegate to the appropriate dispatcher.
+bool TopLevelDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (IpcTag::PING1 == service || IpcTag::PING2 == service)
+ return true;
+
+ Dispatcher* dispatcher = GetDispatcher(service);
+ if (!dispatcher) {
+ NOTREACHED();
+ return false;
+ }
+ return dispatcher->SetupService(manager, service);
+}
+
+// We service PING 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 IpcTag::PING1: {
+ 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 IpcTag::PING2: {
+ CountedBuffer* io_buffer = reinterpret_cast<CountedBuffer*>(arg1);
+ if (sizeof(uint32_t) != io_buffer->Size())
+ return false;
+
+ uint32_t* cookie = reinterpret_cast<uint32_t*>(io_buffer->Buffer());
+ *cookie = (*cookie) * 3;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+Dispatcher* TopLevelDispatcher::GetDispatcher(IpcTag ipc_tag) {
+ if (ipc_tag >= IpcTag::LAST || ipc_tag <= IpcTag::UNUSED)
+ return nullptr;
+
+ return ipc_targets_[static_cast<size_t>(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 0000000000..a80ce1ef5d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h
@@ -0,0 +1,54 @@
+// 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 <memory>
+
+#include "base/macros.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, IpcTag service) override;
+
+ private:
+ // Test IPC provider.
+ bool Ping(IPCInfo* ipc, void* cookie);
+
+ // Returns a dispatcher from ipc_targets_.
+ Dispatcher* GetDispatcher(IpcTag ipc_tag);
+
+ PolicyBase* policy_;
+ std::unique_ptr<Dispatcher> filesystem_dispatcher_;
+ std::unique_ptr<Dispatcher> named_pipe_dispatcher_;
+ std::unique_ptr<Dispatcher> thread_process_dispatcher_;
+ std::unique_ptr<Dispatcher> sync_dispatcher_;
+ std::unique_ptr<Dispatcher> registry_dispatcher_;
+ std::unique_ptr<Dispatcher> handle_dispatcher_;
+ std::unique_ptr<Dispatcher> process_mitigations_win32k_dispatcher_;
+ std::unique_ptr<Dispatcher> signed_dispatcher_;
+ std::unique_ptr<Dispatcher> line_break_dispatcher_;
+ Dispatcher* ipc_targets_[kMaxIpcTag];
+
+ 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
new file mode 100644
index 0000000000..0acb178987
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_handle.h"
+#include "build/build_config.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Loads and or unloads a DLL passed in the second parameter of argv.
+// The first parameter of argv is 'L' = load, 'U' = unload or 'B' for both.
+SBOX_TESTS_COMMAND int UseOneDLL(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ int rv = SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ wchar_t option = (argv[0])[0];
+ if ((option == L'L') || (option == L'B')) {
+ HMODULE module1 = ::LoadLibraryW(argv[1]);
+ rv = (!module1) ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+
+ if ((option == L'U') || (option == L'B')) {
+ HMODULE module2 = ::GetModuleHandleW(argv[1]);
+ rv = ::FreeLibrary(module2) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ }
+ return rv;
+}
+
+// Opens an event passed as the first parameter of argv.
+SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ base::win::ScopedHandle event_open(::OpenEvent(SYNCHRONIZE, false, argv[0]));
+ return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+}
+
+// Fails on Windows ARM64: https://crbug.com/905526
+#if defined(ARCH_CPU_ARM64)
+#define MAYBE_BaselineAvicapDll DISABLED_BaselineAvicapDll
+#else
+#define MAYBE_BaselineAvicapDll BaselineAvicapDll
+#endif
+TEST(UnloadDllTest, MAYBE_BaselineAvicapDll) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ runner.SetTimeout(2000);
+ // Add a sync rule, because that ensures that the interception agent has
+ // more than one item in its internal table.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"t0001"));
+
+ // Note for the puzzled: avicap32.dll is a 64-bit dll in 64-bit versions of
+ // windows so this test and the others just work.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll"));
+}
+
+TEST(UnloadDllTest, UnloadAviCapDllNoPatching) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ runner.SetTimeout(2000);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->AddDllToUnload(L"avicap32.dll");
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll"));
+}
+
+TEST(UnloadDllTest, UnloadAviCapDllWithPatching) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(BEFORE_REVERT);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->AddDllToUnload(L"avicap32.dll");
+
+ base::win::ScopedHandle handle1(
+ ::CreateEvent(nullptr, false, false, L"tst0001"));
+
+ // Add a couple of rules that ensures that the interception agent add EAT
+ // patching on the client which makes sure that the unload dll record does
+ // not interact badly with them.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"tst0001"));
+
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"SimpleOpenEvent tst0001"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc
new file mode 100644
index 0000000000..49cc68bb00
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/win2k_threadpool.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+Win2kThreadPool::Win2kThreadPool() {
+ ::InitializeCriticalSection(&lock_);
+}
+
+bool Win2kThreadPool::RegisterWait(const void* cookie,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) {
+ if (0 == cookie) {
+ return false;
+ }
+ HANDLE pool_object = nullptr;
+ // create a wait for a kernel object, with no timeout
+ if (!::RegisterWaitForSingleObject(&pool_object, waitable_object, callback,
+ context, INFINITE, WT_EXECUTEDEFAULT)) {
+ return false;
+ }
+ PoolObject pool_obj = {cookie, pool_object};
+ AutoLock lock(&lock_);
+ pool_objects_.push_back(pool_obj);
+ return true;
+}
+
+bool Win2kThreadPool::UnRegisterWaits(void* cookie) {
+ if (0 == cookie) {
+ return false;
+ }
+ AutoLock lock(&lock_);
+ bool success = true;
+ PoolObjects::iterator it = pool_objects_.begin();
+ while (it != pool_objects_.end()) {
+ if (it->cookie == cookie) {
+ HANDLE wait = it->wait;
+ it = pool_objects_.erase(it);
+ success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0);
+ } else {
+ ++it;
+ }
+ }
+ return success;
+}
+
+size_t Win2kThreadPool::OutstandingWaits() {
+ AutoLock lock(&lock_);
+ return pool_objects_.size();
+}
+
+Win2kThreadPool::~Win2kThreadPool() {
+ // Here we used to unregister all the pool wait handles. Now, following the
+ // rest of the code we avoid lengthy or blocking calls given that the process
+ // is being torn down.
+ ::DeleteCriticalSection(&lock_);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h
new file mode 100644
index 0000000000..c4d539dd7f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN2K_THREADPOOL_H_
+#define SANDBOX_SRC_WIN2K_THREADPOOL_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <list>
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Win2kThreadPool a simple implementation of a thread provider as required
+// for the sandbox IPC subsystem. See sandbox\crosscall_server.h for the details
+// and requirements of this interface.
+//
+// Implementing the thread provider as a thread pool is desirable in the case
+// of shared memory IPC because it can generate a large number of waitable
+// events: as many as channels. A thread pool does not create a thread per
+// event, instead maintains a few idle threads but can create more if the need
+// arises.
+//
+// This implementation simply thunks to the nice thread pool API of win2k.
+class Win2kThreadPool : public ThreadProvider {
+ public:
+ Win2kThreadPool();
+ ~Win2kThreadPool() override;
+
+ bool RegisterWait(const void* cookie,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) override;
+
+ bool UnRegisterWaits(void* cookie) override;
+
+ // Returns the total number of wait objects associated with
+ // the thread pool.
+ size_t OutstandingWaits();
+
+ private:
+ // record to keep track of a wait and its associated cookie.
+ struct PoolObject {
+ const void* cookie;
+ HANDLE wait;
+ };
+ // The list of pool wait objects.
+ typedef std::list<PoolObject> PoolObjects;
+ PoolObjects pool_objects_;
+ // This lock protects the list of pool wait objects.
+ CRITICAL_SECTION lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win2kThreadPool);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WIN2K_THREADPOOL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.cc b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
new file mode 100644
index 0000000000..8fb345a239
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/win_utils.h"
+
+#include <psapi.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+const size_t kDriveLetterLen = 3;
+
+constexpr wchar_t kNTDotPrefix[] = L"\\\\.\\";
+const size_t kNTDotPrefixLen = base::size(kNTDotPrefix) - 1;
+
+// Holds the information about a known registry key.
+struct KnownReservedKey {
+ const wchar_t* name;
+ HKEY key;
+};
+
+// Contains all the known registry key by name and by handle.
+const KnownReservedKey kKnownKey[] = {
+ {L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT},
+ {L"HKEY_CURRENT_USER", HKEY_CURRENT_USER},
+ {L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
+ {L"HKEY_USERS", HKEY_USERS},
+ {L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
+ {L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT},
+ {L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT},
+ {L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
+ {L"HKEY_DYN_DATA", HKEY_DYN_DATA}};
+
+// These functions perform case independent path comparisons.
+bool EqualPath(const std::wstring& first, const std::wstring& second) {
+ return _wcsicmp(first.c_str(), second.c_str()) == 0;
+}
+
+bool EqualPath(const std::wstring& first,
+ size_t first_offset,
+ const std::wstring& second,
+ size_t second_offset) {
+ return _wcsicmp(first.c_str() + first_offset,
+ second.c_str() + second_offset) == 0;
+}
+
+bool EqualPath(const std::wstring& first,
+ const wchar_t* second,
+ size_t second_len) {
+ return _wcsnicmp(first.c_str(), second, second_len) == 0;
+}
+
+bool EqualPath(const std::wstring& first,
+ size_t first_offset,
+ const wchar_t* second,
+ size_t second_len) {
+ return _wcsnicmp(first.c_str() + first_offset, second, second_len) == 0;
+}
+
+// Returns true if |path| starts with "\??\" and returns a path without that
+// component.
+bool IsNTPath(const std::wstring& path, std::wstring* trimmed_path) {
+ if ((path.size() < sandbox::kNTPrefixLen) ||
+ !EqualPath(path, sandbox::kNTPrefix, sandbox::kNTPrefixLen)) {
+ *trimmed_path = path;
+ return false;
+ }
+
+ *trimmed_path = path.substr(sandbox::kNTPrefixLen);
+ return true;
+}
+
+// Returns true if |path| starts with "\Device\" and returns a path without that
+// component.
+bool IsDevicePath(const std::wstring& path, std::wstring* trimmed_path) {
+ if ((path.size() < sandbox::kNTDevicePrefixLen) ||
+ (!EqualPath(path, sandbox::kNTDevicePrefix,
+ sandbox::kNTDevicePrefixLen))) {
+ *trimmed_path = path;
+ return false;
+ }
+
+ *trimmed_path = path.substr(sandbox::kNTDevicePrefixLen);
+ return true;
+}
+
+// Returns the offset to the path seperator following
+// "\Device\HarddiskVolumeX" in |path|.
+size_t PassHarddiskVolume(const std::wstring& path) {
+ static constexpr wchar_t pattern[] = L"\\Device\\HarddiskVolume";
+ const size_t patternLen = base::size(pattern) - 1;
+
+ // First, check for |pattern|.
+ if ((path.size() < patternLen) || (!EqualPath(path, pattern, patternLen)))
+ return std::wstring::npos;
+
+ // Find the next path separator, after the pattern match.
+ return path.find_first_of(L'\\', patternLen - 1);
+}
+
+// Returns true if |path| starts with "\Device\HarddiskVolumeX\" and returns a
+// path without that component. |removed| will hold the prefix removed.
+bool IsDeviceHarddiskPath(const std::wstring& path,
+ std::wstring* trimmed_path,
+ std::wstring* removed) {
+ size_t offset = PassHarddiskVolume(path);
+ if (offset == std::wstring::npos)
+ return false;
+
+ // Remove up to and including the path separator.
+ *removed = path.substr(0, offset + 1);
+ // Remaining path starts after the path separator.
+ *trimmed_path = path.substr(offset + 1);
+ return true;
+}
+
+bool StartsWithDriveLetter(const std::wstring& path) {
+ if (path.size() < kDriveLetterLen)
+ return false;
+
+ if (path[1] != L':' || path[2] != L'\\')
+ return false;
+
+ return base::IsAsciiAlpha(path[0]);
+}
+
+// Removes "\\\\.\\" from the path.
+void RemoveImpliedDevice(std::wstring* path) {
+ if (EqualPath(*path, kNTDotPrefix, kNTDotPrefixLen))
+ *path = path->substr(kNTDotPrefixLen);
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const std::wstring& path) {
+ size_t start = 0;
+ if (EqualPath(path, sandbox::kNTPrefix, sandbox::kNTPrefixLen))
+ start = sandbox::kNTPrefixLen;
+
+ const wchar_t kPipe[] = L"pipe\\";
+ if (path.size() < start + base::size(kPipe) - 1)
+ return false;
+
+ return EqualPath(path, start, kPipe, base::size(kPipe) - 1);
+}
+
+HKEY GetReservedKeyFromName(const std::wstring& name) {
+ for (size_t i = 0; i < base::size(kKnownKey); ++i) {
+ if (name == kKnownKey[i].name)
+ return kKnownKey[i].key;
+ }
+
+ return nullptr;
+}
+
+bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) {
+ for (size_t i = 0; i < base::size(kKnownKey); ++i) {
+ if (name.find(kKnownKey[i].name) == 0) {
+ HKEY key;
+ DWORD disposition;
+ if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, nullptr,
+ 0, MAXIMUM_ALLOWED, nullptr, &key,
+ &disposition))
+ return false;
+
+ bool result = GetPathFromHandle(key, resolved_name);
+ ::RegCloseKey(key);
+
+ if (!result)
+ return false;
+
+ *resolved_name += name.substr(wcslen(kKnownKey[i].name));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// |full_path| can have any of the following forms:
+// \??\c:\some\foo\bar
+// \Device\HarddiskVolume0\some\foo\bar
+// \??\HarddiskVolume0\some\foo\bar
+// \??\UNC\SERVER\Share\some\foo\bar
+DWORD IsReparsePoint(const std::wstring& full_path) {
+ // Check if it's a pipe. We can't query the attributes of a pipe.
+ if (IsPipe(full_path))
+ return ERROR_NOT_A_REPARSE_POINT;
+
+ std::wstring path;
+ bool nt_path = IsNTPath(full_path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+ bool is_device_path = IsDevicePath(path, &path);
+
+ if (!has_drive && !is_device_path && !nt_path)
+ return ERROR_INVALID_NAME;
+
+ if (!has_drive) {
+ // Add Win32 device namespace prefix, required for some Windows APIs.
+ path.insert(0, kNTDotPrefix);
+ }
+
+ // Ensure that volume path matches start of path.
+ wchar_t vol_path[MAX_PATH];
+ if (!::GetVolumePathNameW(path.c_str(), vol_path, MAX_PATH)) {
+ // This will fail if this is a device that isn't volume related, which can't
+ // then be a reparse point.
+ return is_device_path ? ERROR_NOT_A_REPARSE_POINT : ERROR_INVALID_NAME;
+ }
+
+ // vol_path includes a trailing slash, so reduce size for path and loop check.
+ size_t vol_path_len = wcslen(vol_path) - 1;
+ if (!EqualPath(path, vol_path, vol_path_len)) {
+ return ERROR_INVALID_NAME;
+ }
+
+ do {
+ DWORD attributes = ::GetFileAttributes(path.c_str());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ DWORD error = ::GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND &&
+ error != ERROR_INVALID_FUNCTION &&
+ error != ERROR_INVALID_NAME) {
+ // Unexpected error.
+ return error;
+ }
+ } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
+ // This is a reparse point.
+ return ERROR_SUCCESS;
+ }
+
+ path.resize(path.rfind(L'\\'));
+ } while (path.size() > vol_path_len); // Skip root dir.
+
+ return ERROR_NOT_A_REPARSE_POINT;
+}
+
+// We get a |full_path| of the forms accepted by IsReparsePoint(), and the name
+// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
+bool SameObject(HANDLE handle, const wchar_t* full_path) {
+ // Check if it's a pipe.
+ if (IsPipe(full_path))
+ return true;
+
+ std::wstring actual_path;
+ if (!GetPathFromHandle(handle, &actual_path))
+ return false;
+
+ std::wstring path(full_path);
+ DCHECK_NT(!path.empty());
+
+ // This may end with a backslash.
+ if (path.back() == L'\\') {
+ path.pop_back();
+ }
+
+ // Perfect match (case-insensitive check).
+ if (EqualPath(actual_path, path))
+ return true;
+
+ bool nt_path = IsNTPath(path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+
+ if (!has_drive && nt_path) {
+ std::wstring 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);
+ }
+ }
+
+ // 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)) {
+ return false;
+ }
+ size_t vol_path_len = wcslen(vol_path);
+ base::string16 nt_vol;
+ if (!GetNtPathFromWin32Path(vol_path, &nt_vol)) {
+ return false;
+ }
+
+ // The two paths should be the same length.
+ if (nt_vol.size() + path.size() - vol_path_len != actual_path.size()) {
+ return false;
+ }
+
+ // Check the volume matches.
+ if (!EqualPath(actual_path, nt_vol.c_str(), nt_vol.size())) {
+ return false;
+ }
+
+ // Check the path after the volume matches.
+ if (!EqualPath(actual_path, nt_vol.size(), path, vol_path_len)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Just make a best effort here. There are lots of corner cases that we're
+// not expecting - and will fail to make long.
+bool ConvertToLongPath(std::wstring* native_path,
+ const std::wstring* drive_letter) {
+ if (IsPipe(*native_path))
+ return true;
+
+ bool is_device_harddisk_path = false;
+ bool is_nt_path = false;
+ bool added_implied_device = false;
+ std::wstring temp_path;
+ std::wstring to_restore;
+
+ // Process a few prefix types.
+ if (IsNTPath(*native_path, &temp_path)) {
+ // "\??\"
+ if (!StartsWithDriveLetter(temp_path)) {
+ // Prepend with "\\.\".
+ temp_path = std::wstring(kNTDotPrefix) + temp_path;
+ added_implied_device = true;
+ }
+ is_nt_path = true;
+ } else if (IsDeviceHarddiskPath(*native_path, &temp_path, &to_restore)) {
+ // "\Device\HarddiskVolumeX\" - hacky attempt making ::GetLongPathName
+ // work for native device paths. Remove "\Device\HarddiskVolumeX\" and
+ // replace with drive letter.
+
+ // Nothing we can do if we don't have a drive letter. Leave |native_path|
+ // as is.
+ if (!drive_letter || drive_letter->empty())
+ return false;
+ temp_path = *drive_letter + temp_path;
+ is_device_harddisk_path = true;
+ } else if (IsDevicePath(*native_path, &temp_path)) {
+ // "\Device\" - there's nothing we can do to convert to long here.
+ return false;
+ }
+
+ DWORD size = MAX_PATH;
+ std::unique_ptr<wchar_t[]> long_path_buf(new wchar_t[size]);
+
+ 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(temp_path.c_str(), long_path_buf.get(), size);
+ }
+
+ DWORD last_error = ::GetLastError();
+ if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error ||
+ ERROR_PATH_NOT_FOUND == last_error ||
+ ERROR_INVALID_NAME == last_error)) {
+ // The file does not exist, but maybe a sub path needs to be expanded.
+ std::wstring::size_type last_slash = temp_path.rfind(L'\\');
+ if (std::wstring::npos == last_slash)
+ return false;
+
+ std::wstring begin = temp_path.substr(0, last_slash);
+ std::wstring end = temp_path.substr(last_slash);
+ if (!ConvertToLongPath(&begin))
+ return false;
+
+ // Ok, it worked. Let's reset the return value.
+ temp_path = begin + end;
+ return_value = 1;
+ } else if (0 != return_value) {
+ temp_path = long_path_buf.get();
+ }
+
+ // If successful, re-apply original namespace prefix before returning.
+ if (return_value != 0) {
+ if (added_implied_device)
+ RemoveImpliedDevice(&temp_path);
+
+ if (is_nt_path) {
+ *native_path = kNTPrefix;
+ *native_path += temp_path;
+ } else if (is_device_harddisk_path) {
+ // Remove the added drive letter.
+ temp_path = temp_path.substr(kDriveLetterLen);
+ *native_path = to_restore;
+ *native_path += temp_path;
+ } else {
+ *native_path = temp_path;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GetPathFromHandle(HANDLE handle, std::wstring* path) {
+ NtQueryObjectFunction NtQueryObject = nullptr;
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ OBJECT_NAME_INFORMATION initial_buffer;
+ OBJECT_NAME_INFORMATION* name = &initial_buffer;
+ ULONG size = sizeof(initial_buffer);
+ // Query the name information a first time to get the size of the name.
+ // Windows XP requires that the size of the buffer passed in here be != 0.
+ NTSTATUS status =
+ NtQueryObject(handle, ObjectNameInformation, name, size, &size);
+
+ std::unique_ptr<BYTE[]> name_ptr;
+ if (size) {
+ name_ptr.reset(new BYTE[size]);
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
+ }
+
+ if (STATUS_SUCCESS != status)
+ return false;
+
+ path->assign(name->ObjectName.Buffer,
+ name->ObjectName.Length / sizeof(name->ObjectName.Buffer[0]));
+ return true;
+}
+
+bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) {
+ HANDLE file = ::CreateFileW(
+ path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+ bool rv = GetPathFromHandle(file, nt_path);
+ ::CloseHandle(file);
+ return rv;
+}
+
+bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+ size_t length,
+ DWORD writeProtection) {
+ // First, remove the protections.
+ DWORD old_protection;
+ if (!::VirtualProtectEx(child_process, address, length, writeProtection,
+ &old_protection))
+ return false;
+
+ SIZE_T written;
+ bool ok =
+ ::WriteProcessMemory(child_process, address, buffer, length, &written) &&
+ (length == written);
+
+ // Always attempt to restore the original protection.
+ if (!::VirtualProtectEx(child_process, address, length, old_protection,
+ &old_protection))
+ return false;
+
+ return ok;
+}
+
+bool CopyToChildMemory(HANDLE child,
+ const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer) {
+ DCHECK(remote_buffer);
+ if (0 == buffer_bytes) {
+ *remote_buffer = nullptr;
+ return true;
+ }
+
+ // Allocate memory in the target process without specifying the address
+ void* remote_data = ::VirtualAllocEx(child, nullptr, buffer_bytes, MEM_COMMIT,
+ PAGE_READWRITE);
+ if (!remote_data)
+ return false;
+
+ SIZE_T bytes_written;
+ bool success = ::WriteProcessMemory(child, remote_data, local_buffer,
+ buffer_bytes, &bytes_written);
+ if (!success || bytes_written != buffer_bytes) {
+ ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
+ return false;
+ }
+
+ *remote_buffer = remote_data;
+
+ return true;
+}
+
+DWORD GetLastErrorFromNtStatus(NTSTATUS status) {
+ RtlNtStatusToDosErrorFunction NtStatusToDosError = nullptr;
+ ResolveNTFunctionPtr("RtlNtStatusToDosError", &NtStatusToDosError);
+ return NtStatusToDosError(status);
+}
+
+// This function uses the undocumented PEB ImageBaseAddress field to extract
+// the base address of the new process.
+void* GetProcessBaseAddress(HANDLE process) {
+ NtQueryInformationProcessFunction query_information_process = nullptr;
+ ResolveNTFunctionPtr("NtQueryInformationProcess", &query_information_process);
+ if (!query_information_process)
+ return nullptr;
+ PROCESS_BASIC_INFORMATION process_basic_info = {};
+ NTSTATUS status = query_information_process(
+ process, ProcessBasicInformation, &process_basic_info,
+ sizeof(process_basic_info), nullptr);
+ if (STATUS_SUCCESS != status)
+ return nullptr;
+
+ PEB peb = {};
+ SIZE_T bytes_read = 0;
+ if (!::ReadProcessMemory(process, process_basic_info.PebBaseAddress, &peb,
+ sizeof(peb), &bytes_read) ||
+ (sizeof(peb) != bytes_read)) {
+ return nullptr;
+ }
+
+ void* base_address = peb.ImageBaseAddress;
+ char magic[2] = {};
+ if (!::ReadProcessMemory(process, base_address, magic, sizeof(magic),
+ &bytes_read) ||
+ (sizeof(magic) != bytes_read)) {
+ return nullptr;
+ }
+
+ if (magic[0] != 'M' || magic[1] != 'Z')
+ return nullptr;
+
+#if defined(_M_ARM64)
+ // Windows 10 on ARM64 has multi-threaded DLL loading that does not work with
+ // the sandbox. (On x86 this gets disabled by hook detection code that was not
+ // ported to ARM64). This overwrites the LoaderThreads value in the process
+ // parameters part of the PEB, if it is set to the default of 0 (which
+ // actually means it defaults to 4 loading threads). This is an undocumented
+ // field so there is a, probably small, risk that it might change or move in
+ // the future. In order to slightly guard against that we only update if the
+ // value is currently 0.
+ auto processParameters = reinterpret_cast<uint8_t*>(peb.ProcessParameters);
+ const uint32_t loaderThreadsOffset = 0x40c;
+ uint32_t maxLoaderThreads = 0;
+ BOOL memoryRead = ::ReadProcessMemory(
+ process, processParameters + loaderThreadsOffset, &maxLoaderThreads,
+ sizeof(maxLoaderThreads), &bytes_read);
+ if (memoryRead && (sizeof(maxLoaderThreads) == bytes_read) &&
+ (maxLoaderThreads == 0)) {
+ maxLoaderThreads = 1;
+ WriteProtectedChildMemory(process, processParameters + loaderThreadsOffset,
+ &maxLoaderThreads, sizeof(maxLoaderThreads),
+ PAGE_READWRITE);
+ }
+#endif
+
+ return base_address;
+}
+
+DWORD GetTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS info_class,
+ std::unique_ptr<BYTE[]>* buffer) {
+ // Get the required buffer size.
+ DWORD size = 0;
+ ::GetTokenInformation(token, info_class, nullptr, 0, &size);
+ if (!size) {
+ return ::GetLastError();
+ }
+
+ auto temp_buffer = std::make_unique<BYTE[]>(size);
+ if (!::GetTokenInformation(token, info_class, temp_buffer.get(), size,
+ &size)) {
+ return ::GetLastError();
+ }
+
+ *buffer = std::move(temp_buffer);
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
+
+void ResolveNTFunctionPtr(const char* name, void* ptr) {
+ static volatile HMODULE ntdll = nullptr;
+
+ if (!ntdll) {
+ HMODULE ntdll_local = ::GetModuleHandle(sandbox::kNtdllName);
+ // Use PEImage to sanity-check that we have a valid ntdll handle.
+ base::win::PEImage ntdll_peimage(ntdll_local);
+ CHECK_NT(ntdll_peimage.VerifyMagic());
+ // Race-safe way to set static ntdll.
+ ::InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, nullptr);
+ }
+
+ CHECK_NT(ntdll);
+ FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
+ *function_ptr = ::GetProcAddress(ntdll, name);
+ CHECK_NT(*function_ptr);
+}
diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.h b/security/sandbox/chromium/sandbox/win/src/win_utils.h
new file mode 100644
index 0000000000..7c848f2f08
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win_utils.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN_UTILS_H_
+#define SANDBOX_SRC_WIN_UTILS_H_
+
+#include <stddef.h>
+#include <windows.h>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+// Prefix for path used by NT calls.
+const wchar_t kNTPrefix[] = L"\\??\\";
+const size_t kNTPrefixLen = base::size(kNTPrefix) - 1;
+
+const wchar_t kNTDevicePrefix[] = L"\\Device\\";
+const size_t kNTDevicePrefixLen = base::size(kNTDevicePrefix) - 1;
+
+// Automatically acquires and releases a lock when the object is
+// is destroyed.
+class AutoLock {
+ public:
+ // Acquires the lock.
+ explicit AutoLock(CRITICAL_SECTION* lock) : lock_(lock) {
+ ::EnterCriticalSection(lock);
+ }
+
+ // Releases the lock;
+ ~AutoLock() { ::LeaveCriticalSection(lock_); }
+
+ private:
+ CRITICAL_SECTION* lock_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock);
+};
+
+// Basic implementation of a singleton which calls the destructor
+// when the exe is shutting down or the DLL is being unloaded.
+template <typename Derived>
+class SingletonBase {
+ public:
+ static Derived* GetInstance() {
+ static Derived* instance = nullptr;
+ if (!instance) {
+ instance = new Derived();
+ // Microsoft CRT extension. In an exe this this called after
+ // winmain returns, in a dll is called in DLL_PROCESS_DETACH
+ _onexit(OnExit);
+ }
+ return instance;
+ }
+
+ private:
+ // this is the function that gets called by the CRT when the
+ // process is shutting down.
+ static int __cdecl OnExit() {
+ delete GetInstance();
+ return 0;
+ }
+};
+
+// Function object which invokes LocalFree on its parameter, which must be
+// a pointer. Can be used to store LocalAlloc pointers in std::unique_ptr:
+//
+// std::unique_ptr<int, sandbox::LocalFreeDeleter> foo_ptr(
+// static_cast<int*>(LocalAlloc(LMEM_FIXED, sizeof(int))));
+struct LocalFreeDeleter {
+ inline void operator()(void* ptr) const { ::LocalFree(ptr); }
+};
+
+// 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 argument is not modified.
+// - If passing in a short native device path (\Device\HarddiskVolumeX\path~1),
+// a drive letter string (c:\) must also be provided.
+bool ConvertToLongPath(std::wstring* path,
+ const std::wstring* drive_letter = nullptr);
+
+// 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 std::wstring& full_path);
+
+// Returns true if the handle corresponds to the object pointed by this path.
+bool SameObject(HANDLE handle, const wchar_t* full_path);
+
+// Resolves a handle to an nt path. Returns true if the handle can be resolved.
+bool GetPathFromHandle(HANDLE handle, std::wstring* path);
+
+// Resolves a win32 path to an nt path using GetPathFromHandle. The path must
+// exist. Returs true if the translation was succesful.
+bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path);
+
+// Translates a reserved key name to its handle.
+// For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE.
+// Returns nullptr if the name does not represent any reserved key name.
+HKEY GetReservedKeyFromName(const std::wstring& name);
+
+// Resolves a user-readable registry path to a system-readable registry path.
+// For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to
+// \\registry\\machine\\software\\microsoft. Returns false if the path
+// cannot be resolved.
+bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name);
+
+// Writes |length| bytes from the provided |buffer| into the address space of
+// |child_process|, at the specified |address|, preserving the original write
+// protection attributes. Returns true on success.
+bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+ size_t length,
+ DWORD writeProtection = PAGE_WRITECOPY);
+
+// Allocates |buffer_bytes| in child (PAGE_READWRITE) and copies data
+// from |local_buffer| in this process into |child|. |remote_buffer|
+// contains the address in the chile. If a zero byte copy is
+// requested |true| is returned and no allocation or copying is
+// attempted. Returns false if allocation or copying fails. If
+// copying fails, the allocation will be reversed.
+bool CopyToChildMemory(HANDLE child,
+ const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer);
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const std::wstring& path);
+
+// Converts a NTSTATUS code to a Win32 error code.
+DWORD GetLastErrorFromNtStatus(NTSTATUS status);
+
+// Returns the address of the main exe module in memory taking in account
+// address space layout randomization. This uses the process' PEB to extract
+// the base address. This should only be called on new, suspended processes.
+void* GetProcessBaseAddress(HANDLE process);
+
+// Calls GetTokenInformation with the desired |info_class| and returns a
+// |buffer| and the Win32 error code.
+DWORD GetTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS info_class,
+ std::unique_ptr<BYTE[]>* buffer);
+
+} // namespace sandbox
+
+// Resolves a function name in NTDLL to a function pointer. The second parameter
+// is a pointer to the function pointer.
+void ResolveNTFunctionPtr(const char* name, void* ptr);
+
+#endif // SANDBOX_SRC_WIN_UTILS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc b/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc
new file mode 100644
index 0000000000..36e7d4dd1e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/win_utils.h"
+
+#include <windows.h>
+
+#include <psapi.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/path_service.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+class ScopedTerminateProcess {
+ public:
+ ScopedTerminateProcess(HANDLE process) : process_(process) {}
+
+ ~ScopedTerminateProcess() { ::TerminateProcess(process_, 0); }
+
+ private:
+ HANDLE process_;
+};
+
+bool GetModuleList(HANDLE process, std::vector<HMODULE>* result) {
+ std::vector<HMODULE> modules(256);
+ DWORD size_needed = 0;
+ if (EnumProcessModules(
+ process, &modules[0],
+ base::checked_cast<DWORD>(modules.size() * sizeof(HMODULE)),
+ &size_needed)) {
+ result->assign(modules.begin(),
+ modules.begin() + (size_needed / sizeof(HMODULE)));
+ return true;
+ }
+ modules.resize(size_needed / sizeof(HMODULE));
+ if (EnumProcessModules(
+ process, &modules[0],
+ base::checked_cast<DWORD>(modules.size() * sizeof(HMODULE)),
+ &size_needed)) {
+ result->assign(modules.begin(),
+ modules.begin() + (size_needed / sizeof(HMODULE)));
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+TEST(WinUtils, IsReparsePoint) {
+ using sandbox::IsReparsePoint;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t my_folder[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(my_folder));
+ ASSERT_TRUE(::CreateDirectory(my_folder, nullptr));
+
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_A_REPARSE_POINT),
+ IsReparsePoint(my_folder));
+
+ std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar";
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_A_REPARSE_POINT),
+ IsReparsePoint(not_found));
+
+ std::wstring new_file = std::wstring(my_folder) + L"\\foo";
+ EXPECT_EQ(static_cast<DWORD>(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,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ EXPECT_NE(INVALID_HANDLE_VALUE, dir);
+
+ std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+
+ EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), IsReparsePoint(new_file));
+
+ EXPECT_TRUE(DeleteReparsePoint(dir));
+ EXPECT_TRUE(::CloseHandle(dir));
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
+
+TEST(WinUtils, SameObject) {
+ using sandbox::SameObject;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t my_folder[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(my_folder));
+ ASSERT_TRUE(::CreateDirectory(my_folder, nullptr));
+
+ std::wstring folder(my_folder);
+ std::wstring file_name = folder + L"\\foo.txt";
+ const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+ base::win::ScopedHandle file(CreateFile(file_name.c_str(), GENERIC_WRITE,
+ kSharing, nullptr, CREATE_ALWAYS,
+ FILE_FLAG_DELETE_ON_CLOSE, nullptr));
+
+ EXPECT_TRUE(file.IsValid());
+ std::wstring file_name_nt1 = std::wstring(L"\\??\\") + file_name;
+ std::wstring file_name_nt2 = std::wstring(L"\\??\\") + folder + L"\\FOO.txT";
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str()));
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str()));
+
+ file.Close();
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
+
+TEST(WinUtils, IsPipe) {
+ using sandbox::IsPipe;
+
+ std::wstring 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));
+}
+
+TEST(WinUtils, NtStatusToWin32Error) {
+ using sandbox::GetLastErrorFromNtStatus;
+ EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ GetLastErrorFromNtStatus(STATUS_SUCCESS));
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_SUPPORTED),
+ GetLastErrorFromNtStatus(STATUS_NOT_SUPPORTED));
+ EXPECT_EQ(static_cast<DWORD>(ERROR_ALREADY_EXISTS),
+ GetLastErrorFromNtStatus(STATUS_OBJECT_NAME_COLLISION));
+ EXPECT_EQ(static_cast<DWORD>(ERROR_ACCESS_DENIED),
+ GetLastErrorFromNtStatus(STATUS_ACCESS_DENIED));
+}
+
+TEST(WinUtils, GetProcessBaseAddress) {
+ using sandbox::GetProcessBaseAddress;
+ STARTUPINFO start_info = {};
+ PROCESS_INFORMATION proc_info = {};
+ WCHAR command_line[] = L"notepad";
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+ ASSERT_TRUE(::CreateProcessW(nullptr, command_line, nullptr, nullptr, false,
+ CREATE_SUSPENDED, nullptr, nullptr, &start_info,
+ &proc_info));
+ base::win::ScopedProcessInformation scoped_proc_info(proc_info);
+ ScopedTerminateProcess process_terminate(scoped_proc_info.process_handle());
+ void* base_address = GetProcessBaseAddress(scoped_proc_info.process_handle());
+ ASSERT_NE(nullptr, base_address);
+ ASSERT_NE(static_cast<DWORD>(-1),
+ ::ResumeThread(scoped_proc_info.thread_handle()));
+ ::WaitForInputIdle(scoped_proc_info.process_handle(), 1000);
+ ASSERT_NE(static_cast<DWORD>(-1),
+ ::SuspendThread(scoped_proc_info.thread_handle()));
+
+ std::vector<HMODULE> modules;
+ // Compare against the loader's module list (which should now be initialized).
+ ASSERT_TRUE(GetModuleList(scoped_proc_info.process_handle(), &modules));
+ ASSERT_GT(modules.size(), 0U);
+ EXPECT_EQ(base_address, modules[0]);
+}
+
+// This test requires an elevated prompt to setup.
+TEST(WinUtils, ConvertToLongPath) {
+ // Test setup.
+ base::FilePath orig_path;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &orig_path));
+ orig_path = orig_path.Append(L"calc.exe");
+
+ base::FilePath temp_path;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_PROGRAM_FILES, &temp_path));
+ temp_path = temp_path.Append(L"test_calc.exe");
+
+ ASSERT_TRUE(base::CopyFile(orig_path, temp_path));
+ // No more asserts until cleanup.
+
+ // WIN32 long path: "c:\Program Files\test_calc.exe"
+ wchar_t short_path[MAX_PATH] = {};
+ DWORD size =
+ ::GetShortPathNameW(temp_path.value().c_str(), short_path, MAX_PATH);
+ EXPECT_TRUE(size > 0 && size < MAX_PATH);
+ // WIN32 short path: "C:\PROGRA~1\TEST_C~1.exe"
+
+ // Sanity check that we actually got a short path above! Small chance
+ // it was disabled in the filesystem setup.
+ EXPECT_NE(temp_path.value().length(), ::wcslen(short_path));
+
+ std::wstring short_form_native_path;
+ EXPECT_TRUE(sandbox::GetNtPathFromWin32Path(std::wstring(short_path),
+ &short_form_native_path));
+ // NT short path: "\Device\HarddiskVolume4\PROGRA~1\TEST_C~1.EXE"
+
+ // Test 1: convert win32 short path to long:
+ std::wstring test1(short_path);
+ EXPECT_TRUE(sandbox::ConvertToLongPath(&test1));
+ EXPECT_TRUE(::wcsicmp(temp_path.value().c_str(), test1.c_str()) == 0);
+ // Expected result: "c:\Program Files\test_calc.exe"
+
+ // Test 2: convert native short path to long:
+ std::wstring drive_letter = temp_path.value().substr(0, 3);
+ std::wstring test2(short_form_native_path);
+ EXPECT_TRUE(sandbox::ConvertToLongPath(&test2, &drive_letter));
+
+ size_t index = short_form_native_path.find_first_of(
+ L'\\', ::wcslen(L"\\Device\\HarddiskVolume"));
+ EXPECT_TRUE(index != std::wstring::npos);
+ std::wstring expected_result = short_form_native_path.substr(0, index + 1);
+ expected_result.append(temp_path.value().substr(3));
+ EXPECT_TRUE(::wcsicmp(expected_result.c_str(), test2.c_str()) == 0);
+ // Expected result: "\Device\HarddiskVolumeX\Program Files\test_calc.exe"
+
+ // clean up
+ EXPECT_TRUE(base::DeleteFileW(temp_path, false));
+
+ return;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/window.cc b/security/sandbox/chromium/sandbox/win/src/window.cc
new file mode 100644
index 0000000000..27645a9006
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/window.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/window.h"
+
+#include <aclapi.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/win/win_util.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/sid.h"
+
+namespace {
+
+// Gets the security attributes of a window object referenced by |handle|. The
+// lpSecurityDescriptor member of the SECURITY_ATTRIBUTES parameter returned
+// must be freed using LocalFree by the caller.
+bool GetSecurityAttributes(HANDLE handle, SECURITY_ATTRIBUTES* attributes) {
+ attributes->bInheritHandle = false;
+ attributes->nLength = sizeof(SECURITY_ATTRIBUTES);
+
+ PACL dacl = nullptr;
+ DWORD result = ::GetSecurityInfo(
+ handle, SE_WINDOW_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr,
+ &dacl, nullptr, &attributes->lpSecurityDescriptor);
+ if (ERROR_SUCCESS == result)
+ return true;
+
+ return false;
+}
+
+} // namespace
+
+namespace sandbox {
+
+ResultCode CreateAltWindowStation(HWINSTA* winsta) {
+ // Get the security attributes from the current window station; we will
+ // use this as the base security attributes for the new window station.
+ HWINSTA current_winsta = ::GetProcessWindowStation();
+ if (!current_winsta)
+ return SBOX_ERROR_CANNOT_GET_WINSTATION;
+
+ SECURITY_ATTRIBUTES attributes = {0};
+ if (!GetSecurityAttributes(current_winsta, &attributes))
+ return SBOX_ERROR_CANNOT_QUERY_WINSTATION_SECURITY;
+
+ // Create the window station using nullptr for the name to ask the os to
+ // generate it.
+ *winsta = ::CreateWindowStationW(
+ nullptr, 0, GENERIC_READ | WINSTA_CREATEDESKTOP, &attributes);
+ if (!*winsta && ::GetLastError() == ERROR_ACCESS_DENIED) {
+ *winsta = ::CreateWindowStationW(
+ nullptr, 0, WINSTA_READATTRIBUTES | WINSTA_CREATEDESKTOP, &attributes);
+ }
+ LocalFree(attributes.lpSecurityDescriptor);
+
+ if (*winsta)
+ return SBOX_ALL_OK;
+
+ return SBOX_ERROR_CANNOT_CREATE_WINSTATION;
+}
+
+ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) {
+ std::wstring desktop_name = L"sbox_alternate_desktop_";
+
+ if (!winsta) {
+ desktop_name += L"local_winstation_";
+ }
+
+ // Append the current PID to the desktop name.
+ wchar_t buffer[16];
+ _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X",
+ ::GetCurrentProcessId());
+ desktop_name += buffer;
+
+ HDESK current_desktop = GetThreadDesktop(GetCurrentThreadId());
+
+ if (!current_desktop)
+ return SBOX_ERROR_CANNOT_GET_DESKTOP;
+
+ // Get the security attributes from the current desktop, we will use this as
+ // the base security attributes for the new desktop.
+ SECURITY_ATTRIBUTES attributes = {0};
+ if (!GetSecurityAttributes(current_desktop, &attributes))
+ return SBOX_ERROR_CANNOT_QUERY_DESKTOP_SECURITY;
+
+ // Back up the current window station, in case we need to switch it.
+ HWINSTA current_winsta = ::GetProcessWindowStation();
+
+ if (winsta) {
+ // We need to switch to the alternate window station before creating the
+ // desktop.
+ if (!::SetProcessWindowStation(winsta)) {
+ ::LocalFree(attributes.lpSecurityDescriptor);
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ }
+
+ // Create the destkop.
+ *desktop = ::CreateDesktop(desktop_name.c_str(), nullptr, nullptr, 0,
+ DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
+ READ_CONTROL | WRITE_DAC | WRITE_OWNER,
+ &attributes);
+ ::LocalFree(attributes.lpSecurityDescriptor);
+
+ if (winsta) {
+ // Revert to the right window station.
+ if (!::SetProcessWindowStation(current_winsta)) {
+ return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION;
+ }
+ }
+
+ if (*desktop) {
+ // Replace the DACL on the new Desktop with a reduced privilege version.
+ // We can soft fail on this for now, as it's just an extra mitigation.
+ static const ACCESS_MASK kDesktopDenyMask =
+ WRITE_DAC | WRITE_OWNER | DELETE | DESKTOP_CREATEMENU |
+ DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK |
+ DESKTOP_JOURNALRECORD | DESKTOP_SWITCHDESKTOP;
+ AddKnownSidToObject(*desktop, SE_WINDOW_OBJECT, Sid(WinRestrictedCodeSid),
+ DENY_ACCESS, kDesktopDenyMask);
+ return SBOX_ALL_OK;
+ }
+
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+}
+
+std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
+ if (!desktop) {
+ NOTREACHED();
+ return std::wstring();
+ }
+
+ std::wstring name;
+ if (winsta) {
+ name = base::win::GetWindowObjectName(winsta);
+ name += L'\\';
+ }
+
+ name += base::win::GetWindowObjectName(desktop);
+ return name;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/window.h b/security/sandbox/chromium/sandbox/win/src/window.h
new file mode 100644
index 0000000000..1fe5b6cc72
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/window.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WINDOW_H_
+#define SANDBOX_SRC_WINDOW_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Creates a window station. The name is generated by the OS. The security
+// descriptor is based on the security descriptor of the current window
+// station.
+ResultCode CreateAltWindowStation(HWINSTA* winsta);
+
+// Creates a desktop. The name is a static string followed by the pid of the
+// current process. The security descriptor on the new desktop is based on the
+// security descriptor of the desktop associated with the current thread.
+// If a winsta is specified, the function will switch to it before creating
+// the desktop. If the functions fails the switch back to the current winsta,
+// the function will return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION.
+ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop);
+
+// Returns the name of the desktop referenced by |desktop|. If a window
+// station is specified, the name is prepended with the window station name,
+// followed by a backslash. This name can be used as the lpDesktop parameter
+// to CreateProcess.
+std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WINDOW_H_
diff --git a/security/sandbox/common/SandboxSettings.cpp b/security/sandbox/common/SandboxSettings.cpp
new file mode 100644
index 0000000000..b0b24bf7d0
--- /dev/null
+++ b/security/sandbox/common/SandboxSettings.cpp
@@ -0,0 +1,229 @@
+/* -*- 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/. */
+
+#include "mozilla/SandboxSettings.h"
+#include "mozISandboxSettings.h"
+#include "nsServiceManagerUtils.h"
+
+#include "mozilla/Components.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/StaticPrefs_webgl.h"
+
+#include "prenv.h"
+
+#ifdef XP_WIN
+# include "mozilla/gfx/gfxVars.h"
+# include "mozilla/WindowsVersion.h"
+# include "nsExceptionHandler.h"
+#endif // XP_WIN
+
+using namespace mozilla;
+
+namespace mozilla {
+
+const char* ContentWin32kLockdownStateToString(
+ nsIXULRuntime::ContentWin32kLockdownState aValue) {
+ switch (aValue) {
+ case nsIXULRuntime::ContentWin32kLockdownState::LockdownEnabled:
+ return "Win32k Lockdown enabled";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::MissingWebRender:
+ return "Win32k Lockdown disabled -- Missing WebRender";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported:
+ return "Win32k Lockdown disabled -- Operating system not supported";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::PrefNotSet:
+ return "Win32k Lockdown disabled -- Preference not set";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::MissingRemoteWebGL:
+ return "Win32k Lockdown disabled -- Missing Remote WebGL";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::MissingNonNativeTheming:
+ return "Win32k Lockdown disabled -- Missing Non-Native Theming";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DecodersArentRemote:
+ return "Win32k Lockdown disabled -- Not all media decoders are remoted "
+ "to Utility Process";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DisabledByEnvVar:
+ return "Win32k Lockdown disabled -- MOZ_ENABLE_WIN32K is set";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DisabledBySafeMode:
+ return "Win32k Lockdown disabled -- Running in Safe Mode";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DisabledByE10S:
+ return "Win32k Lockdown disabled -- E10S is disabled";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DisabledByUserPref:
+ return "Win32k Lockdown disabled -- manually set "
+ "security.sandbox.content.win32k-disable to false";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::EnabledByUserPref:
+ return "Win32k Lockdown enabled -- manually set "
+ "security.sandbox.content.win32k-disable to true";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DisabledByControlGroup:
+ return "Win32k Lockdown disabled -- user in Control Group";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::EnabledByTreatmentGroup:
+ return "Win32k Lockdown enabled -- user in Treatment Group";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::DisabledByDefault:
+ return "Win32k Lockdown disabled -- default value is false";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::EnabledByDefault:
+ return "Win32k Lockdown enabled -- default value is true";
+
+ case nsIXULRuntime::ContentWin32kLockdownState::
+ IncompatibleMitigationPolicy:
+ return "Win32k Lockdown disabled -- Incompatible Windows Exploit "
+ "Protection policies enabled";
+ }
+
+ MOZ_CRASH("Should never reach here");
+}
+
+bool GetContentWin32kLockdownEnabled() {
+ auto state = GetContentWin32kLockdownState();
+ return state ==
+ nsIXULRuntime::ContentWin32kLockdownState::EnabledByUserPref ||
+ state == nsIXULRuntime::ContentWin32kLockdownState::
+ EnabledByTreatmentGroup ||
+ state == nsIXULRuntime::ContentWin32kLockdownState::EnabledByDefault;
+}
+
+nsIXULRuntime::ContentWin32kLockdownState GetContentWin32kLockdownState() {
+#ifdef XP_WIN
+
+ static auto getLockdownState = [] {
+ auto state = GetWin32kLockdownState();
+
+ const char* stateStr = ContentWin32kLockdownStateToString(state);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ContentSandboxWin32kState,
+ nsDependentCString(stateStr));
+
+ return state;
+ };
+
+ static nsIXULRuntime::ContentWin32kLockdownState result = getLockdownState();
+ return result;
+
+#else // XP_WIN
+
+ return nsIXULRuntime::ContentWin32kLockdownState::OperatingSystemNotSupported;
+
+#endif // XP_WIN
+}
+
+int GetEffectiveContentSandboxLevel() {
+ if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+ return 0;
+ }
+ int level = StaticPrefs::security_sandbox_content_level_DoNotUseDirectly();
+// On Windows and macOS, enforce a minimum content sandbox level of 1 (except on
+// Nightly, where it can be set to 0).
+#if !defined(NIGHTLY_BUILD) && (defined(XP_WIN) || defined(XP_MACOSX))
+ if (level < 1) {
+ level = 1;
+ }
+#endif
+#ifdef XP_LINUX
+ // Level 1 was a configuration with default-deny seccomp-bpf but
+ // which allowed direct filesystem access; that required additional
+ // code for the syscall filter which was untested and tended to
+ // bit-rot. It was trivially escapable and was no longer being used
+ // even for debugging, so it has been removed.
+ //
+ // If the content sandbox is enabled, enforce a minimum level of 2.
+ static constexpr int kMinSupportedLevel = 2;
+
+ if (level > 0 && level <= kMinSupportedLevel) {
+ level = kMinSupportedLevel;
+ }
+ // Level 4 and up will break direct access to audio.
+ if (level > 3 && !StaticPrefs::media_cubeb_sandbox()) {
+ level = 3;
+ }
+#endif
+
+ return level;
+}
+
+bool IsContentSandboxEnabled() { return GetEffectiveContentSandboxLevel() > 0; }
+
+int GetEffectiveSocketProcessSandboxLevel() {
+ if (PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX")) {
+ return 0;
+ }
+
+ int level =
+ StaticPrefs::security_sandbox_socket_process_level_DoNotUseDirectly();
+
+ return level;
+}
+
+int GetEffectiveGpuSandboxLevel() {
+ return StaticPrefs::security_sandbox_gpu_level();
+}
+
+#if defined(XP_MACOSX)
+int ClampFlashSandboxLevel(const int aLevel) {
+ const int minLevel = 0;
+ const int maxLevel = 3;
+
+ if (aLevel < minLevel) {
+ return minLevel;
+ }
+
+ if (aLevel > maxLevel) {
+ return maxLevel;
+ }
+ return aLevel;
+}
+#endif
+
+class SandboxSettings final : public mozISandboxSettings {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISANDBOXSETTINGS
+
+ SandboxSettings() = default;
+
+ private:
+ ~SandboxSettings() = default;
+};
+
+NS_IMPL_ISUPPORTS(SandboxSettings, mozISandboxSettings)
+
+NS_IMETHODIMP SandboxSettings::GetEffectiveContentSandboxLevel(
+ int32_t* aRetVal) {
+ *aRetVal = mozilla::GetEffectiveContentSandboxLevel();
+ return NS_OK;
+}
+
+NS_IMETHODIMP SandboxSettings::GetContentWin32kLockdownState(int32_t* aRetVal) {
+ *aRetVal = static_cast<int32_t>(mozilla::GetContentWin32kLockdownState());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SandboxSettings::GetContentWin32kLockdownStateString(nsAString& aString) {
+ nsIXULRuntime::ContentWin32kLockdownState lockdownState =
+ mozilla::GetContentWin32kLockdownState();
+ aString = NS_ConvertASCIItoUTF16(
+ mozilla::ContentWin32kLockdownStateToString(lockdownState));
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+NS_IMPL_COMPONENT_FACTORY(mozISandboxSettings) {
+ return MakeAndAddRef<SandboxSettings>().downcast<nsISupports>();
+}
diff --git a/security/sandbox/common/SandboxSettings.h b/security/sandbox/common/SandboxSettings.h
new file mode 100644
index 0000000000..12b55c8cd2
--- /dev/null
+++ b/security/sandbox/common/SandboxSettings.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_SandboxSettings_h
+#define mozilla_SandboxSettings_h
+#include <cinttypes>
+
+#include "nsIXULRuntime.h"
+
+#ifdef __OpenBSD__
+# include "nsXULAppAPI.h"
+# include "mozilla/ipc/UtilityProcessSandboxing.h"
+#endif
+
+namespace mozilla {
+
+// Return the current sandbox level. This is the
+// "security.sandbox.content.level" preference, but rounded up to the current
+// minimum allowed level. Returns 0 (disabled) if the env var
+// MOZ_DISABLE_CONTENT_SANDBOX is set.
+int GetEffectiveContentSandboxLevel();
+int GetEffectiveSocketProcessSandboxLevel();
+int GetEffectiveGpuSandboxLevel();
+
+// Checks whether the effective content sandbox level is > 0.
+bool IsContentSandboxEnabled();
+
+const char* ContentWin32kLockdownStateToString(
+ nsIXULRuntime::ContentWin32kLockdownState aValue);
+
+bool GetContentWin32kLockdownEnabled();
+
+nsIXULRuntime::ContentWin32kLockdownState GetContentWin32kLockdownState();
+
+#if defined(XP_MACOSX)
+int ClampFlashSandboxLevel(const int aLevel);
+#endif
+
+#if defined(__OpenBSD__)
+bool StartOpenBSDSandbox(GeckoProcessType type,
+ ipc::SandboxingKind kind = ipc::SandboxingKind::COUNT);
+#endif
+
+} // namespace mozilla
+#endif // mozilla_SandboxPolicies_h
diff --git a/security/sandbox/common/components.conf b/security/sandbox/common/components.conf
new file mode 100644
index 0000000000..d538b7e832
--- /dev/null
+++ b/security/sandbox/common/components.conf
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{5516303d-9007-45a0-94b9-940ef134a6e2}',
+ 'contract_ids': ['@mozilla.org/sandbox/sandbox-settings;1'],
+ 'type': 'mozISandboxSettings',
+ },
+]
+
+if defined('MOZ_SANDBOX') and defined('MOZ_DEBUG') and defined('ENABLE_TESTS'):
+ Classes += [
+ {
+ 'cid':
+ '{2306c118-3544-4674-9222-670b88dc07a9}',
+ 'contract_ids': ['@mozilla.org/sandbox/sandbox-test;1'],
+ 'type': 'mozISandboxTest',
+ },
+]
diff --git a/security/sandbox/common/moz.build b/security/sandbox/common/moz.build
new file mode 100644
index 0000000000..0116873a9a
--- /dev/null
+++ b/security/sandbox/common/moz.build
@@ -0,0 +1,54 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Security: Process Sandboxing")
+
+UNIFIED_SOURCES += [
+ "SandboxSettings.cpp",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+XPIDL_SOURCES += [
+ "mozISandboxSettings.idl",
+]
+
+XPIDL_MODULE = "sandbox"
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["MOZ_DEBUG"] and CONFIG["ENABLE_TESTS"]:
+ UNIFIED_SOURCES += [
+ "test/SandboxTest.cpp",
+ "test/SandboxTestingChild.cpp",
+ "test/SandboxTestingParent.cpp",
+ ]
+
+ EXPORTS.mozilla += [
+ "test/SandboxTestingChild.h",
+ "test/SandboxTestingParent.h",
+ ]
+
+ IPDL_SOURCES += [
+ "test/PSandboxTesting.ipdl",
+ ]
+
+ XPIDL_SOURCES += [
+ "test/mozISandboxTest.idl",
+ ]
+
+ LOCAL_INCLUDES += [
+ "/netwerk/base",
+ ]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+EXPORTS.mozilla += [
+ "SandboxSettings.h",
+]
diff --git a/security/sandbox/common/mozISandboxSettings.idl b/security/sandbox/common/mozISandboxSettings.idl
new file mode 100644
index 0000000000..98498b4ea3
--- /dev/null
+++ b/security/sandbox/common/mozISandboxSettings.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: IDL; 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/. */
+
+#include "nsISupports.idl"
+
+/* Used to expose information about the configuration of the sanbox.
+ */
+[scriptable, builtinclass, uuid(5516303d-9007-45a0-94b9-940ef134a6e2)]
+interface mozISandboxSettings : nsISupports
+{
+ readonly attribute long effectiveContentSandboxLevel;
+
+ /*
+ * The possible values for this are defined in the ContentWin32kLockdownState
+ * enum in security/sandbox/common/SandboxSettings.h
+ */
+ readonly attribute long contentWin32kLockdownState;
+ readonly attribute AString contentWin32kLockdownStateString;
+};
+
+%{ C++
+
+#define MOZ_SANDBOX_SETTINGS_CID \
+{0x5516303d, 0x9007, 0x45a0, { 0x94, 0xb9, 0x94, 0x0e, 0xf1, 0x34, 0xa6, 0xe2}}
+
+#define MOZ_SANDBOX_SETTINGS_CONTRACTID \
+ "@mozilla.org/sandbox/sandbox-settings;1"
+
+%}
diff --git a/security/sandbox/common/test/PSandboxTesting.ipdl b/security/sandbox/common/test/PSandboxTesting.ipdl
new file mode 100644
index 0000000000..8498f4a235
--- /dev/null
+++ b/security/sandbox/common/test/PSandboxTesting.ipdl
@@ -0,0 +1,20 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+namespace mozilla {
+
+sync protocol PSandboxTesting {
+parent:
+ async ReportTestResults(nsCString testName, bool passed, nsCString message);
+ async TestCompleted();
+
+ sync GetSpecialDirectory(nsCString aSpecialDirName) returns (nsString aDirPath);
+
+child:
+ async ShutDown();
+};
+
+} //namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTest.cpp b/security/sandbox/common/test/SandboxTest.cpp
new file mode 100644
index 0000000000..0f1aa6c784
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTest.cpp
@@ -0,0 +1,362 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "SandboxTest.h"
+
+#include "mozilla/Components.h"
+#include "mozilla/Preferences.h"
+#include "SandboxTestingParent.h"
+#include "SandboxTestingChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/GPUChild.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/RDDProcessManager.h"
+#include "mozilla/RDDChild.h"
+#include "mozilla/ipc/UtilityProcessManager.h"
+#include "mozilla/ipc/UtilityProcessParent.h"
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#include "GMPService.h"
+#include "mozilla/gmp/GMPTypes.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "nsIOService.h"
+
+#ifdef XP_WIN
+# include "nsAppDirectoryServiceDefs.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::ipc;
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(SandboxTest, mozISandboxTest)
+
+inline void UnsetEnvVariable(const nsCString& aEnvVarName) {
+ nsCString aEnvVarNameFull = aEnvVarName + "="_ns;
+ int rv_unset =
+#ifdef XP_UNIX
+ unsetenv(aEnvVarName.get());
+#endif // XP_UNIX
+#ifdef XP_WIN
+ _putenv(aEnvVarNameFull.get());
+#endif // XP_WIN
+ MOZ_ASSERT(rv_unset == 0, "Error unsetting env var");
+}
+
+GeckoProcessType GeckoProcessStringToType(const nsCString& aString) {
+ for (GeckoProcessType type = GeckoProcessType(0);
+ type < GeckoProcessType::GeckoProcessType_End;
+ type = GeckoProcessType(type + 1)) {
+ if (aString == XRE_GeckoProcessTypeToString(type)) {
+ return type;
+ }
+ }
+ return GeckoProcessType::GeckoProcessType_Invalid;
+}
+
+// Set up tests on remote process connected to the given actor.
+// The actor must handle the InitSandboxTesting message.
+template <typename Actor>
+void InitializeSandboxTestingActors(
+ Actor* aActor,
+ const RefPtr<SandboxTest::ProcessPromise::Private>& aProcessPromise) {
+ MOZ_ASSERT(aActor, "Should have provided an IPC actor");
+ Endpoint<PSandboxTestingParent> sandboxTestingParentEnd;
+ Endpoint<PSandboxTestingChild> sandboxTestingChildEnd;
+ nsresult rv = PSandboxTesting::CreateEndpoints(&sandboxTestingParentEnd,
+ &sandboxTestingChildEnd);
+ if (NS_FAILED(rv)) {
+ aProcessPromise->Reject(NS_ERROR_FAILURE, __func__);
+ return;
+ }
+
+ // GMPlugin binds us to the GMP Thread, so we need IPC's Send to be done on
+ // the same thread
+ Unused << aActor->SendInitSandboxTesting(std::move(sandboxTestingChildEnd));
+ // But then the SandboxTestingParent::Create() call needs to be on the main
+ // thread
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxTestingParent::Create",
+ [stpE = std::move(sandboxTestingParentEnd), aProcessPromise]() mutable {
+ return aProcessPromise->Resolve(
+ SandboxTestingParent::Create(std::move(stpE)), __func__);
+ }));
+}
+
+NS_IMETHODIMP
+SandboxTest::StartTests(const nsTArray<nsCString>& aProcessesList) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+#if defined(XP_WIN)
+ nsCOMPtr<nsIFile> testFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile));
+ MOZ_ASSERT(testFile);
+ nsCOMPtr<nsIFile> testChromeFile;
+ testFile->Clone(getter_AddRefs(testChromeFile));
+ testChromeFile->Append(u"chrome"_ns);
+ testChromeFile->Exists(&mChromeDirExisted);
+ testFile->Append(u"sandboxTest.txt"_ns);
+ testChromeFile->Append(u"sandboxTest.txt"_ns);
+ MOZ_ALWAYS_SUCCEEDS(testFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
+ MOZ_ALWAYS_SUCCEEDS(testChromeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666));
+#endif
+
+ for (const auto& processTypeName : aProcessesList) {
+ SandboxingKind sandboxingKind = SandboxingKind::COUNT;
+ GeckoProcessType type = GeckoProcessType::GeckoProcessType_Invalid;
+ if (processTypeName.Find(":") != kNotFound) {
+ int32_t pos = processTypeName.Find(":");
+ nsCString processType = nsCString(Substring(processTypeName, 0, pos));
+ nsCString sandboxKindStr = nsCString(
+ Substring(processTypeName, pos + 1, processTypeName.Length()));
+
+ nsresult err;
+ uint64_t sbVal = (uint64_t)(sandboxKindStr.ToDouble(&err));
+ if (NS_FAILED(err)) {
+ NS_WARNING("Unable to get SandboxingKind");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (sbVal >= SandboxingKind::COUNT) {
+ NS_WARNING("Invalid sandboxing kind");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (!processType.Equals(
+ XRE_GeckoProcessTypeToString(GeckoProcessType_Utility))) {
+ NS_WARNING("Expected utility process type");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ sandboxingKind = (SandboxingKind)sbVal;
+ type = GeckoProcessType_Utility;
+ } else {
+ type = GeckoProcessStringToType(processTypeName);
+
+ if (type == GeckoProcessType::GeckoProcessType_Invalid) {
+ NS_WARNING("Invalid process type");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ }
+
+ RefPtr<ProcessPromise::Private> processPromise =
+ MakeRefPtr<ProcessPromise::Private>(__func__);
+
+ switch (type) {
+ case GeckoProcessType_Content: {
+ nsTArray<ContentParent*> parents;
+ ContentParent::GetAll(parents);
+ if (parents[0]) {
+ InitializeSandboxTestingActors(parents[0], processPromise);
+ } else {
+ processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get Content process");
+ }
+ break;
+ }
+
+ case GeckoProcessType_GPU: {
+ gfx::GPUProcessManager* gpuProc = gfx::GPUProcessManager::Get();
+ gfx::GPUChild* gpuChild = gpuProc ? gpuProc->GetGPUChild() : nullptr;
+ if (gpuChild) {
+ InitializeSandboxTestingActors(gpuChild, processPromise);
+ } else {
+ processPromise->Reject(NS_OK, __func__);
+ }
+ break;
+ }
+
+ case GeckoProcessType_RDD: {
+ RDDProcessManager* rddProc = RDDProcessManager::Get();
+ rddProc->LaunchRDDProcess()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [processPromise, rddProc]() {
+ RDDChild* rddChild = rddProc ? rddProc->GetRDDChild() : nullptr;
+ if (rddChild) {
+ return InitializeSandboxTestingActors(rddChild, processPromise);
+ }
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ },
+ [processPromise](nsresult aError) {
+ MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get RDD process");
+ return processPromise->Reject(aError, __func__);
+ });
+ break;
+ }
+
+ case GeckoProcessType_GMPlugin: {
+ UnsetEnvVariable("MOZ_DISABLE_GMP_SANDBOX"_ns);
+ RefPtr<gmp::GeckoMediaPluginService> service =
+ gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+ MOZ_ASSERT(service, "We have a GeckoMediaPluginService");
+
+ RefPtr<SandboxTest> self = this;
+ nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread();
+ nsresult rv = thread->Dispatch(NS_NewRunnableFunction(
+ "SandboxTest::GMPlugin", [self, processPromise, service, thread]() {
+ service->GetContentParentForTest()->Then(
+ thread, __func__,
+ [self, processPromise](
+ const RefPtr<gmp::GMPContentParent::CloseBlocker>&
+ wrapper) {
+ RefPtr<gmp::GMPContentParent> parent = wrapper->mParent;
+ MOZ_ASSERT(parent,
+ "Wrapper should wrap a valid parent if we're in "
+ "this path.");
+ if (!parent) {
+ return processPromise->Reject(NS_ERROR_ILLEGAL_VALUE,
+ __func__);
+ }
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxTesting::Wrapper", [self, wrapper]() {
+ self->mGMPContentParentWrapper = wrapper;
+ }));
+ return InitializeSandboxTestingActors(parent.get(),
+ processPromise);
+ },
+ [processPromise](const MediaResult& rv) {
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ });
+ }));
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+
+ case GeckoProcessType_Socket: {
+ // mochitest harness force this variable, but we actually do not want
+ // that
+ UnsetEnvVariable("MOZ_DISABLE_SOCKET_PROCESS"_ns);
+
+ nsresult rv_pref =
+ Preferences::SetBool("network.process.enabled", true);
+ MOZ_ASSERT(rv_pref == NS_OK, "Error enforcing pref");
+
+ MOZ_ASSERT(net::gIOService, "No gIOService?");
+
+ net::gIOService->CallOrWaitForSocketProcess([processPromise]() {
+ // If socket process was previously disabled by env,
+ // nsIOService code will take some time before it creates the new
+ // process and it triggers this callback
+ net::SocketProcessParent* parent =
+ net::SocketProcessParent::GetSingleton();
+ if (parent) {
+ return InitializeSandboxTestingActors(parent, processPromise);
+ }
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ });
+ break;
+ }
+
+ case GeckoProcessType_Utility: {
+ RefPtr<UtilityProcessManager> utilityProc =
+ UtilityProcessManager::GetSingleton();
+ utilityProc->LaunchProcess(sandboxingKind)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [processPromise, utilityProc, sandboxingKind]() {
+ RefPtr<UtilityProcessParent> utilityParent =
+ utilityProc
+ ? utilityProc->GetProcessParent(sandboxingKind)
+ : nullptr;
+ if (utilityParent) {
+ return InitializeSandboxTestingActors(utilityParent.get(),
+ processPromise);
+ }
+ return processPromise->Reject(NS_ERROR_FAILURE, __func__);
+ },
+ [processPromise](nsresult aError) {
+ MOZ_ASSERT_UNREACHABLE(
+ "SandboxTest; failure to get Utility process");
+ return processPromise->Reject(aError, __func__);
+ });
+ break;
+ }
+
+ default:
+ MOZ_ASSERT_UNREACHABLE(
+ "SandboxTest does not yet support this process type");
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ RefPtr<SandboxTest> self = this;
+ RefPtr<ProcessPromise> aPromise(processPromise);
+ aPromise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [self](RefPtr<SandboxTestingParent> aValue) {
+ self->mSandboxTestingParents.AppendElement(std::move(aValue));
+ return NS_OK;
+ },
+ [](nsresult aError) {
+ if (aError == NS_OK) {
+ // There is no such process for this OS. Report test done.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "sandbox-test-done",
+ nullptr);
+ return NS_OK;
+ }
+ MOZ_ASSERT_UNREACHABLE("SandboxTest; failure to get a process");
+ return NS_ERROR_FAILURE;
+ });
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SandboxTest::FinishTests() {
+ if (mGMPContentParentWrapper) {
+ RefPtr<gmp::GeckoMediaPluginService> service =
+ gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+ MOZ_ASSERT(service, "We have a GeckoMediaPluginService");
+
+ nsCOMPtr<nsISerialEventTarget> thread = service->GetGMPThread();
+ nsresult rv = thread->Dispatch(NS_NewRunnableFunction(
+ "SandboxTest::FinishTests",
+ [wrapper = std::move(mGMPContentParentWrapper)]() {
+ // Release mGMPContentWrapper's reference. We hold this to keep an
+ // active reference on the CloseBlocker produced by GMPService,
+ // otherwise it would automatically shutdown the GMPlugin thread we
+ // started.
+ // If somehow it does not work as expected, then tests will fail
+ // because of leaks happening on GMPService and others.
+ }));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ for (RefPtr<SandboxTestingParent>& stp : mSandboxTestingParents) {
+ SandboxTestingParent::Destroy(stp.forget());
+ }
+
+ // Make sure there is no leftover for test --verify to run without failure
+ mSandboxTestingParents.Clear();
+
+#if defined(XP_WIN)
+ nsCOMPtr<nsIFile> testFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(testFile));
+ MOZ_ASSERT(testFile);
+ nsCOMPtr<nsIFile> testChromeFile;
+ testFile->Clone(getter_AddRefs(testChromeFile));
+ testChromeFile->Append(u"chrome"_ns);
+ testFile->Append(u"sandboxTest.txt"_ns);
+ if (mChromeDirExisted) {
+ // Chrome dir existed, just delete test file.
+ testChromeFile->Append(u"sandboxTest.txt"_ns);
+ }
+ testFile->Remove(false);
+ testChromeFile->Remove(true);
+#endif
+
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+NS_IMPL_COMPONENT_FACTORY(mozISandboxTest) {
+ return MakeAndAddRef<SandboxTest>().downcast<nsISupports>();
+}
diff --git a/security/sandbox/common/test/SandboxTest.h b/security/sandbox/common/test/SandboxTest.h
new file mode 100644
index 0000000000..1d8c18c4b8
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTest.h
@@ -0,0 +1,45 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_SandboxTest_h
+#define mozilla_SandboxTest_h
+
+#include "SandboxTestingParent.h"
+#include "mozISandboxTest.h"
+#include "mozilla/GfxMessageUtils.h"
+#include "mozilla/MozPromise.h"
+#include "GMPService.h"
+#include "nsTArray.h"
+
+#if !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTest : public mozISandboxTest {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_MOZISANDBOXTEST
+
+ SandboxTest() : mSandboxTestingParents{nullptr} {};
+
+ // We allow nsresult to be rejected with values:
+ // - NS_ERROR_FAILURE in obvious case of error
+ // - NS_OK in case of success to complete the code but missing process (GPU)
+ using ProcessPromise =
+ MozPromise<RefPtr<SandboxTestingParent>, nsresult, true>;
+
+ private:
+ virtual ~SandboxTest() = default;
+ nsTArray<RefPtr<SandboxTestingParent>> mSandboxTestingParents;
+ RefPtr<gmp::GMPContentParent::CloseBlocker> mGMPContentParentWrapper;
+#if defined(XP_WIN)
+ bool mChromeDirExisted = false;
+#endif
+};
+
+} // namespace mozilla
+#endif // mozilla_SandboxTest_h
diff --git a/security/sandbox/common/test/SandboxTestingChild.cpp b/security/sandbox/common/test/SandboxTestingChild.cpp
new file mode 100644
index 0000000000..3a9d61556c
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingChild.cpp
@@ -0,0 +1,194 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "SandboxTestingChild.h"
+#include "SandboxTestingChildTests.h"
+#include "SandboxTestingThread.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#include "mozilla/ipc/UtilityProcessChild.h"
+
+#ifdef XP_LINUX
+# include "mozilla/Sandbox.h"
+#endif
+
+#include "nsXULAppAPI.h"
+
+namespace mozilla {
+
+StaticRefPtr<SandboxTestingChild> SandboxTestingChild::sInstance;
+
+bool SandboxTestingChild::IsTestThread() { return mThread->IsOnThread(); }
+
+void SandboxTestingChild::PostToTestThread(
+ already_AddRefed<nsIRunnable>&& runnable) {
+ mThread->Dispatch(std::move(runnable));
+}
+
+/* static */
+bool SandboxTestingChild::Initialize(
+ Endpoint<PSandboxTestingChild>&& aSandboxTestingEndpoint) {
+ MOZ_ASSERT(!sInstance);
+ SandboxTestingThread* thread = SandboxTestingThread::Create();
+ if (!thread) {
+ return false;
+ }
+ sInstance =
+ new SandboxTestingChild(thread, std::move(aSandboxTestingEndpoint));
+ thread->Dispatch(NewRunnableMethod<Endpoint<PSandboxTestingChild>&&>(
+ "SandboxTestingChild::Bind", sInstance.get(), &SandboxTestingChild::Bind,
+ std::move(aSandboxTestingEndpoint)));
+ return true;
+}
+
+/* static */
+SandboxTestingChild* SandboxTestingChild::GetInstance() {
+ MOZ_ASSERT(sInstance, "Must initialize SandboxTestingChild before using it");
+ return sInstance;
+}
+
+SandboxTestingChild::SandboxTestingChild(
+ SandboxTestingThread* aThread, Endpoint<PSandboxTestingChild>&& aEndpoint)
+ : mThread(aThread) {}
+
+SandboxTestingChild::~SandboxTestingChild() = default;
+
+void SandboxTestingChild::Bind(Endpoint<PSandboxTestingChild>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+
+#ifdef XP_LINUX
+ bool sandboxCrashOnError = SetSandboxCrashOnError(false);
+#endif
+
+ if (XRE_IsContentProcess()) {
+ RunTestsContent(this);
+ }
+
+ if (XRE_IsRDDProcess()) {
+ RunTestsRDD(this);
+ }
+
+ if (XRE_IsGMPluginProcess()) {
+ RunTestsGMPlugin(this);
+ }
+
+ if (XRE_IsSocketProcess()) {
+ RunTestsSocket(this);
+ }
+
+ if (XRE_IsGPUProcess()) {
+ RunTestsGPU(this);
+ }
+
+ if (XRE_IsUtilityProcess()) {
+ RefPtr<ipc::UtilityProcessChild> s = ipc::UtilityProcessChild::Get();
+ MOZ_ASSERT(s, "Unable to grab a UtilityProcessChild");
+ switch (s->mSandbox) {
+ case ipc::SandboxingKind::GENERIC_UTILITY:
+ RunTestsGenericUtility(this);
+#ifdef MOZ_APPLEMEDIA
+ [[fallthrough]];
+ case ipc::SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA:
+#endif
+#ifdef XP_WIN
+ [[fallthrough]];
+ case ipc::SandboxingKind::UTILITY_AUDIO_DECODING_WMF:
+#endif
+ RunTestsUtilityAudioDecoder(this, s->mSandbox);
+ break;
+
+ default:
+ MOZ_ASSERT(false, "Invalid SandboxingKind");
+ break;
+ }
+ }
+
+#ifdef XP_LINUX
+ SetSandboxCrashOnError(sandboxCrashOnError);
+#endif
+
+ // Tell SandboxTest that this process is done with all tests.
+ SendTestCompleted();
+}
+
+void SandboxTestingChild::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(mThread->IsOnThread());
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxChildDestroyer", []() { SandboxTestingChild::Destroy(); }));
+}
+
+void SandboxTestingChild::Destroy() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sInstance);
+ sInstance = nullptr;
+}
+
+ipc::IPCResult SandboxTestingChild::RecvShutDown() {
+ Close();
+ return IPC_OK();
+}
+
+void SandboxTestingChild::ReportNoTests() {
+ SendReportTestResults("dummy_test"_ns, /* passed */ true,
+ "The test framework fails if there are no cases."_ns);
+}
+
+template <typename F>
+void SandboxTestingChild::ErrnoTest(const nsCString& aName, bool aExpectSuccess,
+ F&& aFunction) {
+ int status = aFunction() >= 0 ? 0 : errno;
+ PosixTest(aName, aExpectSuccess, status);
+}
+
+template <typename F>
+void SandboxTestingChild::ErrnoValueTest(const nsCString& aName,
+ int aExpectedErrno, F&& aFunction) {
+ int status = aFunction() >= 0 ? 0 : errno;
+ PosixTest(aName, aExpectedErrno == 0, status, Some(aExpectedErrno));
+}
+
+void SandboxTestingChild::PosixTest(const nsCString& aName, bool aExpectSuccess,
+ int aStatus, Maybe<int> aExpectedError) {
+ nsAutoCString message;
+ bool passed;
+
+ // The "expected" arguments are a little redundant.
+ MOZ_ASSERT(!aExpectedError || aExpectSuccess == (*aExpectedError == 0));
+
+ // Decide whether the test passed, and stringify the actual result.
+ if (aStatus == 0) {
+ message = "Succeeded"_ns;
+ passed = aExpectSuccess;
+ } else {
+ message = "Error: "_ns;
+ message += strerror(aStatus);
+ if (aExpectedError) {
+ passed = aStatus == *aExpectedError;
+ } else {
+ passed = !aExpectSuccess;
+ }
+ }
+
+ // If something unexpected happened, mention the expected result.
+ if (!passed) {
+ message += "; expected ";
+ if (aExpectSuccess) {
+ message += "success";
+ } else {
+ message += "error";
+ if (aExpectedError) {
+ message += ": ";
+ message += strerror(*aExpectedError);
+ }
+ }
+ }
+
+ SendReportTestResults(aName, passed, message);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTestingChild.h b/security/sandbox/common/test/SandboxTestingChild.h
new file mode 100644
index 0000000000..45c9893a63
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingChild.h
@@ -0,0 +1,86 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SandboxTestingChild_h
+#define mozilla_SandboxTestingChild_h
+
+#include "mozilla/PSandboxTestingChild.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsISupports.h"
+
+#ifdef XP_UNIX
+# include "nsString.h"
+#endif
+
+#if !defined(MOZ_SANDBOX) || !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTestingThread;
+
+/**
+ * Runs tests that check sandbox in child process, depending on process type.
+ */
+class SandboxTestingChild : public PSandboxTestingChild {
+ public:
+ static bool Initialize(
+ Endpoint<PSandboxTestingChild>&& aSandboxTestingEndpoint);
+ static SandboxTestingChild* GetInstance();
+ static void Destroy();
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SandboxTestingChild, override)
+
+ bool IsTestThread();
+ void PostToTestThread(already_AddRefed<nsIRunnable>&& runnable);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual ipc::IPCResult RecvShutDown();
+
+ // Helper to return that no test have been executed. Tests should make sure
+ // they have some fallback through that otherwise the framework will consider
+ // absence of test report as a failure.
+ inline void ReportNoTests();
+
+ // For test cases that return an error number or 0, like newer POSIX
+ // APIs. If `aExpectSuccess` is true, the test passes if the status is
+ // 0; otherwise, the test requires a specific error if `aExpectedError`
+ // is `Some(n)` or any nonzero status if it's `Nothing()`.
+ void PosixTest(const nsCString& aName, bool aExpectSuccess, int aStatus,
+ Maybe<int> aExpectedError = Nothing());
+
+ // For test cases that return a negative number and set `errno` to
+ // indicate error, like classical Unix APIs; takes a callable, which
+ // is used only in this function call (so `[&]` captures are safe).
+ template <typename F>
+ void ErrnoTest(const nsCString& aName, bool aExpectSuccess, F&& aFunction);
+
+ // Similar to ErrnoTest, except that we want to compare a specific `errno`
+ // being returned.
+ template <typename F>
+ void ErrnoValueTest(const nsCString& aName, int aExpectedErrno,
+ F&& aFunction);
+
+ private:
+ explicit SandboxTestingChild(SandboxTestingThread* aThread,
+ Endpoint<PSandboxTestingChild>&& aEndpoint);
+ ~SandboxTestingChild();
+
+ void Bind(Endpoint<PSandboxTestingChild>&& aEndpoint);
+
+ UniquePtr<SandboxTestingThread> mThread;
+
+ static StaticRefPtr<SandboxTestingChild> sInstance;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxTestingChild_h
diff --git a/security/sandbox/common/test/SandboxTestingChildTests.h b/security/sandbox/common/test/SandboxTestingChildTests.h
new file mode 100644
index 0000000000..3dd748daba
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingChildTests.h
@@ -0,0 +1,876 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "SandboxTestingChild.h"
+
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#ifdef XP_MACOSX
+# include "nsCocoaFeatures.h"
+#endif
+#include "nsXULAppAPI.h"
+
+#ifdef XP_UNIX
+# include <fcntl.h>
+# include <netdb.h>
+# ifdef XP_LINUX
+# include <linux/mempolicy.h>
+# include <sched.h>
+# include <sys/ioctl.h>
+# include <sys/prctl.h>
+# include <sys/resource.h>
+# include <sys/socket.h>
+# include <sys/statfs.h>
+# include <sys/syscall.h>
+# include <sys/sysmacros.h>
+# include <sys/time.h>
+# include <sys/un.h>
+# include <sys/utsname.h>
+# include <termios.h>
+# include "mozilla/ProcInfo_linux.h"
+# include "mozilla/UniquePtrExtensions.h"
+# ifdef MOZ_X11
+# include "X11/Xlib.h"
+# include "X11UndefineNone.h"
+# endif // MOZ_X11
+# endif // XP_LINUX
+# include <sys/socket.h>
+# include <sys/stat.h>
+# include <sys/types.h>
+# include <time.h>
+# include <unistd.h>
+#endif
+
+#ifdef XP_MACOSX
+# if defined(__SSE2__) || defined(_M_X64) || \
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
+# include "emmintrin.h"
+# endif
+# include <spawn.h>
+# include <CoreFoundation/CoreFoundation.h>
+# include <CoreGraphics/CoreGraphics.h>
+# include <AudioToolbox/AudioToolbox.h>
+namespace ApplicationServices {
+# include <ApplicationServices/ApplicationServices.h>
+}
+#endif
+
+#ifdef XP_WIN
+# include <stdio.h>
+# include <winternl.h>
+
+# include "mozilla/DynamicallyLinkedFunctionPtr.h"
+# include "nsAppDirectoryServiceDefs.h"
+# include "mozilla/WindowsProcessMitigations.h"
+#endif
+
+#ifdef XP_LINUX
+// Defined in <linux/watch_queue.h> which was added in 5.8
+# ifndef O_NOTIFICATION_PIPE
+# define O_NOTIFICATION_PIPE O_EXCL
+# endif
+#endif
+
+constexpr bool kIsDebug =
+#ifdef DEBUG
+ true;
+#else
+ false;
+#endif
+
+namespace mozilla {
+
+#ifdef XP_LINUX
+static void RunTestsSched(SandboxTestingChild* child) {
+ struct sched_param param_pid_0 = {};
+ child->ErrnoTest("sched_getparam(0)"_ns, true,
+ [&] { return sched_getparam(0, &param_pid_0); });
+
+ struct sched_param param_pid_tid = {};
+ child->ErrnoTest("sched_getparam(tid)"_ns, true, [&] {
+ return sched_getparam((pid_t)syscall(__NR_gettid), &param_pid_tid);
+ });
+
+ struct sched_param param_pid_Ntid = {};
+ child->ErrnoValueTest("sched_getparam(Ntid)"_ns, EPERM, [&] {
+ return sched_getparam((pid_t)(syscall(__NR_gettid) - 1), &param_pid_Ntid);
+ });
+}
+#endif
+
+// Tests that apply to every process type (more or less)
+static void RunGenericTests(SandboxTestingChild* child, bool aIsGMP = false) {
+#ifdef XP_LINUX
+ // Check ABI issues with 32-bit arguments on 64-bit platforms.
+ if (sizeof(void*) == 8) {
+ static constexpr uint64_t kHighBits = 0xDEADBEEF00000000;
+
+ struct timespec ts0, ts1;
+ child->ErrnoTest("high_bits_gettime"_ns, true, [&] {
+ return syscall(__NR_clock_gettime, kHighBits | CLOCK_MONOTONIC, &ts0);
+ });
+ // Try to make sure we got the correct clock by reading it again and
+ // comparing to see if the times are vaguely similar.
+ int rv = clock_gettime(CLOCK_MONOTONIC, &ts1);
+ MOZ_RELEASE_ASSERT(rv == 0);
+ MOZ_RELEASE_ASSERT(ts0.tv_sec <= ts1.tv_sec + 1);
+ MOZ_RELEASE_ASSERT(ts1.tv_sec <= ts0.tv_sec + 60);
+
+ // Check some non-zeroth arguments. (fcntl is convenient for
+ // this, but GMP has a stricter policy, so skip it there.)
+ if (!aIsGMP) {
+ int flags;
+ child->ErrnoTest("high_bits_fcntl_getfl"_ns, true, [&] {
+ flags = syscall(__NR_fcntl, 0, kHighBits | F_GETFL);
+ return flags;
+ });
+ MOZ_RELEASE_ASSERT(flags == fcntl(0, F_GETFL));
+
+ int fds[2];
+ rv = pipe(fds);
+ MOZ_RELEASE_ASSERT(rv >= 0);
+ child->ErrnoTest("high_bits_fcntl_setfl"_ns, true, [&] {
+ return syscall(__NR_fcntl, fds[0], kHighBits | F_SETFL,
+ kHighBits | O_NONBLOCK);
+ });
+ flags = fcntl(fds[0], F_GETFL);
+ MOZ_RELEASE_ASSERT(flags >= 0);
+ MOZ_RELEASE_ASSERT(flags & O_NONBLOCK);
+ }
+ }
+#endif // XP_LINUX
+}
+
+#ifdef XP_WIN
+/**
+ * Uses NtCreateFile directly to test file system brokering.
+ *
+ */
+static void FileTest(const nsCString& aName, const char* aSpecialDirName,
+ const nsString& aRelativeFilePath, ACCESS_MASK aAccess,
+ bool aExpectSuccess, SandboxTestingChild* aChild) {
+ static const StaticDynamicallyLinkedFunctionPtr<decltype(&NtCreateFile)>
+ pNtCreateFile(L"ntdll.dll", "NtCreateFile");
+ static const StaticDynamicallyLinkedFunctionPtr<decltype(&NtClose)> pNtClose(
+ L"ntdll.dll", "NtClose");
+
+ // Start the filename with the NT namespace
+ nsString testFilename(u"\\??\\"_ns);
+ nsString dirPath;
+ aChild->SendGetSpecialDirectory(nsDependentCString(aSpecialDirName),
+ &dirPath);
+ testFilename.Append(dirPath);
+ testFilename.AppendLiteral("\\");
+ testFilename.Append(aRelativeFilePath);
+
+ UNICODE_STRING uniFileName;
+ ::RtlInitUnicodeString(&uniFileName, testFilename.get());
+
+ OBJECT_ATTRIBUTES objectAttributes;
+ InitializeObjectAttributes(&objectAttributes, &uniFileName,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ HANDLE fileHandle = INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK ioStatusBlock = {};
+
+ ULONG createOptions = StringEndsWith(testFilename, u"\\"_ns) ||
+ StringEndsWith(testFilename, u"/"_ns)
+ ? FILE_DIRECTORY_FILE
+ : FILE_NON_DIRECTORY_FILE;
+ NTSTATUS status = pNtCreateFile(
+ &fileHandle, aAccess, &objectAttributes, &ioStatusBlock, nullptr, 0, 0,
+ FILE_OPEN_IF, createOptions | FILE_SYNCHRONOUS_IO_NONALERT, nullptr, 0);
+
+ if (fileHandle != INVALID_HANDLE_VALUE) {
+ pNtClose(fileHandle);
+ }
+
+ nsCString accessString;
+ if ((aAccess & FILE_GENERIC_READ) == FILE_GENERIC_READ) {
+ accessString.AppendLiteral("r");
+ }
+ if ((aAccess & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE) {
+ accessString.AppendLiteral("w");
+ }
+ if ((aAccess & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE) {
+ accessString.AppendLiteral("e");
+ }
+
+ nsCString msgRelPath = NS_ConvertUTF16toUTF8(aRelativeFilePath);
+ for (size_t i = 0, j = 0; i < aRelativeFilePath.Length(); ++i, ++j) {
+ if (aRelativeFilePath[i] == u'\\') {
+ msgRelPath.Insert('\\', j++);
+ }
+ }
+
+ nsCString message;
+ message.AppendPrintf(
+ "Special dir: %s, file: %s, access: %s , returned status: %lx",
+ aSpecialDirName, msgRelPath.get(), accessString.get(), status);
+
+ aChild->SendReportTestResults(aName, aExpectSuccess == NT_SUCCESS(status),
+ message);
+}
+#endif
+
+#ifdef XP_MACOSX
+/*
+ * Test if this process can launch another process with posix_spawnp,
+ * exec, and LSOpenCFURLRef. All launches are expected to fail. In processes
+ * where the sandbox permits reading of file metadata (content processes at
+ * this time), we expect the posix_spawnp error to be EPERM. In processes
+ * without that permission, we expect ENOENT. Changing the sandbox policy
+ * may break this assumption, but the important aspect to test for is that the
+ * launch is not permitted.
+ */
+void RunMacTestLaunchProcess(SandboxTestingChild* child,
+ int aPosixSpawnExpectedError = ENOENT) {
+ // Test that posix_spawnp fails
+ char* argv[2];
+ argv[0] = const_cast<char*>("bash");
+ argv[1] = NULL;
+ int rv = posix_spawnp(NULL, "/bin/bash", NULL, NULL, argv, NULL);
+ nsPrintfCString posixSpawnMessage("posix_spawnp returned %d, expected %d", rv,
+ aPosixSpawnExpectedError);
+ child->SendReportTestResults("posix_spawnp test"_ns,
+ rv == aPosixSpawnExpectedError,
+ posixSpawnMessage);
+
+ // Test that exec fails
+ child->ErrnoTest("execv /bin/bash test"_ns, false, [&] {
+ char* argvp = NULL;
+ return execv("/bin/bash", &argvp);
+ });
+
+ // Test that launching an application using LSOpenCFURLRef fails
+ char* uri;
+ if (nsCocoaFeatures::OnCatalinaOrLater()) {
+ uri = const_cast<char*>("/System/Applications/Utilities/Console.app");
+ } else {
+ uri = const_cast<char*>("/Applications/Utilities/Console.app");
+ }
+ CFStringRef filePath = ::CFStringCreateWithCString(kCFAllocatorDefault, uri,
+ kCFStringEncodingUTF8);
+ CFURLRef urlRef = ::CFURLCreateWithFileSystemPath(
+ kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
+ if (!urlRef) {
+ child->SendReportTestResults("LSOpenCFURLRef"_ns, false,
+ "CFURLCreateWithFileSystemPath failed"_ns);
+ return;
+ }
+
+ OSStatus status = ApplicationServices::LSOpenCFURLRef(urlRef, NULL);
+ ::CFRelease(urlRef);
+ nsPrintfCString lsMessage(
+ "LSOpenCFURLRef returned %d, "
+ "expected kLSServerCommunicationErr (%d)",
+ status, ApplicationServices::kLSServerCommunicationErr);
+ child->SendReportTestResults(
+ "LSOpenCFURLRef"_ns,
+ status == ApplicationServices::kLSServerCommunicationErr, lsMessage);
+}
+
+/*
+ * Test if this process can connect to the macOS window server.
+ * When |aShouldHaveAccess| is true, the test passes if access is __permitted__.
+ * When |aShouldHaveAccess| is false, the test passes if access is __blocked__.
+ */
+void RunMacTestWindowServer(SandboxTestingChild* child,
+ bool aShouldHaveAccess = false) {
+ // CGSessionCopyCurrentDictionary() returns NULL when a
+ // connection to the window server is not available.
+ CFDictionaryRef windowServerDict = CGSessionCopyCurrentDictionary();
+ bool gotWindowServerDetails = (windowServerDict != nullptr);
+ bool testPassed = (gotWindowServerDetails == aShouldHaveAccess);
+ child->SendReportTestResults(
+ "CGSessionCopyCurrentDictionary"_ns, testPassed,
+ gotWindowServerDetails
+ ? "dictionary returned, access is permitted"_ns
+ : "no dictionary returned, access appears blocked"_ns);
+ if (windowServerDict != nullptr) {
+ CFRelease(windowServerDict);
+ }
+}
+
+/*
+ * Test if this process can get access to audio components on macOS.
+ * When |aShouldHaveAccess| is true, the test passes if access is __permitted__.
+ * When |aShouldHaveAccess| is false, the test passes if access is __blocked__.
+ */
+void RunMacTestAudioAPI(SandboxTestingChild* child,
+ bool aShouldHaveAccess = false) {
+ AudioStreamBasicDescription inputFormat;
+ inputFormat.mFormatID = kAudioFormatMPEG4AAC;
+ inputFormat.mSampleRate = 48000.0;
+ inputFormat.mChannelsPerFrame = 2;
+ inputFormat.mBitsPerChannel = 0;
+ inputFormat.mFormatFlags = 0;
+ inputFormat.mFramesPerPacket = 1024;
+ inputFormat.mBytesPerPacket = 0;
+
+ UInt32 inputFormatSize = sizeof(inputFormat);
+ OSStatus status = AudioFormatGetProperty(
+ kAudioFormatProperty_FormatInfo, 0, NULL, &inputFormatSize, &inputFormat);
+
+ bool gotAudioFormat = (status == 0);
+ bool testPassed = (gotAudioFormat == aShouldHaveAccess);
+ child->SendReportTestResults(
+ "AudioFormatGetProperty"_ns, testPassed,
+ gotAudioFormat ? "got audio format, access is permitted"_ns
+ : "no audio format, access appears blocked"_ns);
+}
+#endif /* XP_MACOSX */
+
+#ifdef XP_WIN
+void RunWinTestWin32k(SandboxTestingChild* child,
+ bool aShouldHaveAccess = true) {
+ bool isLockedDown = (IsWin32kLockedDown() == true);
+ bool testPassed = (isLockedDown == aShouldHaveAccess);
+ child->SendReportTestResults(
+ "Win32kLockdown"_ns, testPassed,
+ isLockedDown ? "got lockdown, access is blocked"_ns
+ : "no lockdown, access appears permitted"_ns);
+}
+#endif // XP_WIN
+
+void RunTestsContent(SandboxTestingChild* child) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child);
+
+#ifdef XP_UNIX
+ struct stat st;
+ static const char kAllowedPath[] = "/usr/lib";
+
+ child->ErrnoTest("fstatat_as_stat"_ns, true,
+ [&] { return fstatat(AT_FDCWD, kAllowedPath, &st, 0); });
+ child->ErrnoTest("fstatat_as_lstat"_ns, true, [&] {
+ return fstatat(AT_FDCWD, kAllowedPath, &st, AT_SYMLINK_NOFOLLOW);
+ });
+
+# ifdef XP_LINUX
+ child->ErrnoTest("fstatat_as_fstat"_ns, true,
+ [&] { return fstatat(0, "", &st, AT_EMPTY_PATH); });
+
+ const struct timespec usec = {0, 1000};
+ child->ErrnoTest("nanosleep"_ns, true,
+ [&] { return nanosleep(&usec, nullptr); });
+
+ struct timespec res = {0, 0};
+ child->ErrnoTest("clock_getres"_ns, true,
+ [&] { return clock_getres(CLOCK_REALTIME, &res); });
+
+ // same process is allowed
+ struct timespec tproc = {0, 0};
+ clockid_t same_process = MAKE_PROCESS_CPUCLOCK(getpid(), CPUCLOCK_SCHED);
+ child->ErrnoTest("clock_gettime_same_process"_ns, true,
+ [&] { return clock_gettime(same_process, &tproc); });
+
+ // different process is blocked by sandbox (SIGSYS, kernel would return
+ // EINVAL)
+ struct timespec tprocd = {0, 0};
+ clockid_t diff_process = MAKE_PROCESS_CPUCLOCK(1, CPUCLOCK_SCHED);
+ child->ErrnoValueTest("clock_gettime_diff_process"_ns, ENOSYS,
+ [&] { return clock_gettime(diff_process, &tprocd); });
+
+ // thread is allowed
+ struct timespec tthread = {0, 0};
+ clockid_t thread =
+ MAKE_THREAD_CPUCLOCK((pid_t)syscall(__NR_gettid), CPUCLOCK_SCHED);
+ child->ErrnoTest("clock_gettime_thread"_ns, true,
+ [&] { return clock_gettime(thread, &tthread); });
+
+ // getcpu is allowed
+ // We're using syscall directly because:
+ // - sched_getcpu uses vdso and as a result doesn't go through the sandbox.
+ // - getcpu isn't defined in the header files we're using yet.
+ int c;
+ child->ErrnoTest("getcpu"_ns, true,
+ [&] { return syscall(SYS_getcpu, &c, NULL, NULL); });
+
+ // An abstract socket that does not starts with '/', so we don't want it to
+ // work.
+ // Checking ENETUNREACH should be thrown by SandboxBrokerClient::Connect()
+ // when it detects it does not starts with a '/'
+ child->ErrnoValueTest("connect_abstract_blocked"_ns, ENETUNREACH, [&] {
+ int sockfd;
+ struct sockaddr_un addr;
+ char str[] = "\0xyz"; // Abstract socket requires first byte to be NULL
+ size_t str_size = 4;
+
+ memset(&addr, 0, sizeof(struct sockaddr_un));
+ addr.sun_family = AF_UNIX;
+ memcpy(&addr.sun_path, str, str_size);
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd == -1) {
+ return -1;
+ }
+
+ int con_st = connect(sockfd, (struct sockaddr*)&addr,
+ sizeof(sa_family_t) + str_size);
+ return con_st;
+ });
+
+ // An abstract socket that does starts with /, so we do want it to work.
+ // Checking ECONNREFUSED because this is what the broker should get
+ // when trying to establish the connect call for us if it's allowed;
+ // otherwise we get EACCES, meaning that it was passed to the broker
+ // (unlike the previous test) but rejected.
+ const int errorForX =
+ StaticPrefs::security_sandbox_content_headless_AtStartup() ? EACCES
+ : ECONNREFUSED;
+ child->ErrnoValueTest("connect_abstract_permit"_ns, errorForX, [&] {
+ int sockfd;
+ struct sockaddr_un addr;
+ // we re-use actual X path, because this is what is allowed within
+ // SandboxBrokerPolicyFactory::InitContentPolicy()
+ // We can't just use any random path allowed, but one with CONNECT allowed.
+
+ // (Note that the real X11 sockets have names like `X0` for
+ // display `:0`; there shouldn't be anything named just `X`.)
+
+ // Abstract socket requires first byte to be NULL
+ char str[] = "\0/tmp/.X11-unix/X";
+ size_t str_size = 17;
+
+ memset(&addr, 0, sizeof(struct sockaddr_un));
+ addr.sun_family = AF_UNIX;
+ memcpy(&addr.sun_path, str, str_size);
+
+ sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (sockfd == -1) {
+ return -1;
+ }
+
+ int con_st = connect(sockfd, (struct sockaddr*)&addr,
+ sizeof(sa_family_t) + str_size);
+ return con_st;
+ });
+
+ // Testing FIPS-relevant files, which need to be accessible
+ std::vector<std::pair<const char*, bool>> open_tests = {
+ {"/dev/random", true}};
+ // Not all systems have that file, so we only test access, if it exists
+ // in the first place
+ if (stat("/proc/sys/crypto/fips_enabled", &st) == 0) {
+ open_tests.push_back({"/proc/sys/crypto/fips_enabled", true});
+ }
+
+ for (const std::pair<const char*, bool>& to_open : open_tests) {
+ child->ErrnoTest("open("_ns + nsCString(to_open.first) + ")"_ns,
+ to_open.second, [&] {
+ int fd = open(to_open.first, O_RDONLY);
+ if (to_open.second && fd > 0) {
+ close(fd);
+ }
+ return fd;
+ });
+ }
+
+ child->ErrnoTest("statfs"_ns, true, [] {
+ struct statfs sf;
+ return statfs("/usr/share", &sf);
+ });
+
+ child->ErrnoTest("pipe2"_ns, true, [] {
+ int fds[2];
+ int rv = pipe2(fds, O_CLOEXEC);
+ int savedErrno = errno;
+ if (rv == 0) {
+ close(fds[0]);
+ close(fds[1]);
+ }
+ errno = savedErrno;
+ return rv;
+ });
+
+ child->ErrnoValueTest("chroot"_ns, ENOSYS, [] { return chroot("/"); });
+
+ child->ErrnoValueTest("pipe2_notif"_ns, ENOSYS, [] {
+ int fds[2];
+ return pipe2(fds, O_NOTIFICATION_PIPE);
+ });
+
+# ifdef MOZ_X11
+ // Check that X11 access is blocked (bug 1129492).
+ // This will fail if security.sandbox.content.headless is turned off.
+ if (PR_GetEnv("DISPLAY")) {
+ Display* disp = XOpenDisplay(nullptr);
+
+ child->SendReportTestResults(
+ "x11_access"_ns, !disp,
+ disp ? "XOpenDisplay succeeded"_ns : "XOpenDisplay failed"_ns);
+ if (disp) {
+ XCloseDisplay(disp);
+ }
+ }
+# endif // MOZ_X11
+
+ child->ErrnoTest("realpath localtime"_ns, true, [] {
+ char buf[PATH_MAX];
+ return realpath("/etc/localtime", buf) ? 0 : -1;
+ });
+
+# endif // XP_LINUX
+
+# ifdef XP_MACOSX
+ RunMacTestLaunchProcess(child, EPERM);
+ RunMacTestWindowServer(child);
+ RunMacTestAudioAPI(child, true);
+# endif
+
+#elif XP_WIN
+ FileTest("read from chrome"_ns, NS_APP_USER_CHROME_DIR, u"sandboxTest.txt"_ns,
+ FILE_GENERIC_READ, true, child);
+ FileTest("read from profile via relative path"_ns, NS_APP_USER_CHROME_DIR,
+ u"..\\sandboxTest.txt"_ns, FILE_GENERIC_READ, false, child);
+ // The profile dir is the parent of the chrome dir.
+ FileTest("read from chrome using forward slash"_ns,
+ NS_APP_USER_PROFILE_50_DIR, u"chrome/sandboxTest.txt"_ns,
+ FILE_GENERIC_READ, false, child);
+
+ // Note: these only pass in DEBUG builds because we allow write access to the
+ // temp dir for certain test logs and that is where the profile is created.
+ FileTest("read from profile"_ns, NS_APP_USER_PROFILE_50_DIR,
+ u"sandboxTest.txt"_ns, FILE_GENERIC_READ, kIsDebug, child);
+ FileTest("read/write from chrome"_ns, NS_APP_USER_CHROME_DIR,
+ u"sandboxTest.txt"_ns, FILE_GENERIC_READ | FILE_GENERIC_WRITE,
+ kIsDebug, child);
+#else
+ child->ReportNoTests();
+#endif
+}
+
+void RunTestsSocket(SandboxTestingChild* child) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child);
+
+#ifdef XP_UNIX
+ child->ErrnoTest("getaddrinfo"_ns, true, [&] {
+ struct addrinfo* res;
+ int rv = getaddrinfo("localhost", nullptr, nullptr, &res);
+ if (res != nullptr) {
+ freeaddrinfo(res);
+ }
+ return rv;
+ });
+
+# ifdef XP_LINUX
+ child->ErrnoTest("prctl_allowed"_ns, true, [&] {
+ int rv = prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+ return rv;
+ });
+
+ child->ErrnoTest("prctl_blocked"_ns, false, [&] {
+ int rv = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
+ return rv;
+ });
+
+ // Testing FIPS-relevant files, which need to be accessible
+ std::vector<std::pair<const char*, bool>> open_tests = {
+ {"/dev/random", true}};
+ // Not all systems have that file, so we only test access, if it exists
+ // in the first place
+ struct stat st;
+ if (stat("/proc/sys/crypto/fips_enabled", &st) == 0) {
+ open_tests.push_back({"/proc/sys/crypto/fips_enabled", true});
+ }
+
+ for (const std::pair<const char*, bool>& to_open : open_tests) {
+ child->ErrnoTest("open("_ns + nsCString(to_open.first) + ")"_ns,
+ to_open.second, [&] {
+ int fd = open(to_open.first, O_RDONLY);
+ if (to_open.second && fd > 0) {
+ close(fd);
+ }
+ return fd;
+ });
+ }
+
+ // getcpu is allowed
+ // We're using syscall directly because:
+ // - sched_getcpu uses vdso and as a result doesn't go through the sandbox.
+ // - getcpu isn't defined in the header files we're using yet.
+ int c;
+ child->ErrnoTest("getcpu"_ns, true,
+ [&] { return syscall(SYS_getcpu, &c, NULL, NULL); });
+# endif // XP_LINUX
+#elif XP_MACOSX
+ RunMacTestLaunchProcess(child);
+ RunMacTestWindowServer(child);
+ RunMacTestAudioAPI(child);
+#else // XP_UNIX
+ child->ReportNoTests();
+#endif // XP_UNIX
+}
+
+void RunTestsRDD(SandboxTestingChild* child) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child);
+
+#ifdef XP_UNIX
+# ifdef XP_LINUX
+ child->ErrnoValueTest("ioctl_tiocsti"_ns, ENOSYS, [&] {
+ int rv = ioctl(1, TIOCSTI, "x");
+ return rv;
+ });
+
+ struct rusage res = {};
+ child->ErrnoTest("getrusage"_ns, true, [&] {
+ int rv = getrusage(RUSAGE_SELF, &res);
+ return rv;
+ });
+
+ child->ErrnoValueTest("unlink"_ns, ENOENT, [&] {
+ int rv = unlink("");
+ return rv;
+ });
+
+ child->ErrnoValueTest("unlinkat"_ns, ENOENT, [&] {
+ int rv = unlinkat(AT_FDCWD, "", 0);
+ return rv;
+ });
+
+ RunTestsSched(child);
+
+ child->ErrnoTest("socket_inet"_ns, false,
+ [] { return socket(AF_INET, SOCK_STREAM, 0); });
+
+ child->ErrnoTest("socket_unix"_ns, false,
+ [] { return socket(AF_UNIX, SOCK_STREAM, 0); });
+
+ child->ErrnoTest("uname"_ns, true, [] {
+ struct utsname uts;
+ return uname(&uts);
+ });
+
+ child->ErrnoValueTest("ioctl_dma_buf"_ns, ENOTTY, [] {
+ // Apply the ioctl to the wrong kind of fd; it should fail with
+ // ENOTTY (rather than ENOSYS if it were blocked).
+ return ioctl(0, _IOW('b', 0, uint64_t), nullptr);
+ });
+
+ // getcpu is allowed
+ // We're using syscall directly because:
+ // - sched_getcpu uses vdso and as a result doesn't go through the sandbox.
+ // - getcpu isn't defined in the header files we're using yet.
+ int c;
+ child->ErrnoTest("getcpu"_ns, true,
+ [&] { return syscall(SYS_getcpu, &c, NULL, NULL); });
+
+ // The nvidia proprietary drivers will, in some cases, try to
+ // mknod their device files; we reject this politely.
+ child->ErrnoValueTest("mknod"_ns, EPERM, [] {
+ return mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3));
+ });
+
+ // nvidia defines some ioctls with the type 0x46 ('F', otherwise
+ // used by fbdev) and numbers starting from 200 (0xc8).
+ child->ErrnoValueTest("ioctl_nvidia"_ns, ENOTTY,
+ [] { return ioctl(0, 0x46c8, nullptr); });
+
+ child->ErrnoTest("statfs"_ns, true, [] {
+ struct statfs sf;
+ return statfs("/usr/share", &sf);
+ });
+
+# elif XP_MACOSX
+ RunMacTestLaunchProcess(child);
+ RunMacTestWindowServer(child);
+ RunMacTestAudioAPI(child, true);
+# endif
+#else // XP_UNIX
+# ifdef XP_WIN
+ RunWinTestWin32k(child, false);
+# endif // XP_WIN
+ child->ReportNoTests();
+#endif
+}
+
+void RunTestsGMPlugin(SandboxTestingChild* child) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child, /* aIsGMP = */ true);
+
+#ifdef XP_UNIX
+# ifdef XP_LINUX
+ struct utsname utsname_res = {};
+ child->ErrnoTest("uname_restricted"_ns, true, [&] {
+ int rv = uname(&utsname_res);
+
+ nsCString expectedSysname("Linux"_ns);
+ nsCString sysname(utsname_res.sysname);
+ nsCString expectedVersion("3"_ns);
+ nsCString version(utsname_res.version);
+ if ((sysname != expectedSysname) || (version != expectedVersion)) {
+ return -1;
+ }
+
+ return rv;
+ });
+
+ child->ErrnoTest("getuid"_ns, true, [&] { return getuid(); });
+ child->ErrnoTest("getgid"_ns, true, [&] { return getgid(); });
+ child->ErrnoTest("geteuid"_ns, true, [&] { return geteuid(); });
+ child->ErrnoTest("getegid"_ns, true, [&] { return getegid(); });
+
+ RunTestsSched(child);
+
+ std::vector<std::pair<const char*, bool>> open_tests = {
+ {"/etc/ld.so.cache", true},
+ {"/proc/cpuinfo", true},
+ {"/etc/hostname", false}};
+
+ for (const std::pair<const char*, bool>& to_open : open_tests) {
+ child->ErrnoTest("open("_ns + nsCString(to_open.first) + ")"_ns,
+ to_open.second, [&] {
+ int fd = open(to_open.first, O_RDONLY);
+ if (to_open.second && fd > 0) {
+ close(fd);
+ }
+ return fd;
+ });
+ }
+
+ child->ErrnoValueTest("readlink_exe"_ns, EINVAL, [] {
+ char pathBuf[PATH_MAX];
+ return readlink("/proc/self/exe", pathBuf, sizeof(pathBuf));
+ });
+
+ child->ErrnoTest("memfd_sizing"_ns, true, [] {
+ int fd = syscall(__NR_memfd_create, "sandbox-test", 0);
+ if (fd < 0) {
+ if (errno == ENOSYS) {
+ // Don't fail the test if the kernel is old.
+ return 0;
+ }
+ return -1;
+ }
+
+ int rv = ftruncate(fd, 4096);
+ int savedErrno = errno;
+ close(fd);
+ errno = savedErrno;
+ return rv;
+ });
+
+# elif XP_MACOSX // XP_LINUX
+ RunMacTestLaunchProcess(child);
+ /* The Mac GMP process requires access to the window server */
+ RunMacTestWindowServer(child, true /* aShouldHaveAccess */);
+ RunMacTestAudioAPI(child);
+# endif // XP_MACOSX
+#else // XP_UNIX
+ child->ReportNoTests();
+#endif
+}
+
+void RunTestsGenericUtility(SandboxTestingChild* child) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child);
+
+#ifdef XP_UNIX
+# ifdef XP_LINUX
+ child->ErrnoValueTest("ioctl_tiocsti"_ns, ENOSYS, [&] {
+ int rv = ioctl(1, TIOCSTI, "x");
+ return rv;
+ });
+
+ struct rusage res;
+ child->ErrnoTest("getrusage"_ns, true, [&] {
+ int rv = getrusage(RUSAGE_SELF, &res);
+ return rv;
+ });
+# elif XP_MACOSX // XP_LINUX
+ RunMacTestLaunchProcess(child);
+ RunMacTestWindowServer(child);
+ RunMacTestAudioAPI(child);
+# endif // XP_MACOSX
+#elif XP_WIN // XP_UNIX
+ child->ErrnoValueTest("write_only"_ns, EACCES, [&] {
+ FILE* rv = fopen("test_sandbox.txt", "w");
+ if (rv != nullptr) {
+ fclose(rv);
+ return 0;
+ }
+ return -1;
+ });
+ RunWinTestWin32k(child);
+#else // XP_UNIX
+ child->ReportNoTests();
+#endif // XP_MACOSX
+}
+
+void RunTestsUtilityAudioDecoder(SandboxTestingChild* child,
+ ipc::SandboxingKind aSandbox) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child);
+
+#ifdef XP_UNIX
+# ifdef XP_LINUX
+ // getrusage is allowed in Generic Utility and on AudioDecoder
+ struct rusage res;
+ child->ErrnoTest("getrusage"_ns, true, [&] {
+ int rv = getrusage(RUSAGE_SELF, &res);
+ return rv;
+ });
+
+ // get_mempolicy is not allowed in Generic Utility but is on AudioDecoder
+ child->ErrnoTest("get_mempolicy"_ns, true, [&] {
+ int numa_node;
+ int test_val = 0;
+ // <numaif.h> not installed by default, let's call directly the syscall
+ long rv = syscall(SYS_get_mempolicy, &numa_node, NULL, 0, (void*)&test_val,
+ MPOL_F_NODE | MPOL_F_ADDR);
+ return rv;
+ });
+ // set_mempolicy is not allowed in Generic Utility but is on AudioDecoder
+ child->ErrnoValueTest("set_mempolicy"_ns, ENOSYS, [&] {
+ // <numaif.h> not installed by default, let's call directly the syscall
+ long rv = syscall(SYS_set_mempolicy, 0, NULL, 0);
+ return rv;
+ });
+# elif XP_MACOSX // XP_LINUX
+ RunMacTestLaunchProcess(child);
+ RunMacTestWindowServer(child);
+ RunMacTestAudioAPI(
+ child,
+ aSandbox == ipc::SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA);
+# endif // XP_MACOSX
+#else // XP_UNIX
+# ifdef XP_WIN
+ RunWinTestWin32k(child);
+# endif // XP_WIN
+ child->ReportNoTests();
+#endif // XP_UNIX
+}
+
+void RunTestsGPU(SandboxTestingChild* child) {
+ MOZ_ASSERT(child, "No SandboxTestingChild*?");
+
+ RunGenericTests(child);
+
+#if defined(XP_WIN)
+
+ FileTest("R/W access to shader-cache dir"_ns, NS_APP_USER_PROFILE_50_DIR,
+ u"shader-cache\\"_ns, FILE_GENERIC_READ | FILE_GENERIC_WRITE, true,
+ child);
+
+ FileTest("R/W access to shader-cache files"_ns, NS_APP_USER_PROFILE_50_DIR,
+ u"shader-cache\\sandboxTest.txt"_ns,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE, true, child);
+
+#else // defined(XP_WIN)
+ child->ReportNoTests();
+#endif // defined(XP_WIN)
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTestingParent.cpp b/security/sandbox/common/test/SandboxTestingParent.cpp
new file mode 100644
index 0000000000..dff2d03896
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingParent.cpp
@@ -0,0 +1,125 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "SandboxTestingParent.h"
+#include "SandboxTestingThread.h"
+#include "nsIObserverService.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/Services.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsDirectoryServiceUtils.h"
+
+namespace mozilla {
+
+/* static */
+already_AddRefed<SandboxTestingParent> SandboxTestingParent::Create(
+ Endpoint<PSandboxTestingParent>&& aParentEnd) {
+ SandboxTestingThread* thread = SandboxTestingThread::Create();
+ if (!thread) {
+ return nullptr;
+ }
+ RefPtr<SandboxTestingParent> instance = new SandboxTestingParent(thread);
+ thread->Dispatch(NewRunnableMethod<Endpoint<PSandboxTestingParent>&&>(
+ "SandboxTestingParent::Bind", instance, &SandboxTestingParent::Bind,
+ std::move(aParentEnd)));
+ return instance.forget();
+}
+
+SandboxTestingParent::SandboxTestingParent(SandboxTestingThread* aThread)
+ : mThread(aThread),
+ mMonitor("SandboxTestingParent Lock"),
+ mShutdownDone(false) {}
+
+SandboxTestingParent::~SandboxTestingParent() = default;
+
+void SandboxTestingParent::Bind(Endpoint<PSandboxTestingParent>&& aEnd) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+ DebugOnly<bool> ok = aEnd.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+void SandboxTestingParent::ShutdownSandboxTestThread() {
+ MOZ_ASSERT(mThread->IsOnThread());
+ Close();
+ // Notify waiting thread that we are done.
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void SandboxTestingParent::Destroy(
+ already_AddRefed<SandboxTestingParent> aInstance) {
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<SandboxTestingParent> instance(aInstance);
+ if (!instance) {
+ return;
+ }
+
+ {
+ // Hold the lock while we destroy the actor on the test thread.
+ MonitorAutoLock lock(instance->mMonitor);
+ instance->mThread->Dispatch(NewRunnableMethod(
+ "SandboxTestingParent::ShutdownSandboxTestThread", instance,
+ &SandboxTestingParent::ShutdownSandboxTestThread));
+
+ // Wait for test thread to complete destruction.
+ while (!instance->mShutdownDone) {
+ instance->mMonitor.Wait();
+ }
+ }
+}
+
+void SandboxTestingParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_RELEASE_ASSERT(mThread->IsOnThread());
+}
+
+mozilla::ipc::IPCResult SandboxTestingParent::RecvReportTestResults(
+ const nsCString& testName, bool passed, const nsCString& resultMessage) {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("SandboxReportTestResults", [=]() {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ nsCString passedStr(passed ? "true"_ns : "false"_ns);
+ nsString json;
+ json += u"{ \"testid\" : \""_ns + NS_ConvertUTF8toUTF16(testName) +
+ u"\", \"passed\" : "_ns + NS_ConvertUTF8toUTF16(passedStr) +
+ u", \"message\" : \""_ns +
+ NS_ConvertUTF8toUTF16(resultMessage) + u"\" }"_ns;
+ observerService->NotifyObservers(nullptr, "sandbox-test-result",
+ json.BeginReading());
+ }));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SandboxTestingParent::RecvTestCompleted() {
+ Unused << SendShutDown();
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("SandboxReportTestResults", []() {
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ MOZ_RELEASE_ASSERT(observerService);
+ observerService->NotifyObservers(nullptr, "sandbox-test-done", 0);
+ }));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult SandboxTestingParent::RecvGetSpecialDirectory(
+ const nsCString& aSpecialDirName, nsString* aDirPath) {
+ RefPtr<Runnable> runnable = NS_NewRunnableFunction(
+ "SandboxTestingParent::RecvGetSpecialDirectory", [&]() {
+ nsCOMPtr<nsIFile> dir;
+ NS_GetSpecialDirectory(aSpecialDirName.get(), getter_AddRefs(dir));
+ if (dir) {
+ dir->GetPath(*aDirPath);
+ }
+ });
+ SyncRunnable::DispatchToThread(GetMainThreadSerialEventTarget(), runnable,
+ /*aForceDispatch*/ true);
+ return IPC_OK();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/common/test/SandboxTestingParent.h b/security/sandbox/common/test/SandboxTestingParent.h
new file mode 100644
index 0000000000..5a8ef9145a
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingParent.h
@@ -0,0 +1,53 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SandboxTestingParent_h
+#define mozilla_SandboxTestingParent_h
+
+#include "mozilla/PSandboxTestingParent.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+
+#if !defined(MOZ_SANDBOX) || !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTestingThread;
+
+class SandboxTestingParent : public PSandboxTestingParent {
+ public:
+ static already_AddRefed<SandboxTestingParent> Create(
+ Endpoint<PSandboxTestingParent>&& aParentEnd);
+ static void Destroy(already_AddRefed<SandboxTestingParent> aInstance);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SandboxTestingParent, override)
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ mozilla::ipc::IPCResult RecvReportTestResults(const nsCString& testName,
+ bool passed,
+ const nsCString& resultMessage);
+ mozilla::ipc::IPCResult RecvTestCompleted();
+
+ mozilla::ipc::IPCResult RecvGetSpecialDirectory(
+ const nsCString& aSpecialDirName, nsString* aDirPath);
+
+ private:
+ explicit SandboxTestingParent(SandboxTestingThread* aThread);
+ virtual ~SandboxTestingParent();
+ void ShutdownSandboxTestThread();
+ void Bind(Endpoint<PSandboxTestingParent>&& aEnd);
+
+ UniquePtr<SandboxTestingThread> mThread;
+ Monitor mMonitor MOZ_UNANNOTATED;
+ bool mShutdownDone;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxTestingParent_h
diff --git a/security/sandbox/common/test/SandboxTestingThread.h b/security/sandbox/common/test/SandboxTestingThread.h
new file mode 100644
index 0000000000..f85a017f94
--- /dev/null
+++ b/security/sandbox/common/test/SandboxTestingThread.h
@@ -0,0 +1,53 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SandboxTestingThread_h
+#define mozilla_SandboxTestingThread_h
+
+#include "nsThreadManager.h"
+
+#if !defined(MOZ_SANDBOX) || !defined(MOZ_DEBUG) || !defined(ENABLE_TESTS)
+# error "This file should not be used outside of debug with tests"
+#endif
+
+namespace mozilla {
+
+class SandboxTestingThread {
+ public:
+ void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) {
+ mThread->Dispatch(std::move(aRunnable), nsIEventTarget::NS_DISPATCH_NORMAL);
+ }
+
+ bool IsOnThread() {
+ bool on;
+ return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
+ }
+
+ static SandboxTestingThread* Create() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIThread> thread;
+ if (NS_FAILED(
+ NS_NewNamedThread("Sandbox Testing", getter_AddRefs(thread)))) {
+ return nullptr;
+ }
+ return new SandboxTestingThread(thread);
+ }
+
+ ~SandboxTestingThread() {
+ NS_DispatchToMainThread(NewRunnableMethod("~SandboxTestingThread", mThread,
+ &nsIThread::Shutdown));
+ }
+
+ private:
+ explicit SandboxTestingThread(nsIThread* aThread) : mThread(aThread) {
+ MOZ_ASSERT(mThread);
+ }
+
+ nsCOMPtr<nsIThread> mThread;
+};
+} // namespace mozilla
+
+#endif // mozilla_SandboxTestingThread_h
diff --git a/security/sandbox/common/test/mozISandboxTest.idl b/security/sandbox/common/test/mozISandboxTest.idl
new file mode 100644
index 0000000000..7cde1defcf
--- /dev/null
+++ b/security/sandbox/common/test/mozISandboxTest.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; 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/. */
+
+#include "nsISupports.idl"
+
+// This interface is only for testing Sandbox.
+
+[scriptable, builtinclass, uuid(2306c118-3544-4674-9222-670b88dc07a9)]
+interface mozISandboxTest : nsISupports
+{
+ void startTests(in Array<ACString> aProcessesList);
+ void finishTests();
+};
+
+%{ C++
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+#define MOZ_SANDBOX_TEST_CID \
+ {0x989dda27, 0xb144, 0x45f9, {0x90, 0x39, 0x69, 0x74, 0x4e, 0xc6, dd0xd9, 0x12}}
+#define MOZ_SANDBOX_TEST_CONTRACTID \
+ "@mozilla.org/sandbox/sandbox-test;1"
+#else
+#error "This file should not be used outside of debug with tests"
+#endif
+%}
diff --git a/security/sandbox/linux/LinuxSched.h b/security/sandbox/linux/LinuxSched.h
new file mode 100644
index 0000000000..3ec34df670
--- /dev/null
+++ b/security/sandbox/linux/LinuxSched.h
@@ -0,0 +1,35 @@
+/* -*- 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/. */
+
+#ifndef mozilla_LinuxSched_h
+#define mozilla_LinuxSched_h
+
+#include <linux/sched.h>
+
+// Some build environments, in particular the Android NDK, don't
+// define some of the newer clone/unshare flags ("newer" relatively
+// speaking; CLONE_NEWUTS is present since kernel 2.6.19 in 2006).
+
+#ifndef CLONE_NEWUTS
+# define CLONE_NEWUTS 0x04000000
+#endif
+#ifndef CLONE_NEWIPC
+# define CLONE_NEWIPC 0x08000000
+#endif
+#ifndef CLONE_NEWUSER
+# define CLONE_NEWUSER 0x10000000
+#endif
+#ifndef CLONE_NEWPID
+# define CLONE_NEWPID 0x20000000
+#endif
+#ifndef CLONE_NEWNET
+# define CLONE_NEWNET 0x40000000
+#endif
+#ifndef CLONE_IO
+# define CLONE_IO 0x80000000
+#endif
+
+#endif
diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp
new file mode 100644
index 0000000000..a57f6739f9
--- /dev/null
+++ b/security/sandbox/linux/Sandbox.cpp
@@ -0,0 +1,771 @@
+/* -*- 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/. */
+
+#include "Sandbox.h"
+
+#include "LinuxSched.h"
+#include "SandboxBrokerClient.h"
+#include "SandboxChrootProto.h"
+#include "SandboxFilter.h"
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+#include "SandboxOpenedFiles.h"
+#include "SandboxReporterClient.h"
+
+#include <dirent.h>
+#ifdef NIGHTLY_BUILD
+# include "dlfcn.h"
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/futex.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "mozilla/Array.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Range.h"
+#include "mozilla/SandboxInfo.h"
+#include "mozilla/StackWalk.h"
+#include "mozilla/Span.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "prenv.h"
+#include "base/posix/eintr_wrapper.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.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/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/system_headers/linux_ucontext.h"
+#endif
+
+#ifdef MOZ_ASAN
+// Copy libsanitizer declarations to avoid depending on ASAN headers.
+// See also bug 1081242 comment #4.
+extern "C" {
+namespace __sanitizer {
+// Win64 uses long long, but this is Linux.
+typedef signed long sptr;
+} // namespace __sanitizer
+
+typedef struct {
+ int coverage_sandboxed;
+ __sanitizer::sptr coverage_fd;
+ unsigned int coverage_max_block_size;
+} __sanitizer_sandbox_arguments;
+
+MOZ_IMPORT_API void __sanitizer_sandbox_on_notify(
+ __sanitizer_sandbox_arguments* args);
+} // extern "C"
+#endif // MOZ_ASAN
+
+// Signal number used to enable seccomp on each thread.
+mozilla::Atomic<int> gSeccompTsyncBroadcastSignum(0);
+
+namespace mozilla {
+
+static mozilla::Atomic<bool> gSandboxCrashOnError(false);
+
+// This is initialized by SandboxSetCrashFunc().
+SandboxCrashFunc gSandboxCrashFunc;
+
+static SandboxReporterClient* gSandboxReporterClient;
+static void (*gChromiumSigSysHandler)(int, siginfo_t*, void*);
+
+// Test whether a ucontext, interpreted as the state after a syscall,
+// indicates the given error. See also sandbox::Syscall::PutValueInUcontext.
+static bool ContextIsError(const ucontext_t* aContext, int aError) {
+ // Avoid integer promotion warnings. (The unary addition makes
+ // the decltype not evaluate to a reference type.)
+ typedef decltype(+SECCOMP_RESULT(aContext)) reg_t;
+
+#ifdef __mips__
+ return SECCOMP_PARM4(aContext) != 0 &&
+ SECCOMP_RESULT(aContext) == static_cast<reg_t>(aError);
+#else
+ return SECCOMP_RESULT(aContext) == static_cast<reg_t>(-aError);
+#endif
+}
+
+/**
+ * This is the SIGSYS handler function. It delegates to the Chromium
+ * TrapRegistry handler (see InstallSigSysHandler, below) and, if the
+ * trap handler installed by the policy would fail with ENOSYS,
+ * crashes the process. This allows unintentional policy failures to
+ * be reported as crash dumps and fixed. It also logs information
+ * about the failed system call.
+ *
+ * Note that this could be invoked in parallel on multiple threads and
+ * that it could be in async signal context (e.g., intercepting an
+ * open() called from an async signal handler).
+ */
+MOZ_NEVER_INLINE static void SigSysHandler(int nr, siginfo_t* info,
+ void* void_context) {
+ ucontext_t* ctx = static_cast<ucontext_t*>(void_context);
+ // This shouldn't ever be null, but the Chromium handler checks for
+ // that and refrains from crashing, so let's not crash release builds:
+ MOZ_DIAGNOSTIC_ASSERT(ctx);
+ if (!ctx) {
+ return;
+ }
+
+ // Save a copy of the context before invoking the trap handler,
+ // which will overwrite one or more registers with the return value.
+ ucontext_t savedCtx = *ctx;
+
+ gChromiumSigSysHandler(nr, info, ctx);
+ if (!ContextIsError(ctx, ENOSYS)) {
+ return;
+ }
+
+ SandboxReport report = gSandboxReporterClient->MakeReportAndSend(&savedCtx);
+
+ // TODO, someday when this is enabled on MIPS: include the two extra
+ // args in the error message.
+ SANDBOX_LOG(
+ "seccomp sandbox violation: pid %d, tid %d, syscall %d,"
+ " args %d %d %d %d %d %d.%s",
+ report.mPid, report.mTid, report.mSyscall, report.mArgs[0],
+ report.mArgs[1], report.mArgs[2], report.mArgs[3], report.mArgs[4],
+ report.mArgs[5], gSandboxCrashOnError ? " Killing process." : "");
+
+ if (gSandboxCrashOnError) {
+ // Bug 1017393: record syscall number somewhere useful.
+ info->si_addr = reinterpret_cast<void*>(report.mSyscall);
+
+ gSandboxCrashFunc(nr, info, &savedCtx, CallerPC());
+ _exit(127);
+ }
+}
+
+/**
+ * This function installs the SIGSYS handler. This is slightly
+ * complicated because we want to use Chromium's handler to dispatch
+ * to specific trap handlers defined in the policy, but we also need
+ * the full original signal context to give to Breakpad for crash
+ * dumps. So we install Chromium's handler first, then retrieve its
+ * address so our replacement can delegate to it.
+ */
+static void InstallSigSysHandler(void) {
+ struct sigaction act;
+
+ // Ensure that the Chromium handler is installed.
+ Unused << sandbox::Trap::Registry();
+
+ // If the signal handling state isn't as expected, crash now instead
+ // of crashing later (and more confusingly) when SIGSYS happens.
+
+ if (sigaction(SIGSYS, nullptr, &act) != 0) {
+ MOZ_CRASH("Couldn't read old SIGSYS disposition");
+ }
+ if ((act.sa_flags & SA_SIGINFO) != SA_SIGINFO) {
+ MOZ_CRASH("SIGSYS not already set to a siginfo handler?");
+ }
+ MOZ_RELEASE_ASSERT(act.sa_sigaction);
+ gChromiumSigSysHandler = act.sa_sigaction;
+ act.sa_sigaction = SigSysHandler;
+ // Currently, SA_NODEFER should already be set by the Chromium code,
+ // but it's harmless to ensure that it's set:
+ MOZ_ASSERT(act.sa_flags & SA_NODEFER);
+ act.sa_flags |= SA_NODEFER;
+ if (sigaction(SIGSYS, &act, nullptr) < 0) {
+ MOZ_CRASH("Couldn't change SIGSYS disposition");
+ }
+}
+
+/**
+ * This function installs the syscall filter, a.k.a. seccomp. The
+ * aUseTSync flag indicates whether this should apply to all threads
+ * in the process -- which will fail if the kernel doesn't support
+ * that -- or only the current thread.
+ *
+ * SECCOMP_MODE_FILTER is the "bpf" mode of seccomp which allows
+ * to pass a bpf program (in our case, it contains a syscall
+ * whitelist).
+ *
+ * PR_SET_NO_NEW_PRIVS ensures that it is impossible to grant more
+ * syscalls to the process beyond this point (even after fork()), and
+ * prevents gaining capabilities (e.g., by exec'ing a setuid root
+ * program). The kernel won't allow seccomp-bpf without doing this,
+ * because otherwise it could be used for privilege escalation attacks.
+ *
+ * Returns false if the filter was already installed (see the
+ * PR_SET_NO_NEW_PRIVS rule in SandboxFilter.cpp). Crashes on any
+ * other error condition.
+ *
+ * @see SandboxInfo
+ * @see BroadcastSetThreadSandbox
+ */
+[[nodiscard]] static bool InstallSyscallFilter(const sock_fprog* aProg,
+ bool aUseTSync) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ if (!aUseTSync && errno == ETXTBSY) {
+ return false;
+ }
+ SANDBOX_LOG_ERRNO("prctl(PR_SET_NO_NEW_PRIVS) failed");
+ MOZ_CRASH("prctl(PR_SET_NO_NEW_PRIVS)");
+ }
+
+ if (aUseTSync) {
+ if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, aProg) != 0) {
+ SANDBOX_LOG_ERRNO("thread-synchronized seccomp failed");
+ MOZ_CRASH("seccomp+tsync failed, but kernel supports tsync");
+ }
+ } else {
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (unsigned long)aProg, 0,
+ 0)) {
+ SANDBOX_LOG_ERRNO("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER) failed");
+ MOZ_CRASH("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
+ }
+ }
+ return true;
+}
+
+// Use signals for permissions that need to be set per-thread.
+// The communication channel from the signal handler back to the main thread.
+static mozilla::Atomic<int> gSetSandboxDone;
+// Pass the filter itself through a global.
+const sock_fprog* gSetSandboxFilter;
+
+// We have to dynamically allocate the signal number; see bug 1038900.
+// This function returns the first realtime signal currently set to
+// default handling (i.e., not in use), or 0 if none could be found.
+//
+// WARNING: if this function or anything similar to it (including in
+// external libraries) is used on multiple threads concurrently, there
+// will be a race condition.
+static int FindFreeSignalNumber() {
+ for (int signum = SIGRTMAX; signum >= SIGRTMIN; --signum) {
+ struct sigaction sa;
+
+ if (sigaction(signum, nullptr, &sa) == 0 &&
+ (sa.sa_flags & SA_SIGINFO) == 0 && sa.sa_handler == SIG_DFL) {
+ return signum;
+ }
+ }
+ return 0;
+}
+
+// Returns true if sandboxing was enabled, or false if sandboxing
+// already was enabled. Crashes if sandboxing could not be enabled.
+static bool SetThreadSandbox() {
+ return InstallSyscallFilter(gSetSandboxFilter, false);
+}
+
+static void SetThreadSandboxHandler(int signum) {
+ // The non-zero number sent back to the main thread indicates
+ // whether action was taken.
+ if (SetThreadSandbox()) {
+ gSetSandboxDone = 2;
+ } else {
+ gSetSandboxDone = 1;
+ }
+ // Wake up the main thread. See the FUTEX_WAIT call, below, for an
+ // explanation.
+ syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone), FUTEX_WAKE, 1);
+}
+
+static void EnterChroot() {
+ const char* env = PR_GetEnv(kSandboxChrootEnvFlag);
+ if (!env || !*env || *env == '0') {
+ return;
+ }
+ char msg = kSandboxChrootRequest;
+ ssize_t msg_len = HANDLE_EINTR(write(kSandboxChrootClientFd, &msg, 1));
+ MOZ_RELEASE_ASSERT(msg_len == 1);
+ msg_len = HANDLE_EINTR(read(kSandboxChrootClientFd, &msg, 1));
+ MOZ_RELEASE_ASSERT(msg_len == 1);
+ MOZ_RELEASE_ASSERT(msg == kSandboxChrootResponse);
+ close(kSandboxChrootClientFd);
+}
+
+static void BroadcastSetThreadSandbox(const sock_fprog* aFilter) {
+ pid_t pid, tid, myTid;
+ DIR* taskdp;
+ struct dirent* de;
+
+ // This function does not own *aFilter, so this global needs to
+ // always be zeroed before returning.
+ gSetSandboxFilter = aFilter;
+
+ static_assert(sizeof(mozilla::Atomic<int>) == sizeof(int),
+ "mozilla::Atomic<int> isn't represented by an int");
+ pid = getpid();
+ myTid = syscall(__NR_gettid);
+ taskdp = opendir("/proc/self/task");
+ if (taskdp == nullptr) {
+ SANDBOX_LOG_ERRNO("opendir /proc/self/task");
+ MOZ_CRASH("failed while trying to open directory /proc/self/task");
+ }
+
+ // In case this races with a not-yet-deprivileged thread cloning
+ // itself, repeat iterating over all threads until we find none
+ // that are still privileged.
+ bool sandboxProgress;
+ const int tsyncSignum = gSeccompTsyncBroadcastSignum;
+ do {
+ sandboxProgress = false;
+ // For each thread...
+ while ((de = readdir(taskdp))) {
+ char* endptr;
+ tid = strtol(de->d_name, &endptr, 10);
+ if (*endptr != '\0' || tid <= 0) {
+ // Not a task ID.
+ continue;
+ }
+ if (tid == myTid) {
+ // Drop this thread's privileges last, below, so we can
+ // continue to signal other threads.
+ continue;
+ }
+
+ MOZ_RELEASE_ASSERT(tsyncSignum != 0);
+
+ // Reset the futex cell and signal.
+ gSetSandboxDone = 0;
+ if (syscall(__NR_tgkill, pid, tid, tsyncSignum) != 0) {
+ if (errno == ESRCH) {
+ SANDBOX_LOG("Thread %d unexpectedly exited.", tid);
+ // Rescan threads, in case it forked before exiting.
+ sandboxProgress = true;
+ continue;
+ }
+ SANDBOX_LOG_ERRNO("tgkill(%d,%d)", pid, tid);
+ MOZ_CRASH("failed while trying to send a signal to a thread");
+ }
+ // It's unlikely, but if the thread somehow manages to exit
+ // after receiving the signal but before entering the signal
+ // handler, we need to avoid blocking forever.
+ //
+ // Using futex directly lets the signal handler send the wakeup
+ // from an async signal handler (pthread mutex/condvar calls
+ // aren't allowed), and to use a relative timeout that isn't
+ // affected by changes to the system clock (not possible with
+ // POSIX semaphores).
+ //
+ // If a thread doesn't respond within a reasonable amount of
+ // time, but still exists, we crash -- the alternative is either
+ // blocking forever or silently losing security, and it
+ // shouldn't actually happen.
+ static const int crashDelay = 10; // seconds
+ struct timespec timeLimit;
+ clock_gettime(CLOCK_MONOTONIC, &timeLimit);
+ timeLimit.tv_sec += crashDelay;
+ while (true) {
+ static const struct timespec futexTimeout = {0,
+ 10 * 1000 * 1000}; // 10ms
+ // Atomically: if gSetSandboxDone == 0, then sleep.
+ if (syscall(__NR_futex, reinterpret_cast<int*>(&gSetSandboxDone),
+ FUTEX_WAIT, 0, &futexTimeout) != 0) {
+ if (errno != EWOULDBLOCK && errno != ETIMEDOUT && errno != EINTR) {
+ SANDBOX_LOG_ERRNO("FUTEX_WAIT");
+ MOZ_CRASH("failed during FUTEX_WAIT");
+ }
+ }
+ // Did the handler finish?
+ if (gSetSandboxDone > 0) {
+ if (gSetSandboxDone == 2) {
+ sandboxProgress = true;
+ }
+ break;
+ }
+ // Has the thread ceased to exist?
+ if (syscall(__NR_tgkill, pid, tid, 0) != 0) {
+ if (errno == ESRCH) {
+ SANDBOX_LOG("Thread %d unexpectedly exited.", tid);
+ }
+ // Rescan threads, in case it forked before exiting.
+ // Also, if it somehow failed in a way that wasn't ESRCH,
+ // and still exists, that will be handled on the next pass.
+ sandboxProgress = true;
+ break;
+ }
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (now.tv_sec > timeLimit.tv_sec ||
+ (now.tv_sec == timeLimit.tv_sec &&
+ now.tv_nsec > timeLimit.tv_nsec)) {
+ SANDBOX_LOG(
+ "Thread %d unresponsive for %d seconds."
+ " Killing process.",
+ tid, crashDelay);
+
+ MOZ_CRASH("failed while waiting for unresponsive thread");
+ }
+ }
+ }
+ rewinddir(taskdp);
+ } while (sandboxProgress);
+
+ void (*oldHandler)(int);
+ oldHandler = signal(tsyncSignum, SIG_DFL);
+ if (oldHandler != SetThreadSandboxHandler) {
+ // See the comment on FindFreeSignalNumber about race conditions.
+ SANDBOX_LOG("handler for signal %d was changed to %p!", tsyncSignum,
+ oldHandler);
+ MOZ_CRASH("handler for the signal was changed to another");
+ }
+ gSeccompTsyncBroadcastSignum = 0;
+ Unused << closedir(taskdp);
+ // And now, deprivilege the main thread:
+ SetThreadSandbox();
+ gSetSandboxFilter = nullptr;
+}
+
+static void ApplySandboxWithTSync(sock_fprog* aFilter) {
+ // At this point we're committed to using tsync, because we'd have
+ // needed to allocate a signal and prevent it from being blocked on
+ // other threads (see SandboxHooks.cpp), so there's no attempt to
+ // fall back to the non-tsync path.
+ if (!InstallSyscallFilter(aFilter, true)) {
+ MOZ_CRASH("failed while trying to install syscall filter");
+ }
+}
+
+#ifdef NIGHTLY_BUILD
+static bool IsLibPresent(const char* aName) {
+ if (const auto handle = dlopen(aName, RTLD_LAZY | RTLD_NOLOAD)) {
+ dlclose(handle);
+ return true;
+ }
+ return false;
+}
+
+static const Array<const char*, 1> kLibsThatWillCrash{
+ "libesets_pac.so",
+};
+#endif // NIGHTLY_BUILD
+
+void SandboxEarlyInit() {
+ if (PR_GetEnv("MOZ_SANDBOXED") == nullptr) {
+ return;
+ }
+
+ // Fix LD_PRELOAD for any child processes. See bug 1434392 comment #10;
+ // this can probably go away when audio remoting is mandatory.
+ const char* oldPreload = PR_GetEnv("MOZ_ORIG_LD_PRELOAD");
+ char* preloadEntry;
+ // This string is "leaked" because the environment takes ownership.
+ if (asprintf(&preloadEntry, "LD_PRELOAD=%s", oldPreload ? oldPreload : "") !=
+ -1) {
+ PR_SetEnv(preloadEntry);
+ }
+
+ // If TSYNC is not supported, set up signal handler
+ // used to enable seccomp on each thread.
+ if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) {
+ // The signal number has to be chosen early, so that the
+ // interceptions in SandboxHooks.cpp can prevent it from being
+ // masked.
+ const int tsyncSignum = FindFreeSignalNumber();
+ if (tsyncSignum == 0) {
+ SANDBOX_LOG("No available signal numbers!");
+ MOZ_CRASH("failed while trying to find a free signal number");
+ }
+ gSeccompTsyncBroadcastSignum = tsyncSignum;
+
+ // ...and the signal handler also needs to be installed now, to
+ // indicate to anything else looking for free signals that it's
+ // claimed.
+ void (*oldHandler)(int);
+ oldHandler = signal(tsyncSignum, SetThreadSandboxHandler);
+ if (oldHandler != SIG_DFL) {
+ // See the comment on FindFreeSignalNumber about race conditions.
+ if (oldHandler == SIG_ERR) {
+ MOZ_CRASH("failed while registering the signal handler");
+ } else {
+ MOZ_CRASH("failed because the signal is in use by another handler");
+ }
+ SANDBOX_LOG("signal %d in use by handler %p!\n", tsyncSignum, oldHandler);
+ }
+ }
+}
+
+static void RunGlibcLazyInitializers() {
+ // Make glibc's lazy initialization of shm_open() run before sandboxing
+ int fd = shm_open("/dummy", O_RDONLY, 0);
+ if (fd > 0) {
+ close(fd); // In the unlikely case we actually opened something
+ }
+}
+
+static void SandboxLateInit() {
+#ifdef NIGHTLY_BUILD
+ gSandboxCrashOnError = true;
+ for (const char* name : kLibsThatWillCrash) {
+ if (IsLibPresent(name)) {
+ gSandboxCrashOnError = false;
+ break;
+ }
+ }
+#endif
+
+ if (const char* envVar = PR_GetEnv("MOZ_SANDBOX_CRASH_ON_ERROR")) {
+ if (envVar[0]) {
+ gSandboxCrashOnError = envVar[0] != '0';
+ }
+ }
+
+ RunGlibcLazyInitializers();
+}
+
+// Common code for sandbox startup.
+static void SetCurrentProcessSandbox(
+ UniquePtr<sandbox::bpf_dsl::Policy> aPolicy) {
+ MOZ_ASSERT(gSandboxCrashFunc);
+ MOZ_RELEASE_ASSERT(gSandboxReporterClient != nullptr);
+ SandboxLateInit();
+
+ // Auto-collect child processes -- mainly the chroot helper if
+ // present, but also anything setns()ed into the pid namespace (not
+ // yet implemented). This process won't be able to waitpid them
+ // after the seccomp-bpf policy is applied.
+ signal(SIGCHLD, SIG_IGN);
+
+ // Note: PolicyCompiler borrows the policy and registry for its
+ // lifetime, but does not take ownership of them.
+ sandbox::bpf_dsl::PolicyCompiler compiler(aPolicy.get(),
+ sandbox::Trap::Registry());
+
+ // In case of errors detected by the compiler (like ABI violations),
+ // log and crash normally; the default is SECCOMP_RET_KILL_THREAD,
+ // which results in hard-to-debug hangs.
+ compiler.SetPanicFunc([](const char* error) -> sandbox::bpf_dsl::ResultExpr {
+ // Note: this assumes that `error` is a string literal, which is
+ // currently the case for all callers. (An intentionally leaked
+ // heap allocation would also work.)
+ return sandbox::bpf_dsl::Trap(
+ [](const sandbox::arch_seccomp_data&, void* aux) -> intptr_t {
+ auto error = reinterpret_cast<const char*>(aux);
+ SANDBOX_LOG("Panic: %s", error);
+ MOZ_CRASH("Sandbox Panic");
+ // unreachable
+ },
+ (void*)error);
+ });
+
+ sandbox::CodeGen::Program program = compiler.Compile();
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ sandbox::bpf_dsl::DumpBPF::PrintProgram(program);
+ }
+
+ InstallSigSysHandler();
+
+#ifdef MOZ_ASAN
+ __sanitizer_sandbox_arguments asanArgs;
+ asanArgs.coverage_sandboxed = 1;
+ asanArgs.coverage_fd = -1;
+ asanArgs.coverage_max_block_size = 0;
+ __sanitizer_sandbox_on_notify(&asanArgs);
+#endif
+
+ // The syscall takes a C-style array, so copy the vector into one.
+ size_t programLen = program.size();
+ UniquePtr<sock_filter[]> flatProgram(new sock_filter[programLen]);
+ for (auto i = program.begin(); i != program.end(); ++i) {
+ flatProgram[i - program.begin()] = *i;
+ }
+
+ sock_fprog fprog;
+ fprog.filter = flatProgram.get();
+ fprog.len = static_cast<unsigned short>(programLen);
+ MOZ_RELEASE_ASSERT(static_cast<size_t>(fprog.len) == programLen);
+
+ const SandboxInfo info = SandboxInfo::Get();
+ if (info.Test(SandboxInfo::kHasSeccompTSync)) {
+ if (info.Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("using seccomp tsync");
+ }
+ ApplySandboxWithTSync(&fprog);
+ } else {
+ if (info.Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("no tsync support; using signal broadcast");
+ }
+ BroadcastSetThreadSandbox(&fprog);
+ }
+
+ // Now that all threads' filesystem accesses are being intercepted
+ // (if a broker is used) it's safe to chroot the process:
+ EnterChroot();
+}
+
+/**
+ * Starts the seccomp sandbox for a content process. Should be called
+ * only once, and before any potentially harmful content is loaded.
+ *
+ * Will normally make the process exit on failure.
+ */
+bool SetContentProcessSandbox(ContentProcessSandboxParams&& aParams) {
+ int brokerFd = aParams.mBrokerFd;
+ aParams.mBrokerFd = -1;
+
+ if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForContent)) {
+ if (brokerFd >= 0) {
+ close(brokerFd);
+ }
+ return false;
+ }
+
+ auto procType = aParams.mFileProcess ? SandboxReport::ProcType::FILE
+ : SandboxReport::ProcType::CONTENT;
+ gSandboxReporterClient = new SandboxReporterClient(procType);
+
+ // This needs to live until the process exits.
+ static SandboxBrokerClient* sBroker;
+ if (brokerFd >= 0) {
+ sBroker = new SandboxBrokerClient(brokerFd);
+ }
+
+ SetCurrentProcessSandbox(
+ GetContentSandboxPolicy(sBroker, std::move(aParams)));
+ return true;
+}
+/**
+ * Starts the seccomp sandbox for a media plugin process. Should be
+ * called only once, and before any potentially harmful content is
+ * loaded -- including the plugin itself, if it's considered untrusted.
+ *
+ * The file indicated by aFilePath, if non-null, can be open()ed
+ * read-only, once, after the sandbox starts; it should be the .so
+ * file implementing the not-yet-loaded plugin.
+ *
+ * Will normally make the process exit on failure.
+ */
+void SetMediaPluginSandbox(const char* aFilePath) {
+ MOZ_RELEASE_ASSERT(aFilePath != nullptr);
+ if (!SandboxInfo::Get().Test(SandboxInfo::kEnabledForMedia)) {
+ return;
+ }
+
+ gSandboxReporterClient =
+ new SandboxReporterClient(SandboxReport::ProcType::MEDIA_PLUGIN);
+
+ SandboxOpenedFile plugin(aFilePath);
+ if (!plugin.IsOpen()) {
+ SANDBOX_LOG_ERRNO("failed to open plugin file %s", aFilePath);
+ MOZ_CRASH("failed while trying to open the plugin file ");
+ }
+
+ auto files = new SandboxOpenedFiles();
+ files->Add(std::move(plugin));
+ files->Add("/dev/urandom", SandboxOpenedFile::Dup::YES);
+ files->Add("/dev/random", SandboxOpenedFile::Dup::YES);
+ files->Add("/etc/ld.so.cache"); // Needed for NSS in clearkey.
+ files->Add("/sys/devices/system/cpu/cpu0/tsc_freq_khz");
+ files->Add("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
+ files->Add("/proc/cpuinfo"); // Info also available via CPUID instruction.
+ files->Add("/proc/sys/crypto/fips_enabled"); // Needed for NSS in clearkey.
+#ifdef __i386__
+ files->Add("/proc/self/auxv"); // Info also in process's address space.
+#endif
+ // Bug 1712506: the Widevine CDM will try to access these but
+ // doesn't appear to need them.
+ files->Add("/sys/devices/system/cpu/online", SandboxOpenedFile::Error{});
+ files->Add("/proc/stat", SandboxOpenedFile::Error{});
+ files->Add("/proc/net/unix", SandboxOpenedFile::Error{});
+ files->Add("/proc/self/maps", SandboxOpenedFile::Error{});
+
+ // Finally, start the sandbox.
+ SetCurrentProcessSandbox(GetMediaSandboxPolicy(files));
+}
+
+void SetRemoteDataDecoderSandbox(int aBroker) {
+ if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
+ PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX")) {
+ if (aBroker >= 0) {
+ close(aBroker);
+ }
+ return;
+ }
+
+ gSandboxReporterClient =
+ new SandboxReporterClient(SandboxReport::ProcType::RDD);
+
+ // FIXME(bug 1513773): merge this with the one for content?
+ static SandboxBrokerClient* sBroker;
+ if (aBroker >= 0) {
+ sBroker = new SandboxBrokerClient(aBroker);
+ }
+
+ SetCurrentProcessSandbox(GetDecoderSandboxPolicy(sBroker));
+}
+
+void SetSocketProcessSandbox(int aBroker) {
+ if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
+ PR_GetEnv("MOZ_DISABLE_SOCKET_PROCESS_SANDBOX")) {
+ if (aBroker >= 0) {
+ close(aBroker);
+ }
+ return;
+ }
+
+ gSandboxReporterClient =
+ new SandboxReporterClient(SandboxReport::ProcType::SOCKET_PROCESS);
+
+ static SandboxBrokerClient* sBroker;
+ if (aBroker >= 0) {
+ sBroker = new SandboxBrokerClient(aBroker);
+ }
+
+ SetCurrentProcessSandbox(GetSocketProcessSandboxPolicy(sBroker));
+}
+
+void SetUtilitySandbox(int aBroker, ipc::SandboxingKind aKind) {
+ if (!SandboxInfo::Get().Test(SandboxInfo::kHasSeccompBPF) ||
+ PR_GetEnv("MOZ_DISABLE_UTILITY_SANDBOX")) {
+ if (aBroker >= 0) {
+ close(aBroker);
+ }
+ return;
+ }
+
+ gSandboxReporterClient =
+ new SandboxReporterClient(SandboxReport::ProcType::UTILITY);
+
+ static SandboxBrokerClient* sBroker;
+ if (aBroker >= 0) {
+ sBroker = new SandboxBrokerClient(aBroker);
+ }
+
+ UniquePtr<sandbox::bpf_dsl::Policy> policy;
+ switch (aKind) {
+ case ipc::SandboxingKind::GENERIC_UTILITY:
+ policy = GetUtilitySandboxPolicy(sBroker);
+ break;
+
+ default:
+ MOZ_ASSERT(false, "Invalid SandboxingKind");
+ break;
+ }
+
+ SetCurrentProcessSandbox(std::move(policy));
+}
+
+bool SetSandboxCrashOnError(bool aValue) {
+ bool oldValue = gSandboxCrashOnError;
+ gSandboxCrashOnError = aValue;
+ return oldValue;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/Sandbox.h b/security/sandbox/linux/Sandbox.h
new file mode 100644
index 0000000000..575f57a3a3
--- /dev/null
+++ b/security/sandbox/linux/Sandbox.h
@@ -0,0 +1,76 @@
+/* -*- 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/. */
+
+#ifndef mozilla_Sandbox_h
+#define mozilla_Sandbox_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Types.h"
+#include "nsXULAppAPI.h"
+#include <vector>
+
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+
+// This defines the entry points for a content process to start
+// sandboxing itself. See also SandboxInfo.h for what parts of
+// sandboxing are enabled/supported.
+
+namespace mozilla {
+
+namespace ipc {
+class FileDescriptor;
+} // namespace ipc
+
+// This must be called early, before glib creates any worker threads.
+// (See bug 1176099.)
+MOZ_EXPORT void SandboxEarlyInit();
+
+// A collection of sandbox parameters that have to be extracted from
+// prefs or other libxul facilities and passed down, because
+// libmozsandbox can't link against the APIs to read them.
+struct ContentProcessSandboxParams {
+ // Content sandbox level; see also GetEffectiveSandboxLevel in
+ // SandboxSettings.h and the comments for the Linux version of
+ // "security.sandbox.content.level" in browser/app/profile/firefox.js
+ int mLevel = 0;
+ // The filesystem broker client file descriptor, or -1 to allow
+ // direct filesystem access. (Warning: this is not a RAII class and
+ // will not close the fd on destruction.)
+ int mBrokerFd = -1;
+ // Determines whether we allow reading all files, for processes that
+ // render file:/// URLs.
+ bool mFileProcess = false;
+ // Syscall numbers to allow even if the seccomp-bpf policy otherwise
+ // wouldn't.
+ std::vector<int> mSyscallWhitelist;
+
+ static ContentProcessSandboxParams ForThisProcess(
+ const Maybe<ipc::FileDescriptor>& aBroker);
+};
+
+// Call only if SandboxInfo::CanSandboxContent() returns true.
+// (No-op if the sandbox is disabled.)
+// isFileProcess determines whether we allow system wide file reads.
+MOZ_EXPORT bool SetContentProcessSandbox(ContentProcessSandboxParams&& aParams);
+
+// Call only if SandboxInfo::CanSandboxMedia() returns true.
+// (No-op if MOZ_DISABLE_GMP_SANDBOX is set.)
+// aFilePath is the path to the plugin file.
+MOZ_EXPORT void SetMediaPluginSandbox(const char* aFilePath);
+
+MOZ_EXPORT void SetRemoteDataDecoderSandbox(int aBroker);
+
+MOZ_EXPORT void SetSocketProcessSandbox(int aBroker);
+
+MOZ_EXPORT void SetUtilitySandbox(int aBroker, ipc::SandboxingKind aKind);
+
+// We want to turn on/off crashing on error when running some tests
+// This will return current value and set the aValue we pass
+MOZ_EXPORT bool SetSandboxCrashOnError(bool aValue);
+
+} // namespace mozilla
+
+#endif // mozilla_Sandbox_h
diff --git a/security/sandbox/linux/SandboxBrokerClient.cpp b/security/sandbox/linux/SandboxBrokerClient.cpp
new file mode 100644
index 0000000000..8dd8a2f5cf
--- /dev/null
+++ b/security/sandbox/linux/SandboxBrokerClient.cpp
@@ -0,0 +1,274 @@
+/* -*- 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/. */
+
+#include "SandboxBrokerClient.h"
+#include "SandboxInfo.h"
+#include "SandboxLogging.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "mozilla/Assertions.h"
+#include "base/strings/safe_sprintf.h"
+
+namespace mozilla {
+
+SandboxBrokerClient::SandboxBrokerClient(int aFd) : mFileDesc(aFd) {}
+
+SandboxBrokerClient::~SandboxBrokerClient() { close(mFileDesc); }
+
+int SandboxBrokerClient::DoCall(const Request* aReq, const char* aPath,
+ const char* aPath2, void* aResponseBuff,
+ bool expectFd) {
+ // Remap /proc/self to the actual pid, so that the broker can open
+ // it. This happens here instead of in the broker to follow the
+ // principle of least privilege and keep the broker as simple as
+ // possible. (Note: when pid namespaces happen, this will also need
+ // to remap the inner pid to the outer pid.)
+ // We only remap the first path.
+ static const char kProcSelf[] = "/proc/self/";
+ static const size_t kProcSelfLen = sizeof(kProcSelf) - 1;
+ const char* path = aPath;
+ // This buffer just needs to be large enough for any such path that
+ // the policy would actually allow. sizeof("/proc/2147483647/") == 18.
+ char rewrittenPath[64];
+ if (strncmp(aPath, kProcSelf, kProcSelfLen) == 0) {
+ ssize_t len = base::strings::SafeSPrintf(rewrittenPath, "/proc/%d/%s",
+ getpid(), aPath + kProcSelfLen);
+ if (static_cast<size_t>(len) < sizeof(rewrittenPath)) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("rewriting %s -> %s", aPath, rewrittenPath);
+ }
+ path = rewrittenPath;
+ } else {
+ SANDBOX_LOG("not rewriting unexpectedly long path %s", aPath);
+ }
+ }
+
+ struct iovec ios[3];
+ int respFds[2];
+
+ // Set up iovecs for request + path.
+ ios[0].iov_base = const_cast<Request*>(aReq);
+ ios[0].iov_len = sizeof(*aReq);
+ ios[1].iov_base = const_cast<char*>(path);
+ ios[1].iov_len = strlen(path) + 1;
+ if (aPath2 != nullptr) {
+ ios[2].iov_base = const_cast<char*>(aPath2);
+ ios[2].iov_len = strlen(aPath2) + 1;
+ } else {
+ ios[2].iov_base = nullptr;
+ ios[2].iov_len = 0;
+ }
+ if (ios[1].iov_len > kMaxPathLen) {
+ return -ENAMETOOLONG;
+ }
+ if (ios[2].iov_len > kMaxPathLen) {
+ return -ENAMETOOLONG;
+ }
+
+ // Create response socket and send request.
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, respFds) < 0) {
+ return -errno;
+ }
+ const ssize_t sent = SendWithFd(mFileDesc, ios, 3, respFds[1]);
+ const int sendErrno = errno;
+ MOZ_ASSERT(sent < 0 || static_cast<size_t>(sent) ==
+ ios[0].iov_len + ios[1].iov_len + ios[2].iov_len);
+ close(respFds[1]);
+ if (sent < 0) {
+ close(respFds[0]);
+ return -sendErrno;
+ }
+
+ // Set up iovecs for response.
+ Response resp;
+ ios[0].iov_base = &resp;
+ ios[0].iov_len = sizeof(resp);
+ if (aResponseBuff) {
+ ios[1].iov_base = aResponseBuff;
+ ios[1].iov_len = aReq->mBufSize;
+ } else {
+ ios[1].iov_base = nullptr;
+ ios[1].iov_len = 0;
+ }
+
+ // Wait for response and return appropriately.
+ int openedFd = -1;
+ const ssize_t recvd = RecvWithFd(respFds[0], ios, aResponseBuff ? 2 : 1,
+ expectFd ? &openedFd : nullptr);
+ const int recvErrno = errno;
+ close(respFds[0]);
+ if (recvd < 0) {
+ return -recvErrno;
+ }
+ if (recvd == 0) {
+ SANDBOX_LOG("Unexpected EOF, op %d flags 0%o path %s", aReq->mOp,
+ aReq->mFlags, path);
+ return -EIO;
+ }
+ MOZ_ASSERT(static_cast<size_t>(recvd) <= ios[0].iov_len + ios[1].iov_len);
+ // Some calls such as readlink return a size if successful
+ if (resp.mError >= 0) {
+ // Success!
+ if (expectFd) {
+ MOZ_ASSERT(openedFd >= 0);
+ return openedFd;
+ }
+ return resp.mError;
+ }
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ // Keep in mind that "rejected" files can include ones that don't
+ // actually exist, if it's something that's optional or part of a
+ // search path (e.g., shared libraries). In those cases, this
+ // error message is expected.
+ SANDBOX_LOG("Failed errno %d op %s flags 0%o path %s", resp.mError,
+ OperationDescription[aReq->mOp], aReq->mFlags, path);
+ }
+ if (openedFd >= 0) {
+ close(openedFd);
+ }
+ return resp.mError;
+}
+
+int SandboxBrokerClient::Open(const char* aPath, int aFlags) {
+ Request req = {SANDBOX_FILE_OPEN, aFlags, 0};
+ int maybeFd = DoCall(&req, aPath, nullptr, nullptr, true);
+ if (maybeFd >= 0) {
+ // NSPR has opinions about file flags. Fix O_CLOEXEC.
+ if ((aFlags & O_CLOEXEC) == 0) {
+ fcntl(maybeFd, F_SETFD, 0);
+ }
+ }
+ return maybeFd;
+}
+
+int SandboxBrokerClient::Access(const char* aPath, int aMode) {
+ Request req = {SANDBOX_FILE_ACCESS, aMode, 0};
+ return DoCall(&req, aPath, nullptr, nullptr, false);
+}
+
+int SandboxBrokerClient::Stat(const char* aPath, statstruct* aStat) {
+ if (!aPath || !aStat) {
+ return -EFAULT;
+ }
+
+ Request req = {SANDBOX_FILE_STAT, 0, sizeof(statstruct)};
+ return DoCall(&req, aPath, nullptr, (void*)aStat, false);
+}
+
+int SandboxBrokerClient::LStat(const char* aPath, statstruct* aStat) {
+ if (!aPath || !aStat) {
+ return -EFAULT;
+ }
+
+ Request req = {SANDBOX_FILE_STAT, O_NOFOLLOW, sizeof(statstruct)};
+ return DoCall(&req, aPath, nullptr, (void*)aStat, false);
+}
+
+int SandboxBrokerClient::Chmod(const char* aPath, int aMode) {
+ Request req = {SANDBOX_FILE_CHMOD, aMode, 0};
+ return DoCall(&req, aPath, nullptr, nullptr, false);
+}
+
+int SandboxBrokerClient::Link(const char* aOldPath, const char* aNewPath) {
+ Request req = {SANDBOX_FILE_LINK, 0, 0};
+ return DoCall(&req, aOldPath, aNewPath, nullptr, false);
+}
+
+int SandboxBrokerClient::Symlink(const char* aOldPath, const char* aNewPath) {
+ Request req = {SANDBOX_FILE_SYMLINK, 0, 0};
+ return DoCall(&req, aOldPath, aNewPath, nullptr, false);
+}
+
+int SandboxBrokerClient::Rename(const char* aOldPath, const char* aNewPath) {
+ Request req = {SANDBOX_FILE_RENAME, 0, 0};
+ return DoCall(&req, aOldPath, aNewPath, nullptr, false);
+}
+
+int SandboxBrokerClient::Mkdir(const char* aPath, int aMode) {
+ Request req = {SANDBOX_FILE_MKDIR, aMode, 0};
+ return DoCall(&req, aPath, nullptr, nullptr, false);
+}
+
+int SandboxBrokerClient::Unlink(const char* aPath) {
+ Request req = {SANDBOX_FILE_UNLINK, 0, 0};
+ return DoCall(&req, aPath, nullptr, nullptr, false);
+}
+
+int SandboxBrokerClient::Rmdir(const char* aPath) {
+ Request req = {SANDBOX_FILE_RMDIR, 0, 0};
+ return DoCall(&req, aPath, nullptr, nullptr, false);
+}
+
+int SandboxBrokerClient::Readlink(const char* aPath, void* aBuff,
+ size_t aSize) {
+ Request req = {SANDBOX_FILE_READLINK, 0, aSize};
+ return DoCall(&req, aPath, nullptr, aBuff, false);
+}
+
+int SandboxBrokerClient::Connect(const sockaddr_un* aAddr, size_t aLen,
+ int aType) {
+ static constexpr size_t maxLen = sizeof(aAddr->sun_path);
+ const char* path = aAddr->sun_path;
+ const auto addrEnd = reinterpret_cast<const char*>(aAddr) + aLen;
+ // Ensure that the length isn't impossibly small.
+ if (addrEnd <= path) {
+ return -EINVAL;
+ }
+ // Unix domain only
+ if (aAddr->sun_family != AF_UNIX) {
+ return -EAFNOSUPPORT;
+ }
+ // How much of sun_path may be accessed?
+ auto bufLen = static_cast<size_t>(addrEnd - path);
+ if (bufLen > maxLen) {
+ bufLen = maxLen;
+ }
+
+ // Try to handle abstract addresses where the address (the part
+ // after the leading null byte) resembles a pathname: a leading
+ // slash and no embedded nulls.
+ //
+ // `DoCall` expects null-terminated strings, but in this case the
+ // "path" is terminated by the sockaddr length (without a null), so
+ // we need to make a copy.
+ if (bufLen >= 2 && path[0] == '\0' && path[1] == '/' &&
+ !memchr(path + 1, '\0', bufLen - 1)) {
+ char tmpBuf[maxLen];
+ MOZ_RELEASE_ASSERT(bufLen - 1 < maxLen);
+ memcpy(tmpBuf, path + 1, bufLen - 1);
+ tmpBuf[bufLen - 1] = '\0';
+
+ const Request req = {SANDBOX_SOCKET_CONNECT_ABSTRACT, aType, 0};
+ return DoCall(&req, tmpBuf, nullptr, nullptr, true);
+ }
+
+ // Require null-termination. (Linux doesn't require it, but
+ // applications usually null-terminate for portability, and not
+ // handling unterminated strings means we don't have to copy the path.)
+ const size_t pathLen = strnlen(path, bufLen);
+ if (pathLen == bufLen) {
+ return -ENAMETOOLONG;
+ }
+
+ // Abstract addresses are handled only in some specific case, error in others
+ if (pathLen == 0) {
+ return -ENETUNREACH;
+ }
+
+ const Request req = {SANDBOX_SOCKET_CONNECT, aType, 0};
+ return DoCall(&req, path, nullptr, nullptr, true);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxBrokerClient.h b/security/sandbox/linux/SandboxBrokerClient.h
new file mode 100644
index 0000000000..9e4c1825c3
--- /dev/null
+++ b/security/sandbox/linux/SandboxBrokerClient.h
@@ -0,0 +1,57 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxBrokerClient_h
+#define mozilla_SandboxBrokerClient_h
+
+#include "broker/SandboxBrokerCommon.h"
+#include "broker/SandboxBrokerUtils.h"
+
+#include "mozilla/Attributes.h"
+
+// This is the client for the sandbox broker described in
+// broker/SandboxBroker.h; its constructor takes the file descriptor
+// returned by SandboxBroker::Create, passed to the child over IPC.
+//
+// The operations exposed here can be called from any thread and in
+// async signal handlers, like the corresponding system calls. The
+// intended use is from a seccomp-bpf SIGSYS handler, to transparently
+// replace those syscalls, but they could also be used directly.
+
+struct stat;
+struct sockaddr_un;
+
+namespace mozilla {
+
+class SandboxBrokerClient final : private SandboxBrokerCommon {
+ public:
+ explicit SandboxBrokerClient(int aFd);
+ ~SandboxBrokerClient();
+
+ int Open(const char* aPath, int aFlags);
+ int Access(const char* aPath, int aMode);
+ int Stat(const char* aPath, statstruct* aStat);
+ int LStat(const char* aPath, statstruct* aStat);
+ int Chmod(const char* aPath, int aMode);
+ int Link(const char* aPath, const char* aPath2);
+ int Mkdir(const char* aPath, int aMode);
+ int Symlink(const char* aOldPath, const char* aNewPath);
+ int Rename(const char* aOldPath, const char* aNewPath);
+ int Unlink(const char* aPath);
+ int Rmdir(const char* aPath);
+ int Readlink(const char* aPath, void* aBuf, size_t aBufSize);
+ int Connect(const struct sockaddr_un* aAddr, size_t aLen, int aType);
+
+ private:
+ int mFileDesc;
+
+ int DoCall(const Request* aReq, const char* aPath, const char* aPath2,
+ void* aReponseBuff, bool expectFd);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxBrokerClient_h
diff --git a/security/sandbox/linux/SandboxChrootProto.h b/security/sandbox/linux/SandboxChrootProto.h
new file mode 100644
index 0000000000..6afc5311a1
--- /dev/null
+++ b/security/sandbox/linux/SandboxChrootProto.h
@@ -0,0 +1,24 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxChrootProto_h
+#define mozilla_SandboxChrootProto_h
+
+#include "mozilla/Types.h"
+
+namespace mozilla {
+
+static const int kSandboxChrootClientFd = 6;
+#if defined(MOZ_ENABLE_FORKSERVER)
+static const int kSandboxChrootServerFd = 10;
+#endif
+static const char kSandboxChrootRequest = 'C';
+static const char kSandboxChrootResponse = 'O';
+static const char kSandboxChrootEnvFlag[] = "MOZ_SANDBOX_USE_CHROOT";
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxChrootProto_h
diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp
new file mode 100644
index 0000000000..bd0ed9fb20
--- /dev/null
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -0,0 +1,2101 @@
+/* -*- 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/. */
+
+#include "SandboxFilter.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/ioctl.h>
+#include <linux/ipc.h>
+#include <linux/net.h>
+#include <linux/sched.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/un.h>
+#include <sys/utsname.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "Sandbox.h" // for ContentProcessSandboxParams
+#include "SandboxBrokerClient.h"
+#include "SandboxFilterUtil.h"
+#include "SandboxInfo.h"
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+#include "SandboxOpenedFiles.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/ProcInfo_linux.h"
+#include "mozilla/TemplateLib.h"
+#include "mozilla/UniquePtr.h"
+#include "prenv.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.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
+
+// Fill in defines in case of old headers.
+// (Warning: these are wrong on PA-RISC.)
+#ifndef MADV_HUGEPAGE
+# define MADV_HUGEPAGE 14
+#endif
+#ifndef MADV_NOHUGEPAGE
+# define MADV_NOHUGEPAGE 15
+#endif
+#ifndef MADV_DONTDUMP
+# define MADV_DONTDUMP 16
+#endif
+
+// Added in Linux 4.5; see bug 1303813.
+#ifndef MADV_FREE
+# define MADV_FREE 8
+#endif
+
+#ifndef PR_SET_PTRACER
+# define PR_SET_PTRACER 0x59616d61
+#endif
+
+// The headers define O_LARGEFILE as 0 on x86_64, but we need the
+// actual value because it shows up in file flags.
+#define O_LARGEFILE_REAL 00100000
+
+// Not part of UAPI, but userspace sees it in F_GETFL; see bug 1650751.
+#define FMODE_NONOTIFY 0x4000000
+
+#ifndef F_LINUX_SPECIFIC_BASE
+# define F_LINUX_SPECIFIC_BASE 1024
+#else
+static_assert(F_LINUX_SPECIFIC_BASE == 1024);
+#endif
+
+#ifndef F_ADD_SEALS
+# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+#else
+static_assert(F_ADD_SEALS == (F_LINUX_SPECIFIC_BASE + 9));
+static_assert(F_GET_SEALS == (F_LINUX_SPECIFIC_BASE + 10));
+#endif
+
+// To avoid visual confusion between "ifdef ANDROID" and "ifndef ANDROID":
+#ifndef ANDROID
+# define DESKTOP
+#endif
+
+namespace {
+static const unsigned long kIoctlTypeMask = _IOC_TYPEMASK << _IOC_TYPESHIFT;
+static const unsigned long kTtyIoctls = TIOCSTI & kIoctlTypeMask;
+// On some older architectures (but not x86 or ARM), ioctls are
+// assigned type fields differently, and the TIOC/TC/FIO group
+// isn't all the same type. If/when we support those archs,
+// this would need to be revised (but really this should be a
+// default-deny policy; see below).
+static_assert(kTtyIoctls == (TCSETA & kIoctlTypeMask) &&
+ kTtyIoctls == (FIOASYNC & kIoctlTypeMask),
+ "tty-related ioctls use the same type");
+}; // namespace
+
+// This file defines the seccomp-bpf system call filter policies.
+// See also SandboxFilterUtil.h, for the CASES_FOR_* macros and
+// SandboxFilterBase::Evaluate{Socket,Ipc}Call.
+//
+// One important difference from how Chromium bpf_dsl filters are
+// normally interpreted: returning -ENOSYS from a Trap() handler
+// indicates an unexpected system call; SigSysHandler() in Sandbox.cpp
+// will detect this, request a crash dump, and terminate the process.
+// This does not apply to using Error(ENOSYS) in the policy, so that
+// can be used if returning an actual ENOSYS is needed.
+
+namespace mozilla {
+
+// This class allows everything used by the sandbox itself, by the
+// core IPC code, by the crash reporter, or other core code. It also
+// contains support for brokering file operations, but file access is
+// denied if no broker client is provided by the concrete class.
+class SandboxPolicyCommon : public SandboxPolicyBase {
+ protected:
+ // Subclasses can assign these in their constructors to loosen the
+ // default settings.
+ SandboxBrokerClient* mBroker = nullptr;
+ bool mMayCreateShmem = false;
+ bool mAllowUnsafeSocketPair = false;
+ bool mBrokeredConnect = false; // Can connect() be brokered?
+
+ SandboxPolicyCommon() = default;
+
+ typedef const sandbox::arch_seccomp_data& ArgsRef;
+
+ static intptr_t BlockedSyscallTrap(ArgsRef aArgs, void* aux) {
+ MOZ_ASSERT(!aux);
+ return -ENOSYS;
+ }
+
+ // Convert Unix-style "return -1 and set errno" APIs back into the
+ // Linux ABI "return -err" style.
+ static intptr_t ConvertError(long rv) { return rv < 0 ? -errno : rv; }
+
+ template <typename... Args>
+ static intptr_t DoSyscall(long nr, Args... args) {
+ static_assert(std::conjunction_v<
+ std::conditional_t<(sizeof(Args) <= sizeof(void*)),
+ std::true_type, std::false_type>...>,
+ "each syscall arg is at most one word");
+ return ConvertError(syscall(nr, args...));
+ }
+
+ // Mesa's amdgpu driver uses kcmp with KCMP_FILE; see also bug
+ // 1624743. This policy restricts it to the process's own pid,
+ // which should be sufficient on its own if we need to remove the
+ // `type` restriction in the future.
+ //
+ // (Note: if we end up with more Mesa-specific hooks needed in
+ // several process types, we could put them into this class's
+ // EvaluateSyscall guarded by a boolean member variable, or
+ // introduce another layer of subclassing.)
+ ResultExpr KcmpPolicyForMesa() const {
+ // The real KCMP_FILE is part of an anonymous enum in
+ // <linux/kcmp.h>, but we can't depend on having that header,
+ // and it's not a #define so the usual #ifndef approach
+ // doesn't work.
+ static const int kKcmpFile = 0;
+ const pid_t myPid = getpid();
+ Arg<pid_t> pid1(0), pid2(1);
+ Arg<int> type(2);
+ return If(AllOf(pid1 == myPid, pid2 == myPid, type == kKcmpFile), Allow())
+ .Else(InvalidSyscall());
+ }
+
+ static intptr_t SchedTrap(ArgsRef aArgs, void* aux) {
+ const pid_t tid = syscall(__NR_gettid);
+ if (aArgs.args[0] == static_cast<uint64_t>(tid)) {
+ return DoSyscall(aArgs.nr, 0, static_cast<uintptr_t>(aArgs.args[1]),
+ static_cast<uintptr_t>(aArgs.args[2]),
+ static_cast<uintptr_t>(aArgs.args[3]),
+ static_cast<uintptr_t>(aArgs.args[4]),
+ static_cast<uintptr_t>(aArgs.args[5]));
+ }
+ return -EPERM;
+ }
+
+ private:
+ // Bug 1093893: Translate tkill to tgkill for pthread_kill; fixed in
+ // bionic commit 10c8ce59a (in JB and up; API level 16 = Android 4.1).
+ // Bug 1376653: musl also needs this, and security-wise it's harmless.
+ static intptr_t TKillCompatTrap(ArgsRef aArgs, void* aux) {
+ auto tid = static_cast<pid_t>(aArgs.args[0]);
+ auto sig = static_cast<int>(aArgs.args[1]);
+ return DoSyscall(__NR_tgkill, getpid(), tid, sig);
+ }
+
+ static intptr_t SetNoNewPrivsTrap(ArgsRef& aArgs, void* aux) {
+ if (gSetSandboxFilter == nullptr) {
+ // Called after BroadcastSetThreadSandbox finished, therefore
+ // not our doing and not expected.
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ // Signal that the filter is already in place.
+ return -ETXTBSY;
+ }
+
+ // Trap handlers for filesystem brokering.
+ // (The amount of code duplication here could be improved....)
+#ifdef __NR_open
+ static intptr_t OpenTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto flags = static_cast<int>(aArgs.args[1]);
+ return broker->Open(path, flags);
+ }
+
+ static intptr_t AccessTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto mode = static_cast<int>(aArgs.args[1]);
+ return broker->Access(path, mode);
+ }
+
+ static intptr_t StatTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]);
+ return broker->Stat(path, buf);
+ }
+
+ static intptr_t LStatTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<statstruct*>(aArgs.args[1]);
+ return broker->LStat(path, buf);
+ }
+
+ static intptr_t ChmodTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto mode = static_cast<mode_t>(aArgs.args[1]);
+ return broker->Chmod(path, mode);
+ }
+
+ static intptr_t LinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
+ return broker->Link(path, path2);
+ }
+
+ static intptr_t SymlinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
+ return broker->Symlink(path, path2);
+ }
+
+ static intptr_t RenameTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[1]);
+ return broker->Rename(path, path2);
+ }
+
+ static intptr_t MkdirTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto mode = static_cast<mode_t>(aArgs.args[1]);
+ return broker->Mkdir(path, mode);
+ }
+
+ static intptr_t RmdirTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ return broker->Rmdir(path);
+ }
+
+ static intptr_t UnlinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ if (path && path[0] == '\0') {
+ // If the path is empty, then just fail the call here
+ return -ENOENT;
+ }
+ return broker->Unlink(path);
+ }
+
+ static intptr_t ReadlinkTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto buf = reinterpret_cast<char*>(aArgs.args[1]);
+ auto size = static_cast<size_t>(aArgs.args[2]);
+ return broker->Readlink(path, buf, size);
+ }
+#endif // __NR_open
+
+ static intptr_t OpenAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto flags = static_cast<int>(aArgs.args[2]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative openat(%d, \"%s\", 0%o)", fd, path,
+ flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Open(path, flags);
+ }
+
+ static intptr_t AccessAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto mode = static_cast<int>(aArgs.args[2]);
+ // Linux's faccessat syscall has no "flags" argument. Attempting
+ // to handle the flags != 0 case is left to userspace; this is
+ // impossible to do correctly in all cases, but that's not our
+ // problem.
+ //
+ // Starting with kernel 5.8+ and glibc 2.33, there is faccessat2 that
+ // supports flags, handled below.
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative faccessat(%d, \"%s\", %d)", fd, path,
+ mode);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Access(path, mode);
+ }
+
+ static intptr_t AccessAt2Trap(ArgsRef aArgs, void* aux) {
+ auto* broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ const auto* path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto mode = static_cast<int>(aArgs.args[2]);
+ auto flags = static_cast<int>(aArgs.args[3]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative faccessat2(%d, \"%s\", %d, %d)", fd,
+ path, mode, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ if ((flags & ~AT_EACCESS) == 0) {
+ return broker->Access(path, mode);
+ }
+ return ConvertError(ENOSYS);
+ }
+
+ static intptr_t StatAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto buf = reinterpret_cast<statstruct*>(aArgs.args[2]);
+ auto flags = static_cast<int>(aArgs.args[3]);
+
+ if (fd != AT_FDCWD && (flags & AT_EMPTY_PATH) && path &&
+ !strcmp(path, "")) {
+#ifdef __NR_fstat64
+ return DoSyscall(__NR_fstat64, fd, buf);
+#else
+ return DoSyscall(__NR_fstat, fd, buf);
+#endif
+ }
+
+ if (!broker) {
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+
+ if (fd != AT_FDCWD && path && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative fstatat(%d, \"%s\", %p, 0x%x)", fd,
+ path, buf, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+
+ int badFlags = flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT);
+ if (badFlags != 0) {
+ SANDBOX_LOG("unsupported flags 0x%x in fstatat(%d, \"%s\", %p, 0x%x)",
+ badFlags, fd, path, buf, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return (flags & AT_SYMLINK_NOFOLLOW) == 0 ? broker->Stat(path, buf)
+ : broker->LStat(path, buf);
+ }
+
+ static intptr_t ChmodAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto mode = static_cast<mode_t>(aArgs.args[2]);
+ auto flags = static_cast<int>(aArgs.args[3]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative chmodat(%d, \"%s\", 0%o, %d)", fd,
+ path, mode, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ if (flags != 0) {
+ SANDBOX_LOG("unsupported flags in chmodat(%d, \"%s\", 0%o, %d)", fd, path,
+ mode, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Chmod(path, mode);
+ }
+
+ static intptr_t LinkAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto fd2 = static_cast<int>(aArgs.args[2]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[3]);
+ auto flags = static_cast<int>(aArgs.args[4]);
+ if ((fd != AT_FDCWD && path[0] != '/') ||
+ (fd2 != AT_FDCWD && path2[0] != '/')) {
+ SANDBOX_LOG(
+ "unsupported fd-relative linkat(%d, \"%s\", %d, \"%s\", 0x%x)", fd,
+ path, fd2, path2, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ if (flags != 0) {
+ SANDBOX_LOG("unsupported flags in linkat(%d, \"%s\", %d, \"%s\", 0x%x)",
+ fd, path, fd2, path2, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Link(path, path2);
+ }
+
+ static intptr_t SymlinkAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ auto fd2 = static_cast<int>(aArgs.args[1]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[2]);
+ if (fd2 != AT_FDCWD && path2[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative symlinkat(\"%s\", %d, \"%s\")", path,
+ fd2, path2);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Symlink(path, path2);
+ }
+
+ static intptr_t RenameAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto fd2 = static_cast<int>(aArgs.args[2]);
+ auto path2 = reinterpret_cast<const char*>(aArgs.args[3]);
+ if ((fd != AT_FDCWD && path[0] != '/') ||
+ (fd2 != AT_FDCWD && path2[0] != '/')) {
+ SANDBOX_LOG("unsupported fd-relative renameat(%d, \"%s\", %d, \"%s\")",
+ fd, path, fd2, path2);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Rename(path, path2);
+ }
+
+ static intptr_t MkdirAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto mode = static_cast<mode_t>(aArgs.args[2]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative mkdirat(%d, \"%s\", 0%o)", fd, path,
+ mode);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Mkdir(path, mode);
+ }
+
+ static intptr_t UnlinkAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto flags = static_cast<int>(aArgs.args[2]);
+ if (path && path[0] == '\0') {
+ // If the path is empty, then just fail the call here
+ return -ENOENT;
+ }
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative unlinkat(%d, \"%s\", 0x%x)", fd,
+ path, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ int badFlags = flags & ~AT_REMOVEDIR;
+ if (badFlags != 0) {
+ SANDBOX_LOG("unsupported flags 0x%x in unlinkat(%d, \"%s\", 0x%x)",
+ badFlags, fd, path, flags);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return (flags & AT_REMOVEDIR) == 0 ? broker->Unlink(path)
+ : broker->Rmdir(path);
+ }
+
+ static intptr_t ReadlinkAtTrap(ArgsRef aArgs, void* aux) {
+ auto broker = static_cast<SandboxBrokerClient*>(aux);
+ auto fd = static_cast<int>(aArgs.args[0]);
+ auto path = reinterpret_cast<const char*>(aArgs.args[1]);
+ auto buf = reinterpret_cast<char*>(aArgs.args[2]);
+ auto size = static_cast<size_t>(aArgs.args[3]);
+ if (fd != AT_FDCWD && path[0] != '/') {
+ SANDBOX_LOG("unsupported fd-relative readlinkat(%d, %s, %p, %d)", fd,
+ path, buf, size);
+ return BlockedSyscallTrap(aArgs, nullptr);
+ }
+ return broker->Readlink(path, buf, size);
+ }
+
+ static intptr_t SocketpairDatagramTrap(ArgsRef aArgs, void* aux) {
+ auto fds = reinterpret_cast<int*>(aArgs.args[3]);
+ // Return sequential packet sockets instead of the expected
+ // datagram sockets; see bug 1355274 for details.
+ return ConvertError(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ }
+
+ static intptr_t SocketpairUnpackTrap(ArgsRef aArgs, void* aux) {
+#ifdef __NR_socketpair
+ auto argsPtr = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+ return DoSyscall(__NR_socketpair, argsPtr[0], argsPtr[1], argsPtr[2],
+ argsPtr[3]);
+#else
+ MOZ_CRASH("unreachable?");
+ return -ENOSYS;
+#endif
+ }
+
+ static intptr_t GetSockOptUnpackTrap(ArgsRef aArgs, void* aux) {
+#ifdef __NR_getsockopt
+ auto argsPtr = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+ return DoSyscall(__NR_getsockopt, argsPtr[0], argsPtr[1], argsPtr[2],
+ argsPtr[3], argsPtr[4]);
+#else
+ MOZ_CRASH("unreachable?");
+ return -ENOSYS;
+#endif
+ }
+
+ // This just needs to return something to stand in for the
+ // unconnected socket until ConnectTrap, below, and keep track of
+ // the socket type somehow. Half a socketpair *is* a socket, so it
+ // should result in minimal confusion in the caller.
+ static intptr_t FakeSocketTrapCommon(int domain, int type, int protocol) {
+ int fds[2];
+ // X11 client libs will still try to getaddrinfo() even for a
+ // local connection. Also, WebRTC still has vestigial network
+ // code trying to do things in the content process. Politely tell
+ // them no.
+ if (domain != AF_UNIX) {
+ return -EAFNOSUPPORT;
+ }
+ if (socketpair(domain, type, protocol, fds) != 0) {
+ return -errno;
+ }
+ close(fds[1]);
+ return fds[0];
+ }
+
+ static intptr_t FakeSocketTrap(ArgsRef aArgs, void* aux) {
+ return FakeSocketTrapCommon(static_cast<int>(aArgs.args[0]),
+ static_cast<int>(aArgs.args[1]),
+ static_cast<int>(aArgs.args[2]));
+ }
+
+ static intptr_t FakeSocketTrapLegacy(ArgsRef aArgs, void* aux) {
+ const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+
+ return FakeSocketTrapCommon(static_cast<int>(innerArgs[0]),
+ static_cast<int>(innerArgs[1]),
+ static_cast<int>(innerArgs[2]));
+ }
+
+ static Maybe<int> DoGetSockOpt(int fd, int optname) {
+ int optval;
+ socklen_t optlen = sizeof(optval);
+
+ if (getsockopt(fd, SOL_SOCKET, optname, &optval, &optlen) != 0) {
+ return Nothing();
+ }
+ MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(optval));
+ return Some(optval);
+ }
+
+ // Substitute the newly connected socket from the broker for the
+ // original socket. This is meant to be used on a fd from
+ // FakeSocketTrap, above, but it should also work to simulate
+ // re-connect()ing a real connected socket.
+ //
+ // Warning: This isn't quite right if the socket is dup()ed, because
+ // other duplicates will still be the original socket, but hopefully
+ // nothing we're dealing with does that.
+ static intptr_t ConnectTrapCommon(SandboxBrokerClient* aBroker, int aFd,
+ const struct sockaddr_un* aAddr,
+ socklen_t aLen) {
+ if (aFd < 0) {
+ return -EBADF;
+ }
+ const auto maybeDomain = DoGetSockOpt(aFd, SO_DOMAIN);
+ if (!maybeDomain) {
+ return -errno;
+ }
+ if (*maybeDomain != AF_UNIX) {
+ return -EAFNOSUPPORT;
+ }
+ const auto maybeType = DoGetSockOpt(aFd, SO_TYPE);
+ if (!maybeType) {
+ return -errno;
+ }
+ const int oldFlags = fcntl(aFd, F_GETFL);
+ if (oldFlags == -1) {
+ return -errno;
+ }
+ const int newFd = aBroker->Connect(aAddr, aLen, *maybeType);
+ if (newFd < 0) {
+ return newFd;
+ }
+ // Copy over the nonblocking flag. The connect() won't be
+ // nonblocking in that case, but that shouldn't matter for
+ // AF_UNIX. The other fcntl-settable flags are either irrelevant
+ // for sockets (e.g., O_APPEND) or would be blocked by this
+ // seccomp-bpf policy, so they're ignored.
+ if (fcntl(newFd, F_SETFL, oldFlags & O_NONBLOCK) != 0) {
+ close(newFd);
+ return -errno;
+ }
+ if (dup2(newFd, aFd) < 0) {
+ close(newFd);
+ return -errno;
+ }
+ close(newFd);
+ return 0;
+ }
+
+ static intptr_t ConnectTrap(ArgsRef aArgs, void* aux) {
+ typedef const struct sockaddr_un* AddrPtr;
+
+ return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
+ static_cast<int>(aArgs.args[0]),
+ reinterpret_cast<AddrPtr>(aArgs.args[1]),
+ static_cast<socklen_t>(aArgs.args[2]));
+ }
+
+ static intptr_t ConnectTrapLegacy(ArgsRef aArgs, void* aux) {
+ const auto innerArgs = reinterpret_cast<unsigned long*>(aArgs.args[1]);
+ typedef const struct sockaddr_un* AddrPtr;
+
+ return ConnectTrapCommon(static_cast<SandboxBrokerClient*>(aux),
+ static_cast<int>(innerArgs[0]),
+ reinterpret_cast<AddrPtr>(innerArgs[1]),
+ static_cast<socklen_t>(innerArgs[2]));
+ }
+
+ static intptr_t StatFsTrap(ArgsRef aArgs, void* aux) {
+ // Warning: the kernel interface is not the C interface. The
+ // structs are different (<asm/statfs.h> vs. <sys/statfs.h>), and
+ // the statfs64 version takes an additional size parameter.
+ auto path = reinterpret_cast<const char*>(aArgs.args[0]);
+ int fd = open(path, O_RDONLY | O_LARGEFILE);
+ if (fd < 0) {
+ return -errno;
+ }
+
+ intptr_t rv;
+ switch (aArgs.nr) {
+ case __NR_statfs: {
+ auto buf = reinterpret_cast<void*>(aArgs.args[1]);
+ rv = DoSyscall(__NR_fstatfs, fd, buf);
+ break;
+ }
+#ifdef __NR_statfs64
+ case __NR_statfs64: {
+ auto sz = static_cast<size_t>(aArgs.args[1]);
+ auto buf = reinterpret_cast<void*>(aArgs.args[2]);
+ rv = DoSyscall(__NR_fstatfs64, fd, sz, buf);
+ break;
+ }
+#endif
+ default:
+ MOZ_ASSERT(false);
+ rv = -ENOSYS;
+ }
+
+ close(fd);
+ return rv;
+ }
+
+ public:
+ ResultExpr InvalidSyscall() const override {
+ return Trap(BlockedSyscallTrap, nullptr);
+ }
+
+ virtual ResultExpr ClonePolicy(ResultExpr failPolicy) const {
+ // Allow use for simple thread creation (pthread_create) only.
+
+ // WARNING: s390 and cris pass the flags in the second arg -- see
+ // CLONE_BACKWARDS2 in arch/Kconfig in the kernel source -- but we
+ // don't support seccomp-bpf on those archs yet.
+ Arg<int> flags(0);
+
+ // The exact flags used can vary. CLONE_DETACHED is used by musl
+ // and by old versions of Android (<= JB 4.2), but it's been
+ // ignored by the kernel since the beginning of the Git history.
+ //
+ // If we ever need to support Android <= KK 4.4 again, SETTLS
+ // and the *TID flags will need to be made optional.
+ static const int flags_required =
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
+ CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID |
+ CLONE_CHILD_CLEARTID;
+ static const int flags_optional = CLONE_DETACHED;
+
+ return If((flags & ~flags_optional) == flags_required, Allow())
+ .Else(failPolicy);
+ }
+
+ virtual ResultExpr PrctlPolicy() const {
+ // Note: this will probably need PR_SET_VMA if/when it's used on
+ // Android without being overridden by an allow-all policy, and
+ // the constant will need to be defined locally.
+ Arg<int> op(0);
+ return Switch(op)
+ .CASES((PR_GET_SECCOMP, // BroadcastSetThreadSandbox, etc.
+ PR_SET_NAME, // Thread creation
+ PR_SET_DUMPABLE, // Crash reporting
+ PR_SET_PTRACER), // Debug-mode crash handling
+ Allow())
+ .CASES((PR_CAPBSET_READ), // libcap.so.2 loaded by libpulse.so.0
+ // queries for capabilities
+ Error(EINVAL))
+ .Default(InvalidSyscall());
+ }
+
+ Maybe<ResultExpr> EvaluateSocketCall(int aCall,
+ bool aHasArgs) const override {
+ switch (aCall) {
+ case SYS_RECVMSG:
+ case SYS_SENDMSG:
+ // These next four aren't needed for IPC or other core
+ // functionality at the time of this writing, but they're
+ // subsets of recvmsg/sendmsg so there's nothing gained by not
+ // allowing them here (and simplifying subclasses).
+ case SYS_RECVFROM:
+ case SYS_SENDTO:
+ case SYS_RECV:
+ case SYS_SEND:
+ return Some(Allow());
+
+ case SYS_SOCKETPAIR: {
+ // We try to allow "safe" (always connected) socketpairs when using the
+ // file broker, or for content processes, but we may need to fall back
+ // and allow all socketpairs in some cases, see bug 1066750.
+ if (!mBroker && !mAllowUnsafeSocketPair) {
+ return Nothing();
+ }
+ // See bug 1066750.
+ if (!aHasArgs) {
+ // If this is a socketcall(2) platform, but the kernel also
+ // supports separate syscalls (>= 4.2.0), we can unpack the
+ // arguments and filter them.
+ if (HasSeparateSocketCalls()) {
+ return Some(Trap(SocketpairUnpackTrap, nullptr));
+ }
+ // Otherwise, we can't filter the args if the platform passes
+ // them by pointer.
+ return Some(Allow());
+ }
+ Arg<int> domain(0), type(1);
+ return Some(
+ If(domain == AF_UNIX,
+ Switch(type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
+ .Case(SOCK_STREAM, Allow())
+ .Case(SOCK_SEQPACKET, Allow())
+ // This is used only by content (and only for
+ // direct PulseAudio, which is deprecated) but it
+ // doesn't increase attack surface:
+ .Case(SOCK_DGRAM, Trap(SocketpairDatagramTrap, nullptr))
+ .Default(InvalidSyscall()))
+ .Else(InvalidSyscall()));
+ }
+
+ case SYS_GETSOCKOPT: {
+ // Best-effort argument filtering as for socketpair(2), above.
+ if (!aHasArgs) {
+ if (HasSeparateSocketCalls()) {
+ return Some(Trap(GetSockOptUnpackTrap, nullptr));
+ }
+ return Some(Allow());
+ }
+ Arg<int> level(1), optname(2);
+ // SO_SNDBUF is used by IPC to avoid constructing
+ // unnecessarily large gather arrays for `sendmsg`.
+ //
+ // SO_DOMAIN and SO_TYPE are needed for connect() brokering,
+ // but they're harmless even when it's not enabled.
+ return Some(If(AllOf(level == SOL_SOCKET,
+ AnyOf(optname == SO_SNDBUF, optname == SO_DOMAIN,
+ optname == SO_TYPE)),
+ Allow())
+ .Else(InvalidSyscall()));
+ }
+
+ // These two cases are for connect() brokering, if enabled.
+ case SYS_SOCKET:
+ if (mBrokeredConnect) {
+ const auto trapFn = aHasArgs ? FakeSocketTrap : FakeSocketTrapLegacy;
+ MOZ_ASSERT(mBroker);
+ return Some(Trap(trapFn, mBroker));
+ }
+ return Nothing();
+
+ case SYS_CONNECT:
+ if (mBrokeredConnect) {
+ const auto trapFn = aHasArgs ? ConnectTrap : ConnectTrapLegacy;
+ MOZ_ASSERT(mBroker);
+ return Some(Trap(trapFn, mBroker));
+ }
+ return Nothing();
+
+ default:
+ return Nothing();
+ }
+ }
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ // If a file broker client was provided, route syscalls to it;
+ // otherwise, fall through to the main policy, which will deny
+ // them.
+ if (mBroker) {
+ switch (sysno) {
+#ifdef __NR_open
+ case __NR_open:
+ return Trap(OpenTrap, mBroker);
+ case __NR_access:
+ return Trap(AccessTrap, mBroker);
+ CASES_FOR_stat:
+ return Trap(StatTrap, mBroker);
+ CASES_FOR_lstat:
+ return Trap(LStatTrap, mBroker);
+ case __NR_chmod:
+ return Trap(ChmodTrap, mBroker);
+ case __NR_link:
+ return Trap(LinkTrap, mBroker);
+ case __NR_mkdir:
+ return Trap(MkdirTrap, mBroker);
+ case __NR_symlink:
+ return Trap(SymlinkTrap, mBroker);
+ case __NR_rename:
+ return Trap(RenameTrap, mBroker);
+ case __NR_rmdir:
+ return Trap(RmdirTrap, mBroker);
+ case __NR_unlink:
+ return Trap(UnlinkTrap, mBroker);
+ case __NR_readlink:
+ return Trap(ReadlinkTrap, mBroker);
+#endif
+ case __NR_openat:
+ return Trap(OpenAtTrap, mBroker);
+ case __NR_faccessat:
+ return Trap(AccessAtTrap, mBroker);
+ case __NR_faccessat2:
+ return Trap(AccessAt2Trap, mBroker);
+ CASES_FOR_fstatat:
+ return Trap(StatAtTrap, mBroker);
+ // Used by new libc and Rust's stdlib, if available.
+ // We don't have broker support yet so claim it does not exist.
+ case __NR_statx:
+ return Error(ENOSYS);
+ case __NR_fchmodat:
+ return Trap(ChmodAtTrap, mBroker);
+ case __NR_linkat:
+ return Trap(LinkAtTrap, mBroker);
+ case __NR_mkdirat:
+ return Trap(MkdirAtTrap, mBroker);
+ case __NR_symlinkat:
+ return Trap(SymlinkAtTrap, mBroker);
+ case __NR_renameat:
+ return Trap(RenameAtTrap, mBroker);
+ case __NR_unlinkat:
+ return Trap(UnlinkAtTrap, mBroker);
+ case __NR_readlinkat:
+ return Trap(ReadlinkAtTrap, mBroker);
+ }
+ } else {
+ // In the absence of a broker we still need to handle the
+ // fstat-equivalent subset of fstatat; see bug 1673770.
+ switch (sysno) {
+ CASES_FOR_fstatat:
+ return Trap(StatAtTrap, nullptr);
+ }
+ }
+
+ switch (sysno) {
+ // Timekeeping
+ //
+ // (Note: the switch needs to start with a literal case, not a
+ // macro; otherwise clang-format gets confused.)
+ case __NR_gettimeofday:
+#ifdef __NR_time
+ case __NR_time:
+#endif
+ case __NR_nanosleep:
+ return Allow();
+
+ CASES_FOR_clock_gettime:
+ CASES_FOR_clock_getres:
+ CASES_FOR_clock_nanosleep : {
+ // clockid_t can encode a pid or tid to monitor another
+ // process or thread's CPU usage (see CPUCLOCK_PID and related
+ // definitions in include/linux/posix-timers.h in the kernel
+ // source). For threads, the kernel allows only tids within
+ // the calling process, so it isn't a problem if we don't
+ // filter those; pids do need to be restricted to the current
+ // process in order to not leak information.
+ Arg<clockid_t> clk_id(0);
+ clockid_t this_process =
+ MAKE_PROCESS_CPUCLOCK(getpid(), CPUCLOCK_SCHED);
+ return If(clk_id == CLOCK_MONOTONIC, Allow())
+#ifdef CLOCK_MONOTONIC_COARSE
+ // Used by SandboxReporter, among other things.
+ .ElseIf(clk_id == CLOCK_MONOTONIC_COARSE, Allow())
+#endif
+ .ElseIf(clk_id == CLOCK_PROCESS_CPUTIME_ID, Allow())
+ .ElseIf(clk_id == CLOCK_REALTIME, Allow())
+#ifdef CLOCK_REALTIME_COARSE
+ .ElseIf(clk_id == CLOCK_REALTIME_COARSE, Allow())
+#endif
+ .ElseIf(clk_id == CLOCK_THREAD_CPUTIME_ID, Allow())
+#ifdef MOZ_GECKO_PROFILER
+ // Allow clock_gettime on the same process.
+ .ElseIf(clk_id == this_process, Allow())
+ // Allow clock_gettime on a thread.
+ .ElseIf((clk_id & 7u) == (CPUCLOCK_PERTHREAD_MASK | CPUCLOCK_SCHED),
+ Allow())
+#endif
+#ifdef CLOCK_BOOTTIME
+ .ElseIf(clk_id == CLOCK_BOOTTIME, Allow())
+#endif
+ .Else(InvalidSyscall());
+ }
+
+ // Thread synchronization
+ CASES_FOR_futex:
+ // FIXME(bug 1441993): This could be more restrictive.
+ return Allow();
+
+ // Asynchronous I/O
+ CASES_FOR_epoll_create:
+ CASES_FOR_epoll_wait:
+ case __NR_epoll_ctl:
+ CASES_FOR_poll:
+ return Allow();
+
+ // Used when requesting a crash dump.
+ CASES_FOR_pipe:
+ return Allow();
+
+ // Metadata of opened files
+ CASES_FOR_fstat:
+ return Allow();
+
+ CASES_FOR_fcntl : {
+ Arg<int> cmd(1);
+ Arg<int> flags(2);
+ // Typical use of F_SETFL is to modify the flags returned by
+ // F_GETFL and write them back, including some flags that
+ // F_SETFL ignores. This is a default-deny policy in case any
+ // new SETFL-able flags are added. (In particular we want to
+ // forbid O_ASYNC; see bug 1328896, but also see bug 1408438.)
+ static const int ignored_flags =
+ O_ACCMODE | O_LARGEFILE_REAL | O_CLOEXEC | FMODE_NONOTIFY;
+ static const int allowed_flags = ignored_flags | O_APPEND | O_NONBLOCK;
+ return Switch(cmd)
+ // Close-on-exec is meaningless when execve isn't allowed, but
+ // NSPR reads the bit and asserts that it has the expected value.
+ .Case(F_GETFD, Allow())
+ .Case(
+ F_SETFD,
+ If((flags & ~FD_CLOEXEC) == 0, Allow()).Else(InvalidSyscall()))
+ // F_GETFL is also used by fdopen
+ .Case(F_GETFL, Allow())
+ .Case(F_SETFL, If((flags & ~allowed_flags) == 0, Allow())
+ .Else(InvalidSyscall()))
+ // Not much different from other forms of dup(), and commonly used.
+ .Case(F_DUPFD_CLOEXEC, Allow())
+ .Default(SandboxPolicyBase::EvaluateSyscall(sysno));
+ }
+
+ // Simple I/O
+ case __NR_pread64:
+ case __NR_write:
+ case __NR_read:
+ case __NR_readv:
+ case __NR_writev: // see SandboxLogging.cpp
+ CASES_FOR_lseek:
+ return Allow();
+
+ CASES_FOR_getdents:
+ return Allow();
+
+ CASES_FOR_ftruncate:
+ case __NR_fallocate:
+ return mMayCreateShmem ? Allow() : InvalidSyscall();
+
+ // Used by our fd/shm classes
+ case __NR_dup:
+ return Allow();
+
+ // Memory mapping
+ CASES_FOR_mmap:
+ case __NR_munmap:
+ return Allow();
+
+ // Shared memory
+ case __NR_memfd_create:
+ return Allow();
+
+ // ipc::Shmem; also, glibc when creating threads:
+ case __NR_mprotect:
+ return Allow();
+
+#if !defined(MOZ_MEMORY)
+ // No jemalloc means using a system allocator like glibc
+ // that might use brk.
+ case __NR_brk:
+ return Allow();
+#endif
+
+ // madvise hints used by malloc; see bug 1303813 and bug 1364533
+ case __NR_madvise: {
+ Arg<int> advice(2);
+ // The GMP specific sandbox duplicates this logic, so when adding
+ // allowed values here also add them to the GMP sandbox rules.
+ return If(advice == MADV_DONTNEED, Allow())
+ .ElseIf(advice == MADV_FREE, Allow())
+ .ElseIf(advice == MADV_HUGEPAGE, Allow())
+ .ElseIf(advice == MADV_NOHUGEPAGE, Allow())
+#ifdef MOZ_ASAN
+ .ElseIf(advice == MADV_DONTDUMP, Allow())
+#endif
+ .ElseIf(advice == MADV_MERGEABLE, Error(EPERM)) // bug 1705045
+ .Else(InvalidSyscall());
+ }
+
+ // musl libc will set this up in pthreads support.
+ case __NR_membarrier:
+ return Allow();
+
+ // Signal handling
+ case __NR_sigaltstack:
+ CASES_FOR_sigreturn:
+ CASES_FOR_sigprocmask:
+ CASES_FOR_sigaction:
+ return Allow();
+
+ // Send signals within the process (raise(), profiling, etc.)
+ case __NR_tgkill: {
+ Arg<pid_t> tgid(0);
+ return If(tgid == getpid(), Allow()).Else(InvalidSyscall());
+ }
+
+ // Polyfill with tgkill; see above.
+ case __NR_tkill:
+ return Trap(TKillCompatTrap, nullptr);
+
+ // Yield
+ case __NR_sched_yield:
+ return Allow();
+
+ // Thread creation.
+ case __NR_clone:
+ return ClonePolicy(InvalidSyscall());
+
+ case __NR_clone3:
+ return Error(ENOSYS);
+
+ // More thread creation.
+#ifdef __NR_set_robust_list
+ case __NR_set_robust_list:
+ return Allow();
+#endif
+#ifdef ANDROID
+ case __NR_set_tid_address:
+ return Allow();
+#endif
+
+ // prctl
+ case __NR_prctl: {
+ // WARNING: do not handle __NR_prctl directly in subclasses;
+ // override PrctlPolicy instead. The special handling of
+ // PR_SET_NO_NEW_PRIVS is used to detect that a thread already
+ // has the policy applied; see also bug 1257361.
+
+ if (SandboxInfo::Get().Test(SandboxInfo::kHasSeccompTSync)) {
+ return PrctlPolicy();
+ }
+
+ Arg<int> option(0);
+ return If(option == PR_SET_NO_NEW_PRIVS,
+ Trap(SetNoNewPrivsTrap, nullptr))
+ .Else(PrctlPolicy());
+ }
+
+ // NSPR can call this when creating a thread, but it will accept a
+ // polite "no".
+ case __NR_getpriority:
+ // But if thread creation races with sandbox startup, that call
+ // could succeed, and then we get one of these:
+ case __NR_setpriority:
+ return Error(EACCES);
+
+ // Stack bounds are obtained via pthread_getattr_np, which calls
+ // this but doesn't actually need it:
+ case __NR_sched_getaffinity:
+ return Error(ENOSYS);
+
+ // Identifies the processor and node where this thread or process is
+ // running. This is used by "Awake" profiler markers.
+ case __NR_getcpu:
+ return Allow();
+
+ // Read own pid/tid.
+ case __NR_getpid:
+ case __NR_gettid:
+ return Allow();
+
+ // Discard capabilities
+ case __NR_close:
+ return Allow();
+
+ // Machine-dependent stuff
+#ifdef __arm__
+ case __ARM_NR_breakpoint:
+ case __ARM_NR_cacheflush:
+ case __ARM_NR_usr26: // FIXME: do we actually need this?
+ case __ARM_NR_usr32:
+ case __ARM_NR_set_tls:
+ return Allow();
+#endif
+
+ // Needed when being debugged:
+ case __NR_restart_syscall:
+ return Allow();
+
+ // Terminate threads or the process
+ case __NR_exit:
+ case __NR_exit_group:
+ return Allow();
+
+ case __NR_getrandom:
+ return Allow();
+
+ // Used by almost every process: GMP needs them for Clearkey
+ // because of bug 1576006 (but may not need them for other
+ // plugin types; see bug 1737092). Given that fstat is
+ // allowed, the uid/gid are probably available anyway.
+ CASES_FOR_getuid:
+ CASES_FOR_getgid:
+ CASES_FOR_geteuid:
+ CASES_FOR_getegid:
+ return Allow();
+
+#ifdef DESKTOP
+ // Bug 1543858: glibc's qsort calls sysinfo to check the
+ // memory size; it falls back to assuming there's enough RAM.
+ case __NR_sysinfo:
+ return Error(EPERM);
+#endif
+
+ // Bug 1651701: an API for restartable atomic sequences and
+ // per-CPU data; exposing information about CPU numbers and
+ // when threads are migrated or preempted isn't great but the
+ // risk should be relatively low.
+ case __NR_rseq:
+ return Allow();
+
+ case __NR_ioctl: {
+ Arg<unsigned long> request(1);
+#ifdef MOZ_ASAN
+ Arg<int> fd(0);
+#endif // MOZ_ASAN
+ // Make isatty() return false, because none of the terminal
+ // ioctls will be allowed; libraries sometimes call this for
+ // various reasons (e.g., to decide whether to emit ANSI/VT
+ // color codes when logging to stderr). glibc uses TCGETS and
+ // musl uses TIOCGWINSZ.
+ //
+ // This is required by ffmpeg
+ return If(AnyOf(request == TCGETS, request == TIOCGWINSZ),
+ Error(ENOTTY))
+#ifdef MOZ_ASAN
+ // ASAN's error reporter wants to know if stderr is a tty.
+ .ElseIf(fd == STDERR_FILENO, Error(ENOTTY))
+#endif // MOZ_ASAN
+ .Else(SandboxPolicyBase::EvaluateSyscall(sysno));
+ }
+
+ CASES_FOR_dup2: // See ConnectTrapCommon
+ if (mBrokeredConnect) {
+ return Allow();
+ }
+ return SandboxPolicyBase::EvaluateSyscall(sysno);
+
+#ifdef MOZ_ASAN
+ // ...and before compiler-rt r209773, it will call readlink on
+ // /proc/self/exe and use the cached value only if that fails:
+ case __NR_readlink:
+ case __NR_readlinkat:
+ return Error(ENOENT);
+
+ // ...and if it found an external symbolizer, it will try to run it:
+ // (See also bug 1081242 comment #7.)
+ CASES_FOR_stat:
+ return Error(ENOENT);
+#endif // MOZ_ASAN
+
+ // Replace statfs with open (which may be brokered) and
+ // fstatfs (which is not allowed in this policy, but may be
+ // allowed by subclasses if they wish to enable statfs).
+ CASES_FOR_statfs:
+ return Trap(StatFsTrap, nullptr);
+
+ default:
+ return SandboxPolicyBase::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+// The process-type-specific syscall rules start here:
+
+// The seccomp-bpf filter for content processes is not a true sandbox
+// on its own; its purpose is attack surface reduction and syscall
+// interception in support of a semantic sandboxing layer. On B2G
+// this is the Android process permission model; on desktop,
+// namespaces and chroot() will be used.
+class ContentSandboxPolicy : public SandboxPolicyCommon {
+ private:
+ ContentProcessSandboxParams mParams;
+ bool mAllowSysV;
+ bool mUsingRenderDoc;
+
+ bool BelowLevel(int aLevel) const { return mParams.mLevel < aLevel; }
+ ResultExpr AllowBelowLevel(int aLevel, ResultExpr aOrElse) const {
+ return BelowLevel(aLevel) ? Allow() : std::move(aOrElse);
+ }
+ ResultExpr AllowBelowLevel(int aLevel) const {
+ return AllowBelowLevel(aLevel, InvalidSyscall());
+ }
+
+ static intptr_t GetPPidTrap(ArgsRef aArgs, void* aux) {
+ // In a pid namespace, getppid() will return 0. We will return 0 instead
+ // of the real parent pid to see what breaks when we introduce the
+ // pid namespace (Bug 1151624).
+ return 0;
+ }
+
+ public:
+ ContentSandboxPolicy(SandboxBrokerClient* aBroker,
+ ContentProcessSandboxParams&& aParams)
+ : mParams(std::move(aParams)),
+ mAllowSysV(PR_GetEnv("MOZ_SANDBOX_ALLOW_SYSV") != nullptr),
+ mUsingRenderDoc(PR_GetEnv("RENDERDOC_CAPTUREOPTS") != nullptr) {
+ mBroker = aBroker;
+ mMayCreateShmem = true;
+ mAllowUnsafeSocketPair = true;
+ mBrokeredConnect = true;
+ }
+
+ ~ContentSandboxPolicy() override = default;
+
+ Maybe<ResultExpr> EvaluateSocketCall(int aCall,
+ bool aHasArgs) const override {
+ switch (aCall) {
+ case SYS_SENDMMSG: // libresolv via libasyncns; see bug 1355274
+ return Some(Allow());
+
+#ifdef ANDROID
+ case SYS_SOCKET:
+ return Some(Error(EACCES));
+#else // #ifdef DESKTOP
+ case SYS_SOCKET:
+ case SYS_CONNECT:
+ if (BelowLevel(4)) {
+ return Some(Allow());
+ }
+ return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
+
+ // FIXME (bug 1761134): sockopts should be filtered
+ case SYS_GETSOCKOPT:
+ case SYS_SETSOCKOPT:
+ // These next 3 were needed for X11; they may not be needed
+ // with X11 lockdown, but there's not much attack surface here.
+ case SYS_GETSOCKNAME:
+ case SYS_GETPEERNAME:
+ case SYS_SHUTDOWN:
+ return Some(Allow());
+
+ case SYS_ACCEPT:
+ case SYS_ACCEPT4:
+ if (mUsingRenderDoc) {
+ return Some(Allow());
+ }
+ [[fallthrough]];
+#endif
+ default:
+ return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
+ }
+ }
+
+#ifdef DESKTOP
+ Maybe<ResultExpr> EvaluateIpcCall(int aCall, int aArgShift) const override {
+ switch (aCall) {
+ // These are a problem: SysV IPC follows the Unix "same uid
+ // policy" and can't be restricted/brokered like file access.
+ // We're not using it directly, but there are some library
+ // dependencies that do; see ContentNeedsSysVIPC() in
+ // SandboxLaunch.cpp. Also, Cairo as used by GTK will sometimes
+ // try to use MIT-SHM, so shmget() is a non-fatal error. See
+ // also bug 1376910 and bug 1438401.
+ case SHMGET:
+ return Some(mAllowSysV ? Allow() : Error(EPERM));
+ case SHMCTL:
+ case SHMAT:
+ case SHMDT:
+ case SEMGET:
+ case SEMCTL:
+ case SEMOP:
+ if (mAllowSysV) {
+ return Some(Allow());
+ }
+ return SandboxPolicyCommon::EvaluateIpcCall(aCall, aArgShift);
+ default:
+ return SandboxPolicyCommon::EvaluateIpcCall(aCall, aArgShift);
+ }
+ }
+#endif
+
+#ifdef MOZ_PULSEAUDIO
+ ResultExpr PrctlPolicy() const override {
+ if (BelowLevel(4)) {
+ Arg<int> op(0);
+ return If(op == PR_GET_NAME, Allow())
+ .Else(SandboxPolicyCommon::PrctlPolicy());
+ }
+ return SandboxPolicyCommon::PrctlPolicy();
+ }
+#endif
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ // Straight allow for anything that got overriden via prefs
+ const auto& whitelist = mParams.mSyscallWhitelist;
+ if (std::find(whitelist.begin(), whitelist.end(), sysno) !=
+ whitelist.end()) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Allowing syscall nr %d via whitelist", sysno);
+ }
+ return Allow();
+ }
+
+ // Level 1 has been removed. If seccomp-bpf is used, then we're
+ // necessarily at level >= 2 and filesystem access is brokered.
+ MOZ_ASSERT(!BelowLevel(2));
+ MOZ_ASSERT(mBroker);
+
+ switch (sysno) {
+#ifdef DESKTOP
+ case __NR_getppid:
+ return Trap(GetPPidTrap, nullptr);
+
+ // GTK's theme parsing tries to getcwd() while sandboxed, but
+ // only during Talos runs.
+ case __NR_getcwd:
+ return Error(ENOENT);
+
+# ifdef MOZ_PULSEAUDIO
+ CASES_FOR_fchown:
+ case __NR_fchmod:
+ return AllowBelowLevel(4);
+# endif
+ CASES_FOR_fstatfs: // fontconfig, pulseaudio, GIO (see also statfs)
+ case __NR_flock: // graphics
+ return Allow();
+
+ // Bug 1354731: proprietary GL drivers try to mknod() their devices
+# ifdef __NR_mknod
+ case __NR_mknod:
+# endif
+ case __NR_mknodat: {
+ Arg<mode_t> mode(sysno == __NR_mknodat ? 2 : 1);
+ return If((mode & S_IFMT) == S_IFCHR, Error(EPERM))
+ .Else(InvalidSyscall());
+ }
+ // Bug 1438389: ...and nvidia GL will sometimes try to chown the devices
+# ifdef __NR_chown
+ case __NR_chown:
+# endif
+ case __NR_fchownat:
+ return Error(EPERM);
+#endif
+
+ CASES_FOR_select:
+ return Allow();
+
+ case __NR_writev:
+#ifdef DESKTOP
+ case __NR_pwrite64:
+ case __NR_readahead:
+#endif
+ return Allow();
+
+ case __NR_ioctl: {
+#ifdef MOZ_ALSA
+ if (BelowLevel(4)) {
+ return Allow();
+ }
+#endif
+ Arg<unsigned long> request(1);
+ auto shifted_type = request & kIoctlTypeMask;
+
+ // Rust's stdlib seems to use FIOCLEX instead of equivalent fcntls.
+ return If(request == FIOCLEX, Allow())
+ // Rust's stdlib also uses FIONBIO instead of equivalent fcntls.
+ .ElseIf(request == FIONBIO, Allow())
+ // Allow anything that isn't a tty ioctl, for now; bug 1302711
+ // will cover changing this to a default-deny policy.
+ .ElseIf(shifted_type != kTtyIoctls, Allow())
+ .Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
+ CASES_FOR_fcntl : {
+ Arg<int> cmd(1);
+ return Switch(cmd)
+ // Nvidia GL and fontconfig (newer versions) use fcntl file locking.
+ .Case(F_SETLK, Allow())
+#ifdef F_SETLK64
+ .Case(F_SETLK64, Allow())
+#endif
+ // Pulseaudio uses F_SETLKW, as does fontconfig.
+ .Case(F_SETLKW, Allow())
+#ifdef F_SETLKW64
+ .Case(F_SETLKW64, Allow())
+#endif
+ // Wayland client libraries use file seals
+ .Case(F_ADD_SEALS, Allow())
+ .Case(F_GET_SEALS, Allow())
+ .Default(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
+ case __NR_brk:
+ // FIXME(bug 1510861) are we using any hints that aren't allowed
+ // in SandboxPolicyCommon now?
+ case __NR_madvise:
+ // libc's realloc uses mremap (Bug 1286119); wasm does too (bug
+ // 1342385).
+ case __NR_mremap:
+ return Allow();
+
+ // Bug 1462640: Mesa libEGL uses mincore to test whether values
+ // are pointers, for reasons.
+ case __NR_mincore: {
+ Arg<size_t> length(1);
+ return If(length == getpagesize(), Allow())
+ .Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
+#ifdef __NR_set_thread_area
+ case __NR_set_thread_area:
+ return Allow();
+#endif
+
+ case __NR_getrusage:
+ case __NR_times:
+ return Allow();
+
+ case __NR_fsync:
+ case __NR_msync:
+ return Allow();
+
+ case __NR_getpriority:
+ case __NR_setpriority:
+ case __NR_sched_getattr:
+ case __NR_sched_setattr:
+ case __NR_sched_get_priority_min:
+ case __NR_sched_get_priority_max:
+ case __NR_sched_getscheduler:
+ case __NR_sched_setscheduler:
+ case __NR_sched_getparam:
+ case __NR_sched_setparam:
+#ifdef DESKTOP
+ case __NR_sched_getaffinity:
+#endif
+ return Allow();
+
+#ifdef DESKTOP
+ case __NR_sched_setaffinity:
+ return Error(EPERM);
+#endif
+
+#ifdef DESKTOP
+ case __NR_pipe2: {
+ // Restrict the flags; O_NOTIFICATION_PIPE in particular
+ // exposes enough attack surface to be a cause for concern
+ // (bug 1808320). O_DIRECT isn't known to be used currently
+ // (Try passes with it blocked), but should be low-risk, and
+ // Chromium allows it.
+ static constexpr int allowed_flags = O_CLOEXEC | O_NONBLOCK | O_DIRECT;
+ Arg<int> flags(1);
+ return If((flags & ~allowed_flags) == 0, Allow())
+ .Else(InvalidSyscall());
+ }
+
+ CASES_FOR_getrlimit:
+ CASES_FOR_getresuid:
+ CASES_FOR_getresgid:
+ return Allow();
+
+ case __NR_prlimit64: {
+ // Allow only the getrlimit() use case. (glibc seems to use
+ // only pid 0 to indicate the current process; pid == getpid()
+ // is equivalent and could also be allowed if needed.)
+ Arg<pid_t> pid(0);
+ // This is really a const struct ::rlimit*, but Arg<> doesn't
+ // work with pointers, only integer types.
+ Arg<uintptr_t> new_limit(2);
+ return If(AllOf(pid == 0, new_limit == 0), Allow())
+ .Else(InvalidSyscall());
+ }
+
+ // PulseAudio calls umask, even though it's unsafe in
+ // multithreaded applications. But, allowing it here doesn't
+ // really do anything one way or the other, now that file
+ // accesses are brokered to another process.
+ case __NR_umask:
+ return AllowBelowLevel(4);
+
+ case __NR_kill: {
+ if (BelowLevel(4)) {
+ Arg<int> sig(1);
+ // PulseAudio uses kill(pid, 0) to check if purported owners of
+ // shared memory files are still alive; see bug 1397753 for more
+ // details.
+ return If(sig == 0, Error(EPERM)).Else(InvalidSyscall());
+ }
+ return InvalidSyscall();
+ }
+
+ case __NR_wait4:
+# ifdef __NR_waitpid
+ case __NR_waitpid:
+# endif
+ // NSPR will start a thread to wait for child processes even if
+ // fork() fails; see bug 227246 and bug 1299581.
+ return Error(ECHILD);
+
+ case __NR_eventfd2:
+ return Allow();
+
+# ifdef __NR_rt_tgsigqueueinfo
+ // Only allow to send signals within the process.
+ case __NR_rt_tgsigqueueinfo: {
+ Arg<pid_t> tgid(0);
+ return If(tgid == getpid(), Allow()).Else(InvalidSyscall());
+ }
+# endif
+
+ case __NR_mlock:
+ case __NR_munlock:
+ return Allow();
+
+ // We can't usefully allow fork+exec, even on a temporary basis;
+ // the child would inherit the seccomp-bpf policy and almost
+ // certainly die from an unexpected SIGSYS. We also can't have
+ // fork() crash, currently, because there are too many system
+ // libraries/plugins that try to run commands. But they can
+ // usually do something reasonable on error.
+ case __NR_clone:
+ return ClonePolicy(Error(EPERM));
+
+ case __NR_clone3:
+ return Error(ENOSYS);
+
+# ifdef __NR_fadvise64
+ case __NR_fadvise64:
+ return Allow();
+# endif
+
+# ifdef __NR_fadvise64_64
+ case __NR_fadvise64_64:
+ return Allow();
+# endif
+
+ case __NR_fallocate:
+ return Allow();
+
+ case __NR_get_mempolicy:
+ return Allow();
+
+ // Required by libnuma for FFmpeg
+ case __NR_set_mempolicy:
+ return Error(ENOSYS);
+
+ case __NR_kcmp:
+ return KcmpPolicyForMesa();
+
+#endif // DESKTOP
+
+ // nsSystemInfo uses uname (and we cache an instance, so
+ // the info remains present even if we block the syscall)
+ case __NR_uname:
+#ifdef DESKTOP
+ case __NR_sysinfo:
+#endif
+ return Allow();
+
+#ifdef MOZ_JPROF
+ case __NR_setitimer:
+ return Allow();
+#endif // MOZ_JPROF
+
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetContentSandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker, ContentProcessSandboxParams&& aParams) {
+ return MakeUnique<ContentSandboxPolicy>(aMaybeBroker, std::move(aParams));
+}
+
+// Unlike for content, the GeckoMediaPlugin seccomp-bpf policy needs
+// to be an effective sandbox by itself, because we allow GMP on Linux
+// systems where that's the only sandboxing mechanism we can use.
+//
+// Be especially careful about what this policy allows.
+class GMPSandboxPolicy : public SandboxPolicyCommon {
+ static intptr_t OpenTrap(const sandbox::arch_seccomp_data& aArgs, void* aux) {
+ const auto files = static_cast<const SandboxOpenedFiles*>(aux);
+ const char* path;
+ int flags;
+
+ switch (aArgs.nr) {
+#ifdef __NR_open
+ case __NR_open:
+ path = reinterpret_cast<const char*>(aArgs.args[0]);
+ flags = static_cast<int>(aArgs.args[1]);
+ break;
+#endif
+ case __NR_openat:
+ // The path has to be absolute to match the pre-opened file (see
+ // assertion in ctor) so the dirfd argument is ignored.
+ path = reinterpret_cast<const char*>(aArgs.args[1]);
+ flags = static_cast<int>(aArgs.args[2]);
+ break;
+ default:
+ MOZ_CRASH("unexpected syscall number");
+ }
+
+ if ((flags & O_ACCMODE) != O_RDONLY) {
+ SANDBOX_LOG("non-read-only open of file %s attempted (flags=0%o)", path,
+ flags);
+ return -EROFS;
+ }
+ int fd = files->GetDesc(path);
+ if (fd < 0) {
+ // SandboxOpenedFile::GetDesc already logged about this, if appropriate.
+ return -ENOENT;
+ }
+ return fd;
+ }
+
+ static intptr_t UnameTrap(const sandbox::arch_seccomp_data& aArgs,
+ void* aux) {
+ const auto buf = reinterpret_cast<struct utsname*>(aArgs.args[0]);
+ PodZero(buf);
+ // The real uname() increases fingerprinting risk for no benefit.
+ // This is close enough.
+ strcpy(buf->sysname, "Linux");
+ strcpy(buf->version, "3");
+ return 0;
+ };
+
+ static intptr_t FcntlTrap(const sandbox::arch_seccomp_data& aArgs,
+ void* aux) {
+ const auto cmd = static_cast<int>(aArgs.args[1]);
+ switch (cmd) {
+ // This process can't exec, so the actual close-on-exec flag
+ // doesn't matter; have it always read as true and ignore writes.
+ case F_GETFD:
+ return O_CLOEXEC;
+ case F_SETFD:
+ return 0;
+ default:
+ return -ENOSYS;
+ }
+ }
+
+ const SandboxOpenedFiles* mFiles;
+
+ public:
+ explicit GMPSandboxPolicy(const SandboxOpenedFiles* aFiles) : mFiles(aFiles) {
+ // Used by the profiler to send data back to the parent process;
+ // we are not enabling the file broker, so this will only work if
+ // memfd_create is available.
+ mMayCreateShmem = true;
+ }
+
+ ~GMPSandboxPolicy() override = default;
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ // Simulate opening the plugin file.
+#ifdef __NR_open
+ case __NR_open:
+#endif
+ case __NR_openat:
+ return Trap(OpenTrap, mFiles);
+
+ case __NR_brk:
+ return Allow();
+ case __NR_sched_get_priority_min:
+ case __NR_sched_get_priority_max:
+ return Allow();
+ case __NR_sched_getparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_setscheduler: {
+ Arg<pid_t> pid(0);
+ return If(pid == 0, Allow()).Else(Trap(SchedTrap, nullptr));
+ }
+
+ // For clock(3) on older glibcs; bug 1304220.
+ case __NR_times:
+ return Allow();
+
+ // Bug 1372428
+ case __NR_uname:
+ return Trap(UnameTrap, nullptr);
+ CASES_FOR_fcntl:
+ return Trap(FcntlTrap, nullptr);
+
+ // Allow the same advice values as the default policy, but return
+ // Error(ENOSYS) for other values. Because the Widevine CDM may probe
+ // advice arguments, including invalid values, we don't want to return
+ // InvalidSyscall(), as this will crash the process. So instead just
+ // indicate such calls are not available.
+ case __NR_madvise: {
+ Arg<int> advice(2);
+ return If(advice == MADV_DONTNEED, Allow())
+ .ElseIf(advice == MADV_FREE, Allow())
+ .ElseIf(advice == MADV_HUGEPAGE, Allow())
+ .ElseIf(advice == MADV_NOHUGEPAGE, Allow())
+#ifdef MOZ_ASAN
+ .ElseIf(advice == MADV_DONTDUMP, Allow())
+#endif
+ .ElseIf(advice == MADV_MERGEABLE, Error(EPERM)) // bug 1705045
+ .Else(Error(ENOSYS));
+ }
+
+ // The profiler will try to readlink /proc/self/exe for native
+ // stackwalking, but that's broken for several other reasons;
+ // see discussion in bug 1770905. (That can be emulated by
+ // pre-recording the result if/when we need it.)
+#ifdef __NR_readlink
+ case __NR_readlink:
+#endif
+ case __NR_readlinkat:
+ return Error(EINVAL);
+
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetMediaSandboxPolicy(
+ const SandboxOpenedFiles* aFiles) {
+ return UniquePtr<sandbox::bpf_dsl::Policy>(new GMPSandboxPolicy(aFiles));
+}
+
+// The policy for the data decoder process is similar to the one for
+// media plugins, but the codec code is all in-tree so it's better
+// behaved and doesn't need special exceptions (or the ability to load
+// a plugin file). However, it does directly create shared memory
+// segments, so it may need file brokering.
+class RDDSandboxPolicy final : public SandboxPolicyCommon {
+ public:
+ explicit RDDSandboxPolicy(SandboxBrokerClient* aBroker) {
+ mBroker = aBroker;
+ mMayCreateShmem = true;
+ }
+
+#ifndef ANDROID
+ Maybe<ResultExpr> EvaluateIpcCall(int aCall, int aArgShift) const override {
+ // The Intel media driver uses SysV IPC (semaphores and shared
+ // memory) on newer hardware models; it always uses this fixed
+ // key, so we can restrict semget and shmget. Unfortunately, the
+ // calls that operate on these resources take "identifiers", which
+ // are unpredictable (by us) but guessable (by an adversary).
+ static constexpr key_t kIntelKey = 'D' << 24 | 'V' << 8 | 'X' << 0;
+
+ switch (aCall) {
+ case SEMGET:
+ case SHMGET: {
+ Arg<key_t> key(0 + aArgShift);
+ return Some(If(key == kIntelKey, Allow()).Else(InvalidSyscall()));
+ }
+
+ case SEMCTL:
+ case SEMOP:
+ case SEMTIMEDOP:
+ case SHMCTL:
+ case SHMAT:
+ case SHMDT:
+ return Some(Allow());
+
+ default:
+ return SandboxPolicyCommon::EvaluateIpcCall(aCall, aArgShift);
+ }
+ }
+#endif
+
+ Maybe<ResultExpr> EvaluateSocketCall(int aCall,
+ bool aHasArgs) const override {
+ switch (aCall) {
+ // These are for X11.
+ case SYS_GETSOCKNAME:
+ case SYS_GETPEERNAME:
+ case SYS_SHUTDOWN:
+ return Some(Allow());
+
+ default:
+ return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
+ }
+ }
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_getrusage:
+ return Allow();
+
+ case __NR_ioctl: {
+ Arg<unsigned long> request(1);
+ auto shifted_type = request & kIoctlTypeMask;
+ static constexpr unsigned long kDrmType =
+ static_cast<unsigned long>('d') << _IOC_TYPESHIFT;
+ // Note: 'b' is also the Binder device on Android.
+ static constexpr unsigned long kDmaBufType =
+ static_cast<unsigned long>('b') << _IOC_TYPESHIFT;
+ // nvidia uses some ioctls from this range (but not actual
+ // fbdev ioctls; nvidia uses values >= 200 for the NR field
+ // (low 8 bits))
+ static constexpr unsigned long kFbDevType =
+ static_cast<unsigned long>('F') << _IOC_TYPESHIFT;
+
+ // Allow DRI and DMA-Buf for VA-API
+ return If(shifted_type == kDrmType, Allow())
+ .ElseIf(shifted_type == kDmaBufType, Allow())
+ // Hack for nvidia, which isn't supported yet:
+ .ElseIf(shifted_type == kFbDevType, Error(ENOTTY))
+ .Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
+ // Mesa/amdgpu
+ case __NR_kcmp:
+ return KcmpPolicyForMesa();
+
+ // We use this in our DMABuf support code.
+ case __NR_eventfd2:
+ return Allow();
+
+ // Allow the sched_* syscalls for the current thread only.
+ // Mesa attempts to use them to optimize performance; often
+ // this involves passing other threads' tids, which we can't
+ // safely allow, but maybe a future Mesa version could fix that.
+ case __NR_sched_getaffinity:
+ case __NR_sched_setaffinity:
+ case __NR_sched_getparam:
+ case __NR_sched_setparam:
+ case __NR_sched_getscheduler:
+ case __NR_sched_setscheduler:
+ case __NR_sched_getattr:
+ case __NR_sched_setattr: {
+ Arg<pid_t> pid(0);
+ return If(pid == 0, Allow()).Else(Trap(SchedTrap, nullptr));
+ }
+
+ // Mesa sometimes wants to know the OS version.
+ case __NR_uname:
+ return Allow();
+
+ // nvidia tries to mknod(!) its devices; that won't work anyway,
+ // so quietly reject it.
+#ifdef __NR_mknod
+ case __NR_mknod:
+#endif
+ case __NR_mknodat:
+ return Error(EPERM);
+
+ // Used by the nvidia GPU driver, including in multi-GPU
+ // systems when we intend to use a non-nvidia GPU. (Also used
+ // by Mesa for its shader cache, but we disable that in this
+ // process.)
+ CASES_FOR_fstatfs:
+ return Allow();
+
+ // Pass through the common policy.
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetDecoderSandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker) {
+ return UniquePtr<sandbox::bpf_dsl::Policy>(
+ new RDDSandboxPolicy(aMaybeBroker));
+}
+
+// Basically a clone of RDDSandboxPolicy until we know exactly what
+// the SocketProcess sandbox looks like.
+class SocketProcessSandboxPolicy final : public SandboxPolicyCommon {
+ public:
+ explicit SocketProcessSandboxPolicy(SandboxBrokerClient* aBroker) {
+ mBroker = aBroker;
+ mMayCreateShmem = true;
+ }
+
+ static intptr_t FcntlTrap(const sandbox::arch_seccomp_data& aArgs,
+ void* aux) {
+ const auto cmd = static_cast<int>(aArgs.args[1]);
+ switch (cmd) {
+ // This process can't exec, so the actual close-on-exec flag
+ // doesn't matter; have it always read as true and ignore writes.
+ case F_GETFD:
+ return O_CLOEXEC;
+ case F_SETFD:
+ return 0;
+ default:
+ return -ENOSYS;
+ }
+ }
+
+ Maybe<ResultExpr> EvaluateSocketCall(int aCall,
+ bool aHasArgs) const override {
+ switch (aCall) {
+ case SYS_SOCKET:
+ case SYS_CONNECT:
+ case SYS_BIND:
+ return Some(Allow());
+
+ // FIXME(bug 1641401) do we really need this?
+ case SYS_SENDMMSG:
+ return Some(Allow());
+
+ case SYS_GETSOCKOPT:
+ case SYS_SETSOCKOPT:
+ case SYS_GETSOCKNAME:
+ case SYS_GETPEERNAME:
+ case SYS_SHUTDOWN:
+ case SYS_ACCEPT:
+ case SYS_ACCEPT4:
+ return Some(Allow());
+
+ default:
+ return SandboxPolicyCommon::EvaluateSocketCall(aCall, aHasArgs);
+ }
+ }
+
+ ResultExpr PrctlPolicy() const override {
+ Arg<int> op(0);
+ return Switch(op)
+ .CASES((PR_SET_NAME, // Thread creation
+ PR_SET_DUMPABLE, // Crash reporting
+ PR_SET_PTRACER), // Debug-mode crash handling
+ Allow())
+ .Default(InvalidSyscall());
+ }
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_getrusage:
+ return Allow();
+
+ case __NR_ioctl: {
+ Arg<unsigned long> request(1);
+ auto shifted_type = request & kIoctlTypeMask;
+
+ // Rust's stdlib seems to use FIOCLEX instead of equivalent fcntls.
+ return If(request == FIOCLEX, Allow())
+ // Rust's stdlib also uses FIONBIO instead of equivalent fcntls.
+ .ElseIf(request == FIONBIO, Allow())
+ // This is used by PR_Available in nsSocketInputStream::Available.
+ .ElseIf(request == FIONREAD, Allow())
+ // Allow anything that isn't a tty ioctl, for now; bug 1302711
+ // will cover changing this to a default-deny policy.
+ .ElseIf(shifted_type != kTtyIoctls, Allow())
+ .Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
+ CASES_FOR_fcntl : {
+ Arg<int> cmd(1);
+ return Switch(cmd)
+ .Case(F_DUPFD_CLOEXEC, Allow())
+ // Nvidia GL and fontconfig (newer versions) use fcntl file locking.
+ .Case(F_SETLK, Allow())
+#ifdef F_SETLK64
+ .Case(F_SETLK64, Allow())
+#endif
+ // Pulseaudio uses F_SETLKW, as does fontconfig.
+ .Case(F_SETLKW, Allow())
+#ifdef F_SETLKW64
+ .Case(F_SETLKW64, Allow())
+#endif
+ .Default(SandboxPolicyCommon::EvaluateSyscall(sysno));
+ }
+
+#ifdef DESKTOP
+ // This section is borrowed from ContentSandboxPolicy
+ CASES_FOR_getrlimit:
+ CASES_FOR_getresuid:
+ CASES_FOR_getresgid:
+ return Allow();
+
+ case __NR_prlimit64: {
+ // Allow only the getrlimit() use case. (glibc seems to use
+ // only pid 0 to indicate the current process; pid == getpid()
+ // is equivalent and could also be allowed if needed.)
+ Arg<pid_t> pid(0);
+ // This is really a const struct ::rlimit*, but Arg<> doesn't
+ // work with pointers, only integer types.
+ Arg<uintptr_t> new_limit(2);
+ return If(AllOf(pid == 0, new_limit == 0), Allow())
+ .Else(InvalidSyscall());
+ }
+#endif // DESKTOP
+
+ // Bug 1640612
+ case __NR_uname:
+ return Allow();
+
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetSocketProcessSandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker) {
+ return UniquePtr<sandbox::bpf_dsl::Policy>(
+ new SocketProcessSandboxPolicy(aMaybeBroker));
+}
+
+class UtilitySandboxPolicy : public SandboxPolicyCommon {
+ public:
+ explicit UtilitySandboxPolicy(SandboxBrokerClient* aBroker) {
+ mBroker = aBroker;
+ mMayCreateShmem = true;
+ }
+
+ ResultExpr PrctlPolicy() const override {
+ Arg<int> op(0);
+ return Switch(op)
+ .CASES((PR_SET_NAME, // Thread creation
+ PR_SET_DUMPABLE, // Crash reporting
+ PR_SET_PTRACER, // Debug-mode crash handling
+ PR_GET_PDEATHSIG), // PGO profiling, cf
+ // https://reviews.llvm.org/D29954
+ Allow())
+ .Default(InvalidSyscall());
+ }
+
+ ResultExpr EvaluateSyscall(int sysno) const override {
+ switch (sysno) {
+ case __NR_getrusage:
+ return Allow();
+
+ // Required by FFmpeg
+ case __NR_get_mempolicy:
+ return Allow();
+
+ // Required by libnuma for FFmpeg
+ case __NR_sched_getaffinity: {
+ Arg<pid_t> pid(0);
+ return If(pid == 0, Allow()).Else(Trap(SchedTrap, nullptr));
+ }
+
+ // Required by libnuma for FFmpeg
+ case __NR_set_mempolicy:
+ return Error(ENOSYS);
+
+ // Pass through the common policy.
+ default:
+ return SandboxPolicyCommon::EvaluateSyscall(sysno);
+ }
+ }
+};
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetUtilitySandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker) {
+ return UniquePtr<sandbox::bpf_dsl::Policy>(
+ new UtilitySandboxPolicy(aMaybeBroker));
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxFilter.h b/security/sandbox/linux/SandboxFilter.h
new file mode 100644
index 0000000000..04a37a32d4
--- /dev/null
+++ b/security/sandbox/linux/SandboxFilter.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxFilter_h
+#define mozilla_SandboxFilter_h
+
+#include <vector>
+#include "mozilla/Atomics.h"
+#include "mozilla/Range.h"
+#include "mozilla/UniquePtr.h"
+
+namespace sandbox {
+namespace bpf_dsl {
+class Policy;
+}
+} // namespace sandbox
+
+namespace mozilla {
+class SandboxBrokerClient;
+
+struct ContentProcessSandboxParams;
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetContentSandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker, ContentProcessSandboxParams&& aParams);
+
+class SandboxOpenedFiles;
+
+// The SandboxOpenedFiles object must live until the process exits.
+UniquePtr<sandbox::bpf_dsl::Policy> GetMediaSandboxPolicy(
+ const SandboxOpenedFiles* aFiles);
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetDecoderSandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker);
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetSocketProcessSandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker);
+
+UniquePtr<sandbox::bpf_dsl::Policy> GetUtilitySandboxPolicy(
+ SandboxBrokerClient* aMaybeBroker);
+
+} // namespace mozilla
+
+#endif
diff --git a/security/sandbox/linux/SandboxFilterUtil.cpp b/security/sandbox/linux/SandboxFilterUtil.cpp
new file mode 100644
index 0000000000..de065d5483
--- /dev/null
+++ b/security/sandbox/linux/SandboxFilterUtil.cpp
@@ -0,0 +1,142 @@
+/* -*- 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/. */
+
+#include "SandboxFilterUtil.h"
+
+#ifndef ANDROID
+# include <linux/ipc.h>
+#endif
+#include <linux/net.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "mozilla/UniquePtr.h"
+#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
+
+// Older kernel headers (mostly Android, but also some older desktop
+// distributions) are missing some or all of these:
+#ifndef SYS_ACCEPT4
+# define SYS_ACCEPT4 18
+#endif
+#ifndef SYS_RECVMMSG
+# define SYS_RECVMMSG 19
+#endif
+#ifndef SYS_SENDMMSG
+# define SYS_SENDMMSG 20
+#endif
+
+using namespace sandbox::bpf_dsl;
+#define CASES SANDBOX_BPF_DSL_CASES
+
+namespace mozilla {
+
+sandbox::bpf_dsl::ResultExpr SandboxPolicyBase::EvaluateSyscall(
+ int aSysno) const {
+ switch (aSysno) {
+#ifdef __NR_socketcall
+ case __NR_socketcall: {
+ Arg<int> call(0);
+ UniquePtr<Caser<int>> acc(new Caser<int>(Switch(call)));
+ for (int i = SYS_SOCKET; i <= SYS_SENDMMSG; ++i) {
+ auto thisCase = EvaluateSocketCall(i, false);
+ // Optimize out cases that are equal to the default.
+ if (thisCase) {
+ acc.reset(new Caser<int>(acc->Case(i, *thisCase)));
+ }
+ }
+ return acc->Default(InvalidSyscall());
+ }
+# ifndef ANDROID
+ case __NR_ipc: {
+ Arg<int> callAndVersion(0);
+ auto call = callAndVersion & 0xFFFF;
+ UniquePtr<Caser<int>> acc(new Caser<int>(Switch(call)));
+ for (int i = SEMOP; i <= DIPC; ++i) {
+ auto thisCase = EvaluateIpcCall(i, 1);
+ // Optimize out cases that are equal to the default.
+ if (thisCase) {
+ acc.reset(new Caser<int>(acc->Case(i, *thisCase)));
+ }
+ }
+ return acc->Default(InvalidSyscall());
+ }
+# endif // ANDROID
+#endif // __NR_socketcall
+ // clang-format off
+#define DISPATCH_SOCKETCALL(sysnum, socketnum) \
+ case sysnum: \
+ return EvaluateSocketCall(socketnum, true).valueOr(InvalidSyscall())
+ DISPATCH_SOCKETCALL(__NR_socket, SYS_SOCKET);
+ DISPATCH_SOCKETCALL(__NR_bind, SYS_BIND);
+ DISPATCH_SOCKETCALL(__NR_connect, SYS_CONNECT);
+ DISPATCH_SOCKETCALL(__NR_listen, SYS_LISTEN);
+#ifdef __NR_accept
+ DISPATCH_SOCKETCALL(__NR_accept, SYS_ACCEPT);
+#endif
+ DISPATCH_SOCKETCALL(__NR_getsockname, SYS_GETSOCKNAME);
+ DISPATCH_SOCKETCALL(__NR_getpeername, SYS_GETPEERNAME);
+ DISPATCH_SOCKETCALL(__NR_socketpair, SYS_SOCKETPAIR);
+#ifdef __NR_send
+ DISPATCH_SOCKETCALL(__NR_send, SYS_SEND);
+ DISPATCH_SOCKETCALL(__NR_recv, SYS_RECV);
+#endif // __NR_send
+ DISPATCH_SOCKETCALL(__NR_sendto, SYS_SENDTO);
+ DISPATCH_SOCKETCALL(__NR_recvfrom, SYS_RECVFROM);
+ DISPATCH_SOCKETCALL(__NR_shutdown, SYS_SHUTDOWN);
+ DISPATCH_SOCKETCALL(__NR_setsockopt, SYS_SETSOCKOPT);
+ DISPATCH_SOCKETCALL(__NR_getsockopt, SYS_GETSOCKOPT);
+ DISPATCH_SOCKETCALL(__NR_sendmsg, SYS_SENDMSG);
+ DISPATCH_SOCKETCALL(__NR_recvmsg, SYS_RECVMSG);
+ DISPATCH_SOCKETCALL(__NR_accept4, SYS_ACCEPT4);
+ DISPATCH_SOCKETCALL(__NR_recvmmsg, SYS_RECVMMSG);
+ DISPATCH_SOCKETCALL(__NR_sendmmsg, SYS_SENDMMSG);
+#undef DISPATCH_SOCKETCALL
+#ifndef __NR_socketcall
+#ifndef ANDROID
+#define DISPATCH_SYSVCALL(sysnum, ipcnum) \
+ case sysnum: \
+ return EvaluateIpcCall(ipcnum, 0).valueOr(InvalidSyscall())
+ DISPATCH_SYSVCALL(__NR_semop, SEMOP);
+ DISPATCH_SYSVCALL(__NR_semget, SEMGET);
+ DISPATCH_SYSVCALL(__NR_semctl, SEMCTL);
+ DISPATCH_SYSVCALL(__NR_semtimedop, SEMTIMEDOP);
+ DISPATCH_SYSVCALL(__NR_msgsnd, MSGSND);
+ DISPATCH_SYSVCALL(__NR_msgrcv, MSGRCV);
+ DISPATCH_SYSVCALL(__NR_msgget, MSGGET);
+ DISPATCH_SYSVCALL(__NR_msgctl, MSGCTL);
+ DISPATCH_SYSVCALL(__NR_shmat, SHMAT);
+ DISPATCH_SYSVCALL(__NR_shmdt, SHMDT);
+ DISPATCH_SYSVCALL(__NR_shmget, SHMGET);
+ DISPATCH_SYSVCALL(__NR_shmctl, SHMCTL);
+#undef DISPATCH_SYSVCALL
+#endif // ANDROID
+#endif // __NR_socketcall
+ // clang-format on
+ default:
+ return InvalidSyscall();
+ }
+}
+
+/* static */ bool SandboxPolicyBase::HasSeparateSocketCalls() {
+#ifdef __NR_socketcall
+ // If we have both syscalls, dynamically detect (and cache).
+ static const bool kCache = [] {
+ int fd = syscall(__NR_socket, AF_LOCAL, SOCK_STREAM, 0);
+ if (fd < 0) {
+ MOZ_DIAGNOSTIC_ASSERT(errno == ENOSYS);
+ return false;
+ }
+ close(fd);
+ return true;
+ }();
+ return kCache;
+#else // no socketcall; must be separate syscalls
+ return true;
+#endif
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxFilterUtil.h b/security/sandbox/linux/SandboxFilterUtil.h
new file mode 100644
index 0000000000..6e9180bb95
--- /dev/null
+++ b/security/sandbox/linux/SandboxFilterUtil.h
@@ -0,0 +1,248 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxFilterUtil_h
+#define mozilla_SandboxFilterUtil_h
+
+// This header file exists to hold helper code for SandboxFilter.cpp,
+// to make that file easier to read for anyone trying to understand
+// the filter policy. It's mostly about smoothing out differences
+// between different Linux architectures.
+
+#include "mozilla/Maybe.h"
+#include "sandbox/linux/bpf_dsl/policy.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace mozilla {
+
+// This class handles syscalls for BSD socket and SysV IPC operations.
+// On 32-bit x86 they're multiplexed via socketcall(2) and ipc(2),
+// respectively; on most other architectures they're individual system
+// calls. It translates the syscalls into socketcall/ipc selector
+// values, because those are defined (even if not used) for all
+// architectures. (As of kernel 4.2.0, x86 also has regular system
+// calls, but userland will typically still use socketcall.)
+//
+// This EvaluateSyscall() routine always returns InvalidSyscall() for
+// everything else. It's assumed that subclasses will be implementing
+// a whitelist policy, so they can handle what they're whitelisting
+// and then defer to this class in the default case.
+class SandboxPolicyBase : public sandbox::bpf_dsl::Policy {
+ public:
+ using ResultExpr = sandbox::bpf_dsl::ResultExpr;
+
+ virtual ResultExpr EvaluateSyscall(int aSysno) const override;
+
+ // aHasArgs is true if this is a normal syscall, where the arguments
+ // can be inspected by seccomp-bpf, rather than a case of socketcall().
+ virtual Maybe<ResultExpr> EvaluateSocketCall(int aCall, bool aHasArgs) const {
+ return Nothing();
+ }
+
+ // Android doesn't use SysV IPC (and doesn't define the selector
+ // constants in its headers), so this isn't implemented there.
+#ifndef ANDROID
+ // aArgShift is the offset to add the argument index when
+ // constructing `Arg` objects: it's 0 for separate syscalls and 1
+ // for ipc().
+ virtual Maybe<ResultExpr> EvaluateIpcCall(int aCall, int aArgShift) const {
+ return Nothing();
+ }
+#endif
+
+ // Returns true if the running kernel supports separate syscalls for
+ // socket operations, or false if it supports only socketcall(2).
+ static bool HasSeparateSocketCalls();
+};
+
+} // namespace mozilla
+
+// "Machine independent" pseudo-syscall numbers, to deal with arch
+// dependencies. (Most 32-bit archs started with 32-bit off_t; older
+// archs started with 16-bit uid_t/gid_t; 32-bit registers can't hold
+// a 64-bit offset for mmap; and so on.)
+//
+// For some of these, the "old" syscalls are also in use in some
+// cases; see, e.g., the handling of RT vs. non-RT signal syscalls.
+
+#ifdef __NR_mmap2
+# define CASES_FOR_mmap case __NR_mmap2
+#else
+# define CASES_FOR_mmap case __NR_mmap
+#endif
+
+#ifdef __NR_fchown32
+# define CASES_FOR_fchown \
+ case __NR_fchown32: \
+ case __NR_fchown
+#else
+# define CASES_FOR_fchown case __NR_fchown
+#endif
+
+#ifdef __NR_getuid32
+# define CASES_FOR_getuid case __NR_getuid32
+# define CASES_FOR_getgid case __NR_getgid32
+# define CASES_FOR_geteuid case __NR_geteuid32
+# define CASES_FOR_getegid case __NR_getegid32
+# define CASES_FOR_getresuid \
+ case __NR_getresuid32: \
+ case __NR_getresuid
+# define CASES_FOR_getresgid \
+ case __NR_getresgid32: \
+ case __NR_getresgid
+// The set*id syscalls are omitted; we'll probably never need to allow them.
+#else
+# define CASES_FOR_getuid case __NR_getuid
+# define CASES_FOR_getgid case __NR_getgid
+# define CASES_FOR_geteuid case __NR_geteuid
+# define CASES_FOR_getegid case __NR_getegid
+# define CASES_FOR_getresuid case __NR_getresuid
+# define CASES_FOR_getresgid case __NR_getresgid
+#endif
+
+#ifdef __NR_stat64
+# define CASES_FOR_stat case __NR_stat64
+# define CASES_FOR_lstat case __NR_lstat64
+# define CASES_FOR_fstat case __NR_fstat64
+# define CASES_FOR_fstatat case __NR_fstatat64
+# define CASES_FOR_statfs \
+ case __NR_statfs64: \
+ case __NR_statfs
+# define CASES_FOR_fstatfs \
+ case __NR_fstatfs64: \
+ case __NR_fstatfs
+# define CASES_FOR_fcntl case __NR_fcntl64
+// FIXME: we might not need the compat cases for these on non-Android:
+# define CASES_FOR_lseek \
+ case __NR_lseek: \
+ case __NR__llseek
+# define CASES_FOR_ftruncate \
+ case __NR_ftruncate: \
+ case __NR_ftruncate64
+#else
+# define CASES_FOR_stat case __NR_stat
+# define CASES_FOR_lstat case __NR_lstat
+# define CASES_FOR_fstatat case __NR_newfstatat
+# define CASES_FOR_fstat case __NR_fstat
+# define CASES_FOR_fstatfs case __NR_fstatfs
+# define CASES_FOR_statfs case __NR_statfs
+# define CASES_FOR_fcntl case __NR_fcntl
+# define CASES_FOR_lseek case __NR_lseek
+# define CASES_FOR_ftruncate case __NR_ftruncate
+#endif
+
+// getdents is not like the other FS-related syscalls with a "64" variant
+#ifdef __NR_getdents
+# define CASES_FOR_getdents \
+ case __NR_getdents64: \
+ case __NR_getdents
+#else
+# define CASES_FOR_getdents case __NR_getdents64
+#endif
+
+#ifdef __NR_sigprocmask
+# define CASES_FOR_sigprocmask \
+ case __NR_sigprocmask: \
+ case __NR_rt_sigprocmask
+# define CASES_FOR_sigaction \
+ case __NR_sigaction: \
+ case __NR_rt_sigaction
+# define CASES_FOR_sigreturn \
+ case __NR_sigreturn: \
+ case __NR_rt_sigreturn
+#else
+# define CASES_FOR_sigprocmask case __NR_rt_sigprocmask
+# define CASES_FOR_sigaction case __NR_rt_sigaction
+# define CASES_FOR_sigreturn case __NR_rt_sigreturn
+#endif
+
+#ifdef __NR_clock_gettime64
+# define CASES_FOR_clock_gettime \
+ case __NR_clock_gettime: \
+ case __NR_clock_gettime64
+# define CASES_FOR_clock_getres \
+ case __NR_clock_getres: \
+ case __NR_clock_getres_time64
+# define CASES_FOR_clock_nanosleep \
+ case __NR_clock_nanosleep: \
+ case __NR_clock_nanosleep_time64
+# define CASES_FOR_pselect6 \
+ case __NR_pselect6: \
+ case __NR_pselect6_time64
+# define CASES_FOR_ppoll \
+ case __NR_ppoll: \
+ case __NR_ppoll_time64
+# define CASES_FOR_futex \
+ case __NR_futex: \
+ case __NR_futex_time64
+#else
+# define CASES_FOR_clock_gettime case __NR_clock_gettime
+# define CASES_FOR_clock_getres case __NR_clock_getres
+# define CASES_FOR_clock_nanosleep case __NR_clock_nanosleep
+# define CASES_FOR_pselect6 case __NR_pselect6
+# define CASES_FOR_ppoll case __NR_ppoll
+# define CASES_FOR_futex case __NR_futex
+#endif
+
+#if defined(__NR__newselect)
+# define CASES_FOR_select \
+ case __NR__newselect: \
+ CASES_FOR_pselect6
+#elif defined(__NR_select)
+# define CASES_FOR_select \
+ case __NR_select: \
+ CASES_FOR_pselect6
+#else
+# define CASES_FOR_select CASES_FOR_pselect6
+#endif
+
+#ifdef __NR_poll
+# define CASES_FOR_poll \
+ case __NR_poll: \
+ CASES_FOR_ppoll
+#else
+# define CASES_FOR_poll CASES_FOR_ppoll
+#endif
+
+#ifdef __NR_epoll_create
+# define CASES_FOR_epoll_create \
+ case __NR_epoll_create: \
+ case __NR_epoll_create1
+#else
+# define CASES_FOR_epoll_create case __NR_epoll_create1
+#endif
+
+#ifdef __NR_epoll_wait
+# define CASES_FOR_epoll_wait \
+ case __NR_epoll_wait: \
+ case __NR_epoll_pwait
+#else
+# define CASES_FOR_epoll_wait case __NR_epoll_pwait
+#endif
+
+#ifdef __NR_pipe
+# define CASES_FOR_pipe \
+ case __NR_pipe: \
+ case __NR_pipe2
+#else
+# define CASES_FOR_pipe case __NR_pipe2
+#endif
+
+#ifdef __NR_dup2
+# define CASES_FOR_dup2 \
+ case __NR_dup2: \
+ case __NR_dup3
+#else
+# define CASES_FOR_dup2 case __NR_dup3
+#endif
+
+#ifdef __NR_ugetrlimit
+# define CASES_FOR_getrlimit case __NR_ugetrlimit
+#else
+# define CASES_FOR_getrlimit case __NR_getrlimit
+#endif
+
+#endif // mozilla_SandboxFilterUtil_h
diff --git a/security/sandbox/linux/SandboxHooks.cpp b/security/sandbox/linux/SandboxHooks.cpp
new file mode 100644
index 0000000000..be1a9b1541
--- /dev/null
+++ b/security/sandbox/linux/SandboxHooks.cpp
@@ -0,0 +1,110 @@
+/* -*- 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/. */
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Types.h"
+
+#include <dlfcn.h>
+#include <signal.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/inotify.h>
+#ifdef MOZ_X11
+# include <X11/Xlib.h>
+#endif
+
+// Signal number used to enable seccomp on each thread.
+extern mozilla::Atomic<int> gSeccompTsyncBroadcastSignum;
+
+static bool SigSetNeedsFixup(const sigset_t* aSet) {
+ int tsyncSignum = gSeccompTsyncBroadcastSignum;
+
+ return aSet != nullptr &&
+ (sigismember(aSet, SIGSYS) ||
+ (tsyncSignum != 0 && sigismember(aSet, tsyncSignum)));
+}
+
+static void SigSetFixup(sigset_t* aSet) {
+ int tsyncSignum = gSeccompTsyncBroadcastSignum;
+ int rv = sigdelset(aSet, SIGSYS);
+ MOZ_RELEASE_ASSERT(rv == 0);
+ if (tsyncSignum != 0) {
+ rv = sigdelset(aSet, tsyncSignum);
+ MOZ_RELEASE_ASSERT(rv == 0);
+ }
+}
+
+// This file defines a hook for sigprocmask() and pthread_sigmask().
+// Bug 1176099: some threads block SIGSYS signal which breaks our seccomp-bpf
+// sandbox. To avoid this, we intercept the call and remove SIGSYS.
+//
+// ENOSYS indicates an error within the hook function itself.
+static int HandleSigset(int (*aRealFunc)(int, const sigset_t*, sigset_t*),
+ int aHow, const sigset_t* aSet, sigset_t* aOldSet,
+ bool aUseErrno) {
+ if (!aRealFunc) {
+ if (aUseErrno) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return ENOSYS;
+ }
+
+ // Avoid unnecessary work
+ if (aHow == SIG_UNBLOCK || !SigSetNeedsFixup(aSet)) {
+ return aRealFunc(aHow, aSet, aOldSet);
+ }
+
+ sigset_t newSet = *aSet;
+ SigSetFixup(&newSet);
+ return aRealFunc(aHow, &newSet, aOldSet);
+}
+
+extern "C" MOZ_EXPORT int sigprocmask(int how, const sigset_t* set,
+ sigset_t* oldset) {
+ static auto sRealFunc =
+ (int (*)(int, const sigset_t*, sigset_t*))dlsym(RTLD_NEXT, "sigprocmask");
+
+ return HandleSigset(sRealFunc, how, set, oldset, true);
+}
+
+extern "C" MOZ_EXPORT int pthread_sigmask(int how, const sigset_t* set,
+ sigset_t* oldset) {
+ static auto sRealFunc = (int (*)(int, const sigset_t*, sigset_t*))dlsym(
+ RTLD_NEXT, "pthread_sigmask");
+
+ return HandleSigset(sRealFunc, how, set, oldset, false);
+}
+
+extern "C" MOZ_EXPORT int sigaction(int signum, const struct sigaction* act,
+ struct sigaction* oldact) {
+ static auto sRealFunc =
+ (int (*)(int, const struct sigaction*, struct sigaction*))dlsym(
+ RTLD_NEXT, "sigaction");
+
+ if (!sRealFunc) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (act == nullptr || !SigSetNeedsFixup(&act->sa_mask)) {
+ return sRealFunc(signum, act, oldact);
+ }
+
+ struct sigaction newact = *act;
+ SigSetFixup(&newact.sa_mask);
+ return sRealFunc(signum, &newact, oldact);
+}
+
+extern "C" MOZ_EXPORT int inotify_init(void) { return inotify_init1(0); }
+
+extern "C" MOZ_EXPORT int inotify_init1(int flags) {
+ errno = ENOSYS;
+ return -1;
+}
diff --git a/security/sandbox/linux/SandboxInfo.cpp b/security/sandbox/linux/SandboxInfo.cpp
new file mode 100644
index 0000000000..3d71e55921
--- /dev/null
+++ b/security/sandbox/linux/SandboxInfo.cpp
@@ -0,0 +1,201 @@
+/* -*- 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/. */
+
+#include "SandboxInfo.h"
+#include "SandboxLogging.h"
+#include "LinuxSched.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/SandboxSettings.h"
+#include "sandbox/linux/system_headers/linux_seccomp.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+#ifdef MOZ_VALGRIND
+# include <valgrind/valgrind.h>
+#endif
+
+// A note about assertions: in general, the worst thing this module
+// should be able to do is disable sandboxing features, so release
+// asserts or MOZ_CRASH should be avoided, even for seeming
+// impossibilities like an unimplemented syscall returning success
+// (which has happened: https://crbug.com/439795 ).
+//
+// MOZ_DIAGNOSTIC_ASSERT (debug builds, plus Nightly/Aurora non-debug)
+// is probably the best choice for conditions that shouldn't be able
+// to fail without the help of bugs in the kernel or system libraries.
+//
+// Regardless of assertion type, whatever condition caused it to fail
+// should generally also disable the corresponding feature on builds
+// that omit the assertion.
+
+namespace mozilla {
+
+static bool HasSeccompBPF() {
+ // Allow simulating the absence of seccomp-bpf support, for testing.
+ if (getenv("MOZ_FAKE_NO_SANDBOX")) {
+ return false;
+ }
+
+ // Valgrind and the sandbox don't interact well, probably because Valgrind
+ // does various system calls which aren't allowed, even if Firefox itself
+ // is playing by the rules.
+#if defined(MOZ_VALGRIND)
+ if (RUNNING_ON_VALGRIND) {
+ return false;
+ }
+#endif
+
+ // Determine whether seccomp-bpf is supported by trying to
+ // enable it with an invalid pointer for the filter. This will
+ // fail with EFAULT if supported and EINVAL if not, without
+ // changing the process's state.
+
+ int rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, nullptr);
+ MOZ_DIAGNOSTIC_ASSERT(rv == -1,
+ "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER,"
+ " nullptr) didn't fail");
+ MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL);
+ return rv == -1 && errno == EFAULT;
+}
+
+static bool HasSeccompTSync() {
+ // Similar to above, but for thread-sync mode. See also Chromium's
+ // sandbox::SandboxBPF::SupportsSeccompThreadFilterSynchronization
+ if (getenv("MOZ_FAKE_NO_SECCOMP_TSYNC")) {
+ return false;
+ }
+ int rv = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER,
+ SECCOMP_FILTER_FLAG_TSYNC, nullptr);
+ MOZ_DIAGNOSTIC_ASSERT(rv == -1,
+ "seccomp(..., SECCOMP_FILTER_FLAG_TSYNC,"
+ " nullptr) didn't fail");
+ MOZ_DIAGNOSTIC_ASSERT(errno == EFAULT || errno == EINVAL || errno == ENOSYS);
+ return rv == -1 && errno == EFAULT;
+}
+
+static bool HasUserNamespaceSupport() {
+ // Note: the /proc/<pid>/ns/* files track setns(2) support, which in
+ // some cases (e.g., pid) significantly postdates kernel support for
+ // the namespace type, so in general this type of check could be a
+ // false negative. However, for user namespaces, any kernel new
+ // enough for the feature to be usable for us has setns support
+ // (v3.8), so this is okay.
+ //
+ // The non-user namespaces all default to "y" in init/Kconfig, but
+ // check them explicitly in case someone has a weird custom config.
+ static const char* const paths[] = {
+ "/proc/self/ns/user",
+ "/proc/self/ns/pid",
+ "/proc/self/ns/net",
+ "/proc/self/ns/ipc",
+ };
+ for (size_t i = 0; i < ArrayLength(paths); ++i) {
+ if (access(paths[i], F_OK) == -1) {
+ MOZ_ASSERT(errno == ENOENT);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool CanCreateUserNamespace() {
+ // Unfortunately, the only way to verify that this process can
+ // create a new user namespace is to actually create one; because
+ // this process's namespaces shouldn't be side-effected (yet), it's
+ // necessary to clone (and collect) a child process. See also
+ // Chromium's sandbox::Credentials::SupportsNewUserNS.
+ //
+ // This is somewhat more expensive than the other tests, so it's
+ // cached in the environment to prevent child processes from having
+ // to re-run the test.
+ //
+ // This is run at static initializer time, while single-threaded, so
+ // locking isn't needed to access the environment.
+ static const char kCacheEnvName[] = "MOZ_ASSUME_USER_NS";
+ const char* cached = getenv(kCacheEnvName);
+ if (cached) {
+ return cached[0] > '0';
+ }
+
+ // Bug 1434528: In addition to CLONE_NEWUSER, do something that uses
+ // the new capabilities (in this case, cloning another namespace) to
+ // detect AppArmor policies that allow CLONE_NEWUSER but don't allow
+ // doing anything useful with it.
+ pid_t pid = syscall(__NR_clone, SIGCHLD | CLONE_NEWUSER | CLONE_NEWPID,
+ nullptr, nullptr, nullptr, nullptr);
+ if (pid == 0) {
+ // In the child. Do as little as possible.
+ _exit(0);
+ }
+ if (pid == -1) {
+ // Failure.
+ MOZ_ASSERT(errno == EINVAL || // unsupported
+ errno == EPERM || // root-only, or we're already chrooted
+ errno == EUSERS); // already at user namespace nesting limit
+ setenv(kCacheEnvName, "0", 1);
+ return false;
+ }
+ // Otherwise, in the parent and successful.
+ bool waitpid_ok = HANDLE_EINTR(waitpid(pid, nullptr, 0)) == pid;
+ MOZ_ASSERT(waitpid_ok);
+ if (!waitpid_ok) {
+ return false;
+ }
+ setenv(kCacheEnvName, "1", 1);
+ return true;
+}
+
+/* static */
+const SandboxInfo SandboxInfo::sSingleton = SandboxInfo();
+
+SandboxInfo::SandboxInfo() {
+ int flags = 0;
+ static_assert(sizeof(flags) >= sizeof(Flags), "enum Flags fits in an int");
+
+ if (HasSeccompBPF()) {
+ flags |= kHasSeccompBPF;
+ if (HasSeccompTSync()) {
+ flags |= kHasSeccompTSync;
+ }
+ }
+
+ if (HasUserNamespaceSupport()) {
+ flags |= kHasPrivilegedUserNamespaces;
+ if (CanCreateUserNamespace()) {
+ flags |= kHasUserNamespaces;
+ }
+ }
+
+ // We can't use mozilla::IsContentSandboxEnabled() here because a)
+ // libmozsandbox can't depend on libxul, and b) this is called in a static
+ // initializer before the prefences service is ready.
+ if (!getenv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+ flags |= kEnabledForContent;
+ }
+ if (getenv("MOZ_PERMISSIVE_CONTENT_SANDBOX")) {
+ flags |= kPermissive;
+ }
+ if (!getenv("MOZ_DISABLE_GMP_SANDBOX")) {
+ flags |= kEnabledForMedia;
+ }
+ if (getenv("MOZ_SANDBOX_LOGGING")) {
+ flags |= kVerbose;
+ }
+
+ mFlags = static_cast<Flags>(flags);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxInfo.h b/security/sandbox/linux/SandboxInfo.h
new file mode 100644
index 0000000000..fb20d206a3
--- /dev/null
+++ b/security/sandbox/linux/SandboxInfo.h
@@ -0,0 +1,70 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxInfo_h
+#define mozilla_SandboxInfo_h
+
+#include "mozilla/Types.h"
+
+// Information on what parts of sandboxing are enabled in this build
+// and/or supported by the system.
+
+namespace mozilla {
+
+class SandboxInfo {
+ public:
+ // No need to prevent copying; this is essentially just a const int.
+ SandboxInfo(const SandboxInfo& aOther) = default;
+
+ // Flags are checked at initializer time; this returns them.
+ static const SandboxInfo& Get() { return sSingleton; }
+
+ enum Flags {
+ // System call filtering; kernel config option CONFIG_SECCOMP_FILTER.
+ kHasSeccompBPF = 1 << 0,
+ // Whether to use a sandbox for content processes; env var
+ // MOZ_DISABLE_CONTENT_SANDBOX
+ kEnabledForContent = 1 << 1,
+ // Whether to use a sandbox for GMP processes; env var
+ // MOZ_DISABLE_GMP_SANDBOX.
+ kEnabledForMedia = 1 << 2,
+ // Env var MOZ_SANDBOX_LOGGING.
+ kVerbose = 1 << 3,
+ // Kernel can atomically set system call filtering on entire thread group.
+ kHasSeccompTSync = 1 << 4,
+ // Can this process create user namespaces? (Man page user_namespaces(7).)
+ kHasUserNamespaces = 1 << 5,
+ // Could a more privileged process have user namespaces, even if we can't?
+ kHasPrivilegedUserNamespaces = 1 << 6,
+ // Env var MOZ_PERMISSIVE_CONTENT_SANDBOX
+ kPermissive = 1 << 7,
+ // (1 << 8) was kUnexpectedThreads
+ };
+
+ bool Test(Flags aFlag) const { return (mFlags & aFlag) == aFlag; }
+
+ // Returns true if SetContentProcessSandbox may be called.
+ bool CanSandboxContent() const {
+ return !Test(kEnabledForContent) || Test(kHasSeccompBPF);
+ }
+
+ // Returns true if SetMediaPluginSandbox may be called.
+ bool CanSandboxMedia() const {
+ return !Test(kEnabledForMedia) || Test(kHasSeccompBPF);
+ }
+
+ // For telemetry / crash annotation uses.
+ uint32_t AsInteger() const { return mFlags; }
+
+ private:
+ enum Flags mFlags;
+ static const MOZ_EXPORT SandboxInfo sSingleton;
+ SandboxInfo();
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxInfo_h
diff --git a/security/sandbox/linux/SandboxInternal.h b/security/sandbox/linux/SandboxInternal.h
new file mode 100644
index 0000000000..27934f9a8e
--- /dev/null
+++ b/security/sandbox/linux/SandboxInternal.h
@@ -0,0 +1,28 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxInternal_h
+#define mozilla_SandboxInternal_h
+
+#include <signal.h>
+
+#include "mozilla/Types.h"
+
+struct sock_fprog;
+
+namespace mozilla {
+
+// SandboxCrash() has to be in libxul to use internal interfaces, but
+// its caller in libmozsandbox.
+// See also bug 1101170.
+
+typedef void (*SandboxCrashFunc)(int, siginfo_t*, void*, const void*);
+extern MOZ_EXPORT SandboxCrashFunc gSandboxCrashFunc;
+extern const sock_fprog* gSetSandboxFilter;
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxInternal_h
diff --git a/security/sandbox/linux/SandboxLogging.cpp b/security/sandbox/linux/SandboxLogging.cpp
new file mode 100644
index 0000000000..de858a3606
--- /dev/null
+++ b/security/sandbox/linux/SandboxLogging.cpp
@@ -0,0 +1,141 @@
+/* -*- 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/. */
+
+#include "SandboxLogging.h"
+
+#ifdef ANDROID
+# include <android/log.h>
+#endif
+#include <algorithm>
+#include <stdio.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+
+#ifdef __GLIBC_PREREQ
+# if __GLIBC_PREREQ(2, 32)
+# define HAVE_STRERRORNAME_NP 1
+# endif
+#endif
+
+namespace mozilla {
+
+// Alters an iovec array to remove the first `toDrop` bytes. This
+// complexity is necessary because writev can return a short write
+// (e.g., if stderr is a pipe and the buffer is almost full).
+static void IOVecDrop(struct iovec* iov, int iovcnt, size_t toDrop) {
+ while (toDrop > 0 && iovcnt > 0) {
+ size_t toDropHere = std::min(toDrop, iov->iov_len);
+ iov->iov_base = static_cast<char*>(iov->iov_base) + toDropHere;
+ iov->iov_len -= toDropHere;
+ toDrop -= toDropHere;
+ ++iov;
+ --iovcnt;
+ }
+}
+
+void SandboxLogError(const char* message) {
+#ifdef ANDROID
+ // This uses writev internally and appears to be async signal safe.
+ __android_log_write(ANDROID_LOG_ERROR, "Sandbox", message);
+#endif
+ static const char logPrefix[] = "Sandbox: ", logSuffix[] = "\n";
+ struct iovec iovs[3] = {
+ {const_cast<char*>(logPrefix), sizeof(logPrefix) - 1},
+ {const_cast<char*>(message), strlen(message)},
+ {const_cast<char*>(logSuffix), sizeof(logSuffix) - 1},
+ };
+ while (iovs[2].iov_len > 0) {
+ ssize_t written = HANDLE_EINTR(writev(STDERR_FILENO, iovs, 3));
+ if (written <= 0) {
+ break;
+ }
+ IOVecDrop(iovs, 3, static_cast<size_t>(written));
+ }
+}
+
+ssize_t GetLibcErrorName(char* aBuf, size_t aSize, int aErr) {
+ const char* errStr;
+
+#ifdef HAVE_STRERRORNAME_NP
+ // This is the function we'd like to have, but it's a glibc
+ // extension and present only in newer versions.
+ errStr = strerrorname_np(aErr);
+#else
+ // Otherwise, handle most of the basic / common errors with a big
+ // switch statement.
+ switch (aErr) {
+
+# define HANDLE_ERR(x) \
+ case x: \
+ errStr = #x; \
+ break
+
+ // errno-base.h
+ HANDLE_ERR(EPERM);
+ HANDLE_ERR(ENOENT);
+ HANDLE_ERR(ESRCH);
+ HANDLE_ERR(EINTR);
+ HANDLE_ERR(EIO);
+ HANDLE_ERR(ENXIO);
+ HANDLE_ERR(E2BIG);
+ HANDLE_ERR(ENOEXEC);
+ HANDLE_ERR(EBADF);
+ HANDLE_ERR(ECHILD);
+ HANDLE_ERR(EAGAIN);
+ HANDLE_ERR(ENOMEM);
+ HANDLE_ERR(EACCES);
+ HANDLE_ERR(EFAULT);
+ HANDLE_ERR(ENOTBLK);
+ HANDLE_ERR(EBUSY);
+ HANDLE_ERR(EEXIST);
+ HANDLE_ERR(EXDEV);
+ HANDLE_ERR(ENODEV);
+ HANDLE_ERR(ENOTDIR);
+ HANDLE_ERR(EISDIR);
+ HANDLE_ERR(EINVAL);
+ HANDLE_ERR(ENFILE);
+ HANDLE_ERR(EMFILE);
+ HANDLE_ERR(ENOTTY);
+ HANDLE_ERR(ETXTBSY);
+ HANDLE_ERR(EFBIG);
+ HANDLE_ERR(ENOSPC);
+ HANDLE_ERR(ESPIPE);
+ HANDLE_ERR(EROFS);
+ HANDLE_ERR(EMLINK);
+ HANDLE_ERR(EPIPE);
+ HANDLE_ERR(EDOM);
+ HANDLE_ERR(ERANGE);
+
+ // selected other errors
+ HANDLE_ERR(ENAMETOOLONG);
+ HANDLE_ERR(ENOSYS);
+ HANDLE_ERR(ENOTEMPTY);
+ HANDLE_ERR(ELOOP);
+ HANDLE_ERR(ENOTSOCK);
+ HANDLE_ERR(EMSGSIZE);
+ HANDLE_ERR(ECONNRESET);
+ HANDLE_ERR(ECONNREFUSED);
+ HANDLE_ERR(EHOSTUNREACH);
+ HANDLE_ERR(ESTALE);
+
+# undef HANDLE_ERR
+
+ default:
+ errStr = nullptr;
+ }
+#endif // no strerrorname_np
+
+ if (errStr) {
+ return base::strings::SafeSNPrintf(aBuf, aSize, "%s", errStr);
+ }
+
+ return base::strings::SafeSNPrintf(aBuf, aSize, "error %d", aErr);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxLogging.h b/security/sandbox/linux/SandboxLogging.h
new file mode 100644
index 0000000000..94467ececc
--- /dev/null
+++ b/security/sandbox/linux/SandboxLogging.h
@@ -0,0 +1,81 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxLogging_h
+#define mozilla_SandboxLogging_h
+
+// This header defines the SANDBOX_LOG macro used in the Linux
+// sandboxing code. It uses Android logging on Android and writes to
+// stderr otherwise. Android logging has severity levels; currently
+// only "error" severity is exposed here, and this isn't marked when
+// writing to stderr.
+//
+// The format strings are processed by Chromium SafeSPrintf, which
+// doesn't accept size modifiers or %u because it uses C++11 variadic
+// templates to obtain the actual argument types; all decimal integer
+// formatting uses %d. See safe_sprintf.h for more details.
+
+// Build SafeSPrintf without assertions to avoid a dependency on
+// Chromium logging. This doesn't affect safety; it just means that
+// type mismatches (pointer vs. integer) always result in unexpanded
+// %-directives instead of crashing. See also the moz.build files,
+// which apply NDEBUG to the .cc file.
+#ifndef NDEBUG
+# define NDEBUG 1
+# include "base/strings/safe_sprintf.h"
+# undef NDEBUG
+#else
+# include "base/strings/safe_sprintf.h"
+#endif
+
+#include <errno.h>
+
+namespace mozilla {
+// Logs the formatted string (marked with "error" severity, if supported).
+void SandboxLogError(const char* aMessage);
+
+// Writes into aBuf the identifier for an error number (e.g., "EINVAL"
+// rather than "Invalid argument"); may fall back to "error N" (with
+// the number) for unhandled errors.
+//
+// Bounds are handled like snprintf: the return value is the length
+// the string would have (not counting the null terminator) ignoring
+// buffer size, and the string written into the buffer may be
+// truncated to fit but is always null terminated.
+ssize_t GetLibcErrorName(char* aBuf, size_t aSize, int aErr);
+} // namespace mozilla
+
+#define SANDBOX_LOG_LEN 256
+
+// Formats a log message and logs it (with "error" severity, if supported).
+//
+// Note that SafeSPrintf doesn't accept size modifiers or %u; all
+// decimal integers are %d, because it uses C++11 variadic templates
+// to use the actual argument type.
+#define SANDBOX_LOG(fmt, args...) \
+ do { \
+ char _sandboxLogBuf[SANDBOX_LOG_LEN]; \
+ ::base::strings::SafeSPrintf(_sandboxLogBuf, fmt, ##args); \
+ ::mozilla::SandboxLogError(_sandboxLogBuf); \
+ } while (0)
+
+#define SANDBOX_LOG_WITH_ERROR(errnum, fmt, args...) \
+ do { \
+ char _sandboxLogBuf[SANDBOX_LOG_LEN]; \
+ ssize_t _sandboxLogOff = \
+ ::base::strings::SafeSPrintf(_sandboxLogBuf, fmt ": ", ##args); \
+ if (static_cast<size_t>(_sandboxLogOff) < sizeof(_sandboxLogBuf)) { \
+ ::mozilla::GetLibcErrorName(_sandboxLogBuf + _sandboxLogOff, \
+ sizeof(_sandboxLogBuf) - _sandboxLogOff, \
+ errnum); \
+ } \
+ ::mozilla::SandboxLogError(_sandboxLogBuf); \
+ } while (0)
+
+#define SANDBOX_LOG_ERRNO(fmt, args...) \
+ SANDBOX_LOG_WITH_ERROR(errno, fmt, ##args)
+
+#endif // mozilla_SandboxLogging_h
diff --git a/security/sandbox/linux/SandboxOpenedFiles.cpp b/security/sandbox/linux/SandboxOpenedFiles.cpp
new file mode 100644
index 0000000000..8c26f7f206
--- /dev/null
+++ b/security/sandbox/linux/SandboxOpenedFiles.cpp
@@ -0,0 +1,77 @@
+/* -*- 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/. */
+
+#include "SandboxOpenedFiles.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "SandboxLogging.h"
+
+namespace mozilla {
+
+// The default move constructor almost works, but Atomic isn't
+// move-constructable and the fd needs some special handling.
+SandboxOpenedFile::SandboxOpenedFile(SandboxOpenedFile&& aMoved)
+ : mPath(std::move(aMoved.mPath)),
+ mMaybeFd(aMoved.TakeDesc()),
+ mDup(aMoved.mDup),
+ mExpectError(aMoved.mExpectError) {}
+
+SandboxOpenedFile::SandboxOpenedFile(const char* aPath, Dup aDup)
+ : mPath(aPath), mDup(aDup == Dup::YES), mExpectError(false) {
+ MOZ_ASSERT(aPath[0] == '/', "path should be absolute");
+
+ int fd = open(aPath, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ mExpectError = true;
+ }
+ mMaybeFd = fd;
+}
+
+SandboxOpenedFile::SandboxOpenedFile(const char* aPath, Error)
+ : mPath(aPath), mMaybeFd(-1), mDup(false), mExpectError(true) {}
+
+int SandboxOpenedFile::GetDesc() const {
+ int fd;
+ if (mDup) {
+ fd = mMaybeFd;
+ if (fd >= 0) {
+ fd = dup(fd);
+ if (fd < 0) {
+ SANDBOX_LOG_ERRNO("dup");
+ }
+ }
+ } else {
+ fd = TakeDesc();
+ }
+ if (fd < 0 && !mExpectError) {
+ SANDBOX_LOG("unexpected multiple open of file %s", Path());
+ }
+ return fd;
+}
+
+SandboxOpenedFile::~SandboxOpenedFile() {
+ int fd = TakeDesc();
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+int SandboxOpenedFiles::GetDesc(const char* aPath) const {
+ for (const auto& file : mFiles) {
+ if (strcmp(file.Path(), aPath) == 0) {
+ return file.GetDesc();
+ }
+ }
+ SANDBOX_LOG("attempt to open unexpected file %s", aPath);
+ return -1;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxOpenedFiles.h b/security/sandbox/linux/SandboxOpenedFiles.h
new file mode 100644
index 0000000000..24881b93f0
--- /dev/null
+++ b/security/sandbox/linux/SandboxOpenedFiles.h
@@ -0,0 +1,97 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxOpenedFiles_h
+#define mozilla_SandboxOpenedFiles_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Range.h"
+#include "mozilla/UniquePtr.h"
+
+#include <vector>
+#include <string>
+
+// The use of C++ standard library containers here should be safe; the
+// standard (section container.requirements.dataraces) requires that
+// using const methods/pointers not introduce data races (e.g., from
+// interior mutability or global state).
+//
+// Reentrancy isn't guaranteed, and the library could use async signal
+// unsafe mutexes for "read-only" operations, but I'm assuming that that's
+// not the case at least for simple containers like string and vector.
+
+namespace mozilla {
+
+// This class represents a file that's been pre-opened for a media
+// plugin. It can be move-constructed but not copied.
+class SandboxOpenedFile final {
+ public:
+ enum class Dup { NO, YES };
+ struct Error {};
+
+ // This constructor opens the named file and saves the descriptor.
+ // If the open fails, IsOpen() will return false and GetDesc() will
+ // quietly return -1. If aDup is Dup::YES, GetDesc() will return a
+ // dup() of the descriptor every time it's called; otherwise, the
+ // first call will return the descriptor and any further calls will
+ // log an error message and return -1.
+ explicit SandboxOpenedFile(const char* aPath, Dup aDup = Dup::NO);
+
+ // This constructor is for files which the process will try to open
+ // but we don't want to grant access: using it will always fail
+ // (GetDesc will return -1) without logging.
+ SandboxOpenedFile(const char* aPath, Error);
+
+ // Simulates opening the pre-opened file; see the constructor's
+ // comment for details. Does not set errno on error, but may modify
+ // it as a side-effect. Thread-safe and intended to be async signal safe.
+ int GetDesc() const;
+
+ const char* Path() const { return mPath.c_str(); }
+
+ bool IsOpen() const { return mMaybeFd >= 0; }
+
+ ~SandboxOpenedFile();
+
+ MOZ_IMPLICIT SandboxOpenedFile(SandboxOpenedFile&& aMoved);
+
+ private:
+ std::string mPath;
+ mutable Atomic<int> mMaybeFd;
+ bool mDup;
+ bool mExpectError;
+
+ int TakeDesc() const { return mMaybeFd.exchange(-1); }
+};
+
+// This class represents a collection of files to be used to handle
+// open() calls from the media plugin (and the dynamic loader).
+// Because the seccomp-bpf policy exists until the process exits, this
+// object must not be destroyed after the syscall filter is installed.
+class SandboxOpenedFiles {
+ public:
+ SandboxOpenedFiles() = default;
+
+ template <typename... Args>
+ void Add(Args&&... aArgs) {
+ mFiles.emplace_back(std::forward<Args>(aArgs)...);
+ }
+
+ int GetDesc(const char* aPath) const;
+
+ private:
+ std::vector<SandboxOpenedFile> mFiles;
+
+ // We could allow destroying instances of this class that aren't
+ // used with seccomp-bpf (e.g., for unit testing) by having the
+ // destructor check a flag set by the syscall policy and crash,
+ // but let's not write that code until we actually need it.
+ ~SandboxOpenedFiles() = delete;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxOpenedFiles_h
diff --git a/security/sandbox/linux/SandboxReporterClient.cpp b/security/sandbox/linux/SandboxReporterClient.cpp
new file mode 100644
index 0000000000..6359c2cb9a
--- /dev/null
+++ b/security/sandbox/linux/SandboxReporterClient.cpp
@@ -0,0 +1,88 @@
+/* -*- 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/. */
+
+#include "SandboxReporterClient.h"
+#include "SandboxLogging.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/PodOperations.h"
+#include "prenv.h"
+#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
+#ifdef ANDROID
+# include "sandbox/linux/system_headers/linux_ucontext.h"
+#else
+# include <ucontext.h>
+#endif
+
+namespace mozilla {
+
+SandboxReporterClient::SandboxReporterClient(SandboxReport::ProcType aProcType,
+ int aFd)
+ : mProcType(aProcType), mFd(aFd) {
+ // Unfortunately, there isn't a good way to check that the fd is a
+ // socket connected to the right thing without attempting some kind
+ // of in-band handshake. However, the crash reporter (which also
+ // uses a "magic number" fd) doesn't do any kind of checking either,
+ // so it's probably okay to skip it here.
+}
+
+SandboxReporterClient::SandboxReporterClient(SandboxReport::ProcType aProcType)
+ : SandboxReporterClient(aProcType, kSandboxReporterFileDesc) {
+ MOZ_RELEASE_ASSERT(PR_GetEnv("MOZ_SANDBOXED") != nullptr);
+}
+
+SandboxReport SandboxReporterClient::MakeReport(const void* aContext) {
+ SandboxReport report;
+ const auto ctx = static_cast<const ucontext_t*>(aContext);
+
+ // Zero the entire struct; some memory safety analyses care about
+ // sending uninitialized alignment padding to another process.
+ PodZero(&report);
+
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &report.mTime);
+ report.mPid = getpid();
+ report.mTid = syscall(__NR_gettid);
+ report.mProcType = mProcType;
+ report.mSyscall = SECCOMP_SYSCALL(ctx);
+ report.mArgs[0] = SECCOMP_PARM1(ctx);
+ report.mArgs[1] = SECCOMP_PARM2(ctx);
+ report.mArgs[2] = SECCOMP_PARM3(ctx);
+ report.mArgs[3] = SECCOMP_PARM4(ctx);
+ report.mArgs[4] = SECCOMP_PARM5(ctx);
+ report.mArgs[5] = SECCOMP_PARM6(ctx);
+ // Named Return Value Optimization allows the compiler to optimize
+ // out the copy here (and the one in MakeReportAndSend).
+ return report;
+}
+
+void SandboxReporterClient::SendReport(const SandboxReport& aReport) {
+ // The "common" seccomp-bpf policy allows sendmsg but not send(to),
+ // so just use sendmsg even though send would suffice for this.
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = const_cast<void*>(static_cast<const void*>(&aReport));
+ iov.iov_len = sizeof(SandboxReport);
+ PodZero(&msg);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ const auto sent = sendmsg(mFd, &msg, MSG_NOSIGNAL);
+
+ if (sent != sizeof(SandboxReport)) {
+ MOZ_DIAGNOSTIC_ASSERT(sent == -1);
+ SANDBOX_LOG_ERRNO("Failed to report rejected syscall");
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/SandboxReporterClient.h b/security/sandbox/linux/SandboxReporterClient.h
new file mode 100644
index 0000000000..335472a7f9
--- /dev/null
+++ b/security/sandbox/linux/SandboxReporterClient.h
@@ -0,0 +1,47 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxReporterClient_h
+#define mozilla_SandboxReporterClient_h
+
+#include "reporter/SandboxReporterCommon.h"
+
+namespace mozilla {
+
+// This class is instantiated in child processes in Sandbox.cpp to
+// send reports from the SIGSYS handler to the SandboxReporter
+// instance in the parent.
+class SandboxReporterClient {
+ public:
+ // Note: this does not take ownership of the file descriptor; if
+ // it's not kSandboxReporterFileDesc (e.g., for unit testing), the
+ // caller will need to close it to avoid leaks.
+ SandboxReporterClient(SandboxReport::ProcType aProcType, int aFd);
+
+ // This constructor uses the default fd (kSandboxReporterFileDesc)
+ // for a sandboxed child process.
+ explicit SandboxReporterClient(SandboxReport::ProcType aProcType);
+
+ // Constructs a report from a signal context (the ucontext_t* passed
+ // as void* to an sa_sigaction handler); uses the caller's pid and tid.
+ SandboxReport MakeReport(const void* aContext);
+
+ void SendReport(const SandboxReport& aReport);
+
+ SandboxReport MakeReportAndSend(const void* aContext) {
+ SandboxReport report = MakeReport(aContext);
+ SendReport(report);
+ return report;
+ }
+
+ private:
+ SandboxReport::ProcType mProcType;
+ int mFd;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxReporterClient_h
diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp
new file mode 100644
index 0000000000..c6f0cdf6ca
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -0,0 +1,1097 @@
+/* -*- 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/. */
+
+#include "SandboxBroker.h"
+#include "SandboxInfo.h"
+#include "SandboxLogging.h"
+#include "SandboxBrokerUtils.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#ifdef XP_LINUX
+# include <sys/prctl.h>
+#endif
+
+#include <utility>
+
+#include "GeckoProfiler.h"
+#include "SpecialSystemDirectory.h"
+#include "base/string_util.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsThreadUtils.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+namespace mozilla {
+
+// Default/fallback temporary directory
+static const nsLiteralCString tempDirPrefix("/tmp");
+
+// This constructor signals failure by setting mFileDesc and aClientFd to -1.
+SandboxBroker::SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid,
+ int& aClientFd)
+ : mChildPid(aChildPid), mPolicy(std::move(aPolicy)) {
+ int fds[2];
+ if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fds)) {
+ SANDBOX_LOG_ERRNO("SandboxBroker: socketpair failed");
+ mFileDesc = -1;
+ aClientFd = -1;
+ return;
+ }
+ mFileDesc = fds[0];
+ aClientFd = fds[1];
+
+ if (!PlatformThread::Create(0, this, &mThread)) {
+ SANDBOX_LOG_ERRNO("SandboxBroker: thread creation failed");
+ close(mFileDesc);
+ close(aClientFd);
+ mFileDesc = -1;
+ aClientFd = -1;
+ }
+#if defined(MOZ_CONTENT_TEMP_DIR)
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(tmpDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = tmpDir->GetNativePath(mContentTempPath);
+ if (NS_FAILED(rv)) {
+ mContentTempPath.Truncate();
+ }
+ }
+#endif
+}
+
+UniquePtr<SandboxBroker> SandboxBroker::Create(
+ UniquePtr<const Policy> aPolicy, int aChildPid,
+ ipc::FileDescriptor& aClientFdOut) {
+ int clientFd;
+ // Can't use MakeUnique here because the constructor is private.
+ UniquePtr<SandboxBroker> rv(
+ new SandboxBroker(std::move(aPolicy), aChildPid, clientFd));
+ if (clientFd < 0) {
+ rv = nullptr;
+ } else {
+ // FileDescriptor can be constructed from an int, but that dup()s
+ // the fd; instead, transfer ownership:
+ aClientFdOut = ipc::FileDescriptor(UniqueFileHandle(clientFd));
+ }
+ return rv;
+}
+
+SandboxBroker::~SandboxBroker() {
+ // If the constructor failed, there's nothing to be done here.
+ if (mFileDesc < 0) {
+ return;
+ }
+
+ shutdown(mFileDesc, SHUT_RD);
+ // The thread will now get EOF even if the client hasn't exited.
+ PlatformThread::Join(mThread);
+ // Now that the thread has exited, the fd will no longer be accessed.
+ close(mFileDesc);
+ // Having ensured that this object outlives the thread, this
+ // destructor can now return.
+}
+
+SandboxBroker::Policy::Policy() = default;
+SandboxBroker::Policy::~Policy() = default;
+
+SandboxBroker::Policy::Policy(const Policy& aOther)
+ : mMap(aOther.mMap.Clone()) {}
+
+// Chromium
+// sandbox/linux/syscall_broker/broker_file_permission.cc
+// Async signal safe
+bool SandboxBroker::Policy::ValidatePath(const char* path) const {
+ if (!path) return false;
+
+ const size_t len = strlen(path);
+ // No empty paths
+ if (len == 0) return false;
+ // Paths must be absolute and not relative
+ if (path[0] != '/') return false;
+ // No trailing / (but "/" is valid)
+ if (len > 1 && path[len - 1] == '/') return false;
+ // No trailing /.
+ if (len >= 2 && path[len - 2] == '/' && path[len - 1] == '.') return false;
+ // No trailing /..
+ if (len >= 3 && path[len - 3] == '/' && path[len - 2] == '.' &&
+ path[len - 1] == '.')
+ return false;
+ // No /../ anywhere
+ for (size_t i = 0; i < len; i++) {
+ if (path[i] == '/' && (len - i) > 3) {
+ if (path[i + 1] == '.' && path[i + 2] == '.' && path[i + 3] == '/') {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void SandboxBroker::Policy::AddPath(int aPerms, const char* aPath,
+ AddCondition aCond) {
+ nsDependentCString path(aPath);
+ MOZ_ASSERT(path.Length() <= kMaxPathLen);
+ if (aCond == AddIfExistsNow) {
+ struct stat statBuf;
+ if (lstat(aPath, &statBuf) != 0) {
+ return;
+ }
+ }
+ auto& perms = mMap.LookupOrInsert(path, MAY_ACCESS);
+ MOZ_ASSERT(perms & MAY_ACCESS);
+
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("policy for %s: %d -> %d", aPath, perms, perms | aPerms);
+ }
+ perms |= aPerms;
+}
+
+void SandboxBroker::Policy::AddTree(int aPerms, const char* aPath) {
+ struct stat statBuf;
+
+ if (stat(aPath, &statBuf) != 0) {
+ return;
+ }
+ if (!S_ISDIR(statBuf.st_mode)) {
+ AddPath(aPerms, aPath, AddAlways);
+ } else {
+ DIR* dirp = opendir(aPath);
+ if (!dirp) {
+ return;
+ }
+ while (struct dirent* de = readdir(dirp)) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) {
+ continue;
+ }
+ // Note: could optimize the string handling.
+ nsAutoCString subPath;
+ subPath.Assign(aPath);
+ subPath.Append('/');
+ subPath.Append(de->d_name);
+ AddTree(aPerms, subPath.get());
+ }
+ closedir(dirp);
+ }
+}
+
+void SandboxBroker::Policy::AddDir(int aPerms, const char* aPath) {
+ struct stat statBuf;
+
+ if (stat(aPath, &statBuf) != 0) {
+ return;
+ }
+
+ if (!S_ISDIR(statBuf.st_mode)) {
+ return;
+ }
+
+ Policy::AddDirInternal(aPerms, aPath);
+}
+
+void SandboxBroker::Policy::AddFutureDir(int aPerms, const char* aPath) {
+ Policy::AddDirInternal(aPerms, aPath);
+}
+
+void SandboxBroker::Policy::AddDirInternal(int aPerms, const char* aPath) {
+ // Add a Prefix permission on things inside the dir.
+ nsDependentCString path(aPath);
+ MOZ_ASSERT(path.Length() <= kMaxPathLen - 1);
+ // Enforce trailing / on aPath
+ if (path.Last() != '/') {
+ path.Append('/');
+ }
+ Policy::AddPrefixInternal(aPerms, path);
+
+ // Add a path permission on the dir itself so it can
+ // be opened. We're guaranteed to have a trailing / now,
+ // so just cut that.
+ path.Truncate(path.Length() - 1);
+ if (!path.IsEmpty()) {
+ Policy::AddPath(aPerms, path.get(), AddAlways);
+ }
+}
+
+void SandboxBroker::Policy::AddPrefix(int aPerms, const char* aPath) {
+ Policy::AddPrefixInternal(aPerms, nsDependentCString(aPath));
+}
+
+void SandboxBroker::Policy::AddPrefixInternal(int aPerms,
+ const nsACString& aPath) {
+ auto& perms = mMap.LookupOrInsert(aPath, MAY_ACCESS);
+ MOZ_ASSERT(perms & MAY_ACCESS);
+
+ int newPerms = perms | aPerms | RECURSIVE;
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("policy for %s: %d -> %d", PromiseFlatCString(aPath).get(),
+ perms, newPerms);
+ }
+ perms = newPerms;
+}
+
+void SandboxBroker::Policy::AddFilePrefix(int aPerms, const char* aDir,
+ const char* aPrefix) {
+ size_t prefixLen = strlen(aPrefix);
+ DIR* dirp = opendir(aDir);
+ struct dirent* de;
+ if (!dirp) {
+ return;
+ }
+ while ((de = readdir(dirp))) {
+ if (strcmp(de->d_name, ".") != 0 && strcmp(de->d_name, "..") != 0 &&
+ strncmp(de->d_name, aPrefix, prefixLen) == 0) {
+ nsAutoCString subPath;
+ subPath.Assign(aDir);
+ subPath.Append('/');
+ subPath.Append(de->d_name);
+ AddPath(aPerms, subPath.get(), AddAlways);
+ }
+ }
+ closedir(dirp);
+}
+
+void SandboxBroker::Policy::AddDynamic(int aPerms, const char* aPath) {
+ struct stat statBuf;
+ bool exists = (stat(aPath, &statBuf) == 0);
+
+ if (!exists) {
+ AddPrefix(aPerms, aPath);
+ } else {
+ size_t len = strlen(aPath);
+ if (!len) return;
+ if (aPath[len - 1] == '/') {
+ AddDir(aPerms, aPath);
+ } else {
+ AddPath(aPerms, aPath);
+ }
+ }
+}
+
+void SandboxBroker::Policy::AddAncestors(const char* aPath, int aPerms) {
+ nsAutoCString path(aPath);
+
+ while (true) {
+ const auto lastSlash = path.RFindCharInSet("/");
+ if (lastSlash <= 0) {
+ MOZ_ASSERT(lastSlash == 0);
+ return;
+ }
+ path.Truncate(lastSlash);
+ AddPath(aPerms, path.get());
+ }
+}
+
+void SandboxBroker::Policy::FixRecursivePermissions() {
+ // This builds an entirely new hashtable in order to avoid iterator
+ // invalidation problems.
+ PathPermissionMap oldMap;
+ mMap.SwapElements(oldMap);
+
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("fixing recursive policy entries");
+ }
+
+ for (const auto& entry : oldMap) {
+ const nsACString& path = entry.GetKey();
+ const int& localPerms = entry.GetData();
+ int inheritedPerms = 0;
+
+ nsAutoCString ancestor(path);
+ // This is slightly different from the loop in AddAncestors: it
+ // leaves the trailing slashes attached so they'll match AddDir
+ // entries.
+ while (true) {
+ // Last() release-asserts that the string is not empty. We
+ // should never have empty keys in the map, and the Truncate()
+ // below will always give us a non-empty string.
+ if (ancestor.Last() == '/') {
+ ancestor.Truncate(ancestor.Length() - 1);
+ }
+ const auto lastSlash = ancestor.RFindCharInSet("/");
+ if (lastSlash < 0) {
+ MOZ_ASSERT(ancestor.IsEmpty());
+ break;
+ }
+ ancestor.Truncate(lastSlash + 1);
+ const int ancestorPerms = oldMap.Get(ancestor);
+ if (ancestorPerms & RECURSIVE) {
+ // if a child is set with FORCE_DENY, do not compute inheritedPerms
+ if ((localPerms & FORCE_DENY) == FORCE_DENY) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("skip inheritence policy for %s: %d",
+ PromiseFlatCString(path).get(), localPerms);
+ }
+ } else {
+ inheritedPerms |= ancestorPerms & ~RECURSIVE;
+ }
+ }
+ }
+
+ const int newPerms = localPerms | inheritedPerms;
+ if ((newPerms & ~RECURSIVE) == inheritedPerms) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("removing redundant %s: %d -> %d",
+ PromiseFlatCString(path).get(), localPerms, newPerms);
+ }
+ // Skip adding this entry to the new map.
+ continue;
+ }
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("new policy for %s: %d -> %d", PromiseFlatCString(path).get(),
+ localPerms, newPerms);
+ }
+ mMap.InsertOrUpdate(path, newPerms);
+ }
+}
+
+int SandboxBroker::Policy::Lookup(const nsACString& aPath) const {
+ // Early exit for paths explicitly found in the
+ // whitelist.
+ // This means they will not gain extra permissions
+ // from recursive paths.
+ int perms = mMap.Get(aPath);
+ if (perms) {
+ return perms;
+ }
+
+ // Not a legally constructed path
+ if (!ValidatePath(PromiseFlatCString(aPath).get())) return 0;
+
+ // Now it's either an illegal access, or a recursive
+ // directory permission. We'll have to check the entire
+ // whitelist for the best match (slower).
+ int allPerms = 0;
+ for (const auto& entry : mMap) {
+ const nsACString& whiteListPath = entry.GetKey();
+ const int& perms = entry.GetData();
+
+ if (!(perms & RECURSIVE)) continue;
+
+ // passed part starts with something on the whitelist
+ if (StringBeginsWith(aPath, whiteListPath)) {
+ allPerms |= perms;
+ }
+ }
+
+ // Strip away the RECURSIVE flag as it doesn't
+ // necessarily apply to aPath.
+ return allPerms & ~RECURSIVE;
+}
+
+static bool AllowOperation(int aReqFlags, int aPerms) {
+ int needed = 0;
+ if (aReqFlags & R_OK) {
+ needed |= SandboxBroker::MAY_READ;
+ }
+ if (aReqFlags & W_OK) {
+ needed |= SandboxBroker::MAY_WRITE;
+ }
+ // We don't really allow executing anything,
+ // so in true unix tradition we hijack this
+ // for directory access (creation).
+ if (aReqFlags & X_OK) {
+ needed |= SandboxBroker::MAY_CREATE;
+ }
+ return (aPerms & needed) == needed;
+}
+
+static bool AllowAccess(int aReqFlags, int aPerms) {
+ if (aReqFlags & ~(R_OK | W_OK | X_OK | F_OK)) {
+ return false;
+ }
+ int needed = 0;
+ if (aReqFlags & R_OK) {
+ needed |= SandboxBroker::MAY_READ;
+ }
+ if (aReqFlags & W_OK) {
+ needed |= SandboxBroker::MAY_WRITE;
+ }
+ return (aPerms & needed) == needed;
+}
+
+// These flags are added to all opens to prevent possible side-effects
+// on this process. These shouldn't be relevant to the child process
+// in any case due to the sandboxing restrictions on it. (See also
+// the use of MSG_CMSG_CLOEXEC in SandboxBrokerCommon.cpp).
+static const int kRequiredOpenFlags = O_CLOEXEC | O_NOCTTY;
+
+// Linux originally assigned a flag bit to O_SYNC but implemented the
+// semantics standardized as O_DSYNC; later, that bit was renamed and
+// a new bit was assigned to the full O_SYNC, and O_SYNC was redefined
+// to be both bits. As a result, this #define is needed to compensate
+// for outdated kernel headers like Android's.
+#define O_SYNC_NEW 04010000
+static const int kAllowedOpenFlags =
+ O_APPEND | O_DIRECT | O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME |
+ O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY | O_SYNC_NEW | O_TRUNC |
+ O_CLOEXEC | O_CREAT;
+#undef O_SYNC_NEW
+
+static bool AllowOpen(int aReqFlags, int aPerms) {
+ if (aReqFlags & ~O_ACCMODE & ~kAllowedOpenFlags) {
+ return false;
+ }
+ int needed;
+ switch (aReqFlags & O_ACCMODE) {
+ case O_RDONLY:
+ needed = SandboxBroker::MAY_READ;
+ break;
+ case O_WRONLY:
+ needed = SandboxBroker::MAY_WRITE;
+ break;
+ case O_RDWR:
+ needed = SandboxBroker::MAY_READ | SandboxBroker::MAY_WRITE;
+ break;
+ default:
+ return false;
+ }
+ if (aReqFlags & O_CREAT) {
+ needed |= SandboxBroker::MAY_CREATE;
+ }
+ // Linux allows O_TRUNC even with O_RDONLY
+ if (aReqFlags & O_TRUNC) {
+ needed |= SandboxBroker::MAY_WRITE;
+ }
+ return (aPerms & needed) == needed;
+}
+
+static int DoStat(const char* aPath, statstruct* aBuff, int aFlags) {
+ if (aFlags & O_NOFOLLOW) {
+ return lstatsyscall(aPath, aBuff);
+ }
+ return statsyscall(aPath, aBuff);
+}
+
+static int DoLink(const char* aPath, const char* aPath2,
+ SandboxBrokerCommon::Operation aOper) {
+ if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_LINK) {
+ return link(aPath, aPath2);
+ }
+ if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) {
+ return symlink(aPath, aPath2);
+ }
+ MOZ_CRASH("SandboxBroker: Unknown link operation");
+}
+
+static int DoConnect(const char* aPath, size_t aLen, int aType,
+ bool aIsAbstract) {
+ // Deny SOCK_DGRAM for the same reason it's denied for socketpair.
+ if (aType != SOCK_STREAM && aType != SOCK_SEQPACKET) {
+ errno = EACCES;
+ return -1;
+ }
+ // Ensure that the address is a pathname. (An empty string
+ // resulting from an abstract address probably shouldn't have made
+ // it past the policy check, but check explicitly just in case.)
+ if (aPath[0] == '\0') {
+ errno = ENETUNREACH;
+ return -1;
+ }
+
+ // Try to copy the name into a normal-sized sockaddr_un, with
+ // null-termination. Specifically, from man page:
+ //
+ // When the address of an abstract socket is returned, the returned addrlen is
+ // greater than sizeof(sa_family_t) (i.e., greater than 2), and the name of
+ // the socket is contained in the first (addrlen - sizeof(sa_family_t)) bytes
+ // of sun_path.
+ //
+ // As mentionned in `SandboxBrokerClient::Connect()`, `DoCall` expects a
+ // null-terminated string while abstract socket are not. So we receive a copy
+ // here and we have to put things back correctly as a real abstract socket to
+ // perform the brokered `connect()` call.
+ struct sockaddr_un sun;
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ char* sunPath = sun.sun_path;
+ size_t sunLen = sizeof(sun.sun_path);
+ size_t addrLen = sizeof(sun);
+ if (aIsAbstract) {
+ *sunPath++ = '\0';
+ sunLen--;
+ addrLen = offsetof(struct sockaddr_un, sun_path) + aLen + 1;
+ }
+ if (aLen + 1 > sunLen) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ memcpy(sunPath, aPath, aLen);
+
+ // Finally, the actual socket connection.
+ const int fd = socket(AF_UNIX, aType | SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ return -1;
+ }
+ if (connect(fd, reinterpret_cast<struct sockaddr*>(&sun), addrLen) < 0) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+size_t SandboxBroker::RealPath(char* aPath, size_t aBufSize, size_t aPathLen) {
+ char* result = realpath(aPath, nullptr);
+ if (result != nullptr) {
+ base::strlcpy(aPath, result, aBufSize);
+ free(result);
+ // Size changed, but guaranteed to be 0 terminated
+ aPathLen = strlen(aPath);
+ }
+ return aPathLen;
+}
+
+size_t SandboxBroker::ConvertRelativePath(char* aPath, size_t aBufSize,
+ size_t aPathLen) {
+ if (strstr(aPath, "..") != nullptr) {
+ return RealPath(aPath, aBufSize, aPathLen);
+ }
+ return aPathLen;
+}
+
+#if defined(MOZ_CONTENT_TEMP_DIR)
+size_t SandboxBroker::RemapTempDirs(char* aPath, size_t aBufSize,
+ size_t aPathLen) {
+ nsAutoCString path(aPath);
+
+ size_t prefixLen = 0;
+ if (!mTempPath.IsEmpty() && StringBeginsWith(path, mTempPath)) {
+ prefixLen = mTempPath.Length();
+ } else if (StringBeginsWith(path, tempDirPrefix)) {
+ prefixLen = tempDirPrefix.Length();
+ }
+
+ if (prefixLen) {
+ const nsDependentCSubstring cutPath =
+ Substring(path, prefixLen, path.Length() - prefixLen);
+
+ // Only now try to get the content process temp dir
+ if (!mContentTempPath.IsEmpty()) {
+ nsAutoCString tmpPath;
+ tmpPath.Assign(mContentTempPath);
+ tmpPath.Append(cutPath);
+ base::strlcpy(aPath, tmpPath.get(), aBufSize);
+ return strlen(aPath);
+ }
+ }
+
+ return aPathLen;
+}
+#endif
+
+nsCString SandboxBroker::ReverseSymlinks(const nsACString& aPath) {
+ // Revert any symlinks we previously resolved.
+ int32_t cutLength = aPath.Length();
+ nsCString cutPath(Substring(aPath, 0, cutLength));
+
+ for (;;) {
+ nsCString orig;
+ bool found = mSymlinkMap.Get(cutPath, &orig);
+ if (found) {
+ orig.Append(Substring(aPath, cutLength, aPath.Length() - cutLength));
+ return orig;
+ }
+ // Not found? Remove a path component and try again.
+ int32_t pos = cutPath.RFindChar('/');
+ if (pos == kNotFound || pos <= 0) {
+ // will be empty
+ return orig;
+ } else {
+ // Cut until just before the /
+ cutLength = pos;
+ cutPath.Assign(Substring(cutPath, 0, cutLength));
+ }
+ }
+}
+
+int SandboxBroker::SymlinkPermissions(const char* aPath,
+ const size_t aPathLen) {
+ // Work on a temporary copy, so we can reverse it.
+ // Because we bail on a writable dir, SymlinkPath
+ // might not restore the callers' path exactly.
+ char pathBufSymlink[kMaxPathLen + 1];
+ strcpy(pathBufSymlink, aPath);
+
+ nsCString orig =
+ ReverseSymlinks(nsDependentCString(pathBufSymlink, aPathLen));
+ if (!orig.IsEmpty()) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Reversing %s -> %s", aPath, orig.get());
+ }
+ base::strlcpy(pathBufSymlink, orig.get(), sizeof(pathBufSymlink));
+ }
+
+ int perms = 0;
+ // Resolve relative paths, propagate permissions and
+ // fail if a symlink is in a writable path. The output is in perms.
+ char* result =
+ SandboxBroker::SymlinkPath(mPolicy.get(), pathBufSymlink, NULL, &perms);
+ if (result != NULL) {
+ free(result);
+ // We finished the translation, so we have a usable return in "perms".
+ return perms;
+ } else {
+ // Empty path means we got a writable dir in the chain or tried
+ // to back out of a link target.
+ return 0;
+ }
+}
+
+void SandboxBroker::ThreadMain(void) {
+ // Create a nsThread wrapper for the current platform thread, and register it
+ // with the thread manager.
+ (void)NS_GetCurrentThread();
+
+ char threadName[16];
+ SprintfLiteral(threadName, "FSBroker%d", mChildPid);
+ PlatformThread::SetName(threadName);
+
+ AUTO_PROFILER_REGISTER_THREAD(threadName);
+
+ // Permissive mode can only be enabled through an environment variable,
+ // therefore it is sufficient to fetch the value once
+ // before the main thread loop starts
+ bool permissive = SandboxInfo::Get().Test(SandboxInfo::kPermissive);
+
+#if defined(MOZ_CONTENT_TEMP_DIR)
+ // Find the current temporary directory
+ nsCOMPtr<nsIFile> tmpDir;
+ nsresult rv =
+ GetSpecialSystemDirectory(OS_TemporaryDirectory, getter_AddRefs(tmpDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = tmpDir->GetNativePath(mTempPath);
+ if (NS_SUCCEEDED(rv)) {
+ // Make sure there's no terminating /
+ if (mTempPath.Last() == '/') {
+ mTempPath.Truncate(mTempPath.Length() - 1);
+ }
+ }
+ }
+ // If we can't find it, we aren't bothered much: we will
+ // always try /tmp anyway in the substitution code
+ if (NS_FAILED(rv) || mTempPath.IsEmpty()) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Tempdir: /tmp");
+ }
+ } else {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Tempdir: %s", mTempPath.get());
+ }
+ // If it's /tmp, clear it here so we don't compare against
+ // it twice. Just let the fallback code do the work.
+ if (mTempPath.Equals(tempDirPrefix)) {
+ mTempPath.Truncate();
+ }
+ }
+#endif
+
+ while (true) {
+ struct iovec ios[2];
+ // We will receive the path strings in 1 buffer and split them back up.
+ char recvBuf[2 * (kMaxPathLen + 1)];
+ char pathBuf[kMaxPathLen + 1];
+ char pathBuf2[kMaxPathLen + 1];
+ size_t pathLen = 0;
+ size_t pathLen2 = 0;
+ char respBuf[kMaxPathLen + 1]; // Also serves as struct stat
+ Request req;
+ Response resp;
+ int respfd;
+
+ // Make sure stat responses fit in the response buffer
+ MOZ_ASSERT((kMaxPathLen + 1) > sizeof(struct stat));
+
+ // This makes our string handling below a bit less error prone.
+ memset(recvBuf, 0, sizeof(recvBuf));
+
+ ios[0].iov_base = &req;
+ ios[0].iov_len = sizeof(req);
+ ios[1].iov_base = recvBuf;
+ ios[1].iov_len = sizeof(recvBuf);
+
+ const ssize_t recvd = RecvWithFd(mFileDesc, ios, 2, &respfd);
+ if (recvd == 0) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("EOF from pid %d", mChildPid);
+ }
+ break;
+ }
+ // It could be possible to continue after errors and short reads,
+ // at least in some cases, but protocol violation indicates a
+ // hostile client, so terminate the broker instead.
+ if (recvd < 0) {
+ SANDBOX_LOG_ERRNO("bad read from pid %d", mChildPid);
+ shutdown(mFileDesc, SHUT_RD);
+ break;
+ }
+ if (recvd < static_cast<ssize_t>(sizeof(req))) {
+ SANDBOX_LOG("bad read from pid %d (%d < %d)", mChildPid, recvd,
+ sizeof(req));
+ shutdown(mFileDesc, SHUT_RD);
+ break;
+ }
+ if (respfd == -1) {
+ SANDBOX_LOG("no response fd from pid %d", mChildPid);
+ shutdown(mFileDesc, SHUT_RD);
+ break;
+ }
+
+ // Initialize the response with the default failure.
+ memset(&resp, 0, sizeof(resp));
+ memset(&respBuf, 0, sizeof(respBuf));
+ resp.mError = -EACCES;
+ ios[0].iov_base = &resp;
+ ios[0].iov_len = sizeof(resp);
+ ios[1].iov_base = nullptr;
+ ios[1].iov_len = 0;
+ int openedFd = -1;
+
+ // Clear permissions
+ int perms;
+
+ // Find end of first string, make sure the buffer is still
+ // 0 terminated.
+ size_t recvBufLen = static_cast<size_t>(recvd) - sizeof(req);
+ if (recvBufLen > 0 && recvBuf[recvBufLen - 1] != 0) {
+ SANDBOX_LOG("corrupted path buffer from pid %d", mChildPid);
+ shutdown(mFileDesc, SHUT_RD);
+ break;
+ }
+
+ // First path should fit in maximum path length buffer.
+ size_t first_len = strlen(recvBuf);
+ if (first_len <= kMaxPathLen) {
+ strcpy(pathBuf, recvBuf);
+ // Skip right over the terminating 0, and try to copy in the
+ // second path, if any. If there's no path, this will hit a
+ // 0 immediately (we nulled the buffer before receiving).
+ // We do not assume the second path is 0-terminated, this is
+ // enforced below.
+ strncpy(pathBuf2, recvBuf + first_len + 1, kMaxPathLen);
+
+ // First string is guaranteed to be 0-terminated.
+ pathLen = first_len;
+
+ // Look up the first pathname but first translate relative paths.
+ pathLen = ConvertRelativePath(pathBuf, sizeof(pathBuf), pathLen);
+ perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
+
+ // We don't have permissions on the requested dir.
+#if defined(MOZ_CONTENT_TEMP_DIR)
+ if (!perms) {
+ // Was it a tempdir that we can remap?
+ pathLen = RemapTempDirs(pathBuf, sizeof(pathBuf), pathLen);
+ perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
+ }
+#endif
+ if (!perms) {
+ // Did we arrive from a symlink in a path that is not writable?
+ // Then try to figure out the original path and see if that is
+ // readable. Work on the original path, this reverses
+ // ConvertRelative above.
+ int symlinkPerms = SymlinkPermissions(recvBuf, first_len);
+ if (symlinkPerms > 0) {
+ perms = symlinkPerms;
+ }
+ }
+ if (!perms) {
+ // Now try the opposite case: translate symlinks to their
+ // actual destination file. Firefox always resolves symlinks,
+ // and in most cases we have whitelisted fixed paths that
+ // libraries will rely on and try to open. So this codepath
+ // is mostly useful for Mesa which had its kernel interface
+ // moved around.
+ pathLen = RealPath(pathBuf, sizeof(pathBuf), pathLen);
+ perms = mPolicy->Lookup(nsDependentCString(pathBuf, pathLen));
+ }
+
+ // Same for the second path.
+ pathLen2 = strnlen(pathBuf2, kMaxPathLen);
+ if (pathLen2 > 0) {
+ // Force 0 termination.
+ pathBuf2[pathLen2] = '\0';
+ pathLen2 = ConvertRelativePath(pathBuf2, sizeof(pathBuf2), pathLen2);
+ int perms2 = mPolicy->Lookup(nsDependentCString(pathBuf2, pathLen2));
+
+ // Take the intersection of the permissions for both paths.
+ perms &= perms2;
+ }
+ } else {
+ // Failed to receive intelligible paths.
+ perms = 0;
+ }
+
+ // And now perform the operation if allowed.
+ if (perms & CRASH_INSTEAD) {
+ // This is somewhat nonmodular, but it works.
+ resp.mError = -ENOSYS;
+ } else if ((perms & FORCE_DENY) == FORCE_DENY) {
+ resp.mError = -EACCES;
+ } else if (permissive || perms & MAY_ACCESS) {
+ // If the operation was only allowed because of permissive mode, log it.
+ if (permissive && !(perms & MAY_ACCESS)) {
+ AuditPermissive(req.mOp, req.mFlags, perms, pathBuf);
+ }
+
+ switch (req.mOp) {
+ case SANDBOX_FILE_OPEN:
+ if (permissive || AllowOpen(req.mFlags, perms)) {
+ // Permissions for O_CREAT hardwired to 0600; if that's
+ // ever a problem we can change the protocol (but really we
+ // should be trying to remove uses of MAY_CREATE, not add
+ // new ones).
+ openedFd = open(pathBuf, req.mFlags | kRequiredOpenFlags, 0600);
+ if (openedFd >= 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_ACCESS:
+ if (permissive || AllowAccess(req.mFlags, perms)) {
+ if (access(pathBuf, req.mFlags) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_STAT:
+ MOZ_ASSERT(req.mBufSize == sizeof(statstruct));
+ if (DoStat(pathBuf, (statstruct*)&respBuf, req.mFlags) == 0) {
+ resp.mError = 0;
+ ios[1].iov_base = &respBuf;
+ ios[1].iov_len = sizeof(statstruct);
+ } else {
+ resp.mError = -errno;
+ }
+ break;
+
+ case SANDBOX_FILE_CHMOD:
+ if (permissive || AllowOperation(W_OK, perms)) {
+ if (chmod(pathBuf, req.mFlags) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_LINK:
+ case SANDBOX_FILE_SYMLINK:
+ if (permissive || AllowOperation(W_OK | X_OK, perms)) {
+ if (DoLink(pathBuf, pathBuf2, req.mOp) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_RENAME:
+ if (permissive || AllowOperation(W_OK | X_OK, perms)) {
+ if (rename(pathBuf, pathBuf2) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_MKDIR:
+ if (permissive || AllowOperation(W_OK | X_OK, perms)) {
+ if (mkdir(pathBuf, req.mFlags) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ struct stat sb;
+ // This doesn't need an additional policy check because
+ // MAY_ACCESS is required to even enter this switch statement.
+ if (lstat(pathBuf, &sb) == 0) {
+ resp.mError = -EEXIST;
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ }
+ break;
+
+ case SANDBOX_FILE_UNLINK:
+ if (permissive || AllowOperation(W_OK | X_OK, perms)) {
+ if (unlink(pathBuf) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_RMDIR:
+ if (permissive || AllowOperation(W_OK | X_OK, perms)) {
+ if (rmdir(pathBuf) == 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_FILE_READLINK:
+ if (permissive || AllowOperation(R_OK, perms)) {
+ ssize_t respSize =
+ readlink(pathBuf, (char*)&respBuf, sizeof(respBuf));
+ if (respSize >= 0) {
+ if (respSize > 0) {
+ // Record the mapping so we can invert the file to the original
+ // symlink.
+ nsDependentCString orig(pathBuf, pathLen);
+ nsDependentCString xlat(respBuf, respSize);
+ if (!orig.Equals(xlat) && xlat[0] == '/') {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Recording mapping %s -> %s", xlat.get(),
+ orig.get());
+ }
+ mSymlinkMap.InsertOrUpdate(xlat, orig);
+ }
+ // Make sure we can invert a fully resolved mapping too. If our
+ // caller is realpath, and there's a relative path involved, the
+ // client side will try to open this one.
+ char* resolvedBuf = realpath(pathBuf, nullptr);
+ if (resolvedBuf) {
+ nsDependentCString resolvedXlat(resolvedBuf);
+ if (!orig.Equals(resolvedXlat) &&
+ !xlat.Equals(resolvedXlat)) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Recording mapping %s -> %s",
+ resolvedXlat.get(), orig.get());
+ }
+ mSymlinkMap.InsertOrUpdate(resolvedXlat, orig);
+ }
+ free(resolvedBuf);
+ }
+ }
+ resp.mError = respSize;
+ ios[1].iov_base = &respBuf;
+ ios[1].iov_len = respSize;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+
+ case SANDBOX_SOCKET_CONNECT:
+ case SANDBOX_SOCKET_CONNECT_ABSTRACT:
+ if (permissive || (perms & MAY_CONNECT) != 0) {
+ openedFd = DoConnect(pathBuf, pathLen, req.mFlags,
+ req.mOp == SANDBOX_SOCKET_CONNECT_ABSTRACT);
+ if (openedFd >= 0) {
+ resp.mError = 0;
+ } else {
+ resp.mError = -errno;
+ }
+ } else {
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+ break;
+ }
+ } else {
+ MOZ_ASSERT(perms == 0);
+ AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
+ }
+
+ const size_t numIO = ios[1].iov_len > 0 ? 2 : 1;
+ const ssize_t sent = SendWithFd(respfd, ios, numIO, openedFd);
+ if (sent < 0) {
+ SANDBOX_LOG_ERRNO("failed to send broker response to pid %d", mChildPid);
+ } else {
+ MOZ_ASSERT(static_cast<size_t>(sent) == ios[0].iov_len + ios[1].iov_len);
+ }
+
+ // Work around Linux kernel bug: recvmsg checks for pending data
+ // and then checks for EOF or shutdown, without synchronization;
+ // if the sendmsg and last close occur between those points, it
+ // will see no pending data (before) and a closed socket (after),
+ // and incorrectly return EOF even though there is a message to be
+ // read. To avoid this, we send an extra message with a reference
+ // to respfd, so the last close can't happen until after the real
+ // response is read.
+ //
+ // See also: https://bugzil.la/1243108#c48
+ const struct Response fakeResp = {-4095};
+ const struct iovec fakeIO = {const_cast<Response*>(&fakeResp),
+ sizeof(fakeResp)};
+ // If the client has already read the real response and closed its
+ // socket then this will fail, but that's fine.
+ if (SendWithFd(respfd, &fakeIO, 1, respfd) < 0) {
+ MOZ_ASSERT(errno == EPIPE || errno == ECONNREFUSED || errno == ENOTCONN);
+ }
+
+ close(respfd);
+
+ if (openedFd >= 0) {
+ close(openedFd);
+ }
+ }
+}
+
+void SandboxBroker::AuditPermissive(int aOp, int aFlags, int aPerms,
+ const char* aPath) {
+ MOZ_RELEASE_ASSERT(SandboxInfo::Get().Test(SandboxInfo::kPermissive));
+
+ struct stat statBuf;
+
+ if (lstat(aPath, &statBuf) == 0) {
+ // Path exists, set errno to 0 to indicate "success".
+ errno = 0;
+ }
+
+ SANDBOX_LOG_ERRNO(
+ "SandboxBroker: would have denied op=%s rflags=%o perms=%d path=%s for "
+ "pid=%d permissive=1; real status",
+ OperationDescription[aOp], aFlags, aPerms, aPath, mChildPid);
+}
+
+void SandboxBroker::AuditDenial(int aOp, int aFlags, int aPerms,
+ const char* aPath) {
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG(
+ "SandboxBroker: denied op=%s rflags=%o perms=%d path=%s for pid=%d",
+ OperationDescription[aOp], aFlags, aPerms, aPath, mChildPid);
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/broker/SandboxBroker.h b/security/sandbox/linux/broker/SandboxBroker.h
new file mode 100644
index 0000000000..ad3d4b7d49
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBroker.h
@@ -0,0 +1,180 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxBroker_h
+#define mozilla_SandboxBroker_h
+
+#include "mozilla/SandboxBrokerCommon.h"
+
+#include "base/platform_thread.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTHashMap.h"
+#include "nsHashKeys.h"
+#include "nsString.h"
+
+namespace mozilla {
+
+namespace ipc {
+class FileDescriptor;
+}
+
+// This class implements a broker for filesystem operations requested
+// by a sandboxed child process -- opening files and accessing their
+// metadata. (This is necessary in order to restrict access by path;
+// seccomp-bpf can filter only on argument register values, not
+// parameters passed in memory like pathnames.)
+//
+// The broker currently runs on a thread in the parent process (with
+// effective uid changed on B2G), which is for memory efficiency
+// (compared to forking a process) and simplicity (compared to having
+// a separate executable and serializing/deserializing the policy).
+//
+// See also ../SandboxBrokerClient.h for the corresponding client.
+
+class SandboxBroker final : private SandboxBrokerCommon,
+ public PlatformThread::Delegate {
+ public:
+ enum Perms {
+ MAY_ACCESS = 1 << 0,
+ MAY_READ = 1 << 1,
+ MAY_WRITE = 1 << 2,
+ MAY_CREATE = 1 << 3,
+ // This flag is for testing policy changes -- when the client is
+ // used with the seccomp-bpf integration, an access to this file
+ // will invoke a crash dump with the context of the syscall.
+ // (This overrides all other flags.)
+ CRASH_INSTEAD = 1 << 4,
+ // Applies to everything below this path, including subdirs created
+ // at runtime
+ RECURSIVE = 1 << 5,
+ // Allow Unix-domain socket connections to a path
+ MAY_CONNECT = 1 << 6,
+ // This flag is for adding a deny rule, so that we can e.g., allow read
+ // access to ~/.config/ but still deny access to ~/.config/mozilla/.
+ // It will bypass other checks.
+ FORCE_DENY = 1 << 7,
+ };
+ // Bitwise operations on enum values return ints, so just use int in
+ // the hash table type (and below) to avoid cluttering code with casts.
+ typedef nsTHashMap<nsCStringHashKey, int> PathPermissionMap;
+
+ class Policy {
+ PathPermissionMap mMap;
+
+ public:
+ Policy();
+ Policy(const Policy& aOther);
+ ~Policy();
+
+ // Add permissions from AddDir/AddDynamic rules to any rules that
+ // exist for their descendents, and remove any descendent rules
+ // made redundant by this process.
+ //
+ // Call this after adding rules and before using the policy to
+ // prevent the descendent rules from shadowing the ancestor rules
+ // and removing permissions that we expect the file to have.
+ void FixRecursivePermissions();
+
+ enum AddCondition {
+ AddIfExistsNow,
+ AddAlways,
+ };
+ // Typically, files that don't exist at policy creation time don't
+ // need to be whitelisted, but this allows adding entries for
+ // them if they'll exist later. See also the overload below.
+ void AddPath(int aPerms, const char* aPath, AddCondition aCond);
+ // This adds all regular files (not directories) in the tree
+ // rooted at the given path.
+ void AddTree(int aPerms, const char* aPath);
+ // A directory, and all files and directories under it, even those
+ // added after creation (the dir itself must exist).
+ void AddDir(int aPerms, const char* aPath);
+ // A directory, and all files and directories under it, even those
+ // added after creation (the dir itself may not exist).
+ void AddFutureDir(int aPerms, const char* aPath);
+ // All files in a directory with a given prefix; useful for devices.
+ void AddFilePrefix(int aPerms, const char* aDir, const char* aPrefix);
+ // Everything starting with the given path, even those files/dirs
+ // added after creation. The file or directory may or may not exist.
+ void AddPrefix(int aPerms, const char* aPath);
+ // Adds a file or dir (end with /) if it exists, and a prefix otherwhise.
+ void AddDynamic(int aPerms, const char* aPath);
+ // Adds permissions on all ancestors of a path. (This doesn't
+ // include the root directory, but if the path is given with a
+ // trailing slash it includes the path without the slash.)
+ void AddAncestors(const char* aPath, int aPerms = MAY_ACCESS);
+ // Default: add file if it exists when creating policy or if we're
+ // conferring permission to create it (log files, etc.).
+ void AddPath(int aPerms, const char* aPath) {
+ AddPath(aPerms, aPath,
+ (aPerms & MAY_CREATE) ? AddAlways : AddIfExistsNow);
+ }
+ int Lookup(const nsACString& aPath) const;
+ int Lookup(const char* aPath) const {
+ return Lookup(nsDependentCString(aPath));
+ }
+
+ bool IsEmpty() const { return mMap.Count() == 0; }
+
+ private:
+ // ValidatePath checks |path| and returns true if these conditions are met
+ // * Greater than 0 length
+ // * Is an absolute path
+ // * No trailing slash
+ // * No /../ path traversal
+ bool ValidatePath(const char* path) const;
+ void AddPrefixInternal(int aPerms, const nsACString& aPath);
+ void AddDirInternal(int aPerms, const char* aPath);
+ };
+
+ // Constructing a broker involves creating a socketpair and a
+ // background thread to handle requests, so it can fail. If this
+ // returns nullptr, do not use the value of aClientFdOut.
+ static UniquePtr<SandboxBroker> Create(UniquePtr<const Policy> aPolicy,
+ int aChildPid,
+ ipc::FileDescriptor& aClientFdOut);
+ virtual ~SandboxBroker();
+
+ private:
+ PlatformThreadHandle mThread;
+ int mFileDesc;
+ const int mChildPid;
+ const UniquePtr<const Policy> mPolicy;
+#if defined(MOZ_CONTENT_TEMP_DIR)
+ nsCString mTempPath;
+ nsCString mContentTempPath;
+#endif
+
+ typedef nsTHashMap<nsCStringHashKey, nsCString> PathMap;
+ PathMap mSymlinkMap;
+
+ SandboxBroker(UniquePtr<const Policy> aPolicy, int aChildPid, int& aClientFd);
+ void ThreadMain(void) override;
+ void AuditPermissive(int aOp, int aFlags, int aPerms, const char* aPath);
+ void AuditDenial(int aOp, int aFlags, int aPerms, const char* aPath);
+ // Remap relative paths to absolute paths.
+ size_t ConvertRelativePath(char* aPath, size_t aBufSize, size_t aPathLen);
+ size_t RealPath(char* aPath, size_t aBufSize, size_t aPathLen);
+#if defined(MOZ_CONTENT_TEMP_DIR)
+ // Remap references to /tmp and friends to the content process tempdir
+ size_t RemapTempDirs(char* aPath, size_t aBufSize, size_t aPathLen);
+#endif
+ nsCString ReverseSymlinks(const nsACString& aPath);
+ // Retrieves permissions for the path the original symlink sits in.
+ int SymlinkPermissions(const char* aPath, const size_t aPathLen);
+ // In SandboxBrokerRealPath.cpp
+ char* SymlinkPath(const Policy* aPolicy, const char* __restrict aPath,
+ char* __restrict aResolved, int* aPermission);
+
+ // Holding a UniquePtr should disallow copying, but to make that explicit:
+ SandboxBroker(const SandboxBroker&) = delete;
+ void operator=(const SandboxBroker&) = delete;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxBroker_h
diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
new file mode 100644
index 0000000000..2a9dcfff40
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
@@ -0,0 +1,155 @@
+/* -*- 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/. */
+
+#include "SandboxBrokerCommon.h"
+
+#include "mozilla/Assertions.h"
+
+// This file is built both within libxul and as a separate libmozsandbox
+// library. We can only use profiler annotations within libxul.
+#ifdef MOZILLA_INTERNAL_API
+# include "mozilla/ProfilerThreadSleep.h"
+#else
+# define AUTO_PROFILER_THREAD_SLEEP
+#endif
+
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifndef MSG_CMSG_CLOEXEC
+# ifdef XP_LINUX
+// As always, Android's kernel headers are somewhat old.
+# define MSG_CMSG_CLOEXEC 0x40000000
+# else
+// Most of this code can support other POSIX OSes, but being able to
+// receive fds and atomically make them close-on-exec is important,
+// because this is running in a multithreaded process that can fork.
+// In the future, if the broker becomes a dedicated executable, this
+// can change.
+# error "No MSG_CMSG_CLOEXEC?"
+# endif // XP_LINUX
+#endif // MSG_CMSG_CLOEXEC
+
+namespace mozilla {
+
+const char* SandboxBrokerCommon::OperationDescription[] = {
+ "open",
+ "access",
+ "stat",
+ "chmod",
+ "link",
+ "symlink",
+ "mkdir",
+ "rename",
+ "rmdir",
+ "unlink",
+ "readlink",
+ "connect",
+ "connect-abstract",
+};
+
+/* static */
+ssize_t SandboxBrokerCommon::RecvWithFd(int aFd, const iovec* aIO,
+ size_t aNumIO, int* aPassedFdPtr) {
+ struct msghdr msg = {};
+ msg.msg_iov = const_cast<iovec*>(aIO);
+ msg.msg_iovlen = aNumIO;
+
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+ if (aPassedFdPtr) {
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+ *aPassedFdPtr = -1;
+ }
+
+ ssize_t rv;
+ do {
+ // MSG_CMSG_CLOEXEC is needed to prevent the parent process from
+ // accidentally leaking a copy of the child's response socket to a
+ // new child process. (The child won't be able to exec, so this
+ // doesn't matter as much for that direction.)
+ AUTO_PROFILER_THREAD_SLEEP;
+ rv = recvmsg(aFd, &msg, MSG_CMSG_CLOEXEC);
+ } while (rv < 0 && errno == EINTR);
+
+ if (rv <= 0) {
+ return rv;
+ }
+ if (msg.msg_controllen > 0) {
+ MOZ_ASSERT(aPassedFdPtr);
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
+ // A client could, for example, send an extra 32-bit int if
+ // CMSG_SPACE pads to 64-bit size_t alignment. If so, treat
+ // it as an error, but also don't leak the fds.
+ for (size_t i = 0; CMSG_LEN(sizeof(int) * i) < cmsg->cmsg_len; ++i) {
+ close(fds[i]);
+ }
+ // In theory, the kernel should delete the message instead of
+ // giving us an empty one, if errors prevent transferring the
+ // fd.
+ MOZ_DIAGNOSTIC_ASSERT(cmsg->cmsg_len != 0);
+ errno = EMSGSIZE;
+ return -1;
+ }
+ *aPassedFdPtr = fds[0];
+ } else {
+ errno = EPROTO;
+ return -1;
+ }
+ }
+ if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
+ if (aPassedFdPtr && *aPassedFdPtr >= 0) {
+ close(*aPassedFdPtr);
+ *aPassedFdPtr = -1;
+ }
+ // MSG_CTRUNC usually means the fd was dropped due to fd
+ // exhaustion in the receiving process, so map that to `EMFILE`.
+ // MSG_TRUNC (truncation of the data part) shouldn't ever happen.
+ // (If the sender is malicious it can send too many bytes or fds,
+ // but this is about getting an accurate error message in genuine
+ // error cases.)
+ MOZ_DIAGNOSTIC_ASSERT((msg.msg_flags & MSG_TRUNC) == 0);
+ errno = EMFILE;
+ return -1;
+ }
+
+ return rv;
+}
+
+/* static */
+ssize_t SandboxBrokerCommon::SendWithFd(int aFd, const iovec* aIO,
+ size_t aNumIO, int aPassedFd) {
+ struct msghdr msg = {};
+ msg.msg_iov = const_cast<iovec*>(aIO);
+ msg.msg_iovlen = aNumIO;
+
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ if (aPassedFd != -1) {
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ *reinterpret_cast<int*>(CMSG_DATA(cmsg)) = aPassedFd;
+ }
+
+ ssize_t rv;
+ do {
+ rv = sendmsg(aFd, &msg, MSG_NOSIGNAL);
+ } while (rv < 0 && errno == EINTR);
+
+ return rv;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/broker/SandboxBrokerCommon.h b/security/sandbox/linux/broker/SandboxBrokerCommon.h
new file mode 100644
index 0000000000..b6b69e2a36
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -0,0 +1,77 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxBrokerCommon_h
+#define mozilla_SandboxBrokerCommon_h
+
+#include <sys/types.h>
+
+struct iovec;
+
+// This file defines the protocol between the filesystem broker,
+// described in SandboxBroker.h, and its client, described in
+// ../SandboxBrokerClient.h; and it defines some utility functions
+// used by both.
+//
+// In order to keep the client simple while allowing it to be thread
+// safe and async signal safe, the main broker socket is used only for
+// requests; responses arrive on a per-request socketpair sent with
+// the request. (This technique is also used by Chromium and Breakpad.)
+
+namespace mozilla {
+
+class SandboxBrokerCommon {
+ public:
+ enum Operation {
+ SANDBOX_FILE_OPEN,
+ SANDBOX_FILE_ACCESS,
+ SANDBOX_FILE_STAT,
+ SANDBOX_FILE_CHMOD,
+ SANDBOX_FILE_LINK,
+ SANDBOX_FILE_SYMLINK,
+ SANDBOX_FILE_MKDIR,
+ SANDBOX_FILE_RENAME,
+ SANDBOX_FILE_RMDIR,
+ SANDBOX_FILE_UNLINK,
+ SANDBOX_FILE_READLINK,
+ SANDBOX_SOCKET_CONNECT,
+ SANDBOX_SOCKET_CONNECT_ABSTRACT,
+ };
+ // String versions of the above
+ static const char* OperationDescription[];
+
+ struct Request {
+ Operation mOp;
+ // For open, flags; for access, "mode"; for stat, O_NOFOLLOW for lstat.
+ // For connect, the socket type.
+ int mFlags;
+ // Size of return value buffer, if any
+ size_t mBufSize;
+ // The rest of the packet is the pathname.
+ // SCM_RIGHTS for response socket attached.
+ };
+
+ struct Response {
+ // Syscall result, -errno if failure, or 0 for no error
+ int mError;
+ // Followed by struct stat for stat/lstat.
+ // SCM_RIGHTS attached for successful open.
+ };
+
+ // This doesn't need to be the system's maximum path length, just
+ // the largest path that would be allowed by any policy. (It's used
+ // to size a stack-allocated buffer.)
+ static const size_t kMaxPathLen = 4096;
+
+ static ssize_t RecvWithFd(int aFd, const iovec* aIO, size_t aNumIO,
+ int* aPassedFdPtr);
+ static ssize_t SendWithFd(int aFd, const iovec* aIO, size_t aNumIO,
+ int aPassedFd);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxBrokerCommon_h
diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
new file mode 100644
index 0000000000..fe7ce56c7e
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp
@@ -0,0 +1,1017 @@
+/* -*- 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/. */
+
+#include "SandboxBrokerPolicyFactory.h"
+#include "SandboxInfo.h"
+#include "SandboxLogging.h"
+
+#include "base/shared_memory.h"
+#include "mozilla/Array.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SandboxLaunch.h"
+#include "mozilla/SandboxSettings.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsComponentManagerUtils.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "SpecialSystemDirectory.h"
+#include "nsReadableUtils.h"
+#include "nsIFileStreams.h"
+#include "nsILineInputStream.h"
+#include "nsIFile.h"
+
+#include "nsNetCID.h"
+#include "prenv.h"
+
+#ifdef ANDROID
+# include "cutils/properties.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+# include <glib.h>
+#endif
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#ifndef ANDROID
+# include <glob.h>
+#endif
+
+namespace mozilla {
+
+namespace {
+static const int rdonly = SandboxBroker::MAY_READ;
+static const int wronly = SandboxBroker::MAY_WRITE;
+static const int rdwr = rdonly | wronly;
+static const int rdwrcr = rdwr | SandboxBroker::MAY_CREATE;
+static const int access = SandboxBroker::MAY_ACCESS;
+static const int deny = SandboxBroker::FORCE_DENY;
+} // namespace
+
+using CacheE = std::pair<nsCString, int>;
+using FileCacheT = nsTArray<CacheE>;
+
+static void AddDriPaths(SandboxBroker::Policy* aPolicy) {
+ // Bug 1401666: Mesa driver loader part 2: Mesa <= 12 using libudev
+ // Used by libdrm, which is used by Mesa, and
+ // Intel(R) Media Driver for VAAPI.
+ if (auto dir = opendir("/dev/dri")) {
+ while (auto entry = readdir(dir)) {
+ if (entry->d_name[0] != '.') {
+ nsPrintfCString devPath("/dev/dri/%s", entry->d_name);
+ struct stat sb;
+ if (stat(devPath.get(), &sb) == 0 && S_ISCHR(sb.st_mode)) {
+ // For both the DRI node and its parent (the physical
+ // device), allow reading the "uevent" file.
+ static const Array<nsCString, 2> kSuffixes = {""_ns, "/device"_ns};
+ nsPrintfCString prefix("/sys/dev/char/%u:%u", major(sb.st_rdev),
+ minor(sb.st_rdev));
+ for (const auto& suffix : kSuffixes) {
+ nsCString sysPath(prefix + suffix);
+
+ // libudev will expand the symlink but not do full
+ // canonicalization, so it will leave in ".." path
+ // components that will be realpath()ed in the
+ // broker. To match this, allow the canonical paths.
+ UniqueFreePtr<char[]> realSysPath(realpath(sysPath.get(), nullptr));
+ if (realSysPath) {
+ // https://gitlab.freedesktop.org/mesa/drm/-/commit/3988580e4c0f4b3647a0c6af138a3825453fe6e0
+ // > term = strrchr(real_path, '/');
+ // > if (term && strncmp(term, "/virtio", 7) == 0)
+ // > *term = 0;
+ char* term = strrchr(realSysPath.get(), '/');
+ if (term && strncmp(term, "/virtio", 7) == 0) {
+ *term = 0;
+ }
+
+ aPolicy->AddFilePrefix(rdonly, realSysPath.get(), "");
+ // Allowing stat-ing and readlink-ing the parent dirs
+ nsPrintfCString basePath("%s/", realSysPath.get());
+ aPolicy->AddAncestors(basePath.get(), rdonly);
+ }
+ }
+
+ // https://gitlab.freedesktop.org/mesa/drm/-/commit/a02900133b32dd4a7d6da4966f455ab337e80dfc
+ // > strncpy(path, device_path, PATH_MAX);
+ // > strncat(path, "/subsystem", PATH_MAX);
+ // >
+ // > if (readlink(path, link, PATH_MAX) < 0)
+ // > return -errno;
+ nsCString subsystemPath(prefix + "/device/subsystem"_ns);
+ aPolicy->AddPath(rdonly, subsystemPath.get());
+ aPolicy->AddAncestors(subsystemPath.get(), rdonly);
+ }
+ }
+ }
+ closedir(dir);
+ }
+
+ // https://gitlab.freedesktop.org/mesa/mesa/-/commit/04bdbbcab3c4862bf3f54ce60fcc1d2007776f80
+ aPolicy->AddPath(rdonly, "/usr/share/drirc.d");
+
+ // https://dri.freedesktop.org/wiki/ConfigurationInfrastructure/
+ aPolicy->AddPath(rdonly, "/etc/drirc");
+
+ nsCOMPtr<nsIFile> drirc;
+ nsresult rv =
+ GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(drirc));
+ if (NS_SUCCEEDED(rv)) {
+ rv = drirc->AppendNative(".drirc"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = drirc->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ aPolicy->AddPath(rdonly, tmpPath.get());
+ }
+ }
+ }
+}
+
+static void JoinPathIfRelative(const nsACString& aCwd, const nsACString& inPath,
+ nsACString& outPath) {
+ if (inPath.Length() < 1) {
+ outPath.Assign(aCwd);
+ SANDBOX_LOG("Unjoinable path: %s", PromiseFlatCString(aCwd).get());
+ return;
+ }
+ const char* startChar = inPath.BeginReading();
+ if (*startChar != '/') {
+ // Relative path, copy basepath in front
+ outPath.Assign(aCwd);
+ outPath.Append("/");
+ outPath.Append(inPath);
+ } else {
+ // Absolute path, it's ok like this
+ outPath.Assign(inPath);
+ }
+}
+
+static void CachePathsFromFile(FileCacheT& aCache, const nsACString& aPath);
+
+static void CachePathsFromFileInternal(FileCacheT& aCache,
+ const nsACString& aCwd,
+ const nsACString& aPath) {
+ nsresult rv;
+ nsCOMPtr<nsIFile> ldconfig(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ rv = ldconfig->InitWithNativePath(aPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ nsCOMPtr<nsIFileInputStream> fileStream(
+ do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ rv = fileStream->Init(ldconfig, -1, -1, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsAutoCString line;
+ bool more = true;
+ do {
+ rv = lineStream->ReadLine(line, &more);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ // Cut off any comments at the end of the line, also catches lines
+ // that are entirely a comment
+ int32_t hash = line.FindChar('#');
+ if (hash >= 0) {
+ line = Substring(line, 0, hash);
+ }
+ // Simplify our following parsing by trimming whitespace
+ line.CompressWhitespace(true, true);
+ if (line.IsEmpty()) {
+ // Skip comment lines
+ continue;
+ }
+ // Check for any included files and recursively process
+ nsACString::const_iterator start, end, token_end;
+
+ line.BeginReading(start);
+ line.EndReading(end);
+ token_end = end;
+
+ if (FindInReadable("include "_ns, start, token_end)) {
+ nsAutoCString includes(Substring(token_end, end));
+ for (const nsACString& includeGlob : includes.Split(' ')) {
+ // Glob path might be relative, so add cwd if so.
+ nsAutoCString includeFile;
+ JoinPathIfRelative(aCwd, includeGlob, includeFile);
+ glob_t globbuf;
+ if (!glob(PromiseFlatCString(includeFile).get(), GLOB_NOSORT, nullptr,
+ &globbuf)) {
+ for (size_t fileIdx = 0; fileIdx < globbuf.gl_pathc; fileIdx++) {
+ nsAutoCString filePath(globbuf.gl_pathv[fileIdx]);
+ CachePathsFromFile(aCache, filePath);
+ }
+ globfree(&globbuf);
+ }
+ }
+ }
+
+ // Cut off anything behind an = sign, used by dirname=TYPE directives
+ int32_t equals = line.FindChar('=');
+ if (equals >= 0) {
+ line = Substring(line, 0, equals);
+ }
+ char* resolvedPath = realpath(line.get(), nullptr);
+ if (resolvedPath) {
+ aCache.AppendElement(std::make_pair(nsCString(resolvedPath), rdonly));
+ free(resolvedPath);
+ }
+ } while (more);
+}
+
+static void CachePathsFromFile(FileCacheT& aCache, const nsACString& aPath) {
+ // Find the new base path where that file sits in.
+ nsresult rv;
+ nsCOMPtr<nsIFile> includeFile(
+ do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ rv = includeFile->InitWithNativePath(aPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Adding paths from %s to policy.",
+ PromiseFlatCString(aPath).get());
+ }
+
+ // Find the parent dir where this file sits in.
+ nsCOMPtr<nsIFile> parentDir;
+ rv = includeFile->GetParent(getter_AddRefs(parentDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ nsAutoCString parentPath;
+ rv = parentDir->GetNativePath(parentPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ if (SandboxInfo::Get().Test(SandboxInfo::kVerbose)) {
+ SANDBOX_LOG("Parent path is %s", PromiseFlatCString(parentPath).get());
+ }
+ CachePathsFromFileInternal(aCache, parentPath, aPath);
+}
+
+static void AddLdconfigPaths(SandboxBroker::Policy* aPolicy) {
+ static StaticMutex sMutex;
+ StaticMutexAutoLock lock(sMutex);
+
+ static FileCacheT ldConfigCache{};
+ static bool ldConfigCachePopulated = false;
+ if (!ldConfigCachePopulated) {
+ CachePathsFromFile(ldConfigCache, "/etc/ld.so.conf"_ns);
+ ldConfigCachePopulated = true;
+ RunOnShutdown([&] {
+ ldConfigCache.Clear();
+ MOZ_ASSERT(ldConfigCache.IsEmpty(), "ldconfig cache should be empty");
+ });
+ }
+ for (const CacheE& e : ldConfigCache) {
+ aPolicy->AddDir(e.second, e.first.get());
+ }
+}
+
+static void AddLdLibraryEnvPaths(SandboxBroker::Policy* aPolicy) {
+ nsAutoCString LdLibraryEnv(PR_GetEnv("LD_LIBRARY_PATH"));
+ // The items in LD_LIBRARY_PATH can be separated by either colons or
+ // semicolons, according to the ld.so(8) man page, and empirically it
+ // seems to be allowed to mix them (i.e., a:b;c is a list with 3 elements).
+ // There is no support for escaping the delimiters, fortunately (for us).
+ LdLibraryEnv.ReplaceChar(';', ':');
+ for (const nsACString& libPath : LdLibraryEnv.Split(':')) {
+ char* resolvedPath = realpath(PromiseFlatCString(libPath).get(), nullptr);
+ if (resolvedPath) {
+ aPolicy->AddDir(rdonly, resolvedPath);
+ free(resolvedPath);
+ }
+ }
+}
+
+static void AddSharedMemoryPaths(SandboxBroker::Policy* aPolicy, pid_t aPid) {
+ std::string shmPath("/dev/shm");
+ if (base::SharedMemory::AppendPosixShmPrefix(&shmPath, aPid)) {
+ aPolicy->AddPrefix(rdwrcr, shmPath.c_str());
+ }
+}
+
+static void AddMemoryReporting(SandboxBroker::Policy* aPolicy, pid_t aPid) {
+ // Bug 1198552: memory reporting.
+ // Bug 1647957: memory reporting.
+ aPolicy->AddPath(rdonly, nsPrintfCString("/proc/%d/statm", aPid).get());
+ aPolicy->AddPath(rdonly, nsPrintfCString("/proc/%d/smaps", aPid).get());
+}
+
+static void AddDynamicPathList(SandboxBroker::Policy* policy,
+ const char* aPathListPref, int perms) {
+ nsAutoCString pathList;
+ nsresult rv = Preferences::GetCString(aPathListPref, pathList);
+ if (NS_SUCCEEDED(rv)) {
+ for (const nsACString& path : pathList.Split(',')) {
+ nsCString trimPath(path);
+ trimPath.Trim(" ", true, true);
+ policy->AddDynamic(perms, trimPath.get());
+ }
+ }
+}
+
+static void AddX11Dependencies(SandboxBroker::Policy* policy) {
+ // Allow Primus to contact the Bumblebee daemon to manage GPU
+ // switching on NVIDIA Optimus systems.
+ const char* bumblebeeSocket = PR_GetEnv("BUMBLEBEE_SOCKET");
+ if (bumblebeeSocket == nullptr) {
+ bumblebeeSocket = "/var/run/bumblebee.socket";
+ }
+ policy->AddPath(SandboxBroker::MAY_CONNECT, bumblebeeSocket);
+
+#if defined(MOZ_WIDGET_GTK) && defined(MOZ_X11)
+ // Allow local X11 connections, for several purposes:
+ //
+ // * for content processes to use WebGL when the browser is in headless
+ // mode, by opening the X display if/when needed
+ //
+ // * if Primus or VirtualGL is used, to contact the secondary X server
+ static const bool kIsX11 =
+ !mozilla::widget::GdkIsWaylandDisplay() && PR_GetEnv("DISPLAY");
+ if (kIsX11) {
+ policy->AddPrefix(SandboxBroker::MAY_CONNECT, "/tmp/.X11-unix/X");
+ if (auto* const xauth = PR_GetEnv("XAUTHORITY")) {
+ policy->AddPath(rdonly, xauth);
+ } else if (auto* const home = PR_GetEnv("HOME")) {
+ // This follows the logic in libXau: append "/.Xauthority",
+ // even if $HOME ends in a slash, except in the special case
+ // where HOME=/ because POSIX allows implementations to treat
+ // an initial double slash specially.
+ nsAutoCString xauth(home);
+ if (xauth != "/"_ns) {
+ xauth.Append('/');
+ }
+ xauth.AppendLiteral(".Xauthority");
+ policy->AddPath(rdonly, xauth.get());
+ }
+ }
+#endif
+}
+
+static void AddGLDependencies(SandboxBroker::Policy* policy) {
+ // Devices
+ policy->AddDir(rdwr, "/dev/dri");
+ policy->AddFilePrefix(rdwr, "/dev", "nvidia");
+
+ // Hardware info
+ AddDriPaths(policy);
+
+ // /etc and /usr/share (glvnd, libdrm, drirc, ...?)
+ policy->AddDir(rdonly, "/etc");
+ policy->AddDir(rdonly, "/usr/share");
+ policy->AddDir(rdonly, "/usr/local/share");
+
+ // Snap puts the usual /usr/share things in a different place, and
+ // we'll fail to load the library if we don't have (at least) the
+ // glvnd config:
+ if (const char* snapDesktopDir = PR_GetEnv("SNAP_DESKTOP_RUNTIME")) {
+ nsAutoCString snapDesktopShare(snapDesktopDir);
+ snapDesktopShare.AppendLiteral("/usr/share");
+ policy->AddDir(rdonly, snapDesktopShare.get());
+ }
+
+ // Note: This function doesn't do anything about Mesa's shader
+ // cache, because the details can vary by process type, including
+ // whether caching is enabled.
+
+ // This also doesn't include permissions for connecting to a display
+ // server, because headless GL (e.g., Mesa GBM) may not need it.
+}
+
+void SandboxBrokerPolicyFactory::InitContentPolicy() {
+ const bool headless =
+ StaticPrefs::security_sandbox_content_headless_AtStartup();
+
+ // Policy entries that are the same in every process go here, and
+ // are cached over the lifetime of the factory.
+ SandboxBroker::Policy* policy = new SandboxBroker::Policy;
+ // Write permssions
+
+ // Bug 1575985: WASM library sandbox needs RW access to /dev/null
+ policy->AddPath(rdwr, "/dev/null");
+
+ if (!headless) {
+ AddGLDependencies(policy);
+ AddX11Dependencies(policy);
+ }
+
+ // Read permissions
+ policy->AddPath(rdonly, "/dev/urandom");
+ policy->AddPath(rdonly, "/dev/random");
+ policy->AddPath(rdonly, "/proc/sys/crypto/fips_enabled");
+ policy->AddPath(rdonly, "/proc/cpuinfo");
+ policy->AddPath(rdonly, "/proc/meminfo");
+ policy->AddDir(rdonly, "/sys/devices/cpu");
+ policy->AddDir(rdonly, "/sys/devices/system/cpu");
+ policy->AddDir(rdonly, "/lib");
+ policy->AddDir(rdonly, "/lib64");
+ policy->AddDir(rdonly, "/usr/lib");
+ policy->AddDir(rdonly, "/usr/lib32");
+ policy->AddDir(rdonly, "/usr/lib64");
+ policy->AddDir(rdonly, "/etc");
+ policy->AddDir(rdonly, "/usr/share");
+ policy->AddDir(rdonly, "/usr/local/share");
+ // Various places where fonts reside
+ policy->AddDir(rdonly, "/usr/X11R6/lib/X11/fonts");
+ policy->AddDir(rdonly, "/nix/store");
+ // https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/blob/e434e680d22260f277f4a30ec4660ed32b591d16/files/fontconfig-flatpak.conf
+ policy->AddDir(rdonly, "/run/host/fonts");
+ policy->AddDir(rdonly, "/run/host/user-fonts");
+ policy->AddDir(rdonly, "/run/host/local-fonts");
+ policy->AddDir(rdonly, "/var/cache/fontconfig");
+
+ // Bug 1848615
+ policy->AddPath(rdonly, "/usr");
+ policy->AddPath(rdonly, "/nix");
+
+ AddLdconfigPaths(policy);
+ AddLdLibraryEnvPaths(policy);
+
+ if (!headless) {
+ // Bug 1385715: NVIDIA PRIME support
+ policy->AddPath(rdonly, "/proc/modules");
+ }
+
+ // XDG directories might be non existent according to specs:
+ // https://specifications.freedesktop.org/basedir-spec/0.8/ar01s04.html
+ //
+ // > If, when attempting to write a file, the destination directory is
+ // > non-existent an attempt should be made to create it with permission 0700.
+ //
+ // For that we use AddPath(, SandboxBroker::Policy::AddCondition::AddAlways).
+ //
+ // Allow access to XDG_CONFIG_HOME and XDG_CONFIG_DIRS
+ nsAutoCString xdgConfigHome(PR_GetEnv("XDG_CONFIG_HOME"));
+ if (!xdgConfigHome.IsEmpty()) { // AddPath will fail on empty strings
+ policy->AddFutureDir(rdonly, xdgConfigHome.get());
+ }
+
+ nsAutoCString xdgConfigDirs(PR_GetEnv("XDG_CONFIG_DIRS"));
+ for (const auto& path : xdgConfigDirs.Split(':')) {
+ if (!path.IsEmpty()) { // AddPath will fail on empty strings
+ policy->AddFutureDir(rdonly, PromiseFlatCString(path).get());
+ }
+ }
+
+ // Allow fonts subdir in XDG_DATA_HOME
+ nsAutoCString xdgDataHome(PR_GetEnv("XDG_DATA_HOME"));
+ if (!xdgDataHome.IsEmpty()) {
+ nsAutoCString fontPath(xdgDataHome);
+ fontPath.Append("/fonts");
+ policy->AddFutureDir(rdonly, PromiseFlatCString(fontPath).get());
+ }
+
+ // Any font subdirs in XDG_DATA_DIRS
+ nsAutoCString xdgDataDirs(PR_GetEnv("XDG_DATA_DIRS"));
+ for (const auto& path : xdgDataDirs.Split(':')) {
+ nsAutoCString fontPath(path);
+ fontPath.Append("/fonts");
+ policy->AddFutureDir(rdonly, PromiseFlatCString(fontPath).get());
+ }
+
+ // Extra configuration/cache dirs in the homedir that we want to allow read
+ // access to.
+ std::vector<const char*> extraConfDirsAllow = {
+ ".themes",
+ ".fonts",
+ ".cache/fontconfig",
+ };
+
+ // Fallback if XDG_CONFIG_HOME isn't set
+ if (xdgConfigHome.IsEmpty()) {
+ extraConfDirsAllow.emplace_back(".config");
+ }
+
+ nsCOMPtr<nsIFile> homeDir;
+ nsresult rv =
+ GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(homeDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIFile> confDir;
+
+ for (const auto& dir : extraConfDirsAllow) {
+ rv = homeDir->Clone(getter_AddRefs(confDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = confDir->AppendRelativeNativePath(nsDependentCString(dir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = confDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+ }
+ }
+
+ // ~/.config/mozilla/ needs to be manually blocked, because the previous
+ // loop will allow for ~/.config/ access.
+ {
+ // If $XDG_CONFIG_HOME is set, we need to account for it.
+ // FIXME: Bug 1722272: Maybe this should just be handled with
+ // GetSpecialSystemDirectory(Unix_XDG_ConfigHome) ?
+ nsCOMPtr<nsIFile> confDirOrXDGConfigHomeDir;
+ if (!xdgConfigHome.IsEmpty()) {
+ rv = NS_NewNativeLocalFile(xdgConfigHome, true,
+ getter_AddRefs(confDirOrXDGConfigHomeDir));
+ // confDirOrXDGConfigHomeDir = nsIFile($XDG_CONFIG_HOME)
+ } else {
+ rv = homeDir->Clone(getter_AddRefs(confDirOrXDGConfigHomeDir));
+ if (NS_SUCCEEDED(rv)) {
+ // since we will use that later, we dont need to care about trailing
+ // slash
+ rv = confDirOrXDGConfigHomeDir->AppendNative(".config"_ns);
+ // confDirOrXDGConfigHomeDir = nsIFile($HOME/.config/)
+ }
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ rv = confDirOrXDGConfigHomeDir->AppendNative("mozilla"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = confDirOrXDGConfigHomeDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddFutureDir(deny, tmpPath.get());
+ }
+ }
+ }
+ }
+
+ // ~/.local/share (for themes)
+ rv = homeDir->Clone(getter_AddRefs(confDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = confDir->AppendNative(".local"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ rv = confDir->AppendNative("share"_ns);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = confDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+ }
+
+ // ~/.fonts.conf (Fontconfig)
+ rv = homeDir->Clone(getter_AddRefs(confDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = confDir->AppendNative(".fonts.conf"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = confDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddPath(rdonly, tmpPath.get());
+ }
+ }
+ }
+
+ // .pangorc
+ rv = homeDir->Clone(getter_AddRefs(confDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = confDir->AppendNative(".pangorc"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = confDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddPath(rdonly, tmpPath.get());
+ }
+ }
+ }
+ }
+
+ // Firefox binary dir.
+ // Note that unlike the previous cases, we use NS_GetSpecialDirectory
+ // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
+ // system, which may not be the case for some tests. For querying for the
+ // location of XPCOM things, we can use it anyway.
+ nsCOMPtr<nsIFile> ffDir;
+ rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(ffDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = ffDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+
+ if (!mozilla::IsPackagedBuild()) {
+ // If this is not a packaged build the resources are likely symlinks to
+ // outside the binary dir. Therefore in non-release builds we allow reads
+ // from the whole repository. MOZ_DEVELOPER_REPO_DIR is set by mach run.
+ const char* developer_repo_dir = PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
+ if (developer_repo_dir) {
+ policy->AddDir(rdonly, developer_repo_dir);
+ }
+ }
+
+#ifdef DEBUG
+ char* bloatLog = PR_GetEnv("XPCOM_MEM_BLOAT_LOG");
+ // XPCOM_MEM_BLOAT_LOG has the format
+ // /tmp/tmpd0YzFZ.mozrunner/runtests_leaks.log
+ // but stores into /tmp/tmpd0YzFZ.mozrunner/runtests_leaks_tab_pid3411.log
+ // So cut the .log part and whitelist the prefix.
+ if (bloatLog != nullptr) {
+ size_t bloatLen = strlen(bloatLog);
+ if (bloatLen >= 4) {
+ nsAutoCString bloatStr(bloatLog);
+ bloatStr.Truncate(bloatLen - 4);
+ policy->AddPrefix(rdwrcr, bloatStr.get());
+ }
+ }
+#endif
+
+ if (!headless) {
+ AddX11Dependencies(policy);
+ }
+
+ // Bug 1732580: when packaged as a strictly confined snap, may need
+ // read-access to configuration files under $SNAP/.
+ const char* snap = PR_GetEnv("SNAP");
+ if (snap) {
+ // When running as a snap, the directory pointed to by $SNAP is guaranteed
+ // to exist before the app is launched, but unit tests need to create it
+ // dynamically, hence the use of AddFutureDir().
+ policy->AddDir(rdonly, snap);
+ }
+
+ // Read any extra paths that will get write permissions,
+ // configured by the user or distro
+ AddDynamicPathList(policy, "security.sandbox.content.write_path_whitelist",
+ rdwr);
+
+ // Whitelisted for reading by the user/distro
+ AddDynamicPathList(policy, "security.sandbox.content.read_path_whitelist",
+ rdonly);
+
+#if defined(MOZ_CONTENT_TEMP_DIR)
+ // Add write permissions on the content process specific temporary dir.
+ nsCOMPtr<nsIFile> tmpDir;
+ rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR,
+ getter_AddRefs(tmpDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = tmpDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdwrcr, tmpPath.get());
+ }
+ }
+#endif
+
+ // userContent.css and the extensions dir sit in the profile, which is
+ // normally blocked.
+ nsCOMPtr<nsIFile> profileDir;
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIFile> workDir;
+ rv = profileDir->Clone(getter_AddRefs(workDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = workDir->AppendNative("chrome"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = workDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+ }
+ rv = profileDir->Clone(getter_AddRefs(workDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = workDir->AppendNative("extensions"_ns);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = workDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ bool exists;
+ rv = workDir->Exists(&exists);
+ if (NS_SUCCEEDED(rv)) {
+ if (!exists) {
+ policy->AddPrefix(rdonly, tmpPath.get());
+ policy->AddPath(rdonly, tmpPath.get());
+ } else {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ const int level = GetEffectiveContentSandboxLevel();
+ bool allowPulse = false;
+ bool allowAlsa = false;
+ if (level < 4) {
+#ifdef MOZ_PULSEAUDIO
+ allowPulse = true;
+#endif
+#ifdef MOZ_ALSA
+ allowAlsa = true;
+#endif
+ }
+
+ if (allowAlsa) {
+ // Bug 1309098: ALSA support
+ policy->AddDir(rdwr, "/dev/snd");
+ }
+
+ if (allowPulse) {
+ policy->AddDir(rdwrcr, "/dev/shm");
+ }
+
+#ifdef MOZ_WIDGET_GTK
+ if (const auto userDir = g_get_user_runtime_dir()) {
+ // Bug 1321134: DConf's single bit of shared memory
+ // The leaf filename is "user" by default, but is configurable.
+ nsPrintfCString shmPath("%s/dconf/", userDir);
+ policy->AddPrefix(rdwrcr, shmPath.get());
+ policy->AddAncestors(shmPath.get());
+ if (allowPulse) {
+ // PulseAudio, if it can't get server info from X11, will break
+ // unless it can open this directory (or create it, but in our use
+ // case we know it already exists). See bug 1335329.
+ nsPrintfCString pulsePath("%s/pulse", userDir);
+ policy->AddPath(rdonly, pulsePath.get());
+ }
+ }
+#endif // MOZ_WIDGET_GTK
+
+ if (allowPulse) {
+ // PulseAudio also needs access to read the $XAUTHORITY file (see
+ // bug 1384986 comment #1), but that's already allowed for hybrid
+ // GPU drivers (see above).
+ policy->AddPath(rdonly, "/var/lib/dbus/machine-id");
+ }
+
+ // Bug 1434711 - AMDGPU-PRO crashes if it can't read it's marketing ids
+ // and various other things
+ if (!headless && HasAtiDrivers()) {
+ policy->AddDir(rdonly, "/opt/amdgpu/share");
+ policy->AddPath(rdonly, "/sys/module/amdgpu");
+ }
+
+ mCommonContentPolicy.reset(policy);
+}
+
+UniquePtr<SandboxBroker::Policy> SandboxBrokerPolicyFactory::GetContentPolicy(
+ int aPid, bool aFileProcess) {
+ // Policy entries that vary per-process (because they depend on the
+ // pid or content subtype) are added here.
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ const int level = GetEffectiveContentSandboxLevel();
+ // The file broker is used at level 2 and up.
+ if (level <= 1) {
+ // Level 1 has been removed.
+ MOZ_ASSERT(level == 0);
+ return nullptr;
+ }
+
+ std::call_once(mContentInited, [this] { InitContentPolicy(); });
+ MOZ_ASSERT(mCommonContentPolicy);
+ UniquePtr<SandboxBroker::Policy> policy(
+ new SandboxBroker::Policy(*mCommonContentPolicy));
+
+ // No read blocking at level 2 and below.
+ // file:// processes also get global read permissions
+ if (level <= 2 || aFileProcess) {
+ policy->AddDir(rdonly, "/");
+ // Any other read-only rules will be removed as redundant by
+ // Policy::FixRecursivePermissions, so there's no need to
+ // early-return here.
+ }
+
+ // Access to /dev/shm is restricted to a per-process prefix to
+ // prevent interfering with other processes or with services outside
+ // the browser (e.g., PulseAudio).
+ AddSharedMemoryPaths(policy.get(), aPid);
+
+ // Bug 1198550: the profiler's replacement for dl_iterate_phdr
+ policy->AddPath(rdonly, nsPrintfCString("/proc/%d/maps", aPid).get());
+
+ // Bug 1736040: CPU use telemetry
+ policy->AddPath(rdonly, nsPrintfCString("/proc/%d/stat", aPid).get());
+
+ // Bug 1198552: memory reporting.
+ AddMemoryReporting(policy.get(), aPid);
+
+ // Bug 1384804, notably comment 15
+ // Used by libnuma, included by x265/ffmpeg, who falls back
+ // to get_mempolicy if this fails
+ policy->AddPath(rdonly, nsPrintfCString("/proc/%d/status", aPid).get());
+
+ // Finalize the policy.
+ policy->FixRecursivePermissions();
+ return policy;
+}
+
+/* static */ UniquePtr<SandboxBroker::Policy>
+SandboxBrokerPolicyFactory::GetRDDPolicy(int aPid) {
+ auto policy = MakeUnique<SandboxBroker::Policy>();
+
+ AddSharedMemoryPaths(policy.get(), aPid);
+
+ policy->AddPath(rdonly, "/dev/urandom");
+ // FIXME (bug 1662321): we should fix nsSystemInfo so that every
+ // child process doesn't need to re-read these files to get the info
+ // the parent process already has.
+ policy->AddPath(rdonly, "/proc/cpuinfo");
+ policy->AddPath(rdonly,
+ "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq");
+ policy->AddPath(rdonly, "/sys/devices/system/cpu/cpu0/cache/index2/size");
+ policy->AddPath(rdonly, "/sys/devices/system/cpu/cpu0/cache/index3/size");
+ policy->AddDir(rdonly, "/sys/devices/cpu");
+ policy->AddDir(rdonly, "/sys/devices/system/cpu");
+ policy->AddDir(rdonly, "/sys/devices/system/node");
+ policy->AddDir(rdonly, "/lib");
+ policy->AddDir(rdonly, "/lib64");
+ policy->AddDir(rdonly, "/usr/lib");
+ policy->AddDir(rdonly, "/usr/lib32");
+ policy->AddDir(rdonly, "/usr/lib64");
+ policy->AddDir(rdonly, "/run/opengl-driver/lib");
+ policy->AddDir(rdonly, "/nix/store");
+
+ // Bug 1647957: memory reporting.
+ AddMemoryReporting(policy.get(), aPid);
+
+ // Firefox binary dir.
+ // Note that unlike the previous cases, we use NS_GetSpecialDirectory
+ // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
+ // system, which may not be the case for some tests. For querying for the
+ // location of XPCOM things, we can use it anyway.
+ nsCOMPtr<nsIFile> ffDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(ffDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = ffDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+
+ if (!mozilla::IsPackagedBuild()) {
+ // If this is not a packaged build the resources are likely symlinks to
+ // outside the binary dir. Therefore in non-release builds we allow reads
+ // from the whole repository. MOZ_DEVELOPER_REPO_DIR is set by mach run.
+ const char* developer_repo_dir = PR_GetEnv("MOZ_DEVELOPER_REPO_DIR");
+ if (developer_repo_dir) {
+ policy->AddDir(rdonly, developer_repo_dir);
+ }
+ }
+
+ // VA-API needs GPU access and GL context creation (but not display
+ // server access, as of bug 1769499).
+ AddGLDependencies(policy.get());
+
+ // FFmpeg and GPU drivers may need general-case library loading
+ AddLdconfigPaths(policy.get());
+ AddLdLibraryEnvPaths(policy.get());
+
+ if (policy->IsEmpty()) {
+ policy = nullptr;
+ }
+ return policy;
+}
+
+/* static */ UniquePtr<SandboxBroker::Policy>
+SandboxBrokerPolicyFactory::GetSocketProcessPolicy(int aPid) {
+ auto policy = MakeUnique<SandboxBroker::Policy>();
+
+ policy->AddPath(rdonly, "/dev/urandom");
+ policy->AddPath(rdonly, "/dev/random");
+ policy->AddPath(rdonly, "/proc/sys/crypto/fips_enabled");
+ policy->AddPath(rdonly, "/proc/cpuinfo");
+ policy->AddPath(rdonly, "/proc/meminfo");
+ policy->AddDir(rdonly, "/sys/devices/cpu");
+ policy->AddDir(rdonly, "/sys/devices/system/cpu");
+ policy->AddDir(rdonly, "/lib");
+ policy->AddDir(rdonly, "/lib64");
+ policy->AddDir(rdonly, "/usr/lib");
+ policy->AddDir(rdonly, "/usr/lib32");
+ policy->AddDir(rdonly, "/usr/lib64");
+ policy->AddDir(rdonly, "/usr/share");
+ policy->AddDir(rdonly, "/usr/local/share");
+ policy->AddDir(rdonly, "/etc");
+
+ // glibc will try to stat64("/") while populating nsswitch database
+ // https://sourceware.org/git/?p=glibc.git;a=blob;f=nss/nss_database.c;h=cf0306adc47f12d9bc761ab1b013629f4482b7e6;hb=9826b03b747b841f5fc6de2054bf1ef3f5c4bdf3#l396
+ // denying will make getaddrinfo() return ENONAME
+ policy->AddDir(access, "/");
+
+ AddLdconfigPaths(policy.get());
+
+ // Socket process sandbox needs to allow shmem in order to support
+ // profiling. See Bug 1626385.
+ AddSharedMemoryPaths(policy.get(), aPid);
+
+ // Bug 1647957: memory reporting.
+ AddMemoryReporting(policy.get(), aPid);
+
+ // Firefox binary dir.
+ // Note that unlike the previous cases, we use NS_GetSpecialDirectory
+ // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
+ // system, which may not be the case for some tests. For querying for the
+ // location of XPCOM things, we can use it anyway.
+ nsCOMPtr<nsIFile> ffDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(ffDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = ffDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+
+ if (policy->IsEmpty()) {
+ policy = nullptr;
+ }
+ return policy;
+}
+
+/* static */ UniquePtr<SandboxBroker::Policy>
+SandboxBrokerPolicyFactory::GetUtilityProcessPolicy(int aPid) {
+ auto policy = MakeUnique<SandboxBroker::Policy>();
+
+ policy->AddPath(rdonly, "/dev/urandom");
+ policy->AddPath(rdonly, "/proc/cpuinfo");
+ policy->AddPath(rdonly, "/proc/meminfo");
+ policy->AddPath(rdonly, nsPrintfCString("/proc/%d/exe", aPid).get());
+ policy->AddDir(rdonly, "/sys/devices/cpu");
+ policy->AddDir(rdonly, "/sys/devices/system/cpu");
+ policy->AddDir(rdonly, "/lib");
+ policy->AddDir(rdonly, "/lib64");
+ policy->AddDir(rdonly, "/usr/lib");
+ policy->AddDir(rdonly, "/usr/lib32");
+ policy->AddDir(rdonly, "/usr/lib64");
+ policy->AddDir(rdonly, "/usr/share");
+ policy->AddDir(rdonly, "/usr/local/share");
+ policy->AddDir(rdonly, "/etc");
+
+ // glibc will try to stat64("/") while populating nsswitch database
+ // https://sourceware.org/git/?p=glibc.git;a=blob;f=nss/nss_database.c;h=cf0306adc47f12d9bc761ab1b013629f4482b7e6;hb=9826b03b747b841f5fc6de2054bf1ef3f5c4bdf3#l396
+ // denying will make getaddrinfo() return ENONAME
+ policy->AddDir(access, "/");
+
+ AddLdconfigPaths(policy.get());
+ AddLdLibraryEnvPaths(policy.get());
+
+ // Utility process sandbox needs to allow shmem in order to support
+ // profiling. See Bug 1626385.
+ AddSharedMemoryPaths(policy.get(), aPid);
+
+ // Bug 1647957: memory reporting.
+ AddMemoryReporting(policy.get(), aPid);
+
+ // Firefox binary dir.
+ // Note that unlike the previous cases, we use NS_GetSpecialDirectory
+ // instead of GetSpecialSystemDirectory. The former requires a working XPCOM
+ // system, which may not be the case for some tests. For querying for the
+ // location of XPCOM things, we can use it anyway.
+ nsCOMPtr<nsIFile> ffDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(ffDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString tmpPath;
+ rv = ffDir->GetNativePath(tmpPath);
+ if (NS_SUCCEEDED(rv)) {
+ policy->AddDir(rdonly, tmpPath.get());
+ }
+ }
+
+ if (policy->IsEmpty()) {
+ policy = nullptr;
+ }
+ return policy;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h
new file mode 100644
index 0000000000..aff3487ef9
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerPolicyFactory.h
@@ -0,0 +1,36 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxBrokerPolicyFactory_h
+#define mozilla_SandboxBrokerPolicyFactory_h
+
+#include "mozilla/SandboxBroker.h"
+
+#include <mutex>
+
+namespace mozilla {
+
+class SandboxBrokerPolicyFactory {
+ public:
+ SandboxBrokerPolicyFactory() = default;
+
+ UniquePtr<SandboxBroker::Policy> GetContentPolicy(int aPid,
+ bool aFileProcess);
+
+ static UniquePtr<SandboxBroker::Policy> GetRDDPolicy(int aPid);
+ static UniquePtr<SandboxBroker::Policy> GetSocketProcessPolicy(int aPid);
+ static UniquePtr<SandboxBroker::Policy> GetUtilityProcessPolicy(int aPid);
+
+ private:
+ UniquePtr<const SandboxBroker::Policy> mCommonContentPolicy;
+ std::once_flag mContentInited;
+
+ void InitContentPolicy();
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxBrokerPolicyFactory_h
diff --git a/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp b/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp
new file mode 100644
index 0000000000..b129af57fd
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerRealpath.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * 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. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+/*
+ * This is originally from:
+ * android-n-mr2-preview-1-303-gccec0f4c1
+ * libc/upstream-freebsd/lib/libc/stdlib/realpath.c
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)realpath.c 8.1 (Berkeley) 2/16/94";
+#endif /* LIBC_SCCS and not lint */
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "base/string_util.h"
+#include "SandboxBroker.h"
+
+// Original copy in, but not usable from here:
+// toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc
+static size_t my_strlcat(char* s1, const char* s2, size_t len) {
+ size_t pos1 = 0;
+
+ while (pos1 < len && s1[pos1] != '\0') pos1++;
+
+ if (pos1 == len) return pos1;
+
+ return pos1 + base::strlcpy(s1 + pos1, s2, len - pos1);
+}
+
+namespace mozilla {
+
+/*
+ * Original: realpath
+ * Find the real name of path, by removing all ".", ".." and symlink
+ * components. Returns (resolved) on success, or (NULL) on failure,
+ * in which case the path which caused trouble is left in (resolved).
+ * Changes:
+ * Resolve relative paths, but don't allow backing out of a symlink
+ * target. Fail with permission error if any dir is writable.
+ */
+char* SandboxBroker::SymlinkPath(const Policy* policy,
+ const char* __restrict path,
+ char* __restrict resolved, int* perms) {
+ struct stat sb;
+ char *p, *q, *s;
+ size_t left_len, resolved_len, backup_allowed;
+ unsigned symlinks;
+ int m, slen;
+ char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
+
+ if (*perms) {
+ *perms = 0;
+ }
+ if (path == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ if (path[0] == '\0') {
+ errno = ENOENT;
+ return (NULL);
+ }
+ if (resolved == NULL) {
+ resolved = (char*)malloc(PATH_MAX);
+ if (resolved == NULL) return (NULL);
+ m = 1;
+ } else
+ m = 0;
+ symlinks = 0;
+ backup_allowed = PATH_MAX;
+ if (path[0] == '/') {
+ resolved[0] = '/';
+ resolved[1] = '\0';
+ if (path[1] == '\0') return (resolved);
+ resolved_len = 1;
+ left_len = base::strlcpy(left, path + 1, sizeof(left));
+ } else {
+ if (getcwd(resolved, PATH_MAX) == NULL) {
+ if (m)
+ free(resolved);
+ else {
+ resolved[0] = '.';
+ resolved[1] = '\0';
+ }
+ return (NULL);
+ }
+ resolved_len = strlen(resolved);
+ left_len = base::strlcpy(left, path, sizeof(left));
+ }
+ if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
+ if (m) free(resolved);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+
+ /*
+ * Iterate over path components in `left'.
+ */
+ while (left_len != 0) {
+ /*
+ * Extract the next path component and adjust `left'
+ * and its length.
+ */
+ p = strchr(left, '/');
+ s = p ? p : left + left_len;
+ if (s - left >= (ssize_t)sizeof(next_token)) {
+ if (m) free(resolved);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ memcpy(next_token, left, s - left);
+ next_token[s - left] = '\0';
+ left_len -= s - left;
+ if (p != NULL) memmove(left, s + 1, left_len + 1);
+ if (resolved[resolved_len - 1] != '/') {
+ if (resolved_len + 1 >= PATH_MAX) {
+ if (m) free(resolved);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ resolved[resolved_len++] = '/';
+ resolved[resolved_len] = '\0';
+ }
+ if (next_token[0] == '\0') {
+ /* Handle consequential slashes. */
+ continue;
+ } else if (strcmp(next_token, ".") == 0)
+ continue;
+ else if (strcmp(next_token, "..") == 0) {
+ /*
+ * Strip the last path component except when we have
+ * single "/"
+ */
+ if (resolved_len > 1) {
+ if (backup_allowed > 0) {
+ resolved[resolved_len - 1] = '\0';
+ q = strrchr(resolved, '/') + 1;
+ *q = '\0';
+ resolved_len = q - resolved;
+ backup_allowed--;
+ } else {
+ // Backing out past a symlink target.
+ // We don't allow this, because it can eliminate
+ // permissions we accumulated while descending.
+ if (m) free(resolved);
+ errno = EPERM;
+ return (NULL);
+ }
+ }
+ continue;
+ }
+
+ /*
+ * Append the next path component and lstat() it.
+ */
+ resolved_len = my_strlcat(resolved, next_token, PATH_MAX);
+ backup_allowed++;
+ if (resolved_len >= PATH_MAX) {
+ if (m) free(resolved);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ if (lstat(resolved, &sb) != 0) {
+ if (m) free(resolved);
+ return (NULL);
+ }
+ if (S_ISLNK(sb.st_mode)) {
+ if (symlinks++ > MAXSYMLINKS) {
+ if (m) free(resolved);
+ errno = ELOOP;
+ return (NULL);
+ }
+ /* Our changes start here:
+ * It's a symlink, check for write permissions on the path where
+ * it sits in, in which case we won't resolve and just error out. */
+ int link_path_perms = policy->Lookup(resolved);
+ if (link_path_perms & MAY_WRITE) {
+ if (m) free(resolved);
+ errno = EPERM;
+ return (NULL);
+ } else {
+ /* Accumulate permissions so far */
+ *perms |= link_path_perms;
+ }
+ /* Original symlink lookup code */
+ slen = readlink(resolved, symlink, sizeof(symlink) - 1);
+ if (slen < 0) {
+ if (m) free(resolved);
+ return (NULL);
+ }
+ symlink[slen] = '\0';
+ if (symlink[0] == '/') {
+ resolved[1] = 0;
+ resolved_len = 1;
+ } else if (resolved_len > 1) {
+ /* Strip the last path component. */
+ resolved[resolved_len - 1] = '\0';
+ q = strrchr(resolved, '/') + 1;
+ *q = '\0';
+ resolved_len = q - resolved;
+ }
+
+ /*
+ * If there are any path components left, then
+ * append them to symlink. The result is placed
+ * in `left'.
+ */
+ if (p != NULL) {
+ if (symlink[slen - 1] != '/') {
+ if (slen + 1 >= (ssize_t)sizeof(symlink)) {
+ if (m) free(resolved);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ symlink[slen] = '/';
+ symlink[slen + 1] = 0;
+ }
+ left_len = my_strlcat(symlink, left, sizeof(symlink));
+ if (left_len >= sizeof(left)) {
+ if (m) free(resolved);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+ }
+ left_len = base::strlcpy(left, symlink, sizeof(left));
+ backup_allowed = 0;
+ } else if (!S_ISDIR(sb.st_mode) && p != NULL) {
+ if (m) free(resolved);
+ errno = ENOTDIR;
+ return (NULL);
+ }
+ }
+
+ /*
+ * Remove trailing slash except when the resolved pathname
+ * is a single "/".
+ */
+ if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
+ resolved[resolved_len - 1] = '\0';
+
+ /* Accumulate permissions. */
+ *perms |= policy->Lookup(resolved);
+
+ return (resolved);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/broker/SandboxBrokerUtils.h b/security/sandbox/linux/broker/SandboxBrokerUtils.h
new file mode 100644
index 0000000000..89b028bece
--- /dev/null
+++ b/security/sandbox/linux/broker/SandboxBrokerUtils.h
@@ -0,0 +1,32 @@
+/* -*- 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/. */
+#ifndef mozilla_SandboxBrokerUtils_h
+#define mozilla_SandboxBrokerUtils_h
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+// On 32-bit Linux, stat calls are translated by libc into stat64
+// calls. We'll intercept those and handle them in the stat functions
+// but must be sure to use the right structure layout.
+
+#if defined(__NR_stat64) || defined(__NR_fstatat64)
+typedef struct stat64 statstruct;
+# define statsyscall stat64
+# define lstatsyscall lstat64
+# define fstatsyscall fstat64
+#elif defined(__NR_stat) || defined(__NR_newfstatat)
+typedef struct stat statstruct;
+# define statsyscall stat
+# define lstatsyscall lstat
+# define fstatsyscall fstat
+#else
+# error Missing stat syscall include.
+#endif
+
+#endif // mozilla_SandboxBrokerUtils_h
diff --git a/security/sandbox/linux/broker/moz.build b/security/sandbox/linux/broker/moz.build
new file mode 100644
index 0000000000..4ad2dfcb3b
--- /dev/null
+++ b/security/sandbox/linux/broker/moz.build
@@ -0,0 +1,37 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "SandboxBroker.h",
+ "SandboxBrokerCommon.h",
+ "SandboxBrokerPolicyFactory.h",
+]
+
+UNIFIED_SOURCES += [
+ "SandboxBroker.cpp",
+ "SandboxBrokerCommon.cpp",
+ "SandboxBrokerPolicyFactory.cpp",
+ "SandboxBrokerRealpath.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/security/sandbox/linux", # SandboxLogging.h, SandboxInfo.h
+]
+
+# Need this for mozilla::ipc::FileDescriptor etc.
+include("/ipc/chromium/chromium-config.mozbuild")
+
+# Need this for safe_sprintf.h used by SandboxLogging.h,
+# but it has to be after ipc/chromium/src.
+LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["GLIB_CFLAGS"]
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/linux/glue/SandboxCrash.cpp b/security/sandbox/linux/glue/SandboxCrash.cpp
new file mode 100644
index 0000000000..9942925dbb
--- /dev/null
+++ b/security/sandbox/linux/glue/SandboxCrash.cpp
@@ -0,0 +1,118 @@
+/* -*- 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 file needs to be linked into libxul, so it can access the JS
+// stack and the crash reporter. Everything else in this directory
+// should be able to be linked into its own shared library, in order
+// to be able to isolate sandbox/chromium from ipc/chromium.
+
+#include "SandboxInternal.h"
+#include "SandboxLogging.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "mozilla/StackWalk.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Exceptions.h"
+#include "nsContentUtils.h"
+#include "nsExceptionHandler.h"
+#include "nsIException.h" // for nsIStackFrame
+#include "nsString.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+// Log JS stack info in the same place as the sandbox violation
+// message. Useful in case the responsible code is JS and all we have
+// are logs and a minidump with the C++ stacks (e.g., on TBPL).
+static void SandboxLogJSStack(void) {
+ if (!NS_IsMainThread()) {
+ // This might be a worker thread... or it might be a non-JS
+ // thread, or a non-NSPR thread. There's isn't a good API for
+ // dealing with this, yet.
+ return;
+ }
+ if (!nsContentUtils::XPConnect()) {
+ // There is no content (e.g., the process is a media plugin), in
+ // which case this will probably crash and definitely not work.
+ return;
+ }
+ nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
+ // If we got a stack, we must have a current JSContext. This is icky. :(
+ // Would be better if GetCurrentJSStack() handed out the JSContext it ended up
+ // using or something.
+ JSContext* cx = frame ? nsContentUtils::GetCurrentJSContext() : nullptr;
+ for (int i = 0; frame != nullptr; ++i) {
+ nsAutoString fileName, funName;
+ int32_t lineNumber;
+
+ // Don't stop unwinding if an attribute can't be read.
+ fileName.SetIsVoid(true);
+ frame->GetFilename(cx, fileName);
+ lineNumber = frame->GetLineNumber(cx);
+ funName.SetIsVoid(true);
+ frame->GetName(cx, funName);
+
+ if (!funName.IsVoid() || !fileName.IsVoid()) {
+ SANDBOX_LOG("JS frame %d: %s %s line %d", i,
+ funName.IsVoid() ? "(anonymous)"
+ : NS_ConvertUTF16toUTF8(funName).get(),
+ fileName.IsVoid() ? "(no file)"
+ : NS_ConvertUTF16toUTF8(fileName).get(),
+ lineNumber);
+ }
+
+ frame = frame->GetCaller(cx);
+ }
+}
+
+static void SandboxPrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP,
+ void* aClosure) {
+ char buf[1024];
+ MozCodeAddressDetails details;
+
+ MozDescribeCodeAddress(aPC, &details);
+ MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+ SANDBOX_LOG("frame %s", buf);
+}
+
+static void SandboxLogCStack(const void* aFirstFramePC) {
+ // Warning: this might not print any stack frames. MozStackWalk
+ // can't walk past the signal trampoline on ARM (bug 968531), and
+ // x86 frame pointer walking may or may not work (bug 1082276).
+
+ MozStackWalk(SandboxPrintStackFrame, aFirstFramePC, /* max */ 0, nullptr);
+ SANDBOX_LOG("end of stack.");
+}
+
+static void SandboxCrash(int nr, siginfo_t* info, void* void_context,
+ const void* aFirstFramePC) {
+ pid_t pid = getpid(), tid = syscall(__NR_gettid);
+ bool dumped = CrashReporter::WriteMinidumpForSigInfo(nr, info, void_context);
+
+ if (!dumped) {
+ SANDBOX_LOG(
+ "crash reporter is disabled (or failed);"
+ " trying stack trace:");
+ SandboxLogCStack(aFirstFramePC);
+ }
+
+ // Do this last, in case it crashes or deadlocks.
+ SandboxLogJSStack();
+
+ // Try to reraise, so the parent sees that this process crashed.
+ // (If tgkill is forbidden, then seccomp will raise SIGSYS, which
+ // also accomplishes that goal.)
+ signal(SIGSYS, SIG_DFL);
+ syscall(__NR_tgkill, pid, tid, nr);
+}
+
+static void __attribute__((constructor)) SandboxSetCrashFunc() {
+ gSandboxCrashFunc = SandboxCrash;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/glue/SandboxPrefBridge.cpp b/security/sandbox/linux/glue/SandboxPrefBridge.cpp
new file mode 100644
index 0000000000..9782e58817
--- /dev/null
+++ b/security/sandbox/linux/glue/SandboxPrefBridge.cpp
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+
+#include "Sandbox.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/SandboxSettings.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h" // for FILE_REMOTE_TYPE
+
+namespace mozilla {
+
+/* static */ ContentProcessSandboxParams
+ContentProcessSandboxParams::ForThisProcess(
+ const Maybe<ipc::FileDescriptor>& aBroker) {
+ ContentProcessSandboxParams params;
+ params.mLevel = GetEffectiveContentSandboxLevel();
+
+ if (aBroker.isSome()) {
+ auto fd = aBroker.value().ClonePlatformHandle();
+ params.mBrokerFd = fd.release();
+ // brokerFd < 0 means to allow direct filesystem access, so
+ // make absolutely sure that doesn't happen if the parent
+ // didn't intend it.
+ MOZ_RELEASE_ASSERT(params.mBrokerFd >= 0);
+ }
+ // (Otherwise, mBrokerFd will remain -1 from the default ctor.)
+
+ auto* cc = dom::ContentChild::GetSingleton();
+ params.mFileProcess = cc->GetRemoteType() == FILE_REMOTE_TYPE;
+
+ nsAutoCString extraSyscalls;
+ nsresult rv = Preferences::GetCString(
+ "security.sandbox.content.syscall_whitelist", extraSyscalls);
+ if (NS_SUCCEEDED(rv)) {
+ for (const nsACString& callNrString : extraSyscalls.Split(',')) {
+ int callNr = PromiseFlatCString(callNrString).ToInteger(&rv);
+ if (NS_SUCCEEDED(rv)) {
+ params.mSyscallWhitelist.push_back(callNr);
+ }
+ }
+ }
+
+ return params;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/glue/moz.build b/security/sandbox/linux/glue/moz.build
new file mode 100644
index 0000000000..e44a561d69
--- /dev/null
+++ b/security/sandbox/linux/glue/moz.build
@@ -0,0 +1,35 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ "../SandboxLogging.cpp",
+ "SandboxCrash.cpp",
+ "SandboxPrefBridge.cpp",
+]
+
+SOURCES += [
+ "../../chromium/base/strings/safe_sprintf.cc",
+]
+
+# Avoid Chromium logging dependency, because this is going into
+# libxul. See also the comment in SandboxLogging.h.
+SOURCES["../../chromium/base/strings/safe_sprintf.cc"].flags += ["-DNDEBUG"]
+
+# Need this for mozilla::ipc::FileDescriptor etc.
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ # Need this for safe_sprintf.h used by SandboxLogging.h,
+ # but it has to be after ipc/chromium/src.
+ "/security/sandbox/chromium",
+ "/security/sandbox/linux",
+]
+
+USE_LIBS += [
+ "mozsandbox",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/linux/gtest/TestBroker.cpp b/security/sandbox/linux/gtest/TestBroker.cpp
new file mode 100644
index 0000000000..07fcaa889a
--- /dev/null
+++ b/security/sandbox/linux/gtest/TestBroker.cpp
@@ -0,0 +1,689 @@
+/* -*- 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/. */
+
+#include "gtest/gtest.h"
+
+#include "broker/SandboxBroker.h"
+#include "broker/SandboxBrokerUtils.h"
+#include "SandboxBrokerClient.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <semaphore.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mozilla/Atomics.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/FileDescriptor.h"
+
+namespace mozilla {
+
+class SandboxBrokerTest : public ::testing::Test {
+ static const int MAY_ACCESS = SandboxBroker::MAY_ACCESS;
+ static const int MAY_READ = SandboxBroker::MAY_READ;
+ static const int MAY_WRITE = SandboxBroker::MAY_WRITE;
+ static const int MAY_CREATE = SandboxBroker::MAY_CREATE;
+ static const auto AddAlways = SandboxBroker::Policy::AddAlways;
+
+ UniquePtr<SandboxBroker> mServer;
+ UniquePtr<SandboxBrokerClient> mClient;
+
+ UniquePtr<const SandboxBroker::Policy> GetPolicy() const;
+
+ template <class C, void (C::*Main)()>
+ static void* ThreadMain(void* arg) {
+ (static_cast<C*>(arg)->*Main)();
+ return nullptr;
+ }
+
+ protected:
+ int Open(const char* aPath, int aFlags) {
+ return mClient->Open(aPath, aFlags);
+ }
+ int Access(const char* aPath, int aMode) {
+ return mClient->Access(aPath, aMode);
+ }
+ int Stat(const char* aPath, statstruct* aStat) {
+ return mClient->Stat(aPath, aStat);
+ }
+ int LStat(const char* aPath, statstruct* aStat) {
+ return mClient->LStat(aPath, aStat);
+ }
+ int Chmod(const char* aPath, int aMode) {
+ return mClient->Chmod(aPath, aMode);
+ }
+ int Link(const char* aPath, const char* bPath) {
+ return mClient->Link(aPath, bPath);
+ }
+ int Mkdir(const char* aPath, int aMode) {
+ return mClient->Mkdir(aPath, aMode);
+ }
+ int Symlink(const char* aPath, const char* bPath) {
+ return mClient->Symlink(aPath, bPath);
+ }
+ int Rename(const char* aPath, const char* bPath) {
+ return mClient->Rename(aPath, bPath);
+ }
+ int Rmdir(const char* aPath) { return mClient->Rmdir(aPath); }
+ int Unlink(const char* aPath) { return mClient->Unlink(aPath); }
+ ssize_t Readlink(const char* aPath, char* aBuff, size_t aSize) {
+ return mClient->Readlink(aPath, aBuff, aSize);
+ }
+
+ void SetUp() override {
+ ipc::FileDescriptor fd;
+
+ mServer = SandboxBroker::Create(GetPolicy(), getpid(), fd);
+ ASSERT_NE(mServer, nullptr);
+ ASSERT_TRUE(fd.IsValid());
+ auto rawFD = fd.TakePlatformHandle();
+ mClient.reset(new SandboxBrokerClient(rawFD.release()));
+ }
+
+ template <class C, void (C::*Main)()>
+ void StartThread(pthread_t* aThread) {
+ ASSERT_EQ(0, pthread_create(aThread, nullptr, ThreadMain<C, Main>,
+ static_cast<C*>(this)));
+ }
+
+ template <class C, void (C::*Main)()>
+ void RunOnManyThreads() {
+ static const int kNumThreads = 5;
+ pthread_t threads[kNumThreads];
+ for (pthread_t& thread : threads) {
+ StartThread<C, Main>(&thread);
+ }
+ for (pthread_t thread : threads) {
+ void* retval;
+ ASSERT_EQ(pthread_join(thread, &retval), 0);
+ ASSERT_EQ(retval, static_cast<void*>(nullptr));
+ }
+ }
+
+ public:
+ void MultiThreadOpenWorker();
+ void MultiThreadStatWorker();
+};
+
+UniquePtr<const SandboxBroker::Policy> SandboxBrokerTest::GetPolicy() const {
+ UniquePtr<SandboxBroker::Policy> policy(new SandboxBroker::Policy());
+
+ policy->AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways);
+ policy->AddPath(MAY_READ, "/dev/zero", AddAlways);
+ policy->AddPath(MAY_READ, "/var/empty/qwertyuiop", AddAlways);
+ policy->AddPath(MAY_ACCESS, "/proc/self",
+ AddAlways); // Warning: Linux-specific.
+ policy->AddPath(MAY_READ | MAY_WRITE, "/tmp", AddAlways);
+ policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublu", AddAlways);
+ policy->AddPath(MAY_READ | MAY_WRITE | MAY_CREATE, "/tmp/blublublu",
+ AddAlways);
+ // This should be non-writable by the user running the test:
+ policy->AddPath(MAY_READ | MAY_WRITE, "/etc", AddAlways);
+
+ return std::move(policy);
+}
+
+TEST_F(SandboxBrokerTest, OpenForRead) {
+ int fd;
+
+ fd = Open("/dev/null", O_RDONLY);
+ ASSERT_GE(fd, 0) << "Opening /dev/null failed.";
+ close(fd);
+ fd = Open("/dev/zero", O_RDONLY);
+ ASSERT_GE(fd, 0) << "Opening /dev/zero failed.";
+ close(fd);
+ fd = Open("/var/empty/qwertyuiop", O_RDONLY);
+ EXPECT_EQ(-ENOENT, fd) << "Opening allowed but nonexistent file succeeded.";
+ fd = Open("/proc/self", O_RDONLY);
+ EXPECT_EQ(-EACCES, fd) << "Opening stat-only file for read succeeded.";
+ fd = Open("/proc/self/stat", O_RDONLY);
+ EXPECT_EQ(-EACCES, fd) << "Opening disallowed file succeeded.";
+}
+
+TEST_F(SandboxBrokerTest, OpenForWrite) {
+ int fd;
+
+ fd = Open("/dev/null", O_WRONLY);
+ ASSERT_GE(fd, 0) << "Opening /dev/null write-only failed.";
+ close(fd);
+ fd = Open("/dev/null", O_RDWR);
+ ASSERT_GE(fd, 0) << "Opening /dev/null read/write failed.";
+ close(fd);
+ fd = Open("/dev/zero", O_WRONLY);
+ ASSERT_EQ(-EACCES, fd)
+ << "Opening read-only-by-policy file write-only succeeded.";
+ fd = Open("/dev/zero", O_RDWR);
+ ASSERT_EQ(-EACCES, fd)
+ << "Opening read-only-by-policy file read/write succeeded.";
+}
+
+TEST_F(SandboxBrokerTest, SimpleRead) {
+ int fd;
+ char c;
+
+ fd = Open("/dev/null", O_RDONLY);
+ ASSERT_GE(fd, 0);
+ EXPECT_EQ(0, read(fd, &c, 1));
+ close(fd);
+ fd = Open("/dev/zero", O_RDONLY);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(1, read(fd, &c, 1));
+ EXPECT_EQ(c, '\0');
+}
+
+TEST_F(SandboxBrokerTest, BadFlags) {
+ int fd;
+
+ fd = Open("/dev/null", O_RDWR | O_ASYNC);
+ EXPECT_EQ(-EACCES, fd) << "O_ASYNC is banned.";
+
+ fd = Open("/dev/null", O_RDWR | 0x40000000);
+ EXPECT_EQ(-EACCES, fd) << "Unknown flag 0x40000000 is banned.";
+}
+
+TEST_F(SandboxBrokerTest, Access) {
+ EXPECT_EQ(0, Access("/dev/null", F_OK));
+ EXPECT_EQ(0, Access("/dev/null", R_OK));
+ EXPECT_EQ(0, Access("/dev/null", W_OK));
+ EXPECT_EQ(0, Access("/dev/null", R_OK | W_OK));
+ EXPECT_EQ(-EACCES, Access("/dev/null", X_OK));
+ EXPECT_EQ(-EACCES, Access("/dev/null", R_OK | X_OK));
+
+ EXPECT_EQ(0, Access("/dev/zero", R_OK));
+ EXPECT_EQ(-EACCES, Access("/dev/zero", W_OK));
+ EXPECT_EQ(-EACCES, Access("/dev/zero", R_OK | W_OK));
+
+ EXPECT_EQ(-ENOENT, Access("/var/empty/qwertyuiop", R_OK));
+ EXPECT_EQ(-EACCES, Access("/var/empty/qwertyuiop", W_OK));
+
+ EXPECT_EQ(0, Access("/proc/self", F_OK));
+ EXPECT_EQ(-EACCES, Access("/proc/self", R_OK));
+
+ EXPECT_EQ(-EACCES, Access("/proc/self/stat", F_OK));
+
+ EXPECT_EQ(0, Access("/tmp", X_OK));
+ EXPECT_EQ(0, Access("/tmp", R_OK | X_OK));
+ EXPECT_EQ(0, Access("/tmp", R_OK | W_OK | X_OK));
+ EXPECT_EQ(0, Access("/proc/self", X_OK));
+
+ EXPECT_EQ(0, Access("/etc", R_OK | X_OK));
+ EXPECT_EQ(-EACCES, Access("/etc", W_OK));
+}
+
+TEST_F(SandboxBrokerTest, Stat) {
+ statstruct realStat, brokeredStat;
+ ASSERT_EQ(0, statsyscall("/dev/null", &realStat)) << "Shouldn't ever fail!";
+ EXPECT_EQ(0, Stat("/dev/null", &brokeredStat));
+ EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
+ EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev);
+
+#if defined(__clang__) || defined(__GNUC__)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wnonnull"
+#endif
+ EXPECT_EQ(-1, statsyscall(nullptr, &realStat));
+ EXPECT_EQ(errno, EFAULT);
+
+ EXPECT_EQ(-EFAULT, Stat(nullptr, &brokeredStat));
+#if defined(__clang__) || defined(__GNUC__)
+# pragma GCC diagnostic pop
+#endif
+
+ EXPECT_EQ(-ENOENT, Stat("/var/empty/qwertyuiop", &brokeredStat));
+ EXPECT_EQ(-EACCES, Stat("/dev", &brokeredStat));
+
+ EXPECT_EQ(0, Stat("/proc/self", &brokeredStat));
+ EXPECT_TRUE(S_ISDIR(brokeredStat.st_mode));
+}
+
+TEST_F(SandboxBrokerTest, LStat) {
+ statstruct realStat, brokeredStat;
+ ASSERT_EQ(0, lstatsyscall("/dev/null", &realStat));
+ EXPECT_EQ(0, LStat("/dev/null", &brokeredStat));
+ EXPECT_EQ(realStat.st_ino, brokeredStat.st_ino);
+ EXPECT_EQ(realStat.st_rdev, brokeredStat.st_rdev);
+
+ EXPECT_EQ(-ENOENT, LStat("/var/empty/qwertyuiop", &brokeredStat));
+ EXPECT_EQ(-EACCES, LStat("/dev", &brokeredStat));
+
+ EXPECT_EQ(0, LStat("/proc/self", &brokeredStat));
+ EXPECT_TRUE(S_ISLNK(brokeredStat.st_mode));
+}
+
+static void PrePostTestCleanup(void) {
+ unlink("/tmp/blublu");
+ rmdir("/tmp/blublu");
+ unlink("/tmp/nope");
+ rmdir("/tmp/nope");
+ unlink("/tmp/blublublu");
+ rmdir("/tmp/blublublu");
+}
+
+TEST_F(SandboxBrokerTest, Chmod) {
+ PrePostTestCleanup();
+
+ int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
+ ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
+ close(fd);
+ // Set read only. SandboxBroker enforces 0600 mode flags.
+ ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR));
+ EXPECT_EQ(-EACCES, Access("/tmp/blublu", W_OK));
+ statstruct realStat;
+ EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
+ EXPECT_EQ((mode_t)S_IRUSR, realStat.st_mode & 0777);
+
+ ASSERT_EQ(0, Chmod("/tmp/blublu", S_IRUSR | S_IWUSR));
+ EXPECT_EQ(0, statsyscall("/tmp/blublu", &realStat));
+ EXPECT_EQ((mode_t)(S_IRUSR | S_IWUSR), realStat.st_mode & 0777);
+ EXPECT_EQ(0, unlink("/tmp/blublu"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Link) {
+ PrePostTestCleanup();
+
+ int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
+ ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
+ close(fd);
+ ASSERT_EQ(0, Link("/tmp/blublu", "/tmp/blublublu"));
+ EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
+ // Not whitelisted target path
+ EXPECT_EQ(-EACCES, Link("/tmp/blublu", "/tmp/nope"));
+ EXPECT_EQ(0, unlink("/tmp/blublublu"));
+ EXPECT_EQ(0, unlink("/tmp/blublu"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Symlink) {
+ PrePostTestCleanup();
+
+ int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
+ ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
+ close(fd);
+ ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu"));
+ EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
+ statstruct aStat;
+ ASSERT_EQ(0, lstatsyscall("/tmp/blublublu", &aStat));
+ EXPECT_EQ((mode_t)S_IFLNK, aStat.st_mode & S_IFMT);
+ // Not whitelisted target path
+ EXPECT_EQ(-EACCES, Symlink("/tmp/blublu", "/tmp/nope"));
+ EXPECT_EQ(0, unlink("/tmp/blublublu"));
+ EXPECT_EQ(0, unlink("/tmp/blublu"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Mkdir) {
+ PrePostTestCleanup();
+
+ ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
+ << "Creating dir /tmp/blublu failed.";
+ EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
+ // Not whitelisted target path
+ EXPECT_EQ(-EACCES, Mkdir("/tmp/nope", 0600))
+ << "Creating dir without MAY_CREATE succeed.";
+ EXPECT_EQ(0, rmdir("/tmp/blublu"));
+ EXPECT_EQ(-EEXIST, Mkdir("/proc/self", 0600))
+ << "Creating uncreatable dir that already exists didn't fail correctly.";
+ EXPECT_EQ(-EEXIST, Mkdir("/dev/zero", 0600))
+ << "Creating uncreatable dir over preexisting file didn't fail "
+ "correctly.";
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Rename) {
+ PrePostTestCleanup();
+
+ ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
+ << "Creating dir /tmp/blublu failed.";
+ EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
+ ASSERT_EQ(0, Rename("/tmp/blublu", "/tmp/blublublu"));
+ EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
+ EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK));
+ // Not whitelisted target path
+ EXPECT_EQ(-EACCES, Rename("/tmp/blublublu", "/tmp/nope"))
+ << "Renaming dir without write access succeed.";
+ EXPECT_EQ(0, rmdir("/tmp/blublublu"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Rmdir) {
+ PrePostTestCleanup();
+
+ ASSERT_EQ(0, mkdir("/tmp/blublu", 0600))
+ << "Creating dir /tmp/blublu failed.";
+ EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
+ ASSERT_EQ(0, Rmdir("/tmp/blublu"));
+ EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK));
+ // Bypass sandbox to create a non-deletable dir
+ ASSERT_EQ(0, mkdir("/tmp/nope", 0600));
+ EXPECT_EQ(-EACCES, Rmdir("/tmp/nope"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Unlink) {
+ PrePostTestCleanup();
+
+ int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
+ ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
+ close(fd);
+ EXPECT_EQ(0, Access("/tmp/blublu", F_OK));
+ EXPECT_EQ(0, Unlink("/tmp/blublu"));
+ EXPECT_EQ(-ENOENT, Access("/tmp/blublu", F_OK));
+ // Bypass sandbox to write a non-deletable file
+ fd = open("/tmp/nope", O_WRONLY | O_CREAT, 0600);
+ ASSERT_GE(fd, 0) << "Opening /tmp/nope for writing failed.";
+ close(fd);
+ EXPECT_EQ(-EACCES, Unlink("/tmp/nope"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, Readlink) {
+ PrePostTestCleanup();
+
+ int fd = Open("/tmp/blublu", O_WRONLY | O_CREAT);
+ ASSERT_GE(fd, 0) << "Opening /tmp/blublu for writing failed.";
+ close(fd);
+ ASSERT_EQ(0, Symlink("/tmp/blublu", "/tmp/blublublu"));
+ EXPECT_EQ(0, Access("/tmp/blublublu", F_OK));
+ char linkBuff[256];
+ EXPECT_EQ(11, Readlink("/tmp/blublublu", linkBuff, sizeof(linkBuff)));
+ linkBuff[11] = '\0';
+ EXPECT_EQ(0, strcmp(linkBuff, "/tmp/blublu"));
+
+ PrePostTestCleanup();
+}
+
+TEST_F(SandboxBrokerTest, MultiThreadOpen) {
+ RunOnManyThreads<SandboxBrokerTest,
+ &SandboxBrokerTest::MultiThreadOpenWorker>();
+}
+void SandboxBrokerTest::MultiThreadOpenWorker() {
+ static const int kNumLoops = 10000;
+
+ for (int i = 1; i <= kNumLoops; ++i) {
+ int nullfd = Open("/dev/null", O_RDONLY);
+ int zerofd = Open("/dev/zero", O_RDONLY);
+ ASSERT_GE(nullfd, 0) << "Loop " << i << "/" << kNumLoops;
+ ASSERT_GE(zerofd, 0) << "Loop " << i << "/" << kNumLoops;
+ char c;
+ ASSERT_EQ(0, read(nullfd, &c, 1)) << "Loop " << i << "/" << kNumLoops;
+ ASSERT_EQ(1, read(zerofd, &c, 1)) << "Loop " << i << "/" << kNumLoops;
+ ASSERT_EQ('\0', c) << "Loop " << i << "/" << kNumLoops;
+ close(nullfd);
+ close(zerofd);
+ }
+}
+
+TEST_F(SandboxBrokerTest, MultiThreadStat) {
+ RunOnManyThreads<SandboxBrokerTest,
+ &SandboxBrokerTest::MultiThreadStatWorker>();
+}
+void SandboxBrokerTest::MultiThreadStatWorker() {
+ static const int kNumLoops = 7500;
+ statstruct nullStat, zeroStat, selfStat;
+ dev_t realNullDev, realZeroDev;
+ ino_t realSelfInode;
+
+ ASSERT_EQ(0, statsyscall("/dev/null", &nullStat)) << "Shouldn't ever fail!";
+ ASSERT_EQ(0, statsyscall("/dev/zero", &zeroStat)) << "Shouldn't ever fail!";
+ ASSERT_EQ(0, lstatsyscall("/proc/self", &selfStat)) << "Shouldn't ever fail!";
+ ASSERT_TRUE(S_ISLNK(selfStat.st_mode))
+ << "Shouldn't ever fail!";
+ realNullDev = nullStat.st_rdev;
+ realZeroDev = zeroStat.st_rdev;
+ realSelfInode = selfStat.st_ino;
+ for (int i = 1; i <= kNumLoops; ++i) {
+ ASSERT_EQ(0, Stat("/dev/null", &nullStat))
+ << "Loop " << i << "/" << kNumLoops;
+ ASSERT_EQ(0, Stat("/dev/zero", &zeroStat))
+ << "Loop " << i << "/" << kNumLoops;
+ ASSERT_EQ(0, LStat("/proc/self", &selfStat))
+ << "Loop " << i << "/" << kNumLoops;
+
+ ASSERT_EQ(realNullDev, nullStat.st_rdev)
+ << "Loop " << i << "/" << kNumLoops;
+ ASSERT_EQ(realZeroDev, zeroStat.st_rdev)
+ << "Loop " << i << "/" << kNumLoops;
+ ASSERT_TRUE(S_ISLNK(selfStat.st_mode))
+ << "Loop " << i << "/" << kNumLoops;
+ ASSERT_EQ(realSelfInode, selfStat.st_ino)
+ << "Loop " << i << "/" << kNumLoops;
+ }
+}
+
+#if 0
+class SandboxBrokerSigStress : public SandboxBrokerTest
+{
+ int mSigNum;
+ struct sigaction mOldAction;
+ Atomic<void*> mVoidPtr;
+
+ static void SigHandler(int aSigNum, siginfo_t* aSigInfo, void *aCtx) {
+ ASSERT_EQ(SI_QUEUE, aSigInfo->si_code);
+ SandboxBrokerSigStress* that =
+ static_cast<SandboxBrokerSigStress*>(aSigInfo->si_value.sival_ptr);
+ ASSERT_EQ(that->mSigNum, aSigNum);
+ that->DoSomething();
+ }
+
+protected:
+ Atomic<int> mTestIter;
+ sem_t mSemaphore;
+
+ void SignalThread(pthread_t aThread) {
+ union sigval sv;
+ sv.sival_ptr = this;
+ ASSERT_NE(0, mSigNum);
+ ASSERT_EQ(0, pthread_sigqueue(aThread, mSigNum, sv));
+ }
+
+ virtual void SetUp() {
+ ASSERT_EQ(0, sem_init(&mSemaphore, 0, 0));
+ mVoidPtr = nullptr;
+ mSigNum = 0;
+ for (int sigNum = SIGRTMIN; sigNum < SIGRTMAX; ++sigNum) {
+ ASSERT_EQ(0, sigaction(sigNum, nullptr, &mOldAction));
+ if ((mOldAction.sa_flags & SA_SIGINFO) == 0 &&
+ mOldAction.sa_handler == SIG_DFL) {
+ struct sigaction newAction;
+ PodZero(&newAction);
+ newAction.sa_flags = SA_SIGINFO;
+ newAction.sa_sigaction = SigHandler;
+ ASSERT_EQ(0, sigaction(sigNum, &newAction, nullptr));
+ mSigNum = sigNum;
+ break;
+ }
+ }
+ ASSERT_NE(mSigNum, 0);
+
+ SandboxBrokerTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ ASSERT_EQ(0, sem_destroy(&mSemaphore));
+ if (mSigNum != 0) {
+ ASSERT_EQ(0, sigaction(mSigNum, &mOldAction, nullptr));
+ }
+ if (mVoidPtr) {
+ free(mVoidPtr);
+ }
+ }
+
+ void DoSomething();
+
+public:
+ void MallocWorker();
+ void FreeWorker();
+};
+
+TEST_F(SandboxBrokerSigStress, StressTest)
+{
+ static const int kIters = 6250;
+ static const int kNsecPerIterPerIter = 4;
+ struct timespec delay = { 0, 0 };
+ pthread_t threads[2];
+
+ mTestIter = kIters;
+
+ StartThread<SandboxBrokerSigStress,
+ &SandboxBrokerSigStress::MallocWorker>(&threads[0]);
+ StartThread<SandboxBrokerSigStress,
+ &SandboxBrokerSigStress::FreeWorker>(&threads[1]);
+
+ for (int i = kIters; i > 0; --i) {
+ SignalThread(threads[i % 2]);
+ while (sem_wait(&mSemaphore) == -1 && errno == EINTR)
+ /* retry */;
+ ASSERT_EQ(i - 1, mTestIter);
+ delay.tv_nsec += kNsecPerIterPerIter;
+ struct timespec req = delay, rem;
+ while (nanosleep(&req, &rem) == -1 && errno == EINTR) {
+ req = rem;
+ }
+ }
+ void *retval;
+ ASSERT_EQ(0, pthread_join(threads[0], &retval));
+ ASSERT_EQ(nullptr, retval);
+ ASSERT_EQ(0, pthread_join(threads[1], &retval));
+ ASSERT_EQ(nullptr, retval);
+}
+
+void
+SandboxBrokerSigStress::MallocWorker()
+{
+ static const size_t kSize = 64;
+
+ void* mem = malloc(kSize);
+ while (mTestIter > 0) {
+ ASSERT_NE(mem, mVoidPtr);
+ mem = mVoidPtr.exchange(mem);
+ if (mem) {
+ sched_yield();
+ } else {
+ mem = malloc(kSize);
+ }
+ }
+ if (mem) {
+ free(mem);
+ }
+}
+
+void
+SandboxBrokerSigStress::FreeWorker()
+{
+ void *mem = nullptr;
+ while (mTestIter > 0) {
+ mem = mVoidPtr.exchange(mem);
+ if (mem) {
+ free(mem);
+ mem = nullptr;
+ } else {
+ sched_yield();
+ }
+ }
+}
+
+void
+SandboxBrokerSigStress::DoSomething()
+{
+ int fd;
+ char c;
+ struct stat st;
+
+ //fprintf(stderr, "Don't try this at home: %d\n", static_cast<int>(mTestIter));
+ switch (mTestIter % 5) {
+ case 0:
+ fd = Open("/dev/null", O_RDWR);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(0, read(fd, &c, 1));
+ close(fd);
+ break;
+ case 1:
+ fd = Open("/dev/zero", O_RDONLY);
+ ASSERT_GE(fd, 0);
+ ASSERT_EQ(1, read(fd, &c, 1));
+ ASSERT_EQ('\0', c);
+ close(fd);
+ break;
+ case 2:
+ ASSERT_EQ(0, Access("/dev/null", W_OK));
+ break;
+ case 3:
+ ASSERT_EQ(0, Stat("/proc/self", &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+ break;
+ case 4:
+ ASSERT_EQ(0, LStat("/proc/self", &st));
+ ASSERT_TRUE(S_ISLNK(st.st_mode));
+ break;
+ }
+ mTestIter--;
+ sem_post(&mSemaphore);
+}
+#endif
+
+// Check for fd leaks when creating/destroying a broker instance (bug
+// 1719391).
+//
+// (This uses a different test group because it doesn't use the
+// fixture class, and gtest doesn't allow mixing TEST and TEST_F in
+// the same group.)
+TEST(SandboxBrokerMisc, LeakCheck)
+{
+ // If this value is increased in the future, check that it won't
+ // cause the test to take an excessive amount of time:
+ static constexpr size_t kCycles = 4096;
+ struct rlimit oldLimit;
+ bool changedLimit = false;
+
+ // At the time of this writing, we raise the fd soft limit to 4096
+ // (or to the hard limit, if less than that), but we don't lower it
+ // if it was higher than 4096. To allow for that case, or if
+ // Gecko's preferred limit changes, the limit is reduced while this
+ // test is running:
+ ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &oldLimit), 0) << strerror(errno);
+ if (oldLimit.rlim_cur == RLIM_INFINITY ||
+ oldLimit.rlim_cur > static_cast<rlim_t>(kCycles)) {
+ struct rlimit newLimit = oldLimit;
+ newLimit.rlim_cur = static_cast<rlim_t>(kCycles);
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &newLimit), 0) << strerror(errno);
+ changedLimit = true;
+ }
+
+ pid_t pid = getpid();
+ for (size_t i = 0; i < kCycles; ++i) {
+ auto policy = MakeUnique<SandboxBroker::Policy>();
+ // Currently nothing in `Create` tries to check for or
+ // special-case an empty policy, but just in case:
+ policy->AddPath(SandboxBroker::MAY_READ, "/dev/null",
+ SandboxBroker::Policy::AddAlways);
+ ipc::FileDescriptor fd;
+ auto broker = SandboxBroker::Create(std::move(policy), pid, fd);
+ ASSERT_TRUE(broker);
+ ASSERT_TRUE(fd.IsValid());
+ }
+
+ if (changedLimit) {
+ ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &oldLimit), 0) << strerror(errno);
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/gtest/TestBrokerPolicy.cpp b/security/sandbox/linux/gtest/TestBrokerPolicy.cpp
new file mode 100644
index 0000000000..d881e6d290
--- /dev/null
+++ b/security/sandbox/linux/gtest/TestBrokerPolicy.cpp
@@ -0,0 +1,95 @@
+/* -*- 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/. */
+
+#include "gtest/gtest.h"
+
+#include "broker/SandboxBroker.h"
+
+namespace mozilla {
+
+static const int MAY_ACCESS = SandboxBroker::MAY_ACCESS;
+static const int MAY_READ = SandboxBroker::MAY_READ;
+static const int MAY_WRITE = SandboxBroker::MAY_WRITE;
+// static const int MAY_CREATE = SandboxBroker::MAY_CREATE;
+// static const int RECURSIVE = SandboxBroker::RECURSIVE;
+static const auto AddAlways = SandboxBroker::Policy::AddAlways;
+
+TEST(SandboxBrokerPolicyLookup, Simple)
+{
+ SandboxBroker::Policy p;
+ p.AddPath(MAY_READ, "/dev/urandom", AddAlways);
+
+ EXPECT_NE(0, p.Lookup("/dev/urandom")) << "Added path not found.";
+ EXPECT_EQ(MAY_ACCESS | MAY_READ, p.Lookup("/dev/urandom"))
+ << "Added path found with wrong perms.";
+ EXPECT_EQ(0, p.Lookup("/etc/passwd")) << "Non-added path was found.";
+}
+
+TEST(SandboxBrokerPolicyLookup, CopyCtor)
+{
+ SandboxBroker::Policy psrc;
+ psrc.AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways);
+ SandboxBroker::Policy pdst(psrc);
+ psrc.AddPath(MAY_READ, "/dev/zero", AddAlways);
+ pdst.AddPath(MAY_READ, "/dev/urandom", AddAlways);
+
+ EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/null"))
+ << "Common path absent in copy source.";
+ EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, pdst.Lookup("/dev/null"))
+ << "Common path absent in copy destination.";
+
+ EXPECT_EQ(MAY_ACCESS | MAY_READ, psrc.Lookup("/dev/zero"))
+ << "Source-only path is absent.";
+ EXPECT_EQ(0, pdst.Lookup("/dev/zero"))
+ << "Source-only path is present in copy destination.";
+
+ EXPECT_EQ(0, psrc.Lookup("/dev/urandom"))
+ << "Destination-only path is present in copy source.";
+ EXPECT_EQ(MAY_ACCESS | MAY_READ, pdst.Lookup("/dev/urandom"))
+ << "Destination-only path is absent.";
+
+ EXPECT_EQ(0, psrc.Lookup("/etc/passwd"))
+ << "Non-added path is present in copy source.";
+ EXPECT_EQ(0, pdst.Lookup("/etc/passwd"))
+ << "Non-added path is present in copy source.";
+}
+
+TEST(SandboxBrokerPolicyLookup, Recursive)
+{
+ SandboxBroker::Policy psrc;
+ psrc.AddPath(MAY_READ | MAY_WRITE, "/dev/null", AddAlways);
+ psrc.AddPath(MAY_READ, "/dev/zero", AddAlways);
+ psrc.AddPath(MAY_READ, "/dev/urandom", AddAlways);
+
+ EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/null"))
+ << "Basic path is present.";
+ EXPECT_EQ(MAY_ACCESS | MAY_READ, psrc.Lookup("/dev/zero"))
+ << "Basic path has no extra flags";
+
+ psrc.AddDir(MAY_READ | MAY_WRITE, "/dev/");
+
+ EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/random"))
+ << "Permission via recursive dir.";
+ EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/sd/0"))
+ << "Permission via recursive dir, nested deeper";
+ EXPECT_EQ(0, psrc.Lookup("/dev/sd/0/")) << "Invalid path format.";
+ EXPECT_EQ(0, psrc.Lookup("/usr/dev/sd")) << "Match must be a prefix.";
+
+ psrc.AddDir(MAY_READ, "/dev/sd/");
+ EXPECT_EQ(MAY_ACCESS | MAY_READ | MAY_WRITE, psrc.Lookup("/dev/sd/0"))
+ << "Extra permissions from parent path granted.";
+ EXPECT_EQ(0, psrc.Lookup("/dev/..")) << "Refuse attempted subdir escape.";
+
+ psrc.AddDir(MAY_READ, "/tmp");
+ EXPECT_EQ(MAY_ACCESS | MAY_READ, psrc.Lookup("/tmp/good/a"))
+ << "Check whether dir add with no trailing / was sucessful.";
+ EXPECT_EQ(0, psrc.Lookup("/tmp_good_but_bad"))
+ << "Enforce terminator on directories.";
+ EXPECT_EQ(0, psrc.Lookup("/tmp/."))
+ << "Do not allow opening a directory handle.";
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/gtest/TestLogging.cpp b/security/sandbox/linux/gtest/TestLogging.cpp
new file mode 100644
index 0000000000..eb0c9b747e
--- /dev/null
+++ b/security/sandbox/linux/gtest/TestLogging.cpp
@@ -0,0 +1,56 @@
+/* -*- 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/. */
+
+#include "gtest/gtest.h"
+
+#include "SandboxLogging.h"
+
+#include <errno.h>
+
+namespace mozilla {
+
+TEST(SandboxLogging, ErrorName1)
+{
+ char buf[32];
+ ssize_t n = GetLibcErrorName(buf, sizeof(buf), EINVAL);
+ EXPECT_EQ(n, 6);
+ EXPECT_STREQ(buf, "EINVAL");
+}
+
+TEST(SandboxLogging, ErrorName2)
+{
+ char buf[32];
+ ssize_t n = GetLibcErrorName(buf, sizeof(buf), EIO);
+ EXPECT_EQ(n, 3);
+ EXPECT_STREQ(buf, "EIO");
+}
+
+TEST(SandboxLogging, ErrorName3)
+{
+ char buf[32];
+ ssize_t n = GetLibcErrorName(buf, sizeof(buf), ESTALE);
+ EXPECT_EQ(n, 6);
+ EXPECT_STREQ(buf, "ESTALE");
+}
+
+TEST(SandboxLogging, ErrorNameFail)
+{
+ char buf[32];
+ ssize_t n = GetLibcErrorName(buf, sizeof(buf), 4095);
+ EXPECT_EQ(n, 10);
+ EXPECT_STREQ(buf, "error 4095");
+}
+
+TEST(SandboxLogging, ErrorNameTrunc)
+{
+ char buf[] = "++++++++";
+ ssize_t n = GetLibcErrorName(buf, 3, EINVAL);
+ EXPECT_EQ(n, 6);
+ EXPECT_STREQ(buf, "EI");
+ EXPECT_STREQ(buf + 3, "+++++");
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/gtest/moz.build b/security/sandbox/linux/gtest/moz.build
new file mode 100644
index 0000000000..f80482aee3
--- /dev/null
+++ b/security/sandbox/linux/gtest/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Library("sandboxtest")
+
+UNIFIED_SOURCES = [
+ "../SandboxBrokerClient.cpp",
+ "TestBroker.cpp",
+ "TestBrokerPolicy.cpp",
+ "TestLogging.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/security/sandbox/linux",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/security/sandbox/linux/interfaces/moz.build b/security/sandbox/linux/interfaces/moz.build
new file mode 100644
index 0000000000..4ff62e8aba
--- /dev/null
+++ b/security/sandbox/linux/interfaces/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_MODULE = "sandbox"
+
+XPIDL_SOURCES += [
+ "mozISandboxReporter.idl",
+]
diff --git a/security/sandbox/linux/interfaces/mozISandboxReporter.idl b/security/sandbox/linux/interfaces/mozISandboxReporter.idl
new file mode 100644
index 0000000000..82f3ab7a72
--- /dev/null
+++ b/security/sandbox/linux/interfaces/mozISandboxReporter.idl
@@ -0,0 +1,65 @@
+/* -*- Mode: IDL; 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/. */
+
+#include "nsISupports.idl"
+
+// A wrapper for the C++ class SandboxReport, representing one system
+// call that was rejected by policy.
+[scriptable, builtinclass, uuid(ed1e84d3-3346-42e1-b28c-e76a77f549f0)]
+interface mozISandboxReport : nsISupports
+{
+ // The timestamp relative to the time when this property is read.
+ // This is mainly meant for distinguishing recent events that might
+ // be related to an observable failure from older ones that may be
+ // unrelated, not for exact timing.
+ readonly attribute uint64_t msecAgo;
+ readonly attribute int32_t pid;
+ readonly attribute int32_t tid;
+ readonly attribute ACString procType;
+ readonly attribute uint32_t syscall;
+ // Currently numArgs is effectively a constant and indicates the
+ // maximum number of arguments possible on the platform; the actual
+ // system call may use fewer.
+ readonly attribute uint32_t numArgs;
+ // The argument values are presented as strings because JS doesn't
+ // have 64-bit integers and data would be lost on 64-bit platforms
+ // if the XPIDL type uint64_t were used. The string may be decimal
+ // or hex (with leading "0x").
+ ACString getArg(in uint32_t aIndex);
+};
+
+// A wrapper for SandboxReporter::Snapshot, representing the most
+// recent SandboxReport events. Index 0 is the first report in the
+// session, and so on; exposing the indices like this lets us see how
+// many reports have been received even though only a limited number
+// of them are stored.
+[scriptable, builtinclass, uuid(6e8ff6e5-05c9-42d3-853d-40523fd86a50)]
+interface mozISandboxReportArray : nsISupports
+{
+ readonly attribute uint64_t begin;
+ readonly attribute uint64_t end;
+ // (aIndex >= begin && aIndex < end) must be true.
+ mozISandboxReport getElement(in uint64_t aIndex);
+};
+
+// A wrapper for the SandboxReporter; use the component/contract IDs
+// below to access the SandboxReporter singleton. The component
+// constructor will fail if called in a child process.
+[scriptable, builtinclass, uuid(8535bdf7-6d9e-4853-acf9-a146449c4a3b)]
+interface mozISandboxReporter : nsISupports
+{
+ mozISandboxReportArray snapshot();
+};
+
+%{ C++
+
+#define MOZ_SANDBOX_REPORTER_CID \
+{0x5118a6f9, 0x2493, 0x4f97, {0x95, 0x52, 0x62, 0x06, 0x63, 0xe0, 0x3c, 0xb3}}
+
+#define MOZ_SANDBOX_REPORTER_CONTRACTID \
+ "@mozilla.org/sandbox/syscall-reporter;1"
+
+%}
diff --git a/security/sandbox/linux/launch/LinuxCapabilities.cpp b/security/sandbox/linux/launch/LinuxCapabilities.cpp
new file mode 100644
index 0000000000..6a1c73ed37
--- /dev/null
+++ b/security/sandbox/linux/launch/LinuxCapabilities.cpp
@@ -0,0 +1,26 @@
+/* -*- 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/. */
+
+#include "LinuxCapabilities.h"
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+namespace mozilla {
+
+bool LinuxCapabilities::GetCurrent() {
+ __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ return syscall(__NR_capget, &header, &mBits) == 0 &&
+ header.version == _LINUX_CAPABILITY_VERSION_3;
+}
+
+bool LinuxCapabilities::SetCurrentRaw() const {
+ __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ return syscall(__NR_capset, &header, &mBits) == 0 &&
+ header.version == _LINUX_CAPABILITY_VERSION_3;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/launch/LinuxCapabilities.h b/security/sandbox/linux/launch/LinuxCapabilities.h
new file mode 100644
index 0000000000..6d9b79e5d9
--- /dev/null
+++ b/security/sandbox/linux/launch/LinuxCapabilities.h
@@ -0,0 +1,122 @@
+/* -*- 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/. */
+
+#ifndef mozilla_LinuxCapabilities_h
+#define mozilla_LinuxCapabilities_h
+
+#include <linux/capability.h>
+#include <stdint.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/PodOperations.h"
+
+// This class is a relatively simple interface to manipulating the
+// capabilities of a Linux process/thread; see the capabilities(7) man
+// page for background information.
+
+// Unfortunately, Android's kernel headers omit some definitions
+// needed for the low-level capability interface. They're part of the
+// stable syscall ABI, so it's safe to include them here.
+#ifndef _LINUX_CAPABILITY_VERSION_3
+# define _LINUX_CAPABILITY_VERSION_3 0x20080522
+# define _LINUX_CAPABILITY_U32S_3 2
+#endif
+#ifndef CAP_TO_INDEX
+# define CAP_TO_INDEX(x) ((x) >> 5)
+# define CAP_TO_MASK(x) (1 << ((x)&31))
+#endif
+
+namespace mozilla {
+
+class LinuxCapabilities final {
+ public:
+ // A class to represent a bit within the capability sets as an lvalue.
+ class BitRef {
+ __u32& mWord;
+ __u32 mMask;
+ friend class LinuxCapabilities;
+ BitRef(__u32& aWord, uint32_t aMask) : mWord(aWord), mMask(aMask) {}
+ BitRef(const BitRef& aBit) = default;
+
+ public:
+ MOZ_IMPLICIT operator bool() const { return mWord & mMask; }
+ BitRef& operator=(bool aSetTo) {
+ if (aSetTo) {
+ mWord |= mMask;
+ } else {
+ mWord &= mMask;
+ }
+ return *this;
+ }
+ };
+
+ // The default value is the empty set.
+ LinuxCapabilities() { PodArrayZero(mBits); }
+
+ // Get the current thread's capability sets and assign them to this
+ // object. Returns whether it succeeded and sets errno on failure.
+ // Shouldn't fail unless the kernel is very old.
+ bool GetCurrent();
+
+ // Try to set the current thread's capability sets to those
+ // specified in this object. Returns whether it succeeded and sets
+ // errno on failure.
+ bool SetCurrentRaw() const;
+
+ // The capability model requires that the permitted set always be a
+ // superset of the effective and inheritable sets. This method
+ // expands the permitted set as needed and then sets the current
+ // thread's capabilities, as described above.
+ bool SetCurrent() {
+ Normalize();
+ return SetCurrentRaw();
+ }
+
+ void Normalize() {
+ for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; ++i) {
+ mBits[i].permitted |= mBits[i].effective | mBits[i].inheritable;
+ }
+ }
+
+ bool AnyEffective() const {
+ for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; ++i) {
+ if (mBits[i].effective != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // These three methods expose individual bits in the three
+ // capability sets as objects that can be used as bool lvalues.
+ // The argument is the capability number, as defined in
+ // the <linux/capability.h> header.
+ BitRef Effective(unsigned aCap) {
+ return GenericBitRef(&__user_cap_data_struct::effective, aCap);
+ }
+
+ BitRef Permitted(unsigned aCap) {
+ return GenericBitRef(&__user_cap_data_struct::permitted, aCap);
+ }
+
+ BitRef Inheritable(unsigned aCap) {
+ return GenericBitRef(&__user_cap_data_struct::inheritable, aCap);
+ }
+
+ private:
+ __user_cap_data_struct mBits[_LINUX_CAPABILITY_U32S_3];
+
+ BitRef GenericBitRef(__u32 __user_cap_data_struct::*aField, unsigned aCap) {
+ // Please don't pass untrusted data as the capability number.
+ MOZ_ASSERT(CAP_TO_INDEX(aCap) < _LINUX_CAPABILITY_U32S_3);
+ return BitRef(mBits[CAP_TO_INDEX(aCap)].*aField, CAP_TO_MASK(aCap));
+ }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_LinuxCapabilities_h
diff --git a/security/sandbox/linux/launch/SandboxLaunch.cpp b/security/sandbox/linux/launch/SandboxLaunch.cpp
new file mode 100644
index 0000000000..d6f3ec9c77
--- /dev/null
+++ b/security/sandbox/linux/launch/SandboxLaunch.cpp
@@ -0,0 +1,715 @@
+/* -*- 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/. */
+
+#include "SandboxLaunch.h"
+
+#include <fcntl.h>
+#include <sched.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include "LinuxCapabilities.h"
+#include "LinuxSched.h"
+#include "SandboxChrootProto.h"
+#include "SandboxInfo.h"
+#include "SandboxLogging.h"
+#include "base/eintr_wrapper.h"
+#include "base/strings/safe_sprintf.h"
+#include "mozilla/Array.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SandboxReporter.h"
+#include "mozilla/SandboxSettings.h"
+#include "mozilla/Components.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/Unused.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsIGfxInfo.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "prenv.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+#ifdef MOZ_X11
+# ifndef MOZ_WIDGET_GTK
+# error "Unknown toolkit"
+# endif
+# include "mozilla/WidgetUtilsGtk.h"
+# include <gdk/gdk.h>
+# include <gdk/gdkx.h>
+# include "X11UndefineNone.h"
+# include "gfxPlatform.h"
+#endif
+
+namespace mozilla {
+
+// Returns true if graphics will work from a content process
+// started in a new network namespace. Specifically, named
+// Unix-domain sockets will work, but TCP/IP will not, even if it's a
+// connection to localhost: the child process has its own private
+// loopback interface.
+//
+// (Longer-term we intend to either proxy or remove X11 access from
+// content processes, at which point this will stop being an issue.)
+static bool IsGraphicsOkWithoutNetwork() {
+ // For X11, check whether the parent's connection is a Unix-domain
+ // socket. This is done instead of trying to parse the display name
+ // because an empty hostname (e.g., ":0") will fall back to TCP in
+ // case of failure to connect using Unix-domain sockets.
+#ifdef MOZ_X11
+ // First, ensure that the parent process's graphics are initialized.
+ DebugOnly<gfxPlatform*> gfxPlatform = gfxPlatform::GetPlatform();
+
+ const auto display = gdk_display_get_default();
+ if (!display) {
+ // In this case, the browser is headless, but WebGL could still
+ // try to use X11. However, WebGL isn't supported with remote
+ // X11, and in any case these connections are made after sandbox
+ // startup (lazily when WebGL is used), so they aren't being done
+ // directly by the process anyway. (For local X11, they're
+ // brokered.)
+ MOZ_ASSERT(gfxPlatform->IsHeadless());
+ return true;
+ }
+ if (mozilla::widget::GdkIsX11Display(display)) {
+ const int xSocketFd = ConnectionNumber(GDK_DISPLAY_XDISPLAY(display));
+ if (NS_WARN_IF(xSocketFd < 0)) {
+ return false;
+ }
+
+ int domain;
+ socklen_t optlen = static_cast<socklen_t>(sizeof(domain));
+ int rv = getsockopt(xSocketFd, SOL_SOCKET, SO_DOMAIN, &domain, &optlen);
+ if (NS_WARN_IF(rv != 0)) {
+ return false;
+ }
+ MOZ_RELEASE_ASSERT(static_cast<size_t>(optlen) == sizeof(domain));
+ if (domain != AF_LOCAL) {
+ return false;
+ }
+ // There's one more complication: Xorg listens on named sockets
+ // (actual filesystem nodes) as well as abstract addresses (opaque
+ // octet strings scoped to the network namespace; this is a Linux
+ // extension).
+ //
+ // Inside a container environment (e.g., when running as a Snap
+ // package), it's possible that only the abstract addresses are
+ // accessible. In that case, the display must be considered
+ // remote. See also bug 1450740.
+ //
+ // Unfortunately, the Xorg client libraries prefer the abstract
+ // addresses, so this isn't directly detectable by inspecting the
+ // parent process's socket. Instead, parse the DISPLAY env var
+ // (which was updated if necessary in nsAppRunner.cpp) to get the
+ // display number and construct the socket path, falling back to
+ // testing the directory in case that doesn't work. (See bug
+ // 1565972 and bug 1559368 for cases where we need to test the
+ // specific socket.)
+ const char* const displayStr = PR_GetEnv("DISPLAY");
+ nsAutoCString socketPath("/tmp/.X11-unix");
+ int accessFlags = X_OK;
+ int displayNum;
+ // sscanf ignores trailing text, so display names with a screen
+ // number (e.g., ":0.2") will parse correctly.
+ if (displayStr && (sscanf(displayStr, ":%d", &displayNum) == 1 ||
+ sscanf(displayStr, "unix:%d", &displayNum) == 1)) {
+ socketPath.AppendPrintf("/X%d", displayNum);
+ accessFlags = R_OK | W_OK;
+ }
+ if (access(socketPath.get(), accessFlags) != 0) {
+ SANDBOX_LOG_ERRNO(
+ "%s is inaccessible; can't isolate network namespace in"
+ " content processes",
+ socketPath.get());
+ return false;
+ }
+ }
+#endif
+
+ // Assume that other backends (e.g., Wayland) will not use the
+ // network namespace.
+ return true;
+}
+
+bool HasAtiDrivers() {
+ nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
+ nsAutoString vendorID;
+ static const Array<nsresult (nsIGfxInfo::*)(nsAString&), 2> kMethods = {
+ &nsIGfxInfo::GetAdapterVendorID,
+ &nsIGfxInfo::GetAdapterVendorID2,
+ };
+ for (const auto method : kMethods) {
+ if (NS_SUCCEEDED((gfxInfo->*method)(vendorID))) {
+ // This test is based on telemetry data. The proprietary ATI
+ // drivers seem to use this vendor string, including for some
+ // newer devices that have AMD branding in the device name, such
+ // as those using AMDGPU-PRO drivers.
+ // The open-source drivers integrated into Mesa appear to use
+ // the vendor ID "X.Org" instead.
+ if (vendorID.EqualsLiteral("ATI Technologies Inc.")) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// Content processes may need direct access to SysV IPC in certain
+// uncommon use cases.
+static bool ContentNeedsSysVIPC() {
+ // The ALSA dmix plugin uses SysV semaphores and shared memory to
+ // coordinate software mixing.
+#ifdef MOZ_ALSA
+ if (!StaticPrefs::media_cubeb_sandbox()) {
+ return true;
+ }
+#endif
+
+ if (!StaticPrefs::security_sandbox_content_headless_AtStartup()) {
+ // Bug 1438391: VirtualGL uses SysV shm for images and configuration.
+ if (PR_GetEnv("VGL_ISACTIVE") != nullptr) {
+ return true;
+ }
+
+ // The fglrx (ATI Catalyst) GPU drivers use SysV IPC.
+ if (HasAtiDrivers()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void PreloadSandboxLib(base::environment_map* aEnv) {
+ // Preload libmozsandbox.so so that sandbox-related interpositions
+ // can be defined there instead of in the executable.
+ // (This could be made conditional on intent to use sandboxing, but
+ // it's harmless for non-sandboxed processes.)
+ nsAutoCString preload;
+ // Prepend this, because people can and do preload libpthread.
+ // (See bug 1222500.)
+ preload.AssignLiteral("libmozsandbox.so");
+ if (const char* oldPreload = PR_GetEnv("LD_PRELOAD")) {
+ // Doesn't matter if oldPreload is ""; extra separators are ignored.
+ preload.Append(' ');
+ preload.Append(oldPreload);
+ (*aEnv)["MOZ_ORIG_LD_PRELOAD"] = oldPreload;
+ }
+ MOZ_ASSERT(aEnv->count("LD_PRELOAD") == 0);
+ (*aEnv)["LD_PRELOAD"] = preload.get();
+}
+
+static void AttachSandboxReporter(base::file_handle_mapping_vector* aFdMap) {
+ int srcFd, dstFd;
+ SandboxReporter::Singleton()->GetClientFileDescriptorMapping(&srcFd, &dstFd);
+ aFdMap->push_back({srcFd, dstFd});
+}
+
+class SandboxFork : public base::LaunchOptions::ForkDelegate {
+ public:
+ explicit SandboxFork(int aFlags, bool aChroot, int aServerFd = -1,
+ int aClientFd = -1);
+ virtual ~SandboxFork();
+
+ void PrepareMapping(base::file_handle_mapping_vector* aMap);
+ pid_t Fork() override;
+
+ private:
+ int mFlags;
+ int mChrootServer;
+ int mChrootClient;
+
+ void StartChrootServer();
+ SandboxFork(const SandboxFork&) = delete;
+ SandboxFork& operator=(const SandboxFork&) = delete;
+};
+
+static int GetEffectiveSandboxLevel(GeckoProcessType aType) {
+ auto info = SandboxInfo::Get();
+ switch (aType) {
+ case GeckoProcessType_GMPlugin:
+ if (info.Test(SandboxInfo::kEnabledForMedia)) {
+ return 1;
+ }
+ return 0;
+ case GeckoProcessType_Content:
+#ifdef MOZ_ENABLE_FORKSERVER
+ // With this env MOZ_SANDBOXED will be set, and mozsandbox will
+ // be preloaded for the fork server. The content processes rely
+ // on wrappers defined by mozsandbox to work properly.
+ case GeckoProcessType_ForkServer:
+#endif
+ // GetEffectiveContentSandboxLevel is main-thread-only due to prefs.
+ MOZ_ASSERT(NS_IsMainThread());
+ if (info.Test(SandboxInfo::kEnabledForContent)) {
+ return GetEffectiveContentSandboxLevel();
+ }
+ return 0;
+ case GeckoProcessType_RDD:
+ return PR_GetEnv("MOZ_DISABLE_RDD_SANDBOX") == nullptr ? 1 : 0;
+ case GeckoProcessType_Socket:
+ // GetEffectiveSocketProcessSandboxLevel is main-thread-only due to prefs.
+ MOZ_ASSERT(NS_IsMainThread());
+ return GetEffectiveSocketProcessSandboxLevel();
+ case GeckoProcessType_Utility:
+ return PR_GetEnv("MOZ_DISABLE_UTILITY_SANDBOX") == nullptr ? 1 : 0;
+ default:
+ return 0;
+ }
+}
+
+void SandboxLaunchPrepare(GeckoProcessType aType,
+ base::LaunchOptions* aOptions) {
+ auto info = SandboxInfo::Get();
+
+ // We won't try any kind of sandboxing without seccomp-bpf.
+ if (!info.Test(SandboxInfo::kHasSeccompBPF)) {
+ return;
+ }
+
+ // Check prefs (and env vars) controlling sandbox use.
+ int level = GetEffectiveSandboxLevel(aType);
+ if (level == 0) {
+ return;
+ }
+
+ // At this point, we know we'll be using sandboxing; generic
+ // sandboxing support goes here. The MOZ_SANDBOXED env var tells
+ // the child process whether this is the case.
+ aOptions->env_map["MOZ_SANDBOXED"] = "1";
+ PreloadSandboxLib(&aOptions->env_map);
+ AttachSandboxReporter(&aOptions->fds_to_remap);
+
+ bool canChroot = false;
+ int flags = 0;
+
+ if (aType == GeckoProcessType_Content && level >= 1) {
+ static const bool needSysV = ContentNeedsSysVIPC();
+ if (needSysV) {
+ // Tell the child process so it can adjust its seccomp-bpf
+ // policy.
+ aOptions->env_map["MOZ_SANDBOX_ALLOW_SYSV"] = "1";
+ } else {
+ flags |= CLONE_NEWIPC;
+ }
+
+ if (StaticPrefs::security_sandbox_content_headless_AtStartup()) {
+ aOptions->env_map["MOZ_HEADLESS"] = "1";
+ }
+ }
+
+ // Anything below this requires unprivileged user namespaces.
+ if (!info.Test(SandboxInfo::kHasUserNamespaces)) {
+ return;
+ }
+
+ switch (aType) {
+ case GeckoProcessType_Socket:
+ if (level >= 1) {
+ canChroot = true;
+ flags |= CLONE_NEWIPC;
+ }
+ break;
+ case GeckoProcessType_GMPlugin:
+ case GeckoProcessType_RDD:
+ if (level >= 1) {
+ canChroot = true;
+ // Can't use CLONE_NEWIPC because of intel-media-driver.
+ flags |= CLONE_NEWNET;
+ }
+ break;
+ case GeckoProcessType_Content:
+ if (level >= 4) {
+ canChroot = true;
+
+ // Unshare network namespace if allowed by graphics; see
+ // function definition above for details. (The display
+ // local-ness is cached because it won't change.)
+ static const bool canCloneNet =
+ StaticPrefs::security_sandbox_content_headless_AtStartup() ||
+ (IsGraphicsOkWithoutNetwork() &&
+ !PR_GetEnv("RENDERDOC_CAPTUREOPTS"));
+
+ if (canCloneNet) {
+ flags |= CLONE_NEWNET;
+ }
+ }
+ // Hidden pref to allow testing user namespaces separately, even
+ // if there's nothing that would require them.
+ if (Preferences::GetBool("security.sandbox.content.force-namespace",
+ false)) {
+ flags |= CLONE_NEWUSER;
+ }
+ break;
+ default:
+ // Nothing yet.
+ break;
+ }
+
+ if (canChroot || flags != 0) {
+ flags |= CLONE_NEWUSER;
+ auto forker = MakeUnique<SandboxFork>(flags, canChroot);
+ forker->PrepareMapping(&aOptions->fds_to_remap);
+ aOptions->fork_delegate = std::move(forker);
+ // Pass to |SandboxLaunchForkServerPrepare()| in the fork server.
+ aOptions->env_map[kSandboxChrootEnvFlag] =
+ std::to_string(canChroot ? 1 : 0) + std::to_string(flags);
+ }
+}
+
+#if defined(MOZ_ENABLE_FORKSERVER)
+/**
+ * Called by the fork server to install a fork delegator.
+ *
+ * In the case of fork server, the value of the flags of |SandboxFork|
+ * are passed as an env variable to the fork server so that we can
+ * recreate a |SandboxFork| as a fork delegator at the fork server.
+ */
+void SandboxLaunchForkServerPrepare(const std::vector<std::string>& aArgv,
+ base::LaunchOptions& aOptions) {
+ auto chroot = std::find_if(
+ aOptions.env_map.begin(), aOptions.env_map.end(),
+ [](auto& elt) { return elt.first == kSandboxChrootEnvFlag; });
+ if (chroot == aOptions.env_map.end()) {
+ return;
+ }
+ bool canChroot = chroot->second.c_str()[0] == '1';
+ int flags = atoi(chroot->second.c_str() + 1);
+ MOZ_ASSERT(flags || canChroot);
+
+ // Find chroot server fd. It is supposed to be map to
+ // kSandboxChrootServerFd so that we find it out from the mapping.
+ auto fdmap = std::find_if(
+ aOptions.fds_to_remap.begin(), aOptions.fds_to_remap.end(),
+ [](auto& elt) { return elt.second == kSandboxChrootServerFd; });
+ MOZ_ASSERT(fdmap != aOptions.fds_to_remap.end(),
+ "ChrootServerFd is not found with sandbox chroot");
+ int chrootserverfd = fdmap->first;
+ aOptions.fds_to_remap.erase(fdmap);
+
+ // Set only the chroot server fd, not the client fd. Because, the
+ // client fd is already in |fds_to_remap|, we don't need the forker
+ // to do it again. And, the forker need only the server fd, that
+ // chroot server uses it to sync with the client (content). See
+ // |SandboxFox::StartChrootServer()|.
+ auto forker = MakeUnique<SandboxFork>(flags, canChroot, chrootserverfd);
+ aOptions.fork_delegate = std::move(forker);
+}
+#endif
+
+SandboxFork::SandboxFork(int aFlags, bool aChroot, int aServerFd, int aClientFd)
+ : mFlags(aFlags), mChrootServer(aServerFd), mChrootClient(aClientFd) {
+ if (aChroot && mChrootServer < 0) {
+ int fds[2];
+ int rv = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds);
+ if (rv != 0) {
+ SANDBOX_LOG_ERRNO("socketpair");
+ MOZ_CRASH("socketpair failed");
+ }
+ mChrootClient = fds[0];
+ mChrootServer = fds[1];
+ }
+}
+
+void SandboxFork::PrepareMapping(base::file_handle_mapping_vector* aMap) {
+ MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_ForkServer);
+ if (mChrootClient >= 0) {
+ aMap->push_back({mChrootClient, kSandboxChrootClientFd});
+ }
+#if defined(MOZ_ENABLE_FORKSERVER)
+ if (mChrootServer >= 0) {
+ aMap->push_back({mChrootServer, kSandboxChrootServerFd});
+ }
+#endif
+}
+
+SandboxFork::~SandboxFork() {
+ if (mChrootClient >= 0) {
+ close(mChrootClient);
+ }
+ if (mChrootServer >= 0) {
+ close(mChrootServer);
+ }
+}
+
+static void BlockAllSignals(sigset_t* aOldSigs) {
+ sigset_t allSigs;
+ int rv = sigfillset(&allSigs);
+ MOZ_RELEASE_ASSERT(rv == 0);
+ rv = pthread_sigmask(SIG_BLOCK, &allSigs, aOldSigs);
+ if (rv != 0) {
+ SANDBOX_LOG_WITH_ERROR(rv, "pthread_sigmask (block all)");
+ MOZ_CRASH("pthread_sigmask");
+ }
+}
+
+static void RestoreSignals(const sigset_t* aOldSigs) {
+ // Assuming that pthread_sigmask is a thin layer over rt_sigprocmask
+ // and doesn't try to touch TLS, which may be in an "interesting"
+ // state right now:
+ int rv = pthread_sigmask(SIG_SETMASK, aOldSigs, nullptr);
+ if (rv != 0) {
+ SANDBOX_LOG_WITH_ERROR(rv, "pthread_sigmask (restore)");
+ MOZ_CRASH("pthread_sigmask");
+ }
+}
+
+static bool IsSignalIgnored(int aSig) {
+ struct sigaction sa {};
+
+ if (sigaction(aSig, nullptr, &sa) != 0) {
+ if (errno != EINVAL) {
+ SANDBOX_LOG_ERRNO("sigaction(%d)", aSig);
+ }
+ return false;
+ }
+ return sa.sa_handler == SIG_IGN;
+}
+
+static void ResetSignalHandlers() {
+ for (int signum = 1; signum <= SIGRTMAX; ++signum) {
+ if (IsSignalIgnored(signum)) {
+ continue;
+ }
+ if (signal(signum, SIG_DFL) == SIG_ERR) {
+ MOZ_DIAGNOSTIC_ASSERT(errno == EINVAL);
+ }
+ }
+}
+
+namespace {
+
+// The libc clone() routine insists on calling a provided function on
+// a new stack, even if the address space isn't shared and it would be
+// safe to expose the underlying system call's fork()-like behavior.
+// So, we work around this by longjmp()ing back onto the original stack;
+// this technique is also used by Chromium.
+//
+// In theory, the clone syscall could be used directly if we ensure
+// that functions like raise() are never used in the child, including
+// by inherited signal handlers, but the longjmp approach isn't much
+// extra code and avoids a class of potential bugs.
+static int CloneCallee(void* aPtr) {
+ auto ctxPtr = reinterpret_cast<jmp_buf*>(aPtr);
+ longjmp(*ctxPtr, 1);
+ MOZ_CRASH("unreachable");
+ return 1;
+}
+
+// According to the Chromium developers, builds with FORTIFY_SOURCE
+// require that longjump move the stack pointer towards the root
+// function of the call stack. Therefore, we must ensure that the
+// clone callee stack is leafward of the stack pointer captured in
+// setjmp() below by using this no-inline helper function.
+//
+// ASan apparently also causes problems, by the combination of
+// allocating the large stack-allocated buffer outside of the actual
+// stack and then assuming that longjmp is used only to unwind a
+// stack, not switch stacks.
+//
+// Valgrind would disapprove of using clone() without CLONE_VM;
+// Chromium uses the raw syscall as a workaround in that case, but
+// we don't currently support sandboxing under valgrind.
+MOZ_NEVER_INLINE MOZ_ASAN_IGNORE static pid_t DoClone(int aFlags,
+ jmp_buf* aCtx) {
+ static constexpr size_t kStackAlignment = 16;
+ uint8_t miniStack[4096] __attribute__((aligned(kStackAlignment)));
+#ifdef __hppa__
+ void* stackPtr = miniStack;
+#else
+ void* stackPtr = ArrayEnd(miniStack);
+#endif
+ return clone(CloneCallee, stackPtr, aFlags, aCtx);
+}
+
+} // namespace
+
+// Similar to fork(), but allows passing flags to clone() and does not
+// run pthread_atfork hooks.
+static pid_t ForkWithFlags(int aFlags) {
+ // Don't allow flags that would share the address space, or
+ // require clone() arguments we're not passing:
+ static const int kBadFlags = CLONE_VM | CLONE_VFORK | CLONE_SETTLS |
+ CLONE_PARENT_SETTID | CLONE_CHILD_SETTID |
+ CLONE_CHILD_CLEARTID;
+ MOZ_RELEASE_ASSERT((aFlags & kBadFlags) == 0);
+
+ // Block signals due to small stack in DoClone.
+ sigset_t oldSigs;
+ BlockAllSignals(&oldSigs);
+
+ int ret = 0;
+ jmp_buf ctx;
+ if (setjmp(ctx) == 0) {
+ // In the parent and just called setjmp:
+ ret = DoClone(aFlags | SIGCHLD, &ctx);
+ }
+ RestoreSignals(&oldSigs);
+ // In the child and have longjmp'ed:
+ return ret;
+}
+
+static bool WriteStringToFile(const char* aPath, const char* aStr,
+ const size_t aLen) {
+ int fd = open(aPath, O_WRONLY);
+ if (fd < 0) {
+ return false;
+ }
+ ssize_t written = write(fd, aStr, aLen);
+ if (close(fd) != 0 || written != ssize_t(aLen)) {
+ return false;
+ }
+ return true;
+}
+
+// This function sets up uid/gid mappings that preserve the
+// process's previous ids. Mapping the uid/gid to something is
+// necessary in order to nest user namespaces (not currently being
+// used, but could be useful), and leaving the ids unchanged is
+// likely to minimize unexpected side-effects.
+static void ConfigureUserNamespace(uid_t uid, gid_t gid) {
+ using base::strings::SafeSPrintf;
+ char buf[sizeof("18446744073709551615 18446744073709551615 1")];
+ size_t len;
+
+ len = static_cast<size_t>(SafeSPrintf(buf, "%d %d 1", uid, uid));
+ MOZ_RELEASE_ASSERT(len < sizeof(buf));
+ if (!WriteStringToFile("/proc/self/uid_map", buf, len)) {
+ MOZ_CRASH("Failed to write /proc/self/uid_map");
+ }
+
+ // In recent kernels (3.19, 3.18.2, 3.17.8), for security reasons,
+ // establishing gid mappings will fail unless the process first
+ // revokes its ability to call setgroups() by using a /proc node
+ // added in the same set of patches.
+ Unused << WriteStringToFile("/proc/self/setgroups", "deny", 4);
+
+ len = static_cast<size_t>(SafeSPrintf(buf, "%d %d 1", gid, gid));
+ MOZ_RELEASE_ASSERT(len < sizeof(buf));
+ if (!WriteStringToFile("/proc/self/gid_map", buf, len)) {
+ MOZ_CRASH("Failed to write /proc/self/gid_map");
+ }
+}
+
+static void DropAllCaps() {
+ if (!LinuxCapabilities().SetCurrent()) {
+ SANDBOX_LOG_ERRNO("capset (drop all)");
+ }
+}
+
+pid_t SandboxFork::Fork() {
+ if (mFlags == 0) {
+ MOZ_ASSERT(mChrootServer < 0);
+ return fork();
+ }
+
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ // Block signals so that the handlers can be safely reset in the
+ // child process without races, and so that repeated SIGPROF from
+ // the profiler won't prevent clone() from making progress. (The
+ // profiler uses pthread_atfork to do that, but ForkWithFlags
+ // can't run atfork hooks.)
+ sigset_t oldSigs;
+ BlockAllSignals(&oldSigs);
+ pid_t pid = ForkWithFlags(mFlags);
+ if (pid != 0) {
+ RestoreSignals(&oldSigs);
+ return pid;
+ }
+
+ // WARNING: all code from this point on (and in StartChrootServer)
+ // must be async signal safe. In particular, it cannot do anything
+ // that could allocate heap memory or use mutexes.
+ prctl(PR_SET_NAME, "Sandbox Forked");
+
+ // Clear signal handlers in the child, under the assumption that any
+ // actions they would take (running the crash reporter, manipulating
+ // the Gecko profile, etc.) wouldn't work correctly in the child.
+ ResetSignalHandlers();
+ RestoreSignals(&oldSigs);
+ ConfigureUserNamespace(uid, gid);
+
+ if (mChrootServer >= 0) {
+ StartChrootServer();
+ }
+
+ // execve() will drop capabilities, but it seems best to also drop
+ // them here in case they'd do something unexpected in the generic
+ // post-fork code.
+ DropAllCaps();
+ return 0;
+}
+
+void SandboxFork::StartChrootServer() {
+ // Run the rest of this function in a separate process that can
+ // chroot() on behalf of this process after it's sandboxed.
+ pid_t pid = ForkWithFlags(CLONE_FS);
+ if (pid < 0) {
+ MOZ_CRASH("failed to clone chroot helper process");
+ }
+ if (pid > 0) {
+ return;
+ }
+ prctl(PR_SET_NAME, "Chroot Helper");
+
+ LinuxCapabilities caps;
+ caps.Effective(CAP_SYS_CHROOT) = true;
+ if (!caps.SetCurrent()) {
+ SANDBOX_LOG_ERRNO("capset (chroot helper)");
+ MOZ_DIAGNOSTIC_ASSERT(false);
+ }
+
+ base::CloseSuperfluousFds(this, [](void* aCtx, int aFd) {
+ return aFd == static_cast<decltype(this)>(aCtx)->mChrootServer;
+ });
+
+ char msg;
+ ssize_t msgLen = HANDLE_EINTR(read(mChrootServer, &msg, 1));
+ if (msgLen == 0) {
+ // Process exited before chrooting (or chose not to chroot?).
+ _exit(0);
+ }
+ MOZ_RELEASE_ASSERT(msgLen == 1);
+ MOZ_RELEASE_ASSERT(msg == kSandboxChrootRequest);
+
+ // This chroots both processes to this process's procfs fdinfo
+ // directory, which becomes empty and unlinked when this process
+ // exits at the end of this function, and which is always
+ // unwriteable.
+ int rv = chroot("/proc/self/fdinfo");
+ MOZ_RELEASE_ASSERT(rv == 0);
+
+ // Drop CAP_SYS_CHROOT ASAP. This must happen before responding;
+ // the main child won't be able to waitpid(), so it could start
+ // handling hostile content before this process finishes exiting.
+ DropAllCaps();
+
+ // The working directory still grant access to the real filesystem;
+ // remove that. (Note: if the process can obtain directory fds, for
+ // example via SandboxBroker, it must be blocked from using fchdir.)
+ rv = chdir("/");
+ MOZ_RELEASE_ASSERT(rv == 0);
+
+ msg = kSandboxChrootResponse;
+ msgLen = HANDLE_EINTR(write(mChrootServer, &msg, 1));
+ MOZ_RELEASE_ASSERT(msgLen == 1);
+ _exit(0);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/launch/SandboxLaunch.h b/security/sandbox/linux/launch/SandboxLaunch.h
new file mode 100644
index 0000000000..2d7d05e8a1
--- /dev/null
+++ b/security/sandbox/linux/launch/SandboxLaunch.h
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxLaunch_h
+#define mozilla_SandboxLaunch_h
+
+#include "base/process_util.h"
+#include "nsXULAppAPI.h"
+#include <vector>
+
+namespace mozilla {
+
+// Called in the parent process to set up launch-time aspects of
+// sandboxing. If aType is GeckoProcessType_Content, this must be
+// called on the main thread in order to access prefs.
+void SandboxLaunchPrepare(GeckoProcessType aType,
+ base::LaunchOptions* aOptions);
+#if defined(MOZ_ENABLE_FORKSERVER)
+void SandboxLaunchForkServerPrepare(const std::vector<std::string>& aArgv,
+ base::LaunchOptions& aOptions);
+#endif
+bool HasAtiDrivers();
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxLaunch_h
diff --git a/security/sandbox/linux/launch/moz.build b/security/sandbox/linux/launch/moz.build
new file mode 100644
index 0000000000..09f483f519
--- /dev/null
+++ b/security/sandbox/linux/launch/moz.build
@@ -0,0 +1,33 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "SandboxLaunch.h",
+]
+
+UNIFIED_SOURCES += [
+ "LinuxCapabilities.cpp",
+ "SandboxLaunch.cpp",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ # Need this for safe_sprintf.h used by SandboxLogging.h,
+ # but it has to be after ipc/chromium/src.
+ "/security/sandbox/chromium",
+ "/security/sandbox/linux",
+]
+
+USE_LIBS += [
+ "mozsandbox",
+]
+
+# For the X11 socket domain inspection in SandboxLaunch:
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/linux/moz.build b/security/sandbox/linux/moz.build
new file mode 100644
index 0000000000..0723f0660b
--- /dev/null
+++ b/security/sandbox/linux/moz.build
@@ -0,0 +1,146 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SharedLibrary("mozsandbox")
+
+# Depend on mozglue if and only if it's a shared library;
+# this needs to match mozglue/build/moz.build:
+if CONFIG["OS_TARGET"] == "Android":
+ USE_LIBS += [
+ "mozglue",
+ ]
+
+USE_LIBS += [
+ # For PR_GetEnv
+ "nspr",
+]
+
+EXPORTS.mozilla += [
+ "Sandbox.h",
+ "SandboxInfo.h",
+]
+
+UNIFIED_SOURCES += [
+ "../chromium-shim/base/logging.cpp",
+ "../chromium-shim/base/threading/platform_thread_linux.cpp",
+ "../chromium/base/at_exit.cc",
+ "../chromium/base/callback_internal.cc",
+ "../chromium/base/lazy_instance_helpers.cc",
+ "../chromium/base/location.cc",
+ "../chromium/base/memory/ref_counted.cc",
+ "../chromium/base/posix/can_lower_nice_to.cc",
+ "../chromium/base/posix/safe_strerror.cc",
+ "../chromium/base/strings/string16.cc",
+ "../chromium/base/strings/string_number_conversions.cc",
+ "../chromium/base/strings/string_piece.cc",
+ "../chromium/base/strings/string_util.cc",
+ "../chromium/base/strings/string_util_constants.cc",
+ "../chromium/base/strings/stringprintf.cc",
+ "../chromium/base/strings/utf_string_conversion_utils.cc",
+ "../chromium/base/strings/utf_string_conversions.cc",
+ "../chromium/base/synchronization/condition_variable_posix.cc",
+ "../chromium/base/synchronization/lock.cc",
+ "../chromium/base/synchronization/lock_impl_posix.cc",
+ "../chromium/base/synchronization/waitable_event_posix.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/bignum-dtoa.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/bignum.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/cached-powers.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/double-to-string.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/fast-dtoa.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/fixed-dtoa.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/string-to-double.cc",
+ "../chromium/base/third_party/double_conversion/double-conversion/strtod.cc",
+ "../chromium/base/threading/platform_thread.cc",
+ "../chromium/base/threading/platform_thread_internal_posix.cc",
+ "../chromium/base/threading/platform_thread_posix.cc",
+ "../chromium/base/threading/thread_collision_warner.cc",
+ "../chromium/base/threading/thread_id_name_manager.cc",
+ "../chromium/base/threading/thread_local_storage.cc",
+ "../chromium/base/threading/thread_local_storage_posix.cc",
+ "../chromium/base/threading/thread_restrictions.cc",
+ "../chromium/base/time/time.cc",
+ "../chromium/base/time/time_exploded_posix.cc",
+ "../chromium/base/time/time_now_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/bpf_dsl/syscall_set.cc",
+ "../chromium/sandbox/linux/seccomp-bpf/die.cc",
+ "../chromium/sandbox/linux/seccomp-bpf/syscall.cc",
+ "broker/SandboxBrokerCommon.cpp",
+ "Sandbox.cpp",
+ "SandboxBrokerClient.cpp",
+ "SandboxFilter.cpp",
+ "SandboxFilterUtil.cpp",
+ "SandboxHooks.cpp",
+ "SandboxInfo.cpp",
+ "SandboxLogging.cpp",
+ "SandboxOpenedFiles.cpp",
+ "SandboxReporterClient.cpp",
+]
+
+SOURCES += [
+ "../chromium/base/strings/safe_sprintf.cc",
+ "../chromium/base/third_party/icu/icu_utf.cc",
+ "../chromium/sandbox/linux/seccomp-bpf/trap.cc",
+ "../chromium/sandbox/linux/services/syscall_wrappers.cc",
+]
+
+# This copy of SafeSPrintf doesn't need to avoid the Chromium logging
+# dependency like the one in libxul does, but this way the behavior is
+# consistent. See also the comment in SandboxLogging.h.
+SOURCES["../chromium/base/strings/safe_sprintf.cc"].flags += ["-DNDEBUG"]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ # Keep clang from warning about intentional 'switch' fallthrough in icu_utf.cc:
+ SOURCES["../chromium/base/third_party/icu/icu_utf.cc"].flags += [
+ "-Wno-implicit-fallthrough"
+ ]
+ SOURCES["../chromium/sandbox/linux/seccomp-bpf/trap.cc"].flags += [
+ "-Wno-unreachable-code-return"
+ ]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=stack-protector"]
+ 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
+# forcing there to be only one partition.
+for f in CONFIG["OS_CXXFLAGS"]:
+ if f.startswith("-flto") and CONFIG["CC_TYPE"] != "clang":
+ LDFLAGS += ["--param lto-partitions=1"]
+
+DEFINES["NS_NO_XPCOM"] = True
+DisableStlWrapping()
+
+LOCAL_INCLUDES += ["/security/sandbox/linux"]
+LOCAL_INCLUDES += ["/security/sandbox/chromium-shim"]
+LOCAL_INCLUDES += ["/security/sandbox/chromium"]
+LOCAL_INCLUDES += ["/nsprpub"]
+
+
+if CONFIG["OS_TARGET"] != "Android":
+ # Needed for clock_gettime with glibc < 2.17:
+ OS_LIBS += [
+ "rt",
+ ]
+
+DIRS += [
+ "broker",
+ "glue",
+ "interfaces",
+ "launch",
+ "reporter",
+]
+
+TEST_DIRS += [
+ "gtest",
+]
diff --git a/security/sandbox/linux/reporter/SandboxReporter.cpp b/security/sandbox/linux/reporter/SandboxReporter.cpp
new file mode 100644
index 0000000000..a7c71cd5c9
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -0,0 +1,299 @@
+/* -*- 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/. */
+
+#include "SandboxReporter.h"
+#include "SandboxLogging.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <time.h> // for clockid_t
+
+#include "GeckoProfiler.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/PodOperations.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Telemetry.h"
+#include "sandbox/linux/system_headers/linux_syscalls.h"
+
+// Distinguish architectures for the telemetry key.
+#if defined(__i386__)
+# define SANDBOX_ARCH_NAME "x86"
+#elif defined(__x86_64__)
+# define SANDBOX_ARCH_NAME "amd64"
+#elif defined(__arm__)
+# define SANDBOX_ARCH_NAME "arm"
+#elif defined(__aarch64__)
+# define SANDBOX_ARCH_NAME "arm64"
+#else
+# error "unrecognized architecture"
+#endif
+
+namespace mozilla {
+
+StaticAutoPtr<SandboxReporter> SandboxReporter::sSingleton;
+
+SandboxReporter::SandboxReporter()
+ : mClientFd(-1),
+ mServerFd(-1),
+ mMutex("SandboxReporter"),
+ mBuffer(MakeUnique<SandboxReport[]>(kSandboxReporterBufferSize)),
+ mCount(0) {}
+
+bool SandboxReporter::Init() {
+ int fds[2];
+
+ if (0 != socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fds)) {
+ SANDBOX_LOG_ERRNO("SandboxReporter: socketpair failed");
+ return false;
+ }
+ mClientFd = fds[0];
+ mServerFd = fds[1];
+
+ if (!PlatformThread::Create(0, this, &mThread)) {
+ SANDBOX_LOG_ERRNO("SandboxReporter: thread creation failed");
+ close(mClientFd);
+ close(mServerFd);
+ mClientFd = mServerFd = -1;
+ return false;
+ }
+
+ return true;
+}
+
+SandboxReporter::~SandboxReporter() {
+ if (mServerFd < 0) {
+ return;
+ }
+ shutdown(mServerFd, SHUT_RD);
+ PlatformThread::Join(mThread);
+ close(mServerFd);
+ close(mClientFd);
+}
+
+/* static */
+SandboxReporter* SandboxReporter::Singleton() {
+ static StaticMutex sMutex MOZ_UNANNOTATED;
+ StaticMutexAutoLock lock(sMutex);
+
+ if (sSingleton == nullptr) {
+ sSingleton = new SandboxReporter();
+ if (!sSingleton->Init()) {
+ // If socketpair or thread creation failed, trying to continue
+ // with child process creation is unlikely to succeed; crash
+ // instead of trying to handle that case.
+ MOZ_CRASH("SandboxRepoter::Singleton: initialization failed");
+ }
+ // ClearOnShutdown must be called on the main thread and will
+ // destroy the object on the main thread. That *should* be safe;
+ // the destructor will shut down the reporter's socket reader
+ // thread before freeing anything, IPC should already be shut down
+ // by that point (so it won't race by calling Singleton()), all
+ // non-main XPCOM threads will also be shut down, and currently
+ // the only other user is the main-thread-only Troubleshoot.sys.mjs.
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "SandboxReporter::Singleton", [] { ClearOnShutdown(&sSingleton); }));
+ }
+ return sSingleton.get();
+}
+
+void SandboxReporter::GetClientFileDescriptorMapping(int* aSrcFd,
+ int* aDstFd) const {
+ MOZ_ASSERT(mClientFd >= 0);
+ *aSrcFd = mClientFd;
+ *aDstFd = kSandboxReporterFileDesc;
+}
+
+// This function is mentioned in Histograms.json; keep that in mind if
+// it's renamed or moved to a different file.
+static void SubmitToTelemetry(const SandboxReport& aReport) {
+ nsAutoCString key;
+ // The key contains the process type, something that uniquely
+ // identifies the syscall, and in some cases arguments (see below
+ // for details). Arbitrary formatting choice: fields in the key are
+ // separated by ':', except that (arch, syscall#) pairs are
+ // separated by '/'.
+ //
+ // Examples:
+ // * "content:x86/64" (bug 1285768)
+ // * "content:x86_64/110" (bug 1285768)
+ // * "gmp:madvise:8" (bug 1303813)
+ // * "content:clock_gettime:4" (bug 1334687)
+
+ switch (aReport.mProcType) {
+ case SandboxReport::ProcType::CONTENT:
+ key.AppendLiteral("content");
+ break;
+ case SandboxReport::ProcType::FILE:
+ key.AppendLiteral("file");
+ break;
+ case SandboxReport::ProcType::MEDIA_PLUGIN:
+ key.AppendLiteral("gmp");
+ break;
+ case SandboxReport::ProcType::RDD:
+ key.AppendLiteral("rdd");
+ break;
+ case SandboxReport::ProcType::SOCKET_PROCESS:
+ key.AppendLiteral("socket");
+ break;
+ case SandboxReport::ProcType::UTILITY:
+ key.AppendLiteral("utility");
+ break;
+ default:
+ MOZ_ASSERT(false);
+ }
+ key.Append(':');
+
+ switch (aReport.mSyscall) {
+ // Syscalls that are filtered by arguments in one or more of the
+ // policies in SandboxFilter.cpp should generally have those
+ // arguments included here, but don't include irrelevant
+ // information that would cause large numbers of distinct keys for
+ // the same issue -- for example, pids or pointers. When in
+ // doubt, include arguments only if they would typically be
+ // constants (or asm immediates) in the code making the syscall.
+ //
+ // Also, keep in mind that this is opt-out data collection and
+ // privacy is critical. While it's unlikely that information in
+ // the register values alone could personally identify a user
+ // (see also crash reports, where register contents are public),
+ // and the guidelines in the previous paragraph should rule out
+ // any value that's capable of holding PII, please be careful.
+ //
+ // When making changes here, please consult with a data steward
+ // (https://wiki.mozilla.org/Firefox/Data_Collection) and ask for
+ // a review if you are unsure about anything.
+
+ // This macro includes one argument as a decimal number; it should
+ // be enough for most cases.
+#define ARG_DECIMAL(name, idx) \
+ case __NR_##name: \
+ key.AppendLiteral(#name ":"); \
+ key.AppendInt(aReport.mArgs[idx]); \
+ break
+
+ // This may be more convenient if the argument is a set of bit flags.
+#define ARG_HEX(name, idx) \
+ case __NR_##name: \
+ key.AppendLiteral(#name ":0x"); \
+ key.AppendInt(aReport.mArgs[idx], 16); \
+ break
+
+ // clockid_t is annoying: there are a small set of fixed timers,
+ // but it can also encode a pid/tid (or a fd for a hardware clock
+ // device); in this case the value is negative.
+#define ARG_CLOCKID(name, idx) \
+ case __NR_##name: \
+ key.AppendLiteral(#name ":"); \
+ if (static_cast<clockid_t>(aReport.mArgs[idx]) < 0) { \
+ key.AppendLiteral("dynamic"); \
+ } else { \
+ key.AppendInt(aReport.mArgs[idx]); \
+ } \
+ break
+
+ // The syscalls handled specially:
+
+ ARG_HEX(clone, 0); // flags
+ ARG_DECIMAL(prctl, 0); // option
+ ARG_HEX(ioctl, 1); // request
+ ARG_DECIMAL(fcntl, 1); // cmd
+ ARG_DECIMAL(madvise, 2); // advice
+ ARG_CLOCKID(clock_gettime, 0); // clk_id
+
+#ifdef __NR_socketcall
+ ARG_DECIMAL(socketcall, 0); // call
+#endif
+#ifdef __NR_ipc
+ ARG_DECIMAL(ipc, 0); // call
+#endif
+
+#undef ARG_DECIMAL
+#undef ARG_HEX
+#undef ARG_CLOCKID
+
+ default:
+ // Otherwise just use the number, with the arch name to disambiguate.
+ key.Append(SANDBOX_ARCH_NAME "/");
+ key.AppendInt(aReport.mSyscall);
+ }
+
+ Telemetry::Accumulate(Telemetry::SANDBOX_REJECTED_SYSCALLS, key);
+}
+
+void SandboxReporter::AddOne(const SandboxReport& aReport) {
+ SubmitToTelemetry(aReport);
+
+ MutexAutoLock lock(mMutex);
+ mBuffer[mCount % kSandboxReporterBufferSize] = aReport;
+ ++mCount;
+}
+
+void SandboxReporter::ThreadMain(void) {
+ // Create a nsThread wrapper for the current platform thread, and register it
+ // with the thread manager.
+ (void)NS_GetCurrentThread();
+
+ PlatformThread::SetName("SandboxReporter");
+ AUTO_PROFILER_REGISTER_THREAD("SandboxReporter");
+
+ for (;;) {
+ SandboxReport rep;
+ struct iovec iov;
+ struct msghdr msg;
+
+ iov.iov_base = &rep;
+ iov.iov_len = sizeof(rep);
+ PodZero(&msg);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ const auto recvd = recvmsg(mServerFd, &msg, 0);
+ if (recvd < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ SANDBOX_LOG_ERRNO("SandboxReporter: recvmsg");
+ }
+ if (recvd <= 0) {
+ break;
+ }
+
+ if (static_cast<size_t>(recvd) < sizeof(rep)) {
+ SANDBOX_LOG("SandboxReporter: packet too short (%d < %d)", recvd,
+ sizeof(rep));
+ continue;
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ SANDBOX_LOG("SandboxReporter: packet too long");
+ continue;
+ }
+
+ AddOne(rep);
+ }
+}
+
+SandboxReporter::Snapshot SandboxReporter::GetSnapshot() {
+ Snapshot snapshot;
+ MutexAutoLock lock(mMutex);
+
+ const uint64_t bufSize = static_cast<uint64_t>(kSandboxReporterBufferSize);
+ const uint64_t start = std::max(mCount, bufSize) - bufSize;
+ snapshot.mOffset = start;
+ snapshot.mReports.Clear();
+ snapshot.mReports.SetCapacity(mCount - start);
+ for (size_t i = start; i < mCount; ++i) {
+ const SandboxReport* rep = &mBuffer[i % kSandboxReporterBufferSize];
+ MOZ_ASSERT(rep->IsValid());
+ snapshot.mReports.AppendElement(*rep);
+ }
+ return snapshot;
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/linux/reporter/SandboxReporter.h b/security/sandbox/linux/reporter/SandboxReporter.h
new file mode 100644
index 0000000000..0969111c9c
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporter.h
@@ -0,0 +1,86 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxReporter_h
+#define mozilla_SandboxReporter_h
+
+#include "SandboxReporterCommon.h"
+
+#include "base/platform_thread.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Types.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+// This object collects the SandboxReport messages from all of the
+// child processes, submits them to Telemetry, and maintains a ring
+// buffer of the last kSandboxReporterBufferSize reports.
+class SandboxReporter final : public PlatformThread::Delegate {
+ public:
+ // For normal use, don't construct this directly; use the
+ // Singleton() method.
+ //
+ // For unit testing, use this constructor followed by the Init
+ // method; the object isn't usable unless Init returns true.
+ explicit SandboxReporter();
+ ~SandboxReporter();
+
+ // See above; this method is not thread-safe.
+ bool Init();
+
+ // Used in GeckoChildProcessHost to connect the child process's
+ // client to this report collector.
+ void GetClientFileDescriptorMapping(int* aSrcFd, int* aDstFd) const;
+
+ // A snapshot of the report ring buffer; element 0 of `mReports` is
+ // the `mOffset`th report to be received, and so on.
+ struct Snapshot {
+ // The buffer has to fit in memory, but the total number of
+ // reports received in the session can increase without bound and
+ // could potentially overflow a uint32_t, so this is 64-bit.
+ // (It's exposed to JS as a 53-bit int, effectively, but that
+ // should also be large enough.)
+ uint64_t mOffset;
+ nsTArray<SandboxReport> mReports;
+ };
+
+ // Read the ring buffer contents; this method is thread-safe.
+ Snapshot GetSnapshot();
+
+ // Gets or creates the singleton report collector. Crashes if
+ // initialization fails (if a socketpair and/or thread can't be
+ // created, there was almost certainly about to be a crash anyway).
+ // Thread-safe as long as the pointer isn't used during/after XPCOM
+ // shutdown.
+ static SandboxReporter* Singleton();
+
+ private:
+ // These are constant over the life of the object:
+ int mClientFd;
+ int mServerFd;
+ PlatformThreadHandle mThread;
+
+ Mutex mMutex MOZ_UNANNOTATED;
+ // These are protected by mMutex:
+ UniquePtr<SandboxReport[]> mBuffer;
+ uint64_t mCount;
+
+ static StaticAutoPtr<SandboxReporter> sSingleton;
+
+ void ThreadMain(void) override;
+ void AddOne(const SandboxReport& aReport);
+};
+
+// This is a constant so the % operations can be optimized. This is
+// exposed in the header so that unit tests can see it.
+static const size_t kSandboxReporterBufferSize = 32;
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxReporter_h
diff --git a/security/sandbox/linux/reporter/SandboxReporterCommon.h b/security/sandbox/linux/reporter/SandboxReporterCommon.h
new file mode 100644
index 0000000000..9bfb40bb98
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporterCommon.h
@@ -0,0 +1,66 @@
+/* -*- 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/. */
+
+#ifndef mozilla_SandboxReporterCommon_h
+#define mozilla_SandboxReporterCommon_h
+
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Types.h"
+
+#include <sys/types.h>
+
+// Note: this is also used in libmozsandbox, so dependencies on
+// symbols from libxul probably won't work.
+
+namespace mozilla {
+static const size_t kSandboxSyscallArguments = 6;
+// fds 0-2: stdio; fd 3: IPC; fd 4: crash reporter. (The IPC child
+// process launching code will check that we don't try to use the same
+// fd twice.)
+static const int kSandboxReporterFileDesc = 5;
+
+// This struct represents a system call that was rejected by a
+// seccomp-bpf policy.
+struct SandboxReport {
+ // In the future this may include finer distinctions than
+ // GeckoProcessType -- e.g., whether a content process can load
+ // file:/// URLs, or if it's reserved for content with certain
+ // user-granted permissions.
+ enum class ProcType : uint8_t {
+ CONTENT,
+ FILE,
+ MEDIA_PLUGIN,
+ RDD,
+ SOCKET_PROCESS,
+ UTILITY,
+ };
+
+ // The syscall number and arguments are usually `unsigned long`, but
+ // that causes ambiguous overload errors with nsACString::AppendInt.
+ using ULong = UnsignedStdintTypeForSize<sizeof(unsigned long)>::Type;
+
+ // This time uses CLOCK_MONOTONIC_COARSE. Displaying or reporting
+ // it should usually be done relative to the current value of that
+ // clock (or the time at some other event of interest, like a
+ // subsequent crash).
+ struct timespec mTime;
+
+ // The pid/tid values, like every other field in this struct, aren't
+ // authenticated and a compromised process could send anything, so
+ // use the values with caution.
+ pid_t mPid;
+ pid_t mTid;
+ ProcType mProcType;
+ ULong mSyscall;
+ ULong mArgs[kSandboxSyscallArguments];
+
+ SandboxReport() : mPid(0) {}
+ bool IsValid() const { return mPid > 0; }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxReporterCommon_h
diff --git a/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp b/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
new file mode 100644
index 0000000000..755e8e858d
--- /dev/null
+++ b/security/sandbox/linux/reporter/SandboxReporterWrappers.cpp
@@ -0,0 +1,199 @@
+/* -*- 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/. */
+
+#include "mozISandboxReporter.h"
+#include "SandboxReporter.h"
+
+#include <time.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Components.h"
+#include "nsCOMPtr.h"
+#include "nsPrintfCString.h"
+#include "nsTArray.h"
+#include "nsXULAppAPI.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+
+class SandboxReportWrapper final : public mozISandboxReport {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISANDBOXREPORT
+
+ explicit SandboxReportWrapper(const SandboxReport& aReport)
+ : mReport(aReport) {}
+
+ private:
+ ~SandboxReportWrapper() = default;
+ SandboxReport mReport;
+};
+
+NS_IMPL_ISUPPORTS(SandboxReportWrapper, mozISandboxReport)
+
+/* readonly attribute uint64_t msecAgo; */
+NS_IMETHODIMP SandboxReportWrapper::GetMsecAgo(uint64_t* aMsec) {
+ struct timespec then = mReport.mTime, now = {0, 0};
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &now);
+
+ const uint64_t now_msec = uint64_t(now.tv_sec) * 1000 + now.tv_nsec / 1000000;
+ const uint64_t then_msec =
+ uint64_t(then.tv_sec) * 1000 + then.tv_nsec / 1000000;
+ MOZ_DIAGNOSTIC_ASSERT(now_msec >= then_msec);
+ if (now_msec >= then_msec) {
+ *aMsec = now_msec - then_msec;
+ } else {
+ *aMsec = 0;
+ }
+ return NS_OK;
+}
+
+/* readonly attribute int32_t pid; */
+NS_IMETHODIMP SandboxReportWrapper::GetPid(int32_t* aPid) {
+ *aPid = mReport.mPid;
+ return NS_OK;
+}
+
+/* readonly attribute int32_t tid; */
+NS_IMETHODIMP SandboxReportWrapper::GetTid(int32_t* aTid) {
+ *aTid = mReport.mTid;
+ return NS_OK;
+}
+
+/* readonly attribute ACString procType; */
+NS_IMETHODIMP SandboxReportWrapper::GetProcType(nsACString& aProcType) {
+ switch (mReport.mProcType) {
+ case SandboxReport::ProcType::CONTENT:
+ aProcType.AssignLiteral("content");
+ return NS_OK;
+ case SandboxReport::ProcType::FILE:
+ aProcType.AssignLiteral("file");
+ return NS_OK;
+ case SandboxReport::ProcType::MEDIA_PLUGIN:
+ aProcType.AssignLiteral("mediaPlugin");
+ return NS_OK;
+ case SandboxReport::ProcType::RDD:
+ aProcType.AssignLiteral("dataDecoder");
+ return NS_OK;
+ case SandboxReport::ProcType::SOCKET_PROCESS:
+ aProcType.AssignLiteral("socketProcess");
+ return NS_OK;
+ case SandboxReport::ProcType::UTILITY:
+ aProcType.AssignLiteral("utility");
+ return NS_OK;
+ default:
+ MOZ_ASSERT(false);
+ return NS_ERROR_UNEXPECTED;
+ }
+}
+
+/* readonly attribute uint32_t syscall; */
+NS_IMETHODIMP SandboxReportWrapper::GetSyscall(uint32_t* aSyscall) {
+ *aSyscall = static_cast<uint32_t>(mReport.mSyscall);
+ MOZ_ASSERT(static_cast<SandboxReport::ULong>(*aSyscall) == mReport.mSyscall);
+ return NS_OK;
+}
+
+/* readonly attribute uint32_t numArgs; */
+NS_IMETHODIMP SandboxReportWrapper::GetNumArgs(uint32_t* aNumArgs) {
+ *aNumArgs = static_cast<uint32_t>(kSandboxSyscallArguments);
+ return NS_OK;
+}
+
+/* ACString getArg (in uint32_t aIndex); */
+NS_IMETHODIMP SandboxReportWrapper::GetArg(uint32_t aIndex,
+ nsACString& aRetval) {
+ if (aIndex >= kSandboxSyscallArguments) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ const auto arg = mReport.mArgs[aIndex];
+ nsAutoCString str;
+ // Use decimal for smaller numbers (more likely ints) and hex for
+ // larger (more likely pointers). This cutoff is arbitrary.
+ if (arg >= 1000000) {
+ str.AppendLiteral("0x");
+ str.AppendInt(arg, 16);
+ } else {
+ str.AppendInt(arg, 10);
+ }
+ aRetval = str;
+ return NS_OK;
+}
+
+class SandboxReportArray final : public mozISandboxReportArray {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISANDBOXREPORTARRAY
+
+ explicit SandboxReportArray(SandboxReporter::Snapshot&& aSnap)
+ : mOffset(aSnap.mOffset), mArray(std::move(aSnap.mReports)) {}
+
+ private:
+ ~SandboxReportArray() = default;
+ uint64_t mOffset;
+ nsTArray<SandboxReport> mArray;
+};
+
+NS_IMPL_ISUPPORTS(SandboxReportArray, mozISandboxReportArray)
+
+/* readonly attribute uint64_t begin; */
+NS_IMETHODIMP SandboxReportArray::GetBegin(uint64_t* aBegin) {
+ *aBegin = mOffset;
+ return NS_OK;
+}
+
+/* readonly attribute uint64_t end; */
+NS_IMETHODIMP SandboxReportArray::GetEnd(uint64_t* aEnd) {
+ *aEnd = mOffset + mArray.Length();
+ return NS_OK;
+}
+
+/* mozISandboxReport getElement (in uint64_t aIndex); */
+NS_IMETHODIMP SandboxReportArray::GetElement(uint64_t aIndex,
+ mozISandboxReport** aRetval) {
+ uint64_t relIndex = aIndex - mOffset;
+ if (relIndex >= mArray.Length()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsCOMPtr<mozISandboxReport> wrapper =
+ new SandboxReportWrapper(mArray[relIndex]);
+ wrapper.forget(aRetval);
+ return NS_OK;
+}
+
+class SandboxReporterWrapper final : public mozISandboxReporter {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISANDBOXREPORTER
+
+ SandboxReporterWrapper() = default;
+
+ private:
+ ~SandboxReporterWrapper() = default;
+};
+
+NS_IMPL_ISUPPORTS(SandboxReporterWrapper, mozISandboxReporter)
+
+/* mozISandboxReportArray snapshot(); */
+NS_IMETHODIMP SandboxReporterWrapper::Snapshot(
+ mozISandboxReportArray** aRetval) {
+ if (!XRE_IsParentProcess()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsCOMPtr<mozISandboxReportArray> wrapper =
+ new SandboxReportArray(SandboxReporter::Singleton()->GetSnapshot());
+ wrapper.forget(aRetval);
+ return NS_OK;
+}
+
+} // namespace mozilla
+
+NS_IMPL_COMPONENT_FACTORY(mozISandboxReporter) {
+ return MakeAndAddRef<SandboxReporterWrapper>().downcast<nsISupports>();
+}
diff --git a/security/sandbox/linux/reporter/components.conf b/security/sandbox/linux/reporter/components.conf
new file mode 100644
index 0000000000..7bd278f349
--- /dev/null
+++ b/security/sandbox/linux/reporter/components.conf
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{5118a6f9-2493-4f97-9552-620663e03cb3}',
+ 'contract_ids': ['@mozilla.org/sandbox/syscall-reporter;1'],
+ 'type': 'mozISandboxReporter',
+ },
+]
diff --git a/security/sandbox/linux/reporter/moz.build b/security/sandbox/linux/reporter/moz.build
new file mode 100644
index 0000000000..d5b037fd09
--- /dev/null
+++ b/security/sandbox/linux/reporter/moz.build
@@ -0,0 +1,34 @@
+# -*- Mode: python; python-indent: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "SandboxReporter.h",
+ "SandboxReporterCommon.h",
+]
+
+UNIFIED_SOURCES += [
+ "SandboxReporter.cpp",
+ "SandboxReporterWrappers.cpp",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+LOCAL_INCLUDES += [
+ "/security/sandbox/linux", # SandboxLogging.h
+]
+
+# Need this for base::PlatformThread
+include("/ipc/chromium/chromium-config.mozbuild")
+
+# Need this for safe_sprintf.h used by SandboxLogging.h,
+# but it has to be after ipc/chromium/src.
+LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/mac/Sandbox.h b/security/sandbox/mac/Sandbox.h
new file mode 100644
index 0000000000..3500182292
--- /dev/null
+++ b/security/sandbox/mac/Sandbox.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_Sandbox_h
+#define mozilla_Sandbox_h
+
+#include <string>
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+
+enum MacSandboxType {
+ MacSandboxType_Default = 0,
+ MacSandboxType_Content,
+ MacSandboxType_GMP,
+ MacSandboxType_RDD,
+ MacSandboxType_Socket,
+ MacSandboxType_Utility,
+ MacSandboxType_Invalid
+};
+
+typedef struct _MacSandboxInfo {
+ _MacSandboxInfo()
+ : type(MacSandboxType_Default),
+ level(0),
+ hasFilePrivileges(false),
+ hasSandboxedProfile(false),
+ hasAudio(false),
+ hasWindowServer(false),
+ shouldLog(false) {}
+ _MacSandboxInfo(const struct _MacSandboxInfo& other) = default;
+
+ void AppendAsParams(std::vector<std::string>& aParams) const;
+ static void AppendFileAccessParam(std::vector<std::string>& aParams,
+ bool aHasFilePrivileges);
+
+ private:
+ void AppendStartupParam(std::vector<std::string>& aParams) const;
+ void AppendLoggingParam(std::vector<std::string>& aParams) const;
+ void AppendAppPathParam(std::vector<std::string>& aParams) const;
+ void AppendPluginPathParam(std::vector<std::string>& aParams) const;
+ void AppendLevelParam(std::vector<std::string>& aParams) const;
+ void AppendAudioParam(std::vector<std::string>& aParams) const;
+ void AppendWindowServerParam(std::vector<std::string>& aParams) const;
+ void AppendReadPathParams(std::vector<std::string>& aParams) const;
+#ifdef DEBUG
+ void AppendDebugWriteDirParam(std::vector<std::string>& aParams) const;
+#endif
+
+ public:
+ MacSandboxType type;
+ mozilla::ipc::SandboxingKind utilityKind;
+ int32_t level;
+ bool hasFilePrivileges;
+ bool hasSandboxedProfile;
+ bool hasAudio;
+ bool hasWindowServer;
+
+ std::string appPath;
+ std::string appBinaryPath;
+ std::string appDir;
+ std::string profileDir;
+ std::string debugWriteDir;
+
+ std::string pluginPath;
+ std::string pluginBinaryPath;
+
+ std::string testingReadPath1;
+ std::string testingReadPath2;
+ std::string testingReadPath3;
+ std::string testingReadPath4;
+
+ std::string crashServerPort;
+
+ bool shouldLog;
+} MacSandboxInfo;
+
+namespace mozilla {
+
+bool StartMacSandbox(MacSandboxInfo const& aInfo, std::string& aErrorMessage);
+bool StartMacSandboxIfEnabled(MacSandboxType aSandboxType, int aArgc,
+ char** aArgv, std::string& aErrorMessage);
+bool IsMacSandboxStarted();
+#ifdef DEBUG
+void AssertMacSandboxEnabled();
+#endif /* DEBUG */
+
+} // namespace mozilla
+
+#endif // mozilla_Sandbox_h
diff --git a/security/sandbox/mac/Sandbox.mm b/security/sandbox/mac/Sandbox.mm
new file mode 100644
index 0000000000..24589eb00f
--- /dev/null
+++ b/security/sandbox/mac/Sandbox.mm
@@ -0,0 +1,802 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+// The Mac sandbox module is a static library (a Library in moz.build terms)
+// that can be linked into any binary (for example plugin-container or XUL).
+// It must not have dependencies on any other Mozilla module. This is why,
+// for example, it has its own OS X version detection code, rather than
+// linking to nsCocoaFeatures.mm in XUL.
+
+#include "Sandbox.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <iostream>
+#include <sstream>
+#include <vector>
+
+#include "SandboxPolicyContent.h"
+#include "SandboxPolicyGMP.h"
+#include "SandboxPolicyRDD.h"
+#include "SandboxPolicySocket.h"
+#include "SandboxPolicyUtility.h"
+#include "mozilla/Assertions.h"
+
+#include "mozilla/GeckoArgs.h"
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+
+// Undocumented sandbox setup routines.
+extern "C" int sandbox_init_with_parameters(const char* profile, uint64_t flags,
+ const char* const parameters[], char** errorbuf);
+extern "C" void sandbox_free_error(char* errorbuf);
+extern "C" int sandbox_check(pid_t pid, const char* operation, int type, ...);
+
+// Note about "major", "minor" and "bugfix" in the following code:
+//
+// The code decomposes an OS X version number into these components, and in
+// doing so follows Apple's terminology in Gestalt.h. But this is very
+// misleading, because in other contexts Apple uses the "minor" component of
+// an OS X version number to indicate a "major" release (for example the "9"
+// in OS X 10.9.5), and the "bugfix" component to indicate a "minor" release
+// (for example the "5" in OS X 10.9.5).
+class OSXVersion {
+ public:
+ static void Get(int32_t& aMajor, int32_t& aMinor);
+
+ private:
+ static void GetSystemVersion(int32_t& aMajor, int32_t& aMinor, int32_t& aBugFix);
+ static bool mCached;
+ static int32_t mOSXVersionMajor;
+ static int32_t mOSXVersionMinor;
+};
+
+bool OSXVersion::mCached = false;
+int32_t OSXVersion::mOSXVersionMajor;
+int32_t OSXVersion::mOSXVersionMinor;
+
+void OSXVersion::Get(int32_t& aMajor, int32_t& aMinor) {
+ if (!mCached) {
+ int32_t major, minor, bugfix;
+ GetSystemVersion(major, minor, bugfix);
+ mOSXVersionMajor = major;
+ mOSXVersionMinor = minor;
+ mCached = true;
+ }
+ aMajor = mOSXVersionMajor;
+ aMinor = mOSXVersionMinor;
+}
+
+void OSXVersion::GetSystemVersion(int32_t& aMajor, int32_t& aMinor, int32_t& aBugFix) {
+ SInt32 major = 0, minor = 0, bugfix = 0;
+
+ CFURLRef url = CFURLCreateWithString(
+ kCFAllocatorDefault, CFSTR("file:///System/Library/CoreServices/SystemVersion.plist"), NULL);
+ CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ CFReadStreamOpen(stream);
+ CFDictionaryRef sysVersionPlist = (CFDictionaryRef)CFPropertyListCreateWithStream(
+ kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL, NULL);
+ CFReadStreamClose(stream);
+ CFRelease(stream);
+ CFRelease(url);
+
+ CFStringRef versionString =
+ (CFStringRef)CFDictionaryGetValue(sysVersionPlist, CFSTR("ProductVersion"));
+ CFArrayRef versions =
+ CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, versionString, CFSTR("."));
+ CFIndex count = CFArrayGetCount(versions);
+ if (count > 0) {
+ CFStringRef component = (CFStringRef)CFArrayGetValueAtIndex(versions, 0);
+ major = CFStringGetIntValue(component);
+ if (count > 1) {
+ component = (CFStringRef)CFArrayGetValueAtIndex(versions, 1);
+ minor = CFStringGetIntValue(component);
+ if (count > 2) {
+ component = (CFStringRef)CFArrayGetValueAtIndex(versions, 2);
+ bugfix = CFStringGetIntValue(component);
+ }
+ }
+ }
+ CFRelease(sysVersionPlist);
+ CFRelease(versions);
+
+ if (major < 10) {
+ // If 'major' isn't what we expect, assume 10.6.
+ aMajor = 10;
+ aMinor = 6;
+ aBugFix = 0;
+ } else if ((major == 10) && (minor >= 16)) {
+ // Account for SystemVersionCompat.plist being used which is
+ // automatically used for builds using older SDK versions and
+ // results in 11.0 being reported as 10.16. Assume the compat
+ // version will increase in step with the correct version.
+ aMajor = 11;
+ aMinor = minor - 16;
+ aBugFix = bugfix;
+ } else {
+ aMajor = major;
+ aMinor = minor;
+ aBugFix = bugfix;
+ }
+}
+
+bool GetRealPath(std::string& aOutputPath, const char* aInputPath) {
+ char* resolvedPath = realpath(aInputPath, nullptr);
+ if (resolvedPath == nullptr) {
+ return false;
+ }
+
+ aOutputPath = resolvedPath;
+ free(resolvedPath);
+
+ return !aOutputPath.empty();
+}
+
+/*
+ * Returns true if the process is running under Rosetta translation. Returns
+ * false if running natively or if an error was encountered. To be called
+ * before enabling the sandbox therefore not requiring the sysctl be allowed
+ * by the sandbox policy. We use the `sysctl.proc_translated` sysctl which is
+ * documented by Apple to be used for this purpose.
+ */
+bool ProcessIsRosettaTranslated() {
+ int ret = 0;
+ size_t size = sizeof(ret);
+ if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "Failed to check for translation environment\n");
+ }
+ return false;
+ }
+ return (ret == 1);
+}
+
+void MacSandboxInfo::AppendAsParams(std::vector<std::string>& aParams) const {
+ this->AppendStartupParam(aParams);
+ this->AppendLoggingParam(aParams);
+ this->AppendAppPathParam(aParams);
+
+ switch (this->type) {
+ case MacSandboxType_Content:
+ this->AppendLevelParam(aParams);
+ this->AppendAudioParam(aParams);
+ this->AppendWindowServerParam(aParams);
+ this->AppendReadPathParams(aParams);
+#ifdef DEBUG
+ this->AppendDebugWriteDirParam(aParams);
+#endif
+ break;
+ case MacSandboxType_RDD:
+ case MacSandboxType_Socket:
+ case MacSandboxType_Utility:
+ break;
+ case MacSandboxType_GMP:
+ this->AppendPluginPathParam(aParams);
+ this->AppendWindowServerParam(aParams);
+ this->AppendReadPathParams(aParams);
+ break;
+ default:
+ // Before supporting a new process type, add a case statement
+ // here to append any neccesary process-type-specific params.
+ MOZ_RELEASE_ASSERT(false);
+ break;
+ }
+}
+
+void MacSandboxInfo::AppendStartupParam(std::vector<std::string>& aParams) const {
+ aParams.push_back("-sbStartup");
+}
+
+void MacSandboxInfo::AppendLoggingParam(std::vector<std::string>& aParams) const {
+ if (this->shouldLog) {
+ aParams.push_back("-sbLogging");
+ }
+}
+
+void MacSandboxInfo::AppendAppPathParam(std::vector<std::string>& aParams) const {
+ aParams.push_back("-sbAppPath");
+ aParams.push_back(this->appPath);
+}
+
+void MacSandboxInfo::AppendPluginPathParam(std::vector<std::string>& aParams) const {
+ aParams.push_back("-sbPluginPath");
+ aParams.push_back(this->pluginPath);
+}
+
+/* static */
+void MacSandboxInfo::AppendFileAccessParam(std::vector<std::string>& aParams,
+ bool aHasFilePrivileges) {
+ if (aHasFilePrivileges) {
+ aParams.push_back("-sbAllowFileAccess");
+ }
+}
+
+void MacSandboxInfo::AppendLevelParam(std::vector<std::string>& aParams) const {
+ std::ostringstream os;
+ os << this->level;
+ std::string levelString = os.str();
+ aParams.push_back("-sbLevel");
+ aParams.push_back(levelString);
+}
+
+void MacSandboxInfo::AppendAudioParam(std::vector<std::string>& aParams) const {
+ if (this->hasAudio) {
+ aParams.push_back("-sbAllowAudio");
+ }
+}
+
+void MacSandboxInfo::AppendWindowServerParam(std::vector<std::string>& aParams) const {
+ if (this->hasWindowServer) {
+ aParams.push_back("-sbAllowWindowServer");
+ }
+}
+
+void MacSandboxInfo::AppendReadPathParams(std::vector<std::string>& aParams) const {
+ if (!this->testingReadPath1.empty()) {
+ aParams.push_back("-sbTestingReadPath");
+ aParams.push_back(this->testingReadPath1.c_str());
+ }
+ if (!this->testingReadPath2.empty()) {
+ aParams.push_back("-sbTestingReadPath");
+ aParams.push_back(this->testingReadPath2.c_str());
+ }
+ if (!this->testingReadPath3.empty()) {
+ aParams.push_back("-sbTestingReadPath");
+ aParams.push_back(this->testingReadPath3.c_str());
+ }
+ if (!this->testingReadPath4.empty()) {
+ aParams.push_back("-sbTestingReadPath");
+ aParams.push_back(this->testingReadPath4.c_str());
+ }
+}
+
+#ifdef DEBUG
+void MacSandboxInfo::AppendDebugWriteDirParam(std::vector<std::string>& aParams) const {
+ if (!this->debugWriteDir.empty()) {
+ aParams.push_back("-sbDebugWriteDir");
+ aParams.push_back(this->debugWriteDir.c_str());
+ }
+}
+#endif
+
+namespace mozilla {
+
+bool StartMacSandbox(MacSandboxInfo const& aInfo, std::string& aErrorMessage) {
+ std::vector<const char*> params;
+ std::string profile;
+
+ // Use a combined version number to simplify version check logic
+ // in sandbox policies. For example, 10.14 becomes "1014".
+ int32_t major = 0, minor = 0;
+ OSXVersion::Get(major, minor);
+ MOZ_ASSERT(minor >= 0 && minor < 100);
+ std::string combinedVersion = std::to_string((major * 100) + minor);
+
+ params.push_back("IS_ROSETTA_TRANSLATED");
+ params.push_back(ProcessIsRosettaTranslated() ? "TRUE" : "FALSE");
+
+ // Used for the content process to access to parts of the cache dir.
+ std::string userCacheDir;
+
+ if (aInfo.type == MacSandboxType_Utility) {
+ profile = const_cast<char*>(SandboxPolicyUtility);
+
+ switch (aInfo.utilityKind) {
+ case ipc::SandboxingKind::GENERIC_UTILITY:
+ // Nothing to do here specifically
+ break;
+
+ case ipc::SandboxingKind::UTILITY_AUDIO_DECODING_APPLE_MEDIA: {
+ profile.append(SandboxPolicyUtilityAudioDecoderAppleMediaAddend);
+ params.push_back("MAC_OS_VERSION");
+ params.push_back(combinedVersion.c_str());
+ } break;
+
+ default:
+ MOZ_ASSERT(false, "Invalid SandboxingKind");
+ break;
+ }
+ params.push_back("SHOULD_LOG");
+ params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+ params.push_back("APP_PATH");
+ params.push_back(aInfo.appPath.c_str());
+ if (!aInfo.crashServerPort.empty()) {
+ params.push_back("CRASH_PORT");
+ params.push_back(aInfo.crashServerPort.c_str());
+ }
+ } else if (aInfo.type == MacSandboxType_RDD) {
+ profile = const_cast<char*>(SandboxPolicyRDD);
+ params.push_back("SHOULD_LOG");
+ params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+ params.push_back("MAC_OS_VERSION");
+ params.push_back(combinedVersion.c_str());
+ params.push_back("APP_PATH");
+ params.push_back(aInfo.appPath.c_str());
+ params.push_back("HOME_PATH");
+ params.push_back(getenv("HOME"));
+ if (!aInfo.crashServerPort.empty()) {
+ params.push_back("CRASH_PORT");
+ params.push_back(aInfo.crashServerPort.c_str());
+ }
+ } else if (aInfo.type == MacSandboxType_Socket) {
+ profile = const_cast<char*>(SandboxPolicySocket);
+ params.push_back("SHOULD_LOG");
+ params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+ params.push_back("APP_PATH");
+ params.push_back(aInfo.appPath.c_str());
+ if (!aInfo.crashServerPort.empty()) {
+ params.push_back("CRASH_PORT");
+ params.push_back(aInfo.crashServerPort.c_str());
+ }
+ params.push_back("HOME_PATH");
+ params.push_back(getenv("HOME"));
+ } else if (aInfo.type == MacSandboxType_GMP) {
+ profile = const_cast<char*>(SandboxPolicyGMP);
+ params.push_back("SHOULD_LOG");
+ params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+ params.push_back("APP_PATH");
+ params.push_back(aInfo.appPath.c_str());
+ params.push_back("PLUGIN_PATH");
+ params.push_back(aInfo.pluginPath.c_str());
+ if (!aInfo.pluginBinaryPath.empty()) {
+ params.push_back("PLUGIN_BINARY_PATH");
+ params.push_back(aInfo.pluginBinaryPath.c_str());
+ }
+ params.push_back("HAS_WINDOW_SERVER");
+ params.push_back(aInfo.hasWindowServer ? "TRUE" : "FALSE");
+ if (!aInfo.crashServerPort.empty()) {
+ params.push_back("CRASH_PORT");
+ params.push_back(aInfo.crashServerPort.c_str());
+ }
+ if (!aInfo.testingReadPath1.empty()) {
+ params.push_back("TESTING_READ_PATH1");
+ params.push_back(aInfo.testingReadPath1.c_str());
+ }
+ if (!aInfo.testingReadPath2.empty()) {
+ params.push_back("TESTING_READ_PATH2");
+ params.push_back(aInfo.testingReadPath2.c_str());
+ }
+ } else if (aInfo.type == MacSandboxType_Content) {
+ MOZ_ASSERT(aInfo.level >= 1);
+ if (aInfo.level >= 1) {
+ profile = SandboxPolicyContent;
+ params.push_back("SHOULD_LOG");
+ params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+ params.push_back("SANDBOX_LEVEL_1");
+ params.push_back(aInfo.level == 1 ? "TRUE" : "FALSE");
+ params.push_back("SANDBOX_LEVEL_2");
+ params.push_back(aInfo.level == 2 ? "TRUE" : "FALSE");
+ params.push_back("SANDBOX_LEVEL_3");
+ params.push_back(aInfo.level == 3 ? "TRUE" : "FALSE");
+ params.push_back("MAC_OS_VERSION");
+ params.push_back(combinedVersion.c_str());
+ params.push_back("APP_PATH");
+ params.push_back(aInfo.appPath.c_str());
+ params.push_back("PROFILE_DIR");
+ params.push_back(aInfo.profileDir.c_str());
+ params.push_back("HOME_PATH");
+ params.push_back(getenv("HOME"));
+ params.push_back("HAS_SANDBOXED_PROFILE");
+ params.push_back(aInfo.hasSandboxedProfile ? "TRUE" : "FALSE");
+ params.push_back("HAS_WINDOW_SERVER");
+ params.push_back(aInfo.hasWindowServer ? "TRUE" : "FALSE");
+ if (!aInfo.crashServerPort.empty()) {
+ params.push_back("CRASH_PORT");
+ params.push_back(aInfo.crashServerPort.c_str());
+ }
+
+ params.push_back("DARWIN_USER_CACHE_DIR");
+ char confStrBuf[PATH_MAX];
+ if (!confstr(_CS_DARWIN_USER_CACHE_DIR, confStrBuf, sizeof(confStrBuf))) {
+ return false;
+ }
+ if (!GetRealPath(userCacheDir, confStrBuf)) {
+ return false;
+ }
+ params.push_back(userCacheDir.c_str());
+
+ if (!aInfo.testingReadPath1.empty()) {
+ params.push_back("TESTING_READ_PATH1");
+ params.push_back(aInfo.testingReadPath1.c_str());
+ }
+ if (!aInfo.testingReadPath2.empty()) {
+ params.push_back("TESTING_READ_PATH2");
+ params.push_back(aInfo.testingReadPath2.c_str());
+ }
+ if (!aInfo.testingReadPath3.empty()) {
+ params.push_back("TESTING_READ_PATH3");
+ params.push_back(aInfo.testingReadPath3.c_str());
+ }
+ if (!aInfo.testingReadPath4.empty()) {
+ params.push_back("TESTING_READ_PATH4");
+ params.push_back(aInfo.testingReadPath4.c_str());
+ }
+#ifdef DEBUG
+ if (!aInfo.debugWriteDir.empty()) {
+ params.push_back("DEBUG_WRITE_DIR");
+ params.push_back(aInfo.debugWriteDir.c_str());
+ }
+#endif // DEBUG
+
+ if (aInfo.hasFilePrivileges) {
+ profile.append(SandboxPolicyContentFileAddend);
+ }
+ if (aInfo.hasAudio) {
+ profile.append(SandboxPolicyContentAudioAddend);
+ }
+ } else {
+ fprintf(stderr, "Content sandbox disabled due to sandbox level setting\n");
+ return false;
+ }
+ } else {
+ char* msg = NULL;
+ asprintf(&msg, "Unexpected sandbox type %u", aInfo.type);
+ if (msg) {
+ aErrorMessage.assign(msg);
+ free(msg);
+ }
+ return false;
+ }
+
+ if (profile.empty()) {
+ fprintf(stderr, "Out of memory in StartMacSandbox()!\n");
+ return false;
+ }
+
+// In order to avoid relying on any other Mozilla modules (as described at the
+// top of this file), we use our own #define instead of the existing MOZ_LOG
+// infrastructure. This can be used by developers to debug the macOS sandbox
+// policy.
+#define MAC_SANDBOX_PRINT_POLICY 0
+#if MAC_SANDBOX_PRINT_POLICY
+ printf("Sandbox params for PID %d:\n", getpid());
+ for (size_t i = 0; i < params.size() / 2; i++) {
+ printf(" %s = %s\n", params[i * 2], params[(i * 2) + 1]);
+ }
+ printf("Sandbox profile:\n%s\n", profile.c_str());
+#endif
+
+ // The parameters array is null terminated.
+ params.push_back(nullptr);
+
+ char* errorbuf = NULL;
+ int rv = sandbox_init_with_parameters(profile.c_str(), 0, params.data(), &errorbuf);
+ if (rv) {
+ if (errorbuf) {
+ char* msg = NULL;
+ asprintf(&msg, "sandbox_init() failed with error \"%s\"", errorbuf);
+ if (msg) {
+ aErrorMessage.assign(msg);
+ free(msg);
+ }
+ fprintf(stderr, "profile: %s\n", profile.c_str());
+ sandbox_free_error(errorbuf);
+ }
+ }
+ if (rv) {
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Fill |aInfo| with content sandbox params parsed from the provided
+ * command line arguments. Return false if any sandbox parameters needed
+ * for early startup of the sandbox are not present in the arguments.
+ */
+bool GetContentSandboxParamsFromArgs(int aArgc, char** aArgv, MacSandboxInfo& aInfo) {
+ // Ensure we find these paramaters in the command
+ // line arguments. Return false if any are missing.
+ bool foundSandboxLevel = false;
+ bool foundValidSandboxLevel = false;
+ bool foundAppPath = false;
+
+ // Read access directories used in testing
+ int nTestingReadPaths = 0;
+ std::string testingReadPaths[MAX_CONTENT_TESTING_READ_PATHS] = {};
+
+ // Collect sandbox params from CLI arguments
+ for (int i = 0; i < aArgc; i++) {
+ if ((strcmp(aArgv[i], "-sbLevel") == 0) && (i + 1 < aArgc)) {
+ std::stringstream ss(aArgv[i + 1]);
+ int level = 0;
+ ss >> level;
+ foundSandboxLevel = true;
+ aInfo.level = level;
+ foundValidSandboxLevel = level > 0 && level <= 3 ? true : false;
+ if (!foundValidSandboxLevel) {
+ break;
+ }
+ i++;
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-sbLogging") == 0) {
+ aInfo.shouldLog = true;
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-sbAllowFileAccess") == 0) {
+ aInfo.hasFilePrivileges = true;
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-sbAllowAudio") == 0) {
+ aInfo.hasAudio = true;
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-sbAllowWindowServer") == 0) {
+ aInfo.hasWindowServer = true;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-sbAppPath") == 0) && (i + 1 < aArgc)) {
+ foundAppPath = true;
+ aInfo.appPath.assign(aArgv[i + 1]);
+ i++;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-sbTestingReadPath") == 0) && (i + 1 < aArgc)) {
+ if (nTestingReadPaths >= MAX_CONTENT_TESTING_READ_PATHS) {
+ MOZ_CRASH("Too many content process -sbTestingReadPath arguments");
+ }
+ testingReadPaths[nTestingReadPaths] = aArgv[i + 1];
+ nTestingReadPaths++;
+ i++;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-profile") == 0) && (i + 1 < aArgc)) {
+ aInfo.hasSandboxedProfile = true;
+ aInfo.profileDir.assign(aArgv[i + 1]);
+ i++;
+ continue;
+ }
+
+#ifdef DEBUG
+ if ((strcmp(aArgv[i], "-sbDebugWriteDir") == 0) && (i + 1 < aArgc)) {
+ aInfo.debugWriteDir.assign(aArgv[i + 1]);
+ i++;
+ continue;
+ }
+#endif // DEBUG
+
+ // Handle crash server positional argument
+ if (strstr(aArgv[i], "gecko-crash-server-pipe") != NULL) {
+ aInfo.crashServerPort.assign(aArgv[i]);
+ continue;
+ }
+ }
+
+ if (!foundSandboxLevel) {
+ fprintf(stderr, "Content sandbox disabled due to "
+ "missing sandbox CLI level parameter.\n");
+ return false;
+ }
+
+ if (!foundValidSandboxLevel) {
+ fprintf(stderr,
+ "Content sandbox disabled due to invalid"
+ "sandbox level (%d)\n",
+ aInfo.level);
+ return false;
+ }
+
+ if (!foundAppPath) {
+ fprintf(stderr, "Content sandbox disabled due to "
+ "missing sandbox CLI app path parameter.\n");
+ return false;
+ }
+
+ aInfo.testingReadPath1 = testingReadPaths[0];
+ aInfo.testingReadPath2 = testingReadPaths[1];
+ aInfo.testingReadPath3 = testingReadPaths[2];
+ aInfo.testingReadPath4 = testingReadPaths[3];
+
+ return true;
+}
+
+bool GetUtilitySandboxParamsFromArgs(int aArgc, char** aArgv, MacSandboxInfo& aInfo,
+ bool aSandboxingKindRequired = true) {
+ // Ensure we find these paramaters in the command
+ // line arguments. Return false if any are missing.
+ bool foundAppPath = false;
+
+ // Collect sandbox params from CLI arguments
+ for (int i = 0; i < aArgc; i++) {
+ if (strcmp(aArgv[i], "-sbLogging") == 0) {
+ aInfo.shouldLog = true;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-sbAppPath") == 0) && (i + 1 < aArgc)) {
+ foundAppPath = true;
+ aInfo.appPath.assign(aArgv[i + 1]);
+ i++;
+ continue;
+ }
+
+ // Handle crash server positional argument
+ if (strstr(aArgv[i], "gecko-crash-server-pipe") != NULL) {
+ aInfo.crashServerPort.assign(aArgv[i]);
+ continue;
+ }
+ }
+
+ if (aSandboxingKindRequired) {
+ Maybe<uint64_t> sandboxingKind =
+ geckoargs::sSandboxingKind.Get(aArgc, aArgv, CheckArgFlag::None);
+ if (sandboxingKind.isNothing()) {
+ fprintf(stderr, "Utility sandbox requires a sandboxingKind");
+ return false;
+ }
+ aInfo.utilityKind = (ipc::SandboxingKind)*sandboxingKind;
+ }
+
+ if (!foundAppPath) {
+ fprintf(stderr, "Utility sandbox disabled due to "
+ "missing sandbox CLI app path parameter.\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool GetSocketSandboxParamsFromArgs(int aArgc, char** aArgv, MacSandboxInfo& aInfo) {
+ return GetUtilitySandboxParamsFromArgs(aArgc, aArgv, aInfo, false);
+}
+
+bool GetPluginSandboxParamsFromArgs(int aArgc, char** aArgv, MacSandboxInfo& aInfo) {
+ // Ensure we find these paramaters in the command
+ // line arguments. Return false if any are missing.
+ bool foundAppPath = false;
+ bool foundPluginPath = false;
+
+ // Read access directories used in testing
+ int nTestingReadPaths = 0;
+ std::string testingReadPaths[MAX_GMP_TESTING_READ_PATHS] = {};
+
+ // Collect sandbox params from CLI arguments
+ for (int i = 0; i < aArgc; i++) {
+ if (strcmp(aArgv[i], "-sbLogging") == 0) {
+ aInfo.shouldLog = true;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-sbAppPath") == 0) && (i + 1 < aArgc)) {
+ foundAppPath = true;
+ aInfo.appPath.assign(aArgv[i + 1]);
+ i++;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-sbPluginPath") == 0) && (i + 1 < aArgc)) {
+ foundPluginPath = true;
+ aInfo.pluginPath.assign(aArgv[i + 1]);
+ i++;
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-sbAllowWindowServer") == 0) {
+ aInfo.hasWindowServer = true;
+ continue;
+ }
+
+ if ((strcmp(aArgv[i], "-sbTestingReadPath") == 0) && (i + 1 < aArgc)) {
+ if (nTestingReadPaths >= MAX_GMP_TESTING_READ_PATHS) {
+ MOZ_CRASH("Too many GMP process -sbTestingReadPath arguments");
+ }
+ testingReadPaths[nTestingReadPaths] = aArgv[i + 1];
+ nTestingReadPaths++;
+ i++;
+ continue;
+ }
+
+ // Handle crash server positional argument
+ if (strstr(aArgv[i], "gecko-crash-server-pipe") != NULL) {
+ aInfo.crashServerPort.assign(aArgv[i]);
+ continue;
+ }
+ }
+
+ if (!foundPluginPath) {
+ fprintf(stderr, "GMP sandbox disabled due to "
+ "missing sandbox CLI plugin path parameter.\n");
+ return false;
+ }
+
+ if (!foundAppPath) {
+ fprintf(stderr, "GMP sandbox disabled due to "
+ "missing sandbox CLI app path parameter.\n");
+ return false;
+ }
+
+ aInfo.testingReadPath1 = testingReadPaths[0];
+ aInfo.testingReadPath2 = testingReadPaths[1];
+
+ return true;
+}
+
+bool GetRDDSandboxParamsFromArgs(int aArgc, char** aArgv, MacSandboxInfo& aInfo) {
+ return GetUtilitySandboxParamsFromArgs(aArgc, aArgv, aInfo, false);
+}
+
+/*
+ * Returns true if no errors were encountered or if early sandbox startup is
+ * not enabled for this process. Returns false if an error was encountered.
+ */
+bool StartMacSandboxIfEnabled(const MacSandboxType aSandboxType, int aArgc, char** aArgv,
+ std::string& aErrorMessage) {
+ bool earlyStartupEnabled = false;
+
+ // Check for the -sbStartup CLI parameter which
+ // indicates we should start the sandbox now.
+ for (int i = 0; i < aArgc; i++) {
+ if (strcmp(aArgv[i], "-sbStartup") == 0) {
+ earlyStartupEnabled = true;
+ break;
+ }
+ }
+
+ // The sandbox will be started later when/if parent
+ // sends the sandbox startup message. Return true
+ // indicating no errors occurred.
+ if (!earlyStartupEnabled) {
+ return true;
+ }
+
+ MacSandboxInfo info;
+ info.type = aSandboxType;
+
+ // For now, early start is only implemented
+ // for content and utility sandbox types.
+ switch (aSandboxType) {
+ case MacSandboxType_Content:
+ if (!GetContentSandboxParamsFromArgs(aArgc, aArgv, info)) {
+ return false;
+ }
+ break;
+ case MacSandboxType_GMP:
+ if (!GetPluginSandboxParamsFromArgs(aArgc, aArgv, info)) {
+ return false;
+ }
+ break;
+ case MacSandboxType_RDD:
+ if (!GetRDDSandboxParamsFromArgs(aArgc, aArgv, info)) {
+ return false;
+ }
+ break;
+ case MacSandboxType_Socket:
+ if (!GetSocketSandboxParamsFromArgs(aArgc, aArgv, info)) {
+ return false;
+ }
+ break;
+ case MacSandboxType_Utility:
+ if (!GetUtilitySandboxParamsFromArgs(aArgc, aArgv, info)) {
+ return false;
+ }
+ break;
+ default:
+ MOZ_RELEASE_ASSERT(false);
+ break;
+ }
+
+ return StartMacSandbox(info, aErrorMessage);
+}
+
+bool IsMacSandboxStarted() { return sandbox_check(getpid(), NULL, 0) == 1; }
+
+#ifdef DEBUG
+// sandbox_check returns 1 if the specified process is sandboxed
+void AssertMacSandboxEnabled() { MOZ_ASSERT(sandbox_check(getpid(), NULL, 0) == 1); }
+#endif /* DEBUG */
+
+} // namespace mozilla
diff --git a/security/sandbox/mac/SandboxPolicyContent.h b/security/sandbox/mac/SandboxPolicyContent.h
new file mode 100644
index 0000000000..601b4bdc12
--- /dev/null
+++ b/security/sandbox/mac/SandboxPolicyContent.h
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_SandboxPolicyContent_h
+#define mozilla_SandboxPolicyContent_h
+
+#define MAX_CONTENT_TESTING_READ_PATHS 4
+
+namespace mozilla {
+
+static const char SandboxPolicyContent[] = R"SANDBOX_LITERAL(
+ (version 1)
+
+ (define should-log (param "SHOULD_LOG"))
+ (define sandbox-level-1 (param "SANDBOX_LEVEL_1"))
+ (define sandbox-level-2 (param "SANDBOX_LEVEL_2"))
+ (define sandbox-level-3 (param "SANDBOX_LEVEL_3"))
+ (define macosVersion (string->number (param "MAC_OS_VERSION")))
+ (define appPath (param "APP_PATH"))
+ (define hasProfileDir (param "HAS_SANDBOXED_PROFILE"))
+ (define profileDir (param "PROFILE_DIR"))
+ (define hasWindowServer (param "HAS_WINDOW_SERVER"))
+ (define home-path (param "HOME_PATH"))
+ (define debugWriteDir (param "DEBUG_WRITE_DIR"))
+ (define userCacheDir (param "DARWIN_USER_CACHE_DIR"))
+ (define testingReadPath1 (param "TESTING_READ_PATH1"))
+ (define testingReadPath2 (param "TESTING_READ_PATH2"))
+ (define testingReadPath3 (param "TESTING_READ_PATH3"))
+ (define testingReadPath4 (param "TESTING_READ_PATH4"))
+ (define crashPort (param "CRASH_PORT"))
+ (define isRosettaTranslated (param "IS_ROSETTA_TRANSLATED"))
+
+ (define (moz-deny feature)
+ (if (string=? should-log "TRUE")
+ (deny feature)
+ (deny feature (with no-log))))
+
+ (moz-deny default)
+ ; These are not included in (deny default)
+ (moz-deny process-info*)
+ ; This isn't available in some older macOS releases.
+ (if (defined? 'nvram*)
+ (moz-deny nvram*))
+ ; The next two properties both require macOS 10.10+
+ (if (defined? 'iokit-get-properties)
+ (moz-deny iokit-get-properties))
+ (if (defined? 'file-map-executable)
+ (moz-deny file-map-executable))
+
+ (if (string=? should-log "TRUE")
+ (debug deny))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (if (string=? isRosettaTranslated "TRUE")
+ (allow file-map-executable (subpath "/private/var/db/oah")))
+ (allow file-map-executable file-read*
+ (subpath "/System")
+ (subpath "/usr/lib")
+ (subpath "/Library/GPUBundles")
+ (subpath appPath)))
+ (allow file-read*
+ (subpath "/System")
+ (subpath "/usr/lib")
+ (subpath "/Library/GPUBundles")
+ (subpath appPath)))
+
+ ; Allow read access to standard system paths.
+ (allow file-read*
+ (require-all (file-mode #o0004)
+ (require-any
+ (subpath "/Library/Filesystems/NetFSPlugins")
+ (subpath "/usr/share"))))
+
+ ; For stat and symlink resolution
+ (allow file-read-metadata (subpath "/"))
+
+ ; Timezone
+ (allow file-read*
+ (subpath "/private/var/db/timezone")
+ (subpath "/usr/share/zoneinfo")
+ (subpath "/usr/share/zoneinfo.default")
+ (literal "/private/etc/localtime"))
+
+ ; Allow read access to standard special files.
+ (allow file-read*
+ (literal "/dev/autofs_nowait")
+ (literal "/dev/random")
+ (literal "/dev/urandom"))
+
+ (allow file-read*
+ file-write-data
+ (literal "/dev/null")
+ (literal "/dev/zero"))
+
+ (allow file-read*
+ file-write-data
+ file-ioctl
+ (literal "/dev/dtracehelper"))
+
+ ; Needed for things like getpriority()/setpriority()
+ (allow process-info-pidinfo process-info-setcontrol (target self))
+
+ (allow sysctl-read
+ (sysctl-name-regex #"^sysctl\.")
+ (sysctl-name "kern.ostype")
+ (sysctl-name "kern.osversion")
+ (sysctl-name "kern.osrelease")
+ (sysctl-name "kern.version")
+ (sysctl-name "kern.tcsm_available")
+ (sysctl-name "kern.tcsm_enable")
+ ; TODO: remove "kern.hostname". Without it the tests hang, but the hostname
+ ; is arguably sensitive information, so we should see what can be done about
+ ; removing it.
+ (sysctl-name "kern.hostname")
+ (sysctl-name "hw.machine")
+ (sysctl-name "hw.memsize")
+ (sysctl-name "hw.model")
+ (sysctl-name "hw.ncpu")
+ (sysctl-name "hw.activecpu")
+ (sysctl-name "hw.byteorder")
+ (sysctl-name "hw.pagesize_compat")
+ (sysctl-name "hw.logicalcpu")
+ (sysctl-name "hw.logicalcpu_max")
+ (sysctl-name "hw.physicalcpu_max")
+ (sysctl-name "hw.busfrequency_compat")
+ (sysctl-name "hw.busfrequency_max")
+ (sysctl-name "hw.cpufrequency")
+ (sysctl-name "hw.cpufrequency_compat")
+ (sysctl-name "hw.cpufrequency_max")
+ (sysctl-name "hw.l2cachesize")
+ (sysctl-name "hw.l3cachesize")
+ (sysctl-name "hw.cachelinesize")
+ (sysctl-name "hw.cachelinesize_compat")
+ (sysctl-name "hw.tbfrequency_compat")
+ (sysctl-name "hw.vectorunit")
+ (sysctl-name "hw.optional.sse2")
+ (sysctl-name "hw.optional.sse3")
+ (sysctl-name "hw.optional.sse4_1")
+ (sysctl-name "hw.optional.sse4_2")
+ (sysctl-name "hw.optional.avx1_0")
+ (sysctl-name "hw.optional.avx2_0")
+ (sysctl-name "hw.optional.avx512f")
+ (sysctl-name "machdep.cpu.vendor")
+ (sysctl-name "machdep.cpu.family")
+ (sysctl-name "machdep.cpu.model")
+ (sysctl-name "machdep.cpu.stepping")
+ (sysctl-name "debug.intel.gstLevelGST")
+ (sysctl-name "debug.intel.gstLoaderControl"))
+ (allow sysctl-write
+ (sysctl-name "kern.tcsm_enable"))
+
+ (define (home-regex home-relative-regex)
+ (regex (string-append "^" (regex-quote home-path) home-relative-regex)))
+ (define (home-subpath home-relative-subpath)
+ (subpath (string-append home-path home-relative-subpath)))
+ (define (home-literal home-relative-literal)
+ (literal (string-append home-path home-relative-literal)))
+
+ (define (profile-subpath profile-relative-subpath)
+ (subpath (string-append profileDir profile-relative-subpath)))
+
+ (define (allow-shared-list domain)
+ (allow file-read*
+ (home-regex (string-append "/Library/Preferences/" (regex-quote domain)))))
+
+ (allow ipc-posix-shm-read-data ipc-posix-shm-write-data
+ (ipc-posix-name-regex #"^CFPBS:"))
+
+ (allow signal (target self))
+ (if (string? crashPort)
+ (allow mach-lookup (global-name crashPort)))
+ (if (string=? hasWindowServer "TRUE")
+ (allow mach-lookup (global-name "com.apple.windowserver.active")))
+ (allow mach-lookup
+ (global-name "com.apple.system.opendirectoryd.libinfo")
+ (global-name "com.apple.CoreServices.coreservicesd")
+ (global-name "com.apple.coreservices.launchservicesd")
+ (global-name "com.apple.lsd.mapdb"))
+
+ (if (>= macosVersion 1013)
+ (allow mach-lookup
+ ; bug 1392988
+ (xpc-service-name "com.apple.coremedia.videodecoder")
+ (xpc-service-name "com.apple.coremedia.videoencoder")))
+
+ (if (>= macosVersion 1100)
+ (allow mach-lookup
+ ; bug 1655655
+ (global-name "com.apple.trustd.agent")))
+
+ (allow iokit-open
+ (iokit-user-client-class "IOHIDParamUserClient"))
+
+ ; Only supported on macOS 10.10+
+ (if (defined? 'iokit-get-properties)
+ (allow iokit-get-properties
+ (iokit-property "board-id")
+ (iokit-property "class-code")
+ (iokit-property "vendor-id")
+ (iokit-property "device-id")
+ (iokit-property "IODVDBundleName")
+ (iokit-property "IOGLBundleName")
+ (iokit-property "IOGVACodec")
+ (iokit-property "IOGVAHEVCDecode")
+ (iokit-property "IOGVAHEVCEncode")
+ (iokit-property "IOGVAXDecode")
+ (iokit-property "IOPCITunnelled")
+ (iokit-property "IOVARendererID")
+ (iokit-property "MetalPluginName")
+ (iokit-property "MetalPluginClassName")))
+
+ ; depending on systems, the 1st, 2nd or both rules are necessary
+ (allow user-preference-read (preference-domain "com.apple.HIToolbox"))
+ (allow file-read-data (literal "/Library/Preferences/com.apple.HIToolbox.plist"))
+
+ (allow user-preference-read (preference-domain "com.apple.ATS"))
+
+ ; Needed for some global preferences (such as scrolling behavior)
+ (allow file-read-data
+ (literal "/Library/Preferences/.GlobalPreferences.plist")
+ (home-literal "/Library/Preferences/.GlobalPreferences.plist")
+ (home-regex #"/Library/Preferences/ByHost/\.GlobalPreferences.*")
+ (home-literal "/Library/Preferences/com.apple.universalaccess.plist"))
+ (allow mach-lookup
+ (global-name "com.apple.cfprefsd.agent")
+ (global-name "com.apple.cfprefsd.daemon"))
+ (allow ipc-posix-shm-read-data
+ (ipc-posix-name-regex #"^apple\.cfprefs\..*"))
+
+ (allow file-read*
+ (subpath "/Library/ColorSync/Profiles")
+ (subpath "/Library/Spelling")
+ (literal "/")
+ (literal "/private/tmp")
+ (literal "/private/var/tmp")
+ (home-literal "/.CFUserTextEncoding")
+ (home-literal "/Library/Preferences/com.apple.DownloadAssessment.plist")
+ (home-subpath "/Library/Colors")
+ (home-subpath "/Library/ColorSync/Profiles")
+ (home-subpath "/Library/Keyboard Layouts")
+ (home-subpath "/Library/Input Methods")
+ (home-subpath "/Library/Spelling"))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (when testingReadPath1
+ (allow file-read* file-map-executable (subpath testingReadPath1)))
+ (when testingReadPath2
+ (allow file-read* file-map-executable (subpath testingReadPath2)))
+ (when testingReadPath3
+ (allow file-read* file-map-executable (subpath testingReadPath3)))
+ (when testingReadPath4
+ (allow file-read* file-map-executable (subpath testingReadPath4))))
+ (begin
+ (when testingReadPath1
+ (allow file-read* (subpath testingReadPath1)))
+ (when testingReadPath2
+ (allow file-read* (subpath testingReadPath2)))
+ (when testingReadPath3
+ (allow file-read* (subpath testingReadPath3)))
+ (when testingReadPath4
+ (allow file-read* (subpath testingReadPath4)))))
+
+ ; bug 1692220
+ (when userCacheDir
+ (allow file-read*
+ (subpath (string-append userCacheDir "/com.apple.FontRegistry"))))
+
+ ; bug 1303987
+ (if (string? debugWriteDir)
+ (begin
+ (allow file-write-data (subpath debugWriteDir))
+ (allow file-write-create
+ (require-all
+ (subpath debugWriteDir)
+ (vnode-type REGULAR-FILE)))))
+
+ (allow-shared-list "org.mozilla.plugincontainer")
+
+; Per-user and system-wide Extensions dir
+ (allow file-read*
+ (home-regex "/Library/Application Support/[^/]+/Extensions/")
+ (regex "^/Library/Application Support/[^/]+/Extensions/"))
+
+; The following rules impose file access restrictions which get
+; more restrictive in higher levels. When file-origin-specific
+; content processes are used for file:// origin browsing, the
+; global file-read* permission should be removed from each level.
+
+; level 1: global read access permitted, no global write access
+ (if (string=? sandbox-level-1 "TRUE") (allow file-read*))
+
+; level 2: global read access permitted, no global write access,
+; no read/write access to ~/Library,
+; no read/write access to $PROFILE,
+; read access permitted to $PROFILE/{extensions,chrome}
+ (if (string=? sandbox-level-2 "TRUE")
+ (begin
+ ; bug 1201935
+ (allow file-read* (home-subpath "/Library/Caches/TemporaryItems"))
+ (if (string=? hasProfileDir "TRUE")
+ ; we have a profile dir
+ (allow file-read* (require-all
+ (require-not (home-subpath "/Library"))
+ (require-not (subpath profileDir))))
+ ; we don't have a profile dir
+ (allow file-read* (require-not (home-subpath "/Library"))))))
+
+ ; level 3: Does not have any of it's own rules. The global rules provide:
+ ; no global read/write access,
+ ; read access permitted to $PROFILE/{extensions,chrome}
+
+ (if (string=? hasProfileDir "TRUE")
+ ; we have a profile dir
+ (allow file-read*
+ (profile-subpath "/extensions")
+ (profile-subpath "/chrome")))
+
+; accelerated graphics
+ (allow user-preference-read (preference-domain "com.apple.opengl"))
+ (allow user-preference-read (preference-domain "com.nvidia.OpenGL"))
+ (allow mach-lookup
+ (global-name "com.apple.cvmsServ"))
+ (if (>= macosVersion 1014)
+ (allow mach-lookup
+ (global-name "com.apple.MTLCompilerService")))
+ (allow iokit-open
+ (iokit-connection "IOAccelerator")
+ (iokit-user-client-class "IOAccelerationUserClient")
+ (iokit-user-client-class "IOSurfaceRootUserClient")
+ (iokit-user-client-class "IOSurfaceSendRight")
+ (iokit-user-client-class "IOFramebufferSharedUserClient")
+ (iokit-user-client-class "AGPMClient")
+ (iokit-user-client-class "AppleGraphicsControlClient"))
+
+; bug 1153809
+ (allow iokit-open
+ (iokit-user-client-class "NVDVDContextTesla")
+ (iokit-user-client-class "Gen6DVDContext"))
+
+ ; Fonts
+ (allow file-read*
+ (subpath "/Library/Fonts")
+ (subpath "/Library/Application Support/Apple/Fonts")
+ (home-subpath "/Library/Fonts")
+ ; Allow read access to paths allowed via sandbox extensions.
+ ; This is needed for fonts in non-standard locations normally
+ ; due to third party font managers. The extensions are
+ ; automatically issued by the font server in response to font
+ ; API calls.
+ (extension "com.apple.app-sandbox.read"))
+ ; Fonts may continue to work without explicitly allowing these
+ ; services because, at present, connections are made to the services
+ ; before the sandbox is enabled as a side-effect of some API calls.
+ (allow mach-lookup
+ (global-name "com.apple.fonts")
+ (global-name "com.apple.FontObjectsServer"))
+
+ (if (>= macosVersion 1013)
+ (allow mach-lookup
+ ; bug 1565575
+ (global-name "com.apple.audio.AudioComponentRegistrar")))
+)SANDBOX_LITERAL";
+
+// These are additional rules that are added to the content process rules for
+// file content processes.
+static const char SandboxPolicyContentFileAddend[] = R"SANDBOX_LITERAL(
+ ; This process has blanket file read privileges
+ (allow file-read*)
+
+ ; File content processes need access to iconservices to draw file icons in
+ ; directory listings
+ (allow mach-lookup (global-name "com.apple.iconservices"))
+)SANDBOX_LITERAL";
+
+// These are additional rules that are added to the content process rules when
+// audio remoting is not enabled. (Once audio remoting is always used these
+// will be deleted.)
+static const char SandboxPolicyContentAudioAddend[] = R"SANDBOX_LITERAL(
+ (allow ipc-posix-shm-read* ipc-posix-shm-write-data
+ (ipc-posix-name-regex #"^AudioIO"))
+
+ (allow mach-lookup
+ (global-name "com.apple.audio.coreaudiod")
+ (global-name "com.apple.audio.audiohald"))
+
+ (allow iokit-open (iokit-user-client-class "IOAudioEngineUserClient"))
+
+ (allow file-read* (subpath "/Library/Audio/Plug-Ins"))
+
+ (allow device-microphone)
+)SANDBOX_LITERAL";
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxPolicyContent_h
diff --git a/security/sandbox/mac/SandboxPolicyGMP.h b/security/sandbox/mac/SandboxPolicyGMP.h
new file mode 100644
index 0000000000..344721f946
--- /dev/null
+++ b/security/sandbox/mac/SandboxPolicyGMP.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_SandboxPolicyGMP_h
+#define mozilla_SandboxPolicyGMP_h
+
+#define MAX_GMP_TESTING_READ_PATHS 2
+
+namespace mozilla {
+
+static const char SandboxPolicyGMP[] = R"SANDBOX_LITERAL(
+ (version 1)
+
+ (define should-log (param "SHOULD_LOG"))
+ (define app-path (param "APP_PATH"))
+ (define plugin-path (param "PLUGIN_PATH"))
+ (define plugin-binary-path (param "PLUGIN_BINARY_PATH"))
+ (define crashPort (param "CRASH_PORT"))
+ (define hasWindowServer (param "HAS_WINDOW_SERVER"))
+ (define testingReadPath1 (param "TESTING_READ_PATH1"))
+ (define testingReadPath2 (param "TESTING_READ_PATH2"))
+ (define isRosettaTranslated (param "IS_ROSETTA_TRANSLATED"))
+
+ (define (moz-deny feature)
+ (if (string=? should-log "TRUE")
+ (deny feature)
+ (deny feature (with no-log))))
+
+ (moz-deny default)
+ ; These are not included in (deny default)
+ (moz-deny process-info*)
+ (allow process-info-pidinfo (target self))
+ ; This isn't available in some older macOS releases.
+ (if (defined? 'nvram*)
+ (moz-deny nvram*))
+ ; This property requires macOS 10.10+
+ (if (defined? 'file-map-executable)
+ (moz-deny file-map-executable))
+
+ ; Needed for things like getpriority()/setpriority()/pthread_setname()
+ (allow process-info-pidinfo process-info-setcontrol (target self))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (if (string=? isRosettaTranslated "TRUE")
+ (allow file-map-executable (subpath "/private/var/db/oah")))
+ (allow file-map-executable file-read*
+ (subpath "/System/Library")
+ (subpath "/usr/lib")
+ (subpath plugin-path)
+ (subpath app-path)))
+ (allow file-read*
+ (subpath "/System/Library")
+ (subpath "/usr/lib")
+ (subpath plugin-path)
+ (subpath app-path)))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (when plugin-binary-path
+ (allow file-read* file-map-executable (subpath plugin-binary-path)))
+ (when testingReadPath1
+ (allow file-read* file-map-executable (subpath testingReadPath1)))
+ (when testingReadPath2
+ (allow file-read* file-map-executable (subpath testingReadPath2))))
+ (begin
+ (when plugin-binary-path
+ (allow file-read* (subpath plugin-binary-path)))
+ (when testingReadPath1
+ (allow file-read* (subpath testingReadPath1)))
+ (when testingReadPath2
+ (allow file-read* (subpath testingReadPath2)))))
+
+ (if (string? crashPort)
+ (allow mach-lookup (global-name crashPort)))
+
+ (allow signal (target self))
+ (allow sysctl-read)
+ (allow iokit-open (iokit-user-client-class "IOHIDParamUserClient"))
+ (allow file-read*
+ (literal "/etc")
+ (literal "/dev/random")
+ (literal "/dev/urandom")
+ (literal "/usr/share/icu/icudt51l.dat"))
+
+ ; Timezone
+ (allow file-read*
+ (subpath "/private/var/db/timezone")
+ (subpath "/usr/share/zoneinfo")
+ (subpath "/usr/share/zoneinfo.default")
+ (literal "/private/etc/localtime"))
+
+ (if (string=? hasWindowServer "TRUE")
+ (allow mach-lookup (global-name "com.apple.windowserver.active")))
+)SANDBOX_LITERAL";
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxPolicyGMP_h
diff --git a/security/sandbox/mac/SandboxPolicyRDD.h b/security/sandbox/mac/SandboxPolicyRDD.h
new file mode 100644
index 0000000000..b5ab50da23
--- /dev/null
+++ b/security/sandbox/mac/SandboxPolicyRDD.h
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_SandboxPolicyRDD_h
+#define mozilla_SandboxPolicyRDD_h
+
+namespace mozilla {
+
+static const char SandboxPolicyRDD[] = R"SANDBOX_LITERAL(
+ (version 1)
+
+ (define should-log (param "SHOULD_LOG"))
+ (define macosVersion (string->number (param "MAC_OS_VERSION")))
+ (define app-path (param "APP_PATH"))
+ (define home-path (param "HOME_PATH"))
+ (define crashPort (param "CRASH_PORT"))
+ (define isRosettaTranslated (param "IS_ROSETTA_TRANSLATED"))
+
+ (define (moz-deny feature)
+ (if (string=? should-log "TRUE")
+ (deny feature)
+ (deny feature (with no-log))))
+
+ (moz-deny default)
+ ; These are not included in (deny default)
+ (moz-deny process-info*)
+ ; This isn't available in some older macOS releases.
+ (if (defined? 'nvram*)
+ (moz-deny nvram*))
+ ; The next two properties both require macOS 10.10+
+ (if (defined? 'iokit-get-properties)
+ (moz-deny iokit-get-properties))
+ (if (defined? 'file-map-executable)
+ (moz-deny file-map-executable))
+
+ ; Needed for things like getpriority()/setpriority()/pthread_setname()
+ (allow process-info-pidinfo process-info-setcontrol (target self))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (if (string=? isRosettaTranslated "TRUE")
+ (allow file-map-executable (subpath "/private/var/db/oah")))
+ (allow file-map-executable file-read*
+ (subpath "/System")
+ (subpath "/usr/lib")
+ (subpath "/Library/GPUBundles")
+ (subpath app-path)))
+ (allow file-read*
+ (subpath "/System")
+ (subpath "/usr/lib")
+ (subpath "/Library/GPUBundles")
+ (subpath app-path)))
+
+ (if (string? crashPort)
+ (allow mach-lookup (global-name crashPort)))
+
+ (allow signal (target self))
+ (allow sysctl-read)
+ (allow file-read*
+ (literal "/dev/random")
+ (literal "/dev/urandom")
+ (subpath "/usr/share/icu"))
+
+ ; Timezone
+ (allow file-read*
+ (subpath "/private/var/db/timezone")
+ (subpath "/usr/share/zoneinfo")
+ (subpath "/usr/share/zoneinfo.default")
+ (literal "/private/etc/localtime"))
+
+ (allow sysctl-read
+ (sysctl-name-regex #"^sysctl\.")
+ (sysctl-name "kern.ostype")
+ (sysctl-name "kern.osversion")
+ (sysctl-name "kern.osrelease")
+ (sysctl-name "kern.osproductversion")
+ (sysctl-name "kern.version")
+ ; TODO: remove "kern.hostname". Without it the tests hang, but the hostname
+ ; is arguably sensitive information, so we should see what can be done about
+ ; removing it.
+ (sysctl-name "kern.hostname")
+ (sysctl-name "hw.machine")
+ (sysctl-name "hw.memsize")
+ (sysctl-name "hw.model")
+ (sysctl-name "hw.ncpu")
+ (sysctl-name "hw.activecpu")
+ (sysctl-name "hw.byteorder")
+ (sysctl-name "hw.pagesize_compat")
+ (sysctl-name "hw.logicalcpu_max")
+ (sysctl-name "hw.physicalcpu_max")
+ (sysctl-name "hw.busfrequency_compat")
+ (sysctl-name "hw.busfrequency_max")
+ (sysctl-name "hw.cpufrequency")
+ (sysctl-name "hw.cpufrequency_compat")
+ (sysctl-name "hw.cpufrequency_max")
+ (sysctl-name "hw.l2cachesize")
+ (sysctl-name "hw.l3cachesize")
+ (sysctl-name "hw.cachelinesize")
+ (sysctl-name "hw.cachelinesize_compat")
+ (sysctl-name "hw.tbfrequency_compat")
+ (sysctl-name "hw.vectorunit")
+ (sysctl-name "hw.optional.sse2")
+ (sysctl-name "hw.optional.sse3")
+ (sysctl-name "hw.optional.sse4_1")
+ (sysctl-name "hw.optional.sse4_2")
+ (sysctl-name "hw.optional.avx1_0")
+ (sysctl-name "hw.optional.avx2_0")
+ (sysctl-name "hw.optional.avx512f")
+ (sysctl-name "machdep.cpu.vendor")
+ (sysctl-name "machdep.cpu.family")
+ (sysctl-name "machdep.cpu.model")
+ (sysctl-name "machdep.cpu.stepping")
+ (sysctl-name "debug.intel.gstLevelGST")
+ (sysctl-name "debug.intel.gstLoaderControl"))
+
+ (define (home-regex home-relative-regex)
+ (regex (string-append "^" (regex-quote home-path) home-relative-regex)))
+ (define (home-subpath home-relative-subpath)
+ (subpath (string-append home-path home-relative-subpath)))
+ (define (home-literal home-relative-literal)
+ (literal (string-append home-path home-relative-literal)))
+ (define (allow-shared-list domain)
+ (allow file-read*
+ (home-regex (string-append "/Library/Preferences/" (regex-quote domain)))))
+
+ (allow ipc-posix-shm-read-data ipc-posix-shm-write-data
+ (ipc-posix-name-regex #"^CFPBS:"))
+
+ (allow mach-lookup
+ (global-name "com.apple.CoreServices.coreservicesd")
+ (global-name "com.apple.coreservices.launchservicesd")
+ (global-name "com.apple.lsd.mapdb"))
+
+ (allow file-read*
+ (subpath "/Library/ColorSync/Profiles")
+ (literal "/")
+ (literal "/private/tmp")
+ (literal "/private/var/tmp")
+ (home-subpath "/Library/Colors")
+ (home-subpath "/Library/ColorSync/Profiles"))
+
+ (if (>= macosVersion 1013)
+ (allow mach-lookup
+ ; bug 1392988
+ (xpc-service-name "com.apple.coremedia.videodecoder")
+ (xpc-service-name "com.apple.coremedia.videoencoder")))
+
+ (if (>= macosVersion 1100)
+ (allow mach-lookup
+ ; bug 1655655
+ (global-name "com.apple.trustd.agent")))
+
+ ; Only supported on macOS 10.10+
+ (if (defined? 'iokit-get-properties)
+ (allow iokit-get-properties
+ (iokit-property "board-id")
+ (iokit-property "class-code")
+ (iokit-property "vendor-id")
+ (iokit-property "device-id")
+ (iokit-property "IODVDBundleName")
+ (iokit-property "IOGLBundleName")
+ (iokit-property "IOGVACodec")
+ (iokit-property "IOGVAHEVCDecode")
+ (iokit-property "IOAVDHEVCDecodeCapabilities")
+ (iokit-property "IOGVAHEVCEncode")
+ (iokit-property "IOGVAXDecode")
+ (iokit-property "IOPCITunnelled")
+ (iokit-property "IOVARendererID")
+ (iokit-property "MetalPluginName")
+ (iokit-property "MetalPluginClassName")))
+
+; accelerated graphics
+ (allow user-preference-read (preference-domain "com.apple.opengl"))
+ (allow user-preference-read (preference-domain "com.nvidia.OpenGL"))
+ (allow mach-lookup
+ (global-name "com.apple.cvmsServ"))
+ (if (>= macosVersion 1014)
+ (allow mach-lookup
+ (global-name "com.apple.MTLCompilerService")))
+ (allow iokit-open
+ (iokit-connection "IOAccelerator")
+ (iokit-user-client-class "IOAccelerationUserClient")
+ (iokit-user-client-class "IOSurfaceRootUserClient")
+ (iokit-user-client-class "IOSurfaceSendRight")
+ (iokit-user-client-class "IOFramebufferSharedUserClient")
+ (iokit-user-client-class "AGPMClient")
+ (iokit-user-client-class "AppleGraphicsControlClient"))
+
+ (if (>= macosVersion 1013)
+ (allow mach-lookup
+ ; bug 1565575
+ (global-name "com.apple.audio.AudioComponentRegistrar")))
+)SANDBOX_LITERAL";
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxPolicyRDD_h
diff --git a/security/sandbox/mac/SandboxPolicySocket.h b/security/sandbox/mac/SandboxPolicySocket.h
new file mode 100644
index 0000000000..9464a4310e
--- /dev/null
+++ b/security/sandbox/mac/SandboxPolicySocket.h
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_SandboxPolicySocket_h
+#define mozilla_SandboxPolicySocket_h
+
+namespace mozilla {
+
+static const char SandboxPolicySocket[] = R"SANDBOX_LITERAL(
+ (version 1)
+
+ (define should-log (param "SHOULD_LOG"))
+ (define app-path (param "APP_PATH"))
+ (define crashPort (param "CRASH_PORT"))
+ (define home-path (param "HOME_PATH"))
+ (define isRosettaTranslated (param "IS_ROSETTA_TRANSLATED"))
+
+ (define (moz-deny feature)
+ (if (string=? should-log "TRUE")
+ (deny feature)
+ (deny feature (with no-log))))
+
+ (define (home-subpath home-relative-subpath)
+ (subpath (string-append home-path home-relative-subpath)))
+ (define (home-literal home-relative-literal)
+ (literal (string-append home-path home-relative-literal)))
+ (define (home-regex home-relative-regex)
+ (regex (string-append "^" (regex-quote home-path) home-relative-regex)))
+
+ (moz-deny default)
+ ; These are not included in (deny default)
+ (moz-deny process-info*)
+ ; This isn't available in some older macOS releases.
+ (if (defined? 'nvram*)
+ (moz-deny nvram*))
+ ; This property requires macOS 10.10+
+ (if (defined? 'file-map-executable)
+ (moz-deny file-map-executable))
+
+ (if (string=? should-log "TRUE")
+ (debug deny))
+
+ ; Needed for things like getpriority()/setpriority()/pthread_setname()
+ (allow process-info-pidinfo process-info-setcontrol (target self))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (if (string=? isRosettaTranslated "TRUE")
+ (allow file-map-executable (subpath "/private/var/db/oah")))
+ (allow file-map-executable file-read*
+ (subpath "/System/Library")
+ (subpath "/usr/lib")
+ (subpath app-path)))
+ (allow file-read*
+ (subpath "/System/Library")
+ (subpath "/usr/lib")
+ (subpath app-path)))
+
+ (if (string? crashPort)
+ (allow mach-lookup (global-name crashPort)))
+
+ (allow signal (target self))
+ (allow sysctl-read)
+ (allow file-read*
+ (literal "/dev/random")
+ (literal "/dev/urandom")
+ (subpath "/usr/share/icu"))
+
+ ; For stat and symlink resolution
+ (allow file-read-metadata (subpath "/"))
+
+ ; Timezone
+ (allow file-read*
+ (subpath "/private/var/db/timezone")
+ (subpath "/usr/share/zoneinfo")
+ (subpath "/usr/share/zoneinfo.default")
+ (literal "/private/etc/localtime"))
+
+ ; Needed for some global preferences
+ (allow file-read-data
+ (literal "/Library/Preferences/.GlobalPreferences.plist")
+ (home-literal "/Library/Preferences/.GlobalPreferences.plist")
+ (home-regex #"/Library/Preferences/ByHost/\.GlobalPreferences.*")
+ (home-literal "/Library/Preferences/com.apple.universalaccess.plist"))
+
+ (allow file-read-data (literal "/private/etc/passwd"))
+
+ (allow network-outbound
+ (control-name "com.apple.netsrc")
+ (literal "/private/var/run/mDNSResponder")
+ (remote tcp)
+ (remote udp))
+
+ (allow system-socket
+ (require-all (socket-domain AF_SYSTEM)
+ (socket-protocol 2)) ; SYSPROTO_CONTROL
+ (socket-domain AF_ROUTE))
+
+ (allow network-bind network-inbound
+ (local tcp)
+ (local udp))
+
+ ; Distributed notifications memory.
+ (allow ipc-posix-shm-read-data
+ (ipc-posix-name "apple.shm.notification_center"))
+
+ ; Notification data from the security server database.
+ (allow ipc-posix-shm
+ (ipc-posix-name "com.apple.AppleDatabaseChanged"))
+
+ ; From system.sb
+ (allow mach-lookup
+ (global-name "com.apple.bsd.dirhelper")
+ (global-name "com.apple.coreservices.launchservicesd")
+ (global-name "com.apple.system.notification_center"))
+
+ ; resolv.conf and hosts file
+ (allow file-read*
+ (literal "/")
+ (literal "/etc")
+ (literal "/etc/hosts")
+ (literal "/etc/resolv.conf")
+ (literal "/private")
+ (literal "/private/etc")
+ (literal "/private/etc/hosts")
+ (literal "/private/etc/resolv.conf")
+ (literal "/private/var")
+ (literal "/private/var/run")
+ (literal "/private/var/run/resolv.conf")
+ (literal "/var")
+ (literal "/var/run"))
+
+ ; Certificate databases
+ (allow file-read*
+ (subpath "/private/var/db/mds")
+ (subpath "/Library/Keychains")
+ (subpath "/System/Library/Keychains")
+ (subpath "/System/Library/Security")
+ (home-subpath "/Library/Keychains"))
+
+ ; For enabling TCSM
+ (allow sysctl-write
+ (sysctl-name "kern.tcsm_enable"))
+)SANDBOX_LITERAL";
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxPolicySocket_h
diff --git a/security/sandbox/mac/SandboxPolicyUtility.h b/security/sandbox/mac/SandboxPolicyUtility.h
new file mode 100644
index 0000000000..ec10175fda
--- /dev/null
+++ b/security/sandbox/mac/SandboxPolicyUtility.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_SandboxPolicyUtility_h
+#define mozilla_SandboxPolicyUtility_h
+
+namespace mozilla {
+
+static const char SandboxPolicyUtility[] = R"SANDBOX_LITERAL(
+ (version 1)
+
+ (define should-log (param "SHOULD_LOG"))
+ (define app-path (param "APP_PATH"))
+ (define crashPort (param "CRASH_PORT"))
+ (define isRosettaTranslated (param "IS_ROSETTA_TRANSLATED"))
+
+ (define (moz-deny feature)
+ (if (string=? should-log "TRUE")
+ (deny feature)
+ (deny feature (with no-log))))
+
+ (moz-deny default)
+ ; These are not included in (deny default)
+ (moz-deny process-info*)
+ ; This isn't available in some older macOS releases.
+ (if (defined? 'nvram*)
+ (moz-deny nvram*))
+ ; This property requires macOS 10.10+
+ (if (defined? 'file-map-executable)
+ (moz-deny file-map-executable))
+
+ ; Needed for things like getpriority()/setpriority()/pthread_setname()
+ (allow process-info-pidinfo process-info-setcontrol (target self))
+
+ (if (defined? 'file-map-executable)
+ (begin
+ (if (string=? isRosettaTranslated "TRUE")
+ (allow file-map-executable (subpath "/private/var/db/oah")))
+ (allow file-map-executable file-read*
+ (subpath "/System/Library")
+ (subpath "/usr/lib")
+ (subpath app-path)))
+ (allow file-read*
+ (subpath "/System/Library")
+ (subpath "/usr/lib")
+ (subpath app-path)))
+
+ (if (string? crashPort)
+ (allow mach-lookup (global-name crashPort)))
+
+ (allow signal (target self))
+ (allow sysctl-read)
+ (allow file-read*
+ (literal "/dev/random")
+ (literal "/dev/urandom")
+ (subpath "/usr/share/icu"))
+
+ ; Timezone
+ (allow file-read*
+ (subpath "/private/var/db/timezone")
+ (subpath "/usr/share/zoneinfo")
+ (subpath "/usr/share/zoneinfo.default")
+ (literal "/private/etc/localtime"))
+
+ (allow mach-lookup
+ (global-name "com.apple.coreservices.launchservicesd"))
+)SANDBOX_LITERAL";
+
+static const char SandboxPolicyUtilityAudioDecoderAppleMediaAddend[] =
+ R"SANDBOX_LITERAL(
+ ; For Utility AudioDecoder AppleMedia codecs
+ (define macosVersion (string->number (param "MAC_OS_VERSION")))
+ (if (>= macosVersion 1013)
+ (allow mach-lookup
+ ; bug 1565575
+ (global-name "com.apple.audio.AudioComponentRegistrar")))
+)SANDBOX_LITERAL";
+
+} // namespace mozilla
+
+#endif // mozilla_SandboxPolicyUtility_h
diff --git a/security/sandbox/mac/moz.build b/security/sandbox/mac/moz.build
new file mode 100644
index 0000000000..00b96334b6
--- /dev/null
+++ b/security/sandbox/mac/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "Sandbox.h",
+ "SandboxPolicyContent.h",
+ "SandboxPolicyGMP.h",
+ "SandboxPolicyRDD.h",
+ "SandboxPolicyUtility.h",
+]
+
+SOURCES += [
+ "Sandbox.mm",
+]
+
+Library("mozsandbox")
diff --git a/security/sandbox/moz.build b/security/sandbox/moz.build
new file mode 100644
index 0000000000..902c73a783
--- /dev/null
+++ b/security/sandbox/moz.build
@@ -0,0 +1,215 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += [
+ "test/browser.ini",
+ "test/browser_bug1717599_XDG-CONFIG-DIRS.ini",
+ "test/browser_bug1717599_XDG-CONFIG-HOME.ini",
+ "test/browser_snap.ini",
+ "test/browser_xdg.ini",
+]
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Security: Process Sandboxing")
+
+DIRS += ["common"]
+
+if CONFIG["OS_ARCH"] == "Linux":
+ DIRS += ["linux"]
+elif CONFIG["OS_ARCH"] == "Darwin":
+ DIRS += ["mac"]
+elif CONFIG["OS_ARCH"] == "WINNT":
+ Library("sandbox_s")
+ FORCE_STATIC_LIB = True
+
+ DIRS += [
+ "win/src/remotesandboxbroker",
+ "win/src/sandboxbroker",
+ "win/src/sandboxtarget",
+ ]
+
+ EXPORTS.mozilla.sandboxing += [
+ "chromium-shim/sandbox/win/loggingCallbacks.h",
+ "chromium-shim/sandbox/win/loggingTypes.h",
+ "chromium-shim/sandbox/win/sandboxLogging.h",
+ "win/SandboxInitialization.h",
+ ]
+
+ SOURCES += [
+ "chromium-shim/base/debug/crash_logging.cpp",
+ "chromium-shim/base/file_version_info_win.cpp",
+ "chromium-shim/base/files/file_path.cpp",
+ "chromium-shim/base/logging.cpp",
+ "chromium-shim/base/process/memory_win.cpp",
+ "chromium-shim/base/win/win_util.cpp",
+ "chromium-shim/sandbox/win/sandboxLogging.cpp",
+ "chromium-shim/sandbox/win/src/line_break_dispatcher.cc",
+ "chromium-shim/sandbox/win/src/line_break_interception.cc",
+ "chromium-shim/sandbox/win/src/line_break_policy.cc",
+ "chromium/base/at_exit.cc",
+ "chromium/base/base_switches.cc",
+ "chromium/base/callback_internal.cc",
+ "chromium/base/cpu.cc",
+ "chromium/base/debug/alias.cc",
+ "chromium/base/debug/profiler.cc",
+ "chromium/base/environment.cc",
+ "chromium/base/files/file_path_constants.cc",
+ "chromium/base/hash/hash.cc",
+ "chromium/base/lazy_instance_helpers.cc",
+ "chromium/base/location.cc",
+ "chromium/base/memory/platform_shared_memory_region.cc",
+ "chromium/base/memory/platform_shared_memory_region_win.cc",
+ "chromium/base/memory/ref_counted.cc",
+ "chromium/base/memory/shared_memory_mapping.cc",
+ "chromium/base/memory/unsafe_shared_memory_region.cc",
+ "chromium/base/process/environment_internal.cc",
+ "chromium/base/process/process_handle_win.cc",
+ "chromium/base/rand_util_win.cc",
+ "chromium/base/scoped_clear_last_error_win.cc",
+ "chromium/base/strings/nullable_string16.cc",
+ "chromium/base/strings/string_number_conversions.cc",
+ "chromium/base/strings/string_piece.cc",
+ "chromium/base/strings/string_split.cc",
+ "chromium/base/strings/string_util.cc",
+ "chromium/base/strings/string_util_constants.cc",
+ "chromium/base/strings/stringprintf.cc",
+ "chromium/base/strings/utf_string_conversion_utils.cc",
+ "chromium/base/strings/utf_string_conversions.cc",
+ "chromium/base/synchronization/lock.cc",
+ "chromium/base/synchronization/lock_impl_win.cc",
+ "chromium/base/third_party/cityhash/city.cc",
+ "chromium/base/third_party/double_conversion/double-conversion/bignum.cc",
+ "chromium/base/third_party/double_conversion/double-conversion/cached-powers.cc",
+ "chromium/base/third_party/double_conversion/double-conversion/string-to-double.cc",
+ "chromium/base/third_party/double_conversion/double-conversion/strtod.cc",
+ "chromium/base/third_party/icu/icu_utf.cc",
+ "chromium/base/third_party/superfasthash/superfasthash.c",
+ "chromium/base/threading/platform_thread.cc",
+ "chromium/base/threading/platform_thread_win.cc",
+ "chromium/base/threading/thread_collision_warner.cc",
+ "chromium/base/threading/thread_id_name_manager.cc",
+ "chromium/base/threading/thread_local_storage.cc",
+ "chromium/base/threading/thread_local_storage_win.cc",
+ "chromium/base/threading/thread_restrictions.cc",
+ "chromium/base/time/time.cc",
+ "chromium/base/time/time_win.cc",
+ "chromium/base/time/time_win_features.cc",
+ "chromium/base/token.cc",
+ "chromium/base/unguessable_token.cc",
+ "chromium/base/version.cc",
+ "chromium/base/win/pe_image.cc",
+ "chromium/base/win/scoped_handle.cc",
+ "chromium/base/win/scoped_handle_verifier.cc",
+ "chromium/base/win/scoped_process_information.cc",
+ "chromium/base/win/startup_information.cc",
+ "chromium/base/win/static_constants.cc",
+ "chromium/base/win/windows_version.cc",
+ "chromium/sandbox/win/src/acl.cc",
+ "chromium/sandbox/win/src/app_container_profile_base.cc",
+ "chromium/sandbox/win/src/broker_services.cc",
+ "chromium/sandbox/win/src/crosscall_server.cc",
+ "chromium/sandbox/win/src/eat_resolver.cc",
+ "chromium/sandbox/win/src/filesystem_dispatcher.cc",
+ "chromium/sandbox/win/src/filesystem_interception.cc",
+ "chromium/sandbox/win/src/filesystem_policy.cc",
+ "chromium/sandbox/win/src/handle_closer.cc",
+ "chromium/sandbox/win/src/handle_closer_agent.cc",
+ "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/heap_helper.cc",
+ "chromium/sandbox/win/src/interception.cc",
+ "chromium/sandbox/win/src/interception_agent.cc",
+ "chromium/sandbox/win/src/ipc_args.cc",
+ "chromium/sandbox/win/src/job.cc",
+ "chromium/sandbox/win/src/named_pipe_dispatcher.cc",
+ "chromium/sandbox/win/src/named_pipe_interception.cc",
+ "chromium/sandbox/win/src/named_pipe_policy.cc",
+ "chromium/sandbox/win/src/policy_broker.cc",
+ "chromium/sandbox/win/src/policy_engine_opcodes.cc",
+ "chromium/sandbox/win/src/policy_engine_processor.cc",
+ "chromium/sandbox/win/src/policy_low_level.cc",
+ "chromium/sandbox/win/src/policy_target.cc",
+ "chromium/sandbox/win/src/process_mitigations.cc",
+ "chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc",
+ "chromium/sandbox/win/src/process_mitigations_win32k_interception.cc",
+ "chromium/sandbox/win/src/process_mitigations_win32k_policy.cc",
+ "chromium/sandbox/win/src/process_thread_dispatcher.cc",
+ "chromium/sandbox/win/src/process_thread_interception.cc",
+ "chromium/sandbox/win/src/process_thread_policy.cc",
+ "chromium/sandbox/win/src/registry_dispatcher.cc",
+ "chromium/sandbox/win/src/registry_interception.cc",
+ "chromium/sandbox/win/src/registry_policy.cc",
+ "chromium/sandbox/win/src/resolver.cc",
+ "chromium/sandbox/win/src/restricted_token.cc",
+ "chromium/sandbox/win/src/restricted_token_utils.cc",
+ "chromium/sandbox/win/src/sandbox.cc",
+ "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/security_capabilities.cc",
+ "chromium/sandbox/win/src/service_resolver.cc",
+ "chromium/sandbox/win/src/sharedmem_ipc_client.cc",
+ "chromium/sandbox/win/src/sharedmem_ipc_server.cc",
+ "chromium/sandbox/win/src/sid.cc",
+ "chromium/sandbox/win/src/signed_dispatcher.cc",
+ "chromium/sandbox/win/src/signed_interception.cc",
+ "chromium/sandbox/win/src/signed_policy.cc",
+ "chromium/sandbox/win/src/sync_dispatcher.cc",
+ "chromium/sandbox/win/src/sync_interception.cc",
+ "chromium/sandbox/win/src/sync_policy.cc",
+ "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",
+ "win/SandboxInitialization.cpp",
+ ]
+ # Sandbox interceptors can be called before the process's import table
+ # is populated. Don't let the compiler insert any instrumentation that
+ # might call an import.
+ SOURCES["chromium/sandbox/win/src/process_thread_interception.cc"].no_pgo = True
+
+ if CONFIG["CPU_ARCH"] in ("x86_64", "aarch64"):
+ SOURCES += [
+ "chromium/sandbox/win/src/interceptors_64.cc",
+ "chromium/sandbox/win/src/resolver_64.cc",
+ "chromium/sandbox/win/src/service_resolver_64.cc",
+ ]
+ else:
+ SOURCES += [
+ "chromium/sandbox/win/src/resolver_32.cc",
+ "chromium/sandbox/win/src/service_resolver_32.cc",
+ ]
+
+ for var in (
+ "UNICODE",
+ "_UNICODE",
+ "NS_NO_XPCOM",
+ "_CRT_RAND_S",
+ "CHROMIUM_SANDBOX_BUILD",
+ ):
+ DEFINES[var] = True
+ if CONFIG["CC_TYPE"] not in ("gcc", "clang"):
+ DEFINES["SANDBOX_EXPORTS"] = True
+
+ LOCAL_INCLUDES += ["/security/sandbox/chromium-shim"]
+ LOCAL_INCLUDES += ["/security/sandbox/chromium"]
+ LOCAL_INCLUDES += ["/nsprpub"]
+
+ OS_LIBS += ["usp10"]
+
+ DisableStlWrapping()
+
+ # Suppress warnings in third-party code.
+ if CONFIG["CC_TYPE"] == "clang-cl":
+ CXXFLAGS += [
+ "-Wno-deprecated-declarations", # 'GetVersionExW': was declared deprecated
+ ]
diff --git a/security/sandbox/test/browser.ini b/security/sandbox/test/browser.ini
new file mode 100644
index 0000000000..b76f019131
--- /dev/null
+++ b/security/sandbox/test/browser.ini
@@ -0,0 +1,26 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+[DEFAULT]
+skip-if = ccov || (os == "linux" && (asan || tsan)) # bug 1784517
+tags = contentsandbox
+support-files =
+ browser_content_sandbox_utils.js
+ browser_content_sandbox_fs_tests.js
+ mac_register_font.py
+ ../../../layout/reftests/fonts/fira/FiraSans-Regular.otf
+# Bug 1718210, we need to disable rdd in gpu process to avoid leaks at shutdown
+prefs =
+ media.gpu-process-decoder=false
+
+[browser_bug1393259.js]
+support-files =
+ bug1393259.html
+skip-if = (os != 'mac') # This is a Mac-specific test
+[browser_content_sandbox_fs.js]
+skip-if =
+ (debug && os == 'win') # bug 1379635
+[browser_content_sandbox_syscalls.js]
+[browser_sandbox_test.js]
+skip-if =
+ !debug
+ win11_2009 && msix && debug # bug 1823583 \ No newline at end of file
diff --git a/security/sandbox/test/browser_bug1393259.js b/security/sandbox/test/browser_bug1393259.js
new file mode 100644
index 0000000000..58ee9ca06f
--- /dev/null
+++ b/security/sandbox/test/browser_bug1393259.js
@@ -0,0 +1,200 @@
+/* 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/. */
+"use strict";
+
+/*
+ * This test validates that an OTF font installed in a directory not
+ * accessible to content processes is rendered correctly by checking that
+ * content displayed never uses the OS fallback font "LastResort". When
+ * a content process renders a page with the fallback font, that is an
+ * indication the content process failed to read or load the computed font.
+ * The test uses a version of the Fira Sans font and depends on the font
+ * not being already installed and enabled.
+ */
+
+const kPageURL =
+ "http://example.com/browser/security/sandbox/test/bug1393259.html";
+
+// Parameters for running the python script that registers/unregisters fonts.
+const kPythonPath = "/usr/bin/python";
+const kFontInstallerPath = "browser/security/sandbox/test/mac_register_font.py";
+const kUninstallFlag = "-u";
+const kVerboseFlag = "-v";
+
+// Where to find the font in the test environment.
+const kRepoFontPath = "browser/security/sandbox/test/FiraSans-Regular.otf";
+
+// Font name strings to check for.
+const kLastResortFontName = "LastResort";
+const kTestFontName = "Fira Sans";
+
+// Home-relative path to install a private font. Where a private font is
+// a font at a location not readable by content processes.
+const kPrivateFontSubPath = "/FiraSans-Regular.otf";
+
+add_task(async function () {
+ await new Promise(resolve => waitForFocus(resolve, window));
+
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: kPageURL,
+ },
+ async function (aBrowser) {
+ function runProcess(aCmd, aArgs, blocking = true) {
+ let cmdFile = Cc["@mozilla.org/file/local;1"].createInstance(
+ Ci.nsIFile
+ );
+ cmdFile.initWithPath(aCmd);
+
+ let process = Cc["@mozilla.org/process/util;1"].createInstance(
+ Ci.nsIProcess
+ );
+ process.init(cmdFile);
+ process.run(blocking, aArgs, aArgs.length);
+ return process.exitValue;
+ }
+
+ // Register the font at path |fontPath| and wait
+ // for the browser to detect the change.
+ async function registerFont(fontPath) {
+ let fontRegistered = getFontNotificationPromise();
+ let exitCode = runProcess(kPythonPath, [
+ kFontInstallerPath,
+ kVerboseFlag,
+ fontPath,
+ ]);
+ Assert.ok(exitCode == 0, "registering font" + fontPath);
+ if (exitCode == 0) {
+ // Wait for the font registration to be detected by the browser.
+ await fontRegistered;
+ }
+ }
+
+ // Unregister the font at path |fontPath|. If |waitForUnreg| is true,
+ // don't wait for the browser to detect the change and don't use
+ // the verbose arg for the unregister command.
+ async function unregisterFont(fontPath, waitForUnreg = true) {
+ let args = [kFontInstallerPath, kUninstallFlag];
+ let fontUnregistered;
+
+ if (waitForUnreg) {
+ args.push(kVerboseFlag);
+ fontUnregistered = getFontNotificationPromise();
+ }
+
+ let exitCode = runProcess(kPythonPath, args.concat(fontPath));
+ if (waitForUnreg) {
+ Assert.ok(exitCode == 0, "unregistering font" + fontPath);
+ if (exitCode == 0) {
+ await fontUnregistered;
+ }
+ }
+ }
+
+ // Returns a promise that resolves when font info is changed.
+ let getFontNotificationPromise = () =>
+ new Promise(resolve => {
+ const kTopic = "font-info-updated";
+ function observe() {
+ Services.obs.removeObserver(observe, kTopic);
+ resolve();
+ }
+
+ Services.obs.addObserver(observe, kTopic);
+ });
+
+ let homeDir = Services.dirsvc.get("Home", Ci.nsIFile);
+ let privateFontPath = homeDir.path + kPrivateFontSubPath;
+
+ registerCleanupFunction(function () {
+ unregisterFont(privateFontPath, /* waitForUnreg = */ false);
+ runProcess("/bin/rm", [privateFontPath], /* blocking = */ false);
+ });
+
+ // Copy the font file to the private path.
+ runProcess("/bin/cp", [kRepoFontPath, privateFontPath]);
+
+ // Cleanup previous aborted tests.
+ unregisterFont(privateFontPath, /* waitForUnreg = */ false);
+
+ // Get the original width, using the fallback monospaced font
+ let origWidth = await SpecialPowers.spawn(
+ aBrowser,
+ [],
+ async function () {
+ let window = content.window.wrappedJSObject;
+ let contentDiv = window.document.getElementById("content");
+ return contentDiv.offsetWidth;
+ }
+ );
+
+ // Activate the font we want to test at a non-standard path.
+ await registerFont(privateFontPath);
+
+ // Assign the new font to the content.
+ await SpecialPowers.spawn(aBrowser, [], async function () {
+ let window = content.window.wrappedJSObject;
+ let contentDiv = window.document.getElementById("content");
+ contentDiv.style.fontFamily = "'Fira Sans', monospace";
+ });
+
+ // Wait until the width has changed, indicating the content process
+ // has recognized the newly-activated font.
+ while (true) {
+ let width = await SpecialPowers.spawn(aBrowser, [], async function () {
+ let window = content.window.wrappedJSObject;
+ let contentDiv = window.document.getElementById("content");
+ return contentDiv.offsetWidth;
+ });
+ if (width != origWidth) {
+ break;
+ }
+ // If the content wasn't ready yet, wait a little before re-checking.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(c => setTimeout(c, 100));
+ }
+
+ // Get a list of fonts now being used to display the web content.
+ let fontList = await SpecialPowers.spawn(aBrowser, [], async function () {
+ let window = content.window.wrappedJSObject;
+ let range = window.document.createRange();
+ let contentDiv = window.document.getElementById("content");
+ range.selectNode(contentDiv);
+ let fonts = InspectorUtils.getUsedFontFaces(range);
+
+ let fontList = [];
+ for (let i = 0; i < fonts.length; i++) {
+ fontList.push({ name: fonts[i].name });
+ }
+ return fontList;
+ });
+
+ let lastResortFontUsed = false;
+ let testFontUsed = false;
+
+ for (let font of fontList) {
+ // Did we fall back to the "LastResort" font?
+ if (!lastResortFontUsed && font.name.includes(kLastResortFontName)) {
+ lastResortFontUsed = true;
+ continue;
+ }
+ // Did we render using our test font as expected?
+ if (!testFontUsed && font.name.includes(kTestFontName)) {
+ testFontUsed = true;
+ continue;
+ }
+ }
+
+ Assert.ok(
+ !lastResortFontUsed,
+ `The ${kLastResortFontName} fallback font was not used`
+ );
+
+ Assert.ok(testFontUsed, `The test font "${kTestFontName}" was used`);
+
+ await unregisterFont(privateFontPath);
+ }
+ );
+});
diff --git a/security/sandbox/test/browser_bug1717599_XDG-CONFIG-DIRS.ini b/security/sandbox/test/browser_bug1717599_XDG-CONFIG-DIRS.ini
new file mode 100644
index 0000000000..6ef46a6652
--- /dev/null
+++ b/security/sandbox/test/browser_bug1717599_XDG-CONFIG-DIRS.ini
@@ -0,0 +1,9 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+[DEFAULT]
+skip-if = ccov || (os == "linux" && (asan || tsan)) # bug 1784517
+tags = contentsandbox
+environment=XDG_CONFIG_DIRS=:/opt
+
+[browser_content_sandbox_bug1717599_XDG-CONFIG-DIRS.js]
+run-if = (os == 'linux')
diff --git a/security/sandbox/test/browser_bug1717599_XDG-CONFIG-HOME.ini b/security/sandbox/test/browser_bug1717599_XDG-CONFIG-HOME.ini
new file mode 100644
index 0000000000..d0e936d02c
--- /dev/null
+++ b/security/sandbox/test/browser_bug1717599_XDG-CONFIG-HOME.ini
@@ -0,0 +1,9 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+[DEFAULT]
+skip-if = ccov || (os == "linux" && (asan || tsan)) # bug 1784517
+tags = contentsandbox
+environment=XDG_CONFIG_HOME=
+
+[browser_content_sandbox_bug1717599_XDG-CONFIG-HOME.js]
+run-if = (os == 'linux')
diff --git a/security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-DIRS.js b/security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-DIRS.js
new file mode 100644
index 0000000000..e45c0cb078
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-DIRS.js
@@ -0,0 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+//
+// Just test that browser does not die on empty env var
+//
+add_task(async function () {
+ ok(true, "Process can run");
+});
diff --git a/security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-HOME.js b/security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-HOME.js
new file mode 100644
index 0000000000..e45c0cb078
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_bug1717599_XDG-CONFIG-HOME.js
@@ -0,0 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+//
+// Just test that browser does not die on empty env var
+//
+add_task(async function () {
+ ok(true, "Process can run");
+});
diff --git a/security/sandbox/test/browser_content_sandbox_fs.js b/security/sandbox/test/browser_content_sandbox_fs.js
new file mode 100644
index 0000000000..cff7a872fe
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_utils.js",
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_fs_tests.js",
+ this
+);
+
+/*
+ * This test exercises file I/O from web and file content processes using
+ * nsIFile etc. methods to validate that calls that are meant to be blocked by
+ * content sandboxing are blocked.
+ */
+
+//
+// Checks that sandboxing is enabled and at the appropriate level
+// setting before triggering tests that do the file I/O.
+//
+// Tests attempting to write to a file in the home directory from the
+// content process--expected to fail.
+//
+// Tests attempting to write to a file in the content temp directory
+// from the content process--expected to succeed. Uses "ContentTmpD".
+//
+// Tests reading various files and directories from file and web
+// content processes.
+//
+add_task(async function () {
+ sanityChecks();
+
+ // Test creating a file in the home directory from a web content process
+ add_task(createFileInHome); // eslint-disable-line no-undef
+
+ // Test creating a file content temp from a web content process
+ add_task(createTempFile); // eslint-disable-line no-undef
+
+ // Test reading files/dirs from web and file content processes
+ add_task(testFileAccessAllPlatforms); // eslint-disable-line no-undef
+
+ add_task(testFileAccessMacOnly); // eslint-disable-line no-undef
+
+ add_task(testFileAccessLinuxOnly); // eslint-disable-line no-undef
+
+ add_task(testFileAccessWindowsOnly); // eslint-disable-line no-undef
+
+ add_task(cleanupBrowserTabs); // eslint-disable-line no-undef
+});
diff --git a/security/sandbox/test/browser_content_sandbox_fs_snap.js b/security/sandbox/test/browser_content_sandbox_fs_snap.js
new file mode 100644
index 0000000000..a8b26a1e31
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_fs_snap.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_utils.js",
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_fs_tests.js",
+ this
+);
+
+add_task(async function () {
+ // Ensure that SNAP is there
+ const snap = Services.env.get("SNAP");
+ ok(snap.length > 1, "SNAP is defined");
+
+ // If it is there, do actual testing
+ sanityChecks();
+
+ add_task(testFileAccessLinuxOnly); // eslint-disable-line no-undef
+
+ add_task(testFileAccessLinuxSnap); // eslint-disable-line no-undef
+
+ add_task(cleanupBrowserTabs); // eslint-disable-line no-undef
+});
diff --git a/security/sandbox/test/browser_content_sandbox_fs_tests.js b/security/sandbox/test/browser_content_sandbox_fs_tests.js
new file mode 100644
index 0000000000..12678ddbe0
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_fs_tests.js
@@ -0,0 +1,698 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+// Test if the content process can create in $HOME, this should fail
+async function createFileInHome() {
+ let browser = gBrowser.selectedBrowser;
+ let homeFile = fileInHomeDir();
+ let path = homeFile.path;
+ let fileCreated = await SpecialPowers.spawn(browser, [path], createFile);
+ ok(!fileCreated.ok, "creating a file in home dir is not permitted");
+ if (fileCreated.ok) {
+ // content process successfully created the file, now remove it
+ homeFile.remove(false);
+ }
+}
+
+// Test if the content process can create a temp file, this is disallowed on
+// macOS and Windows but allowed everywhere else. Also test that the content
+// process cannot create symlinks on macOS or delete files.
+async function createTempFile() {
+ // On Windows we allow access to the temp dir for DEBUG builds, because of
+ // logging that uses that dir.
+ let isOptWin = isWin() && !SpecialPowers.isDebugBuild;
+
+ let browser = gBrowser.selectedBrowser;
+ let path = fileInTempDir().path;
+ let fileCreated = await SpecialPowers.spawn(browser, [path], createFile);
+ if (isMac() || isOptWin) {
+ ok(!fileCreated.ok, "creating a file in temp is not permitted");
+ } else {
+ ok(!!fileCreated.ok, "creating a file in temp is permitted");
+ }
+ // now delete the file
+ let fileDeleted = await SpecialPowers.spawn(browser, [path], deleteFile);
+ if (isMac() || isOptWin) {
+ // On macOS we do not allow file deletion - it is not needed by the content
+ // process itself, and macOS uses a different permission to control access
+ // so revoking it is easy.
+ ok(!fileDeleted.ok, "deleting a file in temp is not permitted");
+ } else {
+ ok(!!fileDeleted.ok, "deleting a file in temp is permitted");
+ }
+
+ // Test that symlink creation is not allowed on macOS.
+ if (isMac()) {
+ let path = fileInTempDir().path;
+ let symlinkCreated = await SpecialPowers.spawn(
+ browser,
+ [path],
+ createSymlink
+ );
+ ok(!symlinkCreated.ok, "created a symlink in temp is not permitted");
+ }
+}
+
+// Test reading files and dirs from web and file content processes.
+async function testFileAccessAllPlatforms() {
+ let webBrowser = GetWebBrowser();
+ let fileContentProcessEnabled = isFileContentProcessEnabled();
+ let fileBrowser = GetFileBrowser();
+
+ // Directories/files to test accessing from content processes.
+ // For directories, we test whether a directory listing is allowed
+ // or blocked. For files, we test if we can read from the file.
+ // Each entry in the array represents a test file or directory
+ // that will be read from either a web or file process.
+ let tests = [];
+
+ let profileDir = GetProfileDir();
+ tests.push({
+ desc: "profile dir", // description
+ ok: false, // expected to succeed?
+ browser: webBrowser, // browser to run test in
+ file: profileDir, // nsIFile object
+ minLevel: minProfileReadSandboxLevel(), // min level to enable test
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: "profile dir",
+ ok: true,
+ browser: fileBrowser,
+ file: profileDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+
+ let homeDir = GetHomeDir();
+ tests.push({
+ desc: "home dir",
+ ok: false,
+ browser: webBrowser,
+ file: homeDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: "home dir",
+ ok: true,
+ browser: fileBrowser,
+ file: homeDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+
+ let extensionsDir = GetProfileEntry("extensions");
+ if (extensionsDir.exists() && extensionsDir.isDirectory()) {
+ tests.push({
+ desc: "extensions dir",
+ ok: true,
+ browser: webBrowser,
+ file: extensionsDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ } else {
+ ok(false, `${extensionsDir.path} is a valid dir`);
+ }
+
+ let chromeDir = GetProfileEntry("chrome");
+ if (chromeDir.exists() && chromeDir.isDirectory()) {
+ tests.push({
+ desc: "chrome dir",
+ ok: true,
+ browser: webBrowser,
+ file: chromeDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ } else {
+ ok(false, `${chromeDir.path} is valid dir`);
+ }
+
+ let cookiesFile = GetProfileEntry("cookies.sqlite");
+ if (cookiesFile.exists() && !cookiesFile.isDirectory()) {
+ tests.push({
+ desc: "cookies file",
+ ok: false,
+ browser: webBrowser,
+ file: cookiesFile,
+ minLevel: minProfileReadSandboxLevel(),
+ func: readFile,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: "cookies file",
+ ok: true,
+ browser: fileBrowser,
+ file: cookiesFile,
+ minLevel: 0,
+ func: readFile,
+ });
+ }
+ } else {
+ ok(false, `${cookiesFile.path} is a valid file`);
+ }
+
+ if (isMac() || isLinux()) {
+ let varDir = GetDir("/var");
+
+ if (isMac()) {
+ // Mac sandbox rules use /private/var because /var is a symlink
+ // to /private/var on OS X. Make sure that hasn't changed.
+ varDir.normalize();
+ Assert.ok(
+ varDir.path === "/private/var",
+ "/var resolves to /private/var"
+ );
+ }
+
+ tests.push({
+ desc: "/var",
+ ok: false,
+ browser: webBrowser,
+ file: varDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: "/var",
+ ok: true,
+ browser: fileBrowser,
+ file: varDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+ }
+
+ await runTestsList(tests);
+}
+
+async function testFileAccessMacOnly() {
+ if (!isMac()) {
+ return;
+ }
+
+ let webBrowser = GetWebBrowser();
+ let fileContentProcessEnabled = isFileContentProcessEnabled();
+ let fileBrowser = GetFileBrowser();
+ let level = GetSandboxLevel();
+
+ let tests = [];
+
+ // If ~/Library/Caches/TemporaryItems exists, when level <= 2 we
+ // make sure it's readable. For level 3, we make sure it isn't.
+ let homeTempDir = GetHomeDir();
+ homeTempDir.appendRelativePath("Library/Caches/TemporaryItems");
+ if (homeTempDir.exists()) {
+ let shouldBeReadable, minLevel;
+ if (level >= minHomeReadSandboxLevel()) {
+ shouldBeReadable = false;
+ minLevel = minHomeReadSandboxLevel();
+ } else {
+ shouldBeReadable = true;
+ minLevel = 0;
+ }
+ tests.push({
+ desc: "home library cache temp dir",
+ ok: shouldBeReadable,
+ browser: webBrowser,
+ file: homeTempDir,
+ minLevel,
+ func: readDir,
+ });
+ }
+
+ // Test if we can read from $TMPDIR because we expect it
+ // to be within /private/var. Reading from it should be
+ // prevented in a 'web' process.
+ let macTempDir = GetDirFromEnvVariable("TMPDIR");
+
+ macTempDir.normalize();
+ Assert.ok(
+ macTempDir.path.startsWith("/private/var"),
+ "$TMPDIR is in /private/var"
+ );
+
+ tests.push({
+ desc: `$TMPDIR (${macTempDir.path})`,
+ ok: false,
+ browser: webBrowser,
+ file: macTempDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: `$TMPDIR (${macTempDir.path})`,
+ ok: true,
+ browser: fileBrowser,
+ file: macTempDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+
+ // The font registry directory is in the Darwin user cache dir which is
+ // accessible with the getconf(1) library call using DARWIN_USER_CACHE_DIR.
+ // For this test, assume the cache dir is located at $TMPDIR/../C and use
+ // the $TMPDIR to derive the path to the registry.
+ let fontRegistryDir = macTempDir.parent.clone();
+ fontRegistryDir.appendRelativePath("C/com.apple.FontRegistry");
+ if (fontRegistryDir.exists()) {
+ tests.push({
+ desc: `FontRegistry (${fontRegistryDir.path})`,
+ ok: true,
+ browser: webBrowser,
+ file: fontRegistryDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ // Check that we can read the file named `font` which typically
+ // exists in the the font registry directory.
+ let fontFile = fontRegistryDir.clone();
+ fontFile.appendRelativePath("font");
+ if (fontFile.exists()) {
+ tests.push({
+ desc: `FontRegistry file (${fontFile.path})`,
+ ok: true,
+ browser: webBrowser,
+ file: fontFile,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readFile,
+ });
+ }
+ }
+
+ // Test that we cannot read from /Volumes at level 3
+ let volumes = GetDir("/Volumes");
+ tests.push({
+ desc: "/Volumes",
+ ok: false,
+ browser: webBrowser,
+ file: volumes,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ // /Network is not present on macOS 10.15 (xnu 19). Don't
+ // test this directory on 10.15 and later.
+ if (AppConstants.isPlatformAndVersionAtMost("macosx", 18)) {
+ // Test that we cannot read from /Network at level 3
+ let network = GetDir("/Network");
+ tests.push({
+ desc: "/Network",
+ ok: false,
+ browser: webBrowser,
+ file: network,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ }
+ // Test that we cannot read from /Users at level 3
+ let users = GetDir("/Users");
+ tests.push({
+ desc: "/Users",
+ ok: false,
+ browser: webBrowser,
+ file: users,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ // Test that we can stat /Users at level 3
+ tests.push({
+ desc: "/Users",
+ ok: true,
+ browser: webBrowser,
+ file: users,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+
+ // Test that we can stat /Library at level 3, but can't get a
+ // directory listing of /Library. This test uses "/Library"
+ // because it's a path that is expected to always be present.
+ let libraryDir = GetDir("/Library");
+ tests.push({
+ desc: "/Library",
+ ok: true,
+ browser: webBrowser,
+ file: libraryDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+ tests.push({
+ desc: "/Library",
+ ok: false,
+ browser: webBrowser,
+ file: libraryDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ // Similarly, test that we can stat /private, but not /private/etc.
+ let privateDir = GetDir("/private");
+ tests.push({
+ desc: "/private",
+ ok: true,
+ browser: webBrowser,
+ file: privateDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+
+ await runTestsList(tests);
+}
+
+async function testFileAccessLinuxOnly() {
+ if (!isLinux()) {
+ return;
+ }
+
+ let webBrowser = GetWebBrowser();
+ let fileContentProcessEnabled = isFileContentProcessEnabled();
+ let fileBrowser = GetFileBrowser();
+
+ let tests = [];
+
+ // Test /proc/self/fd, because that can be used to unfreeze
+ // frozen shared memory.
+ let selfFdDir = GetDir("/proc/self/fd");
+ tests.push({
+ desc: "/proc/self/fd",
+ ok: false,
+ browser: webBrowser,
+ file: selfFdDir,
+ minLevel: isContentFileIOSandboxed(),
+ func: readDir,
+ });
+
+ let cacheFontConfigDir = GetHomeSubdir(".cache/fontconfig/");
+ tests.push({
+ desc: `$HOME/.cache/fontconfig/ (${cacheFontConfigDir.path})`,
+ ok: true,
+ browser: webBrowser,
+ file: cacheFontConfigDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ // allows to handle both $HOME/.config/ or $XDG_CONFIG_HOME
+ let configDir = GetHomeSubdir(".config");
+
+ const xdgConfigHome = Services.env.get("XDG_CONFIG_HOME");
+
+ if (xdgConfigHome.length > 1) {
+ configDir = GetDir(xdgConfigHome);
+ configDir.normalize();
+
+ tests.push({
+ desc: `$XDG_CONFIG_HOME (${configDir.path})`,
+ ok: true,
+ browser: webBrowser,
+ file: configDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ }
+
+ // $HOME/.config/ or $XDG_CONFIG_HOME/ should have rdonly access
+ tests.push({
+ desc: `${configDir.path} dir`,
+ ok: true,
+ browser: webBrowser,
+ file: configDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: `${configDir.path} dir`,
+ ok: true,
+ browser: fileBrowser,
+ file: configDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+
+ if (xdgConfigHome.length > 1) {
+ // When XDG_CONFIG_HOME is set, dont allow $HOME/.config
+ const homeConfigDir = GetHomeSubdir(".config");
+ tests.push({
+ desc: `${homeConfigDir.path} dir`,
+ ok: false,
+ browser: webBrowser,
+ file: homeConfigDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: `${homeConfigDir.path} dir`,
+ ok: true,
+ browser: fileBrowser,
+ file: homeConfigDir,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+ } else {
+ // WWhen XDG_CONFIG_HOME is not set, verify we do not allow $HOME/.configlol
+ // (i.e., check allow the dir and not the prefix)
+ //
+ // Checking $HOME/.config is already done above.
+ const homeConfigPrefix = GetHomeSubdir(".configlol");
+ tests.push({
+ desc: `${homeConfigPrefix.path} dir`,
+ ok: false,
+ browser: webBrowser,
+ file: homeConfigPrefix,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: `${homeConfigPrefix.path} dir`,
+ ok: false,
+ browser: fileBrowser,
+ file: homeConfigPrefix,
+ minLevel: 0,
+ func: readDir,
+ });
+ }
+ }
+
+ // Create a file under $HOME/.config/ or $XDG_CONFIG_HOME and ensure we can
+ // read it
+ let fileUnderConfig = GetSubdirFile(configDir);
+ await IOUtils.writeUTF8(fileUnderConfig.path, "TEST FILE DUMMY DATA");
+ ok(
+ await IOUtils.exists(fileUnderConfig.path),
+ `File ${fileUnderConfig.path} was properly created`
+ );
+
+ tests.push({
+ desc: `${configDir.path}/xxx is readable (${fileUnderConfig.path})`,
+ ok: true,
+ browser: webBrowser,
+ file: fileUnderConfig,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readFile,
+ cleanup: aPath => IOUtils.remove(aPath),
+ });
+
+ let configFile = GetSubdirFile(configDir);
+ tests.push({
+ desc: `${configDir.path} file write`,
+ ok: false,
+ browser: webBrowser,
+ file: configFile,
+ minLevel: minHomeReadSandboxLevel(),
+ func: createFile,
+ });
+ if (fileContentProcessEnabled) {
+ tests.push({
+ desc: `${configDir.path} file write`,
+ ok: false,
+ browser: fileBrowser,
+ file: configFile,
+ minLevel: 0,
+ func: createFile,
+ });
+ }
+
+ // Create a $HOME/.config/mozilla/ or $XDG_CONFIG_HOME/mozilla/ if none
+ // exists and assert content process cannot access it
+ let configMozilla = GetSubdir(configDir, "mozilla");
+ const emptyFileName = ".test_run_browser_sandbox.tmp";
+ let emptyFile = configMozilla.clone();
+ emptyFile.appendRelativePath(emptyFileName);
+
+ let populateFakeConfigMozilla = async aPath => {
+ // called with configMozilla
+ await IOUtils.makeDirectory(aPath, { permissions: 0o700 });
+ await IOUtils.writeUTF8(emptyFile.path, "");
+ ok(
+ await IOUtils.exists(emptyFile.path),
+ `Temp file ${emptyFile.path} was created`
+ );
+ };
+
+ let unpopulateFakeConfigMozilla = async aPath => {
+ // called with emptyFile
+ await IOUtils.remove(aPath);
+ ok(!(await IOUtils.exists(aPath)), `Temp file ${aPath} was removed`);
+ const parentDir = PathUtils.parent(aPath);
+ try {
+ await IOUtils.remove(parentDir, { recursive: false });
+ } catch (ex) {
+ if (
+ !DOMException.isInstance(ex) ||
+ ex.name !== "OperationError" ||
+ /Could not remove the non-empty directory/.test(ex.message)
+ ) {
+ // If we get here it means the directory was not empty and since we assert
+ // earlier we removed the temp file we created it means we should not
+ // worrying about removing this directory ...
+ throw ex;
+ }
+ }
+ };
+
+ await populateFakeConfigMozilla(configMozilla.path);
+
+ tests.push({
+ desc: `stat ${configDir.path}/mozilla (${configMozilla.path})`,
+ ok: false,
+ browser: webBrowser,
+ file: configMozilla,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+
+ tests.push({
+ desc: `read ${configDir.path}/mozilla (${configMozilla.path})`,
+ ok: false,
+ browser: webBrowser,
+ file: configMozilla,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ tests.push({
+ desc: `stat ${configDir.path}/mozilla/${emptyFileName} (${emptyFile.path})`,
+ ok: false,
+ browser: webBrowser,
+ file: emptyFile,
+ minLevel: minHomeReadSandboxLevel(),
+ func: statPath,
+ });
+
+ tests.push({
+ desc: `read ${configDir.path}/mozilla/${emptyFileName} (${emptyFile.path})`,
+ ok: false,
+ browser: webBrowser,
+ file: emptyFile,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readFile,
+ cleanup: unpopulateFakeConfigMozilla,
+ });
+
+ // Only needed to perform cleanup
+ if (xdgConfigHome.length > 1) {
+ tests.push({
+ desc: `$XDG_CONFIG_HOME (${configDir.path}) cleanup`,
+ ok: true,
+ browser: webBrowser,
+ file: configDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+ }
+
+ await runTestsList(tests);
+}
+
+async function testFileAccessLinuxSnap() {
+ let webBrowser = GetWebBrowser();
+
+ let tests = [];
+
+ // Assert that if we run with SNAP= env, then we allow access to it in the
+ // content process
+ let snap = Services.env.get("SNAP");
+ let snapExpectedResult = false;
+ if (snap.length > 1) {
+ snapExpectedResult = true;
+ } else {
+ snap = "/tmp/.snap_firefox_current/";
+ }
+
+ let snapDir = GetDir(snap);
+ snapDir.normalize();
+
+ let snapFile = GetSubdirFile(snapDir);
+ await createFile(snapFile.path);
+ ok(await IOUtils.exists(snapFile.path), `SNAP ${snapFile.path} was created`);
+ info(`SNAP (file) ${snapFile.path} was created`);
+
+ tests.push({
+ desc: `$SNAP (${snapDir.path} => ${snapFile.path})`,
+ ok: snapExpectedResult,
+ browser: webBrowser,
+ file: snapFile,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readFile,
+ });
+
+ await runTestsList(tests);
+}
+
+async function testFileAccessWindowsOnly() {
+ if (!isWin()) {
+ return;
+ }
+
+ let webBrowser = GetWebBrowser();
+
+ let tests = [];
+
+ let extDir = GetPerUserExtensionDir();
+ tests.push({
+ desc: "per-user extensions dir",
+ ok: true,
+ browser: webBrowser,
+ file: extDir,
+ minLevel: minHomeReadSandboxLevel(),
+ func: readDir,
+ });
+
+ await runTestsList(tests);
+}
+
+function cleanupBrowserTabs() {
+ let fileBrowser = GetFileBrowser();
+ if (fileBrowser.selectedTab) {
+ gBrowser.removeTab(fileBrowser.selectedTab);
+ }
+
+ let webBrowser = GetWebBrowser();
+ if (webBrowser.selectedTab) {
+ gBrowser.removeTab(webBrowser.selectedTab);
+ }
+
+ let tab1 = gBrowser.tabs[1];
+ if (tab1) {
+ gBrowser.removeTab(tab1);
+ }
+}
diff --git a/security/sandbox/test/browser_content_sandbox_fs_xdg.js b/security/sandbox/test/browser_content_sandbox_fs_xdg.js
new file mode 100644
index 0000000000..f5150fc329
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_fs_xdg.js
@@ -0,0 +1,31 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_utils.js",
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_fs_tests.js",
+ this
+);
+
+add_task(async function () {
+ // Ensure that XDG_CONFIG_HOME is there
+ const xdgConfigHome = Services.env.get("XDG_CONFIG_HOME");
+ ok(xdgConfigHome.length > 1, "XDG_CONFIG_HOME is defined");
+
+ // If it is there, do actual testing
+ sanityChecks();
+
+ // The linux only tests are the ones that can behave differently based on
+ // existence of XDG_CONFIG_HOME
+ add_task(testFileAccessLinuxOnly); // eslint-disable-line no-undef
+
+ add_task(cleanupBrowserTabs); // eslint-disable-line no-undef
+});
diff --git a/security/sandbox/test/browser_content_sandbox_syscalls.js b/security/sandbox/test/browser_content_sandbox_syscalls.js
new file mode 100644
index 0000000000..63cc921781
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_syscalls.js
@@ -0,0 +1,436 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from browser_content_sandbox_utils.js */
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/" +
+ "security/sandbox/test/browser_content_sandbox_utils.js",
+ this
+);
+
+const ERRNO = {
+ EACCES: 13,
+ EINVAL: 22,
+ get ENOSYS() {
+ const os = Services.appinfo.OS;
+
+ if (["Linux", "Android"].includes(os)) {
+ // https://github.com/torvalds/linux/blob/9a48d604672220545d209e9996c2a1edbb5637f6/include/uapi/asm-generic/errno.h#L18
+ return 38;
+ } else if (
+ ["Darwin", "DragonFly", "FreeBSD", "OpenBSD", "NetBSD"].includes(os)
+ ) {
+ /*
+ * Darwin: https://opensource.apple.com/source/xnu/xnu-201/bsd/sys/errno.h.auto.html
+ * DragonFly: https://github.com/DragonFlyBSD/DragonFlyBSD/blob/5e488df32cb01056a5b714a522e51c69ab7b4612/sys/sys/errno.h#L172
+ * FreeBSD: https://github.com/freebsd/freebsd-src/blob/7232e6dcc89b978825b30a537bca2e7d3a9b71bb/sys/sys/errno.h#L157
+ * OpenBSD: https://github.com/openbsd/src/blob/025fffe4c6e0113862ce4e1927e67517a2841502/sys/sys/errno.h#L151
+ * NetBSD: https://github.com/NetBSD/src/blob/ff24f695f5f53540b23b6bb4fa5c0b9d79b369e4/sys/sys/errno.h#L137
+ */
+ return 78;
+ } else if (os === "WINNT") {
+ // https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170
+ return 40;
+ }
+ throw new Error("Unsupported OS");
+ },
+};
+
+/*
+ * This test is for executing system calls in content processes to validate
+ * that calls that are meant to be blocked by content sandboxing are blocked.
+ * We use the term system calls loosely so that any OS API call such as
+ * fopen could be included.
+ */
+
+// Calls the native execv library function. Include imports so this can be
+// safely serialized and run remotely by ContentTask.spawn.
+function callExec(args) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let { lib, cmd } = args;
+ let libc = ctypes.open(lib);
+ let exec = libc.declare(
+ "execv",
+ ctypes.default_abi,
+ ctypes.int,
+ ctypes.char.ptr
+ );
+ let rv = exec(cmd);
+ libc.close();
+ return rv;
+}
+
+// Calls the native fork syscall.
+function callFork(args) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let { lib } = args;
+ let libc = ctypes.open(lib);
+ let fork = libc.declare("fork", ctypes.default_abi, ctypes.int);
+ let rv = fork();
+ libc.close();
+ return rv;
+}
+
+// Calls the native sysctl syscall.
+function callSysctl(args) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let { lib, name } = args;
+ let libc = ctypes.open(lib);
+ let sysctlbyname = libc.declare(
+ "sysctlbyname",
+ ctypes.default_abi,
+ ctypes.int,
+ ctypes.char.ptr,
+ ctypes.voidptr_t,
+ ctypes.size_t.ptr,
+ ctypes.voidptr_t,
+ ctypes.size_t.ptr
+ );
+ let rv = sysctlbyname(name, null, null, null, null);
+ libc.close();
+ return rv;
+}
+
+function callPrctl(args) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let { lib, option } = args;
+ let libc = ctypes.open(lib);
+ let prctl = libc.declare(
+ "prctl",
+ ctypes.default_abi,
+ ctypes.int,
+ ctypes.int, // option
+ ctypes.unsigned_long, // arg2
+ ctypes.unsigned_long, // arg3
+ ctypes.unsigned_long, // arg4
+ ctypes.unsigned_long // arg5
+ );
+ let rv = prctl(option, 0, 0, 0, 0);
+ if (rv == -1) {
+ rv = ctypes.errno;
+ }
+ libc.close();
+ return rv;
+}
+
+// Calls the native open/close syscalls.
+function callOpen(args) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let { lib, path, flags } = args;
+ let libc = ctypes.open(lib);
+ let open = libc.declare(
+ "open",
+ ctypes.default_abi,
+ ctypes.int,
+ ctypes.char.ptr,
+ ctypes.int
+ );
+ let close = libc.declare("close", ctypes.default_abi, ctypes.int, ctypes.int);
+ let fd = open(path, flags);
+ close(fd);
+ libc.close();
+ return fd;
+}
+
+// Verify faccessat2
+function callFaccessat2(args) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let { lib, dirfd, path, mode, flag } = args;
+ let libc = ctypes.open(lib);
+ let faccessat = libc.declare(
+ "faccessat",
+ ctypes.default_abi,
+ ctypes.int,
+ ctypes.int, // dirfd
+ ctypes.char.ptr, // path
+ ctypes.int, // mode
+ ctypes.int // flag
+ );
+ let rv = faccessat(dirfd, path, mode, flag);
+ if (rv == -1) {
+ rv = ctypes.errno;
+ }
+ libc.close();
+ return rv;
+}
+
+// open syscall flags
+function openWriteCreateFlags() {
+ Assert.ok(isMac() || isLinux());
+ if (isMac()) {
+ let O_WRONLY = 0x001;
+ let O_CREAT = 0x200;
+ return O_WRONLY | O_CREAT;
+ }
+ // Linux
+ let O_WRONLY = 0x01;
+ let O_CREAT = 0x40;
+ return O_WRONLY | O_CREAT;
+}
+
+// Returns the name of the native library needed for native syscalls
+function getOSLib() {
+ switch (Services.appinfo.OS) {
+ case "WINNT":
+ return "kernel32.dll";
+ case "Darwin":
+ return "libc.dylib";
+ case "Linux":
+ return "libc.so.6";
+ default:
+ Assert.ok(false, "Unknown OS");
+ return 0;
+ }
+}
+
+// Reading a header might be weird, but the alternatives to read a stable
+// version number we can easily check against are not much more fun
+async function getKernelVersion() {
+ let header = await IOUtils.readUTF8("/usr/include/linux/version.h");
+ let hr = header.split("\n");
+ for (let line in hr) {
+ let hrs = hr[line].split(" ");
+ if (hrs[0] === "#define" && hrs[1] === "LINUX_VERSION_CODE") {
+ return Number(hrs[2]);
+ }
+ }
+ throw Error("No LINUX_VERSION_CODE");
+}
+
+// This is how it is done in /usr/include/linux/version.h
+function computeKernelVersion(major, minor, dot) {
+ return (major << 16) + (minor << 8) + dot;
+}
+
+function getGlibcVersion() {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+ let libc = ctypes.open(getOSLib());
+ let gnu_get_libc_version = libc.declare(
+ "gnu_get_libc_version",
+ ctypes.default_abi,
+ ctypes.char.ptr
+ );
+ let rv = gnu_get_libc_version().readString();
+ libc.close();
+ let ar = rv.split(".");
+ // return a number made of MAJORMINOR
+ return Number(ar[0] + ar[1]);
+}
+
+// Returns a harmless command to execute with execv
+function getOSExecCmd() {
+ Assert.ok(!isWin());
+ return "/bin/cat";
+}
+
+// Returns true if the current content sandbox level, passed in
+// the |level| argument, supports syscall sandboxing.
+function areContentSyscallsSandboxed(level) {
+ let syscallsSandboxMinLevel = 0;
+
+ // Set syscallsSandboxMinLevel to the lowest level that has
+ // syscall sandboxing enabled. For now, this varies across
+ // Windows, Mac, Linux, other.
+ switch (Services.appinfo.OS) {
+ case "WINNT":
+ syscallsSandboxMinLevel = 1;
+ break;
+ case "Darwin":
+ syscallsSandboxMinLevel = 1;
+ break;
+ case "Linux":
+ syscallsSandboxMinLevel = 1;
+ break;
+ default:
+ Assert.ok(false, "Unknown OS");
+ }
+
+ return level >= syscallsSandboxMinLevel;
+}
+
+//
+// Drive tests for a single content process.
+//
+// Tests executing OS API calls in the content process. Limited to Mac
+// and Linux calls for now.
+//
+add_task(async function () {
+ // This test is only relevant in e10s
+ if (!gMultiProcessBrowser) {
+ ok(false, "e10s is enabled");
+ info("e10s is not enabled, exiting");
+ return;
+ }
+
+ let level = 0;
+ let prefExists = true;
+
+ // Read the security.sandbox.content.level pref.
+ // If the pref isn't set and we're running on Linux on !isNightly(),
+ // exit without failing. The Linux content sandbox is only enabled
+ // on Nightly at this time.
+ // eslint-disable-next-line mozilla/use-default-preference-values
+ try {
+ level = Services.prefs.getIntPref("security.sandbox.content.level");
+ } catch (e) {
+ prefExists = false;
+ }
+
+ ok(prefExists, "pref security.sandbox.content.level exists");
+ if (!prefExists) {
+ return;
+ }
+
+ info(`security.sandbox.content.level=${level}`);
+ ok(level > 0, "content sandbox is enabled.");
+
+ let areSyscallsSandboxed = areContentSyscallsSandboxed(level);
+
+ // Content sandbox enabled, but level doesn't include syscall sandboxing.
+ ok(areSyscallsSandboxed, "content syscall sandboxing is enabled.");
+ if (!areSyscallsSandboxed) {
+ info("content sandbox level too low for syscall tests, exiting\n");
+ return;
+ }
+
+ let browser = gBrowser.selectedBrowser;
+ let lib = getOSLib();
+
+ // use execv syscall
+ // (causes content process to be killed on Linux)
+ if (isMac()) {
+ // exec something harmless, this should fail
+ let cmd = getOSExecCmd();
+ let rv = await SpecialPowers.spawn(browser, [{ lib, cmd }], callExec);
+ ok(rv == -1, `exec(${cmd}) is not permitted`);
+ }
+
+ // use open syscall
+ if (isLinux() || isMac()) {
+ // open a file for writing in $HOME, this should fail
+ let path = fileInHomeDir().path;
+ let flags = openWriteCreateFlags();
+ let fd = await SpecialPowers.spawn(
+ browser,
+ [{ lib, path, flags }],
+ callOpen
+ );
+ ok(fd < 0, "opening a file for writing in home is not permitted");
+ }
+
+ // use open syscall
+ if (isLinux() || isMac()) {
+ // open a file for writing in the content temp dir, this should fail on
+ // macOS and work on Linux. The open handler in the content process closes
+ // the file for us
+ let path = fileInTempDir().path;
+ let flags = openWriteCreateFlags();
+ let fd = await SpecialPowers.spawn(
+ browser,
+ [{ lib, path, flags }],
+ callOpen
+ );
+ if (isMac()) {
+ ok(
+ fd === -1,
+ "opening a file for writing in content temp is not permitted"
+ );
+ } else {
+ ok(fd >= 0, "opening a file for writing in content temp is permitted");
+ }
+ }
+
+ // use fork syscall
+ if (isLinux() || isMac()) {
+ let rv = await SpecialPowers.spawn(browser, [{ lib }], callFork);
+ ok(rv == -1, "calling fork is not permitted");
+ }
+
+ // On macOS before 10.10 the |sysctl-name| predicate didn't exist for
+ // filtering |sysctl| access. Check the Darwin version before running the
+ // tests (Darwin 14.0.0 is macOS 10.10). This branch can be removed when we
+ // remove support for macOS 10.9.
+ if (isMac() && Services.sysinfo.getProperty("version") >= "14.0.0") {
+ let rv = await SpecialPowers.spawn(
+ browser,
+ [{ lib, name: "kern.boottime" }],
+ callSysctl
+ );
+ ok(rv == -1, "calling sysctl('kern.boottime') is not permitted");
+
+ rv = await SpecialPowers.spawn(
+ browser,
+ [{ lib, name: "net.inet.ip.ttl" }],
+ callSysctl
+ );
+ ok(rv == -1, "calling sysctl('net.inet.ip.ttl') is not permitted");
+
+ rv = await SpecialPowers.spawn(
+ browser,
+ [{ lib, name: "hw.ncpu" }],
+ callSysctl
+ );
+ ok(rv == 0, "calling sysctl('hw.ncpu') is permitted");
+ }
+
+ if (isLinux()) {
+ // These constants are not portable.
+ const AT_EACCESS = 512;
+ const PR_CAPBSET_READ = 23;
+
+ // verify we block PR_CAPBSET_READ with EINVAL
+ let option = PR_CAPBSET_READ;
+ let rv = await SpecialPowers.spawn(browser, [{ lib, option }], callPrctl);
+ ok(rv === ERRNO.EINVAL, "prctl(PR_CAPBSET_READ) is blocked");
+
+ const kernelVersion = await getKernelVersion();
+ const glibcVersion = getGlibcVersion();
+ // faccessat2 is only used with kernel 5.8+ by glibc 2.33+
+ if (glibcVersion >= 233 && kernelVersion >= computeKernelVersion(5, 8, 0)) {
+ info("Linux v5.8+, glibc 2.33+, checking faccessat2");
+ const dirfd = 0;
+ const path = "/";
+ const mode = 0;
+ // the value 0x01 is just one we know should get rejected
+ let rv = await SpecialPowers.spawn(
+ browser,
+ [{ lib, dirfd, path, mode, flag: 0x01 }],
+ callFaccessat2
+ );
+ ok(rv === ERRNO.ENOSYS, "faccessat2 (flag=0x01) was blocked with ENOSYS");
+
+ rv = await SpecialPowers.spawn(
+ browser,
+ [{ lib, dirfd, path, mode, flag: AT_EACCESS }],
+ callFaccessat2
+ );
+ ok(
+ rv === ERRNO.EACCES,
+ "faccessat2 (flag=0x200) was allowed, errno=EACCES"
+ );
+ } else {
+ info(
+ "Unsupported kernel (" +
+ kernelVersion +
+ " )/glibc (" +
+ glibcVersion +
+ "), skipping faccessat2"
+ );
+ }
+ }
+});
diff --git a/security/sandbox/test/browser_content_sandbox_utils.js b/security/sandbox/test/browser_content_sandbox_utils.js
new file mode 100644
index 0000000000..ce6ed39ff6
--- /dev/null
+++ b/security/sandbox/test/browser_content_sandbox_utils.js
@@ -0,0 +1,464 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const uuidGenerator = Services.uuid;
+
+/*
+ * Utility functions for the browser content sandbox tests.
+ */
+
+function sanityChecks() {
+ // This test is only relevant in e10s
+ if (!gMultiProcessBrowser) {
+ ok(false, "e10s is enabled");
+ info("e10s is not enabled, exiting");
+ return;
+ }
+
+ let level = 0;
+ let prefExists = true;
+
+ // Read the security.sandbox.content.level pref.
+ // eslint-disable-next-line mozilla/use-default-preference-values
+ try {
+ level = Services.prefs.getIntPref("security.sandbox.content.level");
+ } catch (e) {
+ prefExists = false;
+ }
+
+ ok(prefExists, "pref security.sandbox.content.level exists");
+ if (!prefExists) {
+ return;
+ }
+
+ info(`security.sandbox.content.level=${level}`);
+ ok(level > 0, "content sandbox is enabled.");
+
+ let isFileIOSandboxed = isContentFileIOSandboxed(level);
+
+ // Content sandbox enabled, but level doesn't include file I/O sandboxing.
+ ok(isFileIOSandboxed, "content file I/O sandboxing is enabled.");
+ if (!isFileIOSandboxed) {
+ info("content sandbox level too low for file I/O tests, exiting\n");
+ }
+}
+
+// Creates file at |path| and returns a promise that resolves with an object
+// with .ok boolean to indicate true if the file was successfully created,
+// otherwise false. Include imports so this can be safely serialized and run
+// remotely by ContentTask.spawn.
+function createFile(path) {
+ const { FileUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+
+ try {
+ const fstream = Cc[
+ "@mozilla.org/network/file-output-stream;1"
+ ].createInstance(Ci.nsIFileOutputStream);
+
+ fstream.init(
+ new FileUtils.File(path),
+ -1, // readonly mode
+ -1, // default permissions
+ 0
+ ); // behaviour flags
+
+ const ostream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
+ Ci.nsIBinaryOutputStream
+ );
+ ostream.setOutputStream(fstream);
+
+ const data = "TEST FILE DUMMY DATA";
+ ostream.writeBytes(data, data.length);
+
+ ostream.close();
+ fstream.close();
+ } catch (e) {
+ return { ok: false };
+ }
+
+ return { ok: true };
+}
+
+// Creates a symlink at |path| and returns a promise that resolves with an
+// object with .ok boolean to indicate true if the symlink was successfully
+// created, otherwise false. Include imports so this can be safely serialized
+// and run remotely by ContentTask.spawn.
+function createSymlink(path) {
+ const { ctypes } = ChromeUtils.importESModule(
+ "resource://gre/modules/ctypes.sys.mjs"
+ );
+
+ try {
+ const libc = ctypes.open(
+ Services.appinfo.OS === "Darwin" ? "libSystem.B.dylib" : "libc.so"
+ );
+
+ const symlink = libc.declare(
+ "symlink",
+ ctypes.default_abi,
+ ctypes.int, // return value
+ ctypes.char.ptr, // target
+ ctypes.char.ptr //linkpath
+ );
+
+ if (symlink("/etc", path)) {
+ return { ok: false };
+ }
+ } catch (e) {
+ return { ok: false };
+ }
+
+ return { ok: true };
+}
+
+// Deletes file at |path| and returns a promise that resolves with an object
+// with .ok boolean to indicate true if the file was successfully deleted,
+// otherwise false. Include imports so this can be safely serialized and run
+// remotely by ContentTask.spawn.
+function deleteFile(path) {
+ const { FileUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+
+ try {
+ const file = new FileUtils.File(path);
+ file.remove(false);
+ } catch (e) {
+ return { ok: false };
+ }
+
+ return { ok: true };
+}
+
+// Reads the directory at |path| and returns a promise that resolves when
+// iteration over the directory finishes or encounters an error. The promise
+// resolves with an object where .ok indicates success or failure and
+// .numEntries is the number of directory entries found.
+function readDir(path) {
+ const { FileUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+
+ let numEntries = 0;
+
+ try {
+ const file = new FileUtils.File(path);
+ const enumerator = file.directoryEntries;
+
+ while (enumerator.hasMoreElements()) {
+ void enumerator.nextFile;
+ numEntries++;
+ }
+ } catch (e) {
+ return { ok: false, numEntries };
+ }
+
+ return { ok: true, numEntries };
+}
+
+// Reads the file at |path| and returns a promise that resolves when
+// reading is completed. Returned object has boolean .ok to indicate
+// success or failure.
+function readFile(path) {
+ const { FileUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+
+ try {
+ const file = new FileUtils.File(path);
+
+ const fstream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ fstream.init(file, -1, -1, 0);
+
+ const istream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIBinaryInputStream
+ );
+ istream.setInputStream(fstream);
+
+ const available = istream.available();
+ void istream.readBytes(available);
+ } catch (e) {
+ return { ok: false };
+ }
+
+ return { ok: true };
+}
+
+// Does a stat of |path| and returns a promise that resolves if the
+// stat is successful. Returned object has boolean .ok to indicate
+// success or failure.
+function statPath(path) {
+ const { FileUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+
+ try {
+ const file = new FileUtils.File(path);
+ void file.lastModifiedTime;
+ } catch (e) {
+ return { ok: false };
+ }
+
+ return { ok: true };
+}
+
+// Returns true if the current content sandbox level, passed in
+// the |level| argument, supports filesystem sandboxing.
+function isContentFileIOSandboxed(level) {
+ let fileIOSandboxMinLevel = 0;
+
+ // Set fileIOSandboxMinLevel to the lowest level that has
+ // content filesystem sandboxing enabled. For now, this
+ // varies across Windows, Mac, Linux, other.
+ switch (Services.appinfo.OS) {
+ case "WINNT":
+ fileIOSandboxMinLevel = 1;
+ break;
+ case "Darwin":
+ fileIOSandboxMinLevel = 1;
+ break;
+ case "Linux":
+ fileIOSandboxMinLevel = 2;
+ break;
+ default:
+ Assert.ok(false, "Unknown OS");
+ }
+
+ return level >= fileIOSandboxMinLevel;
+}
+
+// Returns the lowest sandbox level where blanket reading of the profile
+// directory from the content process should be blocked by the sandbox.
+function minProfileReadSandboxLevel(level) {
+ switch (Services.appinfo.OS) {
+ case "WINNT":
+ return 3;
+ case "Darwin":
+ return 2;
+ case "Linux":
+ return 3;
+ default:
+ Assert.ok(false, "Unknown OS");
+ return 0;
+ }
+}
+
+// Returns the lowest sandbox level where blanket reading of the home
+// directory from the content process should be blocked by the sandbox.
+function minHomeReadSandboxLevel(level) {
+ switch (Services.appinfo.OS) {
+ case "WINNT":
+ return 3;
+ case "Darwin":
+ return 3;
+ case "Linux":
+ return 3;
+ default:
+ Assert.ok(false, "Unknown OS");
+ return 0;
+ }
+}
+
+function isMac() {
+ return Services.appinfo.OS == "Darwin";
+}
+function isWin() {
+ return Services.appinfo.OS == "WINNT";
+}
+function isLinux() {
+ return Services.appinfo.OS == "Linux";
+}
+
+function isNightly() {
+ let version = SpecialPowers.Services.appinfo.version;
+ return version.endsWith("a1");
+}
+
+function uuid() {
+ return uuidGenerator.generateUUID().toString();
+}
+
+// Returns a file object for a new file in the home dir ($HOME/<UUID>).
+function fileInHomeDir() {
+ // get home directory, make sure it exists
+ let homeDir = Services.dirsvc.get("Home", Ci.nsIFile);
+ Assert.ok(homeDir.exists(), "Home dir exists");
+ Assert.ok(homeDir.isDirectory(), "Home dir is a directory");
+
+ // build a file object for a new file named $HOME/<UUID>
+ let homeFile = homeDir.clone();
+ homeFile.appendRelativePath(uuid());
+ Assert.ok(!homeFile.exists(), homeFile.path + " does not exist");
+ return homeFile;
+}
+
+// Returns a file object for a new file in the content temp dir (.../<UUID>).
+function fileInTempDir() {
+ let contentTempKey = "TmpD";
+
+ // get the content temp dir, make sure it exists
+ let ctmp = Services.dirsvc.get(contentTempKey, Ci.nsIFile);
+ Assert.ok(ctmp.exists(), "Temp dir exists");
+ Assert.ok(ctmp.isDirectory(), "Temp dir is a directory");
+
+ // build a file object for a new file in content temp
+ let tempFile = ctmp.clone();
+ tempFile.appendRelativePath(uuid());
+ Assert.ok(!tempFile.exists(), tempFile.path + " does not exist");
+ return tempFile;
+}
+
+function GetProfileDir() {
+ // get profile directory
+ let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ return profileDir;
+}
+
+function GetHomeDir() {
+ // get home directory
+ let homeDir = Services.dirsvc.get("Home", Ci.nsIFile);
+ return homeDir;
+}
+
+function GetHomeSubdir(subdir) {
+ return GetSubdir(GetHomeDir(), subdir);
+}
+
+function GetHomeSubdirFile(subdir) {
+ return GetSubdirFile(GetHomeSubdir(subdir));
+}
+
+function GetSubdir(dir, subdir) {
+ let newSubdir = dir.clone();
+ newSubdir.appendRelativePath(subdir);
+ return newSubdir;
+}
+
+function GetSubdirFile(dir) {
+ let newFile = dir.clone();
+ newFile.appendRelativePath(uuid());
+ return newFile;
+}
+
+function GetPerUserExtensionDir() {
+ return Services.dirsvc.get("XREUSysExt", Ci.nsIFile);
+}
+
+// Returns a file object for the file or directory named |name| in the
+// profile directory.
+function GetProfileEntry(name) {
+ let entry = GetProfileDir();
+ entry.append(name);
+ return entry;
+}
+
+function GetDir(path) {
+ let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ dir.initWithPath(path);
+ Assert.ok(dir.isDirectory(), `${path} is a directory`);
+ return dir;
+}
+
+function GetDirFromEnvVariable(varName) {
+ return GetDir(Services.env.get(varName));
+}
+
+function GetFile(path) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ file.initWithPath(path);
+ return file;
+}
+
+function GetBrowserType(type) {
+ let browserType = undefined;
+
+ if (!GetBrowserType[type]) {
+ if (type === "web") {
+ GetBrowserType[type] = gBrowser.selectedBrowser;
+ } else {
+ // open a tab in a `type` content process
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
+ preferredRemoteType: type,
+ });
+ // get the browser for the `type` process tab
+ GetBrowserType[type] = gBrowser.getBrowserForTab(gBrowser.selectedTab);
+ }
+ }
+
+ browserType = GetBrowserType[type];
+ ok(
+ browserType.remoteType === type,
+ `GetBrowserType(${type}) returns a ${type} process`
+ );
+ return browserType;
+}
+
+function GetWebBrowser() {
+ return GetBrowserType("web");
+}
+
+function isFileContentProcessEnabled() {
+ // Ensure that the file content process is enabled.
+ let fileContentProcessEnabled = Services.prefs.getBoolPref(
+ "browser.tabs.remote.separateFileUriProcess"
+ );
+ ok(fileContentProcessEnabled, "separate file content process is enabled");
+ return fileContentProcessEnabled;
+}
+
+function GetFileBrowser() {
+ if (!isFileContentProcessEnabled()) {
+ return undefined;
+ }
+ return GetBrowserType("file");
+}
+
+function GetSandboxLevel() {
+ // Current level
+ return Services.prefs.getIntPref("security.sandbox.content.level");
+}
+
+async function runTestsList(tests) {
+ let level = GetSandboxLevel();
+
+ // remove tests not enabled by the current sandbox level
+ tests = tests.filter(test => test.minLevel <= level);
+
+ for (let test of tests) {
+ let okString = test.ok ? "allowed" : "blocked";
+ let processType = test.browser.remoteType;
+
+ // ensure the file/dir exists before we ask a content process to stat
+ // it so we know a failure is not due to a nonexistent file/dir
+ if (test.func === statPath) {
+ ok(test.file.exists(), `${test.file.path} exists`);
+ }
+
+ let result = await ContentTask.spawn(
+ test.browser,
+ test.file.path,
+ test.func
+ );
+
+ ok(
+ result.ok == test.ok,
+ `reading ${test.desc} from a ${processType} process ` +
+ `is ${okString} (${test.file.path})`
+ );
+
+ // if the directory is not expected to be readable,
+ // ensure the listing has zero entries
+ if (test.func === readDir && !test.ok) {
+ ok(result.numEntries == 0, `directory list is empty (${test.file.path})`);
+ }
+
+ if (test.cleanup != undefined) {
+ await test.cleanup(test.file.path);
+ }
+ }
+}
diff --git a/security/sandbox/test/browser_sandbox_test.js b/security/sandbox/test/browser_sandbox_test.js
new file mode 100644
index 0000000000..e0d456b236
--- /dev/null
+++ b/security/sandbox/test/browser_sandbox_test.js
@@ -0,0 +1,59 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+function test() {
+ waitForExplicitFinish();
+
+ // Types of processes to test, taken from GeckoProcessTypes.h
+ // GPU process might not run depending on the platform, so we need it to be
+ // the last one of the list to allow the remainingTests logic below to work
+ // as expected.
+ //
+ // For UtilityProcess, allow constructing a string made of the process type
+ // and the sandbox variant we want to test, e.g.,
+ // utility:0 for GENERIC_UTILITY
+ // utility:1 for AppleMedia/WMF on macOS/Windows
+ var processTypes = ["tab", "socket", "rdd", "gmplugin", "utility:0", "gpu"];
+
+ const platform = SpecialPowers.Services.appinfo.OS;
+ if (platform === "WINNT" || platform === "Darwin") {
+ processTypes.push("utility:1");
+ }
+
+ // A callback called after each test-result.
+ let sandboxTestResult = (subject, topic, data) => {
+ let { testid, passed, message } = JSON.parse(data);
+ ok(
+ passed,
+ "Test " + testid + (passed ? " passed: " : " failed: ") + message
+ );
+ };
+ Services.obs.addObserver(sandboxTestResult, "sandbox-test-result");
+
+ var remainingTests = processTypes.length;
+
+ // A callback that is notified when a child process is done running tests.
+ let sandboxTestDone = () => {
+ remainingTests = remainingTests - 1;
+ if (remainingTests == 0) {
+ Services.obs.removeObserver(sandboxTestResult, "sandbox-test-result");
+ Services.obs.removeObserver(sandboxTestDone, "sandbox-test-done");
+
+ // Notify SandboxTest component that it should terminate the connection
+ // with the child processes.
+ comp.finishTests();
+ // Notify mochitest that all process tests are complete.
+ finish();
+ }
+ };
+ Services.obs.addObserver(sandboxTestDone, "sandbox-test-done");
+
+ var comp = Cc["@mozilla.org/sandbox/sandbox-test;1"].getService(
+ Ci.mozISandboxTest
+ );
+
+ comp.startTests(processTypes);
+}
diff --git a/security/sandbox/test/browser_snap.ini b/security/sandbox/test/browser_snap.ini
new file mode 100644
index 0000000000..ad7b058cb1
--- /dev/null
+++ b/security/sandbox/test/browser_snap.ini
@@ -0,0 +1,14 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+[DEFAULT]
+skip-if = ccov || (os == "linux" && (asan || tsan)) # bug 1784517
+tags = contentsandbox
+support-files =
+ browser_content_sandbox_utils.js
+ browser_content_sandbox_fs_tests.js
+test-directories =
+ /tmp/.snap_firefox_current_real/
+environment=SNAP=/tmp/.snap_firefox_current_real/
+
+[browser_content_sandbox_fs_snap.js]
+run-if = (os == 'linux')
diff --git a/security/sandbox/test/browser_xdg.ini b/security/sandbox/test/browser_xdg.ini
new file mode 100644
index 0000000000..aee4c63c5a
--- /dev/null
+++ b/security/sandbox/test/browser_xdg.ini
@@ -0,0 +1,14 @@
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+[DEFAULT]
+skip-if = ccov || (os == "linux" && (asan || tsan)) # bug 1784517
+tags = contentsandbox
+support-files =
+ browser_content_sandbox_utils.js
+ browser_content_sandbox_fs_tests.js
+test-directories =
+ /tmp/.xdg_config_home_test
+environment=XDG_CONFIG_HOME=/tmp/.xdg_config_home_test
+
+[browser_content_sandbox_fs_xdg.js]
+run-if = (os == 'linux')
diff --git a/security/sandbox/test/bug1393259.html b/security/sandbox/test/bug1393259.html
new file mode 100644
index 0000000000..b1e3cca99a
--- /dev/null
+++ b/security/sandbox/test/bug1393259.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8"/>
+</head>
+<style>
+#content { display: inline-block; }
+.monospace_fallback { font: 3em monospace; }
+</style>
+<body>
+
+<div id="content" class="monospace_fallback">
+abcdefghijklmnopqrstuvwxyz<br>
+<b>abcdefghijklmnopqrstuvwxyz</b><br>
+<i>abcdefghijklmnopqrstuvwxyz</i>
+</div>
+
+</body>
+</html>
diff --git a/security/sandbox/test/mac_register_font.py b/security/sandbox/test/mac_register_font.py
new file mode 100755
index 0000000000..549becf565
--- /dev/null
+++ b/security/sandbox/test/mac_register_font.py
@@ -0,0 +1,85 @@
+#!/usr/bin/python
+# 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/. */
+
+"""
+mac_register_font.py
+
+Mac-specific utility command to register a font file with the OS.
+"""
+
+import argparse
+import sys
+
+import Cocoa
+import CoreText
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-v",
+ "--verbose",
+ action="store_true",
+ help="print verbose registration failures",
+ default=False,
+ )
+ parser.add_argument(
+ "file", nargs="*", help="font file to register or unregister", default=[]
+ )
+ parser.add_argument(
+ "-u",
+ "--unregister",
+ action="store_true",
+ help="unregister the provided fonts",
+ default=False,
+ )
+ parser.add_argument(
+ "-p",
+ "--persist-user",
+ action="store_true",
+ help="permanently register the font",
+ default=False,
+ )
+
+ args = parser.parse_args()
+
+ if args.persist_user:
+ scope = CoreText.kCTFontManagerScopeUser
+ scopeDesc = "user"
+ else:
+ scope = CoreText.kCTFontManagerScopeSession
+ scopeDesc = "session"
+
+ failureCount = 0
+ for fontPath in args.file:
+ fontURL = Cocoa.NSURL.fileURLWithPath_(fontPath)
+ (result, error) = register_or_unregister_font(fontURL, args.unregister, scope)
+ if result:
+ print(
+ "%sregistered font %s with %s scope"
+ % (("un" if args.unregister else ""), fontPath, scopeDesc)
+ )
+ else:
+ print(
+ "Failed to %sregister font %s with %s scope"
+ % (("un" if args.unregister else ""), fontPath, scopeDesc)
+ )
+ if args.verbose:
+ print(error)
+ failureCount += 1
+
+ sys.exit(failureCount)
+
+
+def register_or_unregister_font(fontURL, unregister, scope):
+ return (
+ CoreText.CTFontManagerUnregisterFontsForURL(fontURL, scope, None)
+ if unregister
+ else CoreText.CTFontManagerRegisterFontsForURL(fontURL, scope, None)
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/security/sandbox/win/SandboxInitialization.cpp b/security/sandbox/win/SandboxInitialization.cpp
new file mode 100644
index 0000000000..8ba8b4e69a
--- /dev/null
+++ b/security/sandbox/win/SandboxInitialization.cpp
@@ -0,0 +1,202 @@
+/* -*- 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/. */
+
+#include "SandboxInitialization.h"
+
+#include "base/memory/ref_counted.h"
+#include "nsWindowsDllInterceptor.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/WindowsProcessMitigations.h"
+
+namespace mozilla {
+namespace sandboxing {
+
+typedef BOOL(WINAPI* CloseHandle_func)(HANDLE hObject);
+static WindowsDllInterceptor::FuncHookType<CloseHandle_func> stub_CloseHandle;
+
+typedef BOOL(WINAPI* DuplicateHandle_func)(
+ HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
+ HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess,
+ BOOL bInheritHandle, DWORD dwOptions);
+static WindowsDllInterceptor::FuncHookType<DuplicateHandle_func>
+ stub_DuplicateHandle;
+
+static BOOL WINAPI patched_CloseHandle(HANDLE hObject) {
+ // Check all handles being closed against the sandbox's tracked handles.
+ base::win::OnHandleBeingClosed(hObject);
+ return stub_CloseHandle(hObject);
+}
+
+static BOOL WINAPI patched_DuplicateHandle(
+ HANDLE hSourceProcessHandle, HANDLE hSourceHandle,
+ HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess,
+ BOOL bInheritHandle, DWORD dwOptions) {
+ // If closing a source handle from our process check it against the sandbox's
+ // tracked handles.
+ if ((dwOptions & DUPLICATE_CLOSE_SOURCE) &&
+ (GetProcessId(hSourceProcessHandle) == ::GetCurrentProcessId())) {
+ base::win::OnHandleBeingClosed(hSourceHandle);
+ }
+
+ return stub_DuplicateHandle(hSourceProcessHandle, hSourceHandle,
+ hTargetProcessHandle, lpTargetHandle,
+ dwDesiredAccess, bInheritHandle, dwOptions);
+}
+
+typedef BOOL(WINAPI* ApiSetQueryApiSetPresence_func)(PCUNICODE_STRING,
+ PBOOLEAN);
+static WindowsDllInterceptor::FuncHookType<ApiSetQueryApiSetPresence_func>
+ stub_ApiSetQueryApiSetPresence;
+
+static const WCHAR gApiSetNtUserWindowStation[] =
+ L"ext-ms-win-ntuser-windowstation-l1-1-0";
+
+static BOOL WINAPI patched_ApiSetQueryApiSetPresence(
+ PCUNICODE_STRING aNamespace, PBOOLEAN aPresent) {
+ if (aNamespace && aPresent &&
+ !wcsncmp(aNamespace->Buffer, gApiSetNtUserWindowStation,
+ aNamespace->Length / sizeof(WCHAR))) {
+ *aPresent = FALSE;
+ return TRUE;
+ }
+
+ return stub_ApiSetQueryApiSetPresence(aNamespace, aPresent);
+}
+
+static WindowsDllInterceptor Kernel32Intercept;
+static WindowsDllInterceptor gApiQueryIntercept;
+
+static bool EnableHandleCloseMonitoring() {
+ Kernel32Intercept.Init("kernel32.dll");
+ bool hooked = stub_CloseHandle.Set(Kernel32Intercept, "CloseHandle",
+ &patched_CloseHandle);
+ if (!hooked) {
+ return false;
+ }
+
+ hooked = stub_DuplicateHandle.Set(Kernel32Intercept, "DuplicateHandle",
+ &patched_DuplicateHandle);
+ if (!hooked) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * There is a bug in COM that causes its initialization to fail when user32.dll
+ * is loaded but Win32k lockdown is enabled. COM uses ApiSetQueryApiSetPresence
+ * to make this check. When we are under Win32k lockdown, we hook
+ * ApiSetQueryApiSetPresence and force it to tell the caller that the DLL of
+ * interest is not present.
+ */
+static void EnableApiQueryInterception() {
+ if (!IsWin32kLockedDown()) {
+ return;
+ }
+
+ gApiQueryIntercept.Init(L"Api-ms-win-core-apiquery-l1-1-0.dll");
+ DebugOnly<bool> hookSetOk = stub_ApiSetQueryApiSetPresence.Set(
+ gApiQueryIntercept, "ApiSetQueryApiSetPresence",
+ &patched_ApiSetQueryApiSetPresence);
+ MOZ_ASSERT(hookSetOk);
+}
+
+static bool ShouldDisableHandleVerifier() {
+#if defined(_X86_) && (defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG))
+ // Chromium only has the verifier enabled for 32-bit and our close monitoring
+ // hooks cause debug assertions for 64-bit anyway.
+ // For x86 keep the verifier enabled by default only for Nightly or debug.
+ return false;
+#else
+ return !getenv("MOZ_ENABLE_HANDLE_VERIFIER");
+#endif
+}
+
+static void InitializeHandleVerifier() {
+ // Disable the handle verifier if we don't want it or can't enable the close
+ // monitoring hooks.
+ if (ShouldDisableHandleVerifier() || !EnableHandleCloseMonitoring()) {
+ base::win::DisableHandleVerifier();
+ }
+}
+
+static sandbox::TargetServices* InitializeTargetServices() {
+ // This might disable the verifier, so we want to do it before it is used.
+ InitializeHandleVerifier();
+
+ EnableApiQueryInterception();
+
+ sandbox::TargetServices* targetServices =
+ sandbox::SandboxFactory::GetTargetServices();
+ if (!targetServices) {
+ return nullptr;
+ }
+
+ if (targetServices->Init() != sandbox::SBOX_ALL_OK) {
+ return nullptr;
+ }
+
+ return targetServices;
+}
+
+sandbox::TargetServices* GetInitializedTargetServices() {
+ static sandbox::TargetServices* sInitializedTargetServices =
+ InitializeTargetServices();
+
+ return sInitializedTargetServices;
+}
+
+void LowerSandbox() { GetInitializedTargetServices()->LowerToken(); }
+
+static sandbox::BrokerServices* InitializeBrokerServices() {
+ // This might disable the verifier, so we want to do it before it is used.
+ InitializeHandleVerifier();
+
+ sandbox::BrokerServices* brokerServices =
+ sandbox::SandboxFactory::GetBrokerServices();
+ if (!brokerServices) {
+ return nullptr;
+ }
+
+ if (brokerServices->Init() != sandbox::SBOX_ALL_OK) {
+ return nullptr;
+ }
+
+ // Comment below copied from Chromium code.
+ // Precreate the desktop and window station used by the renderers.
+ // IMPORTANT: This piece of code needs to run as early as possible in the
+ // process because it will initialize the sandbox broker, which requires
+ // the process to swap its window station. During this time all the UI
+ // will be broken. This has to run before threads and windows are created.
+ scoped_refptr<sandbox::TargetPolicy> policy = brokerServices->CreatePolicy();
+ policy->CreateAlternateDesktop(true);
+
+ // Ensure the relevant mitigations are enforced.
+ mozilla::sandboxing::ApplyParentProcessMitigations();
+
+ return brokerServices;
+}
+
+sandbox::BrokerServices* GetInitializedBrokerServices() {
+ static sandbox::BrokerServices* sInitializedBrokerServices =
+ InitializeBrokerServices();
+
+ return sInitializedBrokerServices;
+}
+
+void ApplyParentProcessMitigations() {
+ // The main reason for this call is for the token hardening, but chromium code
+ // also ensures DEP without ATL thunk so we do the same.
+ sandbox::ApplyProcessMitigationsToCurrentProcess(
+ sandbox::MITIGATION_DEP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+ sandbox::MITIGATION_HARDEN_TOKEN_IL_POLICY);
+}
+
+} // namespace sandboxing
+} // namespace mozilla
diff --git a/security/sandbox/win/SandboxInitialization.h b/security/sandbox/win/SandboxInitialization.h
new file mode 100644
index 0000000000..1d49c0d899
--- /dev/null
+++ b/security/sandbox/win/SandboxInitialization.h
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+#ifndef mozilla_sandboxing_SandboxInitialization_h
+#define mozilla_sandboxing_SandboxInitialization_h
+
+namespace sandbox {
+class BrokerServices;
+class TargetServices;
+} // namespace sandbox
+
+// Things that use this file will probably want access to the IsSandboxedProcess
+// function defined in one of the Chromium sandbox cc files.
+extern "C" bool IsSandboxedProcess();
+
+namespace mozilla {
+// Note the Chromium code just uses a bare sandbox namespace, which makes using
+// sandbox for our namespace painful.
+namespace sandboxing {
+
+/**
+ * Initializes (if required) and returns the Chromium sandbox TargetServices.
+ *
+ * @return the TargetServices or null if the creation or initialization failed.
+ */
+sandbox::TargetServices* GetInitializedTargetServices();
+
+/**
+ * Lowers the permissions on the process sandbox.
+ * Provided because the GMP sandbox needs to be lowered from the executable.
+ */
+void LowerSandbox();
+
+/**
+ * Initializes (if required) and returns the Chromium sandbox BrokerServices.
+ *
+ * @return the BrokerServices or null if the creation or initialization failed.
+ */
+sandbox::BrokerServices* GetInitializedBrokerServices();
+
+/**
+ * Apply mitigations for parent processes.
+ */
+void ApplyParentProcessMitigations();
+
+} // namespace sandboxing
+} // namespace mozilla
+
+#endif // mozilla_sandboxing_SandboxInitialization_h
diff --git a/security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl b/security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl
new file mode 100644
index 0000000000..7057a2ca42
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/PRemoteSandboxBroker.ipdl
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
+
+namespace mozilla {
+
+struct EnvVar {
+ nsString name;
+ nsString value;
+};
+
+struct LaunchParameters {
+ nsString path;
+ nsString args;
+ EnvVar[] env;
+ uint32_t processType;
+ uint32_t sandboxLevel;
+ nsString[] allowedReadFiles;
+ uint64_t[] shareHandles;
+ bool enableLogging;
+};
+
+[NeedsOtherPid, NestedUpTo=inside_sync]
+sync protocol PRemoteSandboxBroker
+{
+parent:
+ async InitCrashReporter(NativeThreadId threadId);
+child:
+ [Nested=inside_sync] sync LaunchApp(LaunchParameters params)
+ returns (bool ok, uint64_t handle);
+};
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp
new file mode 100644
index 0000000000..0ce2a60a8e
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.cpp
@@ -0,0 +1,97 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerChild.h"
+#include "chrome/common/ipc_channel.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "nsDebugImpl.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "RemoteSandboxBrokerProcessChild.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+
+RemoteSandboxBrokerChild::RemoteSandboxBrokerChild() {
+ nsDebugImpl::SetMultiprocessMode("RemoteSandboxBroker");
+}
+
+RemoteSandboxBrokerChild::~RemoteSandboxBrokerChild() {}
+
+bool RemoteSandboxBrokerChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint) {
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return false;
+ }
+ CrashReporterClient::InitSingleton(this);
+ return true;
+}
+
+void RemoteSandboxBrokerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Abnormal shutdown of GMP process!");
+ ipc::ProcessChild::QuickExit();
+ }
+ CrashReporterClient::DestroySingleton();
+ XRE_ShutdownChildProcess();
+}
+
+mozilla::ipc::IPCResult RemoteSandboxBrokerChild::RecvLaunchApp(
+ LaunchParameters&& aParams, bool* aOutOk, uint64_t* aOutHandle) {
+ auto towstring = [](const nsString& s) {
+ return std::wstring(s.get(), s.Length());
+ };
+
+ base::EnvironmentMap envmap;
+ for (const EnvVar& env : aParams.env()) {
+ envmap[towstring(env.name())] = towstring(env.value());
+ }
+
+ // We need to add our parent as a target peer, so that the sandboxed child can
+ // duplicate handles to it for crash reporting. AddTargetPeer duplicates the
+ // handle, so we use a ScopedProcessHandle to automatically close ours.
+ ipc::ScopedProcessHandle parentProcHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &parentProcHandle.rwget())) {
+ *aOutOk = false;
+ return IPC_OK();
+ }
+ mSandboxBroker.AddTargetPeer(parentProcHandle);
+
+ if (!mSandboxBroker.SetSecurityLevelForGMPlugin(
+ AbstractSandboxBroker::SandboxLevel(aParams.sandboxLevel()),
+ /* aIsRemoteLaunch */ true)) {
+ *aOutOk = false;
+ return IPC_OK();
+ }
+
+ for (const auto& path : aParams.allowedReadFiles()) {
+ if (!mSandboxBroker.AllowReadFile(path.get())) {
+ *aOutOk = false;
+ return IPC_OK();
+ }
+ }
+
+ for (const auto& handle : aParams.shareHandles()) {
+ mSandboxBroker.AddHandleToShare(HANDLE(handle));
+ }
+
+ HANDLE p;
+ mozilla::Result<mozilla::Ok, LaunchError> err =
+ mSandboxBroker.LaunchApp(aParams.path().get(), aParams.args().get(),
+ envmap, GeckoProcessType(aParams.processType()),
+ aParams.enableLogging(), nullptr, (void**)&p);
+ *aOutOk = err.isOk();
+ if (*aOutOk) {
+ *aOutHandle = uint64_t(p);
+ }
+
+ for (const auto& handle : aParams.shareHandles()) {
+ CloseHandle(HANDLE(handle));
+ }
+
+ return IPC_OK();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h
new file mode 100644
index 0000000000..cc707609df
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerChild.h
@@ -0,0 +1,35 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef RemoteSandboxBrokerChild_h_
+#define RemoteSandboxBrokerChild_h_
+
+#include "mozilla/PRemoteSandboxBrokerChild.h"
+#include "sandboxBroker.h"
+
+namespace mozilla {
+
+class RemoteSandboxBrokerChild : public PRemoteSandboxBrokerChild {
+ friend class PRemoteSandboxBrokerChild;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(RemoteSandboxBrokerChild, override)
+
+ RemoteSandboxBrokerChild();
+ bool Init(mozilla::ipc::UntypedEndpoint&& aEndpoint);
+
+ private:
+ virtual ~RemoteSandboxBrokerChild();
+ mozilla::ipc::IPCResult RecvLaunchApp(LaunchParameters&& aParams,
+ bool* aOutOk, uint64_t* aOutHandle);
+
+ void ActorDestroy(ActorDestroyReason aWhy);
+ SandboxBroker mSandboxBroker;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp
new file mode 100644
index 0000000000..7306f4f7bf
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.cpp
@@ -0,0 +1,83 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerParent.h"
+#include "RemoteSandboxBrokerProcessParent.h"
+#include "mozilla/Telemetry.h"
+#include <windows.h>
+
+namespace mozilla {
+
+RefPtr<GenericPromise> RemoteSandboxBrokerParent::Launch(
+ uint32_t aLaunchArch, const nsTArray<uint64_t>& aHandlesToShare,
+ nsISerialEventTarget* aThread) {
+ MOZ_ASSERT(!mProcess);
+ if (mProcess) {
+ // Don't re-init.
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ }
+
+ mProcess = new RemoteSandboxBrokerProcessParent();
+#ifdef ALLOW_GECKO_CHILD_PROCESS_ARCH
+ mProcess->SetLaunchArchitecture(aLaunchArch);
+#endif
+ for (uint64_t handle : aHandlesToShare) {
+ mProcess->AddHandleToShare(HANDLE(handle));
+ }
+
+ auto resolve = [self = RefPtr{this}](base::ProcessHandle handle) {
+ self->mOpened = self->mProcess->TakeInitialEndpoint().Bind(self);
+ if (!self->mOpened) {
+ self->mProcess->Destroy();
+ self->mProcess = nullptr;
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ }
+ return GenericPromise::CreateAndResolve(true, __func__);
+ };
+
+ auto reject = [self = RefPtr{this}]() {
+ NS_ERROR("failed to launch child in the parent");
+ if (self->mProcess) {
+ self->mProcess->Destroy();
+ self->mProcess = nullptr;
+ }
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ };
+
+ return mProcess->AsyncLaunch()->Then(aThread, __func__, std::move(resolve),
+ std::move(reject));
+}
+
+bool RemoteSandboxBrokerParent::DuplicateFromLauncher(HANDLE aLauncherHandle,
+ LPHANDLE aOurHandle) {
+ return ::DuplicateHandle(mProcess->GetChildProcessHandle(), aLauncherHandle,
+ ::GetCurrentProcess(), aOurHandle, 0, false,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
+}
+
+void RemoteSandboxBrokerParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
+ nsDependentCString(XRE_GeckoProcessTypeToString(
+ GeckoProcessType_RemoteSandboxBroker)),
+ 1);
+ GenerateCrashReport(OtherPid());
+ }
+ Shutdown();
+}
+
+void RemoteSandboxBrokerParent::Shutdown() {
+ if (mOpened) {
+ mOpened = false;
+ Close();
+ }
+ if (mProcess) {
+ mProcess->Destroy();
+ mProcess = nullptr;
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h
new file mode 100644
index 0000000000..7645b68923
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerParent.h
@@ -0,0 +1,48 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef RemoteSandboxBrokerParent_h_
+#define RemoteSandboxBrokerParent_h_
+
+#include "mozilla/PRemoteSandboxBrokerParent.h"
+#include "RemoteSandboxBrokerProcessParent.h"
+#include "mozilla/ipc/CrashReporterHelper.h"
+
+namespace mozilla {
+
+class RemoteSandboxBrokerParent
+ : public PRemoteSandboxBrokerParent,
+ public ipc::CrashReporterHelper<GeckoProcessType_RemoteSandboxBroker> {
+ friend class PRemoteSandboxBrokerParent;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(RemoteSandboxBrokerParent, override)
+
+ bool DuplicateFromLauncher(HANDLE aLauncherHandle, LPHANDLE aOurHandle);
+
+ void Shutdown();
+
+ // Asynchronously launches the launcher process.
+ // Note: we rely on the caller to keep this instance alive
+ // until this promise resolves.
+ // aThread is the thread to use to resolve the promise on if needed.
+ RefPtr<GenericPromise> Launch(uint32_t aLaunchArch,
+ const nsTArray<uint64_t>& aHandlesToShare,
+ nsISerialEventTarget* aThread);
+
+ private:
+ ~RemoteSandboxBrokerParent() = default;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ RemoteSandboxBrokerProcessParent* mProcess = nullptr;
+
+ bool mOpened = false;
+};
+
+} // namespace mozilla
+
+#endif // RemoteSandboxBrokerParent_h_
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp
new file mode 100644
index 0000000000..32e7d3b1f4
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.cpp
@@ -0,0 +1,27 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerProcessChild.h"
+
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/BackgroundHangMonitor.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+
+RemoteSandboxBrokerProcessChild::~RemoteSandboxBrokerProcessChild() {}
+
+bool RemoteSandboxBrokerProcessChild::Init(int aArgc, char* aArgv[]) {
+ BackgroundHangMonitor::Startup();
+ return mSandboxBrokerChild->Init(TakeInitialEndpoint());
+}
+
+void RemoteSandboxBrokerProcessChild::CleanUp() {
+ BackgroundHangMonitor::Shutdown();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h
new file mode 100644
index 0000000000..7b47f1e828
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessChild.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef RemoteSandboxBrokerProcessChild_h_
+#define RemoteSandboxBrokerProcessChild_h_
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "RemoteSandboxBrokerChild.h"
+
+namespace mozilla {
+
+class RemoteSandboxBrokerProcessChild final
+ : public mozilla::ipc::ProcessChild {
+ protected:
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+ using ProcessChild::ProcessChild;
+ ~RemoteSandboxBrokerProcessChild();
+
+ bool Init(int aArgc, char* aArgv[]) override;
+ void CleanUp() override;
+
+ private:
+ RefPtr<RemoteSandboxBrokerChild> mSandboxBrokerChild =
+ new RemoteSandboxBrokerChild;
+};
+
+} // namespace mozilla
+
+#endif // GMPProcessChild_h_
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp
new file mode 100644
index 0000000000..218753bb52
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "RemoteSandboxBrokerProcessParent.h"
+#include <windows.h>
+
+#include "mozilla/ipc/LaunchError.h"
+
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::ipc::LaunchError;
+using mozilla::ipc::ProcessHandlePromise;
+
+namespace mozilla {
+
+RemoteSandboxBrokerProcessParent::RemoteSandboxBrokerProcessParent()
+ : GeckoChildProcessHost(GeckoProcessType_RemoteSandboxBroker) {
+ MOZ_COUNT_CTOR(RemoteSandboxBrokerProcessParent);
+}
+
+RemoteSandboxBrokerProcessParent::~RemoteSandboxBrokerProcessParent() {
+ MOZ_COUNT_DTOR(RemoteSandboxBrokerProcessParent);
+}
+
+RefPtr<ProcessHandlePromise> RemoteSandboxBrokerProcessParent::AsyncLaunch() {
+ if (!GeckoChildProcessHost::AsyncLaunch()) {
+ return ProcessHandlePromise::CreateAndReject(
+ LaunchError("RSBPP::AsyncLaunch"), __func__);
+ }
+ return WhenProcessHandleReady();
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h
new file mode 100644
index 0000000000..6b298ee9c6
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/RemoteSandboxBrokerProcessParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=4 et :
+ * 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/. */
+
+#ifndef RemoteSandboxBrokerProcessParent_h_
+#define RemoteSandboxBrokerProcessParent_h_
+
+#include "mozilla/Attributes.h"
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/thread.h"
+#include "chrome/common/child_process_host.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+class nsIRunnable;
+
+namespace mozilla {
+
+class RemoteSandboxBrokerProcessParent final
+ : public mozilla::ipc::GeckoChildProcessHost {
+ public:
+ RemoteSandboxBrokerProcessParent();
+
+ RefPtr<ipc::ProcessHandlePromise> AsyncLaunch();
+
+ bool CanShutdown() override { return true; }
+
+ using mozilla::ipc::GeckoChildProcessHost::GetChannel;
+ using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle;
+
+ private:
+ ~RemoteSandboxBrokerProcessParent();
+
+ DISALLOW_COPY_AND_ASSIGN(RemoteSandboxBrokerProcessParent);
+};
+
+} // namespace mozilla
+
+#endif // ifndef GMPProcessParent_h
diff --git a/security/sandbox/win/src/remotesandboxbroker/moz.build b/security/sandbox/win/src/remotesandboxbroker/moz.build
new file mode 100644
index 0000000000..6d661ace4c
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/moz.build
@@ -0,0 +1,30 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ "remoteSandboxBroker.cpp",
+ "RemoteSandboxBrokerChild.cpp",
+ "RemoteSandboxBrokerParent.cpp",
+ "RemoteSandboxBrokerProcessChild.cpp",
+ "RemoteSandboxBrokerProcessParent.cpp",
+]
+
+EXPORTS.mozilla += [
+ "remoteSandboxBroker.h",
+ "RemoteSandboxBrokerChild.h",
+ "RemoteSandboxBrokerParent.h",
+ "RemoteSandboxBrokerProcessChild.h",
+ "RemoteSandboxBrokerProcessParent.h",
+]
+
+for var in ("UNICODE", "_UNICODE"):
+ DEFINES[var] = True
+
+FINAL_LIBRARY = "xul"
+
+IPDL_SOURCES += ["PRemoteSandboxBroker.ipdl"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp
new file mode 100644
index 0000000000..d1148e985a
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.cpp
@@ -0,0 +1,170 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "remoteSandboxBroker.h"
+
+#include "RemoteSandboxBrokerParent.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsIThread.h"
+
+namespace mozilla {
+
+RemoteSandboxBroker::RemoteSandboxBroker(uint32_t aLaunchArch)
+ : mParent(new RemoteSandboxBrokerParent), mLaunchArch(aLaunchArch) {}
+
+RemoteSandboxBroker::~RemoteSandboxBroker() {
+ MOZ_ASSERT(
+ mShutdown,
+ "Shutdown must be called on RemoteSandboxBroker before destruction!");
+}
+
+void RemoteSandboxBroker::Shutdown() {
+ MOZ_ASSERT(!mShutdown, "Don't call Shutdown() twice!");
+ mShutdown = true;
+
+ if (!mIPCLaunchThread) {
+ // Can't have launched child process, nothing to shutdown.
+ return;
+ }
+
+ RefPtr<RemoteSandboxBroker> self = this;
+ mIPCLaunchThread->Dispatch(
+ NS_NewRunnableFunction("Remote Sandbox Launch", [self, this]() {
+ // Note: `self` here should be the last reference to this instance.
+ mParent->Shutdown();
+ mIPCLaunchThread = nullptr;
+ }));
+}
+
+Result<Ok, mozilla::ipc::LaunchError> RemoteSandboxBroker::LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA*, void** aProcessHandle) {
+ // Note: we expect to be called on the IPC launch thread from
+ // GeckoChildProcesHost while it's launching a child process. The IPC launch
+ // thread is a TaskQueue. We can't run a synchronous launch here as that
+ // blocks the calling thread while it dispatches a task to the IPC launch
+ // thread to spawn the process. Since our calling thread is the IPC launch
+ // thread, we'd then be deadlocked. So instead we do an async launch, and spin
+ // the event loop until the process launch succeeds.
+
+ // We should be on the IPC launch thread. We're shutdown on the IO thread,
+ // so save a ref to the IPC launch thread here, so we can close the channel
+ // on the IPC launch thread on shutdown.
+ mIPCLaunchThread = GetCurrentSerialEventTarget();
+
+ mParameters.path() = nsDependentString(aPath);
+ mParameters.args() = nsDependentString(aArguments);
+
+ auto toNsString = [](const std::wstring& s) {
+ return nsDependentString(s.c_str());
+ };
+ for (auto itr : aEnvironment) {
+ mParameters.env().AppendElement(
+ EnvVar(toNsString(itr.first), toNsString(itr.second)));
+ }
+
+ mParameters.processType() = uint32_t(aProcessType);
+ mParameters.enableLogging() = aEnableLogging;
+
+ enum Result { Pending, Succeeded, Failed };
+ Result res = Pending;
+ auto resolve = [&](bool ok) {
+ res = Succeeded;
+ return GenericPromise::CreateAndResolve(ok, __func__);
+ };
+
+ auto reject = [&](nsresult) {
+ res = Failed;
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ };
+
+ // We need to wait on the current thread for the process to launch which will
+ // block the running IPC Launch taskqueue. We cannot use
+ // GetCurrentSerialEventTarget() (as this returns the currently running
+ // TaskQueue) to resolve our promise as it will be blocked until we return
+ // from this function.
+ nsCOMPtr<nsISerialEventTarget> target = NS_GetCurrentThread();
+ mParent->Launch(mLaunchArch, mParameters.shareHandles(), target)
+ ->Then(target, __func__, std::move(resolve), std::move(reject));
+
+ // Spin the event loop while the sandbox launcher process launches.
+ SpinEventLoopUntil("RemoteSandboxBroker::LaunchApp"_ns,
+ [&]() { return res != Pending; });
+
+ if (res == Failed) {
+ return Err(mozilla::ipc::LaunchError("RSB::LaunchApp"));
+ }
+
+ uint64_t handle = 0;
+ bool ok = false;
+ bool rv = mParent->SendLaunchApp(std::move(mParameters), &ok, &handle) && ok;
+ mParameters.shareHandles().Clear();
+ if (!rv) {
+ mParent->Shutdown();
+ return Err(mozilla::ipc::LaunchError("RSB::SendLaunchApp"));
+ }
+
+ // Duplicate the handle of the child process that the launcher launched from
+ // the launcher process's space into this process' space.
+ HANDLE ourChildHandle = 0;
+ bool dh = mParent->DuplicateFromLauncher((HANDLE)handle, &ourChildHandle);
+ if (!dh) {
+ mParent->Shutdown();
+ return Err(mozilla::ipc::LaunchError("RSB::DuplicateFromLauncher"));
+ }
+
+ *aProcessHandle = (void**)(ourChildHandle);
+
+ base::ProcessHandle process = *aProcessHandle;
+ SandboxBroker::AddTargetPeer(process);
+
+ return Ok();
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch) {
+ mParameters.sandboxLevel() = uint32_t(aLevel);
+ return true;
+}
+
+bool RemoteSandboxBroker::AllowReadFile(wchar_t const* aFile) {
+ mParameters.allowedReadFiles().AppendElement(nsDependentString(aFile));
+ return true;
+}
+
+void RemoteSandboxBroker::AddHandleToShare(HANDLE aHandle) {
+ mParameters.shareHandles().AppendElement(uint64_t(aHandle));
+}
+
+void RemoteSandboxBroker::SetSecurityLevelForContentProcess(
+ int32_t aSandboxLevel, bool aIsFileProcess) {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForContentProcess not Implemented");
+}
+
+void RemoteSandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForGPUProcess not Implemented");
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForRDDProcess() {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForRDDProcess not Implemented");
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForSocketProcess() {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForSocketProcess not Implemented");
+}
+
+bool RemoteSandboxBroker::SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) {
+ MOZ_CRASH(
+ "RemoteSandboxBroker::SetSecurityLevelForUtilityProcess not Implemented");
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h
new file mode 100644
index 0000000000..bb02470e0e
--- /dev/null
+++ b/security/sandbox/win/src/remotesandboxbroker/remoteSandboxBroker.h
@@ -0,0 +1,75 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef __REMOTE_SANDBOXBROKER_H__
+#define __REMOTE_SANDBOXBROKER_H__
+
+#include "sandboxBroker.h"
+#include "RemoteSandboxBrokerParent.h"
+
+#include "mozilla/Result.h"
+#include "mozilla/ipc/LaunchError.h"
+
+namespace mozilla {
+
+// To make sandboxing an x86 plugin-container process on Windows on ARM64,
+// we launch an x86 child process which in turn launches and sandboxes the x86
+// plugin-container child. This means the sandbox broker (in the remote
+// x86 sandbox launcher process) can be same-arch with the process that it's
+// sandboxing, which means all the sandbox's assumptions about things being
+// same arch still hold.
+class RemoteSandboxBroker : public AbstractSandboxBroker {
+ public:
+ explicit RemoteSandboxBroker(uint32_t aLaunchArch);
+
+ void Shutdown() override;
+
+ // Note: This should be called on the IPC launch thread, and this spins
+ // the event loop. So this means potentially another IPC launch could occur
+ // re-entrantly while calling this.
+ Result<Ok, mozilla::ipc::LaunchError> LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA*,
+ void** aProcessHandle) override;
+
+ // Security levels for different types of processes
+ void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) override;
+ void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) override;
+ bool SetSecurityLevelForRDDProcess() override;
+ bool SetSecurityLevelForSocketProcess() override;
+ bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch = false) override;
+ bool SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) override;
+ bool AllowReadFile(wchar_t const* file) override;
+ void AddHandleToShare(HANDLE aHandle) override;
+
+ bool IsWin32kLockedDown() final { return false; };
+
+ private:
+ virtual ~RemoteSandboxBroker();
+
+ // Parameters that we use to launch the child process.
+ LaunchParameters mParameters;
+
+ RefPtr<RemoteSandboxBrokerParent> mParent;
+
+ // We bind the RemoteSandboxBrokerParent to the IPC launch thread.
+ // As such, we must close its channel on the same thread. So we save
+ // a reference to the IPC launch thread here.
+ nsCOMPtr<nsISerialEventTarget> mIPCLaunchThread;
+
+ // True if we've been shutdown.
+ bool mShutdown = false;
+
+ uint32_t mLaunchArch;
+};
+
+} // namespace mozilla
+
+#endif // __REMOTE_SANDBOXBROKER_H__
diff --git a/security/sandbox/win/src/sandboxbroker/moz.build b/security/sandbox/win/src/sandboxbroker/moz.build
new file mode 100644
index 0000000000..5eaf0de937
--- /dev/null
+++ b/security/sandbox/win/src/sandboxbroker/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ "sandboxBroker.cpp",
+]
+
+EXPORTS += [
+ "sandboxBroker.h",
+]
+
+for var in ("UNICODE", "_UNICODE"):
+ DEFINES[var] = True
+
+LOCAL_INCLUDES += ["/security/sandbox/chromium-shim"]
+LOCAL_INCLUDES += ["/security/sandbox/chromium"]
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
new file mode 100644
index 0000000000..bd154d3c78
--- /dev/null
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -0,0 +1,2029 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#define MOZ_USE_LAUNCHER_ERROR
+
+#include "sandboxBroker.h"
+
+#include <aclapi.h>
+#include <shlobj.h>
+#include <string>
+
+#include "base/win/windows_version.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ImportDir.h"
+#include "mozilla/Logging.h"
+#include "mozilla/NSPRLogModulesParser.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SandboxSettings.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/StaticPrefs_widget.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/WinDllServices.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "mozilla/ipc/LaunchError.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsCOMPtr.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsIProperties.h"
+#include "nsIXULRuntime.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsTHashtable.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/security_level.h"
+#include "WinUtils.h"
+
+namespace mozilla {
+
+constexpr wchar_t kLpacFirefoxInstallFiles[] = L"lpacFirefoxInstallFiles";
+
+sandbox::BrokerServices* sBrokerService = nullptr;
+
+// This is set to true in Initialize when our exe file name has a drive type of
+// DRIVE_REMOTE, so that we can tailor the sandbox policy as some settings break
+// fundamental things when running from a network drive. We default to false in
+// case those checks fail as that gives us the strongest policy.
+bool SandboxBroker::sRunningFromNetworkDrive = false;
+
+// Cached special directories used for adding policy rules.
+static UniquePtr<nsString> sBinDir;
+static UniquePtr<nsString> sProfileDir;
+static UniquePtr<nsString> sLocalAppDataDir;
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+static UniquePtr<nsString> sUserExtensionsDir;
+#endif
+
+static LazyLogModule sSandboxBrokerLog("SandboxBroker");
+
+#define LOG_E(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Error, (__VA_ARGS__))
+#define LOG_W(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Warning, (__VA_ARGS__))
+#define LOG_D(...) MOZ_LOG(sSandboxBrokerLog, LogLevel::Debug, (__VA_ARGS__))
+
+// Used to store whether we have accumulated an error combination for this
+// session.
+static UniquePtr<nsTHashtable<nsCStringHashKey>> sLaunchErrors;
+
+// This helper function is our version of SandboxWin::AddWin32kLockdownPolicy
+// of Chromium, making sure the MITIGATION_WIN32K_DISABLE flag is set before
+// adding the SUBSYS_WIN32K_LOCKDOWN rule which is required by
+// PolicyBase::AddRuleInternal.
+static sandbox::ResultCode AddWin32kLockdownPolicy(
+ sandbox::TargetPolicy* aPolicy, bool aEnableOpm) {
+ // On Windows 7, where Win32k lockdown is not supported, the Chromium
+ // sandbox does something weird that breaks COM instantiation.
+ if (!IsWin8OrLater()) {
+ return sandbox::SBOX_ALL_OK;
+ }
+
+ sandbox::MitigationFlags flags = aPolicy->GetProcessMitigations();
+ MOZ_ASSERT(flags,
+ "Mitigations should be set before AddWin32kLockdownPolicy.");
+ MOZ_ASSERT(!(flags & sandbox::MITIGATION_WIN32K_DISABLE),
+ "Check not enabling twice. Should not happen.");
+
+ flags |= sandbox::MITIGATION_WIN32K_DISABLE;
+ sandbox::ResultCode result = aPolicy->SetProcessMitigations(flags);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ result =
+ aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_WIN32K_LOCKDOWN,
+ aEnableOpm ? sandbox::TargetPolicy::IMPLEMENT_OPM_APIS
+ : sandbox::TargetPolicy::FAKE_USER_GDI_INIT,
+ nullptr);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+ if (aEnableOpm) {
+ aPolicy->SetEnableOPMRedirection();
+ }
+
+ return result;
+}
+
+/* static */
+void SandboxBroker::Initialize(sandbox::BrokerServices* aBrokerServices) {
+ sBrokerService = aBrokerServices;
+
+ sRunningFromNetworkDrive = widget::WinUtils::RunningFromANetworkDrive();
+}
+
+static void CacheDirAndAutoClear(nsIProperties* aDirSvc, const char* aDirKey,
+ UniquePtr<nsString>* cacheVar) {
+ nsCOMPtr<nsIFile> dirToCache;
+ nsresult rv =
+ aDirSvc->Get(aDirKey, NS_GET_IID(nsIFile), getter_AddRefs(dirToCache));
+ if (NS_FAILED(rv)) {
+ // This can only be an NS_WARNING, because it can fail for xpcshell tests.
+ NS_WARNING("Failed to get directory to cache.");
+ LOG_E("Failed to get directory to cache, key: %s.", aDirKey);
+ return;
+ }
+
+ *cacheVar = MakeUnique<nsString>();
+ ClearOnShutdown(cacheVar);
+ MOZ_ALWAYS_SUCCEEDS(dirToCache->GetPath(**cacheVar));
+
+ // Convert network share path to format for sandbox policy.
+ if (Substring(**cacheVar, 0, 2).Equals(u"\\\\"_ns)) {
+ (*cacheVar)->InsertLiteral(u"??\\UNC", 1);
+ }
+}
+
+/* static */
+void SandboxBroker::GeckoDependentInitialize() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool haveXPCOM = XRE_GetProcessType() != GeckoProcessType_RemoteSandboxBroker;
+ if (haveXPCOM) {
+ // Cache directory paths for use in policy rules, because the directory
+ // service must be called on the main thread.
+ nsresult rv;
+ nsCOMPtr<nsIProperties> dirSvc =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false,
+ "Failed to get directory service, cannot cache directories "
+ "for rules.");
+ LOG_E(
+ "Failed to get directory service, cannot cache directories for "
+ "rules.");
+ return;
+ }
+
+ CacheDirAndAutoClear(dirSvc, NS_GRE_DIR, &sBinDir);
+ CacheDirAndAutoClear(dirSvc, NS_APP_USER_PROFILE_50_DIR, &sProfileDir);
+ CacheDirAndAutoClear(dirSvc, NS_WIN_LOCAL_APPDATA_DIR, &sLocalAppDataDir);
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ CacheDirAndAutoClear(dirSvc, XRE_USER_SYS_EXTENSION_DIR,
+ &sUserExtensionsDir);
+#endif
+ }
+
+ // Create sLaunchErrors up front because ClearOnShutdown must be called on the
+ // main thread.
+ sLaunchErrors = MakeUnique<nsTHashtable<nsCStringHashKey>>();
+ ClearOnShutdown(&sLaunchErrors);
+}
+
+SandboxBroker::SandboxBroker() {
+ if (sBrokerService) {
+ scoped_refptr<sandbox::TargetPolicy> policy =
+ sBrokerService->CreatePolicy();
+ mPolicy = policy.get();
+ mPolicy->AddRef();
+ if (sRunningFromNetworkDrive) {
+ mPolicy->SetDoNotUseRestrictingSIDs();
+ }
+ } else {
+ mPolicy = nullptr;
+ }
+}
+
+#define WSTRING(STRING) L"" STRING
+
+static void AddMozLogRulesToPolicy(sandbox::TargetPolicy* aPolicy,
+ const base::EnvironmentMap& aEnvironment) {
+ auto it = aEnvironment.find(ENVIRONMENT_LITERAL("MOZ_LOG_FILE"));
+ if (it == aEnvironment.end()) {
+ it = aEnvironment.find(ENVIRONMENT_LITERAL("NSPR_LOG_FILE"));
+ }
+ if (it == aEnvironment.end()) {
+ return;
+ }
+
+ char const* logFileModules = getenv("MOZ_LOG");
+ if (!logFileModules) {
+ return;
+ }
+
+ // MOZ_LOG files have a standard file extension appended.
+ std::wstring logFileName(it->second);
+ logFileName.append(WSTRING(MOZ_LOG_FILE_EXTENSION));
+
+ // Allow for rotation number if rotate is on in the MOZ_LOG settings.
+ bool rotate = false;
+ NSPRLogModulesParser(
+ logFileModules,
+ [&rotate](const char* aName, LogLevel aLevel, int32_t aValue) {
+ if (strcmp(aName, "rotate") == 0) {
+ // Less or eq zero means to turn rotate off.
+ rotate = aValue > 0;
+ }
+ });
+ if (rotate) {
+ logFileName.append(L".?");
+ }
+
+ // Allow for %PID token in the filename. We don't allow it in the dir path, if
+ // specified, because we have to use a wildcard as we don't know the PID yet.
+ auto pidPos = logFileName.find(WSTRING(MOZ_LOG_PID_TOKEN));
+ auto lastSlash = logFileName.find_last_of(L"/\\");
+ if (pidPos != std::wstring::npos &&
+ (lastSlash == std::wstring::npos || lastSlash < pidPos)) {
+ logFileName.replace(pidPos, strlen(MOZ_LOG_PID_TOKEN), L"*");
+ }
+
+ aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY, logFileName.c_str());
+}
+
+static void AddDeveloperRepoDirToPolicy(sandbox::TargetPolicy* aPolicy) {
+ const wchar_t* developer_repo_dir =
+ _wgetenv(WSTRING("MOZ_DEVELOPER_REPO_DIR"));
+ if (!developer_repo_dir) {
+ return;
+ }
+
+ std::wstring repoPath(developer_repo_dir);
+ std::replace(repoPath.begin(), repoPath.end(), '/', '\\');
+ repoPath.append(WSTRING("\\*"));
+
+ aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ repoPath.c_str());
+}
+
+#undef WSTRING
+
+Result<Ok, mozilla::ipc::LaunchError> SandboxBroker::LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA* aCachedNtdllThunk,
+ void** aProcessHandle) {
+ if (!sBrokerService) {
+ return Err(mozilla::ipc::LaunchError("SB::LA::sBrokerService"));
+ }
+
+ if (!mPolicy) {
+ return Err(mozilla::ipc::LaunchError("SB::LA::mPolicy"));
+ }
+
+ // Set stdout and stderr, to allow inheritance for logging.
+ mPolicy->SetStdoutHandle(::GetStdHandle(STD_OUTPUT_HANDLE));
+ mPolicy->SetStderrHandle(::GetStdHandle(STD_ERROR_HANDLE));
+
+ // If logging enabled, set up the policy.
+ if (aEnableLogging) {
+ ApplyLoggingPolicy();
+ }
+
+#if defined(DEBUG)
+ // Allow write access to TEMP directory in debug builds for logging purposes.
+ // The path from GetTempPathW can have a length up to MAX_PATH + 1, including
+ // the null, so we need MAX_PATH + 2, so we can add an * to the end.
+ wchar_t tempPath[MAX_PATH + 2];
+ uint32_t pathLen = ::GetTempPathW(MAX_PATH + 1, tempPath);
+ if (pathLen > 0) {
+ // GetTempPath path ends with \ and returns the length without the null.
+ tempPath[pathLen] = L'*';
+ tempPath[pathLen + 1] = L'\0';
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY, tempPath);
+ }
+#endif
+
+ // Enable the child process to write log files when setup
+ AddMozLogRulesToPolicy(mPolicy, aEnvironment);
+
+ if (!mozilla::IsPackagedBuild()) {
+ AddDeveloperRepoDirToPolicy(mPolicy);
+ }
+
+ // Create the sandboxed process
+ PROCESS_INFORMATION targetInfo = {0};
+ sandbox::ResultCode result;
+ sandbox::ResultCode last_warning = sandbox::SBOX_ALL_OK;
+ DWORD last_error = ERROR_SUCCESS;
+ result = sBrokerService->SpawnTarget(aPath, aArguments, aEnvironment, mPolicy,
+ &last_warning, &last_error, &targetInfo);
+ if (sandbox::SBOX_ALL_OK != result) {
+ nsAutoCString key;
+ key.AppendASCII(XRE_GeckoProcessTypeToString(aProcessType));
+ key.AppendLiteral("/0x");
+ key.AppendInt(static_cast<uint32_t>(last_error), 16);
+
+ // Only accumulate for each combination once per session.
+ if (sLaunchErrors) {
+ if (!sLaunchErrors->Contains(key)) {
+ Telemetry::Accumulate(Telemetry::SANDBOX_FAILED_LAUNCH_KEYED, key,
+ result);
+ sLaunchErrors->PutEntry(key);
+ }
+ } else {
+ // If sLaunchErrors not created yet then always accumulate.
+ Telemetry::Accumulate(Telemetry::SANDBOX_FAILED_LAUNCH_KEYED, key,
+ result);
+ }
+
+ LOG_E(
+ "Failed (ResultCode %d) to SpawnTarget with last_error=%lu, "
+ "last_warning=%d",
+ result, last_error, last_warning);
+
+ return Err(mozilla::ipc::LaunchError("SB::LA::SpawnTarget", last_error));
+ } else if (sandbox::SBOX_ALL_OK != last_warning) {
+ // If there was a warning (but the result was still ok), log it and proceed.
+ LOG_W("Warning on SpawnTarget with last_error=%lu, last_warning=%d",
+ last_error, last_warning);
+ }
+
+#ifdef MOZ_THUNDERBIRD
+ // In Thunderbird, mInitDllBlocklistOOP is null, so InitDllBlocklistOOP would
+ // hit MOZ_RELEASE_ASSERT.
+ constexpr bool isThunderbird = true;
+#else
+ constexpr bool isThunderbird = false;
+#endif
+
+ if (!isThunderbird &&
+ XRE_GetChildProcBinPathType(aProcessType) == BinPathType::Self) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ LauncherVoidResultWithLineInfo blocklistInitOk =
+ dllSvc->InitDllBlocklistOOP(aPath, targetInfo.hProcess,
+ aCachedNtdllThunk, aProcessType);
+ if (blocklistInitOk.isErr()) {
+ dllSvc->HandleLauncherError(blocklistInitOk.unwrapErr(),
+ XRE_GeckoProcessTypeToString(aProcessType));
+ LOG_E("InitDllBlocklistOOP failed at %s:%d with HRESULT 0x%08lX",
+ blocklistInitOk.unwrapErr().mFile,
+ blocklistInitOk.unwrapErr().mLine,
+ blocklistInitOk.unwrapErr().mError.AsHResult());
+ TerminateProcess(targetInfo.hProcess, 1);
+ CloseHandle(targetInfo.hThread);
+ CloseHandle(targetInfo.hProcess);
+ return Err(mozilla::ipc::LaunchError(
+ "InitDllBlocklistOOP",
+ blocklistInitOk.unwrapErr().mError.AsHResult()));
+ }
+ } else {
+ // Load the child executable as a datafile so that we can examine its
+ // headers without doing a full load with dependencies and such.
+ nsModuleHandle moduleHandle(
+ ::LoadLibraryExW(aPath, nullptr, LOAD_LIBRARY_AS_DATAFILE));
+ if (moduleHandle) {
+ nt::CrossExecTransferManager transferMgr(targetInfo.hProcess,
+ moduleHandle);
+ if (!!transferMgr) {
+ LauncherVoidResult importsRestored =
+ RestoreImportDirectory(aPath, transferMgr);
+ if (importsRestored.isErr()) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->HandleLauncherError(
+ importsRestored.unwrapErr(),
+ XRE_GeckoProcessTypeToString(aProcessType));
+ LOG_E("Failed to restore import directory with HRESULT 0x%08lX",
+ importsRestored.unwrapErr().mError.AsHResult());
+ TerminateProcess(targetInfo.hProcess, 1);
+ CloseHandle(targetInfo.hThread);
+ CloseHandle(targetInfo.hProcess);
+ return Err(mozilla::ipc::LaunchError(
+ "RestoreImportDirectory",
+ importsRestored.unwrapErr().mError.AsHResult()));
+ }
+ }
+ }
+ }
+
+ // The sandboxed process is started in a suspended state, resume it now that
+ // we've set things up.
+ ResumeThread(targetInfo.hThread);
+ CloseHandle(targetInfo.hThread);
+
+ // Return the process handle to the caller
+ *aProcessHandle = targetInfo.hProcess;
+
+ return Ok();
+}
+
+static void AddCachedDirRule(sandbox::TargetPolicy* aPolicy,
+ sandbox::TargetPolicy::Semantics aAccess,
+ const UniquePtr<nsString>& aBaseDir,
+ const nsLiteralString& aRelativePath) {
+ if (!aBaseDir) {
+ // This can only be an NS_WARNING, because it can null for xpcshell tests.
+ NS_WARNING("Tried to add rule with null base dir.");
+ LOG_E("Tried to add rule with null base dir. Relative path: %S, Access: %d",
+ static_cast<const wchar_t*>(aRelativePath.get()), aAccess);
+ return;
+ }
+
+ nsAutoString rulePath(*aBaseDir);
+ rulePath.Append(aRelativePath);
+
+ sandbox::ResultCode result = aPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_FILES, aAccess, rulePath.get());
+ if (sandbox::SBOX_ALL_OK != result) {
+ NS_ERROR("Failed to add file policy rule.");
+ LOG_E("Failed (ResultCode %d) to add %d access to: %S", result, aAccess,
+ static_cast<const wchar_t*>(rulePath.get()));
+ }
+}
+
+// This function caches and returns an array of NT paths of the executable's
+// dependent modules.
+// If this returns Nothing(), it means the retrieval of the modules failed
+// (e.g. when the launcher process is disabled), so the process should not
+// enable pre-spawn CIG.
+static const Maybe<Vector<const wchar_t*>>& GetPrespawnCigExceptionModules() {
+ // We enable pre-spawn CIG only in Nightly for now
+ // because it caused a compat issue (bug 1682304 and 1704373).
+#if defined(NIGHTLY_BUILD)
+ // The shared section contains a list of dependent modules as a
+ // null-delimited string. We convert it to a string vector and
+ // cache it to avoid converting the same data every time.
+ static Maybe<Vector<const wchar_t*>> sDependentModules =
+ []() -> Maybe<Vector<const wchar_t*>> {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ auto sharedSection = dllSvc->GetSharedSection();
+ if (!sharedSection) {
+ return Nothing();
+ }
+
+ Span<const wchar_t> dependentModules = sharedSection->GetDependentModules();
+ if (dependentModules.IsEmpty()) {
+ return Nothing();
+ }
+
+ // Convert a null-delimited string set to a string vector.
+ Vector<const wchar_t*> paths;
+ for (const wchar_t* p = dependentModules.data();
+ (p - dependentModules.data() <
+ static_cast<long long>(dependentModules.size()) &&
+ *p);) {
+ Unused << paths.append(p);
+ while (*p) {
+ ++p;
+ }
+ ++p;
+ }
+
+ return Some(std::move(paths));
+ }();
+
+ return sDependentModules;
+#else
+ static const Maybe<Vector<const wchar_t*>> sNothing = Nothing();
+ return sNothing;
+#endif
+}
+
+static sandbox::ResultCode AllowProxyLoadFromBinDir(
+ sandbox::TargetPolicy* aPolicy) {
+ // Allow modules in the directory containing the executable such as
+ // mozglue.dll, nss3.dll, etc.
+ static UniquePtr<nsString> sInstallDir;
+ if (!sInstallDir) {
+ // Since this function can be called before sBinDir is initialized,
+ // we cache the install path by ourselves.
+ UniquePtr<wchar_t[]> appDirStr;
+ if (GetInstallDirectory(appDirStr)) {
+ sInstallDir = MakeUnique<nsString>(appDirStr.get());
+ sInstallDir->Append(u"\\*");
+
+ auto setClearOnShutdown = [ptr = &sInstallDir]() -> void {
+ ClearOnShutdown(ptr);
+ };
+ if (NS_IsMainThread()) {
+ setClearOnShutdown();
+ } else {
+ SchedulerGroup::Dispatch(
+ TaskCategory::Other,
+ NS_NewRunnableFunction("InitSignedPolicyRulesToBypassCig",
+ std::move(setClearOnShutdown)));
+ }
+ }
+
+ if (!sInstallDir) {
+ return sandbox::SBOX_ERROR_GENERIC;
+ }
+ }
+ return aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SIGNED_BINARY,
+ sandbox::TargetPolicy::SIGNED_ALLOW_LOAD,
+ sInstallDir->get());
+}
+
+static sandbox::ResultCode AddCigToPolicy(
+ sandbox::TargetPolicy* aPolicy, bool aAlwaysProxyBinDirLoading = false) {
+ const Maybe<Vector<const wchar_t*>>& exceptionModules =
+ GetPrespawnCigExceptionModules();
+ if (exceptionModules.isNothing()) {
+ sandbox::MitigationFlags delayedMitigations =
+ aPolicy->GetDelayedProcessMitigations();
+ MOZ_ASSERT(delayedMitigations,
+ "Delayed mitigations should be set before AddCigToPolicy.");
+ MOZ_ASSERT(!(delayedMitigations & sandbox::MITIGATION_FORCE_MS_SIGNED_BINS),
+ "AddCigToPolicy should not be called twice.");
+
+ delayedMitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS;
+ sandbox::ResultCode result =
+ aPolicy->SetDelayedProcessMitigations(delayedMitigations);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ if (aAlwaysProxyBinDirLoading) {
+ result = AllowProxyLoadFromBinDir(aPolicy);
+ }
+ return result;
+ }
+
+ sandbox::MitigationFlags mitigations = aPolicy->GetProcessMitigations();
+ MOZ_ASSERT(mitigations, "Mitigations should be set before AddCigToPolicy.");
+ MOZ_ASSERT(!(mitigations & sandbox::MITIGATION_FORCE_MS_SIGNED_BINS),
+ "AddCigToPolicy should not be called twice.");
+
+ mitigations |= sandbox::MITIGATION_FORCE_MS_SIGNED_BINS;
+ sandbox::ResultCode result = aPolicy->SetProcessMitigations(mitigations);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ result = AllowProxyLoadFromBinDir(aPolicy);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ for (const wchar_t* path : exceptionModules.ref()) {
+ result = aPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SIGNED_BINARY,
+ sandbox::TargetPolicy::SIGNED_ALLOW_LOAD, path);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+ }
+
+ return sandbox::SBOX_ALL_OK;
+}
+
+// Checks whether we can use a job object as part of the sandbox.
+static bool CanUseJob() {
+ // Windows 8 and later allows nested jobs, no need for further checks.
+ if (IsWin8OrLater()) {
+ return true;
+ }
+
+ BOOL inJob = true;
+ // If we can't determine if we are in a job then assume we can use one.
+ if (!::IsProcessInJob(::GetCurrentProcess(), nullptr, &inJob)) {
+ return true;
+ }
+
+ // If there is no job then we are fine to use one.
+ if (!inJob) {
+ return true;
+ }
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = {};
+ // If we can't get the job object flags then again assume we can use a job.
+ if (!::QueryInformationJobObject(nullptr, JobObjectExtendedLimitInformation,
+ &job_info, sizeof(job_info), nullptr)) {
+ return true;
+ }
+
+ // If we can break away from the current job then we are free to set our own.
+ if (job_info.BasicLimitInformation.LimitFlags &
+ JOB_OBJECT_LIMIT_BREAKAWAY_OK) {
+ return true;
+ }
+
+ // Chromium added a command line flag to allow no job to be used, which was
+ // originally supposed to only be used for remote sessions. If you use runas
+ // to start Firefox then this also uses a separate job and we would fail to
+ // start on Windows 7. An unknown number of people use (or used to use) runas
+ // with Firefox for some security benefits (see bug 1228880). This is now a
+ // counterproductive technique, but allowing both the remote and local case
+ // for now and adding telemetry to see if we can restrict this to just remote.
+ nsAutoString localRemote(::GetSystemMetrics(SM_REMOTESESSION) ? u"remote"
+ : u"local");
+ Telemetry::ScalarSet(Telemetry::ScalarID::SANDBOX_NO_JOB, localRemote, true);
+
+ // Allow running without the job object in this case. This slightly reduces
+ // the ability of the sandbox to protect its children from spawning new
+ // processes or preventing them from shutting down Windows or accessing the
+ // clipboard.
+ return false;
+}
+
+// Returns the most strict dynamic code mitigation flag that is compatible with
+// system libraries MSAudDecMFT.dll and msmpeg2vdec.dll. This depends on the
+// Windows version and the architecture. See bug 1783223 comment 27.
+//
+// Use the result with SetDelayedProcessMitigations. Using non-delayed ACG
+// results in incompatibility with third-party antivirus software, the Windows
+// internal Shim Engine mechanism, parts of our own DLL blocklist code, and
+// AddressSanitizer initialization code. See bug 1783223.
+static sandbox::MitigationFlags DynamicCodeFlagForSystemMediaLibraries() {
+ static auto dynamicCodeFlag = []() {
+#ifdef _M_X64
+ if (IsWin10CreatorsUpdateOrLater()) {
+ return sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+ }
+#endif // _M_X64
+
+ if (IsWin10AnniversaryUpdateOrLater()) {
+ return sandbox::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT;
+ }
+
+ return sandbox::MitigationFlags{};
+ }();
+ return dynamicCodeFlag;
+}
+
+static sandbox::ResultCode SetJobLevel(sandbox::TargetPolicy* aPolicy,
+ sandbox::JobLevel aJobLevel,
+ uint32_t aUiExceptions) {
+ static bool sCanUseJob = CanUseJob();
+ if (sCanUseJob) {
+ return aPolicy->SetJobLevel(aJobLevel, aUiExceptions);
+ }
+
+ return aPolicy->SetJobLevel(sandbox::JOB_NONE, 0);
+}
+
+static void HexEncode(const Span<const uint8_t>& aBytes, nsACString& aEncoded) {
+ static const char kHexChars[] = "0123456789abcdef";
+
+ // Each input byte creates two output hex characters.
+ char* encodedPtr;
+ aEncoded.GetMutableData(&encodedPtr, aBytes.size() * 2);
+
+ for (auto byte : aBytes) {
+ *(encodedPtr++) = kHexChars[byte >> 4];
+ *(encodedPtr++) = kHexChars[byte & 0xf];
+ }
+}
+
+// This is left as a void because we might fail to set the permission for some
+// reason and yet the LPAC permission is already granted. So returning success
+// or failure isn't really that useful.
+static void EnsureLpacPermsissionsOnBinDir() {
+ BYTE sidBytes[SECURITY_MAX_SID_SIZE];
+ PSID lpacFirefoxInstallFilesSid = static_cast<PSID>(sidBytes);
+ if (!sBrokerService->DeriveCapabilitySidFromName(kLpacFirefoxInstallFiles,
+ lpacFirefoxInstallFilesSid,
+ sizeof(sidBytes))) {
+ LOG_E("Failed to derive Firefox install files capability SID.");
+ return;
+ }
+
+ HANDLE hBinDir =
+ ::CreateFileW(sBinDir->get(), WRITE_DAC | READ_CONTROL, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (hBinDir == INVALID_HANDLE_VALUE) {
+ LOG_W("Unable to get binary directory handle.");
+ return;
+ }
+
+ UniquePtr<HANDLE, CloseHandleDeleter> autoHandleCloser(hBinDir);
+ PACL pBinDirAcl = nullptr;
+ PSECURITY_DESCRIPTOR pSD = nullptr;
+ DWORD result =
+ ::GetSecurityInfo(hBinDir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
+ nullptr, nullptr, &pBinDirAcl, nullptr, &pSD);
+ if (result != ERROR_SUCCESS) {
+ LOG_E("Failed to get DACL for binary directory.");
+ return;
+ }
+
+ UniquePtr<VOID, LocalFreeDeleter> autoFreeSecDesc(pSD);
+ if (!pBinDirAcl) {
+ LOG_E("DACL for binary directory was null.");
+ return;
+ }
+
+ for (DWORD i = 0; i < pBinDirAcl->AceCount; ++i) {
+ VOID* pAce = nullptr;
+ if (!::GetAce(pBinDirAcl, i, &pAce) ||
+ static_cast<PACE_HEADER>(pAce)->AceType != ACCESS_ALLOWED_ACE_TYPE) {
+ continue;
+ }
+
+ auto* pAllowedAce = static_cast<ACCESS_ALLOWED_ACE*>(pAce);
+ if ((pAllowedAce->Mask & (GENERIC_READ | GENERIC_EXECUTE)) !=
+ (GENERIC_READ | GENERIC_EXECUTE)) {
+ continue;
+ }
+
+ PSID aceSID = reinterpret_cast<PSID>(&(pAllowedAce->SidStart));
+ if (::EqualSid(aceSID, lpacFirefoxInstallFilesSid)) {
+ LOG_D("Firefox install files permission found on binary directory.");
+ return;
+ }
+ }
+
+ EXPLICIT_ACCESS_W newAccess = {0};
+ newAccess.grfAccessMode = GRANT_ACCESS;
+ newAccess.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;
+ newAccess.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
+ ::BuildTrusteeWithSidW(&newAccess.Trustee, lpacFirefoxInstallFilesSid);
+ PACL newDacl = nullptr;
+ if (ERROR_SUCCESS !=
+ ::SetEntriesInAclW(1, &newAccess, pBinDirAcl, &newDacl)) {
+ LOG_E("Failed to create new DACL with Firefox install files SID.");
+ return;
+ }
+
+ UniquePtr<ACL, LocalFreeDeleter> autoFreeAcl(newDacl);
+ if (ERROR_SUCCESS != ::SetSecurityInfo(hBinDir, SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION, nullptr,
+ nullptr, newDacl, nullptr)) {
+ LOG_E("Failed to set new DACL on binary directory.");
+ }
+
+ LOG_D("Firefox install files permission granted on binary directory.");
+}
+
+static bool IsLowPrivilegedAppContainerSupported() {
+ // Chromium doesn't support adding an LPAC before this version due to
+ // incompatibility with some process mitigations.
+ return IsWin10Sep2018UpdateOrLater();
+}
+
+// AddAndConfigureAppContainerProfile deliberately fails if it is called on an
+// unsupported version. This is because for some process types the LPAC is
+// required to provide a sufficiently strong sandbox. Processes where the use of
+// an LPAC is an optional extra should use IsLowPrivilegedAppContainerSupported
+// to check support first.
+static sandbox::ResultCode AddAndConfigureAppContainerProfile(
+ sandbox::TargetPolicy* aPolicy, const nsAString& aPackagePrefix,
+ const nsTArray<sandbox::WellKnownCapabilities>& aWellKnownCapabilites,
+ const nsTArray<const wchar_t*>& aNamedCapabilites) {
+ // CreateAppContainerProfile requires that the profile name is at most 64
+ // characters but 50 on WCOS systems. The size of sha1 is a constant 40,
+ // so validate that the base names are sufficiently short that the total
+ // length is valid on all systems.
+ MOZ_ASSERT(aPackagePrefix.Length() <= 10U,
+ "AppContainer Package prefix too long.");
+
+ if (!IsLowPrivilegedAppContainerSupported()) {
+ return sandbox::SBOX_ERROR_UNSUPPORTED;
+ }
+
+ static nsAutoString uniquePackageStr = []() {
+ // userenv.dll may not have been loaded and some of the chromium sandbox
+ // AppContainer code assumes that it is. Done here to load once.
+ ::LoadLibraryW(L"userenv.dll");
+
+ // Done during the package string initialization so we only do it once.
+ EnsureLpacPermsissionsOnBinDir();
+
+ // This mirrors Edge's use of the exe path for the SHA1 hash to give a
+ // machine unique name per install.
+ nsAutoString ret;
+ char exePathBuf[MAX_PATH];
+ DWORD pathSize = ::GetModuleFileNameA(nullptr, exePathBuf, MAX_PATH);
+ if (!pathSize) {
+ return ret;
+ }
+
+ SHA1Sum sha1Sum;
+ SHA1Sum::Hash sha1Hash;
+ sha1Sum.update(exePathBuf, pathSize);
+ sha1Sum.finish(sha1Hash);
+
+ nsAutoCString hexEncoded;
+ HexEncode(sha1Hash, hexEncoded);
+ ret = NS_ConvertUTF8toUTF16(hexEncoded);
+ return ret;
+ }();
+
+ if (uniquePackageStr.IsEmpty()) {
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE;
+ }
+
+ // The bool parameter is called create_profile, but in fact it tries to create
+ // and then opens if it already exists. So always passing true is fine.
+ bool createOrOpenProfile = true;
+ nsAutoString packageName = aPackagePrefix + uniquePackageStr;
+ sandbox::ResultCode result =
+ aPolicy->AddAppContainerProfile(packageName.get(), createOrOpenProfile);
+ if (result != sandbox::SBOX_ALL_OK) {
+ return result;
+ }
+
+ // This looks odd, but unfortunately holding a scoped_refptr and
+ // dereferencing has DCHECKs that cause a linking problem.
+ sandbox::AppContainerProfile* profile =
+ aPolicy->GetAppContainerProfile().get();
+ profile->SetEnableLowPrivilegeAppContainer(true);
+
+ for (auto wkCap : aWellKnownCapabilites) {
+ if (!profile->AddCapability(wkCap)) {
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
+ }
+ }
+
+ for (auto namedCap : aNamedCapabilites) {
+ if (!profile->AddCapability(namedCap)) {
+ return sandbox::SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY;
+ }
+ }
+
+ return sandbox::SBOX_ALL_OK;
+}
+
+void SandboxBroker::SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) {
+ MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
+
+ sandbox::JobLevel jobLevel;
+ sandbox::TokenLevel accessTokenLevel;
+ sandbox::IntegrityLevel initialIntegrityLevel;
+ sandbox::IntegrityLevel delayedIntegrityLevel;
+
+ // The setting of these levels is pretty arbitrary, but they are a useful (if
+ // crude) tool while we are tightening the policy. Gaps are left to try and
+ // avoid changing their meaning.
+ MOZ_RELEASE_ASSERT(aSandboxLevel >= 1,
+ "Should not be called with aSandboxLevel < 1");
+ if (aSandboxLevel >= 20) {
+ jobLevel = sandbox::JOB_LOCKDOWN;
+ accessTokenLevel = sandbox::USER_LOCKDOWN;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_UNTRUSTED;
+ } else if (aSandboxLevel >= 4) {
+ jobLevel = sandbox::JOB_LOCKDOWN;
+ accessTokenLevel = sandbox::USER_LIMITED;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else if (aSandboxLevel >= 3) {
+ jobLevel = sandbox::JOB_RESTRICTED;
+ accessTokenLevel = sandbox::USER_LIMITED;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else if (aSandboxLevel == 2) {
+ jobLevel = sandbox::JOB_INTERACTIVE;
+ accessTokenLevel = sandbox::USER_INTERACTIVE;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else {
+ MOZ_ASSERT(aSandboxLevel == 1);
+
+ jobLevel = sandbox::JOB_NONE;
+ accessTokenLevel = sandbox::USER_NON_ADMIN;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ }
+
+ // If the process will handle file: URLs, don't allow settings that
+ // block reads.
+ if (aIsFileProcess) {
+ if (accessTokenLevel < sandbox::USER_NON_ADMIN) {
+ accessTokenLevel = sandbox::USER_NON_ADMIN;
+ }
+ if (delayedIntegrityLevel > sandbox::INTEGRITY_LEVEL_LOW) {
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ }
+ }
+
+#if defined(DEBUG)
+ // This is required for a MOZ_ASSERT check in WindowsMessageLoop.cpp
+ // WinEventHook, see bug 1366694 for details.
+ DWORD uiExceptions = JOB_OBJECT_UILIMIT_HANDLES;
+#else
+ DWORD uiExceptions = 0;
+#endif
+ sandbox::ResultCode result = SetJobLevel(mPolicy, jobLevel, uiExceptions);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Setting job level failed, have you set memory limit when "
+ "jobLevel == JOB_NONE?");
+
+ // If the delayed access token is not restricted we don't want the initial one
+ // to be either, because it can interfere with running from a network drive.
+ sandbox::TokenLevel initialAccessTokenLevel =
+ (accessTokenLevel == sandbox::USER_UNPROTECTED ||
+ accessTokenLevel == sandbox::USER_NON_ADMIN)
+ ? sandbox::USER_UNPROTECTED
+ : sandbox::USER_RESTRICTED_SAME_ACCESS;
+
+ result = mPolicy->SetTokenLevel(initialAccessTokenLevel, accessTokenLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Lockdown level cannot be USER_UNPROTECTED or USER_LAST "
+ "if initial level was USER_RESTRICTED_SAME_ACCESS");
+
+ result = mPolicy->SetIntegrityLevel(initialIntegrityLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "SetIntegrityLevel should never fail, what happened?");
+ result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel);
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "SetDelayedIntegrityLevel should never fail, what happened?");
+
+ if (aSandboxLevel > 5) {
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+ }
+
+ if (aSandboxLevel > 4) {
+ // Alternate winstation breaks native theming.
+ bool useAlternateWinstation =
+ StaticPrefs::widget_non_native_theme_enabled();
+ result = mPolicy->SetAlternateDesktop(useAlternateWinstation);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+ }
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+ sandbox::MITIGATION_DEP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+
+#if defined(_M_ARM64)
+ // Disable CFG on older versions of ARM64 Windows to avoid a crash in COM.
+ if (!IsWin10Sep2018UpdateOrLater()) {
+ mitigations |= sandbox::MITIGATION_CONTROL_FLOW_GUARD_DISABLE;
+ }
+#endif
+
+ if (aSandboxLevel > 3) {
+ // If we're running from a network drive then we can't block loading from
+ // remote locations. Strangely using MITIGATION_IMAGE_LOAD_NO_LOW_LABEL in
+ // this situation also means the process fails to start (bug 1423296).
+ if (!sRunningFromNetworkDrive) {
+ mitigations |= sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE |
+ sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL;
+ }
+ }
+
+ if (StaticPrefs::security_sandbox_content_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetProcessMitigations.");
+
+ nsIXULRuntime::ContentWin32kLockdownState win32kLockdownState =
+ GetContentWin32kLockdownState();
+
+ LOG_W("Win32k Lockdown State: '%s'",
+ ContentWin32kLockdownStateToString(win32kLockdownState));
+
+ if (GetContentWin32kLockdownEnabled()) {
+ result = AddWin32kLockdownPolicy(mPolicy, false);
+ MOZ_RELEASE_ASSERT(result == sandbox::SBOX_ALL_OK,
+ "Failed to add the win32k lockdown policy");
+ }
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // We still have edge cases where the child at low integrity can't read some
+ // files, so add a rule to allow read access to everything when required.
+ if (aSandboxLevel == 1 || aIsFileProcess) {
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY, L"*");
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, "
+ "what happened?");
+ } else {
+ // Add rule to allow access to user specific fonts.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sLocalAppDataDir, u"\\Microsoft\\Windows\\Fonts\\*"_ns);
+
+ // Add rule to allow read access to installation directory.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sBinDir, u"\\*"_ns);
+
+ // Add rule to allow read access to the chrome directory within profile.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sProfileDir, u"\\chrome\\*"_ns);
+
+ // Add rule to allow read access to the extensions directory within profile.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sProfileDir, u"\\extensions\\*"_ns);
+
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ // Add rule to allow read access to the per-user extensions directory.
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ sUserExtensionsDir, u"\\*"_ns);
+#endif
+ }
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The content process needs to be able to duplicate named pipes back to the
+ // broker and other child processes, which are File type handles.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"File");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"File");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The content process needs to be able to duplicate shared memory handles,
+ // which are Section handles, to the broker process and other child processes.
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The content process needs to be able to duplicate semaphore handles,
+ // to the broker process and other child processes.
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Semaphore");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Semaphore");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Allow content processes to use complex line breaking brokering.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_LINE_BREAK,
+ sandbox::TargetPolicy::LINE_BREAK_ALLOW, nullptr);
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ if (aSandboxLevel >= 20) {
+ // Content process still needs to be able to read fonts.
+ wchar_t* fontsPath;
+ if (SUCCEEDED(
+ ::SHGetKnownFolderPath(FOLDERID_Fonts, 0, nullptr, &fontsPath))) {
+ std::wstring fontsStr = fontsPath;
+ ::CoTaskMemFree(fontsPath);
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ fontsStr.c_str());
+ if (sandbox::SBOX_ALL_OK != result) {
+ NS_ERROR("Failed to add fonts dir read access policy rule.");
+ LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
+ fontsStr.c_str());
+ }
+
+ fontsStr += L"\\*";
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ fontsStr.c_str());
+ if (sandbox::SBOX_ALL_OK != result) {
+ NS_ERROR("Failed to add fonts read access policy rule.");
+ LOG_E("Failed (ResultCode %d) to add read access to: %S", result,
+ fontsStr.c_str());
+ }
+ }
+
+ // We still currently create IPC named pipes in the content process.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
+ sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\chrome.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail.");
+ }
+}
+
+void SandboxBroker::SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) {
+ MOZ_RELEASE_ASSERT(mPolicy, "mPolicy must be set before this call.");
+
+ sandbox::JobLevel jobLevel;
+ sandbox::TokenLevel accessTokenLevel;
+ sandbox::IntegrityLevel initialIntegrityLevel;
+ sandbox::IntegrityLevel delayedIntegrityLevel;
+
+ // The setting of these levels is pretty arbitrary, but they are a useful (if
+ // crude) tool while we are tightening the policy. Gaps are left to try and
+ // avoid changing their meaning.
+ if (aSandboxLevel >= 2) {
+ jobLevel = sandbox::JOB_NONE;
+ accessTokenLevel = sandbox::USER_LIMITED;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ } else {
+ MOZ_RELEASE_ASSERT(aSandboxLevel >= 1,
+ "Should not be called with aSandboxLevel < 1");
+ jobLevel = sandbox::JOB_NONE;
+ accessTokenLevel = sandbox::USER_NON_ADMIN;
+ initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+ }
+
+ sandbox::ResultCode result =
+ SetJobLevel(mPolicy, jobLevel, 0 /* ui_exceptions */);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Setting job level failed, have you set memory limit when "
+ "jobLevel == JOB_NONE?");
+
+ // If the delayed access token is not restricted we don't want the initial one
+ // to be either, because it can interfere with running from a network drive.
+ sandbox::TokenLevel initialAccessTokenLevel =
+ (accessTokenLevel == sandbox::USER_UNPROTECTED ||
+ accessTokenLevel == sandbox::USER_NON_ADMIN)
+ ? sandbox::USER_UNPROTECTED
+ : sandbox::USER_RESTRICTED_SAME_ACCESS;
+
+ result = mPolicy->SetTokenLevel(initialAccessTokenLevel, accessTokenLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Lockdown level cannot be USER_UNPROTECTED or USER_LAST "
+ "if initial level was USER_RESTRICTED_SAME_ACCESS");
+
+ result = mPolicy->SetIntegrityLevel(initialIntegrityLevel);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "SetIntegrityLevel should never fail, what happened?");
+ result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel);
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "SetDelayedIntegrityLevel should never fail, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+ sandbox::MITIGATION_DEP;
+
+ if (StaticPrefs::security_sandbox_gpu_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetProcessMitigations.");
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ MOZ_RELEASE_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The GPU process needs to write to a shader cache for performance reasons
+ if (sProfileDir) {
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_DIR_ANY,
+ sProfileDir, u"\\shader-cache"_ns);
+
+ AddCachedDirRule(mPolicy, sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ sProfileDir, u"\\shader-cache\\*"_ns);
+ }
+
+ // The process needs to be able to duplicate shared memory handles,
+ // which are Section handles, to the broker process and other child processes.
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Section");
+ MOZ_RELEASE_ASSERT(
+ sandbox::SBOX_ALL_OK == result,
+ "With these static arguments AddRule should never fail, what happened?");
+}
+
+#define SANDBOX_ENSURE_SUCCESS(result, message) \
+ do { \
+ MOZ_ASSERT(sandbox::SBOX_ALL_OK == result, message); \
+ if (sandbox::SBOX_ALL_OK != result) return false; \
+ } while (0)
+
+bool SandboxBroker::SetSecurityLevelForRDDProcess() {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ SetJobLevel(mPolicy, sandbox::JOB_LOCKDOWN, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LIMITED);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetAlternateDesktop(true);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+
+ result = mPolicy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+
+ result = mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetDelayedIntegrityLevel should never fail with "
+ "these arguments, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+
+ if (StaticPrefs::security_sandbox_rdd_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ if (StaticPrefs::security_sandbox_rdd_acg_enabled()) {
+ // The RDD process depends on msmpeg2vdec.dll.
+ mitigations |= DynamicCodeFlagForSystemMediaLibraries();
+ }
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ result = AddCigToPolicy(mPolicy);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to initialize signed policy rules.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The process needs to be able to duplicate shared memory handles,
+ // which are Section handles, to the content processes.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_ANY, L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // This section is needed to avoid an assert during crash reporting code
+ // when running mochitests. The assertion is here:
+ // toolkit/crashreporter/nsExceptionHandler.cpp:2041
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+
+bool SandboxBroker::SetSecurityLevelForSocketProcess() {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ SetJobLevel(mPolicy, sandbox::JOB_LOCKDOWN, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+ sandbox::USER_LIMITED);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetAlternateDesktop(true);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+
+ result = mPolicy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+
+ result =
+ mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetDelayedIntegrityLevel should never fail with "
+ "these arguments, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+
+ if (StaticPrefs::security_sandbox_socket_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ if (StaticPrefs::security_sandbox_socket_win32k_disable()) {
+ result = AddWin32kLockdownPolicy(mPolicy, false);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy");
+ }
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER |
+ sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ result = AddCigToPolicy(mPolicy);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to initialize signed policy rules.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // This section is needed to avoid an assert during crash reporting code
+ // when running mochitests. The assertion is here:
+ // toolkit/crashreporter/nsExceptionHandler.cpp:2041
+ result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+
+// A strict base sandbox for utility sandboxes to adapt.
+struct UtilitySandboxProps {
+ sandbox::JobLevel mJobLevel = sandbox::JOB_LOCKDOWN;
+
+ sandbox::TokenLevel mInitialTokenLevel = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ sandbox::TokenLevel mDelayedTokenLevel = sandbox::USER_LOCKDOWN;
+
+ sandbox::IntegrityLevel mInitialIntegrityLevel = // before lockdown
+ sandbox::INTEGRITY_LEVEL_LOW;
+ sandbox::IntegrityLevel mDelayedIntegrityLevel = // after lockdown
+ sandbox::INTEGRITY_LEVEL_UNTRUSTED;
+
+ bool mUseAlternateWindowStation = true;
+ bool mUseAlternateDesktop = true;
+ bool mLockdownDefaultDacl = true;
+ bool mAddRestrictingRandomSid = true;
+ bool mUseWin32kLockdown = true;
+ bool mUseCig = true;
+
+ sandbox::MitigationFlags mInitialMitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32 |
+ sandbox::MITIGATION_CET_COMPAT_MODE;
+
+ sandbox::MitigationFlags mDelayedMitigations =
+ sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER |
+ sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+
+ // Low Privileged Application Container settings;
+ nsString mPackagePrefix;
+ nsTArray<sandbox::WellKnownCapabilities> mWellKnownCapabilites;
+ nsTArray<const wchar_t*> mNamedCapabilites;
+};
+
+struct GenericUtilitySandboxProps : public UtilitySandboxProps {};
+
+struct UtilityAudioDecodingWmfSandboxProps : public UtilitySandboxProps {
+ UtilityAudioDecodingWmfSandboxProps() {
+ mDelayedTokenLevel = sandbox::USER_LIMITED;
+ mDelayedMitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+#ifdef MOZ_WMF
+ if (StaticPrefs::security_sandbox_utility_wmf_acg_enabled()) {
+ mDelayedMitigations |= DynamicCodeFlagForSystemMediaLibraries();
+ }
+#else
+ mDelayedMitigations |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
+#endif // MOZ_WMF
+ }
+};
+
+#ifdef MOZ_WMF_MEDIA_ENGINE
+struct UtilityMfMediaEngineCdmSandboxProps : public UtilitySandboxProps {
+ UtilityMfMediaEngineCdmSandboxProps() {
+ mJobLevel = sandbox::JOB_INTERACTIVE;
+ mInitialTokenLevel = sandbox::USER_UNPROTECTED;
+ mDelayedTokenLevel = sandbox::USER_UNPROTECTED;
+ mUseAlternateDesktop = false;
+ mUseAlternateWindowStation = false;
+ mLockdownDefaultDacl = false;
+ mAddRestrictingRandomSid = false;
+
+ // When we have an LPAC we can't set an integrity level and the process will
+ // default to low integrity anyway. Without an LPAC using low integrity
+ // causes problems with the CDMs.
+ mInitialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LAST;
+ mDelayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LAST;
+
+ if (StaticPrefs::security_sandbox_utility_wmf_cdm_lpac_enabled()) {
+ mPackagePrefix = u"fx.sb.cdm"_ns;
+ mWellKnownCapabilites = {
+ sandbox::WellKnownCapabilities::kPrivateNetworkClientServer,
+ sandbox::WellKnownCapabilities::kInternetClient,
+ };
+ mNamedCapabilites = {
+ L"lpacCom",
+ L"lpacIdentityServices",
+ L"lpacMedia",
+ L"lpacPnPNotifications",
+ L"lpacServicesManagement",
+ L"lpacSessionManagement",
+ L"lpacAppExperience",
+ L"lpacInstrumentation",
+ L"lpacCryptoServices",
+ L"lpacEnterprisePolicyChangeNotifications",
+ L"mediaFoundationCdmFiles",
+ L"lpacMediaFoundationCdmData",
+ L"registryRead",
+ kLpacFirefoxInstallFiles,
+ L"lpacDeviceAccess",
+ };
+ }
+ mUseWin32kLockdown = false;
+ mInitialMitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR |
+ sandbox::MITIGATION_HEAP_TERMINATE | sandbox::MITIGATION_SEHOP |
+ sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP |
+ sandbox::MITIGATION_CET_COMPAT_MODE;
+
+ mDelayedMitigations = sandbox::MITIGATION_DLL_SEARCH_ORDER;
+ }
+};
+#endif
+
+struct WindowsUtilitySandboxProps : public UtilitySandboxProps {
+ WindowsUtilitySandboxProps() {
+ mJobLevel = sandbox::JOB_INTERACTIVE;
+ mDelayedTokenLevel = sandbox::USER_RESTRICTED_SAME_ACCESS;
+ mUseAlternateWindowStation = false;
+ mInitialIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
+ mDelayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
+ mUseWin32kLockdown = false;
+ mUseCig = false;
+ mDelayedMitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+ }
+};
+
+static const char* WellKnownCapabilityNames[] = {
+ "InternetClient",
+ "InternetClientServer",
+ "PrivateNetworkClientServer",
+ "PicturesLibrary",
+ "VideosLibrary",
+ "MusicLibrary",
+ "DocumentsLibrary",
+ "EnterpriseAuthentication",
+ "SharedUserCertificates",
+ "RemovableStorage",
+ "Appointments",
+ "Contacts",
+};
+
+void LogUtilitySandboxProps(const UtilitySandboxProps& us) {
+ if (!static_cast<LogModule*>(sSandboxBrokerLog)->ShouldLog(LogLevel::Debug)) {
+ return;
+ }
+
+ nsAutoCString logMsg;
+ logMsg.AppendPrintf("Building sandbox for utility process:\n");
+ logMsg.AppendPrintf("\tJob Level: %d\n", static_cast<int>(us.mJobLevel));
+ logMsg.AppendPrintf("\tInitial Token Level: %d\n",
+ static_cast<int>(us.mInitialTokenLevel));
+ logMsg.AppendPrintf("\tDelayed Token Level: %d\n",
+ static_cast<int>(us.mDelayedTokenLevel));
+ logMsg.AppendPrintf("\tInitial Integrity Level: %d\n",
+ static_cast<int>(us.mInitialIntegrityLevel));
+ logMsg.AppendPrintf("\tDelayed Integrity Level: %d\n",
+ static_cast<int>(us.mDelayedIntegrityLevel));
+ logMsg.AppendPrintf("\tUse Alternate Window Station: %s\n",
+ us.mUseAlternateWindowStation ? "yes" : "no");
+ logMsg.AppendPrintf("\tUse Alternate Desktop: %s\n",
+ us.mUseAlternateDesktop ? "yes" : "no");
+ logMsg.AppendPrintf("\tLockdown Default Dacl: %s\n",
+ us.mLockdownDefaultDacl ? "yes" : "no");
+ logMsg.AppendPrintf("\tAdd Random Restricting SID: %s\n",
+ us.mAddRestrictingRandomSid ? "yes" : "no");
+ logMsg.AppendPrintf("\tUse Win32k Lockdown: %s\n",
+ us.mUseWin32kLockdown ? "yes" : "no");
+ logMsg.AppendPrintf("\tUse CIG: %s\n", us.mUseCig ? "yes" : "no");
+ logMsg.AppendPrintf("\tInitial mitigations: %016llx\n",
+ static_cast<uint64_t>(us.mInitialMitigations));
+ logMsg.AppendPrintf("\tDelayed mitigations: %016llx\n",
+ static_cast<uint64_t>(us.mDelayedMitigations));
+ if (us.mPackagePrefix.IsEmpty()) {
+ logMsg.AppendPrintf("\tNo Low Privileged Application Container\n");
+ } else {
+ logMsg.AppendPrintf("\tLow Privileged Application Container Settings:\n");
+ logMsg.AppendPrintf("\t\tPackage Name Prefix: %S\n",
+ static_cast<wchar_t*>(us.mPackagePrefix.get()));
+ logMsg.AppendPrintf("\t\tWell Known Capabilities:\n");
+ for (auto wkCap : us.mWellKnownCapabilites) {
+ logMsg.AppendPrintf("\t\t\t%s\n", WellKnownCapabilityNames[wkCap]);
+ }
+ logMsg.AppendPrintf("\t\tNamed Capabilities:\n");
+ for (auto namedCap : us.mNamedCapabilites) {
+ logMsg.AppendPrintf("\t\t\t%S\n", namedCap);
+ }
+ }
+
+ LOG_D("%s", logMsg.get());
+}
+
+bool BuildUtilitySandbox(sandbox::TargetPolicy* policy,
+ const UtilitySandboxProps& us) {
+ LogUtilitySandboxProps(us);
+
+ auto result = SetJobLevel(policy, us.mJobLevel, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+
+ result = policy->SetTokenLevel(us.mInitialTokenLevel, us.mDelayedTokenLevel);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ if (us.mInitialIntegrityLevel != sandbox::INTEGRITY_LEVEL_LAST) {
+ result = policy->SetIntegrityLevel(us.mInitialIntegrityLevel);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+ }
+
+ if (us.mDelayedIntegrityLevel != sandbox::INTEGRITY_LEVEL_LAST) {
+ result = policy->SetDelayedIntegrityLevel(us.mDelayedIntegrityLevel);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+ }
+
+ if (us.mUseAlternateDesktop) {
+ result = policy->SetAlternateDesktop(us.mUseAlternateWindowStation);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+ }
+
+ if (us.mLockdownDefaultDacl) {
+ policy->SetLockdownDefaultDacl();
+ }
+ if (us.mAddRestrictingRandomSid) {
+ policy->AddRestrictingRandomSid();
+ }
+
+ result = policy->SetProcessMitigations(us.mInitialMitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ result = policy->SetDelayedProcessMitigations(us.mDelayedMitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // Win32k lockdown might not work on earlier versions
+ // Bug 1719212, 1769992
+ if (us.mUseWin32kLockdown && IsWin10FallCreatorsUpdateOrLater()) {
+ result = AddWin32kLockdownPolicy(policy, false);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy");
+ }
+
+ if (us.mUseCig) {
+ bool alwaysProxyBinDirLoading = mozilla::HasPackageIdentity();
+ result = AddCigToPolicy(policy, alwaysProxyBinDirLoading);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to initialize signed policy rules.");
+ }
+
+ if (!us.mPackagePrefix.IsEmpty()) {
+ MOZ_ASSERT(us.mInitialIntegrityLevel == sandbox::INTEGRITY_LEVEL_LAST,
+ "Initial integrity level cannot be specified if using an LPAC.");
+
+ result = AddAndConfigureAppContainerProfile(policy, us.mPackagePrefix,
+ us.mWellKnownCapabilites,
+ us.mNamedCapabilites);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to configure AppContainer profile.");
+ }
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = policy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+
+bool SandboxBroker::SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) {
+ if (!mPolicy) {
+ return false;
+ }
+
+ switch (aSandbox) {
+ case mozilla::ipc::SandboxingKind::GENERIC_UTILITY:
+ return BuildUtilitySandbox(mPolicy, GenericUtilitySandboxProps());
+ case mozilla::ipc::SandboxingKind::UTILITY_AUDIO_DECODING_WMF:
+ return BuildUtilitySandbox(mPolicy,
+ UtilityAudioDecodingWmfSandboxProps());
+#ifdef MOZ_WMF_MEDIA_ENGINE
+ case mozilla::ipc::SandboxingKind::MF_MEDIA_ENGINE_CDM:
+ return BuildUtilitySandbox(mPolicy,
+ UtilityMfMediaEngineCdmSandboxProps());
+#endif
+ case mozilla::ipc::SandboxingKind::WINDOWS_UTILS:
+ return BuildUtilitySandbox(mPolicy, WindowsUtilitySandboxProps());
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown sandboxing value");
+ return false;
+ }
+}
+
+bool SandboxBroker::SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch) {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ SetJobLevel(mPolicy, sandbox::JOB_LOCKDOWN, 0 /* ui_exceptions */);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetJobLevel should never fail with these arguments, what happened?");
+ auto level = (aLevel == Restricted) ? sandbox::USER_RESTRICTED
+ : sandbox::USER_LOCKDOWN;
+ result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS, level);
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "SetTokenLevel should never fail with these arguments, what happened?");
+
+ result = mPolicy->SetAlternateDesktop(true);
+ if (NS_WARN_IF(result != sandbox::SBOX_ALL_OK)) {
+ LOG_W("SetAlternateDesktop failed, result: %i, last error: %lx", result,
+ ::GetLastError());
+ }
+
+ result = mPolicy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
+ MOZ_ASSERT(sandbox::SBOX_ALL_OK == result,
+ "SetIntegrityLevel should never fail with these arguments, what "
+ "happened?");
+
+ result =
+ mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "SetIntegrityLevel should never fail with these "
+ "arguments, what happened?");
+
+ mPolicy->SetLockdownDefaultDacl();
+ mPolicy->AddRestrictingRandomSid();
+
+ sandbox::MitigationFlags mitigations =
+ sandbox::MITIGATION_BOTTOM_UP_ASLR | sandbox::MITIGATION_HEAP_TERMINATE |
+ sandbox::MITIGATION_SEHOP | sandbox::MITIGATION_EXTENSION_POINT_DISABLE |
+ sandbox::MITIGATION_DEP_NO_ATL_THUNK | sandbox::MITIGATION_DEP;
+
+ if (StaticPrefs::security_sandbox_gmp_shadow_stack_enabled()) {
+ mitigations |= sandbox::MITIGATION_CET_COMPAT_MODE;
+ }
+
+ result = mPolicy->SetProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result, "Invalid flags for SetProcessMitigations.");
+
+ // Chromium only implements win32k disable for PPAPI on Win10 or later,
+ // believed to be due to the interceptions required for OPM.
+ if (StaticPrefs::security_sandbox_gmp_win32k_disable() && IsWin10OrLater()) {
+ result = AddWin32kLockdownPolicy(mPolicy, true);
+ SANDBOX_ENSURE_SUCCESS(result, "Failed to add the win32k lockdown policy");
+ }
+
+ mitigations = sandbox::MITIGATION_STRICT_HANDLE_CHECKS |
+ sandbox::MITIGATION_DLL_SEARCH_ORDER;
+
+ result = mPolicy->SetDelayedProcessMitigations(mitigations);
+ SANDBOX_ENSURE_SUCCESS(result,
+ "Invalid flags for SetDelayedProcessMitigations.");
+
+ // Add the policy for the client side of a pipe. It is just a file
+ // in the \pipe\ namespace. We restrict it to pipes that start with
+ // "chrome." so the sandboxed process cannot connect to system services.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\chrome.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // Add the policy for the client side of the crash server pipe.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_ANY,
+ L"\\??\\pipe\\gecko-crash-server-pipe.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+#ifdef DEBUG
+ // The plugin process can't create named events, but we'll
+ // make an exception for the events used in logging. Removing
+ // this will break EME in debug builds.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SYNC,
+ sandbox::TargetPolicy::EVENTS_ALLOW_ANY,
+ L"ChromeIPCLog.*");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+#endif
+
+ // The following rules were added because, during analysis of an EME
+ // plugin during development, these registry keys were accessed when
+ // loading the plugin. Commenting out these policy exceptions caused
+ // plugin loading to fail, so they are necessary for proper functioning
+ // of at least one EME plugin.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Control Panel\\Desktop");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Control Panel\\Desktop\\LanguageConfiguration");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_"
+ L"MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\SideBySide");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The following rules were added because, during analysis of an EME
+ // plugin during development, these registry keys were accessed when
+ // loading the plugin. Commenting out these policy exceptions did not
+ // cause anything to break during initial testing, but might cause
+ // unforeseen issues down the road.
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\MUI\\Settings");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Software\\Policies\\Microsoft\\Control "
+ L"Panel\\Desktop");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Control Panel\\Desktop\\PreferredUILanguages");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_"
+ L"MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVer"
+ L"sion\\SideBySide\\PreferExternalManifest");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The following rules were added to allow a GMP to be loaded when any
+ // AppLocker DLL rules are specified. If the rules specifically block the DLL
+ // then it will not load.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY,
+ L"\\Device\\SrpDevice");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Srp\\GP\\");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+ // On certain Windows versions there is a double slash before GP in the path.
+ result = mPolicy->AddRule(
+ sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Srp\\\\GP\\");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ // The GMP process needs to be able to share memory with the main process for
+ // crash reporting. On arm64 when we are launching remotely via an x86 broker,
+ // we need the rule to be HANDLES_DUP_ANY, because we still need to duplicate
+ // to the main process not the child's broker.
+ result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ aIsRemoteLaunch
+ ? sandbox::TargetPolicy::HANDLES_DUP_ANY
+ : sandbox::TargetPolicy::HANDLES_DUP_BROKER,
+ L"Section");
+ SANDBOX_ENSURE_SUCCESS(
+ result,
+ "With these static arguments AddRule should never fail, what happened?");
+
+ return true;
+}
+#undef SANDBOX_ENSURE_SUCCESS
+
+bool SandboxBroker::AllowReadFile(wchar_t const* file) {
+ if (!mPolicy) {
+ return false;
+ }
+
+ auto result =
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+ sandbox::TargetPolicy::FILES_ALLOW_READONLY, file);
+ if (sandbox::SBOX_ALL_OK != result) {
+ LOG_E("Failed (ResultCode %d) to add read access to: %S", result, file);
+ return false;
+ }
+
+ return true;
+}
+
+/* static */
+bool SandboxBroker::AddTargetPeer(HANDLE aPeerProcess) {
+ if (!sBrokerService) {
+ return false;
+ }
+
+ sandbox::ResultCode result = sBrokerService->AddTargetPeer(aPeerProcess);
+ return (sandbox::SBOX_ALL_OK == result);
+}
+
+void SandboxBroker::AddHandleToShare(HANDLE aHandle) {
+ mPolicy->AddHandleToShare(aHandle);
+}
+
+bool SandboxBroker::IsWin32kLockedDown() {
+ return mPolicy->GetProcessMitigations() & sandbox::MITIGATION_WIN32K_DISABLE;
+}
+
+void SandboxBroker::ApplyLoggingPolicy() {
+ MOZ_ASSERT(mPolicy);
+
+ // Add dummy rules, so that we can log in the interception code.
+ // We already have a file interception set up for the client side of pipes.
+ // Also, passing just "dummy" for file system policy causes win_utils.cc
+ // IsReparsePoint() to loop.
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_NAMED_PIPES,
+ sandbox::TargetPolicy::NAMEDPIPES_ALLOW_ANY, L"dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_PROCESS,
+ sandbox::TargetPolicy::PROCESS_MIN_EXEC, L"dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_REGISTRY,
+ sandbox::TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_SYNC,
+ sandbox::TargetPolicy::EVENTS_ALLOW_READONLY, L"dummy");
+ mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+ sandbox::TargetPolicy::HANDLES_DUP_BROKER, L"dummy");
+}
+
+SandboxBroker::~SandboxBroker() {
+ if (mPolicy) {
+ mPolicy->Release();
+ mPolicy = nullptr;
+ }
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/sandboxbroker/sandboxBroker.h b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
new file mode 100644
index 0000000000..752fbaa9aa
--- /dev/null
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.h
@@ -0,0 +1,137 @@
+/* -*- 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/. */
+
+#ifndef __SECURITY_SANDBOX_SANDBOXBROKER_H__
+#define __SECURITY_SANDBOX_SANDBOXBROKER_H__
+
+#include <stdint.h>
+#include <windows.h>
+
+#include "build/build_config.h"
+#include "mozilla/ipc/EnvironmentMap.h"
+#include "nsCOMPtr.h"
+#include "nsXULAppAPI.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#include "mozilla/ipc/LaunchError.h"
+#include "mozilla/Result.h"
+
+namespace sandbox {
+class BrokerServices;
+class TargetPolicy;
+} // namespace sandbox
+
+namespace mozilla {
+
+class AbstractSandboxBroker {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractSandboxBroker)
+
+ virtual void Shutdown() = 0;
+ virtual Result<Ok, mozilla::ipc::LaunchError> LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA* aCachedNtdllThunk,
+ void** aProcessHandle) = 0;
+
+ // Security levels for different types of processes
+ virtual void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) = 0;
+
+ virtual void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) = 0;
+ virtual bool SetSecurityLevelForRDDProcess() = 0;
+ virtual bool SetSecurityLevelForSocketProcess() = 0;
+ virtual bool SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) = 0;
+
+ enum SandboxLevel { LockDown, Restricted };
+ virtual bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch = false) = 0;
+
+ // File system permissions
+ virtual bool AllowReadFile(wchar_t const* file) = 0;
+
+ /**
+ * Share a HANDLE with the child process. The HANDLE will be made available
+ * in the child process at the memory address
+ * |reinterpret_cast<uintptr_t>(aHandle)|. It is the caller's responsibility
+ * to communicate this address to the child.
+ */
+ virtual void AddHandleToShare(HANDLE aHandle) = 0;
+
+ /**
+ * @return true if policy has win32k locked down, otherwise false
+ */
+ virtual bool IsWin32kLockedDown() = 0;
+
+ protected:
+ virtual ~AbstractSandboxBroker() {}
+};
+
+class SandboxBroker : public AbstractSandboxBroker {
+ public:
+ SandboxBroker();
+
+ static void Initialize(sandbox::BrokerServices* aBrokerServices);
+
+ void Shutdown() override {}
+
+ /**
+ * Do initialization that depends on parts of the Gecko machinery having been
+ * created first.
+ */
+ static void GeckoDependentInitialize();
+
+ Result<Ok, mozilla::ipc::LaunchError> LaunchApp(
+ const wchar_t* aPath, const wchar_t* aArguments,
+ base::EnvironmentMap& aEnvironment, GeckoProcessType aProcessType,
+ const bool aEnableLogging, const IMAGE_THUNK_DATA* aCachedNtdllThunk,
+ void** aProcessHandle) override;
+ virtual ~SandboxBroker();
+
+ // Security levels for different types of processes
+ void SetSecurityLevelForContentProcess(int32_t aSandboxLevel,
+ bool aIsFileProcess) override;
+
+ void SetSecurityLevelForGPUProcess(int32_t aSandboxLevel) override;
+ bool SetSecurityLevelForRDDProcess() override;
+ bool SetSecurityLevelForSocketProcess() override;
+ bool SetSecurityLevelForGMPlugin(SandboxLevel aLevel,
+ bool aIsRemoteLaunch = false) override;
+ bool SetSecurityLevelForUtilityProcess(
+ mozilla::ipc::SandboxingKind aSandbox) override;
+
+ // File system permissions
+ bool AllowReadFile(wchar_t const* file) override;
+
+ /**
+ * Exposes AddTargetPeer from broker services, so that non-sandboxed
+ * processes can be added as handle duplication targets.
+ */
+ static bool AddTargetPeer(HANDLE aPeerProcess);
+
+ /**
+ * Share a HANDLE with the child process. The HANDLE will be made available
+ * in the child process at the memory address
+ * |reinterpret_cast<uintptr_t>(aHandle)|. It is the caller's responsibility
+ * to communicate this address to the child.
+ */
+ void AddHandleToShare(HANDLE aHandle) override;
+
+ bool IsWin32kLockedDown() final;
+
+ // Set up dummy interceptions via the broker, so we can log calls.
+ void ApplyLoggingPolicy();
+
+ private:
+ static bool sRunningFromNetworkDrive;
+ sandbox::TargetPolicy* mPolicy;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/security/sandbox/win/src/sandboxtarget/moz.build b/security/sandbox/win/src/sandboxtarget/moz.build
new file mode 100644
index 0000000000..3c28eebba2
--- /dev/null
+++ b/security/sandbox/win/src/sandboxtarget/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla += [
+ "sandboxTarget.h",
+]
+
+SOURCES += [
+ "sandboxTarget.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+ "/security/sandbox/chromium-shim",
+]
+
+DEFINES["UNICODE"] = True
+
+FINAL_LIBRARY = "xul"
diff --git a/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp b/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp
new file mode 100644
index 0000000000..b0b0876bca
--- /dev/null
+++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.cpp
@@ -0,0 +1,64 @@
+/* -*- 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/. */
+
+#include "sandboxTarget.h"
+
+#include "sandbox/win/src/sandbox.h"
+
+namespace mozilla {
+
+// We need to define this function out of line so that clang-cl doesn't inline
+// it.
+/* static */
+SandboxTarget* SandboxTarget::Instance() {
+ static SandboxTarget sb;
+ return &sb;
+}
+
+void SandboxTarget::StartSandbox() {
+ if (mTargetServices) {
+ mTargetServices->LowerToken();
+ NotifyStartObservers();
+ }
+}
+
+void SandboxTarget::NotifyStartObservers() {
+ for (auto&& obs : mStartObservers) {
+ if (!obs) {
+ continue;
+ }
+
+ obs();
+ }
+
+ mStartObservers.clear();
+}
+
+bool SandboxTarget::BrokerDuplicateHandle(HANDLE aSourceHandle,
+ DWORD aTargetProcessId,
+ HANDLE* aTargetHandle,
+ DWORD aDesiredAccess,
+ DWORD aOptions) {
+ if (!mTargetServices) {
+ return false;
+ }
+
+ sandbox::ResultCode result = mTargetServices->DuplicateHandle(
+ aSourceHandle, aTargetProcessId, aTargetHandle, aDesiredAccess, aOptions);
+ return (sandbox::SBOX_ALL_OK == result);
+}
+bool SandboxTarget::GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before) {
+ if (!mTargetServices) {
+ return false;
+ }
+
+ sandbox::ResultCode result =
+ mTargetServices->GetComplexLineBreaks(text, length, break_before);
+ return (sandbox::SBOX_ALL_OK == result);
+}
+
+} // namespace mozilla
diff --git a/security/sandbox/win/src/sandboxtarget/sandboxTarget.h b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h
new file mode 100644
index 0000000000..7867f0bb33
--- /dev/null
+++ b/security/sandbox/win/src/sandboxtarget/sandboxTarget.h
@@ -0,0 +1,80 @@
+/* -*- 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/. */
+
+#ifndef __SECURITY_SANDBOX_SANDBOXTARGET_H__
+#define __SECURITY_SANDBOX_SANDBOXTARGET_H__
+
+#include <windows.h>
+
+#include <functional>
+#include <list>
+#include <utility>
+
+#include "mozilla/Assertions.h"
+
+namespace sandbox {
+class TargetServices;
+}
+
+namespace mozilla {
+
+class SandboxTarget {
+ public:
+ /**
+ * Obtains a pointer to the singleton instance
+ */
+ static SandboxTarget* Instance();
+
+ /**
+ * Used by the application to pass in the target services that provide certain
+ * functions to the sandboxed code.
+ * The target services must already be initialized.
+ *
+ * @param aTargetServices The target services that will be used
+ */
+ void SetTargetServices(sandbox::TargetServices* aTargetServices) {
+ MOZ_ASSERT(aTargetServices);
+ MOZ_ASSERT(!mTargetServices,
+ "Sandbox TargetServices must only be set once.");
+
+ mTargetServices = aTargetServices;
+ }
+
+ template <typename CallbackT>
+ void RegisterSandboxStartCallback(CallbackT&& aCallback) {
+ mStartObservers.push_back(std::forward<CallbackT>(aCallback));
+ }
+
+ /**
+ * Called by the library that wants to "start" the sandbox, i.e. change to the
+ * more secure delayed / lockdown policy.
+ */
+ void StartSandbox();
+
+ /**
+ * Used to duplicate handles via the broker process. The permission for the
+ * handle type and target process has to have been set on the sandbox policy.
+ */
+ bool BrokerDuplicateHandle(HANDLE aSourceHandle, DWORD aTargetProcessId,
+ HANDLE* aTargetHandle, DWORD aDesiredAccess,
+ DWORD aOptions);
+
+ bool GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before);
+
+ protected:
+ SandboxTarget() : mTargetServices(nullptr) {}
+
+ sandbox::TargetServices* mTargetServices;
+
+ private:
+ void NotifyStartObservers();
+ std::list<std::function<void()>> mStartObservers;
+};
+
+} // namespace mozilla
+
+#endif