From f7eaed5286974984ba5f9e3189d8f49d03e99f81 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 12 May 2022 15:59:20 +0200 Subject: BASELINE: Update Chromium to 100.0.4896.167 Change-Id: I98cbeb5d7543d966ffe04d8cefded0c493a11333 Reviewed-by: Allan Sandfeld Jensen --- chromium/sandbox/linux/BUILD.gn | 6 +- .../integration_tests/bpf_dsl_seccomp_unittest.cc | 16 +- .../seccomp_broker_process_unittest.cc | 3 +- .../linux/seccomp-bpf-helpers/baseline_policy.cc | 2 +- .../baseline_policy_unittest.cc | 9 +- .../syscall_parameters_restrictions.cc | 16 +- .../syscall_parameters_restrictions_unittests.cc | 6 +- chromium/sandbox/linux/seccomp-bpf/die.cc | 6 +- chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 4 +- chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h | 3 +- chromium/sandbox/linux/services/credentials.cc | 5 +- chromium/sandbox/linux/services/credentials.h | 24 +- .../sandbox/linux/services/libc_interceptor.cc | 9 - chromium/sandbox/linux/services/namespace_utils.h | 7 +- chromium/sandbox/linux/services/proc_util.h | 4 +- chromium/sandbox/linux/services/resource_limits.h | 12 +- .../linux/services/resource_limits_unittest.cc | 44 + .../linux/services/resource_limits_unittests.cc | 43 - .../linux/services/thread_helpers_unittest.cc | 147 +++ .../linux/services/thread_helpers_unittests.cc | 147 --- chromium/sandbox/linux/services/yama_unittest.cc | 170 +++ chromium/sandbox/linux/services/yama_unittests.cc | 172 --- .../suid/client/setuid_sandbox_host_unittest.cc | 4 +- .../sandbox/linux/syscall_broker/broker_client.cc | 2 +- .../sandbox/linux/syscall_broker/broker_process.cc | 16 +- .../syscall_broker/broker_process_unittest.cc | 23 +- .../remote_syscall_arg_handler_unittest.cc | 5 +- .../sandbox/linux/system_headers/linux_prctl.h | 4 +- chromium/sandbox/mac/seatbelt_exec.h | 12 +- chromium/sandbox/policy/BUILD.gn | 1 + chromium/sandbox/policy/features.cc | 24 +- chromium/sandbox/policy/features.h | 12 +- .../policy/fuchsia/sandbox_policy_fuchsia.cc | 45 +- .../sandbox/policy/linux/bpf_gpu_policy_linux.cc | 2 +- .../policy/linux/sandbox_debug_handling_linux.cc | 7 +- .../policy/linux/sandbox_seccomp_bpf_linux.cc | 3 + chromium/sandbox/policy/mac/common.sb | 34 + chromium/sandbox/policy/mac/sandbox_mac.mm | 1 + chromium/sandbox/policy/mojom/sandbox.mojom | 5 + chromium/sandbox/policy/sandbox.cc | 34 +- chromium/sandbox/policy/sandbox.h | 10 +- chromium/sandbox/policy/sandbox_delegate.h | 4 +- chromium/sandbox/policy/sandbox_type.cc | 65 +- chromium/sandbox/policy/sandbox_type_unittest.cc | 9 +- chromium/sandbox/policy/switches.cc | 18 +- chromium/sandbox/policy/switches.h | 16 +- .../policy/win/sandbox_policy_feature_test.cc | 52 +- .../policy/win/sandbox_policy_feature_test.h | 18 +- chromium/sandbox/policy/win/sandbox_win.cc | 7 +- chromium/sandbox/win/BUILD.gn | 36 - chromium/sandbox/win/src/app_container.h | 11 + chromium/sandbox/win/src/app_container_base.h | 14 +- chromium/sandbox/win/src/app_container_test.cc | 11 +- chromium/sandbox/win/src/broker_services.cc | 5 +- chromium/sandbox/win/src/broker_services.h | 1 - chromium/sandbox/win/src/file_policy_test.cc | 326 +++--- chromium/sandbox/win/src/filesystem_dispatcher.cc | 82 +- chromium/sandbox/win/src/filesystem_dispatcher.h | 6 + .../sandbox/win/src/filesystem_interception.cc | 141 ++- chromium/sandbox/win/src/filesystem_policy.cc | 103 +- chromium/sandbox/win/src/filesystem_policy.h | 5 - chromium/sandbox/win/src/handle_closer.cc | 29 - chromium/sandbox/win/src/handle_closer.h | 3 - chromium/sandbox/win/src/handle_closer_agent.cc | 98 +- chromium/sandbox/win/src/handle_closer_test.cc | 62 +- chromium/sandbox/win/src/integrity_level_test.cc | 44 +- chromium/sandbox/win/src/interception_agent.cc | 55 +- chromium/sandbox/win/src/interception_unittest.cc | 29 +- chromium/sandbox/win/src/interceptors_64.cc | 1 - chromium/sandbox/win/src/ipc_ping_test.cc | 13 +- chromium/sandbox/win/src/job.cc | 8 +- chromium/sandbox/win/src/named_pipe_policy_test.cc | 76 +- chromium/sandbox/win/src/nt_internals.h | 10 +- chromium/sandbox/win/src/policy_broker.cc | 61 +- chromium/sandbox/win/src/policy_broker.h | 3 - chromium/sandbox/win/src/policy_engine_opcodes.cc | 13 +- chromium/sandbox/win/src/policy_engine_unittest.cc | 4 - chromium/sandbox/win/src/policy_low_level.cc | 2 +- .../sandbox/win/src/policy_low_level_unittest.cc | 14 - .../sandbox/win/src/policy_opcodes_unittest.cc | 30 +- chromium/sandbox/win/src/policy_params.h | 17 +- chromium/sandbox/win/src/policy_target.cc | 6 - chromium/sandbox/win/src/policy_target_test.cc | 27 +- .../src/process_mitigations_dyncode_unittest.cc | 47 +- .../src/process_mitigations_imageload_unittest.cc | 462 -------- .../win/src/process_mitigations_unittest.cc | 112 +- .../sandbox/win/src/process_thread_interception.cc | 2 - chromium/sandbox/win/src/process_thread_policy.cc | 30 +- chromium/sandbox/win/src/restricted_token_utils.cc | 12 +- chromium/sandbox/win/src/sandbox_nt_types.h | 16 +- chromium/sandbox/win/src/sandbox_nt_util.cc | 247 +++-- chromium/sandbox/win/src/sandbox_nt_util.h | 22 +- .../sandbox/win/src/sandbox_nt_util_unittest.cc | 55 +- chromium/sandbox/win/src/sandbox_policy.h | 2 - chromium/sandbox/win/src/sandbox_policy_base.cc | 35 +- chromium/sandbox/win/src/sandbox_policy_base.h | 17 +- .../sandbox/win/src/sandbox_policy_diagnostic.cc | 30 +- .../sandbox/win/src/sandbox_policy_diagnostic.h | 2 +- chromium/sandbox/win/src/sandbox_types.h | 11 +- chromium/sandbox/win/src/sharedmem_ipc_client.cc | 25 +- .../sandbox/win/src/sidestep/ia32_modrm_map.cpp | 92 -- .../sandbox/win/src/sidestep/ia32_opcode_map.cpp | 1159 -------------------- .../sandbox/win/src/sidestep/mini_disassembler.cpp | 396 ------- .../sandbox/win/src/sidestep/mini_disassembler.h | 153 --- .../win/src/sidestep/mini_disassembler_types.h | 197 ---- .../sandbox/win/src/sidestep/preamble_patcher.h | 112 -- .../src/sidestep/preamble_patcher_with_stub.cpp | 181 --- chromium/sandbox/win/src/sidestep_resolver.cc | 205 ---- chromium/sandbox/win/src/sidestep_resolver.h | 77 -- chromium/sandbox/win/src/signed_policy.cc | 14 +- .../sandbox/win/src/startup_information_helper.cc | 2 +- .../sandbox/win/src/startup_information_helper.h | 4 +- chromium/sandbox/win/src/target_interceptions.cc | 16 +- chromium/sandbox/win/src/target_process.cc | 17 +- chromium/sandbox/win/src/target_process.h | 4 +- chromium/sandbox/win/src/unload_dll_test.cc | 75 +- chromium/sandbox/win/src/win_utils.cc | 142 ++- chromium/sandbox/win/src/win_utils.h | 23 +- chromium/sandbox/win/src/win_utils_unittest.cc | 110 +- 119 files changed, 1855 insertions(+), 4885 deletions(-) create mode 100644 chromium/sandbox/linux/services/resource_limits_unittest.cc delete mode 100644 chromium/sandbox/linux/services/resource_limits_unittests.cc create mode 100644 chromium/sandbox/linux/services/thread_helpers_unittest.cc delete mode 100644 chromium/sandbox/linux/services/thread_helpers_unittests.cc create mode 100644 chromium/sandbox/linux/services/yama_unittest.cc delete mode 100644 chromium/sandbox/linux/services/yama_unittests.cc delete mode 100644 chromium/sandbox/win/src/process_mitigations_imageload_unittest.cc delete mode 100644 chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp delete mode 100644 chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp delete mode 100644 chromium/sandbox/win/src/sidestep/mini_disassembler.cpp delete mode 100644 chromium/sandbox/win/src/sidestep/mini_disassembler.h delete mode 100644 chromium/sandbox/win/src/sidestep/mini_disassembler_types.h delete mode 100644 chromium/sandbox/win/src/sidestep/preamble_patcher.h delete mode 100644 chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp delete mode 100644 chromium/sandbox/win/src/sidestep_resolver.cc delete mode 100644 chromium/sandbox/win/src/sidestep_resolver.h (limited to 'chromium/sandbox') diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn index abbcc509692..ec24cd81e27 100644 --- a/chromium/sandbox/linux/BUILD.gn +++ b/chromium/sandbox/linux/BUILD.gn @@ -78,11 +78,11 @@ source_set("sandbox_linux_unittests_sources") { sources = [ "services/proc_util_unittest.cc", - "services/resource_limits_unittests.cc", + "services/resource_limits_unittest.cc", "services/scoped_process_unittest.cc", "services/syscall_wrappers_unittest.cc", - "services/thread_helpers_unittests.cc", - "services/yama_unittests.cc", + "services/thread_helpers_unittest.cc", + "services/yama_unittest.cc", "syscall_broker/broker_file_permission_unittest.cc", "syscall_broker/broker_process_unittest.cc", "syscall_broker/broker_simple_message_unittest.cc", diff --git a/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc b/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc index 61298af943d..d1e018ff66f 100644 --- a/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc +++ b/chromium/sandbox/linux/integration_tests/bpf_dsl_seccomp_unittest.cc @@ -18,8 +18,6 @@ #include #include -#include "base/memory/raw_ptr.h" - #if defined(ANDROID) // Work-around for buggy headers in Android's NDK #define __user @@ -28,6 +26,7 @@ #include "base/bind.h" #include "base/check.h" +#include "base/memory/raw_ptr.h" #include "base/posix/eintr_wrapper.h" #include "base/synchronization/waitable_event.h" #include "base/system/sys_info.h" @@ -183,7 +182,12 @@ bool IsSyscallForTestHarness(int sysno) { // ASan and MSan don't need any of these for normal operation, but they // require at least mmap & munmap to print a report if an error is detected. // ASan requires sigaltstack. - if (sysno == kMMapNr || sysno == __NR_munmap || sysno == __NR_pipe || + if (sysno == kMMapNr || sysno == __NR_munmap || +#if !defined(__aarch64__) + sysno == __NR_pipe || +#else + sysno == __NR_pipe2 || +#endif sysno == __NR_sigaltstack) { return true; } @@ -801,7 +805,7 @@ ResultExpr SimpleCondTestPolicy::EvaluateSyscall(int sysno) const { #if defined(__NR_open) case __NR_open: flags_argument_position = 1; - FALLTHROUGH; + [[fallthrough]]; #endif case __NR_openat: { // open can be a wrapper for openat(2). if (sysno == __NR_openat) @@ -2087,7 +2091,7 @@ SANDBOX_TEST(SandboxBPF, DISABLE_ON_TSAN(SeccompRetTrace)) { } // Android does not expose pread64 nor pwrite64. -#if !defined(OS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) bool FullPwrite64(int fd, const char* buffer, size_t count, off64_t offset) { while (count > 0) { @@ -2168,7 +2172,7 @@ BPF_TEST_C(SandboxBPF, Pread64, TrapPread64Policy) { BPF_ASSERT(pread_64_was_forwarded); } -#endif // !defined(OS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) void* TsyncApplyToTwoThreadsFunc(void* cond_ptr) { base::WaitableEvent* event = static_cast(cond_ptr); diff --git a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc index 0d6b6b377ad..376e855f138 100644 --- a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc +++ b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc @@ -310,7 +310,8 @@ class IPCSyscaller : public Syscaller { // Only use syscall(...) on x64 to avoid having to reimplement a libc-like // layer that uses different syscalls on different architectures. -#if (defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)) && \ +#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ + BUILDFLAG(IS_ANDROID)) && \ defined(__x86_64__) #define DIRECT_SYSCALLER_ENABLED #endif diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc index f9eea2783e1..74ce4c75d4d 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc @@ -150,7 +150,7 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno, return Allow(); } -#if defined(__NR_rseq) && !defined(OS_ANDROID) +#if defined(__NR_rseq) && !BUILDFLAG(IS_ANDROID) // See https://crbug.com/1104160. Rseq can only be disabled right before an // execve, because glibc registers it with the kernel and so far it's unclear // whether shared libraries (which, during initialization, may observe that diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc index 2f53b8f4586..ea405e47d75 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc @@ -24,9 +24,10 @@ #include #include +#include + #include "base/clang_profiling_buildflags.h" #include "base/files/scoped_file.h" -#include "base/ignore_result.h" #include "base/posix/eintr_wrapper.h" #include "base/threading/thread.h" #include "build/build_config.h" @@ -185,7 +186,7 @@ BPF_TEST_C(BaselinePolicy, CreateThread, BaselinePolicy) { } // Rseq should be enabled if it exists (i.e. shouldn't receive EPERM). -#if !defined(OS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) BPF_TEST_C(BaselinePolicy, RseqEnabled, BaselinePolicy) { errno = 0; int res = syscall(__NR_rseq, 0, 0, 0, 0); @@ -195,7 +196,7 @@ BPF_TEST_C(BaselinePolicy, RseqEnabled, BaselinePolicy) { // EINVAL, or ENOSYS if the kernel is too old to recognize the system call. BPF_ASSERT(EINVAL == errno || ENOSYS == errno); } -#endif // !defined(OS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) BPF_DEATH_TEST_C(BaselinePolicy, DisallowedCloneFlagCrashes, @@ -252,7 +253,7 @@ BPF_DEATH_TEST_C(BaselinePolicy, DEATH_SEGV_MESSAGE(GetErrorMessageContentForTests()), BaselinePolicy) { int sv[2]; - ignore_result(socketpair(AF_INET, SOCK_STREAM, 0, sv)); + std::ignore = socketpair(AF_INET, SOCK_STREAM, 0, sv); _exit(1); } #endif // defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc index 36221d24bd0..33112b7c1eb 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc @@ -35,8 +35,8 @@ #include "sandbox/linux/system_headers/linux_syscalls.h" #include "sandbox/linux/system_headers/linux_time.h" -#if (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \ - !defined(__arm__) && !defined(__aarch64__) && \ +#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)) && \ + !defined(__arm__) && !defined(__aarch64__) && \ !defined(PTRACE_GET_THREAD_AREA) // Also include asm/ptrace-abi.h since ptrace.h in older libc (for instance // the one in Ubuntu 16.04 LTS) is missing PTRACE_GET_THREAD_AREA. @@ -45,13 +45,13 @@ #include #endif -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) #if !defined(F_DUPFD_CLOEXEC) #define F_DUPFD_CLOEXEC (F_LINUX_SPECIFIC_BASE + 6) #endif -#endif // defined(OS_ANDROID) +#endif // BUILDFLAG(IS_ANDROID) #if defined(__arm__) && !defined(MAP_STACK) #define MAP_STACK 0x20000 // Daisy build environment has old headers. @@ -87,7 +87,7 @@ inline bool IsArchitectureI386() { } inline bool IsAndroid() { -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) return true; #else return false; @@ -172,7 +172,7 @@ ResultExpr RestrictPrctl() { const Arg option(0); return Switch(option) .CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) , PR_SET_VMA, PR_SET_PTRACER, PR_SET_TIMERSLACK , PR_GET_NO_NEW_PRIVS, PR_PAC_RESET_KEYS @@ -202,7 +202,7 @@ ResultExpr RestrictPrctl() { , PR_SET_TIMERSLACK_PID_1 , PR_SET_TIMERSLACK_PID_2 , PR_SET_TIMERSLACK_PID_3 -#endif // defined(OS_ANDROID) +#endif // BUILDFLAG(IS_ANDROID) ), Allow()) .Default(CrashSIGSYSPrctl()); @@ -397,7 +397,7 @@ ResultExpr RestrictClockID() { CLOCK_THREAD_CPUTIME_ID), Allow()) .Default(CrashSIGSYS())) -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) // Allow per-pid and per-tid clocks. .ElseIf((clockid & CPUCLOCK_CLOCK_MASK) != CLOCKFD, Allow()) #endif diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc index 4adc80bb761..67da5d15c08 100644 --- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc +++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc @@ -95,7 +95,7 @@ BPF_TEST_C(ParameterRestrictions, CheckClock(CLOCK_REALTIME); CheckClock(CLOCK_REALTIME_COARSE); CheckClock(CLOCK_THREAD_CPUTIME_ID); -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) clockid_t clock_id; pthread_getcpuclockid(pthread_self(), &clock_id); CheckClock(clock_id); @@ -140,7 +140,7 @@ BPF_DEATH_TEST_C(ParameterRestrictions, syscall(SYS_clock_nanosleep, (~0) | CLOCKFD, 0, &ts, &out_ts); } -#if !defined(OS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) BPF_DEATH_TEST_C(ParameterRestrictions, clock_gettime_crash_cpu_clock, DEATH_SEGV_MESSAGE(sandbox::GetErrorMessageContentForTests()), @@ -154,7 +154,7 @@ BPF_DEATH_TEST_C(ParameterRestrictions, struct timespec ts; clock_gettime(kInitCPUClockID, &ts); } -#endif // !defined(OS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) class RestrictSchedPolicy : public bpf_dsl::Policy { public: diff --git a/chromium/sandbox/linux/seccomp-bpf/die.cc b/chromium/sandbox/linux/seccomp-bpf/die.cc index 40c7f8999f5..4e6b1b7146d 100644 --- a/chromium/sandbox/linux/seccomp-bpf/die.cc +++ b/chromium/sandbox/linux/seccomp-bpf/die.cc @@ -12,8 +12,8 @@ #include #include +#include -#include "base/ignore_result.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "sandbox/linux/seccomp-bpf/syscall.h" @@ -83,8 +83,8 @@ void Die::LogToStderr(const char* msg, const char* file, int line) { // No need to loop. Short write()s are unlikely and if they happen we // probably prefer them over a loop that blocks. - ignore_result( - HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length()))); + std::ignore = + HANDLE_EINTR(Syscall::Call(__NR_write, 2, s.c_str(), s.length())); } } diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index 7069cd752d4..30911a50cdd 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -58,7 +58,7 @@ bool KernelSupportsSeccompBPF() { // 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) +#if BUILDFLAG(IS_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. @@ -73,7 +73,7 @@ bool KernelHasLGBug() { if (rv != -1) { return true; } -#endif // defined(OS_ANDROID) +#endif // BUILDFLAG(IS_ANDROID) return false; } diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h index b926eaeae13..5b5e0588853 100644 --- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -68,8 +68,7 @@ class SANDBOX_EXPORT SandboxBPF { // // |enable_ibpb| controls if the sandbox will forcibly enable indirect branch // prediction barrier through prctl(2) to mitigate Spectre variant 2. - bool StartSandbox(SeccompLevel level, - bool enable_ibpb = true) WARN_UNUSED_RESULT; + [[nodiscard]] bool StartSandbox(SeccompLevel level, bool enable_ibpb = true); // The sandbox needs to be able to access files in "/proc/self/". If // this directory is not accessible when "StartSandbox()" gets called, the diff --git a/chromium/sandbox/linux/services/credentials.cc b/chromium/sandbox/linux/services/credentials.cc index ca6b5954798..c933eafd163 100644 --- a/chromium/sandbox/linux/services/credentials.cc +++ b/chromium/sandbox/linux/services/credentials.cc @@ -100,7 +100,10 @@ bool ChrootToSafeEmptyDir() { // TODO(crbug.com/1247458) Broken in MSan builds after LLVM f1bb30a4956f. clone_flags |= CLONE_VM | CLONE_VFORK | CLONE_SETTLS; - char tls_buf[PTHREAD_STACK_MIN] = {0}; + // PTHREAD_STACK_MIN can be dynamic in glibc2.34+, so it is not possible to + // zeroify tls_buf assigning { 0 } + char tls_buf[PTHREAD_STACK_MIN]; + memset(tls_buf, 0, PTHREAD_STACK_MIN); tls = tls_buf; #endif diff --git a/chromium/sandbox/linux/services/credentials.h b/chromium/sandbox/linux/services/credentials.h index 189d16132fa..17ff667cfd8 100644 --- a/chromium/sandbox/linux/services/credentials.h +++ b/chromium/sandbox/linux/services/credentials.h @@ -7,14 +7,13 @@ #include "build/build_config.h" // Link errors are tedious to track, raise a compile-time error instead. -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) #error "Android is not supported." -#endif // defined(OS_ANDROID). +#endif // BUILDFLAG(IS_ANDROID). #include #include -#include "base/compiler_specific.h" #include "sandbox/linux/system_headers/capability.h" #include "sandbox/sandbox_export.h" @@ -50,21 +49,22 @@ class SANDBOX_EXPORT Credentials { // when calling this API. // |proc_fd| must be a file descriptor to /proc/ and remains owned by // the caller. - static bool DropAllCapabilities(int proc_fd) WARN_UNUSED_RESULT; + [[nodiscard]] static bool DropAllCapabilities(int proc_fd); // A similar API which assumes that it can open /proc/self/ by itself. - static bool DropAllCapabilities() WARN_UNUSED_RESULT; + [[nodiscard]] static bool DropAllCapabilities(); // Sets the effective and permitted capability sets for the current thread to // the list of capabiltiies in |caps|. All other capability flags are cleared. - static bool SetCapabilities(int proc_fd, const std::vector& caps) - WARN_UNUSED_RESULT; + [[nodiscard]] static bool SetCapabilities( + int proc_fd, + const std::vector& caps); // Versions of the above functions which do not check that the process is // single-threaded. After calling these functions, capabilities of other // threads will not be changed. This is dangerous, do not use unless you know // what you are doing. - static bool DropAllCapabilitiesOnCurrentThread() WARN_UNUSED_RESULT; - static bool SetCapabilitiesOnCurrentThread( - const std::vector& caps) WARN_UNUSED_RESULT; + [[nodiscard]] static bool DropAllCapabilitiesOnCurrentThread(); + [[nodiscard]] static bool SetCapabilitiesOnCurrentThread( + const std::vector& caps); // Returns true if the current thread has either the effective, permitted, or // inheritable flag set for the given capability. @@ -88,7 +88,7 @@ class SANDBOX_EXPORT Credentials { // If this call succeeds, the current process will be granted a full set of // capabilities in the new namespace. // This will fail if the process is not mono-threaded. - static bool MoveToNewUserNS() WARN_UNUSED_RESULT; + [[nodiscard]] static bool MoveToNewUserNS(); // Removes the ability of the process to access the file system. File // descriptors which are already open prior to calling this API remain @@ -102,7 +102,7 @@ class SANDBOX_EXPORT Credentials { // - the caller must close |proc_fd| eventually or access to the file // system can be recovered. // - DropAllCapabilities() must be called to prevent escapes. - static bool DropFileSystemAccess(int proc_fd) WARN_UNUSED_RESULT; + [[nodiscard]] static bool DropFileSystemAccess(int proc_fd); // This function returns true if the process can still access the filesystem. static bool HasFileSystemAccess(); diff --git a/chromium/sandbox/linux/services/libc_interceptor.cc b/chromium/sandbox/linux/services/libc_interceptor.cc index cc2a52917f3..0a6e5fc6ea4 100644 --- a/chromium/sandbox/linux/services/libc_interceptor.cc +++ b/chromium/sandbox/linux/services/libc_interceptor.cc @@ -23,14 +23,12 @@ #include "base/compiler_specific.h" #include "base/lazy_instance.h" #include "base/logging.h" -#include "base/metrics/histogram_functions.h" #include "base/pickle.h" #include "base/posix/eintr_wrapper.h" #include "base/posix/global_descriptors.h" #include "base/posix/unix_domain_socket.h" #include "base/synchronization/lock.h" #include "base/time/time.h" -#include "base/timer/elapsed_timer.h" namespace sandbox { @@ -122,8 +120,6 @@ void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, char* timezone_out, size_t timezone_out_len) { - base::ElapsedTimer timer; - base::Pickle request; request.WriteInt(METHOD_LOCALTIME); request.WriteString( @@ -142,11 +138,6 @@ void ProxyLocaltimeCallToBrowser(time_t input, if (!ReadTimeStruct(&iter, output, timezone_out, timezone_out_len)) { memset(output, 0, sizeof(struct tm)); } - - base::UmaHistogramCustomMicrosecondsTimes( - "Linux.ProxyLocaltimeCallToBrowserUs", timer.Elapsed(), - base::Microseconds(1), base::Seconds(1), - /*buckets=*/50); } // The other side of this call is ProxyLocaltimeCallToBrowser(). diff --git a/chromium/sandbox/linux/services/namespace_utils.h b/chromium/sandbox/linux/services/namespace_utils.h index c2c3776b81c..398c8e0a98c 100644 --- a/chromium/sandbox/linux/services/namespace_utils.h +++ b/chromium/sandbox/linux/services/namespace_utils.h @@ -9,7 +9,6 @@ #include -#include "base/compiler_specific.h" #include "sandbox/sandbox_export.h" namespace sandbox { @@ -28,8 +27,8 @@ class SANDBOX_EXPORT NamespaceUtils { // Write a uid or gid mapping from |id| to |id| in |map_file|. This function // is async-signal-safe. - static bool WriteToIdMapFile(const char* map_file, - generic_id_t id) WARN_UNUSED_RESULT; + [[nodiscard]] static bool WriteToIdMapFile(const char* map_file, + generic_id_t id); // Returns true if unprivileged namespaces of type |type| is supported // (meaning that both CLONE_NEWUSER and type are are supported). |type| must @@ -47,7 +46,7 @@ class SANDBOX_EXPORT NamespaceUtils { // later, this is required in order to write to /proc/self/gid_map without // having CAP_SETGID. Callers can determine whether is this needed with // KernelSupportsDenySetgroups. This function is async-signal-safe. - static bool DenySetgroups() WARN_UNUSED_RESULT; + [[nodiscard]] static bool DenySetgroups(); }; } // namespace sandbox diff --git a/chromium/sandbox/linux/services/proc_util.h b/chromium/sandbox/linux/services/proc_util.h index 82052cc15b0..3687199908e 100644 --- a/chromium/sandbox/linux/services/proc_util.h +++ b/chromium/sandbox/linux/services/proc_util.h @@ -30,8 +30,8 @@ class SANDBOX_EXPORT ProcUtil { // /proc/. The file descriptor in |proc_fd| will be ignored by // HasOpenDirectory() and remains owned by the caller. It is very important // for the caller to close it. - static bool HasOpenDirectory(int proc_fd) WARN_UNUSED_RESULT; - static bool HasOpenDirectory() WARN_UNUSED_RESULT; + [[nodiscard]] static bool HasOpenDirectory(int proc_fd); + [[nodiscard]] static bool HasOpenDirectory(); // Open /proc/ or crash if not possible. static base::ScopedFD OpenProc(); diff --git a/chromium/sandbox/linux/services/resource_limits.h b/chromium/sandbox/linux/services/resource_limits.h index f35482441f0..af8a33e625d 100644 --- a/chromium/sandbox/linux/services/resource_limits.h +++ b/chromium/sandbox/linux/services/resource_limits.h @@ -7,7 +7,6 @@ #include -#include "base/compiler_specific.h" #include "sandbox/sandbox_export.h" namespace sandbox { @@ -21,21 +20,20 @@ class SANDBOX_EXPORT ResourceLimits { // Lower the soft and hard limit of |resource| to |limit|. If the current // limit is lower than |limit|, keep it. Returns 0 on success, or |errno|. - static int Lower(int resource, rlim_t limit) WARN_UNUSED_RESULT; + [[nodiscard]] static int Lower(int resource, rlim_t limit); // Lower the soft limit of |resource| to |limit| and the hard limit to |max|. // If the current limit is lower than the new values, keep it. Returns 0 on // success, or |errno|. - static int LowerSoftAndHardLimits(int resource, - rlim_t soft_limit, - rlim_t hard_limit) WARN_UNUSED_RESULT; + [[nodiscard]] static int LowerSoftAndHardLimits(int resource, + rlim_t soft_limit, + rlim_t hard_limit); // Change soft limit of |resource| to the current limit plus |change|. If // |resource + change| is larger than the hard limit, the soft limit is set to // be the same as the hard limit. Fails and returns false if the underlying // call to setrlimit fails. Returns 0 on success, or |errno|. - static int AdjustCurrent(int resource, - long long int change) WARN_UNUSED_RESULT; + [[nodiscard]] static int AdjustCurrent(int resource, long long int change); }; } // namespace sandbox diff --git a/chromium/sandbox/linux/services/resource_limits_unittest.cc b/chromium/sandbox/linux/services/resource_limits_unittest.cc new file mode 100644 index 00000000000..805411a1349 --- /dev/null +++ b/chromium/sandbox/linux/services/resource_limits_unittest.cc @@ -0,0 +1,44 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/linux/services/resource_limits.h" + +#include +#include +#include +#include + +#include "base/check_op.h" +#include "build/build_config.h" +#include "sandbox/linux/tests/test_utils.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +namespace { + +// Fails on Android: crbug.com/459158 +#if !BUILDFLAG(IS_ANDROID) +#define MAYBE_NoFork DISABLE_ON_ASAN(NoFork) +#else +#define MAYBE_NoFork DISABLED_NoFork +#endif // BUILDFLAG(IS_ANDROID) + +// Not being able to fork breaks LeakSanitizer, so disable on +// all ASAN builds. +SANDBOX_TEST(ResourceLimits, MAYBE_NoFork) { + // Make sure that fork will fail with EAGAIN. + SANDBOX_ASSERT(ResourceLimits::Lower(RLIMIT_NPROC, 0) == 0); + errno = 0; + pid_t pid = fork(); + // Reap any child if fork succeeded. + TestUtils::HandlePostForkReturn(pid); + SANDBOX_ASSERT_EQ(-1, pid); + CHECK_EQ(EAGAIN, errno); +} + +} // namespace + +} // namespace sandbox diff --git a/chromium/sandbox/linux/services/resource_limits_unittests.cc b/chromium/sandbox/linux/services/resource_limits_unittests.cc deleted file mode 100644 index b79404dbc49..00000000000 --- a/chromium/sandbox/linux/services/resource_limits_unittests.cc +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/services/resource_limits.h" - -#include -#include -#include -#include - -#include "base/check_op.h" -#include "sandbox/linux/tests/test_utils.h" -#include "sandbox/linux/tests/unit_tests.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -namespace { - -// Fails on Android: crbug.com/459158 -#if !defined(OS_ANDROID) -#define MAYBE_NoFork DISABLE_ON_ASAN(NoFork) -#else -#define MAYBE_NoFork DISABLED_NoFork -#endif // OS_ANDROID - -// Not being able to fork breaks LeakSanitizer, so disable on -// all ASAN builds. -SANDBOX_TEST(ResourceLimits, MAYBE_NoFork) { - // Make sure that fork will fail with EAGAIN. - SANDBOX_ASSERT(ResourceLimits::Lower(RLIMIT_NPROC, 0) == 0); - errno = 0; - pid_t pid = fork(); - // Reap any child if fork succeeded. - TestUtils::HandlePostForkReturn(pid); - SANDBOX_ASSERT_EQ(-1, pid); - CHECK_EQ(EAGAIN, errno); -} - -} // namespace - -} // namespace sandbox diff --git a/chromium/sandbox/linux/services/thread_helpers_unittest.cc b/chromium/sandbox/linux/services/thread_helpers_unittest.cc new file mode 100644 index 00000000000..4ba79f43d12 --- /dev/null +++ b/chromium/sandbox/linux/services/thread_helpers_unittest.cc @@ -0,0 +1,147 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "sandbox/linux/services/thread_helpers.h" + +#include +#include +#include +#include +#include + +#include "base/check_op.h" +#include "base/posix/eintr_wrapper.h" +#include "base/process/process_metrics.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread.h" +#include "build/build_config.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::PlatformThread; + +namespace sandbox { + +namespace { + +// These tests fail under ThreadSanitizer, see http://crbug.com/342305 +#if !defined(THREAD_SANITIZER) + +const int kRaceTestIterations = 1000; + +class ScopedProc { + public: + ScopedProc() : fd_(-1) { + fd_ = open("/proc/", O_RDONLY | O_DIRECTORY); + CHECK_LE(0, fd_); + } + + ScopedProc(const ScopedProc&) = delete; + ScopedProc& operator=(const ScopedProc&) = delete; + + ~ScopedProc() { PCHECK(0 == IGNORE_EINTR(close(fd_))); } + + int fd() { return fd_; } + + private: + int fd_; +}; + +TEST(ThreadHelpers, IsSingleThreadedBasic) { + ScopedProc proc_fd; + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded()); + + base::Thread thread("sandbox_tests"); + ASSERT_TRUE(ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread)); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded()); + // Explicitly stop the thread here to not pollute the next test. + ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread)); +} + +SANDBOX_TEST(ThreadHelpers, AssertSingleThreaded) { + ScopedProc proc_fd; + SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded()); + + ThreadHelpers::AssertSingleThreaded(proc_fd.fd()); + ThreadHelpers::AssertSingleThreaded(); +} + +TEST(ThreadHelpers, IsSingleThreadedIterated) { + ScopedProc proc_fd; + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + + // Iterate to check for race conditions. + for (int i = 0; i < kRaceTestIterations; ++i) { + base::Thread thread("sandbox_tests"); + ASSERT_TRUE( + ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread)); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + // Explicitly stop the thread here to not pollute the next test. + ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread)); + } +} + +TEST(ThreadHelpers, IsSingleThreadedStartAndStop) { + ScopedProc proc_fd; + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + + base::Thread thread("sandbox_tests"); + // This is testing for a race condition, so iterate. + // Manually, this has been tested with more that 1M iterations. + for (int i = 0; i < kRaceTestIterations; ++i) { + ASSERT_TRUE( + ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread)); + ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + + ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread)); + ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); + ASSERT_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle())); + } +} + +SANDBOX_TEST(ThreadHelpers, AssertSingleThreadedAfterThreadStopped) { + ScopedProc proc_fd; + SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded()); + + base::Thread thread1("sandbox_tests"); + base::Thread thread2("sandbox_tests"); + + for (int i = 0; i < kRaceTestIterations; ++i) { + SANDBOX_ASSERT( + ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread1)); + SANDBOX_ASSERT( + ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread2)); + SANDBOX_ASSERT(!ThreadHelpers::IsSingleThreaded()); + + thread1.Stop(); + thread2.Stop(); + // This will wait on /proc/ to reflect the state of threads in the + // process. + ThreadHelpers::AssertSingleThreaded(); + SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded()); + } +} + +// Only run this test in Debug mode, where AssertSingleThreaded() will return +// in less than 64ms. +#if !defined(NDEBUG) +SANDBOX_DEATH_TEST( + ThreadHelpers, + AssertSingleThreadedDies, + DEATH_MESSAGE( + ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests())) { + base::Thread thread1("sandbox_tests"); + SANDBOX_ASSERT(thread1.Start()); + ThreadHelpers::AssertSingleThreaded(); +} +#endif // !defined(NDEBUG) + +#endif // !defined(THREAD_SANITIZER) + +} // namespace + +} // namespace sandbox diff --git a/chromium/sandbox/linux/services/thread_helpers_unittests.cc b/chromium/sandbox/linux/services/thread_helpers_unittests.cc deleted file mode 100644 index 4ba79f43d12..00000000000 --- a/chromium/sandbox/linux/services/thread_helpers_unittests.cc +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/linux/services/thread_helpers.h" - -#include -#include -#include -#include -#include - -#include "base/check_op.h" -#include "base/posix/eintr_wrapper.h" -#include "base/process/process_metrics.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "build/build_config.h" -#include "sandbox/linux/tests/unit_tests.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::PlatformThread; - -namespace sandbox { - -namespace { - -// These tests fail under ThreadSanitizer, see http://crbug.com/342305 -#if !defined(THREAD_SANITIZER) - -const int kRaceTestIterations = 1000; - -class ScopedProc { - public: - ScopedProc() : fd_(-1) { - fd_ = open("/proc/", O_RDONLY | O_DIRECTORY); - CHECK_LE(0, fd_); - } - - ScopedProc(const ScopedProc&) = delete; - ScopedProc& operator=(const ScopedProc&) = delete; - - ~ScopedProc() { PCHECK(0 == IGNORE_EINTR(close(fd_))); } - - int fd() { return fd_; } - - private: - int fd_; -}; - -TEST(ThreadHelpers, IsSingleThreadedBasic) { - ScopedProc proc_fd; - ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - ASSERT_TRUE(ThreadHelpers::IsSingleThreaded()); - - base::Thread thread("sandbox_tests"); - ASSERT_TRUE(ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread)); - ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - ASSERT_FALSE(ThreadHelpers::IsSingleThreaded()); - // Explicitly stop the thread here to not pollute the next test. - ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread)); -} - -SANDBOX_TEST(ThreadHelpers, AssertSingleThreaded) { - ScopedProc proc_fd; - SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded()); - - ThreadHelpers::AssertSingleThreaded(proc_fd.fd()); - ThreadHelpers::AssertSingleThreaded(); -} - -TEST(ThreadHelpers, IsSingleThreadedIterated) { - ScopedProc proc_fd; - ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - - // Iterate to check for race conditions. - for (int i = 0; i < kRaceTestIterations; ++i) { - base::Thread thread("sandbox_tests"); - ASSERT_TRUE( - ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread)); - ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - // Explicitly stop the thread here to not pollute the next test. - ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread)); - } -} - -TEST(ThreadHelpers, IsSingleThreadedStartAndStop) { - ScopedProc proc_fd; - ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - - base::Thread thread("sandbox_tests"); - // This is testing for a race condition, so iterate. - // Manually, this has been tested with more that 1M iterations. - for (int i = 0; i < kRaceTestIterations; ++i) { - ASSERT_TRUE( - ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread)); - ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - - ASSERT_TRUE(ThreadHelpers::StopThreadAndWatchProcFS(proc_fd.fd(), &thread)); - ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd())); - ASSERT_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle())); - } -} - -SANDBOX_TEST(ThreadHelpers, AssertSingleThreadedAfterThreadStopped) { - ScopedProc proc_fd; - SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded()); - - base::Thread thread1("sandbox_tests"); - base::Thread thread2("sandbox_tests"); - - for (int i = 0; i < kRaceTestIterations; ++i) { - SANDBOX_ASSERT( - ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread1)); - SANDBOX_ASSERT( - ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread2)); - SANDBOX_ASSERT(!ThreadHelpers::IsSingleThreaded()); - - thread1.Stop(); - thread2.Stop(); - // This will wait on /proc/ to reflect the state of threads in the - // process. - ThreadHelpers::AssertSingleThreaded(); - SANDBOX_ASSERT(ThreadHelpers::IsSingleThreaded()); - } -} - -// Only run this test in Debug mode, where AssertSingleThreaded() will return -// in less than 64ms. -#if !defined(NDEBUG) -SANDBOX_DEATH_TEST( - ThreadHelpers, - AssertSingleThreadedDies, - DEATH_MESSAGE( - ThreadHelpers::GetAssertSingleThreadedErrorMessageForTests())) { - base::Thread thread1("sandbox_tests"); - SANDBOX_ASSERT(thread1.Start()); - ThreadHelpers::AssertSingleThreaded(); -} -#endif // !defined(NDEBUG) - -#endif // !defined(THREAD_SANITIZER) - -} // namespace - -} // namespace sandbox diff --git a/chromium/sandbox/linux/services/yama_unittest.cc b/chromium/sandbox/linux/services/yama_unittest.cc new file mode 100644 index 00000000000..79038574c6e --- /dev/null +++ b/chromium/sandbox/linux/services/yama_unittest.cc @@ -0,0 +1,170 @@ +// 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 +#include +#include +#include +#include +#include + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/compiler_specific.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_util.h" +#include "base/system/sys_info.h" +#include "sandbox/linux/services/scoped_process.h" +#include "sandbox/linux/services/yama.h" +#include "sandbox/linux/tests/unit_tests.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +namespace { + +bool HasLinux32Bug() { +#if defined(__i386__) + // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels. + // This is fixed in 3.4. + bool is_kernel_64bit = + base::SysInfo::OperatingSystemArchitecture() == "x86_64"; + bool is_linux = base::SysInfo::OperatingSystemName() == "Linux"; + bool is_3_dot_2 = + base::StartsWith(base::SysInfo::OperatingSystemVersion(), "3.2", + base::CompareCase::INSENSITIVE_ASCII); + if (is_kernel_64bit && is_linux && is_3_dot_2) + return true; +#endif // defined(__i386__) + return false; +} + +bool CanPtrace(pid_t pid) { + int ret; + ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL); + if (ret == -1) { + CHECK_EQ(EPERM, errno); + return false; + } + // Wait for the process to be stopped so that it can be detached. + siginfo_t process_info; + int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED)); + PCHECK(0 == wait_ret); + PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL)); + return true; +} + +// _exit(0) if pid can be ptraced by the current process. +// _exit(1) otherwise. +void ExitZeroIfCanPtrace(pid_t pid) { + if (CanPtrace(pid)) { + _exit(0); + } else { + _exit(1); + } +} + +bool CanSubProcessPtrace(pid_t pid) { + ScopedProcess process(base::BindOnce(&ExitZeroIfCanPtrace, pid)); + bool signaled; + int exit_code = process.WaitForExit(&signaled); + CHECK(!signaled); + return 0 == exit_code; +} + +// The tests below assume that the system-level configuration will not change +// while they run. + +TEST(Yama, GetStatus) { + int status1 = Yama::GetStatus(); + + // Check that the value is a possible bitmask. + ASSERT_LE(0, status1); + ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING | + Yama::STATUS_STRICT_ENFORCING, + status1); + + // The status should not just be a random value. + int status2 = Yama::GetStatus(); + EXPECT_EQ(status1, status2); + + // This test is not running sandboxed, there is no reason to not know the + // status. + EXPECT_NE(0, Yama::STATUS_KNOWN & status1); + + if (status1 & Yama::STATUS_STRICT_ENFORCING) { + // If Yama is strictly enforcing, it is also enforcing. + EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING); + } + + if (status1 & Yama::STATUS_ENFORCING) { + // If Yama is enforcing, Yama is present. + EXPECT_NE(0, status1 & Yama::STATUS_PRESENT); + } + + // Verify that the helper functions work as intended. + EXPECT_EQ(static_cast(status1 & Yama::STATUS_ENFORCING), + Yama::IsEnforcing()); + EXPECT_EQ(static_cast(status1 & Yama::STATUS_PRESENT), + Yama::IsPresent()); + + fprintf(stdout, "Yama present: %s - enforcing: %s\n", + Yama::IsPresent() ? "Y" : "N", Yama::IsEnforcing() ? "Y" : "N"); +} + +SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) { + // This call will succeed iff Yama is present. + bool restricted = Yama::RestrictPtracersToAncestors(); + CHECK_EQ(restricted, Yama::IsPresent()); +} + +// Attempts to enable or disable Yama restrictions. +void SetYamaRestrictions(bool enable_restriction) { + if (enable_restriction) { + Yama::RestrictPtracersToAncestors(); + } else { + Yama::DisableYamaRestrictions(); + } +} + +TEST(Yama, RestrictPtraceWorks) { + if (HasLinux32Bug()) + return; + + ScopedProcess process1(base::BindOnce(&SetYamaRestrictions, true)); + ASSERT_TRUE(process1.WaitForClosureToRun()); + + if (Yama::IsEnforcing()) { + // A sibling process cannot ptrace process1. + ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid())); + } + + if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) { + // However, parent can ptrace process1. + ASSERT_TRUE(CanPtrace(process1.GetPid())); + + // A sibling can ptrace process2 which disables any Yama protection. + ScopedProcess process2(base::BindOnce(&SetYamaRestrictions, false)); + ASSERT_TRUE(process2.WaitForClosureToRun()); + ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid())); + } +} + +SANDBOX_TEST(Yama, RestrictPtraceIsDefault) { + if (!Yama::IsPresent() || HasLinux32Bug()) + return; + + CHECK(Yama::DisableYamaRestrictions()); + ScopedProcess process1{base::DoNothing()}; + + if (Yama::IsEnforcing()) { + // Check that process1 is protected by Yama, even though it has + // been created from a process that disabled Yama. + CHECK(!CanSubProcessPtrace(process1.GetPid())); + } +} + +} // namespace + +} // namespace sandbox diff --git a/chromium/sandbox/linux/services/yama_unittests.cc b/chromium/sandbox/linux/services/yama_unittests.cc deleted file mode 100644 index 9f576abe2c1..00000000000 --- a/chromium/sandbox/linux/services/yama_unittests.cc +++ /dev/null @@ -1,172 +0,0 @@ -// 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 -#include -#include -#include -#include -#include - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/compiler_specific.h" -#include "base/posix/eintr_wrapper.h" -#include "base/strings/string_util.h" -#include "base/system/sys_info.h" -#include "sandbox/linux/services/scoped_process.h" -#include "sandbox/linux/services/yama.h" -#include "sandbox/linux/tests/unit_tests.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sandbox { - -namespace { - -bool HasLinux32Bug() { -#if defined(__i386__) - // On 3.2 kernels, yama doesn't work for 32-bit binaries on 64-bit kernels. - // This is fixed in 3.4. - bool is_kernel_64bit = - base::SysInfo::OperatingSystemArchitecture() == "x86_64"; - bool is_linux = base::SysInfo::OperatingSystemName() == "Linux"; - bool is_3_dot_2 = base::StartsWith( - base::SysInfo::OperatingSystemVersion(), "3.2", - base::CompareCase::INSENSITIVE_ASCII); - if (is_kernel_64bit && is_linux && is_3_dot_2) - return true; -#endif // defined(__i386__) - return false; -} - -bool CanPtrace(pid_t pid) { - int ret; - ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL); - if (ret == -1) { - CHECK_EQ(EPERM, errno); - return false; - } - // Wait for the process to be stopped so that it can be detached. - siginfo_t process_info; - int wait_ret = HANDLE_EINTR(waitid(P_PID, pid, &process_info, WSTOPPED)); - PCHECK(0 == wait_ret); - PCHECK(0 == ptrace(PTRACE_DETACH, pid, NULL, NULL)); - return true; -} - -// _exit(0) if pid can be ptraced by the current process. -// _exit(1) otherwise. -void ExitZeroIfCanPtrace(pid_t pid) { - if (CanPtrace(pid)) { - _exit(0); - } else { - _exit(1); - } -} - -bool CanSubProcessPtrace(pid_t pid) { - ScopedProcess process(base::BindOnce(&ExitZeroIfCanPtrace, pid)); - bool signaled; - int exit_code = process.WaitForExit(&signaled); - CHECK(!signaled); - return 0 == exit_code; -} - -// The tests below assume that the system-level configuration will not change -// while they run. - -TEST(Yama, GetStatus) { - int status1 = Yama::GetStatus(); - - // Check that the value is a possible bitmask. - ASSERT_LE(0, status1); - ASSERT_GE(Yama::STATUS_KNOWN | Yama::STATUS_PRESENT | Yama::STATUS_ENFORCING | - Yama::STATUS_STRICT_ENFORCING, - status1); - - // The status should not just be a random value. - int status2 = Yama::GetStatus(); - EXPECT_EQ(status1, status2); - - // This test is not running sandboxed, there is no reason to not know the - // status. - EXPECT_NE(0, Yama::STATUS_KNOWN & status1); - - if (status1 & Yama::STATUS_STRICT_ENFORCING) { - // If Yama is strictly enforcing, it is also enforcing. - EXPECT_TRUE(status1 & Yama::STATUS_ENFORCING); - } - - if (status1 & Yama::STATUS_ENFORCING) { - // If Yama is enforcing, Yama is present. - EXPECT_NE(0, status1 & Yama::STATUS_PRESENT); - } - - // Verify that the helper functions work as intended. - EXPECT_EQ(static_cast(status1 & Yama::STATUS_ENFORCING), - Yama::IsEnforcing()); - EXPECT_EQ(static_cast(status1 & Yama::STATUS_PRESENT), - Yama::IsPresent()); - - fprintf(stdout, - "Yama present: %s - enforcing: %s\n", - Yama::IsPresent() ? "Y" : "N", - Yama::IsEnforcing() ? "Y" : "N"); -} - -SANDBOX_TEST(Yama, RestrictPtraceSucceedsWhenYamaPresent) { - // This call will succeed iff Yama is present. - bool restricted = Yama::RestrictPtracersToAncestors(); - CHECK_EQ(restricted, Yama::IsPresent()); -} - -// Attempts to enable or disable Yama restrictions. -void SetYamaRestrictions(bool enable_restriction) { - if (enable_restriction) { - Yama::RestrictPtracersToAncestors(); - } else { - Yama::DisableYamaRestrictions(); - } -} - -TEST(Yama, RestrictPtraceWorks) { - if (HasLinux32Bug()) - return; - - ScopedProcess process1(base::BindOnce(&SetYamaRestrictions, true)); - ASSERT_TRUE(process1.WaitForClosureToRun()); - - if (Yama::IsEnforcing()) { - // A sibling process cannot ptrace process1. - ASSERT_FALSE(CanSubProcessPtrace(process1.GetPid())); - } - - if (!(Yama::GetStatus() & Yama::STATUS_STRICT_ENFORCING)) { - // However, parent can ptrace process1. - ASSERT_TRUE(CanPtrace(process1.GetPid())); - - // A sibling can ptrace process2 which disables any Yama protection. - ScopedProcess process2(base::BindOnce(&SetYamaRestrictions, false)); - ASSERT_TRUE(process2.WaitForClosureToRun()); - ASSERT_TRUE(CanSubProcessPtrace(process2.GetPid())); - } -} - -SANDBOX_TEST(Yama, RestrictPtraceIsDefault) { - if (!Yama::IsPresent() || HasLinux32Bug()) - return; - - CHECK(Yama::DisableYamaRestrictions()); - ScopedProcess process1{base::DoNothing()}; - - if (Yama::IsEnforcing()) { - // Check that process1 is protected by Yama, even though it has - // been created from a process that disabled Yama. - CHECK(!CanSubProcessPtrace(process1.GetPid())); - } -} - -} // namespace - -} // namespace sandbox diff --git a/chromium/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc b/chromium/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc index c33a1ed9a13..728a2320c50 100644 --- a/chromium/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc +++ b/chromium/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc @@ -6,9 +6,9 @@ #include #include +#include #include "base/environment.h" -#include "base/ignore_result.h" #include "base/strings/string_number_conversions.h" #include "sandbox/linux/suid/common/sandbox.h" #include "testing/gtest/include/gtest/gtest.h" @@ -66,7 +66,7 @@ TEST(SetuidSandboxHost, SetupLaunchEnvironment) { TEST(SetuidSandboxHost, GetSandboxBinaryPath) { std::unique_ptr setuid_sandbox_host( SetuidSandboxHost::Create()); - ignore_result(setuid_sandbox_host->GetSandboxBinaryPath()); + std::ignore = setuid_sandbox_host->GetSandboxBinaryPath(); } } // namespace sandbox diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.cc b/chromium/sandbox/linux/syscall_broker/broker_client.cc index 0147524dc2b..17a85131123 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_client.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_client.cc @@ -22,7 +22,7 @@ #include "sandbox/linux/syscall_broker/broker_permission_list.h" #include "sandbox/linux/syscall_broker/broker_simple_message.h" -#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC) +#if BUILDFLAG(IS_ANDROID) && !defined(MSG_CMSG_CLOEXEC) #define MSG_CMSG_CLOEXEC 0x40000000 #endif diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc index 51e1e9d59c9..7dd4688011f 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_process.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc @@ -117,44 +117,44 @@ bool BrokerProcess::IsSyscallBrokerable(int sysno, bool fast_check) const { // and are default disabled in Android. So, we should refuse to broker them // to be consistent with the platform's restrictions. switch (sysno) { -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_access: #endif case __NR_faccessat: case __NR_faccessat2: return !fast_check || policy_->allowed_command_set.test(COMMAND_ACCESS); -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_mkdir: #endif case __NR_mkdirat: return !fast_check || policy_->allowed_command_set.test(COMMAND_MKDIR); -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_open: #endif case __NR_openat: return !fast_check || policy_->allowed_command_set.test(COMMAND_OPEN); -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_readlink: #endif case __NR_readlinkat: return !fast_check || policy_->allowed_command_set.test(COMMAND_READLINK); -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_rename: #endif case __NR_renameat: case __NR_renameat2: return !fast_check || policy_->allowed_command_set.test(COMMAND_RENAME); -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_rmdir: return !fast_check || policy_->allowed_command_set.test(COMMAND_RMDIR); #endif -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_stat: case __NR_lstat: #endif @@ -179,7 +179,7 @@ bool BrokerProcess::IsSyscallBrokerable(int sysno, bool fast_check) const { return !fast_check || policy_->allowed_command_set.test(COMMAND_STAT); #endif -#if !defined(__aarch64__) && !defined(OS_ANDROID) +#if !defined(__aarch64__) && !BUILDFLAG(IS_ANDROID) case __NR_unlink: return !fast_check || policy_->allowed_command_set.test(COMMAND_UNLINK); #endif diff --git a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc index 310d705fa24..ad6e502e612 100644 --- a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc +++ b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc @@ -586,7 +586,7 @@ TEST(BrokerProcess, OpenComplexFlagsNoClientCheck) { // expected. } -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // Flaky on Linux NG bots: https://crbug.com/595199. #define MAYBE_RecvMsgDescriptorLeak DISABLED_RecvMsgDescriptorLeak #else @@ -1752,54 +1752,53 @@ TEST(BrokerProcess, UnlinkHost) { TEST(BrokerProcess, IsSyscallAllowed) { const base::flat_map> kSysnosForCommand = { {COMMAND_ACCESS, - {__NR_faccessat, - __NR_faccessat2, -#if defined(__NR_access) && !defined(OS_ANDROID) + {__NR_faccessat, __NR_faccessat2, +#if defined(__NR_access) && !BUILDFLAG(IS_ANDROID) __NR_access #endif }}, {COMMAND_MKDIR, {__NR_mkdirat, -#if defined(__NR_mkdir) && !defined(OS_ANDROID) +#if defined(__NR_mkdir) && !BUILDFLAG(IS_ANDROID) __NR_mkdir #endif }}, {COMMAND_OPEN, {__NR_openat, -#if defined(__NR_open) && !defined(OS_ANDROID) +#if defined(__NR_open) && !BUILDFLAG(IS_ANDROID) __NR_open #endif }}, {COMMAND_READLINK, {__NR_readlinkat, -#if defined(__NR_readlink) && !defined(OS_ANDROID) +#if defined(__NR_readlink) && !BUILDFLAG(IS_ANDROID) __NR_readlink #endif }}, {COMMAND_RENAME, {__NR_renameat, -#if defined(__NR_rename) && !defined(OS_ANDROID) +#if defined(__NR_rename) && !BUILDFLAG(IS_ANDROID) __NR_rename #endif }}, {COMMAND_UNLINK, {__NR_unlinkat, -#if defined(__NR_unlink) && !defined(OS_ANDROID) +#if defined(__NR_unlink) && !BUILDFLAG(IS_ANDROID) __NR_unlink #endif }}, {COMMAND_RMDIR, {__NR_unlinkat, -#if defined(__NR_rmdir) && !defined(OS_ANDROID) +#if defined(__NR_rmdir) && !BUILDFLAG(IS_ANDROID) __NR_rmdir #endif }}, {COMMAND_STAT, { -#if defined(__NR_stat) && !defined(OS_ANDROID) +#if defined(__NR_stat) && !BUILDFLAG(IS_ANDROID) __NR_stat, #endif -#if defined(__NR_lstat) && !defined(OS_ANDROID) +#if defined(__NR_lstat) && !BUILDFLAG(IS_ANDROID) __NR_lstat, #endif #if defined(__NR_fstatat) diff --git a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc index 2e7f360a473..2c9492c513c 100644 --- a/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc +++ b/chromium/sandbox/linux/syscall_broker/remote_syscall_arg_handler_unittest.cc @@ -6,13 +6,14 @@ #include #include + #include #include +#include #include "base/bind.h" #include "base/callback_helpers.h" #include "base/files/scoped_file.h" -#include "base/ignore_result.h" #include "base/memory/page_size.h" #include "base/posix/unix_domain_socket.h" #include "base/test/bind.h" @@ -76,7 +77,7 @@ pid_t ForkWaitingChild(base::OnceCallback if (parent_sync_fd) *parent_sync_fd = std::move(parent_sync); else - ignore_result(parent_sync.release()); // Closes when parent dies. + std::ignore = parent_sync.release(); // Closes when parent dies. return pid; } diff --git a/chromium/sandbox/linux/system_headers/linux_prctl.h b/chromium/sandbox/linux/system_headers/linux_prctl.h index 50f639b0fef..b2b4baeeb8a 100644 --- a/chromium/sandbox/linux/system_headers/linux_prctl.h +++ b/chromium/sandbox/linux/system_headers/linux_prctl.h @@ -15,14 +15,14 @@ #define PR_SET_TIMERSLACK 29 #endif -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) // https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/private/bionic_prctl.h #if !defined(PR_SET_VMA) #define PR_SET_VMA 0x53564d41 #endif -#endif // defined(OS_ANDROID) +#endif // BUILDFLAG(IS_ANDROID) #if !defined(PR_SET_PTRACER) #define PR_SET_PTRACER 0x59616d61 diff --git a/chromium/sandbox/mac/seatbelt_exec.h b/chromium/sandbox/mac/seatbelt_exec.h index ca9249f356f..fa711bd1201 100644 --- a/chromium/sandbox/mac/seatbelt_exec.h +++ b/chromium/sandbox/mac/seatbelt_exec.h @@ -7,7 +7,6 @@ #include -#include "base/compiler_specific.h" //nogncheck #include "sandbox/mac/seatbelt.pb.h" #include "sandbox/mac/seatbelt_export.h" @@ -39,12 +38,11 @@ class SEATBELT_EXPORT SeatbeltExecClient { // added successfully. // Set a boolean parameter in the sandbox profile. - bool SetBooleanParameter(const std::string& key, - bool value) WARN_UNUSED_RESULT; + [[nodiscard]] bool SetBooleanParameter(const std::string& key, bool value); // Set a string parameter in the sandbox profile. - bool SetParameter(const std::string& key, - const std::string& value) WARN_UNUSED_RESULT; + [[nodiscard]] bool SetParameter(const std::string& key, + const std::string& value); // Set the actual sandbox profile, using the scheme-like SBPL. void SetProfile(const std::string& policy); @@ -114,8 +112,8 @@ class SEATBELT_EXPORT SeatbeltExecServer { // server because the process about to initialize a sandbox may need to add // some extra parameters, such as the path to the executable or the current // PID. This must be called before InitializeSandbox(). - bool SetParameter(const std::string& key, - const std::string& value) WARN_UNUSED_RESULT; + [[nodiscard]] bool SetParameter(const std::string& key, + const std::string& value); private: // Reads from the |fd_| and stores the data into a string. This does diff --git a/chromium/sandbox/policy/BUILD.gn b/chromium/sandbox/policy/BUILD.gn index 7ed3004ca77..b412c8db4d2 100644 --- a/chromium/sandbox/policy/BUILD.gn +++ b/chromium/sandbox/policy/BUILD.gn @@ -145,6 +145,7 @@ component("policy") { ] deps += [ + "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.buildinfo", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.fonts", "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.intl", diff --git a/chromium/sandbox/policy/features.cc b/chromium/sandbox/policy/features.cc index 1a19cdd89d8..f89d9ca72b2 100644 --- a/chromium/sandbox/policy/features.cc +++ b/chromium/sandbox/policy/features.cc @@ -12,14 +12,14 @@ namespace sandbox { namespace policy { namespace features { -#if !defined(OS_MAC) && !defined(OS_FUCHSIA) +#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA) // Enables network service sandbox. // (Only causes an effect when feature kNetworkService is enabled.) const base::Feature kNetworkServiceSandbox{"NetworkServiceSandbox", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // !defined(OS_MAC) && !defined(OS_FUCHSIA) +#endif // !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Emergency "off switch" for new Windows KTM security mitigation, // sandbox::MITIGATION_KTM_COMPONENT. const base::Feature kWinSboxDisableKtmComponent{ @@ -41,12 +41,12 @@ const base::Feature kGpuLPAC{"GpuLPAC", base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kRendererAppContainer{"RendererAppContainer", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) -#if !defined(OS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) // Controls whether the isolated XR service is sandboxed. const base::Feature kXRSandbox{"XRSandbox", base::FEATURE_ENABLED_BY_DEFAULT}; -#endif // !defined(OS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_CHROMEOS_ASH) // Controls whether the Spectre variant 2 mitigation is enabled. We use a USE @@ -62,7 +62,7 @@ const base::Feature kForceSpectreVariant2Mitigation{ "ForceSpectreVariant2Mitigation", base::FEATURE_DISABLED_BY_DEFAULT}; #endif // BUILDFLAG(IS_CHROMEOS_ASH) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) bool IsWinNetworkServiceSandboxSupported() { // Since some APIs used for LPAC are unsupported below Windows 10 RS2 (1703 // build 15063) so place a check here in a central place. @@ -70,19 +70,19 @@ bool IsWinNetworkServiceSandboxSupported() { return false; return true; } -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) bool IsNetworkSandboxEnabled() { -#if defined(OS_MAC) || defined(OS_FUCHSIA) +#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_FUCHSIA) return true; #else -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) if (!IsWinNetworkServiceSandboxSupported()) return false; -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) // Check feature status. return base::FeatureList::IsEnabled(kNetworkServiceSandbox); -#endif // defined(OS_MAC) || defined(OS_FUCHSIA) +#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_FUCHSIA) } } // namespace features diff --git a/chromium/sandbox/policy/features.h b/chromium/sandbox/policy/features.h index 34f4bb517d1..a9c557e2599 100644 --- a/chromium/sandbox/policy/features.h +++ b/chromium/sandbox/policy/features.h @@ -17,21 +17,21 @@ namespace sandbox { namespace policy { namespace features { -#if !defined(OS_MAC) && !defined(OS_FUCHSIA) +#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_FUCHSIA) SANDBOX_POLICY_EXPORT extern const base::Feature kNetworkServiceSandbox; #endif -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableKtmComponent; SANDBOX_POLICY_EXPORT extern const base::Feature kWinSboxDisableExtensionPoints; SANDBOX_POLICY_EXPORT extern const base::Feature kGpuAppContainer; SANDBOX_POLICY_EXPORT extern const base::Feature kGpuLPAC; SANDBOX_POLICY_EXPORT extern const base::Feature kRendererAppContainer; -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) -#if !defined(OS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) SANDBOX_POLICY_EXPORT extern const base::Feature kXRSandbox; -#endif // !defined(OS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) #if BUILDFLAG(IS_CHROMEOS_ASH) SANDBOX_POLICY_EXPORT extern const base::Feature kSpectreVariant2Mitigation; @@ -39,7 +39,7 @@ SANDBOX_POLICY_EXPORT extern const base::Feature kForceSpectreVariant2Mitigation; #endif // BUILDFLAG(IS_CHROMEOS_ASH) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Returns whether the Network Service Sandbox is supported by the current // Windows platform. Call this function rather than checking the // kNetworkServiceSandbox feature directly. diff --git a/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc b/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc index b511e3cde13..9772736124b 100644 --- a/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc +++ b/chromium/sandbox/policy/fuchsia/sandbox_policy_fuchsia.cc @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -66,6 +67,30 @@ struct SandboxConfig { uint32_t features; }; +// Services that are passed to all processes. +// Prevent incorrect indentation due to the preprocessor lines within `({...})`: +// clang-format off +constexpr auto kMinimalServices = base::make_span((const char* const[]){ + // TODO(crbug.com/1286960): Remove this and/or intl below if an alternative + // solution does not require access to the service in all processes. + fuchsia::buildinfo::Provider::Name_, + +// DebugData service is needed only for profiling. +#if BUILDFLAG(CLANG_PROFILING) + "fuchsia.debugdata.DebugData", +#endif + + fuchsia::intl::PropertyProvider::Name_, + fuchsia::logger::LogSink::Name_, +}); +// clang-format on + +// For processes that only get kMinimalServices and no other capabilities. +constexpr SandboxConfig kMinimalConfig = { + base::span(), + 0, +}; + constexpr SandboxConfig kGpuConfig = { base::make_span((const char* const[]){ // TODO(crbug.com/1224707): Use the fuchsia.scheduler API instead. @@ -109,10 +134,9 @@ constexpr SandboxConfig kVideoCaptureConfig = { 0, }; -// No-access-to-anything. -constexpr SandboxConfig kEmptySandboxConfig = { +constexpr SandboxConfig kServiceWithJitConfig = { base::span(), - 0, + kAmbientMarkVmoAsExecutable, }; const SandboxConfig* GetConfigForSandboxType(sandbox::mojom::Sandbox type) { @@ -127,6 +151,8 @@ const SandboxConfig* GetConfigForSandboxType(sandbox::mojom::Sandbox type) { return &kRendererConfig; case sandbox::mojom::Sandbox::kVideoCapture: return &kVideoCaptureConfig; + case sandbox::mojom::Sandbox::kServiceWithJit: + return &kServiceWithJitConfig; // Remaining types receive no-access-to-anything. case sandbox::mojom::Sandbox::kAudio: case sandbox::mojom::Sandbox::kCdm: @@ -134,19 +160,10 @@ const SandboxConfig* GetConfigForSandboxType(sandbox::mojom::Sandbox type) { case sandbox::mojom::Sandbox::kService: case sandbox::mojom::Sandbox::kSpeechRecognition: case sandbox::mojom::Sandbox::kUtility: - return &kEmptySandboxConfig; + return &kMinimalConfig; } } -// Services that are passed to all processes. -constexpr auto kDefaultServices = base::make_span((const char* const[]) { -// DebugData service is needed only for profiling. -#if BUILDFLAG(CLANG_PROFILING) - "fuchsia.debugdata.DebugData", -#endif - fuchsia::intl::PropertyProvider::Name_, fuchsia::logger::LogSink::Name_ -}); - } // namespace SandboxPolicyFuchsia::SandboxPolicyFuchsia(sandbox::mojom::Sandbox type) { @@ -165,7 +182,7 @@ SandboxPolicyFuchsia::SandboxPolicyFuchsia(sandbox::mojom::Sandbox type) { service_directory_task_runner_ = base::ThreadTaskRunnerHandle::Get(); service_directory_ = std::make_unique( base::ComponentContextForProcess()->svc().get()); - for (const char* service_name : kDefaultServices) { + for (const char* service_name : kMinimalServices) { zx_status_t status = service_directory_->AddService(service_name); ZX_CHECK(status == ZX_OK, status) << "AddService(" << service_name << ") failed"; diff --git a/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc b/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc index 48b16aba593..f8df9dcbd93 100644 --- a/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc +++ b/chromium/sandbox/policy/linux/bpf_gpu_policy_linux.cc @@ -102,7 +102,7 @@ ResultExpr GpuProcessPolicy::EvaluateSyscall(int sysno) const { break; } -#if defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS) if (SyscallSets::IsSystemVSharedMemory(sysno)) return Allow(); #endif diff --git a/chromium/sandbox/policy/linux/sandbox_debug_handling_linux.cc b/chromium/sandbox/policy/linux/sandbox_debug_handling_linux.cc index a1fcded8bfa..e854ef675a0 100644 --- a/chromium/sandbox/policy/linux/sandbox_debug_handling_linux.cc +++ b/chromium/sandbox/policy/linux/sandbox_debug_handling_linux.cc @@ -10,8 +10,9 @@ #include #include +#include + #include "base/command_line.h" -#include "base/ignore_result.h" #include "base/logging.h" #include "base/strings/safe_sprintf.h" #include "sandbox/policy/switches.h" @@ -24,7 +25,7 @@ namespace { void DoChrootSignalHandler(int) { const int old_errno = errno; const char kFirstMessage[] = "Chroot signal handler called.\n"; - ignore_result(write(STDERR_FILENO, kFirstMessage, sizeof(kFirstMessage) - 1)); + std::ignore = write(STDERR_FILENO, kFirstMessage, sizeof(kFirstMessage) - 1); const int chroot_ret = chroot("/"); @@ -33,7 +34,7 @@ void DoChrootSignalHandler(int) { kSecondMessage, "chroot() returned %d. Errno is %d.\n", chroot_ret, errno); if (printed > 0 && printed < static_cast(sizeof(kSecondMessage))) { - ignore_result(write(STDERR_FILENO, kSecondMessage, printed)); + std::ignore = write(STDERR_FILENO, kSecondMessage, printed); } errno = old_errno; } diff --git a/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc b/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc index 75ffefc2a72..62c4551fe40 100644 --- a/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc +++ b/chromium/sandbox/policy/linux/sandbox_seccomp_bpf_linux.cc @@ -186,6 +186,8 @@ std::unique_ptr SandboxSeccompBPF::PolicyForSandboxType( return std::make_unique(); case sandbox::mojom::Sandbox::kService: return std::make_unique(); + case sandbox::mojom::Sandbox::kServiceWithJit: + return std::make_unique(); case sandbox::mojom::Sandbox::kSpeechRecognition: return std::make_unique(); #if BUILDFLAG(IS_CHROMEOS_ASH) @@ -259,6 +261,7 @@ void SandboxSeccompBPF::RunSandboxSanityChecks( #endif // BUILDFLAG(IS_CHROMEOS_ASH) case sandbox::mojom::Sandbox::kAudio: case sandbox::mojom::Sandbox::kService: + case sandbox::mojom::Sandbox::kServiceWithJit: case sandbox::mojom::Sandbox::kSpeechRecognition: case sandbox::mojom::Sandbox::kNetwork: #if BUILDFLAG(ENABLE_OOP_PRINTING) diff --git a/chromium/sandbox/policy/mac/common.sb b/chromium/sandbox/policy/mac/common.sb index 84c7e99b329..1c5319884c0 100644 --- a/chromium/sandbox/policy/mac/common.sb +++ b/chromium/sandbox/policy/mac/common.sb @@ -111,6 +111,40 @@ ; All processes can read the bundle contents. (allow file-read* (subpath (param bundle-path))) +; A sandbox bug on macOS 11 and 12 causes the sandbox to see any paths within +; the data volume (/System/Volumes/Data) outside of system firm link locations +; (listed in /usr/share/firmlinks) as though they were not on the data volume, +; causing it to deny access to user paths containing the data volume mount point +; as a prefix. This is filed as https://openradar.appspot.com/FB9738355 and +; tracked at https://crbug.com/1266490. Although macOS 10.15 also has the root +; volume split, this bug does not appear to affect that OS version. +; +; When the bundle path appears in the data volume, this causes the sandbox to +; deny access to the bundle. +; +; This is not a problem in normal use, as typical bundle paths, while on the +; data volume, will be in system firm link locations such as /Applications or +; /Users. As a workaround for other cases where the bundle may be present on the +; data volume but not in a system firm link location, configure the sandbox with +; an alternate bundle path so that it permits access to the bundle. +(define (string-prefix? str prefix) + (let ((l (string-length prefix))) + (if (< (string-length str) l) + #f + (equal? (substring str 0 l) prefix) + ) + ) +) +(define data-volume-root "/System/Volumes/Data/") +(when (string-prefix? (param bundle-path) data-volume-root) + (define (strip-leading-chars str count) + (substring str count (string-length str)) + ) + (allow file-read* + (subpath (strip-leading-chars (param bundle-path) + (- (string-length data-volume-root) 1)))) +) + ; Allow reads of system libraries and frameworks. (allow file-read* (subpath "/System/Library/CoreServices/CoreTypes.bundle") diff --git a/chromium/sandbox/policy/mac/sandbox_mac.mm b/chromium/sandbox/policy/mac/sandbox_mac.mm index 248f5714db4..34f8b003c96 100644 --- a/chromium/sandbox/policy/mac/sandbox_mac.mm +++ b/chromium/sandbox/policy/mac/sandbox_mac.mm @@ -85,6 +85,7 @@ std::string GetSandboxProfile(sandbox::mojom::Sandbox sandbox_type) { break; // kService and kUtility are the same on OS_MAC, so fallthrough. case sandbox::mojom::Sandbox::kService: + case sandbox::mojom::Sandbox::kServiceWithJit: case sandbox::mojom::Sandbox::kUtility: profile += kSeatbeltPolicyString_utility; break; diff --git a/chromium/sandbox/policy/mojom/sandbox.mojom b/chromium/sandbox/policy/mojom/sandbox.mojom index 6ee006b2a94..916ead93bbc 100644 --- a/chromium/sandbox/policy/mojom/sandbox.mojom +++ b/chromium/sandbox/policy/mojom/sandbox.mojom @@ -16,6 +16,11 @@ enum Sandbox { // if possible. kService, + // |kServiceWithJit| hosts computation only services that make use of + // dynamic code (e.g. v8 or wasm) but do not need access to OS resources. + // Prefer |kService| if possible. + kServiceWithJit, + // Hosts generic utilities with limited access to system services. // On some platforms, may be slightly less locked down than |kService|. // For instance, it allows dynamic code and wider access to APIs on Windows. diff --git a/chromium/sandbox/policy/sandbox.cc b/chromium/sandbox/policy/sandbox.cc index 3b31605f49f..d76a6162a5b 100644 --- a/chromium/sandbox/policy/sandbox.cc +++ b/chromium/sandbox/policy/sandbox.cc @@ -9,37 +9,37 @@ #include "sandbox/policy/mojom/sandbox.mojom.h" #include "sandbox/policy/switches.h" -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) #include "base/android/jni_android.h" -#endif // defined(OS_ANDROID) +#endif // BUILDFLAG(IS_ANDROID) -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #include "sandbox/policy/linux/sandbox_linux.h" -#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) #include "sandbox/mac/seatbelt.h" -#endif // defined(OS_MAC) +#endif // BUILDFLAG(IS_MAC) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include "base/process/process_info.h" #include "sandbox/policy/win/sandbox_win.h" #include "sandbox/win/src/sandbox.h" -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) namespace sandbox { namespace policy { -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) bool Sandbox::Initialize(sandbox::mojom::Sandbox sandbox_type, SandboxLinux::PreSandboxHook hook, const SandboxLinux::Options& options) { return SandboxLinux::GetInstance()->InitializeSandbox( sandbox_type, std::move(hook), options); } -#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) bool Sandbox::Initialize(sandbox::mojom::Sandbox sandbox_type, SandboxInterfaceInfo* sandbox_info) { BrokerServices* broker_services = sandbox_info->broker_services; @@ -65,7 +65,7 @@ bool Sandbox::Initialize(sandbox::mojom::Sandbox sandbox_type, return IsUnsandboxedSandboxType(sandbox_type) || SandboxWin::InitTargetServices(sandbox_info->target_services); } -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) // static bool Sandbox::IsProcessSandboxed() { @@ -80,7 +80,7 @@ bool Sandbox::IsProcessSandboxed() { return true; } -#if defined(OS_ANDROID) +#if BUILDFLAG(IS_ANDROID) // Note that this does not check the status of the Seccomp sandbox. Call // https://developer.android.com/reference/android/os/Process#isIsolated(). JNIEnv* env = base::android::AttachCurrentThread(); @@ -90,12 +90,12 @@ bool Sandbox::IsProcessSandboxed() { base::android::MethodID::Get( env, process_class.obj(), "isIsolated", "()Z"); return env->CallStaticBooleanMethod(process_class.obj(), is_isolated); -#elif defined(OS_FUCHSIA) +#elif BUILDFLAG(IS_FUCHSIA) // TODO(https://crbug.com/1071420): Figure out what to do here. Process // launching controls the sandbox and there are no ambient capabilities, so // basically everything but the browser is considered sandboxed. return !is_browser; -#elif defined(OS_LINUX) || defined(OS_CHROMEOS) +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) int status = SandboxLinux::GetInstance()->GetStatus(); constexpr int kLayer1Flags = SandboxLinux::Status::kSUID | SandboxLinux::Status::kPIDNS | @@ -103,9 +103,9 @@ bool Sandbox::IsProcessSandboxed() { constexpr int kLayer2Flags = SandboxLinux::Status::kSeccompBPF | SandboxLinux::Status::kSeccompTSYNC; return (status & kLayer1Flags) != 0 && (status & kLayer2Flags) != 0; -#elif defined(OS_MAC) +#elif BUILDFLAG(IS_MAC) return Seatbelt::IsSandboxed(); -#elif defined(OS_WIN) +#elif BUILDFLAG(IS_WIN) return base::GetCurrentProcessIntegrityLevel() < base::MEDIUM_INTEGRITY; #else return false; diff --git a/chromium/sandbox/policy/sandbox.h b/chromium/sandbox/policy/sandbox.h index 4212646e997..b003a1febe3 100644 --- a/chromium/sandbox/policy/sandbox.h +++ b/chromium/sandbox/policy/sandbox.h @@ -8,7 +8,7 @@ #include "build/build_config.h" #include "sandbox/policy/export.h" -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) #include "sandbox/policy/linux/sandbox_linux.h" #endif @@ -32,16 +32,16 @@ namespace policy { class SANDBOX_POLICY_EXPORT Sandbox { public: -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) static bool Initialize(sandbox::mojom::Sandbox sandbox_type, SandboxLinux::PreSandboxHook hook, const SandboxLinux::Options& options); -#endif // defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) static bool Initialize(sandbox::mojom::Sandbox sandbox_type, SandboxInterfaceInfo* sandbox_info); -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) // Returns true if the current process is running with a sandbox, and false // if the process is not sandboxed. This should be used to assert that code is diff --git a/chromium/sandbox/policy/sandbox_delegate.h b/chromium/sandbox/policy/sandbox_delegate.h index 991eb065c7a..46913ae019d 100644 --- a/chromium/sandbox/policy/sandbox_delegate.h +++ b/chromium/sandbox/policy/sandbox_delegate.h @@ -27,7 +27,7 @@ class SandboxDelegate { // Sandbox::kNoSandbox to run without a sandbox policy. virtual sandbox::mojom::Sandbox GetSandboxType() = 0; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Whether to disable the default policy specified in // AddPolicyForSandboxedProcess. virtual bool DisableDefaultPolicy() = 0; @@ -48,7 +48,7 @@ class SandboxDelegate { // Whether this process will be compatible with Control-flow Enforcement // Technology (CET) / Hardware-enforced Stack Protection. virtual bool CetCompatible() = 0; -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) }; } // namespace policy diff --git a/chromium/sandbox/policy/sandbox_type.cc b/chromium/sandbox/policy/sandbox_type.cc index 2a5385d07e8..944e9ba4fe4 100644 --- a/chromium/sandbox/policy/sandbox_type.cc +++ b/chromium/sandbox/policy/sandbox_type.cc @@ -26,7 +26,7 @@ bool IsUnsandboxedSandboxType(Sandbox sandbox_type) { switch (sandbox_type) { case Sandbox::kNoSandbox: return true; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) case Sandbox::kNoSandboxAndElevatedPrivileges: return true; case Sandbox::kXrCompositing: @@ -39,7 +39,7 @@ bool IsUnsandboxedSandboxType(Sandbox sandbox_type) { #endif case Sandbox::kAudio: return false; -#if defined(OS_FUCHSIA) +#if BUILDFLAG(IS_FUCHSIA) case Sandbox::kVideoCapture: return false; #endif @@ -47,6 +47,7 @@ bool IsUnsandboxedSandboxType(Sandbox sandbox_type) { return false; case Sandbox::kRenderer: case Sandbox::kService: + case Sandbox::kServiceWithJit: case Sandbox::kUtility: case Sandbox::kGpu: #if BUILDFLAG(ENABLE_PLUGINS) @@ -57,7 +58,7 @@ bool IsUnsandboxedSandboxType(Sandbox sandbox_type) { case Sandbox::kPrintBackend: #endif case Sandbox::kPrintCompositor: -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) case Sandbox::kMirroring: case Sandbox::kNaClLoader: #endif @@ -69,7 +70,7 @@ bool IsUnsandboxedSandboxType(Sandbox sandbox_type) { case Sandbox::kLibassistant: #endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // // BUILDFLAG(IS_CHROMEOS_ASH) -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) case Sandbox::kZygoteIntermediateSandbox: #endif case Sandbox::kSpeechRecognition: @@ -112,6 +113,7 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, break; #endif case Sandbox::kService: + case Sandbox::kServiceWithJit: case Sandbox::kUtility: case Sandbox::kNetwork: case Sandbox::kCdm: @@ -120,17 +122,17 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, #endif case Sandbox::kPrintCompositor: case Sandbox::kAudio: -#if defined(OS_FUCHSIA) +#if BUILDFLAG(IS_FUCHSIA) case Sandbox::kVideoCapture: #endif -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) case Sandbox::kNoSandboxAndElevatedPrivileges: case Sandbox::kXrCompositing: case Sandbox::kPdfConversion: case Sandbox::kIconReader: case Sandbox::kMediaFoundationCdm: case Sandbox::kWindowsSystemProxyResolver: -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_CHROMEOS_ASH) case Sandbox::kHardwareVideoDecoding: case Sandbox::kIme: @@ -139,9 +141,9 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, case Sandbox::kLibassistant: #endif // BUILDFLAG(ENABLE_CROS_LIBASSISTANT) #endif // BUILDFLAG(IS_CHROMEOS_ASH) -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) case Sandbox::kMirroring: -#endif // defined(OS_MAC) +#endif // BUILDFLAG(IS_MAC) case Sandbox::kSpeechRecognition: DCHECK(command_line->GetSwitchValueASCII(switches::kProcessType) == switches::kUtilityProcess); @@ -150,11 +152,11 @@ void SetCommandLineFlagsForSandboxType(base::CommandLine* command_line, switches::kServiceSandboxType, StringFromUtilitySandboxType(sandbox_type)); break; -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) case Sandbox::kNaClLoader: break; -#endif // defined(OS_MAC) -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // BUILDFLAG(IS_MAC) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) case Sandbox::kZygoteIntermediateSandbox: break; #endif @@ -191,7 +193,7 @@ sandbox::mojom::Sandbox SandboxTypeFromCommandLine( // NaCl tests on all platforms use the loader process. if (process_type == switches::kNaClLoaderProcess) { -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) return Sandbox::kNaClLoader; #else return Sandbox::kUtility; @@ -201,20 +203,17 @@ sandbox::mojom::Sandbox SandboxTypeFromCommandLine( if (process_type == switches::kNaClBrokerProcess) return Sandbox::kNoSandbox; -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // Intermediate process gains a sandbox later. if (process_type == switches::kZygoteProcessType) return Sandbox::kZygoteIntermediateSandbox; #endif -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) if (process_type == switches::kRelauncherProcessType) return Sandbox::kNoSandbox; #endif - if (process_type == switches::kCloudPrintServiceProcess) - return Sandbox::kNoSandbox; - CHECK(false) << "Command line does not provide a valid sandbox configuration: " << command_line.GetCommandLineString(); @@ -226,10 +225,10 @@ std::string StringFromUtilitySandboxType(Sandbox sandbox_type) { switch (sandbox_type) { case Sandbox::kNoSandbox: return switches::kNoneSandbox; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) case Sandbox::kNoSandboxAndElevatedPrivileges: return switches::kNoneSandboxAndElevatedPrivileges; -#endif // defined(OS_WIN) +#endif // BUILDFLAG(IS_WIN) case Sandbox::kNetwork: return switches::kNetworkSandbox; #if BUILDFLAG(ENABLE_PLUGINS) @@ -248,15 +247,17 @@ std::string StringFromUtilitySandboxType(Sandbox sandbox_type) { return switches::kUtilitySandbox; case Sandbox::kAudio: return switches::kAudioSandbox; -#if defined(OS_FUCHSIA) +#if BUILDFLAG(IS_FUCHSIA) case Sandbox::kVideoCapture: return switches::kVideoCaptureSandbox; #endif case Sandbox::kService: return switches::kServiceSandbox; + case Sandbox::kServiceWithJit: + return switches::kServiceSandboxWithJit; case Sandbox::kSpeechRecognition: return switches::kSpeechRecognitionSandbox; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) case Sandbox::kXrCompositing: return switches::kXrCompositingSandbox; case Sandbox::kPdfConversion: @@ -267,8 +268,8 @@ std::string StringFromUtilitySandboxType(Sandbox sandbox_type) { return switches::kMediaFoundationCdmSandbox; case Sandbox::kWindowsSystemProxyResolver: return switches::kWindowsSystemProxyResolverSandbox; -#endif // defined(OS_WIN) -#if defined(OS_MAC) +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_MAC) case Sandbox::kMirroring: return switches::kMirroringSandbox; #endif @@ -287,10 +288,10 @@ std::string StringFromUtilitySandboxType(Sandbox sandbox_type) { // The following are not utility processes so should not occur. case Sandbox::kRenderer: case Sandbox::kGpu: -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) case Sandbox::kNaClLoader: -#endif // defined(OS_MAC) -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#endif // BUILDFLAG(IS_MAC) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) case Sandbox::kZygoteIntermediateSandbox: #endif NOTREACHED(); @@ -308,11 +309,13 @@ sandbox::mojom::Sandbox UtilitySandboxTypeFromString( return Sandbox::kUtility; if (sandbox_string == switches::kServiceSandbox) return Sandbox::kService; + if (sandbox_string == switches::kServiceSandboxWithJit) + return Sandbox::kServiceWithJit; if (sandbox_string == switches::kNoneSandbox) return Sandbox::kNoSandbox; if (sandbox_string == switches::kNoneSandboxAndElevatedPrivileges) { -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) return Sandbox::kNoSandboxAndElevatedPrivileges; #else return Sandbox::kNoSandbox; @@ -332,7 +335,7 @@ sandbox::mojom::Sandbox UtilitySandboxTypeFromString( #endif if (sandbox_string == switches::kPrintCompositorSandbox) return Sandbox::kPrintCompositor; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) if (sandbox_string == switches::kXrCompositingSandbox) return Sandbox::kXrCompositing; if (sandbox_string == switches::kPdfConversionSandbox) @@ -344,7 +347,7 @@ sandbox::mojom::Sandbox UtilitySandboxTypeFromString( if (sandbox_string == switches::kWindowsSystemProxyResolverSandbox) return Sandbox::kWindowsSystemProxyResolver; #endif -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) if (sandbox_string == switches::kMirroringSandbox) return Sandbox::kMirroring; #endif @@ -352,7 +355,7 @@ sandbox::mojom::Sandbox UtilitySandboxTypeFromString( return Sandbox::kAudio; if (sandbox_string == switches::kSpeechRecognitionSandbox) return Sandbox::kSpeechRecognition; -#if defined(OS_FUCHSIA) +#if BUILDFLAG(IS_FUCHSIA) if (sandbox_string == switches::kVideoCaptureSandbox) return Sandbox::kVideoCapture; #endif diff --git a/chromium/sandbox/policy/sandbox_type_unittest.cc b/chromium/sandbox/policy/sandbox_type_unittest.cc index 83da610d317..187043a5bda 100644 --- a/chromium/sandbox/policy/sandbox_type_unittest.cc +++ b/chromium/sandbox/policy/sandbox_type_unittest.cc @@ -86,7 +86,7 @@ TEST(SandboxTypeTest, Utility) { EXPECT_EQ(Sandbox::kSpeechRecognition, SandboxTypeFromCommandLine(command_line9)); -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) base::CommandLine command_line10(command_line); SetCommandLineFlagsForSandboxType(&command_line10, Sandbox::kXrCompositing); EXPECT_EQ(Sandbox::kXrCompositing, @@ -115,6 +115,11 @@ TEST(SandboxTypeTest, Utility) { switches::kNoneSandbox); EXPECT_EQ(Sandbox::kNoSandbox, SandboxTypeFromCommandLine(command_line14)); + base::CommandLine command_line15(command_line); + SetCommandLineFlagsForSandboxType(&command_line15, Sandbox::kServiceWithJit); + EXPECT_EQ(Sandbox::kServiceWithJit, + SandboxTypeFromCommandLine(command_line15)); + command_line.AppendSwitch(switches::kNoSandbox); EXPECT_EQ(Sandbox::kNoSandbox, SandboxTypeFromCommandLine(command_line)); } @@ -183,7 +188,7 @@ TEST(SandboxTypeTest, ElevatedPrivileges) { // specific default to no sandbox on non Windows platforms. Sandbox elevated_type = UtilitySandboxTypeFromString(switches::kNoneSandboxAndElevatedPrivileges); -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) EXPECT_EQ(Sandbox::kNoSandboxAndElevatedPrivileges, elevated_type); #else EXPECT_EQ(Sandbox::kNoSandbox, elevated_type); diff --git a/chromium/sandbox/policy/switches.cc b/chromium/sandbox/policy/switches.cc index c7fa0112ebe..4ad0ff39cba 100644 --- a/chromium/sandbox/policy/switches.cc +++ b/chromium/sandbox/policy/switches.cc @@ -8,7 +8,7 @@ #include "build/chromeos_buildflags.h" #include "printing/buildflags/buildflags.h" -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include "base/command_line.h" #include "base/win/windows_version.h" #endif @@ -35,20 +35,21 @@ const char kPrintBackendSandbox[] = "print_backend"; const char kPrintCompositorSandbox[] = "print_compositor"; const char kAudioSandbox[] = "audio"; const char kServiceSandbox[] = "service"; +const char kServiceSandboxWithJit[] = "service_with_jit"; const char kSpeechRecognitionSandbox[] = "speech_recognition"; const char kVideoCaptureSandbox[] = "video_capture"; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) const char kPdfConversionSandbox[] = "pdf_conversion"; const char kXrCompositingSandbox[] = "xr_compositing"; const char kIconReaderSandbox[] = "icon_reader"; const char kMediaFoundationCdmSandbox[] = "mf_cdm"; const char kWindowsSystemProxyResolverSandbox[] = "proxy_resolver_win"; -#endif // OS_WIN +#endif // BUILDFLAG(IS_WIN) -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) const char kMirroringSandbox[] = "mirroring"; -#endif // OS_MAC +#endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_CHROMEOS_ASH) const char kHardwareVideoDecodingSandbox[] = "hardware_video_decoding"; @@ -94,13 +95,13 @@ const char kGpuSandboxFailuresFatal[] = "gpu-sandbox-failures-fatal"; // Meant to be used as a browser-level switch for testing purposes only. const char kNoSandbox[] = "no-sandbox"; -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) // Instructs the zygote to launch without a sandbox. Processes forked from this // type of zygote will apply their own custom sandboxes later. const char kNoZygoteSandbox[] = "no-zygote-sandbox"; #endif -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) // Allows third party modules to inject by disabling the BINARY_SIGNATURE // mitigation policy on Win10+. Also has other effects in ELF. const char kAllowThirdPartyModules[] = "allow-third-party-modules"; @@ -113,7 +114,7 @@ const char kAddGpuAppContainerCaps[] = "add-gpu-appcontainer-caps"; const char kAddXrAppContainerCaps[] = "add-xr-appcontainer-caps"; #endif -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) // Cause the OS X sandbox write to syslog every time an access to a resource // is denied by the sandbox. const char kEnableSandboxLogging[] = "enable-sandbox-logging"; @@ -130,7 +131,6 @@ const char kNaClLoaderProcess[] = "nacl-loader"; const char kPpapiPluginProcess[] = "ppapi"; const char kRendererProcess[] = "renderer"; const char kUtilityProcess[] = "utility"; -const char kCloudPrintServiceProcess[] = "service"; const char kZygoteProcessType[] = "zygote"; const char kRelauncherProcessType[] = "relauncher"; diff --git a/chromium/sandbox/policy/switches.h b/chromium/sandbox/policy/switches.h index 5a4cb0afe91..d07e692c1b4 100644 --- a/chromium/sandbox/policy/switches.h +++ b/chromium/sandbox/policy/switches.h @@ -36,20 +36,21 @@ SANDBOX_POLICY_EXPORT extern const char kPrintBackendSandbox[]; SANDBOX_POLICY_EXPORT extern const char kPrintCompositorSandbox[]; SANDBOX_POLICY_EXPORT extern const char kAudioSandbox[]; SANDBOX_POLICY_EXPORT extern const char kServiceSandbox[]; +SANDBOX_POLICY_EXPORT extern const char kServiceSandboxWithJit[]; SANDBOX_POLICY_EXPORT extern const char kSpeechRecognitionSandbox[]; SANDBOX_POLICY_EXPORT extern const char kVideoCaptureSandbox[]; -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) SANDBOX_POLICY_EXPORT extern const char kPdfConversionSandbox[]; SANDBOX_POLICY_EXPORT extern const char kXrCompositingSandbox[]; SANDBOX_POLICY_EXPORT extern const char kIconReaderSandbox[]; SANDBOX_POLICY_EXPORT extern const char kMediaFoundationCdmSandbox[]; SANDBOX_POLICY_EXPORT extern const char kWindowsSystemProxyResolverSandbox[]; -#endif // OS_WIN +#endif // BUILDFLAG(IS_WIN) -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) SANDBOX_POLICY_EXPORT extern const char kMirroringSandbox[]; -#endif // OS_MAC +#endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_CHROMEOS_ASH) SANDBOX_POLICY_EXPORT extern const char kHardwareVideoDecodingSandbox[]; @@ -70,15 +71,15 @@ SANDBOX_POLICY_EXPORT extern const char kDisableSetuidSandbox[]; SANDBOX_POLICY_EXPORT extern const char kGpuSandboxAllowSysVShm[]; SANDBOX_POLICY_EXPORT extern const char kGpuSandboxFailuresFatal[]; SANDBOX_POLICY_EXPORT extern const char kNoSandbox[]; -#if defined(OS_LINUX) || defined(OS_CHROMEOS) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) SANDBOX_POLICY_EXPORT extern const char kNoZygoteSandbox[]; #endif -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) SANDBOX_POLICY_EXPORT extern const char kAllowThirdPartyModules[]; SANDBOX_POLICY_EXPORT extern const char kAddGpuAppContainerCaps[]; SANDBOX_POLICY_EXPORT extern const char kAddXrAppContainerCaps[]; #endif -#if defined(OS_MAC) +#if BUILDFLAG(IS_MAC) SANDBOX_POLICY_EXPORT extern const char kEnableSandboxLogging[]; SANDBOX_POLICY_EXPORT extern const char kDisableMetalShaderCache[]; #endif @@ -91,7 +92,6 @@ SANDBOX_POLICY_EXPORT extern const char kNaClLoaderProcess[]; SANDBOX_POLICY_EXPORT extern const char kPpapiPluginProcess[]; SANDBOX_POLICY_EXPORT extern const char kRendererProcess[]; SANDBOX_POLICY_EXPORT extern const char kUtilityProcess[]; -SANDBOX_POLICY_EXPORT extern const char kCloudPrintServiceProcess[]; SANDBOX_POLICY_EXPORT extern const char kZygoteProcessType[]; SANDBOX_POLICY_EXPORT extern const char kRelauncherProcessType[]; diff --git a/chromium/sandbox/policy/win/sandbox_policy_feature_test.cc b/chromium/sandbox/policy/win/sandbox_policy_feature_test.cc index ee0e463aa2b..5cf590966dc 100644 --- a/chromium/sandbox/policy/win/sandbox_policy_feature_test.cc +++ b/chromium/sandbox/policy/win/sandbox_policy_feature_test.cc @@ -11,12 +11,12 @@ SandboxFeatureTest::SandboxFeatureTest() { std::vector enabled_features; std::vector disabled_features; - if (::testing::get<0>(GetParam())) + if (::testing::get(GetParam())) enabled_features.push_back(features::kRendererAppContainer); else disabled_features.push_back(features::kRendererAppContainer); - if (::testing::get<1>(GetParam())) + if (::testing::get(GetParam())) enabled_features.push_back(features::kWinSboxDisableKtmComponent); else disabled_features.push_back(features::kWinSboxDisableKtmComponent); @@ -24,8 +24,16 @@ SandboxFeatureTest::SandboxFeatureTest() { feature_list_.InitWithFeatures(enabled_features, disabled_features); } -AppContainerType SandboxFeatureTest::GetExpectedAppContainerType() { - return AppContainerType::kNone; +IntegrityLevel SandboxFeatureTest::GetExpectedIntegrityLevel() { + return IntegrityLevel::INTEGRITY_LEVEL_LOW; +} + +TokenLevel SandboxFeatureTest::GetExpectedLockdownTokenLevel() { + return TokenLevel::USER_LOCKDOWN; +} + +TokenLevel SandboxFeatureTest::GetExpectedInitialTokenLevel() { + return TokenLevel::USER_RESTRICTED_SAME_ACCESS; } MitigationFlags SandboxFeatureTest::GetExpectedMitigationFlags() { @@ -47,7 +55,7 @@ MitigationFlags SandboxFeatureTest::GetExpectedMitigationFlags() { flags = flags | ::sandbox::MITIGATION_WIN32K_DISABLE; #endif - if (::testing::get<1>(GetParam())) + if (::testing::get(GetParam())) flags = flags | ::sandbox::MITIGATION_KTM_COMPONENT; return flags; @@ -58,5 +66,39 @@ MitigationFlags SandboxFeatureTest::GetExpectedDelayedMitigationFlags() { ::sandbox::MITIGATION_FORCE_MS_SIGNED_BINS; } +AppContainerType SandboxFeatureTest::GetExpectedAppContainerType() { + return AppContainerType::kNone; +} + +std::vector SandboxFeatureTest::GetExpectedCapabilities() { + return {}; +} + +void SandboxFeatureTest::ValidateSecurityLevels( + const scoped_refptr& policy) { + EXPECT_EQ(policy->GetIntegrityLevel(), GetExpectedIntegrityLevel()); + EXPECT_EQ(policy->GetLockdownTokenLevel(), GetExpectedLockdownTokenLevel()); + EXPECT_EQ(policy->GetInitialTokenLevel(), GetExpectedInitialTokenLevel()); +} + +void SandboxFeatureTest::ValidatePolicyFlagSettings( + const scoped_refptr& policy) { + EXPECT_EQ(policy->GetProcessMitigations(), GetExpectedMitigationFlags()); + EXPECT_EQ(policy->GetDelayedProcessMitigations(), + GetExpectedDelayedMitigationFlags()); +} + +void SandboxFeatureTest::ValidateAppContainerSettings( + const scoped_refptr& policy) { + if (GetExpectedAppContainerType() == ::sandbox::AppContainerType::kLowbox) { + EXPECT_EQ(GetExpectedAppContainerType(), + policy->GetAppContainer()->GetAppContainerType()); + + EqualSidList(policy->GetAppContainer()->GetCapabilities(), + GetExpectedCapabilities()); + } else { + EXPECT_EQ(policy->GetAppContainer().get(), nullptr); + } +} } // namespace policy } // namespace sandbox \ No newline at end of file diff --git a/chromium/sandbox/policy/win/sandbox_policy_feature_test.h b/chromium/sandbox/policy/win/sandbox_policy_feature_test.h index ca29735fdbf..8629153ba00 100644 --- a/chromium/sandbox/policy/win/sandbox_policy_feature_test.h +++ b/chromium/sandbox/policy/win/sandbox_policy_feature_test.h @@ -12,7 +12,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include "base/win/windows_version.h" #include "sandbox/policy/win/sandbox_test_utils.h" #include "sandbox/policy/win/sandbox_win.h" @@ -31,15 +31,25 @@ class SandboxFeatureTest ::testing::tuple> { public: + enum TestParameter { kEnableRendererAppContainer, kEnableKtmMitigation }; + SandboxFeatureTest(); - // App Containers are only available in Windows 8 and up - virtual AppContainerType GetExpectedAppContainerType(); + virtual IntegrityLevel GetExpectedIntegrityLevel(); + virtual TokenLevel GetExpectedLockdownTokenLevel(); + virtual TokenLevel GetExpectedInitialTokenLevel(); virtual MitigationFlags GetExpectedMitigationFlags(); - virtual MitigationFlags GetExpectedDelayedMitigationFlags(); + // App Containers are only available in Windows 8 and up + virtual AppContainerType GetExpectedAppContainerType(); + virtual std::vector GetExpectedCapabilities(); + + void ValidateSecurityLevels(const scoped_refptr& policy); + void ValidatePolicyFlagSettings(const scoped_refptr& policy); + void ValidateAppContainerSettings(const scoped_refptr& policy); + base::test::ScopedFeatureList feature_list_; }; } // namespace policy diff --git a/chromium/sandbox/policy/win/sandbox_win.cc b/chromium/sandbox/policy/win/sandbox_win.cc index 497d535f278..197e66eeb5b 100644 --- a/chromium/sandbox/policy/win/sandbox_win.cc +++ b/chromium/sandbox/policy/win/sandbox_win.cc @@ -25,6 +25,7 @@ #include "base/no_destructor.h" #include "base/path_service.h" #include "base/process/launch.h" +#include "base/process/process.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" @@ -463,7 +464,7 @@ typedef BOOL(WINAPI* DuplicateHandleFunctionPtr)(HANDLE source_process_handle, DuplicateHandleFunctionPtr g_iat_orig_duplicate_handle; -NtQueryObject g_QueryObject = NULL; +NtQueryObjectFunction g_QueryObject = NULL; static const char* kDuplicateHandleWarning = "You are attempting to duplicate a privileged handle into a sandboxed" @@ -805,6 +806,8 @@ ResultCode LaunchWithoutSandbox( } *process = base::LaunchProcess(cmd_line, options); + if (!process->IsValid()) + return SBOX_ERROR_CANNOT_LAUNCH_UNSANDBOXED_PROCESS; return SBOX_ALL_OK; } @@ -1263,6 +1266,8 @@ std::string SandboxWin::GetSandboxTypeInEnglish(Sandbox sandbox_type) { return "Media Foundation CDM"; case Sandbox::kService: return "Service"; + case Sandbox::kServiceWithJit: + return "Service With Jit"; case Sandbox::kIconReader: return "Icon Reader"; case Sandbox::kWindowsSystemProxyResolver: diff --git a/chromium/sandbox/win/BUILD.gn b/chromium/sandbox/win/BUILD.gn index f33d9e8dde7..1eecd589044 100644 --- a/chromium/sandbox/win/BUILD.gn +++ b/chromium/sandbox/win/BUILD.gn @@ -146,15 +146,6 @@ static_library("sandbox") { sources += [ "src/resolver_32.cc", "src/service_resolver_32.cc", - "src/sidestep/ia32_modrm_map.cpp", - "src/sidestep/ia32_opcode_map.cpp", - "src/sidestep/mini_disassembler.cpp", - "src/sidestep/mini_disassembler.h", - "src/sidestep/mini_disassembler_types.h", - "src/sidestep/preamble_patcher.h", - "src/sidestep/preamble_patcher_with_stub.cpp", - "src/sidestep_resolver.cc", - "src/sidestep_resolver.h", ] } @@ -193,7 +184,6 @@ test("sbox_integration_tests") { "src/policy_target_test.cc", "src/process_mitigations_dyncode_unittest.cc", "src/process_mitigations_extensionpoints_unittest.cc", - "src/process_mitigations_imageload_unittest.cc", "src/process_mitigations_unittest.cc", "src/process_mitigations_win32k_unittest.cc", "src/process_policy_test.cc", @@ -216,8 +206,6 @@ test("sbox_integration_tests") { ] data_deps = [ - ":sbox_integration_test_hijack_dll", - ":sbox_integration_test_hijack_shim_dll", ":sbox_integration_test_hooking_dll", ":sbox_integration_test_win_proc", ] @@ -228,15 +216,6 @@ test("sbox_integration_tests") { ] } -shared_library("sbox_integration_test_hijack_dll") { - sources = [ - "tests/integration_tests/hijack_dll.cc", - "tests/integration_tests/hijack_dll.def", - ] - - deps = [ ":maybe_set_appcontainer_acls" ] -} - group("maybe_set_appcontainer_acls") { # NACL on 32-bit builds this target twice, once for 64-bit and once for 32-bit # so avoid this dep from running twice with the same output in that case. @@ -260,20 +239,6 @@ if (current_cpu == target_cpu && host_os == "win") { } } -shared_library("sbox_integration_test_hijack_shim_dll") { - sources = [ - "tests/integration_tests/hijack_shim_dll.cc", - "tests/integration_tests/hijack_shim_dll.def", - ] - - # Implicitly linking hijack_dll as loader import resolution required. - deps = [ - ":common", - ":sbox_integration_test_hijack_dll", - "//base", - ] -} - shared_library("sbox_integration_test_hooking_dll") { sources = [ "tests/integration_tests/hooking_dll.cc" ] @@ -353,7 +318,6 @@ source_set("common") { "src/sandbox_types.h", "src/security_level.h", "tests/common/controller.h", - "tests/integration_tests/hijack_shim_dll.h", ] deps = [ "//base" ] diff --git a/chromium/sandbox/win/src/app_container.h b/chromium/sandbox/win/src/app_container.h index fc3f17fd618..1552a59e7f6 100644 --- a/chromium/sandbox/win/src/app_container.h +++ b/chromium/sandbox/win/src/app_container.h @@ -10,6 +10,7 @@ #include "base/win/sid.h" #include "base/win/windows_types.h" #include "sandbox/win/src/acl.h" +#include "sandbox/win/src/security_capabilities.h" namespace sandbox { @@ -70,6 +71,16 @@ class AppContainer { virtual bool GetEnableLowPrivilegeAppContainer() = 0; virtual AppContainerType GetAppContainerType() = 0; + + // Get a vector of capabilities. + virtual const std::vector& GetCapabilities() = 0; + + // Get a vector of impersonation only capabilities. Used if the process needs + // a more privileged token to start. + virtual const std::vector& GetImpersonationCapabilities() = 0; + + // Get an allocated SecurityCapabilities object for this App Container. + virtual std::unique_ptr GetSecurityCapabilities() = 0; }; } // namespace sandbox diff --git a/chromium/sandbox/win/src/app_container_base.h b/chromium/sandbox/win/src/app_container_base.h index 1215cf90f93..fabfe815059 100644 --- a/chromium/sandbox/win/src/app_container_base.h +++ b/chromium/sandbox/win/src/app_container_base.h @@ -15,7 +15,6 @@ #include "base/win/windows_types.h" #include "sandbox/win/src/app_container.h" #include "sandbox/win/src/sandbox_types.h" -#include "sandbox/win/src/security_capabilities.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace sandbox { @@ -47,20 +46,13 @@ class AppContainerBase final : public AppContainer { void SetEnableLowPrivilegeAppContainer(bool enable) override; bool GetEnableLowPrivilegeAppContainer() override; AppContainerType GetAppContainerType() override; + const std::vector& GetCapabilities() override; + const std::vector& GetImpersonationCapabilities() override; + std::unique_ptr GetSecurityCapabilities() override; // Get the package SID for this AC. const base::win::Sid& GetPackageSid() const; - // Get an allocated SecurityCapabilities object for this App Container. - std::unique_ptr GetSecurityCapabilities(); - - // Get a vector of capabilities. - const std::vector& GetCapabilities(); - - // Get a vector of impersonation only capabilities. Used if the process needs - // a more privileged token to start. - const std::vector& GetImpersonationCapabilities(); - // Creates a new AppContainer 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. diff --git a/chromium/sandbox/win/src/app_container_test.cc b/chromium/sandbox/win/src/app_container_test.cc index d942eab35d6..73bb402eca2 100644 --- a/chromium/sandbox/win/src/app_container_test.cc +++ b/chromium/sandbox/win/src/app_container_test.cc @@ -491,7 +491,8 @@ TEST_F(AppContainerTest, DenyOpenEventForLowBox) { ::CreateEvent(nullptr, false, false, event_name.c_str())); ASSERT_TRUE(event.IsValid()); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"AppContainerEvent_Open test")); + TestRunner runner2(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED); + EXPECT_EQ(SBOX_TEST_DENIED, runner2.RunTest(L"AppContainerEvent_Open test")); } TEST_F(AppContainerTest, CheckIncompatibleOptions) { @@ -631,10 +632,10 @@ TEST_F(AppContainerTest, NoCapabilitiesLPAC) { } SBOX_TESTS_COMMAND int LoadDLL(int argc, wchar_t** argv) { - // DLL here doesn't matter as long as it's in the output directory: re-use one - // from another sbox test. - base::ScopedNativeLibrary test_dll(base::FilePath( - FILE_PATH_LITERAL("sbox_integration_test_hijack_dll.dll"))); + // Library here doesn't matter as long as it's in the output directory: re-use + // one from another sbox test. + base::ScopedNativeLibrary test_dll( + base::FilePath(FILE_PATH_LITERAL("sbox_integration_test_win_proc.exe"))); if (test_dll.is_valid()) return SBOX_TEST_SUCCEEDED; return SBOX_TEST_FAILED; diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc index 99d04dfe938..6bce0cc3c35 100644 --- a/chromium/sandbox/win/src/broker_services.cc +++ b/chromium/sandbox/win/src/broker_services.cc @@ -520,8 +520,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, for (HANDLE handle : policy_handle_list) startup_info->AddInheritedHandle(handle); - scoped_refptr container = - policy_base->GetAppContainerBase(); + scoped_refptr container = policy_base->GetAppContainer(); if (container) startup_info->SetAppContainer(container); @@ -577,7 +576,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, // Now the policy is the owner of the target. TargetProcess will terminate // the process if it has not completed when it is destroyed. - result = policy_base->AddTarget(std::move(target)); + result = policy_base->ApplyToTarget(std::move(target)); if (result != SBOX_ALL_OK) { *last_error = ::GetLastError(); diff --git a/chromium/sandbox/win/src/broker_services.h b/chromium/sandbox/win/src/broker_services.h index e63b0cb0eb4..a848198093e 100644 --- a/chromium/sandbox/win/src/broker_services.h +++ b/chromium/sandbox/win/src/broker_services.h @@ -5,7 +5,6 @@ #ifndef SANDBOX_WIN_SRC_BROKER_SERVICES_H_ #define SANDBOX_WIN_SRC_BROKER_SERVICES_H_ -#include #include #include #include diff --git a/chromium/sandbox/win/src/file_policy_test.cc b/chromium/sandbox/win/src/file_policy_test.cc index b061c049c89..dd02b5cf0ee 100644 --- a/chromium/sandbox/win/src/file_policy_test.cc +++ b/chromium/sandbox/win/src/file_policy_test.cc @@ -48,8 +48,9 @@ SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t** argv) { 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 file1( + CreateFile(argv[1], GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE, + kSharing, nullptr, OPEN_EXISTING, 0, nullptr)); base::win::ScopedHandle file2( CreateFile(argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, nullptr, OPEN_EXISTING, 0, nullptr)); @@ -273,54 +274,63 @@ SBOX_TESTS_COMMAND int File_CopyFile(int argc, wchar_t** argv) { TEST(FilePolicyTest, DenyNtCreateCalc) { TestRunner runner; - EXPECT_TRUE( - runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"calc.exe")); - + EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.txt")); 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")); + TestRunner before_revert; + EXPECT_TRUE( + before_revert.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.txt")); + before_revert.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + before_revert.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")); + TestRunner before_revert; + EXPECT_TRUE( + before_revert.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe")); + before_revert.SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, + before_revert.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)); + TestRunner runner2; + runner2.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str()); 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)); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(buff)); } -TEST(FilePolicyTest, AllowReadOnly) { - TestRunner runner; +std::unique_ptr AllowReadOnlyRunner(wchar_t* temp_file_name) { + auto runner = std::make_unique(); + EXPECT_TRUE( + runner->AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name)); + return runner; +} +TEST(FilePolicyTest, AllowReadOnly) { // 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] = {}; @@ -330,25 +340,27 @@ TEST(FilePolicyTest, AllowReadOnly) { 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)); + auto runner = AllowReadOnlyRunner(temp_file_name); + 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)); + runner = AllowReadOnlyRunner(temp_file_name); + 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)); + runner = AllowReadOnlyRunner(temp_file_name); + 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)); + runner = AllowReadOnlyRunner(temp_file_name); + 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); @@ -363,9 +375,13 @@ TEST(FilePolicyTest, AllowImplicitDeviceName) { wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str()); path = std::wstring(kNTPrefix) + path; + TestRunner runner; 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)); + + TestRunner runner_with_rule; + EXPECT_TRUE( + runner_with_rule.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, path.c_str())); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner_with_rule.RunTest(command)); DeleteFile(temp_file_name); } @@ -391,18 +407,28 @@ TEST(FilePolicyTest, AllowWildcard) { DeleteFile(temp_file_name); } -TEST(FilePolicyTest, AllowNtCreatePatternRule) { - TestRunner runner; - EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll")); +std::unique_ptr AllowNtCreatePatternRunner() { + auto runner = std::make_unique(); + EXPECT_TRUE(runner->AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll")); + return runner; +} +TEST(FilePolicyTest, AllowNtCreatePatternRule) { + auto runner = AllowNtCreatePatternRunner(); 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->RunTest(L"File_OpenSys32 apphelp.dll")); + + runner = AllowNtCreatePatternRunner(); + EXPECT_EQ(SBOX_TEST_DENIED, runner->RunTest(L"File_OpenSys32 appwiz.cpl")); - runner.SetTestState(BEFORE_REVERT); + runner = AllowNtCreatePatternRunner(); + 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")); + runner->RunTest(L"File_OpenSys32 apphelp.dll")); + + runner = AllowNtCreatePatternRunner(); + runner->SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"File_OpenSys32 appwiz.cpl")); } TEST(FilePolicyTest, CheckNotFound) { @@ -418,30 +444,38 @@ TEST(FilePolicyTest, CheckNoLeak) { EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe")); } -TEST(FilePolicyTest, TestQueryAttributesFile) { - TestRunner runner; +std::unique_ptr QueryAttributesFileRunner() { + auto runner = std::make_unique(); EXPECT_TRUE( - runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"apphelp.dll")); + 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")); + 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")); + runner->AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, L"ipconfig.exe")); + return runner; +} +TEST(FilePolicyTest, TestQueryAttributesFile) { + auto runner = QueryAttributesFileRunner(); EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_QueryAttributes drivers d")); + runner->RunTest(L"File_QueryAttributes drivers d")); + runner = QueryAttributesFileRunner(); EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_QueryAttributes apphelp.dll f")); + runner->RunTest(L"File_QueryAttributes apphelp.dll f")); + runner = QueryAttributesFileRunner(); EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_QueryAttributes ipconfig.exe f")); + runner->RunTest(L"File_QueryAttributes ipconfig.exe f")); + runner = QueryAttributesFileRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"File_QueryAttributes ftp.exe f")); + runner->RunTest(L"File_QueryAttributes ftp.exe f")); + runner = QueryAttributesFileRunner(); EXPECT_EQ(SBOX_TEST_NOT_FOUND, - runner.RunTest(L"File_QueryAttributes notfound.exe f")); + runner->RunTest(L"File_QueryAttributes notfound.exe f")); } // Makes sure that we don't leak information when there is not policy to allow @@ -451,133 +485,139 @@ TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) { EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_QueryAttributes ftp.exe f")); + TestRunner runner2; EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"File_QueryAttributes notfound.exe f")); + runner2.RunTest(L"File_QueryAttributes notfound.exe f")); +} + +// Expects 8 file names. Attempts to copy even to odd files will happen. +std::unique_ptr RenameRunner( + std::vector& temp_files) { + auto runner = std::make_unique(); + // Add rules to make file0->file1 succeed. + runner->AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_files[0].c_str()); + runner->AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_files[1].c_str()); + + // Add rules to make file2->file3 fail. + runner->AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_files[2].c_str()); + runner->AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_files[3].c_str()); + + // Add rules to make file4->file5 fail. + runner->AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_files[4].c_str()); + runner->AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_files[5].c_str()); + + // Add rules to make file6->no_pol_file fail. + runner->AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_files[6].c_str()); + return runner; } TEST(FilePolicyTest, TestRename) { - TestRunner runner; + const size_t nFiles = 8; // 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]; + std::vector temp_files; 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)); + for (size_t i = 0; i < nFiles; i++) { + wchar_t temp_file[MAX_PATH]; + ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file), 0u); + temp_files.push_back(std::wstring(temp_file)); + } // 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); + ::DeleteFile(temp_files[1].c_str()); + ::DeleteFile(temp_files[3].c_str()); + ::DeleteFile(temp_files[5].c_str()); + ::DeleteFile(temp_files[7].c_str()); + auto runner = RenameRunner(temp_files); 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_files[0].c_str(), + temp_files[1].c_str()); + 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)); + runner = RenameRunner(temp_files); + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_files[2].c_str(), + temp_files[3].c_str()); + 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)); + runner = RenameRunner(temp_files); + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_files[4].c_str(), + temp_files[5].c_str()); + 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)); + runner = RenameRunner(temp_files); + wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_files[6].c_str(), + temp_files[7].c_str()); + 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); + for (auto& file : temp_files) + ::DeleteFile(file.c_str()); } -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")); +std::unique_ptr AllowNotepadRunner() { + auto runner = std::make_unique(); + runner->AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notepad.exe"); + return runner; } TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) { - TestRunner runner; - EXPECT_TRUE( - runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notepad.exe")); - + auto runner = AllowNotepadRunner(); EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"File_Win32Create notepad.exe")); + runner->RunTest(L"File_Win32Create notepad.exe")); - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe")); + runner = AllowNotepadRunner(); + EXPECT_EQ(SBOX_TEST_DENIED, runner->RunTest(L"File_Win32Create calc.exe")); - runner.SetTestState(BEFORE_REVERT); + runner = AllowNotepadRunner(); + 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")); + runner->RunTest(L"File_Win32Create notepad.exe")); + + runner = AllowNotepadRunner(); + runner->SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"File_Win32Create calc.exe")); +} + +std::unique_ptr FileGetDiskSpaceRunner() { + auto runner = std::make_unique(); + runner->AddRuleSys32(TargetPolicy::FILES_ALLOW_READONLY, L""); + return runner; } 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")); + auto runner = std::make_unique(); + EXPECT_EQ(SBOX_TEST_DENIED, runner->RunTest(L"File_GetDiskSpace")); + + runner = std::make_unique(); + 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 = FileGetDiskSpaceRunner(); + runner->SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"File_GetDiskSpace")); + + runner = FileGetDiskSpaceRunner(); + runner->SetTestState(AFTER_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")); + runner = FileGetDiskSpaceRunner(); + runner->SetTestState(AFTER_REVERT); + EXPECT_EQ(SBOX_TEST_DENIED, runner->RunTest(L"File_Win32Create notepad.exe")); } -TEST(FilePolicyTest, TestReparsePoint) { - TestRunner runner; +std::unique_ptr ReparsePointRunner( + std::wstring& temp_dir_wildcard) { + auto runner = std::make_unique(); + runner->AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_dir_wildcard.c_str()); + return runner; +} +TEST(FilePolicyTest, TestReparsePoint) { // Create a temp file because we need write access to it. wchar_t temp_directory[MAX_PATH]; wchar_t temp_file_name[MAX_PATH]; @@ -593,7 +633,7 @@ TEST(FilePolicyTest, TestReparsePoint) { 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, + HANDLE file = ::CreateFile(temp_file.c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, 0, nullptr); ASSERT_TRUE(INVALID_HANDLE_VALUE != file); @@ -602,7 +642,7 @@ TEST(FilePolicyTest, TestReparsePoint) { // 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 = ::CreateFile(temp_file_in_temp.c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, 0, nullptr); ASSERT_TRUE(INVALID_HANDLE_VALUE != file); @@ -610,8 +650,7 @@ TEST(FilePolicyTest, TestReparsePoint) { // 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())); + auto runner = ReparsePointRunner(temp_dir_wildcard); // Prepare the command to execute. std::wstring command_write; @@ -620,11 +659,11 @@ TEST(FilePolicyTest, TestReparsePoint) { command_write += L"\""; // Verify that we have write access to the original file - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str())); + 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, + HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); EXPECT_TRUE(INVALID_HANDLE_VALUE != dir); @@ -636,10 +675,11 @@ TEST(FilePolicyTest, TestReparsePoint) { EXPECT_TRUE(::CloseHandle(dir)); // Try to open the file again. - EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str())); + runner = ReparsePointRunner(temp_dir_wildcard); + EXPECT_EQ(SBOX_TEST_DENIED, runner->RunTest(command_write.c_str())); // Remove the reparse point. - dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS, + dir = ::CreateFile(subfolder.c_str(), FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr); diff --git a/chromium/sandbox/win/src/filesystem_dispatcher.cc b/chromium/sandbox/win/src/filesystem_dispatcher.cc index ea1c3314ba2..50dd777bf41 100644 --- a/chromium/sandbox/win/src/filesystem_dispatcher.cc +++ b/chromium/sandbox/win/src/filesystem_dispatcher.cc @@ -95,21 +95,8 @@ bool FilesystemDispatcher::NtCreateFile(IPCInfo* ipc, return true; } - const wchar_t* filename = name->c_str(); - - uint32_t broker = BROKER_TRUE; - CountedParameterSet params; - params[OpenFile::NAME] = ParamPickerMake(filename); - params[OpenFile::ACCESS] = ParamPickerMake(desired_access); - params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition); - params[OpenFile::OPTIONS] = ParamPickerMake(create_options); - params[OpenFile::BROKER] = ParamPickerMake(broker); - - // 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()); + EvalResult result = EvalPolicy(IpcTag::NTCREATEFILE, *name, desired_access, + create_disposition == FILE_OPEN); HANDLE handle; ULONG_PTR io_information = 0; NTSTATUS nt_status; @@ -139,22 +126,8 @@ bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc, return true; } - const wchar_t* filename = name->c_str(); - - uint32_t broker = BROKER_TRUE; - uint32_t create_disposition = FILE_OPEN; - CountedParameterSet params; - params[OpenFile::NAME] = ParamPickerMake(filename); - params[OpenFile::ACCESS] = ParamPickerMake(desired_access); - params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition); - params[OpenFile::OPTIONS] = ParamPickerMake(open_options); - params[OpenFile::BROKER] = ParamPickerMake(broker); - - // 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()); + EvalPolicy(IpcTag::NTOPENFILE, *name, desired_access, true); HANDLE handle; ULONG_PTR io_information = 0; NTSTATUS nt_status; @@ -184,17 +157,7 @@ bool FilesystemDispatcher::NtQueryAttributesFile(IPCInfo* ipc, return true; } - uint32_t broker = BROKER_TRUE; - const wchar_t* filename = name->c_str(); - CountedParameterSet 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()); + EvalResult result = EvalPolicy(IpcTag::NTQUERYATTRIBUTESFILE, *name); FILE_BASIC_INFORMATION* information = reinterpret_cast(info->Buffer()); @@ -224,17 +187,7 @@ bool FilesystemDispatcher::NtQueryFullAttributesFile(IPCInfo* ipc, return true; } - uint32_t broker = BROKER_TRUE; - const wchar_t* filename = name->c_str(); - CountedParameterSet 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()); + EvalResult result = EvalPolicy(IpcTag::NTQUERYFULLATTRIBUTESFILE, *name); FILE_NETWORK_OPEN_INFORMATION* information = reinterpret_cast(info->Buffer()); @@ -277,17 +230,7 @@ bool FilesystemDispatcher::NtSetInformationFile(IPCInfo* ipc, return true; } - uint32_t broker = BROKER_TRUE; - const wchar_t* filename = name.c_str(); - CountedParameterSet 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()); + EvalResult result = EvalPolicy(IpcTag::NTSETINFO_RENAME, name); IO_STATUS_BLOCK* io_status = reinterpret_cast(status->Buffer()); @@ -304,4 +247,17 @@ bool FilesystemDispatcher::NtSetInformationFile(IPCInfo* ipc, return true; } +EvalResult FilesystemDispatcher::EvalPolicy(IpcTag ipc_tag, + const std::wstring& name, + uint32_t desired_access, + bool open_only) { + CountedParameterSet params; + const wchar_t* name_ptr = name.c_str(); + params[OpenFile::NAME] = ParamPickerMake(name_ptr); + params[OpenFile::ACCESS] = ParamPickerMake(desired_access); + uint32_t open_only_int = open_only; + params[OpenFile::OPENONLY] = ParamPickerMake(open_only_int); + return policy_base_->EvalPolicy(ipc_tag, params.GetBase()); +} + } // namespace sandbox diff --git a/chromium/sandbox/win/src/filesystem_dispatcher.h b/chromium/sandbox/win/src/filesystem_dispatcher.h index 6bc5462a204..5b5b147e266 100644 --- a/chromium/sandbox/win/src/filesystem_dispatcher.h +++ b/chromium/sandbox/win/src/filesystem_dispatcher.h @@ -71,6 +71,12 @@ class FilesystemDispatcher : public Dispatcher { uint32_t length, uint32_t info_class); + // Evaluate the sandbox policy for the file system call. + EvalResult EvalPolicy(IpcTag ipc_tag, + const std::wstring& name, + uint32_t desired_access = 0, + bool open_only = true); + raw_ptr policy_base_; }; diff --git a/chromium/sandbox/win/src/filesystem_interception.cc b/chromium/sandbox/win/src/filesystem_interception.cc index 59934ebd59a..4210ba45574 100644 --- a/chromium/sandbox/win/src/filesystem_interception.cc +++ b/chromium/sandbox/win/src/filesystem_interception.cc @@ -18,6 +18,36 @@ namespace sandbox { +namespace { +// This checks for three conditions on whether to ask the broker. +// - The path looks like a DOS device path (namely \??\something). +// - The path looks like a short-name path. +// - Whether the details match the policy. +bool ShouldAskBroker(IpcTag ipc_tag, + const std::unique_ptr& name, + size_t name_len, + uint32_t desired_access = 0, + bool open_only = true) { + const wchar_t* name_ptr = name.get(); + if (name_len >= 4 && name_ptr[0] == L'\\' && name_ptr[1] == L'?' && + name_ptr[2] == L'?' && name_ptr[3] == L'\\') { + return true; + } + + for (size_t index = 0; index < name_len; ++index) { + if (name_ptr[index] == L'~') + return true; + } + + CountedParameterSet params; + params[OpenFile::NAME] = ParamPickerMake(name_ptr); + params[OpenFile::ACCESS] = ParamPickerMake(desired_access); + uint32_t open_only_int = open_only; + params[OpenFile::OPENONLY] = ParamPickerMake(open_only_int); + return QueryBroker(ipc_tag, params.GetBase()); +} +} // namespace + NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, PHANDLE file, ACCESS_MASK desired_access, @@ -52,35 +82,24 @@ NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile, break; std::unique_ptr name; - uint32_t attributes = 0; + size_t name_len; + uint32_t attributes; NTSTATUS ret = - AllocAndCopyName(object_attributes, &name, &attributes, nullptr); - if (!NT_SUCCESS(ret) || !name) + CopyNameAndAttributes(object_attributes, &name, &name_len, &attributes); + if (!NT_SUCCESS(ret) || !name || !name_len) break; - - uint32_t desired_access_uint32 = desired_access; - uint32_t options_uint32 = options; - uint32_t disposition_uint32 = disposition; - uint32_t broker = BROKER_FALSE; - CountedParameterSet 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())) + if (!ShouldAskBroker(IpcTag::NTCREATEFILE, name, name_len, desired_access, + disposition == FILE_OPEN)) { 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); + ResultCode code = CrossCall(ipc, IpcTag::NTCREATEFILE, name.get(), + attributes, desired_access, file_attributes, + sharing, disposition, options, &answer); if (SBOX_ALL_OK != code) break; @@ -129,32 +148,21 @@ NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile, break; std::unique_ptr name; + size_t name_len; uint32_t attributes; NTSTATUS ret = - AllocAndCopyName(object_attributes, &name, &attributes, nullptr); - if (!NT_SUCCESS(ret) || !name) + CopyNameAndAttributes(object_attributes, &name, &name_len, &attributes); + if (!NT_SUCCESS(ret) || !name || !name_len) 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 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())) + if (!ShouldAskBroker(IpcTag::NTOPENFILE, name, name_len, desired_access, + true)) { break; + } SharedMemIPCClient ipc(memory); CrossCallReturn answer = {0}; - ResultCode code = - CrossCall(ipc, IpcTag::NTOPENFILE, name.get(), attributes, - desired_access_uint32, sharing, options_uint32, &answer); + ResultCode code = CrossCall(ipc, IpcTag::NTOPENFILE, name.get(), attributes, + desired_access, sharing, options, &answer); if (SBOX_ALL_OK != code) break; @@ -197,24 +205,17 @@ TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes, break; std::unique_ptr name; - uint32_t attributes = 0; + size_t name_len; + uint32_t attributes; NTSTATUS ret = - AllocAndCopyName(object_attributes, &name, &attributes, nullptr); - if (!NT_SUCCESS(ret) || !name) + CopyNameAndAttributes(object_attributes, &name, &name_len, &attributes); + if (!NT_SUCCESS(ret) || !name || !name_len) + break; + if (!ShouldAskBroker(IpcTag::NTQUERYATTRIBUTESFILE, name, name_len)) break; InOutCountedBuffer file_info(file_attributes, sizeof(FILE_BASIC_INFORMATION)); - - uint32_t broker = BROKER_FALSE; - CountedParameterSet 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(), @@ -254,24 +255,17 @@ NTSTATUS WINAPI TargetNtQueryFullAttributesFile( break; std::unique_ptr name; - uint32_t attributes = 0; + size_t name_len; + uint32_t attributes; NTSTATUS ret = - AllocAndCopyName(object_attributes, &name, &attributes, nullptr); - if (!NT_SUCCESS(ret) || !name) + CopyNameAndAttributes(object_attributes, &name, &name_len, &attributes); + if (!NT_SUCCESS(ret) || !name || !name_len) + break; + if (!ShouldAskBroker(IpcTag::NTQUERYFULLATTRIBUTESFILE, name, name_len)) break; InOutCountedBuffer file_info(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION)); - - uint32_t broker = BROKER_FALSE; - CountedParameterSet 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, @@ -334,18 +328,11 @@ TargetNtSetInformationFile(NtSetInformationFileFunction orig_SetInformationFile, } std::unique_ptr name; - NTSTATUS ret = - AllocAndCopyName(&object_attributes, &name, nullptr, nullptr); - if (!NT_SUCCESS(ret) || !name) + size_t name_len; + NTSTATUS ret = CopyNameAndAttributes(&object_attributes, &name, &name_len); + if (!NT_SUCCESS(ret) || !name || !name_len) break; - - uint32_t broker = BROKER_FALSE; - CountedParameterSet 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())) + if (!ShouldAskBroker(IpcTag::NTSETINFO_RENAME, name, name_len)) break; InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK)); diff --git a/chromium/sandbox/win/src/filesystem_policy.cc b/chromium/sandbox/win/src/filesystem_policy.cc index fe995b7be70..0560ad31d62 100644 --- a/chromium/sandbox/win/src/filesystem_policy.cc +++ b/chromium/sandbox/win/src/filesystem_policy.cc @@ -15,6 +15,7 @@ #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_nt_util.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/sandbox_utils.h" #include "sandbox/win/src/win_utils.h" @@ -32,14 +33,11 @@ NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, 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); + NTSTATUS status = sandbox::GetNtExports()->CreateFile( + &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; } @@ -82,6 +80,7 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, return false; } + bool is_pipe = IsPipe(mod_name); if (!PreProcessName(&mod_name)) { // The path to be added might contain a reparse point. NOTREACHED(); @@ -116,11 +115,6 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, 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. @@ -129,9 +123,9 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL; DWORD restricted_flags = ~allowed_flags; open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); - open.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL); + open.AddNumberMatch(IF, OpenFile::OPENONLY, true, EQUAL); create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); - create.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL); + create.AddNumberMatch(IF, OpenFile::OPENONLY, true, EQUAL); // Read only access don't work for rename. rule_to_add &= ~kCallNtSetInfoRename; @@ -165,19 +159,19 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, } if ((rule_to_add & kCallNtQueryAttributesFile) && - (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || + (!query.AddStringMatch(IF, OpenFile::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) || + (!query_full.AddStringMatch(IF, OpenFile::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) || + if ((rule_to_add & kCallNtSetInfoRename) && !is_pipe && + (!rename.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || !policy->AddRule(IpcTag::NTSETINFO_RENAME, &rename))) { return false; } @@ -185,60 +179,6 @@ bool FileSystemPolicy::GenerateRules(const wchar_t* name, 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, @@ -322,16 +262,13 @@ bool FileSystemPolicy::QueryAttributesFileAction( 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); + *nt_status = GetNtExports()->QueryAttributesFile(&obj_attributes, file_info); return true; } @@ -349,17 +286,14 @@ bool FileSystemPolicy::QueryFullAttributesFileAction( *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); + *nt_status = + GetNtExports()->QueryFullAttributesFile(&obj_attributes, file_info); return true; } @@ -379,9 +313,6 @@ bool FileSystemPolicy::SetInformationFileAction(EvalResult eval_result, 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, @@ -394,8 +325,8 @@ bool FileSystemPolicy::SetInformationFileAction(EvalResult eval_result, FILE_INFORMATION_CLASS file_info_class = static_cast(info_class); - *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length, - file_info_class); + *nt_status = GetNtExports()->SetInformationFile( + local_handle, io_block, file_info, length, file_info_class); return true; } diff --git a/chromium/sandbox/win/src/filesystem_policy.h b/chromium/sandbox/win/src/filesystem_policy.h index 38d7484e269..e42b245f193 100644 --- a/chromium/sandbox/win/src/filesystem_policy.h +++ b/chromium/sandbox/win/src/filesystem_policy.h @@ -16,8 +16,6 @@ namespace sandbox { -enum IsBroker { BROKER_FALSE, BROKER_TRUE }; - // This class centralizes most of the knowledge related to file system policy class FileSystemPolicy { public: @@ -30,9 +28,6 @@ class FileSystemPolicy { 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. diff --git a/chromium/sandbox/win/src/handle_closer.cc b/chromium/sandbox/win/src/handle_closer.cc index 841a81506b3..56e27d20826 100644 --- a/chromium/sandbox/win/src/handle_closer.cc +++ b/chromium/sandbox/win/src/handle_closer.cc @@ -11,10 +11,6 @@ #include "base/check_op.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 { @@ -157,29 +153,4 @@ bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) { 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 name; - NTSTATUS result; - - do { - name.reset(static_cast(malloc(size))); - DCHECK(name.get()); - result = - QueryObject(handle, ObjectNameInformation, name.get(), size, &size); - } while (result == STATUS_INFO_LENGTH_MISMATCH || - result == STATUS_BUFFER_OVERFLOW); - - if (NT_SUCCESS(result) && name->Buffer && name->Length) - handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t)); - else - handle_name->clear(); - - return NT_SUCCESS(result); -} - } // namespace sandbox diff --git a/chromium/sandbox/win/src/handle_closer.h b/chromium/sandbox/win/src/handle_closer.h index 4bfab944178..ef885dc664e 100644 --- a/chromium/sandbox/win/src/handle_closer.h +++ b/chromium/sandbox/win/src/handle_closer.h @@ -72,9 +72,6 @@ class HandleCloser { HandleMap handles_to_close_; }; -// Returns the object manager's name associated with a handle -bool GetHandleName(HANDLE handle, std::wstring* handle_name); - } // namespace sandbox #endif // SANDBOX_WIN_SRC_HANDLE_CLOSER_H_ diff --git a/chromium/sandbox/win/src/handle_closer_agent.cc b/chromium/sandbox/win/src/handle_closer_agent.cc index 045b17ac397..a0bb6569b31 100644 --- a/chromium/sandbox/win/src/handle_closer_agent.cc +++ b/chromium/sandbox/win/src/handle_closer_agent.cc @@ -4,36 +4,13 @@ #include "sandbox/win/src/handle_closer_agent.h" -#include #include #include "base/check.h" #include "base/win/static_constants.h" -#include "sandbox/win/src/nt_internals.h" +#include "base/win/windows_version.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 +#include "third_party/abseil-cpp/absl/types/optional.h" namespace sandbox { @@ -165,69 +142,42 @@ void HandleCloserAgent::InitializeHandlesToClose(bool* is_csrss_connected) { } 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; + // If the accurate handle enumeration fails then fallback to the old brute + // force approach. This should only happen on Windows 7. + absl::optional handle_map = GetCurrentProcessHandles(); + if (!handle_map) { + DCHECK(base::win::GetVersion() < base::win::Version::WIN8); + handle_map = GetCurrentProcessHandlesWin7(); + } - // Set up buffers for the type info and the name. - std::vector type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + - 32 * sizeof(wchar_t)); - OBJECT_TYPE_INFORMATION* type_info = - reinterpret_cast(&(type_info_buffer[0])); - 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(handle) += kHandleOffset; - NTSTATUS rc; - - // Get the type name, reusing the buffer. - ULONG size = static_cast(type_info_buffer.size()); - rc = QueryObjectTypeInformation(handle, type_info, &size); - while (rc == STATUS_INFO_LENGTH_MISMATCH || rc == STATUS_BUFFER_OVERFLOW) { - type_info_buffer.resize(size + sizeof(wchar_t)); - type_info = - reinterpret_cast(&(type_info_buffer[0])); - rc = QueryObjectTypeInformation(handle, type_info, &size); - // Leave padding for the nul terminator. - if (NT_SUCCESS(rc) && size == type_info_buffer.size()) - rc = STATUS_INFO_LENGTH_MISMATCH; - } - 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'; + if (!handle_map) + return false; - // 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; + for (const HandleMap::value_type& handle_to_close : handles_to_close_) { + ProcessHandleMap::iterator result = handle_map->find(handle_to_close.first); + if (result == handle_map->end()) + continue; + const HandleMap::mapped_type& names = handle_to_close.second; + for (HANDLE handle : result->second) { // Empty set means close all handles of this type; otherwise check name. if (!names.empty()) { + std::wstring handle_name; // Move on to the next handle if this name doesn't match. - if (!GetHandleName(handle, &handle_name) || !names.count(handle_name)) + if (!GetPathFromHandle(handle, &handle_name) || + !names.count(handle_name)) { continue; + } } + // If we can't unprotect or close the handle we should keep going. if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0)) - return false; + continue; if (!::CloseHandle(handle)) - return false; + continue; // Attempt to stuff this handle with a new dummy Event. AttemptToStuffHandleSlot(handle, result->first); } diff --git a/chromium/sandbox/win/src/handle_closer_test.cc b/chromium/sandbox/win/src/handle_closer_test.cc index 7406f77592a..abfb21ca88f 100644 --- a/chromium/sandbox/win/src/handle_closer_test.cc +++ b/chromium/sandbox/win/src/handle_closer_test.cc @@ -5,6 +5,7 @@ #include #include +#include "base/strings/string_util_win.h" #include "base/strings/stringprintf.h" #include "base/win/scoped_handle.h" #include "sandbox/win/src/handle_closer_agent.h" @@ -12,6 +13,7 @@ #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/target_services.h" +#include "sandbox/win/src/win_utils.h" #include "sandbox/win/tests/common/controller.h" #include "testing/gtest/include/gtest/gtest.h" @@ -45,25 +47,6 @@ HANDLE GetMarkerFile(const wchar_t* extension) { 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; @@ -105,7 +88,7 @@ SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t** argv) { while (handle_count && invalid_count < kInvalidHandleThreshold) { reinterpret_cast(handle) += kHandleOffset; - if (GetHandleName(handle, &handle_name)) { + if (GetPathFromHandle(handle, &handle_name)) { for (int i = 1; i < argc; ++i) { if (handle_name == argv[i]) return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED; @@ -144,36 +127,9 @@ SBOX_TESTS_COMMAND int CheckForEventHandles(int argc, wchar_t** argv) { case AFTER_REVERT: for (HANDLE handle : to_check) { - // Set up buffers for the type info and the name. - std::vector type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) + - 32 * sizeof(wchar_t)); - OBJECT_TYPE_INFORMATION* type_info = - reinterpret_cast(&(type_info_buffer[0])); - NTSTATUS rc; - - // Get the type name, reusing the buffer. - ULONG size = static_cast(type_info_buffer.size()); - rc = QueryObjectTypeInformation(handle, type_info, &size); - while (rc == STATUS_INFO_LENGTH_MISMATCH || - rc == STATUS_BUFFER_OVERFLOW) { - type_info_buffer.resize(size + sizeof(wchar_t)); - type_info = reinterpret_cast( - &(type_info_buffer[0])); - rc = QueryObjectTypeInformation(handle, type_info, &size); - // Leave padding for the nul terminator. - if (NT_SUCCESS(rc) && size == type_info_buffer.size()) - rc = STATUS_INFO_LENGTH_MISMATCH; - } - - CHECK(NT_SUCCESS(rc)); - CHECK(type_info->Name.Buffer); - - type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = - L'\0'; - - // Should be an Event now. - CHECK_EQ(wcslen(type_info->Name.Buffer), 5U); - CHECK_EQ(wcscmp(L"Event", type_info->Name.Buffer), 0); + std::wstring type_name; + CHECK(GetTypeNameFromHandle(handle, &type_name)); + CHECK(base::EqualsCaseInsensitiveASCII(type_name, L"Event")); // Should not be able to wait. CHECK_EQ(WaitForSingleObject(handle, INFINITE), WAIT_FAILED); @@ -200,7 +156,7 @@ TEST(HandleCloserTest, CheckForMarkerFiles) { std::wstring handle_name; base::win::ScopedHandle marker(GetMarkerFile(kExtension)); CHECK(marker.IsValid()); - CHECK(sandbox::GetHandleName(marker.Get(), &handle_name)); + CHECK(GetPathFromHandle(marker.Get(), &handle_name)); command += (L" "); command += handle_name; } @@ -220,7 +176,7 @@ TEST(HandleCloserTest, CloseMarkerFiles) { std::wstring handle_name; base::win::ScopedHandle marker(GetMarkerFile(kExtension)); CHECK(marker.IsValid()); - CHECK(sandbox::GetHandleName(marker.Get(), &handle_name)); + CHECK(GetPathFromHandle(marker.Get(), &handle_name)); CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()), SBOX_ALL_OK); command += (L" "); @@ -241,7 +197,7 @@ TEST(HandleCloserTest, CheckStuffedHandle) { std::wstring handle_name; base::win::ScopedHandle marker(GetMarkerFile(kExtension)); CHECK(marker.IsValid()); - CHECK(sandbox::GetHandleName(marker.Get(), &handle_name)); + CHECK(GetPathFromHandle(marker.Get(), &handle_name)); CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()), SBOX_ALL_OK); } diff --git a/chromium/sandbox/win/src/integrity_level_test.cc b/chromium/sandbox/win/src/integrity_level_test.cc index 0fa2627e897..b698cc1200f 100644 --- a/chromium/sandbox/win/src/integrity_level_test.cc +++ b/chromium/sandbox/win/src/integrity_level_test.cc @@ -42,31 +42,39 @@ SBOX_TESTS_COMMAND int CheckIntegrityLevel(int argc, wchar_t** argv) { return SBOX_TEST_DENIED; } -TEST(IntegrityLevelTest, TestLowILReal) { - TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); - - runner.SetTimeout(INFINITE); +std::unique_ptr LowILRealRunner() { + auto runner = std::make_unique(JOB_LOCKDOWN, USER_INTERACTIVE, + USER_INTERACTIVE); + runner->SetTimeout(INFINITE); + runner->GetPolicy()->SetAlternateDesktop(true); + runner->GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); + return runner; +} - runner.GetPolicy()->SetAlternateDesktop(true); - runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW); +TEST(IntegrityLevelTest, TestLowILReal) { + auto runner = LowILRealRunner(); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"CheckIntegrityLevel")); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); + runner = LowILRealRunner(); + runner->SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"CheckIntegrityLevel")); +} - runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel")); +std::unique_ptr LowILDelayedRunner() { + auto runner = std::make_unique(JOB_LOCKDOWN, USER_INTERACTIVE, + USER_INTERACTIVE); + runner->SetTimeout(INFINITE); + runner->GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW); + return runner; } TEST(DelayedIntegrityLevelTest, TestLowILDelayed) { - TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE); - - runner.SetTimeout(INFINITE); + auto runner = LowILDelayedRunner(); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"CheckIntegrityLevel")); - 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")); + runner = LowILDelayedRunner(); + runner->SetTestState(BEFORE_REVERT); + EXPECT_EQ(SBOX_TEST_DENIED, runner->RunTest(L"CheckIntegrityLevel")); } TEST(IntegrityLevelTest, TestNoILChange) { diff --git a/chromium/sandbox/win/src/interception_agent.cc b/chromium/sandbox/win/src/interception_agent.cc index e095328a710..00d7c283e7a 100644 --- a/chromium/sandbox/win/src/interception_agent.cc +++ b/chromium/sandbox/win/src/interception_agent.cc @@ -15,7 +15,6 @@ #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 { @@ -29,9 +28,6 @@ bool IsWithinRange(const void* base, size_t range, const void* target) { 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; @@ -68,21 +64,18 @@ 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(g_nt.wcslen(dll_info->dll_name) * sizeof(wchar_t)); + current_name.Length = static_cast( + GetNtExports()->wcslen(dll_info->dll_name) * sizeof(wchar_t)); current_name.MaximumLength = current_name.Length; current_name.Buffer = const_cast(dll_info->dll_name); BOOLEAN case_insensitive = TRUE; - if (full_path && - !g_nt.RtlCompareUnicodeString(¤t_name, full_path, case_insensitive)) - return true; - - if (name && - !g_nt.RtlCompareUnicodeString(¤t_name, name, case_insensitive)) + if (full_path && !GetNtExports()->RtlCompareUnicodeString( + ¤t_name, full_path, case_insensitive)) { return true; - - return false; + } + return name && !GetNtExports()->RtlCompareUnicodeString(¤t_name, name, + case_insensitive); } bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, @@ -129,9 +122,9 @@ bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, 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)); + VERIFY_SUCCESS(GetNtExports()->ProtectVirtualMemory( + NtCurrentProcess, &to_protect, &real_size, PAGE_EXECUTE_READ, + &old_protect)); return true; } @@ -168,7 +161,7 @@ bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info, return false; const char* interceptor = - function->function + g_nt.strlen(function->function) + 1; + function->function + GetNtExports()->strlen(function->function) + 1; if (!IsWithinRange(function, function->record_bytes, interceptor) || !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) { @@ -202,32 +195,12 @@ bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info, // 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(); - } - + if (type == INTERCEPTION_EAT) + return eat_resolver; + NOTREACHED_NT(); return nullptr; } diff --git a/chromium/sandbox/win/src/interception_unittest.cc b/chromium/sandbox/win/src/interception_unittest.cc index f898a2dda2b..1cbde5c5d10 100644 --- a/chromium/sandbox/win/src/interception_unittest.cc +++ b/chromium/sandbox/win/src/interception_unittest.cc @@ -146,9 +146,6 @@ TEST(InterceptionManagerTest, BufferLayout1) { 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", @@ -163,15 +160,10 @@ TEST(InterceptionManagerTest, BufferLayout1) { 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, @@ -185,7 +177,7 @@ TEST(InterceptionManagerTest, BufferLayout1) { function, OPEN_KEY_ID); // Verify that all interceptions were added - ASSERT_EQ(18u, interceptions.interceptions_.size()); + ASSERT_EQ(15u, interceptions.interceptions_.size()); size_t buffer_size = interceptions.GetBufferSize(); std::unique_ptr local_buffer(new BYTE[buffer_size]); @@ -198,20 +190,20 @@ TEST(InterceptionManagerTest, BufferLayout1) { // 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()); + // "interceptions"). There are 2 local interceptions (of ntdll); the + // other 13 have to be sent to the child to be performed "hot". + EXPECT_EQ(2u, 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 + // The 13 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); + EXPECT_EQ(13, num_functions); + EXPECT_EQ(3, num_names); } TEST(InterceptionManagerTest, BufferLayout2) { @@ -233,11 +225,8 @@ TEST(InterceptionManagerTest, BufferLayout2) { 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()); + ASSERT_EQ(4u, interceptions.interceptions_.size()); size_t buffer_size = interceptions.GetBufferSize(); std::unique_ptr local_buffer(new BYTE[buffer_size]); @@ -256,7 +245,7 @@ TEST(InterceptionManagerTest, BufferLayout2) { &num_names); EXPECT_EQ(3, num_dlls); - EXPECT_EQ(4, num_functions); + EXPECT_EQ(3, num_functions); EXPECT_EQ(0, num_names); } diff --git a/chromium/sandbox/win/src/interceptors_64.cc b/chromium/sandbox/win/src/interceptors_64.cc index ba1726bdc47..b355f0d69e1 100644 --- a/chromium/sandbox/win/src/interceptors_64.cc +++ b/chromium/sandbox/win/src/interceptors_64.cc @@ -17,7 +17,6 @@ namespace sandbox { -SANDBOX_INTERCEPT NtExports g_nt; SANDBOX_INTERCEPT OriginalFunctions g_originals; NTSTATUS WINAPI TargetNtMapViewOfSection64(HANDLE section, diff --git a/chromium/sandbox/win/src/ipc_ping_test.cc b/chromium/sandbox/win/src/ipc_ping_test.cc index 44f6be433d0..39aa63226d1 100644 --- a/chromium/sandbox/win/src/ipc_ping_test.cc +++ b/chromium/sandbox/win/src/ipc_ping_test.cc @@ -48,11 +48,14 @@ TEST(IPCTest, IPCPingTestSimple) { } 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")); + TestRunner runner1; + runner1.SetTimeout(2000); + runner1.SetTestState(EVERY_STATE); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner1.RunTest(L"IPC_Ping 2")); + TestRunner runner2; + runner2.SetTimeout(2000); + runner2.SetTestState(EVERY_STATE); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(L"IPC_Ping 2")); } } // namespace sandbox diff --git a/chromium/sandbox/win/src/job.cc b/chromium/sandbox/win/src/job.cc index 7ef429d8291..d12a565f735 100644 --- a/chromium/sandbox/win/src/job.cc +++ b/chromium/sandbox/win/src/job.cc @@ -39,26 +39,26 @@ DWORD Job::Init(JobLevel security_level, case JOB_LOCKDOWN: { jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION; - FALLTHROUGH; + [[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; + [[fallthrough]]; } case JOB_LIMITED_USER: { jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS; jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS; jeli.BasicLimitInformation.ActiveProcessLimit = 1; - FALLTHROUGH; + [[fallthrough]]; } case JOB_INTERACTIVE: { jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS; jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP; jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; - FALLTHROUGH; + [[fallthrough]]; } case JOB_UNPROTECTED: { if (memory_limit) { diff --git a/chromium/sandbox/win/src/named_pipe_policy_test.cc b/chromium/sandbox/win/src/named_pipe_policy_test.cc index db532d618d6..817aa43580b 100644 --- a/chromium/sandbox/win/src/named_pipe_policy_test.cc +++ b/chromium/sandbox/win/src/named_pipe_policy_test.cc @@ -3,10 +3,10 @@ // 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/src/win_utils.h" #include "sandbox/win/tests/common/controller.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,7 +26,7 @@ SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t** argv) { // pipe should be in the object namespace after creation. if (argc == 2) { std::wstring handle_name; - if (GetHandleName(pipe, &handle_name)) { + if (GetPathFromHandle(pipe, &handle_name)) { if (handle_name.compare(0, wcslen(argv[1]), argv[1]) != 0) return SBOX_TEST_FAILED; } else { @@ -52,39 +52,52 @@ SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t** argv) { return SBOX_TEST_SUCCEEDED; } -// Tests if we can create a pipe in the sandbox. -TEST(NamedPipePolicyTest, CreatePipe) { - TestRunner runner; +std::unique_ptr CreatePipeRunner() { + auto runner = std::make_unique(); // 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*")); + runner->AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, + TargetPolicy::NAMEDPIPES_ALLOW_ANY, L"\\\\.\\pipe\\test*"); + return runner; +} +// Tests if we can create a pipe in the sandbox. +TEST(NamedPipePolicyTest, CreatePipe) { + auto runner = CreatePipeRunner(); EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); + runner = CreatePipeRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh")); + 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; +std::unique_ptr PipeTraversalRunner() { + auto runner = std::make_unique(); // 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*")); + runner->AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, + TargetPolicy::NAMEDPIPES_ALLOW_ANY, L"\\\\.\\pipe\\test*"); + return runner; +} +// Tests if we can create a pipe with a path traversal in the sandbox. +TEST(NamedPipePolicyTest, CreatePipeTraversal) { + auto runner = PipeTraversalRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\..\\bleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\..\\bleh")); + + runner = PipeTraversalRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/../bleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/../bleh")); + + runner = PipeTraversalRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\../bleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\../bleh")); + + runner = PipeTraversalRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/..\\bleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/..\\bleh")); } // This tests that path canonicalization is actually disabled if we use \\?\ @@ -100,22 +113,25 @@ TEST(NamedPipePolicyTest, CreatePipeCanonicalization) { NamedPipe_Create(2, const_cast(argv))); } -// The same test as CreatePipe but this time using strict interceptions. -TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) { - TestRunner runner; - runner.GetPolicy()->SetStrictInterceptions(); - +std::unique_ptr StrictInterceptionsRunner() { + auto runner = std::make_unique(); + 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*")); + runner->AddRule(TargetPolicy::SUBSYS_NAMED_PIPES, + TargetPolicy::NAMEDPIPES_ALLOW_ANY, L"\\\\.\\pipe\\test*"); + return runner; +} +// The same test as CreatePipe but this time using strict interceptions. +TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) { + auto runner = StrictInterceptionsRunner(); EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh")); + runner = StrictInterceptionsRunner(); EXPECT_EQ(SBOX_TEST_DENIED, - runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh")); + runner->RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh")); } } // namespace sandbox diff --git a/chromium/sandbox/win/src/nt_internals.h b/chromium/sandbox/win/src/nt_internals.h index 46d489fe2c5..f9847fc5b05 100644 --- a/chromium/sandbox/win/src/nt_internals.h +++ b/chromium/sandbox/win/src/nt_internals.h @@ -313,7 +313,8 @@ typedef NTSTATUS(WINAPI* NtSetInformationThreadFunction)( // Partial definition only: typedef enum _PROCESSINFOCLASS { ProcessBasicInformation = 0, - ProcessExecuteFlags = 0x22 + ProcessExecuteFlags = 0x22, + ProcessHandleTable = 0x3A } PROCESSINFOCLASS; // For the structure documentation, see @@ -723,13 +724,6 @@ typedef NTSTATUS(WINAPI* NtQuerySystemInformation)( 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 diff --git a/chromium/sandbox/win/src/policy_broker.cc b/chromium/sandbox/win/src/policy_broker.cc index 1f9fa6b613b..51e71a3b097 100644 --- a/chromium/sandbox/win/src/policy_broker.cc +++ b/chromium/sandbox/win/src/policy_broker.cc @@ -18,6 +18,7 @@ #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_nt_util.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/target_process.h" @@ -26,65 +27,9 @@ 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( \ - ntdll_image.GetProcAddress("Nt" #member)); \ - if (!g_nt.member) \ - return false - -#define INIT_GLOBAL_RTL(member) \ - g_nt.member = \ - reinterpret_cast(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(&g_nt)[i]); -#endif - return (SBOX_ALL_OK == child.TransferVariable("g_nt", &g_nt, sizeof(g_nt))); + return (SBOX_ALL_OK == + child.TransferVariable("g_nt", GetNtExports(), sizeof(NtExports))); } #undef INIT_GLOBAL_NT diff --git a/chromium/sandbox/win/src/policy_broker.h b/chromium/sandbox/win/src/policy_broker.h index 913a57a2c76..dbdac2cb3da 100644 --- a/chromium/sandbox/win/src/policy_broker.h +++ b/chromium/sandbox/win/src/policy_broker.h @@ -11,9 +11,6 @@ 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); diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.cc b/chromium/sandbox/win/src/policy_engine_opcodes.cc index d3ed12f3c57..931dad169be 100644 --- a/chromium/sandbox/win/src/policy_engine_opcodes.cc +++ b/chromium/sandbox/win/src/policy_engine_opcodes.cc @@ -9,6 +9,7 @@ #include "base/check_op.h" #include "sandbox/win/src/sandbox_nt_types.h" +#include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sandbox_types.h" namespace { @@ -30,8 +31,6 @@ bool InitStringUnicode(const wchar_t* source, 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 @@ -275,7 +274,7 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, // 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(g_nt.wcslen(source_str)); + int source_len = static_cast(GetNtExports()->wcslen(source_str)); if (0 == source_len) { // If we reached the end of the source string there is nothing we can @@ -320,8 +319,8 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, !InitStringUnicode(source_str, match_len, &source_ustr)) return EVAL_ERROR; - if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, - case_sensitive)) { + if (0 == GetNtExports()->RtlCompareUnicodeString(&match_ustr, &source_ustr, + case_sensitive)) { // Match! update the match context. context->position += start_position + match_len; return EVAL_TRUE; @@ -336,8 +335,8 @@ EvalResult OpcodeEval(PolicyOpcode* opcode, return EVAL_ERROR; do { - if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr, - case_sensitive)) { + if (0 == GetNtExports()->RtlCompareUnicodeString( + &match_ustr, &source_ustr, case_sensitive)) { // Match! update the match context. context->position += (source_ustr.Buffer - source_str) + match_len; return EVAL_TRUE; diff --git a/chromium/sandbox/win/src/policy_engine_unittest.cc b/chromium/sandbox/win/src/policy_engine_unittest.cc index b8b20021083..cd36c767602 100644 --- a/chromium/sandbox/win/src/policy_engine_unittest.cc +++ b/chromium/sandbox/win/src/policy_engine_unittest.cc @@ -15,11 +15,7 @@ namespace sandbox { -bool SetupNtdllImports(); - TEST(PolicyEngineTest, Rules1) { - SetupNtdllImports(); - // Construct two policy rules that say: // // #1 diff --git a/chromium/sandbox/win/src/policy_low_level.cc b/chromium/sandbox/win/src/policy_low_level.cc index 79ef57cb43c..471d641c8d6 100644 --- a/chromium/sandbox/win/src/policy_low_level.cc +++ b/chromium/sandbox/win/src/policy_low_level.cc @@ -267,7 +267,7 @@ bool PolicyRule::AddStringMatch(RuleType rule_type, if (L'?' == current_char[1]) { ++current_char; } - FALLTHROUGH; + [[fallthrough]]; default: fragment += *current_char; last_char = kLastCharIsAlpha; diff --git a/chromium/sandbox/win/src/policy_low_level_unittest.cc b/chromium/sandbox/win/src/policy_low_level_unittest.cc index 84846600961..53b7c10d291 100644 --- a/chromium/sandbox/win/src/policy_low_level_unittest.cc +++ b/chromium/sandbox/win/src/policy_low_level_unittest.cc @@ -17,11 +17,8 @@ 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)); @@ -33,7 +30,6 @@ TEST(PolicyEngineTest, StringPatternsOK) { // 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)); @@ -54,7 +50,6 @@ PolicyGlobal* MakePolicyMemory() { // 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)); @@ -87,7 +82,6 @@ TEST(PolicyEngineTest, SimpleStrMatch) { } TEST(PolicyEngineTest, SimpleIfNotStrMatch) { - SetupNtdllImports(); PolicyRule pr(ASK_BROKER); EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\", CASE_SENSITIVE)); @@ -124,7 +118,6 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatch) { } TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) { - SetupNtdllImports(); PolicyRule pr(ASK_BROKER); EXPECT_TRUE( pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", CASE_SENSITIVE)); @@ -157,7 +150,6 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) { } TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) { - SetupNtdllImports(); PolicyRule pr(ASK_BROKER); EXPECT_TRUE( pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*.txt", CASE_SENSITIVE)); @@ -195,7 +187,6 @@ TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) { } TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) { - SetupNtdllImports(); PolicyRule pr(ASK_BROKER); EXPECT_TRUE( pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", CASE_SENSITIVE)); @@ -243,7 +234,6 @@ TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) { } TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) { - SetupNtdllImports(); PolicyRule pr(ASK_BROKER); EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL)); EXPECT_TRUE( @@ -315,7 +305,6 @@ TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) { // 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)); @@ -386,7 +375,6 @@ TEST(PolicyEngineTest, OneRuleTest) { // 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)); @@ -590,7 +578,6 @@ TEST(PolicyEngineTest, ThreeRulesTest) { } 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)); @@ -633,7 +620,6 @@ TEST(PolicyEngineTest, PolicyRuleCopyConstructorTwoStrings) { } 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)); diff --git a/chromium/sandbox/win/src/policy_opcodes_unittest.cc b/chromium/sandbox/win/src/policy_opcodes_unittest.cc index c83efcba18a..4336c6a8afc 100644 --- a/chromium/sandbox/win/src/policy_opcodes_unittest.cc +++ b/chromium/sandbox/win/src/policy_opcodes_unittest.cc @@ -9,37 +9,14 @@ #include "sandbox/win/src/policy_engine_params.h" #include "sandbox/win/src/sandbox_nt_types.h" +#include "sandbox/win/src/sandbox_nt_util.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(::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(0x477EAA5); const void* pv2 = reinterpret_cast(0x987654); @@ -151,7 +128,6 @@ TEST(PolicyEngineTest, OpcodeMakerCase1) { } 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 @@ -236,8 +212,6 @@ TEST(PolicyEngineTest, LogicalOpcodes) { } 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"; @@ -324,8 +298,6 @@ TEST(PolicyEngineTest, WCharOpcodes1) { } 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); diff --git a/chromium/sandbox/win/src/policy_params.h b/chromium/sandbox/win/src/policy_params.h index 485ecb14c80..ae48d57a162 100644 --- a/chromium/sandbox/win/src/policy_params.h +++ b/chromium/sandbox/win/src/policy_params.h @@ -18,26 +18,13 @@ class ParameterSet; #define POLPARAMS_END(type) PolParamLast }; }; \ typedef sandbox::ParameterSet type##Array [type::PolParamLast]; -// Policy parameters for file open / create. +// Policy parameters for file access. POLPARAMS_BEGIN(OpenFile) POLPARAM(NAME) - POLPARAM(BROKER) // true if called from the broker. POLPARAM(ACCESS) - POLPARAM(DISPOSITION) - POLPARAM(OPTIONS) + POLPARAM(OPENONLY) 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(FileName::NAME), - "to simplify fs policies"); -static_assert(OpenFile::BROKER == static_cast(FileName::BROKER), - "to simplify fs policies"); - // Policy parameter for name-based policies. POLPARAMS_BEGIN(NameBased) POLPARAM(NAME) diff --git a/chromium/sandbox/win/src/policy_target.cc b/chromium/sandbox/win/src/policy_target.cc index 5cf894a4482..2be9bd0b7ba 100644 --- a/chromium/sandbox/win/src/policy_target.cc +++ b/chromium/sandbox/win/src/policy_target.cc @@ -18,12 +18,6 @@ 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; diff --git a/chromium/sandbox/win/src/policy_target_test.cc b/chromium/sandbox/win/src/policy_target_test.cc index b490cc40c73..1712142a4d6 100644 --- a/chromium/sandbox/win/src/policy_target_test.cc +++ b/chromium/sandbox/win/src/policy_target_test.cc @@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "base/win/scoped_process_information.h" #include "base/win/windows_version.h" +#include "build/build_config.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/sandbox_utils.h" @@ -16,7 +17,7 @@ #include "sandbox/win/tests/common/controller.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_WIN) +#if BUILDFLAG(IS_WIN) #include "base/win/win_util.h" #endif @@ -179,11 +180,13 @@ TEST(PolicyTargetTest, SetInformationThread) { 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")); + TestRunner runner1; + runner1.SetTestState(AFTER_REVERT); + EXPECT_EQ(ERROR_NO_TOKEN, runner1.RunTest(L"PolicyTargetTest_token")); - runner.SetTestState(EVERY_STATE); - EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal")); + TestRunner runner2; + runner2.SetTestState(EVERY_STATE); + EXPECT_EQ(SBOX_TEST_FAILED, runner2.RunTest(L"PolicyTargetTest_steal")); } TEST(PolicyTargetTest, OpenThreadToken) { @@ -191,18 +194,19 @@ TEST(PolicyTargetTest, OpenThreadToken) { 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")); + TestRunner runner2; + runner2.SetTestState(AFTER_REVERT); + EXPECT_EQ(ERROR_NO_TOKEN, runner2.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")); + TestRunner runner2; + runner2.SetTestState(AFTER_REVERT); + EXPECT_EQ(ERROR_NO_TOKEN, runner2.RunTest(L"PolicyTargetTest_token3")); } TEST(PolicyTargetTest, OpenThread) { @@ -210,7 +214,8 @@ TEST(PolicyTargetTest, OpenThread) { EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) << "Opens the current thread"; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2")) + TestRunner runner2; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(L"PolicyTargetTest_thread2")) << "Creates a new thread and opens it"; } diff --git a/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc b/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc index 21d7021f343..b4dc8f2fd1a 100644 --- a/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc +++ b/chromium/sandbox/win/src/process_mitigations_dyncode_unittest.cc @@ -244,6 +244,15 @@ class DynamicCodeOptOutThread { int return_code_; }; +// Helpers to set up rules for dynamic code tests, needed as policy +// (from the TestRunner) can only be applied to a single process. +std::unique_ptr RunnerWithMitigation( + sandbox::MitigationFlags mitigations) { + auto runner = std::make_unique(); + runner->GetPolicy()->SetDelayedProcessMitigations(mitigations); + return runner; +} + //------------------------------------------------------------------------------ // DisableDynamicCode test harness helper function. Tests numerous APIs. // - APIs fail with ERROR_DYNAMIC_CODE_BLOCKED if this mitigation is @@ -265,14 +274,6 @@ void DynamicCodeTestHarness(sandbox::MitigationFlags which_mitigation, return; } - sandbox::TestRunner runner; - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - if (enable_mitigation) { - EXPECT_EQ(policy->SetDelayedProcessMitigations(which_mitigation), - sandbox::SBOX_ALL_OK); - } - std::wstring shared = (which_mitigation == sandbox::MITIGATION_DYNAMIC_CODE_DISABLE) ? L"TestWin81DynamicCode " @@ -283,32 +284,34 @@ void DynamicCodeTestHarness(sandbox::MitigationFlags which_mitigation, } // Test 1: + auto runner = enable_mitigation ? RunnerWithMitigation(which_mitigation) + : std::make_unique(); std::wstring test = base::StringPrintf(L"%ls %u", shared.c_str(), VIRTUALALLOC); EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED : ERROR_DYNAMIC_CODE_BLOCKED), - runner.RunTest(test.c_str())); + runner->RunTest(test.c_str())); // Test 2: + runner = enable_mitigation ? RunnerWithMitigation(which_mitigation) + : std::make_unique(); test = base::StringPrintf(L"%ls %u", shared.c_str(), VIRTUALPROTECT); EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED : ERROR_DYNAMIC_CODE_BLOCKED), - runner.RunTest(test.c_str())); + runner->RunTest(test.c_str())); // Test 3: // Need token level >= USER_LIMITED to be able to successfully run test 3. - policy->SetTokenLevel(sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS, - sandbox::TokenLevel::USER_LIMITED); + runner = enable_mitigation ? RunnerWithMitigation(which_mitigation) + : std::make_unique(); + runner->GetPolicy()->SetTokenLevel( + sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS, + sandbox::TokenLevel::USER_LIMITED); test = base::StringPrintf(L"%ls %u", shared.c_str(), MAPVIEWCUSTOM); EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED : ERROR_DYNAMIC_CODE_BLOCKED), - runner.RunTest(test.c_str())); - - // Test 4: - // Set token levels back to default. - policy->SetTokenLevel(sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS, - sandbox::TokenLevel::USER_LOCKDOWN); + runner->RunTest(test.c_str())); // Ensure sandbox access to the file on disk. base::FilePath dll_path; @@ -322,14 +325,16 @@ void DynamicCodeTestHarness(sandbox::MitigationFlags which_mitigation, temp_dir.GetPath().Append(hooking_dll::g_hook_dll_file); ASSERT_TRUE(base::CopyFile(dll_path, temp_dll_path)); - EXPECT_TRUE(runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_ANY, - temp_dll_path.value().c_str())); + runner = enable_mitigation ? RunnerWithMitigation(which_mitigation) + : std::make_unique(); + EXPECT_TRUE(runner->AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_ANY, + temp_dll_path.value().c_str())); test = base::StringPrintf(L"%ls %u \"%ls\"", shared.c_str(), MAPVIEWFILE, temp_dll_path.value().c_str()); EXPECT_EQ((expect_success ? sandbox::SBOX_TEST_SUCCEEDED : ERROR_DYNAMIC_CODE_BLOCKED), - runner.RunTest(test.c_str())); + runner->RunTest(test.c_str())); } } // namespace diff --git a/chromium/sandbox/win/src/process_mitigations_imageload_unittest.cc b/chromium/sandbox/win/src/process_mitigations_imageload_unittest.cc deleted file mode 100644 index f5094665a26..00000000000 --- a/chromium/sandbox/win/src/process_mitigations_imageload_unittest.cc +++ /dev/null @@ -1,462 +0,0 @@ -// 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/process_mitigations.h" - -#include - -#include "base/files/file_util.h" -#include "base/files/scoped_temp_dir.h" -#include "base/path_service.h" -#include "base/scoped_native_library.h" -#include "base/strings/stringprintf.h" -#include "base/test/test_timeouts.h" -#include "base/win/windows_version.h" -#include "sandbox/win/src/sandbox.h" -#include "sandbox/win/src/target_services.h" -#include "sandbox/win/tests/common/controller.h" -#include "sandbox/win/tests/integration_tests/hijack_shim_dll.h" -#include "sandbox/win/tests/integration_tests/integration_tests_common.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -//------------------------------------------------------------------------------ -// Internal Defines & Functions -//------------------------------------------------------------------------------ -constexpr wchar_t g_hijack_dll_file[] = L"sbox_integration_test_hijack_dll.dll"; -constexpr wchar_t g_hijack_shim_dll_file[] = - L"sbox_integration_test_hijack_shim_dll.dll"; - -// System mutex to prevent conflicting tests from running at the same time. -// This particular mutex is related to the use of the hijack dlls. -constexpr wchar_t g_hijack_dlls_mutex[] = L"ChromeTestHijackDllsMutex"; - -// API defined in hijack_shim_dll.h. -using CheckHijackResultFunction = decltype(&CheckHijackResult); - -//------------------------------------------------------------------------------ -// ImageLoadRemote test helper function. -// -// Trigger test child process (with or without mitigation enabled). -//------------------------------------------------------------------------------ -void TestWin10ImageLoadRemote(bool is_success_test) { - // ***Insert a manual testing share UNC path here! - // E.g.: \\\\hostname\\sharename\\calc.exe - std::wstring unc = L"\"\\\\hostname\\sharename\\calc.exe\""; - - sandbox::TestRunner runner; - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - // Set a policy that would normally allow for process creation. - policy->SetJobLevel(sandbox::JOB_NONE, 0); - policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED); - runner.SetDisableCsrss(false); - - if (!is_success_test) { - // Enable the NoRemote mitigation. - EXPECT_EQ(policy->SetDelayedProcessMitigations( - sandbox::MITIGATION_IMAGE_LOAD_NO_REMOTE), - sandbox::SBOX_ALL_OK); - } - - std::wstring test = L"TestChildProcess "; - test += unc.c_str(); - EXPECT_EQ((is_success_test ? sandbox::SBOX_TEST_SUCCEEDED - : sandbox::SBOX_TEST_FAILED), - runner.RunTest(test.c_str())); -} - -//------------------------------------------------------------------------------ -// ImageLoadLow test helper function. -// -// 1. Set up a copy of calc, using icacls to make it low integrity. -// 2. Trigger test child process (with or without mitigation enabled). -//------------------------------------------------------------------------------ -void TestWin10ImageLoadLowLabel(bool is_success_test) { - // Setup a mandatory low executable for this test (calc.exe). - // If anything fails during setup, ASSERT to end test. - base::FilePath orig_path; - ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &orig_path)); - orig_path = orig_path.Append(L"calc.exe"); - - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - base::FilePath new_path = temp_dir.GetPath(); - new_path = new_path.Append(L"lowIL_calc.exe"); - - // Test file will be cleaned up by the ScopedTempDir. - ASSERT_TRUE(base::CopyFileW(orig_path, new_path)); - - std::wstring cmd_line = L"icacls \""; - cmd_line += new_path.value().c_str(); - cmd_line += L"\" /setintegritylevel Low"; - - base::LaunchOptions options = base::LaunchOptionsForTest(); - base::Process setup_proc = base::LaunchProcess(cmd_line.c_str(), options); - ASSERT_TRUE(setup_proc.IsValid()); - - int exit_code = 1; - if (!setup_proc.WaitForExitWithTimeout(TestTimeouts::action_max_timeout(), - &exit_code)) { - // Might have timed out, or might have failed. - // Terminate to make sure we clean up any mess. - setup_proc.Terminate(0, false); - ASSERT_TRUE(false); - } - // Make sure icacls was successful. - ASSERT_EQ(0, exit_code); - - sandbox::TestRunner runner; - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - // Set a policy that would normally allow for process creation. - policy->SetJobLevel(sandbox::JOB_NONE, 0); - policy->SetTokenLevel(sandbox::USER_UNPROTECTED, sandbox::USER_UNPROTECTED); - runner.SetDisableCsrss(false); - - if (!is_success_test) { - // Enable the NoLowLabel mitigation. - EXPECT_EQ(policy->SetDelayedProcessMitigations( - sandbox::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), - sandbox::SBOX_ALL_OK); - } - - std::wstring test = L"TestChildProcess \""; - test += new_path.value().c_str(); - test += L"\" false"; - - EXPECT_EQ((is_success_test ? sandbox::SBOX_TEST_SUCCEEDED - : sandbox::SBOX_TEST_FAILED), - runner.RunTest(test.c_str())); -} - -//------------------------------------------------------------------------------ -// ImageLoadPreferSystem32 test helper function. -// -// - Acquire the global g_hijack_dlls_mutex mutex before calling -// (as we meddle with a shared system resource). -// - Note: Do not use ASSERTs in this function, as a global mutex is held. -// - If |baseline_test|, there is no attempted hijacking. Just test the -// implicit import in the local directory. -// -// 1. Put a copy of the hijack DLL into system32. -// 2. Trigger test child process (with or without mitigation enabled). When -// the OS resolves the import table for the child process, it will either -// choose the version in the local app directory, or the copy in system32. -//------------------------------------------------------------------------------ -void TestWin10ImageLoadPreferSys32(bool baseline_test, bool expect_sys32_path) { - base::FilePath app_path; - EXPECT_TRUE(base::PathService::Get(base::DIR_EXE, &app_path)); - - // Put a copy of the hijack dll into system32. So there's one in the - // local dir, and one in system32. - base::FilePath old_dll_path = app_path.Append(g_hijack_dll_file); - - base::FilePath sys_path; - EXPECT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &sys_path)); - base::FilePath new_dll_path = sys_path.Append(g_hijack_dll_file); - - // Note: test requires admin to copy/delete files in system32. - EXPECT_TRUE(base::CopyFileW(old_dll_path, new_dll_path)); - - // Get path for the test shim DLL to pass along to test child proc. - base::FilePath shim_dll_path = app_path.Append(g_hijack_shim_dll_file); - - sandbox::TestRunner runner; - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - // ACCESS_DENIED errors loading DLLs without a higher token - AddFsRule - // alone doesn't cut it. - policy->SetTokenLevel(sandbox::TokenLevel::USER_RESTRICTED_SAME_ACCESS, - sandbox::TokenLevel::USER_LIMITED); - runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_READONLY, - shim_dll_path.value().c_str()); - runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_READONLY, - old_dll_path.value().c_str()); - runner.AddFsRule(sandbox::TargetPolicy::FILES_ALLOW_READONLY, - new_dll_path.value().c_str()); - - if (!baseline_test && expect_sys32_path) { - // Enable the PreferSystem32 mitigation. - EXPECT_EQ(policy->SetDelayedProcessMitigations( - sandbox::MITIGATION_IMAGE_LOAD_PREFER_SYS32), - sandbox::SBOX_ALL_OK); - } - - // For the baseline test, disable sandbox completely. Just trying to ensure - // that the implicit link & import succeeds from local dir. - if (baseline_test) - runner.SetUnsandboxed(true); - - // If this is the "success" test, expect a return value of "false" - as the - // hijack was successful so the hijack_dll is NOT in system32. - // The failure case has the mitigation enabled, so expect the hijack_dll to be - // in system32. - std::wstring test = base::StringPrintf( - L"%ls %ls \"%ls\"", L"TestImageLoadHijack", - (!expect_sys32_path) ? L"true" : L"false", shim_dll_path.value().c_str()); - - // NOTE: 1706098690 == SBOX_TEST_NOT_FOUND == LoadLibrary on the shim dll - // failed, or the secondary import load of hijack.dll failed. - EXPECT_EQ((expect_sys32_path ? sandbox::SBOX_TEST_SUCCEEDED - : sandbox::SBOX_TEST_FAILED), - runner.RunTest(test.c_str())); - - EXPECT_TRUE(base::DeleteFile(new_dll_path)); -} - -} // namespace - -namespace sandbox { - -//------------------------------------------------------------------------------ -// Exported functions called by child test processes. -//------------------------------------------------------------------------------ - -// This test loading and using the shim DLL is required. -// The waterfall bots (test environment/harness) have unique resource -// access failures if the main sbox_integration_tests executable -// implicitely links against the hijack DLL (and implicit linking is required -// to test this mitigation) - regardless of whether this test runs or is -// disabled. -// -// - Arg1: "true" or "false", if the DLL path should be in system32. -// - Arg2: the full path to the test shim DLL to load. -SBOX_TESTS_COMMAND int TestImageLoadHijack(int argc, wchar_t** argv) { - if (argc < 2 || !argv[0] || !argv[1]) - return SBOX_TEST_INVALID_PARAMETER; - - bool expect_system = false; - if (::wcsicmp(argv[0], L"true") == 0) - expect_system = true; - - // Ensure implicitely linked, secondary hijack DLL is not already - // loaded in process. - HMODULE check = ::GetModuleHandleW(g_hijack_dll_file); - if (check) - return SBOX_TEST_FAILED; - - // Load the shim DLL for this test. - base::ScopedNativeLibrary shim_dll((base::FilePath(argv[1]))); - if (!shim_dll.is_valid()) - return SBOX_TEST_NOT_FOUND; - - CheckHijackResultFunction check_hijack_result = - reinterpret_cast( - shim_dll.GetFunctionPointer(g_hijack_shim_func)); - - if (!check_hijack_result) - return SBOX_TEST_FAILED_TO_RUN_TEST; - - return check_hijack_result(expect_system); -} - -//------------------------------------------------------------------------------ -// Exported Image Load Tests -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -// Disable image load from remote devices (MITIGATION_IMAGE_LOAD_NO_REMOTE). -// >= Win10_TH2 -//------------------------------------------------------------------------------ - -// This test validates that setting the MITIGATION_IMAGE_LOAD_NO_REMOTE -// mitigation enables the setting on a process. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoRemotePolicySuccess) { - if (base::win::GetVersion() < base::win::Version::WIN10_TH2) - return; - - std::wstring test_command = L"CheckPolicy "; - test_command += std::to_wstring(TESTPOLICY_LOADNOREMOTE); - - //--------------------------------- - // 1) Test setting pre-startup. - //--------------------------------- - TestRunner runner; - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_IMAGE_LOAD_NO_REMOTE), - SBOX_ALL_OK); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); - - //--------------------------------- - // 2) Test setting post-startup. - //--------------------------------- - TestRunner runner2; - sandbox::TargetPolicy* policy2 = runner2.GetPolicy(); - - EXPECT_EQ( - policy2->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_NO_REMOTE), - SBOX_ALL_OK); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str())); -} - -// This test validates that we CAN create a new process from -// a remote UNC device, if the MITIGATION_IMAGE_LOAD_NO_REMOTE -// mitigation is NOT set. -// -// MANUAL testing only. -TEST(ProcessMitigationsTest, DISABLED_CheckWin10ImageLoadNoRemoteSuccess) { - if (base::win::GetVersion() < base::win::Version::WIN10_TH2) - return; - - TestWin10ImageLoadRemote(true /* is_success_test */); -} - -// This test validates that setting the MITIGATION_IMAGE_LOAD_NO_REMOTE -// mitigation prevents creating a new process from a remote -// UNC device. -// -// MANUAL testing only. -TEST(ProcessMitigationsTest, DISABLED_CheckWin10ImageLoadNoRemoteFailure) { - if (base::win::GetVersion() < base::win::Version::WIN10_TH2) - return; - - TestWin10ImageLoadRemote(false /* is_success_test */); -} - -//------------------------------------------------------------------------------ -// Disable image load when "mandatory low label" (integrity level). -// (MITIGATION_IMAGE_LOAD_NO_LOW_LABEL) -// >= Win10_TH2 -//------------------------------------------------------------------------------ - -// This test validates that setting the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL -// mitigation enables the setting on a process. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelPolicySuccess) { - if (base::win::GetVersion() < base::win::Version::WIN10_TH2) - return; - - std::wstring test_command = L"CheckPolicy "; - test_command += std::to_wstring(TESTPOLICY_LOADNOLOW); - - //--------------------------------- - // 1) Test setting pre-startup. - //--------------------------------- - TestRunner runner; - sandbox::TargetPolicy* policy = runner.GetPolicy(); - - EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), - SBOX_ALL_OK); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); - - //--------------------------------- - // 2) Test setting post-startup. - //--------------------------------- - TestRunner runner2; - sandbox::TargetPolicy* policy2 = runner2.GetPolicy(); - - EXPECT_EQ( - policy2->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), - SBOX_ALL_OK); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str())); -} - -// This test validates that we CAN create a new process with -// low mandatory label (IL), if the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL -// mitigation is NOT set. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelSuccess) { - if (base::win::GetVersion() < base::win::Version::WIN10_TH2) - return; - - TestWin10ImageLoadLowLabel(true /* is_success_test */); -} - -// This test validates that setting the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL -// mitigation prevents creating a new process with low mandatory label (IL). -TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelFailure) { - if (base::win::GetVersion() < base::win::Version::WIN10_TH2) - return; - - TestWin10ImageLoadLowLabel(false /* is_success_test */); -} - -//------------------------------------------------------------------------------ -// Prefer system32 directory on image load (MITIGATION_IMAGE_LOAD_PREFER_SYS32). -// >= Win10_RS1 (Anniversary) -//------------------------------------------------------------------------------ - -// This test validates that setting the MITIGATION_IMAGE_LOAD_PREFER_SYS32 -// mitigation enables the setting on a process. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadPreferSys32PolicySuccess) { - if (base::win::GetVersion() < base::win::Version::WIN10_RS1) - return; - - std::wstring test_command = L"CheckPolicy "; - test_command += std::to_wstring(TESTPOLICY_LOADPREFERSYS32); - - //--------------------------------- - // 1) Test setting pre-startup. - // ** Currently disabled. All PreferSys32 tests start to explode on - // >= Win10 1703/RS2 when this mitigation is set pre-startup. - // Child process creation works fine, but when ::ResumeThread() is called, - // there is a fatal error: "Entry point ucnv_convertEx_60 could not be - // located in the DLL ... sbox_integration_tests.exe." - // This is a character conversion function in a ucnv (unicode) DLL. - // Potentially the loader is finding a different version of this DLL that - // we have a dependency on in System32... but it doesn't match up with - // what we build against???! - //--------------------------------- - - //--------------------------------- - // 2) Test setting post-startup. - //--------------------------------- - TestRunner runner2; - sandbox::TargetPolicy* policy2 = runner2.GetPolicy(); - - EXPECT_EQ( - policy2->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_PREFER_SYS32), - SBOX_ALL_OK); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str())); -} - -// This baseline test validates that the implicit import works in the local -// working directory. -// MITIGATION_IMAGE_LOAD_PREFER_SYS32 mitigation is NOT set. -// -// Must run this test as admin/elevated. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadPreferSys32_Baseline) { - if (base::win::GetVersion() < base::win::Version::WIN10_RS1) - return; - - ScopedTestMutex mutex(g_hijack_dlls_mutex); - - // Baseline test, and expect the DLL to NOT be in system32. - TestWin10ImageLoadPreferSys32(true /* baseline_test */, - false /* expect_sys32_path */); -} - -// This test validates that import hijacking succeeds, if the -// MITIGATION_IMAGE_LOAD_PREFER_SYS32 mitigation is NOT set. -// (Hijack success.) -// -// Must run this test as admin/elevated. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadPreferSys32_Success) { - if (base::win::GetVersion() < base::win::Version::WIN10_RS1) - return; - - ScopedTestMutex mutex(g_hijack_dlls_mutex); - - // Not a baseline test, and expect the DLL to be in system32. - TestWin10ImageLoadPreferSys32(false /* baseline_test */, - true /* expect_sys32_path */); -} - -// This test validates that setting the MITIGATION_IMAGE_LOAD_PREFER_SYS32 -// mitigation prevents import hijacking. (Hijack failure.) -// -// Must run this test as admin/elevated. -TEST(ProcessMitigationsTest, CheckWin10ImageLoadPreferSys32_Failure) { - if (base::win::GetVersion() < base::win::Version::WIN10_RS1) - return; - - ScopedTestMutex mutex(g_hijack_dlls_mutex); - - // Not a baseline test, and expect the DLL to NOT be in system32. - TestWin10ImageLoadPreferSys32(false /* baseline_test */, - false /* expect_sys32_path */); -} - -} // namespace sandbox diff --git a/chromium/sandbox/win/src/process_mitigations_unittest.cc b/chromium/sandbox/win/src/process_mitigations_unittest.cc index 31138318dd2..551b07d0d6d 100644 --- a/chromium/sandbox/win/src/process_mitigations_unittest.cc +++ b/chromium/sandbox/win/src/process_mitigations_unittest.cc @@ -16,6 +16,7 @@ #include "base/win/scoped_handle.h" #include "base/win/windows_version.h" #include "sandbox/win/src/nt_internals.h" +#include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/target_services.h" #include "sandbox/win/tests/common/controller.h" #include "sandbox/win/tests/integration_tests/hooking_dll.h" @@ -482,17 +483,11 @@ SBOX_TESTS_COMMAND int CheckDep(int argc, wchar_t** argv) { return SBOX_TEST_SECOND_ERROR; } else { - NtQueryInformationProcessFunction query_information_process = nullptr; - ResolveNTFunctionPtr("NtQueryInformationProcess", - &query_information_process); - if (!query_information_process) - return SBOX_TEST_NOT_FOUND; - ULONG size = 0; ULONG dep_flags = 0; - if (!SUCCEEDED(query_information_process(::GetCurrentProcess(), - ProcessExecuteFlags, &dep_flags, - sizeof(dep_flags), &size))) { + if (!SUCCEEDED(GetNtExports()->QueryInformationProcess( + ::GetCurrentProcess(), ProcessExecuteFlags, &dep_flags, + sizeof(dep_flags), &size))) { return SBOX_TEST_THIRD_ERROR; } @@ -925,13 +920,9 @@ TEST(ProcessMitigationsTest, CheckWin10MsSigned_Failure) { // ASAN doesn't initialize early enough for the intercepts in NtCreateSection to // be able to use std::unique_ptr, so disable pre-launch CIG on ASAN builds. #if !defined(ADDRESS_SANITIZER) -#define MAYBE_CheckWin10MsSignedWithIntercept_Success \ - CheckWin10MsSignedWithIntercept_Success #define MAYBE_CheckWin10MsSigned_FailurePreSpawn \ CheckWin10MsSigned_FailurePreSpawn #else -#define MAYBE_CheckWin10MsSignedWithIntercept_Success \ - DISABLED_CheckWin10MsSignedWithIntercept_Success #define MAYBE_CheckWin10MsSigned_FailurePreSpawn \ DISABLED_CheckWin10MsSigned_FailurePreSpawn #endif @@ -1298,4 +1289,99 @@ TEST(ProcessMitigationsTest, CheckWin10KernelTransactionManagerMitigation) { EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_policy_command.c_str())); } +TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoRemotePolicySuccess) { + if (base::win::GetVersion() < base::win::Version::WIN10_TH2) + return; + + std::wstring test_command = L"CheckPolicy "; + test_command += std::to_wstring(TESTPOLICY_LOADNOREMOTE); + + //--------------------------------- + // 1) Test setting pre-startup. + //--------------------------------- + TestRunner runner; + sandbox::TargetPolicy* policy = runner.GetPolicy(); + + EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_IMAGE_LOAD_NO_REMOTE), + SBOX_ALL_OK); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); + + //--------------------------------- + // 2) Test setting post-startup. + //--------------------------------- + TestRunner runner2; + sandbox::TargetPolicy* policy2 = runner2.GetPolicy(); + + EXPECT_EQ( + policy2->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_NO_REMOTE), + SBOX_ALL_OK); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str())); +} + +//--------------- +// This test validates that setting the MITIGATION_IMAGE_LOAD_NO_LOW_LABEL +// mitigation enables the setting on a process. +TEST(ProcessMitigationsTest, CheckWin10ImageLoadNoLowLabelPolicySuccess) { + if (base::win::GetVersion() < base::win::Version::WIN10_TH2) + return; + + std::wstring test_command = L"CheckPolicy "; + test_command += std::to_wstring(TESTPOLICY_LOADNOLOW); + + //--------------------------------- + // 1) Test setting pre-startup. + //--------------------------------- + TestRunner runner; + sandbox::TargetPolicy* policy = runner.GetPolicy(); + + EXPECT_EQ(policy->SetProcessMitigations(MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), + SBOX_ALL_OK); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(test_command.c_str())); + + //--------------------------------- + // 2) Test setting post-startup. + //--------------------------------- + TestRunner runner2; + sandbox::TargetPolicy* policy2 = runner2.GetPolicy(); + + EXPECT_EQ( + policy2->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_NO_LOW_LABEL), + SBOX_ALL_OK); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str())); +} + +// This test validates that setting the MITIGATION_IMAGE_LOAD_PREFER_SYS32 +// mitigation enables the setting on a process. +TEST(ProcessMitigationsTest, CheckWin10ImageLoadPreferSys32PolicySuccess) { + if (base::win::GetVersion() < base::win::Version::WIN10_RS1) + return; + + std::wstring test_command = L"CheckPolicy "; + test_command += std::to_wstring(TESTPOLICY_LOADPREFERSYS32); + + //--------------------------------- + // 1) Test setting pre-startup. + // ** Currently disabled. All PreferSys32 tests start to explode on + // >= Win10 1703/RS2 when this mitigation is set pre-startup. + // Child process creation works fine, but when ::ResumeThread() is called, + // there is a fatal error: "Entry point ucnv_convertEx_60 could not be + // located in the DLL ... sbox_integration_tests.exe." + // This is a character conversion function in a ucnv (unicode) DLL. + // Potentially the loader is finding a different version of this DLL that + // we have a dependency on in System32... but it doesn't match up with + // what we build against???! + //--------------------------------- + + //--------------------------------- + // 2) Test setting post-startup. + //--------------------------------- + TestRunner runner2; + sandbox::TargetPolicy* policy2 = runner2.GetPolicy(); + + EXPECT_EQ( + policy2->SetDelayedProcessMitigations(MITIGATION_IMAGE_LOAD_PREFER_SYS32), + SBOX_ALL_OK); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(test_command.c_str())); +} + } // namespace sandbox diff --git a/chromium/sandbox/win/src/process_thread_interception.cc b/chromium/sandbox/win/src/process_thread_interception.cc index 4a9fff232aa..a0de6e68add 100644 --- a/chromium/sandbox/win/src/process_thread_interception.cc +++ b/chromium/sandbox/win/src/process_thread_interception.cc @@ -18,8 +18,6 @@ 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, diff --git a/chromium/sandbox/win/src/process_thread_policy.cc b/chromium/sandbox/win/src/process_thread_policy.cc index 636f1ddf723..66b0a8a44e0 100644 --- a/chromium/sandbox/win/src/process_thread_policy.cc +++ b/chromium/sandbox/win/src/process_thread_policy.cc @@ -14,6 +14,7 @@ #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_nt_util.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/win_utils.h" @@ -24,10 +25,6 @@ NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, 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}; @@ -37,8 +34,8 @@ NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info, reinterpret_cast(static_cast(thread_id)); HANDLE local_handle = nullptr; - NTSTATUS status = - NtOpenThread(&local_handle, desired_access, &attributes, &client_id); + NTSTATUS status = GetNtExports()->OpenThread(&local_handle, desired_access, + &attributes, &client_id); if (NT_SUCCESS(status)) { if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, false, @@ -56,9 +53,6 @@ NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, HANDLE* handle) { *handle = nullptr; - NtOpenProcessFunction NtOpenProcess = nullptr; - ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess); - if (client_info.process_id != process_id) return STATUS_ACCESS_DENIED; @@ -68,8 +62,8 @@ NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info, client_id.UniqueProcess = reinterpret_cast(static_cast(client_info.process_id)); HANDLE local_handle = nullptr; - NTSTATUS status = - NtOpenProcess(&local_handle, desired_access, &attributes, &client_id); + NTSTATUS status = GetNtExports()->OpenProcess(&local_handle, desired_access, + &attributes, &client_id); if (NT_SUCCESS(status)) { if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, false, @@ -86,15 +80,12 @@ NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info, 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); + NTSTATUS status = GetNtExports()->OpenProcessToken( + client_info.process, desired_access, &local_handle); if (NT_SUCCESS(status)) { if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, false, @@ -111,15 +102,12 @@ NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info, 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); + NTSTATUS status = GetNtExports()->OpenProcessTokenEx( + client_info.process, desired_access, attributes, &local_handle); if (NT_SUCCESS(status)) { if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, client_info.process, handle, 0, false, diff --git a/chromium/sandbox/win/src/restricted_token_utils.cc b/chromium/sandbox/win/src/restricted_token_utils.cc index f865c3e804c..3ca5062008f 100644 --- a/chromium/sandbox/win/src/restricted_token_utils.cc +++ b/chromium/sandbox/win/src/restricted_token_utils.cc @@ -17,6 +17,7 @@ #include "base/win/windows_version.h" #include "sandbox/win/src/job.h" #include "sandbox/win/src/restricted_token.h" +#include "sandbox/win/src/sandbox_nt_util.h" #include "sandbox/win/src/sandbox_utils.h" #include "sandbox/win/src/security_level.h" #include "sandbox/win/src/win_utils.h" @@ -379,21 +380,18 @@ DWORD CreateLowBoxObjectDirectory(const base::win::Sid& lowbox_sid, base::StringPrintf(L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls", session_id, sid_string->c_str()); - 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); + InitObjectAttribs(directory_path, attributes, nullptr, &obj_attr, &obj_name, + nullptr); HANDLE handle = nullptr; - NTSTATUS status = - CreateObjectDirectory(&handle, DIRECTORY_ALL_ACCESS, &obj_attr); + NTSTATUS status = GetNtExports()->CreateDirectoryObject( + &handle, DIRECTORY_ALL_ACCESS, &obj_attr); if (!NT_SUCCESS(status)) return GetLastErrorFromNtStatus(status); diff --git a/chromium/sandbox/win/src/sandbox_nt_types.h b/chromium/sandbox/win/src/sandbox_nt_types.h index 6700e5c51ac..58e033d7209 100644 --- a/chromium/sandbox/win/src/sandbox_nt_types.h +++ b/chromium/sandbox/win/src/sandbox_nt_types.h @@ -10,18 +10,31 @@ namespace sandbox { struct NtExports { + bool Initialized; NtAllocateVirtualMemoryFunction AllocateVirtualMemory; + NtCreateDirectoryObjectFunction CreateDirectoryObject; + NtCreateFileFunction CreateFile; + NtCreateSectionFunction CreateSection; NtCloseFunction Close; NtDuplicateObjectFunction DuplicateObject; NtFreeVirtualMemoryFunction FreeVirtualMemory; NtMapViewOfSectionFunction MapViewOfSection; + NtOpenFileFunction OpenFile; + NtOpenThreadFunction OpenThread; + NtOpenProcessFunction OpenProcess; + NtOpenProcessTokenFunction OpenProcessToken; + NtOpenProcessTokenExFunction OpenProcessTokenEx; NtProtectVirtualMemoryFunction ProtectVirtualMemory; + NtQueryAttributesFileFunction QueryAttributesFile; + NtQueryFullAttributesFileFunction QueryFullAttributesFile; NtQueryInformationProcessFunction QueryInformationProcess; NtQueryObjectFunction QueryObject; NtQuerySectionFunction QuerySection; NtQueryVirtualMemoryFunction QueryVirtualMemory; - NtUnmapViewOfSectionFunction UnmapViewOfSection; + NtSetInformationFileFunction SetInformationFile; + NtSetInformationProcessFunction SetInformationProcess; NtSignalAndWaitForSingleObjectFunction SignalAndWaitForSingleObject; + NtUnmapViewOfSectionFunction UnmapViewOfSection; NtWaitForSingleObjectFunction WaitForSingleObject; RtlAllocateHeapFunction RtlAllocateHeap; RtlAnsiStringToUnicodeStringFunction RtlAnsiStringToUnicodeString; @@ -30,6 +43,7 @@ struct NtExports { RtlCreateUserThreadFunction RtlCreateUserThread; RtlDestroyHeapFunction RtlDestroyHeap; RtlFreeHeapFunction RtlFreeHeap; + RtlNtStatusToDosErrorFunction RtlNtStatusToDosError; _strnicmpFunction _strnicmp; strlenFunction strlen; wcslenFunction wcslen; diff --git a/chromium/sandbox/win/src/sandbox_nt_util.cc b/chromium/sandbox/win/src/sandbox_nt_util.cc index 8ba59ff7bf9..c17b311452d 100644 --- a/chromium/sandbox/win/src/sandbox_nt_util.cc +++ b/chromium/sandbox/win/src/sandbox_nt_util.cc @@ -11,6 +11,7 @@ #include "base/compiler_specific.h" #include "base/win/pe_image.h" +#include "sandbox/win/src/internal_types.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/target_services.h" @@ -40,7 +41,6 @@ inline char* AlignToBoundary(void* ptr, size_t increment) { // 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 @@ -61,9 +61,9 @@ void* AllocateNearTo(void* source, size_t size) { 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); + NTSTATUS status = sandbox::GetNtExports()->QueryVirtualMemory( + NtCurrentProcess, base, MemoryBasicInformation, &mem_info, + sizeof(mem_info), nullptr); if (!NT_SUCCESS(status)) break; @@ -72,9 +72,9 @@ void* AllocateNearTo(void* source, size_t size) { // 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); + status = sandbox::GetNtExports()->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; @@ -89,8 +89,6 @@ void* AllocateNearTo(void* source, size_t size) { } #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(512 * 1024); @@ -100,21 +98,74 @@ void* AllocateNearTo(void* source, size_t size) { do { base = nullptr; actual_size = 64 * 1024; - ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size, - MEM_RESERVE, PAGE_NOACCESS); + ret = sandbox::GetNtExports()->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); + ret = sandbox::GetNtExports()->AllocateVirtualMemory( + NtCurrentProcess, &base, 0, &actual_size, MEM_COMMIT, PAGE_READWRITE); if (!NT_SUCCESS(ret)) return nullptr; return base; } #endif // defined(_WIN64). +template +void InitFunc(const base::win::PEImage& image, T& member, const char* name) { + member = reinterpret_cast(image.GetProcAddress(name)); + DCHECK(member); +} + +#define INIT_NT(member) InitFunc(image, sandbox::g_nt.member, "Nt" #member) +#define INIT_RTL(member) InitFunc(image, sandbox::g_nt.member, #member) + +void InitGlobalNt() { + HMODULE ntdll = ::GetModuleHandle(sandbox::kNtdllName); + base::win::PEImage image(ntdll); + INIT_NT(AllocateVirtualMemory); + INIT_NT(CreateDirectoryObject); + INIT_NT(CreateFile); + INIT_NT(CreateSection); + INIT_NT(Close); + INIT_NT(DuplicateObject); + INIT_NT(FreeVirtualMemory); + INIT_NT(MapViewOfSection); + INIT_NT(OpenFile); + INIT_NT(OpenThread); + INIT_NT(OpenProcess); + INIT_NT(OpenProcessToken); + INIT_NT(OpenProcessTokenEx); + INIT_NT(ProtectVirtualMemory); + INIT_NT(QueryAttributesFile); + INIT_NT(QueryFullAttributesFile); + INIT_NT(QueryInformationProcess); + INIT_NT(QueryObject); + INIT_NT(QuerySection); + INIT_NT(QueryVirtualMemory); + INIT_NT(SetInformationFile); + INIT_NT(SetInformationProcess); + INIT_NT(SignalAndWaitForSingleObject); + INIT_NT(UnmapViewOfSection); + INIT_NT(WaitForSingleObject); + + INIT_RTL(RtlAllocateHeap); + INIT_RTL(RtlAnsiStringToUnicodeString); + INIT_RTL(RtlCompareUnicodeString); + INIT_RTL(RtlCreateHeap); + INIT_RTL(RtlCreateUserThread); + INIT_RTL(RtlDestroyHeap); + INIT_RTL(RtlFreeHeap); + INIT_RTL(RtlNtStatusToDosError); + INIT_RTL(_strnicmp); + INIT_RTL(strlen); + INIT_RTL(wcslen); + INIT_RTL(memcpy); + sandbox::g_nt.Initialized = true; +} + } // namespace. namespace sandbox { @@ -136,9 +187,9 @@ bool MapGlobalMemory() { 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); + NTSTATUS ret = GetNtExports()->MapViewOfSection( + g_shared_section, NtCurrentProcess, &memory, 0, 0, nullptr, &size, + ViewUnmap, 0, PAGE_READWRITE); if (!NT_SUCCESS(ret) || !memory) { NOTREACHED_NT(); @@ -148,7 +199,8 @@ bool MapGlobalMemory() { if (_InterlockedCompareExchangePointer(&g_shared_IPC_memory, memory, nullptr)) { // Somebody beat us to the memory setup. - VERIFY_SUCCESS(g_nt.UnmapViewOfSection(NtCurrentProcess, memory)); + VERIFY_SUCCESS( + GetNtExports()->UnmapViewOfSection(NtCurrentProcess, memory)); } DCHECK_NT(g_shared_IPC_size > 0); g_shared_policy_memory = @@ -171,17 +223,24 @@ void* GetGlobalPolicyMemory() { return g_shared_policy_memory; } +const NtExports* GetNtExports() { + if (!g_nt.Initialized) + InitGlobalNt(); + + return &g_nt; +} + 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); + void* heap = GetNtExports()->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); + GetNtExports()->RtlDestroyHeap(heap); } } return !!g_heap; @@ -223,113 +282,48 @@ bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) { NTSTATUS CopyData(void* destination, const void* source, size_t bytes) { NTSTATUS ret = STATUS_SUCCESS; __try { - g_nt.memcpy(destination, source, bytes); + GetNtExports()->memcpy(destination, source, bytes); } __except (EXCEPTION_EXECUTE_HANDLER) { ret = GetExceptionCode(); } return ret; } -NTSTATUS AllocAndGetFullPath( - HANDLE root, - const wchar_t* path, - std::unique_ptr* 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 handle_name; - if (size) { - handle_name.reset(reinterpret_cast( - new (NT_ALLOC) BYTE[size])); - - // Query the name information a second time to get the name of the - // object referenced by the handle. - ret = NtQueryObject(root, ObjectNameInformation, handle_name.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* out_name, - uint32_t* attributes, - HANDLE* root) { +NTSTATUS CopyNameAndAttributes( + const OBJECT_ATTRIBUTES* in_object, + std::unique_ptr* out_name, + size_t* out_name_len, + uint32_t* attributes) { if (!InitHeap()) return STATUS_NO_MEMORY; DCHECK_NT(out_name); + DCHECK_NT(out_name_len); NTSTATUS ret = STATUS_UNSUCCESSFUL; __try { do { - if (in_object->RootDirectory != static_cast(0) && !root) + if (in_object->RootDirectory != nullptr) 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)]); + size_t size = in_object->ObjectName->Length / sizeof(wchar_t); + out_name->reset(new (NT_ALLOC) wchar_t[size + 1]); if (!*out_name) break; ret = CopyData(out_name->get(), in_object->ObjectName->Buffer, - size - sizeof(wchar_t)); + size * sizeof(wchar_t)); if (!NT_SUCCESS(ret)) break; - out_name->get()[size / sizeof(wchar_t) - 1] = L'\0'; - + *out_name_len = size; + out_name->get()[size] = L'\0'; if (attributes) *attributes = in_object->Attributes; - if (root) - *root = in_object->RootDirectory; ret = STATUS_SUCCESS; } while (false); } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -346,9 +340,9 @@ 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); + NTSTATUS ret = GetNtExports()->QueryInformationProcess( + process, ProcessBasicInformation, &proc_info, sizeof(proc_info), + &bytes_returned); if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned) return ret; @@ -385,18 +379,19 @@ bool IsValidImageSection(HANDLE section, HANDLE query_section; - NTSTATUS ret = - g_nt.DuplicateObject(NtCurrentProcess, section, NtCurrentProcess, - &query_section, SECTION_QUERY, 0, 0); + NTSTATUS ret = GetNtExports()->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); + ret = GetNtExports()->QuerySection(query_section, SectionBasicInformation, + &basic_info, sizeof(basic_info), + &bytes_returned); - VERIFY_SUCCESS(g_nt.Close(query_section)); + VERIFY_SUCCESS(GetNtExports()->Close(query_section)); if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned) return false; @@ -410,8 +405,8 @@ bool IsValidImageSection(HANDLE section, // 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); + ret = GetNtExports()->QueryObject(section, ObjectBasicInformation, &obj_info, + sizeof(obj_info), &obj_size_returned); if (!NT_SUCCESS(ret) || sizeof(obj_info) != obj_size_returned) return false; @@ -424,7 +419,7 @@ bool IsValidImageSection(HANDLE section, UNICODE_STRING* AnsiToUnicode(const char* string) { ANSI_STRING ansi_string; - ansi_string.Length = static_cast(g_nt.strlen(string)); + ansi_string.Length = static_cast(GetNtExports()->strlen(string)); ansi_string.MaximumLength = ansi_string.Length + 1; ansi_string.Buffer = const_cast(string); @@ -443,8 +438,8 @@ UNICODE_STRING* AnsiToUnicode(const char* string) { out_string->Buffer = reinterpret_cast(&out_string[1]); BOOLEAN alloc_destination = false; - NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string, - alloc_destination); + NTSTATUS ret = GetNtExports()->RtlAnsiStringToUnicodeString( + out_string, &ansi_string, alloc_destination); DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret); if (!NT_SUCCESS(ret)) { operator delete(out_string, NT_ALLOC); @@ -524,9 +519,9 @@ UNICODE_STRING* GetBackingFilePath(PVOID address) { return nullptr; SIZE_T returned_bytes; - NTSTATUS ret = - g_nt.QueryVirtualMemory(NtCurrentProcess, address, MemorySectionName, - section_name, buffer_bytes, &returned_bytes); + NTSTATUS ret = GetNtExports()->QueryVirtualMemory( + NtCurrentProcess, address, MemorySectionName, section_name, + buffer_bytes, &returned_bytes); if (STATUS_BUFFER_OVERFLOW == ret) { // Retry the call with the given buffer size. @@ -598,8 +593,8 @@ NTSTATUS AutoProtectMemory::ChangeProtection(void* address, ULONG protect) { DCHECK_NT(!changed_); SIZE_T new_bytes = bytes; - NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address, - &new_bytes, protect, &old_protect_); + NTSTATUS ret = GetNtExports()->ProtectVirtualMemory( + NtCurrentProcess, &address, &new_bytes, protect, &old_protect_); if (NT_SUCCESS(ret)) { changed_ = true; address_ = address; @@ -617,7 +612,7 @@ NTSTATUS AutoProtectMemory::RevertProtection() { DCHECK_NT(bytes_); SIZE_T new_bytes = bytes_; - NTSTATUS ret = g_nt.ProtectVirtualMemory( + NTSTATUS ret = GetNtExports()->ProtectVirtualMemory( NtCurrentProcess, &address_, &new_bytes, old_protect_, &old_protect_); DCHECK_NT(NT_SUCCESS(ret)); @@ -669,8 +664,8 @@ bool NtGetPathFromHandle(HANDLE handle, 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); + NTSTATUS status = GetNtExports()->QueryObject(handle, ObjectNameInformation, + &initial_buffer, size, &size); if (!NT_SUCCESS(status) && status != STATUS_INFO_LENGTH_MISMATCH) return false; @@ -683,7 +678,8 @@ bool NtGetPathFromHandle(HANDLE handle, // 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); + status = GetNtExports()->QueryObject(handle, ObjectNameInformation, name, + size, &size); if (STATUS_SUCCESS != status) return false; @@ -704,7 +700,8 @@ void* operator new(size_t size, sandbox::AllocationType type, void* near_to) { if (type == sandbox::NT_ALLOC) { if (sandbox::InitHeap()) { // Use default flags for the allocation. - result = sandbox::g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size); + result = + sandbox::GetNtExports()->RtlAllocateHeap(sandbox::g_heap, 0, size); } } else if (type == sandbox::NT_PAGE) { result = AllocateNearTo(near_to, size); @@ -722,12 +719,12 @@ void* operator new(size_t size, sandbox::AllocationType type, void* 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)); + VERIFY(sandbox::GetNtExports()->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)); + VERIFY_SUCCESS(sandbox::GetNtExports()->FreeVirtualMemory( + NtCurrentProcess, &base, &size, MEM_RELEASE)); } else { NOTREACHED_NT(); } diff --git a/chromium/sandbox/win/src/sandbox_nt_util.h b/chromium/sandbox/win/src/sandbox_nt_util.h index f7a91d8eefa..cfe109aa2b7 100644 --- a/chromium/sandbox/win/src/sandbox_nt_util.h +++ b/chromium/sandbox/win/src/sandbox_nt_util.h @@ -101,6 +101,9 @@ void* GetGlobalIPCMemory(); // Returns a pointer to the Policy shared memory. void* GetGlobalPolicyMemory(); +// Returns a reference to imported NT functions. +const NtExports* GetNtExports(); + enum RequiredAccess { READ, WRITE }; // Performs basic user mode buffer validation. In any case, buffers access must @@ -111,17 +114,14 @@ 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* 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* full_path); +// Copies the name from an object attributes. |out_name| is a NUL terminated +// string and |out_name_len| is the number of characters copied. |attributes| +// is a copy of the attribute flags from |in_object|. +NTSTATUS CopyNameAndAttributes( + const OBJECT_ATTRIBUTES* in_object, + std::unique_ptr* out_name, + size_t* out_name_len, + uint32_t* attributes = nullptr); // Initializes our ntdll level heap bool InitHeap(); diff --git a/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc b/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc index 011f1dbb326..6da41b2b159 100644 --- a/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc +++ b/chromium/sandbox/win/src/sandbox_nt_util_unittest.cc @@ -9,6 +9,7 @@ #include #include +#include "base/cxx17_backports.h" #include "base/files/file.h" #include "base/path_service.h" #include "base/strings/string_util.h" @@ -22,15 +23,11 @@ namespace sandbox { namespace { TEST(SandboxNtUtil, IsSameProcessPseudoHandle) { - InitGlobalNt(); - HANDLE current_process_pseudo = GetCurrentProcess(); EXPECT_TRUE(IsSameProcess(current_process_pseudo)); } TEST(SandboxNtUtil, IsSameProcessNonPseudoHandle) { - InitGlobalNt(); - base::win::ScopedHandle current_process( OpenProcess(PROCESS_QUERY_INFORMATION, false, GetCurrentProcessId())); ASSERT_TRUE(current_process.IsValid()); @@ -38,8 +35,6 @@ TEST(SandboxNtUtil, IsSameProcessNonPseudoHandle) { } TEST(SandboxNtUtil, IsSameProcessDifferentProcess) { - InitGlobalNt(); - STARTUPINFO si = {sizeof(si)}; PROCESS_INFORMATION pi = {}; wchar_t notepad[] = L"notepad"; @@ -183,7 +178,6 @@ void TestExtremes() { // Test nearest allocator, only do this for 64 bit. We test through the exposed // new operator as we can't call the AllocateNearTo function directly. TEST(SandboxNtUtil, NearestAllocator) { - InitGlobalNt(); std::vector mem_range; AllocateTestRange(&mem_range); ASSERT_LT(0U, mem_range.size()); @@ -242,8 +236,6 @@ TEST(SandboxNtUtil, ValidParameter) { } TEST(SandboxNtUtil, NtGetPathFromHandle) { - InitGlobalNt(); - base::FilePath exe; ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &exe)); base::File exe_file(exe, base::File::FLAG_OPEN); @@ -263,5 +255,50 @@ TEST(SandboxNtUtil, NtGetPathFromHandle) { EXPECT_STREQ(path.get(), nt_path.c_str()); } +TEST(SandboxNtUtil, CopyNameAndAttributes) { + OBJECT_ATTRIBUTES object_attributes; + InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); + std::unique_ptr name; + size_t name_len; + uint32_t attributes; + EXPECT_EQ(STATUS_UNSUCCESSFUL, + sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len, + &attributes)); + UNICODE_STRING object_name = {}; + InitializeObjectAttributes(&object_attributes, &object_name, 0, + reinterpret_cast(0x88), nullptr); + EXPECT_EQ(STATUS_UNSUCCESSFUL, + sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len, + &attributes)); + wchar_t name_buffer[] = {L'A', L'B', L'C', L'D'}; + object_name.Length = static_cast(sizeof(name_buffer)); + object_name.MaximumLength = object_name.Length; + object_name.Buffer = name_buffer; + + InitializeObjectAttributes(&object_attributes, &object_name, 0, + reinterpret_cast(0x88), nullptr); + EXPECT_EQ(STATUS_UNSUCCESSFUL, + sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len, + &attributes)); + InitializeObjectAttributes(&object_attributes, &object_name, 0x12345678, + nullptr, nullptr); + ASSERT_EQ(STATUS_SUCCESS, + sandbox::CopyNameAndAttributes(&object_attributes, &name, &name_len, + &attributes)); + EXPECT_EQ(object_attributes.Attributes, attributes); + EXPECT_EQ(base::size(name_buffer), name_len); + EXPECT_EQ(0, wcsncmp(name.get(), name_buffer, base::size(name_buffer))); + EXPECT_EQ(L'\0', name.get()[name_len]); +} + +TEST(SandboxNtUtil, GetNtExports) { + const NtExports* exports = GetNtExports(); + ASSERT_TRUE(exports); + static_assert((sizeof(NtExports) % sizeof(void*)) == 0); + // Verify that the structure is fully initialized. + for (size_t i = 0; i < sizeof(NtExports) / sizeof(void*); i++) + EXPECT_TRUE(reinterpret_cast(exports)[i]); +} + } // namespace } // namespace sandbox diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h index 5f3f1896060..2d7b1767149 100644 --- a/chromium/sandbox/win/src/sandbox_policy.h +++ b/chromium/sandbox/win/src/sandbox_policy.h @@ -40,8 +40,6 @@ class TargetPolicy { // 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. NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe. FAKE_USER_GDI_INIT, // Fakes user32 and gdi32 initialization. This can // be used to allow the DLLs to load and initialize diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc index 580968c1ef1..9e9de1b138a 100644 --- a/chromium/sandbox/win/src/sandbox_policy_base.cc +++ b/chromium/sandbox/win/src/sandbox_policy_base.cc @@ -96,7 +96,6 @@ PolicyBase::PolicyBase() 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), @@ -488,7 +487,10 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial, return SBOX_ALL_OK; } -ResultCode PolicyBase::AddTarget(std::unique_ptr target) { +ResultCode PolicyBase::ApplyToTarget(std::unique_ptr target) { + if (target_) + return SBOX_ERROR_UNEXPECTED_CALL; + if (policy_) { if (!policy_maker_->Done()) return SBOX_ERROR_NO_SPACE; @@ -537,27 +539,19 @@ ResultCode PolicyBase::AddTarget(std::unique_ptr target) { if (SBOX_ALL_OK != ret) return ret; - base::AutoLock lock(lock_); - targets_.push_back(std::move(target)); + target_ = std::move(target); return SBOX_ALL_OK; } bool PolicyBase::OnJobEmpty(HANDLE job) { - base::AutoLock lock(lock_); - targets_.erase( - std::remove_if(targets_.begin(), targets_.end(), - [&](auto&& p) -> bool { return p->Job() == job; }), - targets_.end()); + if (target_->Job() == job) + target_.reset(); return true; } bool PolicyBase::OnProcessFinished(DWORD process_id) { - base::AutoLock lock(lock_); - targets_.erase(std::remove_if(targets_.begin(), targets_.end(), - [&](auto&& p) -> bool { - return p->ProcessId() == process_id; - }), - targets_.end()); + if (target_->ProcessId() == process_id) + target_.reset(); return true; } @@ -644,7 +638,7 @@ ResultCode PolicyBase::AddAppContainerProfile(const wchar_t* package_name, } scoped_refptr PolicyBase::GetAppContainer() { - return GetAppContainerBase(); + return app_container_; } void PolicyBase::SetEffectiveToken(HANDLE token) { @@ -652,10 +646,6 @@ void PolicyBase::SetEffectiveToken(HANDLE token) { effective_token_ = token; } -scoped_refptr PolicyBase::GetAppContainerBase() { - return app_container_; -} - ResultCode PolicyBase::SetupAllInterceptions(TargetProcess& target) { InterceptionManager manager(target, relaxed_interceptions_); @@ -700,11 +690,6 @@ ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem, 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; diff --git a/chromium/sandbox/win/src/sandbox_policy_base.h b/chromium/sandbox/win/src/sandbox_policy_base.h index 695e942ff14..a88a95bb111 100644 --- a/chromium/sandbox/win/src/sandbox_policy_base.h +++ b/chromium/sandbox/win/src/sandbox_policy_base.h @@ -85,9 +85,6 @@ class PolicyBase final : public TargetPolicy { void SetAllowNoSandboxJob() override; bool GetAllowNoSandboxJob() override; - // Get the AppContainer profile as its internal type. - scoped_refptr GetAppContainerBase(); - // Creates a Job object with the level specified in a previous call to // SetJobLevel(). ResultCode MakeJobObject(base::win::ScopedHandle* job); @@ -103,9 +100,9 @@ class PolicyBase final : public TargetPolicy { base::win::ScopedHandle* lockdown, base::win::ScopedHandle* lowbox); - // Adds a target process to the internal list of targets. Internally a + // Applies the sandbox to |target| and takes ownership. Internally a // call to TargetProcess::Init() is issued. - ResultCode AddTarget(std::unique_ptr target); + ResultCode ApplyToTarget(std::unique_ptr target); // Called when there are no more active processes in a Job. // Removes a Job object associated with this policy and the target associated @@ -139,12 +136,8 @@ class PolicyBase final : public TargetPolicy { Semantics semantics, const wchar_t* pattern); - // This lock synchronizes operations on the targets_ collection. - base::Lock lock_; - // Maintains the list of target process associated with this policy. - // The policy takes ownership of them. - typedef std::list> TargetSet; - TargetSet targets_; + // The policy takes ownership of a target as it is applied to it. + std::unique_ptr target_; // Standard object-lifetime reference counter. volatile LONG ref_count; // The user-defined global policy settings. @@ -155,8 +148,6 @@ class PolicyBase final : public TargetPolicy { 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_; diff --git a/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc b/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc index 660fe487048..19ed93fbeac 100644 --- a/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc +++ b/chromium/sandbox/win/src/sandbox_policy_diagnostic.cc @@ -4,6 +4,7 @@ #include "sandbox/win/src/sandbox_policy_diagnostic.h" +#include #include #include @@ -34,6 +35,7 @@ const char kAppContainerCapabilities[] = "appContainerCapabilities"; const char kAppContainerInitialCapabilities[] = "appContainerInitialCapabilities"; const char kAppContainerSid[] = "appContainerSid"; +const char kComponentFilters[] = "componentFilters"; const char kDesiredIntegrityLevel[] = "desiredIntegrityLevel"; const char kDesiredMitigations[] = "desiredMitigations"; const char kDisconnectCsrss[] = "disconnectCsrss"; @@ -43,20 +45,12 @@ const char kLockdownLevel[] = "lockdownLevel"; const char kLowboxSid[] = "lowboxSid"; const char kPlatformMitigations[] = "platformMitigations"; const char kPolicyRules[] = "policyRules"; -const char kProcessIds[] = "processIds"; +const char kProcessId[] = "processId"; // Values in snapshots of Policies. const char kDisabled[] = "disabled"; const char kEnabled[] = "enabled"; -base::Value ProcessIdList(std::vector process_ids) { - base::Value results(base::Value::Type::LIST); - for (const auto pid : process_ids) { - results.Append(base::strict_cast(pid)); - } - return results; -} - std::string GetTokenLevelInEnglish(TokenLevel token) { switch (token) { case USER_LOCKDOWN: @@ -144,6 +138,12 @@ std::string GetPlatformMitigationsAsHex(MitigationFlags mitigations) { return base::StringPrintf("%016" PRIx64, platform_flags[0]); } +std::string GetComponentFilterAsHex(MitigationFlags mitigations) { + COMPONENT_FILTER filter; + sandbox::ConvertProcessMitigationsToComponentFilter(mitigations, &filter); + return base::StringPrintf("%08lx", filter.ComponentFlags); +} + std::string GetIpcTagAsString(IpcTag service) { switch (service) { case IpcTag::UNUSED: @@ -371,13 +371,7 @@ base::Value GetHandlesToClose(const HandleMap& handle_map) { PolicyDiagnostic::PolicyDiagnostic(PolicyBase* policy) { DCHECK(policy); // TODO(crbug/997273) Add more fields once webui plumbing is complete. - { - base::AutoLock lock(policy->lock_); - for (auto&& target_process : policy->targets_) { - process_ids_.push_back( - base::strict_cast(target_process->ProcessId())); - } - } + process_id_ = base::strict_cast(policy->target_->ProcessId()); lockdown_level_ = policy->lockdown_level_; job_level_ = policy->job_level_; @@ -432,7 +426,7 @@ const char* PolicyDiagnostic::JsonString() { return json_string_->c_str(); base::Value value(base::Value::Type::DICTIONARY); - value.SetKey(kProcessIds, ProcessIdList(process_ids_)); + value.SetKey(kProcessId, base::Value(base::strict_cast(process_id_))); value.SetKey(kLockdownLevel, base::Value(GetTokenLevelInEnglish(lockdown_level_))); value.SetKey(kJobLevel, base::Value(GetJobLevelInEnglish(job_level_))); @@ -443,6 +437,8 @@ const char* PolicyDiagnostic::JsonString() { base::Value(GetMitigationsAsHex(desired_mitigations_))); value.SetKey(kPlatformMitigations, base::Value(GetPlatformMitigationsAsHex(desired_mitigations_))); + value.SetKey(kComponentFilters, + base::Value(GetComponentFilterAsHex(desired_mitigations_))); if (app_container_sid_) { value.SetStringKey( diff --git a/chromium/sandbox/win/src/sandbox_policy_diagnostic.h b/chromium/sandbox/win/src/sandbox_policy_diagnostic.h index 31f3a52fc10..9cb74679bac 100644 --- a/chromium/sandbox/win/src/sandbox_policy_diagnostic.h +++ b/chromium/sandbox/win/src/sandbox_policy_diagnostic.h @@ -41,7 +41,7 @@ class PolicyDiagnostic final : public PolicyInfo { private: // |json_string_| is lazily constructed. std::unique_ptr json_string_; - std::vector process_ids_; + uint32_t process_id_; TokenLevel lockdown_level_ = USER_LAST; JobLevel job_level_ = JOB_NONE; IntegrityLevel desired_integrity_level_ = INTEGRITY_LEVEL_LAST; diff --git a/chromium/sandbox/win/src/sandbox_types.h b/chromium/sandbox/win/src/sandbox_types.h index 40ae300a27a..bfbfc15dbed 100644 --- a/chromium/sandbox/win/src/sandbox_types.h +++ b/chromium/sandbox/win/src/sandbox_types.h @@ -15,7 +15,7 @@ namespace sandbox { // // 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. +// sandbox_init_win.h must be updated. // enum ResultCode : int { SBOX_ALL_OK = 0, @@ -146,6 +146,9 @@ enum ResultCode : int { SBOX_ERROR_CANNOT_CREATE_LOWBOX_IMPERSONATION_TOKEN = 61, // Cannot create a sandbox policy for an unsandboxed process. SBOX_ERROR_UNSANDBOXED_PROCESS = 62, + // Could not create the unsandboxed process. Extended error from + // base::LaunchProcess will be in GetLastError(). + SBOX_ERROR_CANNOT_LAUNCH_UNSANDBOXED_PROCESS = 63, // Placeholder for last item of the enum. SBOX_ERROR_LAST }; @@ -185,10 +188,8 @@ 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 + INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch) + INTERCEPTION_LAST // Placeholder for last item in the enumeration }; } // namespace sandbox diff --git a/chromium/sandbox/win/src/sharedmem_ipc_client.cc b/chromium/sandbox/win/src/sharedmem_ipc_client.cc index 9df60cf3671..93078c5c106 100644 --- a/chromium/sandbox/win/src/sharedmem_ipc_client.cc +++ b/chromium/sandbox/win/src/sharedmem_ipc_client.cc @@ -16,24 +16,15 @@ 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); + DWORD millis) { LARGE_INTEGER timeout; timeout.QuadPart = millis * -10000LL; - NTSTATUS status = g_nt.SignalAndWaitForSingleObject( - object_to_signal, object_to_wait_on, alertable, + NTSTATUS status = GetNtExports()->SignalAndWaitForSingleObject( + object_to_signal, object_to_wait_on, FALSE, millis == INFINITE ? nullptr : &timeout); if (!NT_SUCCESS(status)) return WAIT_FAILED; @@ -41,12 +32,9 @@ DWORD SignalObjectAndWaitWrapper(HANDLE object_to_signal, } 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( + NTSTATUS status = GetNtExports()->WaitForSingleObject( handle, FALSE, millis == INFINITE ? nullptr : &timeout); if (!NT_SUCCESS(status)) return WAIT_FAILED; @@ -109,9 +97,8 @@ ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params, // 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); + DWORD wait = SignalObjectAndWaitWrapper( + channel[num].ping_event, channel[num].pong_event, kIPCWaitTimeOut1); 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 diff --git a/chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp b/chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp deleted file mode 100644 index c66421f969e..00000000000 --- a/chromium/sandbox/win/src/sidestep/ia32_modrm_map.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Table of relevant information about how to decode the ModR/M byte. -// Based on information in the IA-32 Intel Architecture -// Software Developer's Manual Volume 2: Instruction Set Reference. - -#include "sandbox/win/src/sidestep/mini_disassembler.h" -#include "sandbox/win/src/sidestep/mini_disassembler_types.h" - -namespace sidestep { - -const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = { -// mod == 00 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, false, OS_ZERO }, - /* r/m == 101 */ { false, false, OS_ZERO }, - /* r/m == 110 */ { true, false, OS_WORD }, - /* r/m == 111 */ { false, false, OS_ZERO }, -// mod == 01 - /* r/m == 000 */ { true, false, OS_BYTE }, - /* r/m == 001 */ { true, false, OS_BYTE }, - /* r/m == 010 */ { true, false, OS_BYTE }, - /* r/m == 011 */ { true, false, OS_BYTE }, - /* r/m == 100 */ { true, false, OS_BYTE }, - /* r/m == 101 */ { true, false, OS_BYTE }, - /* r/m == 110 */ { true, false, OS_BYTE }, - /* r/m == 111 */ { true, false, OS_BYTE }, -// mod == 10 - /* r/m == 000 */ { true, false, OS_WORD }, - /* r/m == 001 */ { true, false, OS_WORD }, - /* r/m == 010 */ { true, false, OS_WORD }, - /* r/m == 011 */ { true, false, OS_WORD }, - /* r/m == 100 */ { true, false, OS_WORD }, - /* r/m == 101 */ { true, false, OS_WORD }, - /* r/m == 110 */ { true, false, OS_WORD }, - /* r/m == 111 */ { true, false, OS_WORD }, -// mod == 11 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, false, OS_ZERO }, - /* r/m == 101 */ { false, false, OS_ZERO }, - /* r/m == 110 */ { false, false, OS_ZERO }, - /* r/m == 111 */ { false, false, OS_ZERO } -}; - -const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = { -// mod == 00 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, true, OS_ZERO }, - /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 110 */ { false, false, OS_ZERO }, - /* r/m == 111 */ { false, false, OS_ZERO }, -// mod == 01 - /* r/m == 000 */ { true, false, OS_BYTE }, - /* r/m == 001 */ { true, false, OS_BYTE }, - /* r/m == 010 */ { true, false, OS_BYTE }, - /* r/m == 011 */ { true, false, OS_BYTE }, - /* r/m == 100 */ { true, true, OS_BYTE }, - /* r/m == 101 */ { true, false, OS_BYTE }, - /* r/m == 110 */ { true, false, OS_BYTE }, - /* r/m == 111 */ { true, false, OS_BYTE }, -// mod == 10 - /* r/m == 000 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 001 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 010 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 011 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 100 */ { true, true, OS_DOUBLE_WORD }, - /* r/m == 101 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 110 */ { true, false, OS_DOUBLE_WORD }, - /* r/m == 111 */ { true, false, OS_DOUBLE_WORD }, -// mod == 11 - /* r/m == 000 */ { false, false, OS_ZERO }, - /* r/m == 001 */ { false, false, OS_ZERO }, - /* r/m == 010 */ { false, false, OS_ZERO }, - /* r/m == 011 */ { false, false, OS_ZERO }, - /* r/m == 100 */ { false, false, OS_ZERO }, - /* r/m == 101 */ { false, false, OS_ZERO }, - /* r/m == 110 */ { false, false, OS_ZERO }, - /* r/m == 111 */ { false, false, OS_ZERO }, -}; - -} // namespace sidestep diff --git a/chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp b/chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp deleted file mode 100644 index 6cd232c0598..00000000000 --- a/chromium/sandbox/win/src/sidestep/ia32_opcode_map.cpp +++ /dev/null @@ -1,1159 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Opcode decoding maps. Based on the IA-32 Intel Architecture -// Software Developer's Manual Volume 2: Instruction Set Reference. Idea -// for how to lay out the tables in memory taken from the implementation -// in the Bastard disassembly environment. - -#include "sandbox/win/src/sidestep/mini_disassembler.h" - -namespace sidestep { - -/* -* This is the first table to be searched; the first field of each -* Opcode in the table is either 0 to indicate you're in the -* right table, or an index to the correct table, in the global -* map g_pentiumOpcodeMap -*/ -const Opcode s_first_opcode_byte[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF */ { 1, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x10 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x11 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x12 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x13 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x14 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x15 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x16 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x17 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x18 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x19 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1E */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1F */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x20 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x21 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x22 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x23 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x24 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x25 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x26 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x27 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "daa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x28 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x29 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "das", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x30 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x31 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x32 */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x33 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x34 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x35 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x36 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x37 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aaa", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x38 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x39 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3C */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3E */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "aas", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x40 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x41 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x42 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x43 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x44 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x45 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x46 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x47 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x48 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x49 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x50 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x51 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x52 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x53 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x54 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x55 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x56 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x57 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x58 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x59 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5A */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5B */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5C */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5D */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5E */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x60 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x61 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x62 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_A, AM_NOT_USED, "bound", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x63 */ { 0, IT_GENERIC, AM_E | OT_W, AM_G | OT_W, AM_NOT_USED, "arpl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x64 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x65 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x66 */ { 0, IT_PREFIX_OPERAND, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x67 */ { 0, IT_PREFIX_ADDRESS, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x68 */ { 0, IT_GENERIC, AM_I | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x69 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_V, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6A */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_I | OT_B, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6C */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "insb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6D */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "insd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6E */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X | OT_B, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6F */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X | OT_V, AM_NOT_USED, "outsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x70 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x71 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x72 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x73 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x74 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x75 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x76 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x77 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x78 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x79 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7A */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7B */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7C */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7D */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7E */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7F */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x80 */ { 2, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x81 */ { 3, IT_REFERENCE, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x82 */ { 4, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x83 */ { 5, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x84 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x85 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x86 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x87 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x88 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x89 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8A */ { 0, IT_GENERIC, AM_G | OT_B, AM_E | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8C */ { 0, IT_GENERIC, AM_E | OT_W, AM_S | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8D */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, "lea", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8E */ { 0, IT_GENERIC, AM_S | OT_W, AM_E | OT_W, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8F */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x90 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "nop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x91 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x92 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x93 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x94 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x95 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x96 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x97 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "xchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x98 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cwde", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x99 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cdq", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9A */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "callf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9B */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wait", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "pushfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "popfd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9E */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9F */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lahf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_O | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_O | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA2 */ { 0, IT_GENERIC, AM_O | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA3 */ { 0, IT_GENERIC, AM_O | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA4 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "movsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA5 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "movsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA6 */ { 0, IT_GENERIC, AM_X | OT_B, AM_Y | OT_B, AM_NOT_USED, "cmpsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA7 */ { 0, IT_GENERIC, AM_X | OT_V, AM_Y | OT_V, AM_NOT_USED, "cmpsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAA */ { 0, IT_GENERIC, AM_Y | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "stosb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAB */ { 0, IT_GENERIC, AM_Y | OT_V, AM_REGISTER | OT_V, AM_NOT_USED, "stosd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_X| OT_B, AM_NOT_USED, "lodsb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_X| OT_V, AM_NOT_USED, "lodsd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAE */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_Y | OT_B, AM_NOT_USED, "scasb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_Y | OT_V, AM_NOT_USED, "scasd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB0 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB1 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB2 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB3 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB8 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB9 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBA */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBB */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBC */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBD */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBE */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBF */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC0 */ { 6, IT_REFERENCE, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC1 */ { 7, IT_REFERENCE, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC2 */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC3 */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC4 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "les", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_V, AM_M | OT_P, AM_NOT_USED, "lds", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC8 */ { 0, IT_GENERIC, AM_I | OT_W, AM_I | OT_B, AM_NOT_USED, "enter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "leave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCA */ { 0, IT_RETURN, AM_I | OT_W, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCB */ { 0, IT_RETURN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "retf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "int3", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCD */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "int", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCE */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "into", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCF */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "iret", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD0 */ { 8, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD1 */ { 9, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD2 */ { 10, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD3 */ { 11, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD4 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aam", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD5 */ { 0, IT_GENERIC, AM_I | OT_B, AM_NOT_USED, AM_NOT_USED, "aad", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "xlat", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - // The following 8 lines would be references to the FPU tables, but we currently - // do not support the FPU instructions in this disassembler. - - /* 0xD8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDA */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDB */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDC */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDD */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDE */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xDF */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - - /* 0xE0 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE1 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loopz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE2 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "loop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE3 */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jcxz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE4 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE5 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_I | OT_B, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE6 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE7 */ { 0, IT_GENERIC, AM_I | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE8 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE9 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEA */ { 0, IT_JUMP, AM_A | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEB */ { 0, IT_JUMP, AM_J | OT_B, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEC */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xED */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_REGISTER | OT_W, AM_NOT_USED, "in", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEE */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_B, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xEF */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_REGISTER | OT_V, AM_NOT_USED, "out", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF0 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lock:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF2 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "repne:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF3 */ { 0, IT_PREFIX, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rep:", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF4 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "hlt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF6 */ { 12, IT_REFERENCE, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF7 */ { 13, IT_REFERENCE, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cli", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFB */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFC */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFD */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "std", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFE */ { 14, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xFF */ { 15, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f[] = { - /* 0x0 */ { 16, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 17, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "lsl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "invd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wbinvd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud2", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xE */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x10 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movups", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "movsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "movss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movupd" } }, - /* 0x11 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movups", true, - /* F2h */ { 0, IT_GENERIC, AM_W | OT_SD, AM_V | OT_SD, AM_NOT_USED, "movsd" }, - /* F3h */ { 0, IT_GENERIC, AM_W | OT_SS, AM_V | OT_SS, AM_NOT_USED, "movss" }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movupd" } }, - /* 0x12 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // only one of ... - /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhlps" }, // ...these two is correct, Intel doesn't specify which - /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_S, AM_NOT_USED, "movlpd" } }, - /* 0x13 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movlpd" } }, - /* 0x14 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpcklps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpcklpd" } }, - /* 0x15 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_Q, AM_NOT_USED, "unpckhps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_Q, AM_NOT_USED, "unpckhpd" } }, - /* 0x16 */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // only one of... - /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movlhps" }, // ...these two is correct, Intel doesn't specify which - /* 66h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movhpd" } }, - /* 0x17 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movhpd" } }, - /* 0x18 */ { 18, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x19 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1C */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x20 */ { 0, IT_GENERIC, AM_R | OT_D, AM_C | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x21 */ { 0, IT_GENERIC, AM_R | OT_D, AM_D | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x22 */ { 0, IT_GENERIC, AM_C | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x23 */ { 0, IT_GENERIC, AM_D | OT_D, AM_R | OT_D, AM_NOT_USED, "mov", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x24 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x25 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x26 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x27 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x28 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "movaps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "movapd" } }, - /* 0x29 */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movaps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movapd" } }, - /* 0x2A */ { 0, IT_GENERIC, AM_V | OT_PS, AM_Q | OT_Q, AM_NOT_USED, "cvtpi2ps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_E | OT_D, AM_NOT_USED, "cvtsi2sd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_E | OT_D, AM_NOT_USED, "cvtsi2ss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_Q | OT_DQ, AM_NOT_USED, "cvtpi2pd" } }, - /* 0x2B */ { 0, IT_GENERIC, AM_W | OT_PS, AM_V | OT_PS, AM_NOT_USED, "movntps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_PD, AM_V | OT_PD, AM_NOT_USED, "movntpd" } }, - /* 0x2C */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvttps2pi", true, - /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvttsd2si" }, - /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvttss2si" }, - /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2pi" } }, - /* 0x2D */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_W | OT_PS, AM_NOT_USED, "cvtps2pi", true, - /* F2h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SD, AM_NOT_USED, "cvtsd2si" }, - /* F3h */ { 0, IT_GENERIC, AM_G | OT_D, AM_W | OT_SS, AM_NOT_USED, "cvtss2si" }, - /* 66h */ { 0, IT_GENERIC, AM_Q | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2pi" } }, - /* 0x2E */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "ucomiss", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "ucomisd" } }, - /* 0x2F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_SS, AM_NOT_USED, "comiss", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "comisd" } }, - /* 0x30 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "wrmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x31 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdtsc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x32 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdmsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x33 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rdpmc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x34 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysenter", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x35 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "sysexit", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x36 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x37 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x38 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x39 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3A */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3B */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3D */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3E */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3F */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x40 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x41 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x42 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x43 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x44 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x45 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x46 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x47 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmova", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x48 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x49 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4A */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4B */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4C */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4D */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4E */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4F */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "cmovg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x50 */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PS, AM_NOT_USED, "movmskps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_PD, AM_NOT_USED, "movmskpd" } }, - /* 0x51 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "sqrtps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "sqrtsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "sqrtss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "sqrtpd" } }, - /* 0x52 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rsqrtps", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rsqrtss" }, - /* 66h */ { 0 } }, - /* 0x53 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "rcpps", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "rcpss" }, - /* 66h */ { 0 } }, - /* 0x54 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andpd" } }, - /* 0x55 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "andnps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "andnpd" } }, - /* 0x56 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "orps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "orpd" } }, - /* 0x57 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "xorps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "xorpd" } }, - /* 0x58 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "addps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "addsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "addss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "addpd" } }, - /* 0x59 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "mulps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "mulsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "mulss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "mulpd" } }, - /* 0x5A */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PS, AM_NOT_USED, "cvtps2pd", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "cvtsd2ss" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "cvtss2sd" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PD, AM_NOT_USED, "cvtpd2ps" } }, - /* 0x5B */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2ps", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvttps2dq" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PS, AM_NOT_USED, "cvtps2dq" } }, - /* 0x5C */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "subps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "subsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "subss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "subpd" } }, - /* 0x5D */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "minps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "minsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "minss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "minpd" } }, - /* 0x5E */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "divps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "divsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "divss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "divpd" } }, - /* 0x5F */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_NOT_USED, "maxps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_NOT_USED, "maxsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_NOT_USED, "maxss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_NOT_USED, "maxpd" } }, - /* 0x60 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklbw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklbw" } }, - /* 0x61 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpcklwd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklwd" } }, - /* 0x62 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckldq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpckldq" } }, - /* 0x63 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packsswb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packsswb" } }, - /* 0x64 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtb" } }, - /* 0x65 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtw" } }, - /* 0x66 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "pcmpgtd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpgtd" } }, - /* 0x67 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packuswb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "packuswb" } }, - /* 0x68 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhbw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhbw" } }, - /* 0x69 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhwd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhwd" } }, - /* 0x6A */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "punpckhdq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "punpckhdq" } }, - /* 0x6B */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "packssdw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_Q | OT_DQ, AM_NOT_USED, "packssdw" } }, - /* 0x6C */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, - /* 0x6D */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "punpcklqdq" } }, - /* 0x6E */ { 0, IT_GENERIC, AM_P | OT_D, AM_E | OT_D, AM_NOT_USED, "movd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_NOT_USED, "movd" } }, - /* 0x6F */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_D, AM_NOT_USED, "movq", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqu" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "movdqa" } }, - /* 0x70 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_I | OT_B, "pshuf", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshuflw" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufhw" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_I | OT_B, "pshufd" } }, - /* 0x71 */ { 19, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x72 */ { 20, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x73 */ { 21, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x74 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqb" } }, - /* 0x75 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqw" } }, - /* 0x76 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pcmpeqd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pcmpeqd" } }, - /* 0x77 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "emms", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - // The following six opcodes are escapes into the MMX stuff, which this disassembler does not support. - /* 0x78 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x79 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7A */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7B */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7C */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7D */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - - /* 0x7E */ { 0, IT_GENERIC, AM_E | OT_D, AM_P | OT_D, AM_NOT_USED, "movd", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movq" }, - /* 66h */ { 0, IT_GENERIC, AM_E | OT_D, AM_V | OT_DQ, AM_NOT_USED, "movd" } }, - /* 0x7F */ { 0, IT_GENERIC, AM_Q | OT_Q, AM_P | OT_Q, AM_NOT_USED, "movq", true, - /* F2h */ { 0 }, - /* F3h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqu" }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movdqa" } }, - /* 0x80 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x81 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x82 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x83 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x84 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x85 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x86 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x87 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "ja", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x88 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "js", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x89 */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8A */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8B */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8C */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8D */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8E */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x8F */ { 0, IT_JUMP, AM_J | OT_V, AM_NOT_USED, AM_NOT_USED, "jg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x90 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seto", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x91 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setno", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x92 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x93 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x94 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x95 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setnz", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x96 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setbe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x97 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "seta", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x98 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "sets", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x99 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setns", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9A */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpe", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9B */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setpo", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9C */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9D */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setge", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9E */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setle", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x9F */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "setg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA0 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA1 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "cpuid", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shld", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA6 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA7 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA8 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xA9 */ { 0, IT_GENERIC, AM_REGISTER | OT_W, AM_NOT_USED, AM_NOT_USED, "pop", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAA */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "rsm", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAC */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAD */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_I | OT_B | AM_REGISTER, "shrd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAE */ { 22, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xAF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "cmpxchg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB2 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lss", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB4 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lfs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB5 */ { 0, IT_GENERIC, AM_M | OT_P, AM_NOT_USED, AM_NOT_USED, "lgs", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB6 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB7 */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movzx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB8 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xB9 */ { 0, IT_UNKNOWN, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ud1", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBA */ { 23, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBB */ { 0, IT_GENERIC, AM_E | OT_V, AM_G | OT_V, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBC */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsf", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBD */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_V, AM_NOT_USED, "bsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBE */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_B, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xBF */ { 0, IT_GENERIC, AM_G | OT_V, AM_E | OT_W, AM_NOT_USED, "movsx", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_G | OT_B, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "xadd", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC2 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "cmpps", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_SD, AM_W | OT_SD, AM_I | OT_B, "cmpsd" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_SS, AM_W | OT_SS, AM_I | OT_B, "cmpss" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "cmppd" } }, - /* 0xC3 */ { 0, IT_GENERIC, AM_E | OT_D, AM_G | OT_D, AM_NOT_USED, "movnti", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_E | OT_D, AM_I | OT_B, "pinsrw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_E | OT_D, AM_I | OT_B, "pinsrw" } }, - /* 0xC5 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_I | OT_B, "pextrw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_I | OT_B, "pextrw" } }, - /* 0xC6 */ { 0, IT_GENERIC, AM_V | OT_PS, AM_W | OT_PS, AM_I | OT_B, "shufps", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_PD, AM_I | OT_B, "shufpd" } }, - /* 0xC7 */ { 24, IT_REFERENCE, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC8 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xC9 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCA */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCB */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCC */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCD */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCE */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xCF */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "bswap", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xD1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlw" } }, - /* 0xD2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrld" } }, - /* 0xD3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrlq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrlq" } }, - /* 0xD4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddq" } }, - /* 0xD5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmullw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmullw" } }, - /* 0xD6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "unused without prefix", true, - /* F2h */ { 0, IT_GENERIC, AM_P | OT_Q, AM_W | OT_Q, AM_NOT_USED, "movdq2q" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_Q | OT_Q, AM_NOT_USED, "movq2dq" }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movq" } }, - /* 0xD7 */ { 0, IT_GENERIC, AM_G | OT_D, AM_P | OT_Q, AM_NOT_USED, "pmovmskb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_G | OT_D, AM_V | OT_DQ, AM_NOT_USED, "pmovmskb" } }, - /* 0xD8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusb" } }, - /* 0xD9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubusw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubusw" } }, - /* 0xDA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminub", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminub" } }, - /* 0xDB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pand", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pand" } }, - /* 0xDC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusb" } }, - /* 0xDD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddusw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddusw" } }, - /* 0xDE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxub", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxub" } }, - /* 0xDF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pandn", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pandn" } }, - /* 0xE0 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgb" } }, - /* 0xE1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psraw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrqw" } }, - /* 0xE2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psrad", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psrad" } }, - /* 0xE3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pavgw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pavgw" } }, - /* 0xE4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhuw" } }, - /* 0xE5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmulhuw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmulhw" } }, - /* 0xE6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "not used without prefix", true, - /* F2h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvtpd2dq" }, - /* F3h */ { 0, IT_GENERIC, AM_V | OT_PD, AM_W | OT_DQ, AM_NOT_USED, "cvtdq2pd" }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_PD, AM_NOT_USED, "cvttpd2dq" } }, - /* 0xE7 */ { 0, IT_GENERIC, AM_W | OT_Q, AM_V | OT_Q, AM_NOT_USED, "movntq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_V | OT_DQ, AM_NOT_USED, "movntdq" } }, - /* 0xE8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsb" } }, - /* 0xE9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubsw" } }, - /* 0xEA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pminsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pminsw" } }, - /* 0xEB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "por", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "por" } }, - /* 0xEC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsb" } }, - /* 0xED */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddsw" } }, - /* 0xEE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaxsw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaxsw" } }, - /* 0xEF */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pxor", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pxor" } }, - /* 0xF0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0xF1 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllw" } }, - /* 0xF2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pslld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pslld" } }, - /* 0xF3 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psllq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psllq" } }, - /* 0xF4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmuludq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmuludq" } }, - /* 0xF5 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "pmaddwd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "pmaddwd" } }, - /* 0xF6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psadbw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psadbw" } }, - /* 0xF7 */ { 0, IT_GENERIC, AM_P | OT_PI, AM_Q | OT_PI, AM_NOT_USED, "maskmovq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "maskmovdqu" } }, - /* 0xF8 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubb" } }, - /* 0xF9 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubw" } }, - /* 0xFA */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubd" } }, - /* 0xFB */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "psubq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "psubq" } }, - /* 0xFC */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddb", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddb" } }, - /* 0xFD */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddw" } }, - /* 0xFE */ { 0, IT_GENERIC, AM_P | OT_Q, AM_Q | OT_Q, AM_NOT_USED, "paddd", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_V | OT_DQ, AM_W | OT_DQ, AM_NOT_USED, "paddd" } }, - /* 0xFF */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f00[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "sldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "str", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lldt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "ltr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "verw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f01[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "sidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lgdt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_M | OT_S, AM_NOT_USED, AM_NOT_USED, "lidt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "smsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_W, AM_NOT_USED, AM_NOT_USED, "lmsw", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_M | OT_B, AM_NOT_USED, AM_NOT_USED, "invlpg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f18[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_M | OT_ADDRESS_MODE_M, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_REGISTER | OT_D, AM_NOT_USED, AM_NOT_USED, "prefetch", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f71[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlw" } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psraw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psraw" } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllw", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_P | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllw" } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f72[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrld" } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrad", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrad" } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "pslld", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslld" } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0f73[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psrlq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psrlq" } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_P | OT_Q, AM_I | OT_B, AM_NOT_USED, "psllq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "psllq" } }, - /* 0x7 */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq", true, - /* F2h */ { 0 }, - /* F3h */ { 0 }, - /* 66h */ { 0, IT_GENERIC, AM_W | OT_DQ, AM_I | OT_B, AM_NOT_USED, "pslldq" } }, -}; - -const Opcode s_opcode_byte_after_0fae[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxsave", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "fxrstor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "ldmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "stmxcsr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "lfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "mfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, "clflush/sfence", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, -}; - -const Opcode s_opcode_byte_after_0fba[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bt", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "bts", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "btc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_0fc7[] = { - /* 0x0 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_M | OT_Q, AM_NOT_USED, AM_NOT_USED, "cmpxch8b", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_80[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_81[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_82[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_83[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "add", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "or", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "adc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sbb", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "and", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sub", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "xor", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "cmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_c0[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_c1[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d0[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d1[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_IMPLICIT, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d2[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_B, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_d3[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rol", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "ror", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "rcr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shl", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "shr", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sal", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_E | OT_V, AM_REGISTER | OT_B, AM_NOT_USED, "sar", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_f6[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_I | OT_B, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, OT_B | AM_REGISTER, AM_E | OT_B, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_B, AM_E | OT_B, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_f7[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_I | OT_V, AM_NOT_USED, "test", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "not", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "neg", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "mul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "imul", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "div", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_GENERIC, AM_REGISTER | OT_V, AM_E | OT_V, AM_NOT_USED, "idiv", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_fe[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_B, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -const Opcode s_opcode_byte_after_ff[] = { - /* 0x0 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "inc", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x1 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "dec", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x2 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x3 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "call", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x4 */ { 0, IT_JUMP, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x5 */ { 0, IT_JUMP, AM_E | OT_P, AM_NOT_USED, AM_NOT_USED, "jmp", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x6 */ { 0, IT_GENERIC, AM_E | OT_V, AM_NOT_USED, AM_NOT_USED, "push", false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } }, - /* 0x7 */ { 0, IT_UNUSED, AM_NOT_USED, AM_NOT_USED, AM_NOT_USED, 0, false, /* F2h */ { 0 }, /* F3h */ { 0 }, /* 66h */ { 0 } } -}; - -/* -* A table of all the other tables, containing some extra information, e.g. -* how to mask out the byte we're looking at. -*/ -const OpcodeTable MiniDisassembler::s_ia32_opcode_map_[]={ - // One-byte opcodes and jumps to larger - /* 0 */ {s_first_opcode_byte, 0, 0xff, 0, 0xff}, - // Two-byte opcodes (second byte) - /* 1 */ {s_opcode_byte_after_0f, 0, 0xff, 0, 0xff}, - // Start of tables for opcodes using ModR/M bits as extension - /* 2 */ {s_opcode_byte_after_80, 3, 0x07, 0, 0x07}, - /* 3 */ {s_opcode_byte_after_81, 3, 0x07, 0, 0x07}, - /* 4 */ {s_opcode_byte_after_82, 3, 0x07, 0, 0x07}, - /* 5 */ {s_opcode_byte_after_83, 3, 0x07, 0, 0x07}, - /* 6 */ {s_opcode_byte_after_c0, 3, 0x07, 0, 0x07}, - /* 7 */ {s_opcode_byte_after_c1, 3, 0x07, 0, 0x07}, - /* 8 */ {s_opcode_byte_after_d0, 3, 0x07, 0, 0x07}, - /* 9 */ {s_opcode_byte_after_d1, 3, 0x07, 0, 0x07}, - /* 10 */ {s_opcode_byte_after_d2, 3, 0x07, 0, 0x07}, - /* 11 */ {s_opcode_byte_after_d3, 3, 0x07, 0, 0x07}, - /* 12 */ {s_opcode_byte_after_f6, 3, 0x07, 0, 0x07}, - /* 13 */ {s_opcode_byte_after_f7, 3, 0x07, 0, 0x07}, - /* 14 */ {s_opcode_byte_after_fe, 3, 0x07, 0, 0x01}, - /* 15 */ {s_opcode_byte_after_ff, 3, 0x07, 0, 0x07}, - /* 16 */ {s_opcode_byte_after_0f00, 3, 0x07, 0, 0x07}, - /* 17 */ {s_opcode_byte_after_0f01, 3, 0x07, 0, 0x07}, - /* 18 */ {s_opcode_byte_after_0f18, 3, 0x07, 0, 0x07}, - /* 19 */ {s_opcode_byte_after_0f71, 3, 0x07, 0, 0x07}, - /* 20 */ {s_opcode_byte_after_0f72, 3, 0x07, 0, 0x07}, - /* 21 */ {s_opcode_byte_after_0f73, 3, 0x07, 0, 0x07}, - /* 22 */ {s_opcode_byte_after_0fae, 3, 0x07, 0, 0x07}, - /* 23 */ {s_opcode_byte_after_0fba, 3, 0x07, 0, 0x07}, - /* 24 */ {s_opcode_byte_after_0fc7, 3, 0x07, 0, 0x01} -}; - -} // namespace sidestep diff --git a/chromium/sandbox/win/src/sidestep/mini_disassembler.cpp b/chromium/sandbox/win/src/sidestep/mini_disassembler.cpp deleted file mode 100644 index b2648bbf02b..00000000000 --- a/chromium/sandbox/win/src/sidestep/mini_disassembler.cpp +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Implementation of MiniDisassembler. - -#ifdef _WIN64 -#error The code in this file should not be used on 64-bit Windows. -#endif - -#include "sandbox/win/src/sidestep/mini_disassembler.h" - -namespace sidestep { - -MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits, - bool address_default_is_32_bits) - : operand_default_is_32_bits_(operand_default_is_32_bits), - address_default_is_32_bits_(address_default_is_32_bits) { - Initialize(); -} - -MiniDisassembler::MiniDisassembler() - : operand_default_is_32_bits_(true), - address_default_is_32_bits_(true) { - Initialize(); -} - -InstructionType MiniDisassembler::Disassemble( - unsigned char* start_byte, - unsigned int* instruction_bytes) { - // Clean up any state from previous invocations. - Initialize(); - - // Start by processing any prefixes. - unsigned char* current_byte = start_byte; - unsigned int size = 0; - InstructionType instruction_type = ProcessPrefixes(current_byte, &size); - - if (IT_UNKNOWN == instruction_type) - return instruction_type; - - current_byte += size; - size = 0; - - // Invariant: We have stripped all prefixes, and the operand_is_32_bits_ - // and address_is_32_bits_ flags are correctly set. - - instruction_type = ProcessOpcode(current_byte, 0, &size); - - // Check for error processing instruction - if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) { - return IT_UNKNOWN; - } - - current_byte += size; - - // Invariant: operand_bytes_ indicates the total size of operands - // specified by the opcode and/or ModR/M byte and/or SIB byte. - // pCurrentByte points to the first byte after the ModR/M byte, or after - // the SIB byte if it is present (i.e. the first byte of any operands - // encoded in the instruction). - - // We get the total length of any prefixes, the opcode, and the ModR/M and - // SIB bytes if present, by taking the difference of the original starting - // address and the current byte (which points to the first byte of the - // operands if present, or to the first byte of the next instruction if - // they are not). Adding the count of bytes in the operands encoded in - // the instruction gives us the full length of the instruction in bytes. - *instruction_bytes += operand_bytes_ + (current_byte - start_byte); - - // Return the instruction type, which was set by ProcessOpcode(). - return instruction_type_; -} - -void MiniDisassembler::Initialize() { - operand_is_32_bits_ = operand_default_is_32_bits_; - address_is_32_bits_ = address_default_is_32_bits_; - operand_bytes_ = 0; - have_modrm_ = false; - should_decode_modrm_ = false; - instruction_type_ = IT_UNKNOWN; - got_f2_prefix_ = false; - got_f3_prefix_ = false; - got_66_prefix_ = false; -} - -InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte, - unsigned int* size) { - InstructionType instruction_type = IT_GENERIC; - const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte]; - - switch (opcode.type_) { - case IT_PREFIX_ADDRESS: - address_is_32_bits_ = !address_default_is_32_bits_; - goto nochangeoperand; - case IT_PREFIX_OPERAND: - operand_is_32_bits_ = !operand_default_is_32_bits_; - nochangeoperand: - case IT_PREFIX: - - if (0xF2 == (*start_byte)) - got_f2_prefix_ = true; - else if (0xF3 == (*start_byte)) - got_f3_prefix_ = true; - else if (0x66 == (*start_byte)) - got_66_prefix_ = true; - - instruction_type = opcode.type_; - (*size)++; - // we got a prefix, so add one and check next byte - ProcessPrefixes(start_byte + 1, size); - break; - default: - break; // not a prefix byte - } - - return instruction_type; -} - -InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte, - unsigned int table_index, - unsigned int* size) { - const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table - unsigned char current_byte = (*start_byte) >> table.shift_; - current_byte = current_byte & table.mask_; // Mask out the bits we will use - - // Check whether the byte we have is inside the table we have. - if (current_byte < table.min_lim_ || current_byte > table.max_lim_) { - instruction_type_ = IT_UNKNOWN; - return instruction_type_; - } - - const Opcode& opcode = table.table_[current_byte]; - if (IT_UNUSED == opcode.type_) { - // This instruction is not used by the IA-32 ISA, so we indicate - // this to the user. Probably means that we were pointed to - // a byte in memory that was not the start of an instruction. - instruction_type_ = IT_UNUSED; - return instruction_type_; - } else if (IT_REFERENCE == opcode.type_) { - // We are looking at an opcode that has more bytes (or is continued - // in the ModR/M byte). Recursively find the opcode definition in - // the table for the opcode's next byte. - (*size)++; - ProcessOpcode(start_byte + 1, opcode.table_index_, size); - return instruction_type_; - } - - const SpecificOpcode* specific_opcode = reinterpret_cast< - const SpecificOpcode*>(&opcode); - if (opcode.is_prefix_dependent_) { - if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) { - specific_opcode = &opcode.opcode_if_f2_prefix_; - } else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) { - specific_opcode = &opcode.opcode_if_f3_prefix_; - } else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) { - specific_opcode = &opcode.opcode_if_66_prefix_; - } - } - - // Inv: The opcode type is known. - instruction_type_ = specific_opcode->type_; - - // Let's process the operand types to see if we have any immediate - // operands, and/or a ModR/M byte. - - ProcessOperand(specific_opcode->flag_dest_); - ProcessOperand(specific_opcode->flag_source_); - ProcessOperand(specific_opcode->flag_aux_); - - // Inv: We have processed the opcode and incremented operand_bytes_ - // by the number of bytes of any operands specified by the opcode - // that are stored in the instruction (not registers etc.). Now - // we need to return the total number of bytes for the opcode and - // for the ModR/M or SIB bytes if they are present. - - if (table.mask_ != 0xff) { - if (have_modrm_) { - // we're looking at a ModR/M byte so we're not going to - // count that into the opcode size - ProcessModrm(start_byte, size); - return IT_GENERIC; - } else { - // need to count the ModR/M byte even if it's just being - // used for opcode extension - (*size)++; - return IT_GENERIC; - } - } else { - if (have_modrm_) { - // The ModR/M byte is the next byte. - (*size)++; - ProcessModrm(start_byte + 1, size); - return IT_GENERIC; - } else { - (*size)++; - return IT_GENERIC; - } - } -} - -bool MiniDisassembler::ProcessOperand(int flag_operand) { - bool succeeded = true; - if (AM_NOT_USED == flag_operand) - return succeeded; - - // Decide what to do based on the addressing mode. - switch (flag_operand & AM_MASK) { - // No ModR/M byte indicated by these addressing modes, and no - // additional (e.g. immediate) parameters. - case AM_A: // Direct address - case AM_F: // EFLAGS register - case AM_X: // Memory addressed by the DS:SI register pair - case AM_Y: // Memory addressed by the ES:DI register pair - case AM_IMPLICIT: // Parameter is implicit, occupies no space in - // instruction - break; - - // There is a ModR/M byte but it does not necessarily need - // to be decoded. - case AM_C: // reg field of ModR/M selects a control register - case AM_D: // reg field of ModR/M selects a debug register - case AM_G: // reg field of ModR/M selects a general register - case AM_P: // reg field of ModR/M selects an MMX register - case AM_R: // mod field of ModR/M may refer only to a general register - case AM_S: // reg field of ModR/M selects a segment register - case AM_T: // reg field of ModR/M selects a test register - case AM_V: // reg field of ModR/M selects a 128-bit XMM register - have_modrm_ = true; - break; - - // In these addressing modes, there is a ModR/M byte and it needs to be - // decoded. No other (e.g. immediate) params than indicated in ModR/M. - case AM_E: // Operand is either a general-purpose register or memory, - // specified by ModR/M byte - case AM_M: // ModR/M byte will refer only to memory - case AM_Q: // Operand is either an MMX register or memory (complex - // evaluation), specified by ModR/M byte - case AM_W: // Operand is either a 128-bit XMM register or memory (complex - // eval), specified by ModR/M byte - have_modrm_ = true; - should_decode_modrm_ = true; - break; - - // These addressing modes specify an immediate or an offset value - // directly, so we need to look at the operand type to see how many - // bytes. - case AM_I: // Immediate data. - case AM_J: // Jump to offset. - case AM_O: // Operand is at offset. - switch (flag_operand & OT_MASK) { - case OT_B: // Byte regardless of operand-size attribute. - operand_bytes_ += OS_BYTE; - break; - case OT_C: // Byte or word, depending on operand-size attribute. - if (operand_is_32_bits_) - operand_bytes_ += OS_WORD; - else - operand_bytes_ += OS_BYTE; - break; - case OT_D: // Doubleword, regardless of operand-size attribute. - operand_bytes_ += OS_DOUBLE_WORD; - break; - case OT_DQ: // Double-quadword, regardless of operand-size attribute. - operand_bytes_ += OS_DOUBLE_QUAD_WORD; - break; - case OT_P: // 32-bit or 48-bit pointer, depending on operand-size - // attribute. - if (operand_is_32_bits_) - operand_bytes_ += OS_48_BIT_POINTER; - else - operand_bytes_ += OS_32_BIT_POINTER; - break; - case OT_PS: // 128-bit packed single-precision floating-point data. - operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING; - break; - case OT_Q: // Quadword, regardless of operand-size attribute. - operand_bytes_ += OS_QUAD_WORD; - break; - case OT_S: // 6-byte pseudo-descriptor. - operand_bytes_ += OS_PSEUDO_DESCRIPTOR; - break; - case OT_SD: // Scalar Double-Precision Floating-Point Value - case OT_PD: // Unaligned packed double-precision floating point value - operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING; - break; - case OT_SS: - // Scalar element of a 128-bit packed single-precision - // floating data. - // We simply return enItUnknown since we don't have to support - // floating point - succeeded = false; - break; - case OT_V: // Word or doubleword, depending on operand-size attribute. - if (operand_is_32_bits_) - operand_bytes_ += OS_DOUBLE_WORD; - else - operand_bytes_ += OS_WORD; - break; - case OT_W: // Word, regardless of operand-size attribute. - operand_bytes_ += OS_WORD; - break; - - // Can safely ignore these. - case OT_A: // Two one-word operands in memory or two double-word - // operands in memory - case OT_PI: // Quadword MMX technology register (e.g. mm0) - case OT_SI: // Doubleword integer register (e.g., eax) - break; - - default: - break; - } - break; - - default: - break; - } - - return succeeded; -} - -bool MiniDisassembler::ProcessModrm(unsigned char* start_byte, - unsigned int* size) { - // If we don't need to decode, we just return the size of the ModR/M - // byte (there is never a SIB byte in this case). - if (!should_decode_modrm_) { - (*size)++; - return true; - } - - // We never care about the reg field, only the combination of the mod - // and r/m fields, so let's start by packing those fields together into - // 5 bits. - unsigned char modrm = (*start_byte); - unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field - modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field - mod = mod >> 3; // shift the mod field to the right place - modrm = mod | modrm; // combine the r/m and mod fields as discussed - mod = mod >> 3; // shift the mod field to bits 2..0 - - // Invariant: modrm contains the mod field in bits 4..3 and the r/m field - // in bits 2..0, and mod contains the mod field in bits 2..0 - - const ModrmEntry* modrm_entry = 0; - if (address_is_32_bits_) - modrm_entry = &s_ia32_modrm_map_[modrm]; - else - modrm_entry = &s_ia16_modrm_map_[modrm]; - - // Invariant: modrm_entry points to information that we need to decode - // the ModR/M byte. - - // Add to the count of operand bytes, if the ModR/M byte indicates - // that some operands are encoded in the instruction. - if (modrm_entry->is_encoded_in_instruction_) - operand_bytes_ += modrm_entry->operand_size_; - - // Process the SIB byte if necessary, and return the count - // of ModR/M and SIB bytes. - if (modrm_entry->use_sib_byte_) { - (*size)++; - return ProcessSib(start_byte + 1, mod, size); - } else { - (*size)++; - return true; - } -} - -bool MiniDisassembler::ProcessSib(unsigned char* start_byte, - unsigned char mod, - unsigned int* size) { - // get the mod field from the 2..0 bits of the SIB byte - unsigned char sib_base = (*start_byte) & 0x07; - if (0x05 == sib_base) { - switch (mod) { - case 0x00: // mod == 00 - case 0x02: // mod == 10 - operand_bytes_ += OS_DOUBLE_WORD; - break; - case 0x01: // mod == 01 - operand_bytes_ += OS_BYTE; - break; - case 0x03: // mod == 11 - // According to the IA-32 docs, there does not seem to be a disp - // value for this value of mod - default: - break; - } - } - - (*size)++; - return true; -} - -} // namespace sidestep diff --git a/chromium/sandbox/win/src/sidestep/mini_disassembler.h b/chromium/sandbox/win/src/sidestep/mini_disassembler.h deleted file mode 100644 index 501ab638296..00000000000 --- a/chromium/sandbox/win/src/sidestep/mini_disassembler.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Definition of MiniDisassembler. - -#ifndef SANDBOX_WIN_SRC_SIDESTEP_MINI_DISASSEMBLER_H_ -#define SANDBOX_WIN_SRC_SIDESTEP_MINI_DISASSEMBLER_H_ - -#include "sandbox/win/src/sidestep/mini_disassembler_types.h" - -namespace sidestep { - -// This small disassembler is very limited -// in its functionality, and in fact does only the bare minimum required by the -// preamble patching utility. It may be useful for other purposes, however. -// -// The limitations include at least the following: -// -# No support for coprocessor opcodes, MMX, etc. -// -# No machine-readable identification of opcodes or decoding of -// assembly parameters. The name of the opcode (as a string) is given, -// however, to aid debugging. -// -// You may ask what this little disassembler actually does, then? The answer is -// that it does the following, which is exactly what the patching utility needs: -// -# Indicates if opcode is a jump (any kind) or a return (any kind) -// because this is important for the patching utility to determine if -// a function is too short or there are jumps too early in it for it -// to be preamble patched. -// -# The opcode length is always calculated, so that the patching utility -// can figure out where the next instruction starts, and whether it -// already has enough instructions to replace with the absolute jump -// to the patching code. -// -// The usage is quite simple; just create a MiniDisassembler and use its -// Disassemble() method. -// -// If you would like to extend this disassembler, please refer to the -// IA-32 Intel Architecture Software Developer's Manual Volume 2: -// Instruction Set Reference for information about operand decoding -// etc. -class MiniDisassembler { - public: - // Creates a new instance and sets defaults. - // - // operand_default_32_bits: If true, the default operand size is - // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. - // address_default_32_bits: If true, the default address size is - // set to 32 bits, which is the default under Win32. Otherwise it is 16 bits. - MiniDisassembler(bool operand_default_32_bits, bool address_default_32_bits); - - // Equivalent to MiniDisassembler(true, true); - MiniDisassembler(); - - // Attempts to disassemble a single instruction starting from the - // address in memory it is pointed to. - // - // start: Address where disassembly should start. - // instruction_bytes: Variable that will be incremented by - // the length in bytes of the instruction. - // Returns enItJump, enItReturn or enItGeneric on success. enItUnknown - // if unable to disassemble, enItUnused if this seems to be an unused - // opcode. In the last two (error) cases, cbInstruction will be set - // to 0xffffffff. - // - // Postcondition: This instance of the disassembler is ready to be used again, - // with unchanged defaults from creation time. - InstructionType Disassemble(unsigned char* start, - unsigned int* instruction_bytes); - - private: - // Makes the disassembler ready for reuse. - void Initialize(); - - // Sets the flags for address and operand sizes. - // Returns Number of prefix bytes. - InstructionType ProcessPrefixes(unsigned char* start, unsigned int* size); - - // Sets the flag for whether we have ModR/M, and increments - // operand_bytes_ if any are specifies by the opcode directly. - // Returns Number of opcode bytes. - InstructionType ProcessOpcode(unsigned char* start, - unsigned int table, - unsigned int* size); - - // Checks the type of the supplied operand. Increments - // operand_bytes_ if it directly indicates an immediate etc. - // operand. Asserts have_modrm_ if the operand specifies - // a ModR/M byte. - bool ProcessOperand(int flag_operand); - - // Increments operand_bytes_ by size specified by ModR/M and - // by SIB if present. - // Returns 0 in case of error, 1 if there is just a ModR/M byte, - // 2 if there is a ModR/M byte and a SIB byte. - bool ProcessModrm(unsigned char* start, unsigned int* size); - - // Processes the SIB byte that it is pointed to. - // start: Pointer to the SIB byte. - // mod: The mod field from the ModR/M byte. - // Returns 1 to indicate success (indicates 1 SIB byte) - bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int* size); - - // The instruction type we have decoded from the opcode. - InstructionType instruction_type_; - - // Counts the number of bytes that is occupied by operands in - // the current instruction (note: we don't care about how large - // operands stored in registers etc. are). - unsigned int operand_bytes_; - - // True iff there is a ModR/M byte in this instruction. - bool have_modrm_; - - // True iff we need to decode the ModR/M byte (sometimes it just - // points to a register, we can tell by the addressing mode). - bool should_decode_modrm_; - - // Current operand size is 32 bits if true, 16 bits if false. - bool operand_is_32_bits_; - - // Default operand size is 32 bits if true, 16 bits if false. - bool operand_default_is_32_bits_; - - // Current address size is 32 bits if true, 16 bits if false. - bool address_is_32_bits_; - - // Default address size is 32 bits if true, 16 bits if false. - bool address_default_is_32_bits_; - - // Huge big opcode table based on the IA-32 manual, defined - // in Ia32OpcodeMap.cpp - static const OpcodeTable s_ia32_opcode_map_[]; - - // Somewhat smaller table to help with decoding ModR/M bytes - // when 16-bit addressing mode is being used. Defined in - // Ia32ModrmMap.cpp - static const ModrmEntry s_ia16_modrm_map_[]; - - // Somewhat smaller table to help with decoding ModR/M bytes - // when 32-bit addressing mode is being used. Defined in - // Ia32ModrmMap.cpp - static const ModrmEntry s_ia32_modrm_map_[]; - - // Indicators of whether we got certain prefixes that certain - // silly Intel instructions depend on in nonstandard ways for - // their behaviors. - bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_; -}; - -} // namespace sidestep - -#endif // SANDBOX_WIN_SRC_SIDESTEP_MINI_DISASSEMBLER_H_ diff --git a/chromium/sandbox/win/src/sidestep/mini_disassembler_types.h b/chromium/sandbox/win/src/sidestep/mini_disassembler_types.h deleted file mode 100644 index 394d8d8d61b..00000000000 --- a/chromium/sandbox/win/src/sidestep/mini_disassembler_types.h +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Several simple types used by the disassembler and some of the patching -// mechanisms. - -#ifndef SANDBOX_WIN_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H_ -#define SANDBOX_WIN_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H_ - -namespace sidestep { - -// Categories of instructions that we care about -enum InstructionType { - // This opcode is not used - IT_UNUSED, - // This disassembler does not recognize this opcode (error) - IT_UNKNOWN, - // This is not an instruction but a reference to another table - IT_REFERENCE, - // This byte is a prefix byte that we can ignore - IT_PREFIX, - // This is a prefix byte that switches to the nondefault address size - IT_PREFIX_ADDRESS, - // This is a prefix byte that switches to the nondefault operand size - IT_PREFIX_OPERAND, - // A jump or call instruction - IT_JUMP, - // A return instruction - IT_RETURN, - // Any other type of instruction (in this case we don't care what it is) - IT_GENERIC, -}; - -// Lists IA-32 operand sizes in multiples of 8 bits -enum OperandSize { - OS_ZERO = 0, - OS_BYTE = 1, - OS_WORD = 2, - OS_DOUBLE_WORD = 4, - OS_QUAD_WORD = 8, - OS_DOUBLE_QUAD_WORD = 16, - OS_32_BIT_POINTER = 32 / 8, - OS_48_BIT_POINTER = 48 / 8, - OS_SINGLE_PRECISION_FLOATING = 32 / 8, - OS_DOUBLE_PRECISION_FLOATING = 64 / 8, - OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80 / 8, - OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128 / 8, - OS_PSEUDO_DESCRIPTOR = 6 -}; - -// Operand addressing methods from the IA-32 manual. The enAmMask value -// is a mask for the rest. The other enumeration values are named for the -// names given to the addressing methods in the manual, e.g. enAm_D is for -// the D addressing method. -// -// The reason we use a full 4 bytes and a mask, is that we need to combine -// these flags with the enOperandType to store the details -// on the operand in a single integer. -enum AddressingMethod { - AM_NOT_USED = 0, // This operand is not used for this instruction - AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration - AM_A = 0x00010000, // A addressing type - AM_C = 0x00020000, // C addressing type - AM_D = 0x00030000, // D addressing type - AM_E = 0x00040000, // E addressing type - AM_F = 0x00050000, // F addressing type - AM_G = 0x00060000, // G addressing type - AM_I = 0x00070000, // I addressing type - AM_J = 0x00080000, // J addressing type - AM_M = 0x00090000, // M addressing type - AM_O = 0x000A0000, // O addressing type - AM_P = 0x000B0000, // P addressing type - AM_Q = 0x000C0000, // Q addressing type - AM_R = 0x000D0000, // R addressing type - AM_S = 0x000E0000, // S addressing type - AM_T = 0x000F0000, // T addressing type - AM_V = 0x00100000, // V addressing type - AM_W = 0x00110000, // W addressing type - AM_X = 0x00120000, // X addressing type - AM_Y = 0x00130000, // Y addressing type - AM_REGISTER = 0x00140000, // Specific register is always used as this op - AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used -}; - -// Operand types from the IA-32 manual. The enOtMask value is -// a mask for the rest. The rest of the values are named for the -// names given to these operand types in the manual, e.g. enOt_ps -// is for the ps operand type in the manual. -// -// The reason we use a full 4 bytes and a mask, is that we need -// to combine these flags with the enAddressingMethod to store the details -// on the operand in a single integer. -enum OperandType { - OT_MASK = 0xFF000000, - OT_A = 0x01000000, - OT_B = 0x02000000, - OT_C = 0x03000000, - OT_D = 0x04000000, - OT_DQ = 0x05000000, - OT_P = 0x06000000, - OT_PI = 0x07000000, - OT_PS = 0x08000000, // actually unsupported for (we don't know its size) - OT_Q = 0x09000000, - OT_S = 0x0A000000, - OT_SS = 0x0B000000, - OT_SI = 0x0C000000, - OT_V = 0x0D000000, - OT_W = 0x0E000000, - OT_SD = 0x0F000000, // scalar double-precision floating-point value - OT_PD = 0x10000000, // double-precision floating point - // dummy "operand type" for address mode M - which doesn't specify - // operand type - OT_ADDRESS_MODE_M = 0x80000000 -}; - -// Everything that's in an Opcode (see below) except the three -// alternative opcode structs for different prefixes. -struct SpecificOpcode { - // Index to continuation table, or 0 if this is the last - // byte in the opcode. - int table_index_; - - // The opcode type - InstructionType type_; - - // Description of the type of the dest, src and aux operands, - // put together from an enOperandType flag and an enAddressingMethod - // flag. - int flag_dest_; - int flag_source_; - int flag_aux_; - - // We indicate the mnemonic for debugging purposes - const char* mnemonic_; -}; - -// The information we keep in our tables about each of the different -// valid instructions recognized by the IA-32 architecture. -struct Opcode { - // Index to continuation table, or 0 if this is the last - // byte in the opcode. - int table_index_; - - // The opcode type - InstructionType type_; - - // Description of the type of the dest, src and aux operands, - // put together from an enOperandType flag and an enAddressingMethod - // flag. - int flag_dest_; - int flag_source_; - int flag_aux_; - - // We indicate the mnemonic for debugging purposes - const char* mnemonic_; - - // Alternative opcode info if certain prefixes are specified. - // In most cases, all of these are zeroed-out. Only used if - // bPrefixDependent is true. - bool is_prefix_dependent_; - SpecificOpcode opcode_if_f2_prefix_; - SpecificOpcode opcode_if_f3_prefix_; - SpecificOpcode opcode_if_66_prefix_; -}; - -// Information about each table entry. -struct OpcodeTable { - // Table of instruction entries - const Opcode* table_; - // How many bytes left to shift ModR/M byte before applying mask - unsigned char shift_; - // Mask to apply to byte being looked at before comparing to table - unsigned char mask_; - // Minimum/maximum indexes in table. - unsigned char min_lim_; - unsigned char max_lim_; -}; - -// Information about each entry in table used to decode ModR/M byte. -struct ModrmEntry { - // Is the operand encoded as bytes in the instruction (rather than - // if it's e.g. a register in which case it's just encoded in the - // ModR/M byte) - bool is_encoded_in_instruction_; - - // Is there a SIB byte? In this case we always need to decode it. - bool use_sib_byte_; - - // What is the size of the operand (only important if it's encoded - // in the instruction)? - OperandSize operand_size_; -}; - -} // namespace sidestep - -#endif // SANDBOX_WIN_SRC_SIDESTEP_MINI_DISASSEMBLER_TYPES_H_ diff --git a/chromium/sandbox/win/src/sidestep/preamble_patcher.h b/chromium/sandbox/win/src/sidestep/preamble_patcher.h deleted file mode 100644 index 1083e89f44e..00000000000 --- a/chromium/sandbox/win/src/sidestep/preamble_patcher.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Definition of PreamblePatcher - -#ifndef SANDBOX_WIN_SRC_SIDESTEP_PREAMBLE_PATCHER_H_ -#define SANDBOX_WIN_SRC_SIDESTEP_PREAMBLE_PATCHER_H_ - -#include - -namespace sidestep { - -// Maximum size of the preamble stub. We overwrite at least the first 5 -// bytes of the function. Considering the worst case scenario, we need 4 -// bytes + the max instruction size + 5 more bytes for our jump back to -// the original code. With that in mind, 32 is a good number :) -const size_t kMaxPreambleStubSize = 32; - -// Possible results of patching/unpatching -enum SideStepError { - SIDESTEP_SUCCESS = 0, - SIDESTEP_INVALID_PARAMETER, - SIDESTEP_INSUFFICIENT_BUFFER, - SIDESTEP_JUMP_INSTRUCTION, - SIDESTEP_FUNCTION_TOO_SMALL, - SIDESTEP_UNSUPPORTED_INSTRUCTION, - SIDESTEP_NO_SUCH_MODULE, - SIDESTEP_NO_SUCH_FUNCTION, - SIDESTEP_ACCESS_DENIED, - SIDESTEP_UNEXPECTED, -}; - -// Implements a patching mechanism that overwrites the first few bytes of -// a function preamble with a jump to our hook function, which is then -// able to call the original function via a specially-made preamble-stub -// that imitates the action of the original preamble. -// -// Note that there are a number of ways that this method of patching can -// fail. The most common are: -// - If there is a jump (jxx) instruction in the first 5 bytes of -// the function being patched, we cannot patch it because in the -// current implementation we do not know how to rewrite relative -// jumps after relocating them to the preamble-stub. Note that -// if you really really need to patch a function like this, it -// would be possible to add this functionality (but at some cost). -// - If there is a return (ret) instruction in the first 5 bytes -// we cannot patch the function because it may not be long enough -// for the jmp instruction we use to inject our patch. -// - If there is another thread currently executing within the bytes -// that are copied to the preamble stub, it will crash in an undefined -// way. -// -// If you get any other error than the above, you're either pointing the -// patcher at an invalid instruction (e.g. into the middle of a multi- -// byte instruction, or not at memory containing executable instructions) -// or, there may be a bug in the disassembler we use to find -// instruction boundaries. -class PreamblePatcher { - public: - // Patches target_function to point to replacement_function using a provided - // preamble_stub of stub_size bytes. - // Returns An error code indicating the result of patching. - template - static SideStepError Patch(T target_function, - T replacement_function, - void* preamble_stub, - size_t stub_size) { - return RawPatchWithStub(target_function, replacement_function, - reinterpret_cast(preamble_stub), - stub_size, nullptr); - } - - private: - // Patches a function by overwriting its first few bytes with - // a jump to a different function. This is similar to the RawPatch - // function except that it uses the stub allocated by the caller - // instead of allocating it. - // - // To use this function, you first have to call VirtualProtect to make the - // target function writable at least for the duration of the call. - // - // target_function: A pointer to the function that should be - // patched. - // - // replacement_function: A pointer to the function that should - // replace the target function. The replacement function must have - // exactly the same calling convention and parameters as the original - // function. - // - // preamble_stub: A pointer to a buffer where the preamble stub - // should be copied. The size of the buffer should be sufficient to - // hold the preamble bytes. - // - // stub_size: Size in bytes of the buffer allocated for the - // preamble_stub - // - // bytes_needed: Pointer to a variable that receives the minimum - // number of bytes required for the stub. Can be set to nullptr if you're - // not interested. - // - // Returns An error code indicating the result of patching. - static SideStepError RawPatchWithStub(void* target_function, - void* replacement_function, - unsigned char* preamble_stub, - size_t stub_size, - size_t* bytes_needed); -}; - -} // namespace sidestep - -#endif // SANDBOX_WIN_SRC_SIDESTEP_PREAMBLE_PATCHER_H_ diff --git a/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp b/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp deleted file mode 100644 index cbe98d05783..00000000000 --- a/chromium/sandbox/win/src/sidestep/preamble_patcher_with_stub.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Implementation of PreamblePatcher - -#include "sandbox/win/src/sidestep/preamble_patcher.h" - -#include - -#include "sandbox/win/src/sandbox_nt_util.h" -#include "sandbox/win/src/sidestep/mini_disassembler.h" - -// Definitions of assembly statements we need -#define ASM_JMP32REL 0xE9 -#define ASM_INT3 0xCC - -namespace { - -// Very basic memcpy. We are copying 4 to 12 bytes most of the time, so there -// is no attempt to optimize this code or have a general purpose function. -// We don't want to call the crt from this code. -inline void* RawMemcpy(void* destination, const void* source, size_t bytes) { - const char* from = reinterpret_cast(source); - char* to = reinterpret_cast(destination); - - for (size_t i = 0; i < bytes ; i++) - to[i] = from[i]; - - return destination; -} - -// Very basic memset. We are filling 1 to 7 bytes most of the time, so there -// is no attempt to optimize this code or have a general purpose function. -// We don't want to call the crt from this code. -inline void* RawMemset(void* destination, int value, size_t bytes) { - char* to = reinterpret_cast(destination); - - for (size_t i = 0; i < bytes ; i++) - to[i] = static_cast(value); - - return destination; -} - -} // namespace - -#define ASSERT(a, b) DCHECK_NT(a) - -namespace sidestep { - -SideStepError PreamblePatcher::RawPatchWithStub( - void* target_function, - void* replacement_function, - unsigned char* preamble_stub, - size_t stub_size, - size_t* bytes_needed) { - if ((NULL == target_function) || - (NULL == replacement_function) || - (NULL == preamble_stub)) { - ASSERT(false, (L"Invalid parameters - either pTargetFunction or " - L"pReplacementFunction or pPreambleStub were NULL.")); - return SIDESTEP_INVALID_PARAMETER; - } - - // TODO(V7:joi) Siggi and I just had a discussion and decided that both - // patching and unpatching are actually unsafe. We also discussed a - // method of making it safe, which is to freeze all other threads in the - // process, check their thread context to see if their eip is currently - // inside the block of instructions we need to copy to the stub, and if so - // wait a bit and try again, then unfreeze all threads once we've patched. - // Not implementing this for now since we're only using SideStep for unit - // testing, but if we ever use it for production code this is what we - // should do. - // - // NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using - // FPU instructions, and on newer processors we could use cmpxchg8b or - // cmpxchg16b. So it might be possible to do the patching/unpatching - // atomically and avoid having to freeze other threads. Note though, that - // doing it atomically does not help if one of the other threads happens - // to have its eip in the middle of the bytes you change while you change - // them. - unsigned char* target = reinterpret_cast(target_function); - - // Let's disassemble the preamble of the target function to see if we can - // patch, and to see how much of the preamble we need to take. We need 5 - // bytes for our jmp instruction, so let's find the minimum number of - // instructions to get 5 bytes. - MiniDisassembler disassembler; - unsigned int preamble_bytes = 0; - while (preamble_bytes < 5) { - InstructionType instruction_type = - disassembler.Disassemble(target + preamble_bytes, &preamble_bytes); - if (IT_JUMP == instruction_type) { - ASSERT(false, (L"Unable to patch because there is a jump instruction " - L"in the first 5 bytes.")); - return SIDESTEP_JUMP_INSTRUCTION; - } else if (IT_RETURN == instruction_type) { - ASSERT(false, (L"Unable to patch because function is too short")); - return SIDESTEP_FUNCTION_TOO_SMALL; - } else if (IT_GENERIC != instruction_type) { - ASSERT(false, (L"Disassembler encountered unsupported instruction " - L"(either unused or unknown")); - return SIDESTEP_UNSUPPORTED_INSTRUCTION; - } - } - - if (NULL != bytes_needed) - *bytes_needed = preamble_bytes + 5; - - // Inv: preamble_bytes is the number of bytes (at least 5) that we need to - // take from the preamble to have whole instructions that are 5 bytes or more - // in size total. The size of the stub required is cbPreamble + size of - // jmp (5) - if (preamble_bytes + 5 > stub_size) { - NOTREACHED_NT(); - return SIDESTEP_INSUFFICIENT_BUFFER; - } - - // First, copy the preamble that we will overwrite. - RawMemcpy(reinterpret_cast(preamble_stub), - reinterpret_cast(target), preamble_bytes); - - // Now, make a jmp instruction to the rest of the target function (minus the - // preamble bytes we moved into the stub) and copy it into our preamble-stub. - // find address to jump to, relative to next address after jmp instruction -#pragma warning(push) -#pragma warning(disable:4244) - // This assignment generates a warning because it is 32 bit specific. - int relative_offset_to_target_rest - = ((reinterpret_cast(target) + preamble_bytes) - - (preamble_stub + preamble_bytes + 5)); -#pragma warning(pop) - // jmp (Jump near, relative, displacement relative to next instruction) - preamble_stub[preamble_bytes] = ASM_JMP32REL; - // copy the address - RawMemcpy(reinterpret_cast(preamble_stub + preamble_bytes + 1), - reinterpret_cast(&relative_offset_to_target_rest), 4); - - // Inv: preamble_stub points to assembly code that will execute the - // original function by first executing the first cbPreamble bytes of the - // preamble, then jumping to the rest of the function. - - // Overwrite the first 5 bytes of the target function with a jump to our - // replacement function. - // (Jump near, relative, displacement relative to next instruction) - target[0] = ASM_JMP32REL; - - // Find offset from instruction after jmp, to the replacement function. -#pragma warning(push) -#pragma warning(disable:4244) - int offset_to_replacement_function = - reinterpret_cast(replacement_function) - - reinterpret_cast(target) - 5; -#pragma warning(pop) - // complete the jmp instruction - RawMemcpy(reinterpret_cast(target + 1), - reinterpret_cast(&offset_to_replacement_function), 4); - // Set any remaining bytes that were moved to the preamble-stub to INT3 so - // as not to cause confusion (otherwise you might see some strange - // instructions if you look at the disassembly, or even invalid - // instructions). Also, by doing this, we will break into the debugger if - // some code calls into this portion of the code. If this happens, it - // means that this function cannot be patched using this patcher without - // further thought. - if (preamble_bytes > 5) { - RawMemset(reinterpret_cast(target + 5), ASM_INT3, - preamble_bytes - 5); - } - - // Inv: The memory pointed to by target_function now points to a relative - // jump instruction that jumps over to the preamble_stub. The preamble - // stub contains the first stub_size bytes of the original target - // function's preamble code, followed by a relative jump back to the next - // instruction after the first cbPreamble bytes. - - return SIDESTEP_SUCCESS; -} - -} // namespace sidestep - -#undef ASSERT diff --git a/chromium/sandbox/win/src/sidestep_resolver.cc b/chromium/sandbox/win/src/sidestep_resolver.cc deleted file mode 100644 index 59274893b8e..00000000000 --- a/chromium/sandbox/win/src/sidestep_resolver.cc +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sandbox/win/src/sidestep_resolver.h" - -#include - -#include "base/win/pe_image.h" -#include "sandbox/win/src/sandbox_nt_util.h" -#include "sandbox/win/src/sidestep/preamble_patcher.h" - -namespace { - -const size_t kSizeOfSidestepStub = sidestep::kMaxPreambleStubSize; - -struct SidestepThunk { - char sidestep[kSizeOfSidestepStub]; // Storage for the sidestep stub. - int internal_thunk; // Dummy member to the beginning of the internal thunk. -}; - -struct SmartThunk { - const void* module_base; // Target module's base. - const void* interceptor; // Real interceptor. - SidestepThunk sidestep; // Standard sidestep thunk. -}; - -} // namespace - -namespace sandbox { - -NTSTATUS SidestepResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - NTSTATUS ret = - Init(target_module, interceptor_module, target_name, interceptor_name, - interceptor_entry_point, thunk_storage, storage_bytes); - if (!NT_SUCCESS(ret)) - return ret; - - SidestepThunk* thunk = reinterpret_cast(thunk_storage); - - size_t internal_bytes = storage_bytes - kSizeOfSidestepStub; - if (!SetInternalThunk(&thunk->internal_thunk, internal_bytes, thunk_storage, - interceptor_)) - return STATUS_BUFFER_TOO_SMALL; - - AutoProtectMemory memory; - ret = memory.ChangeProtection(target_, kSizeOfSidestepStub, PAGE_READWRITE); - if (!NT_SUCCESS(ret)) - return ret; - - sidestep::SideStepError rv = sidestep::PreamblePatcher::Patch( - target_, reinterpret_cast(&thunk->internal_thunk), thunk_storage, - kSizeOfSidestepStub); - - if (sidestep::SIDESTEP_INSUFFICIENT_BUFFER == rv) - return STATUS_BUFFER_TOO_SMALL; - - if (sidestep::SIDESTEP_SUCCESS != rv) - return STATUS_UNSUCCESSFUL; - - if (storage_used) - *storage_used = GetThunkSize(); - - return ret; -} - -size_t SidestepResolverThunk::GetThunkSize() const { - return GetInternalThunkSize() + kSizeOfSidestepStub; -} - -// This is basically a wrapper around the normal sidestep patch that extends -// the thunk to use a chained interceptor. It uses the fact that -// SetInternalThunk generates the code to pass as the first parameter whatever -// it receives as original_function; we let SidestepResolverThunk set this value -// to its saved code, and then we change it to our thunk data. -NTSTATUS SmartSidestepResolverThunk::Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) { - if (storage_bytes < GetThunkSize()) - return STATUS_BUFFER_TOO_SMALL; - - SmartThunk* thunk = reinterpret_cast(thunk_storage); - thunk->module_base = target_module; - - NTSTATUS ret; - if (interceptor_entry_point) { - thunk->interceptor = interceptor_entry_point; - } else { - ret = ResolveInterceptor(interceptor_module, interceptor_name, - &thunk->interceptor); - if (!NT_SUCCESS(ret)) - return ret; - } - - // Perform a standard sidestep patch on the last part of the thunk, but point - // to our internal smart interceptor. - size_t standard_bytes = storage_bytes - offsetof(SmartThunk, sidestep); - ret = SidestepResolverThunk::Setup(target_module, interceptor_module, - target_name, nullptr, - reinterpret_cast(&SmartStub), - &thunk->sidestep, standard_bytes, nullptr); - if (!NT_SUCCESS(ret)) - return ret; - - // Fix the internal thunk to pass the whole buffer to the interceptor. - SetInternalThunk(&thunk->sidestep.internal_thunk, GetInternalThunkSize(), - thunk_storage, reinterpret_cast(&SmartStub)); - - if (storage_used) - *storage_used = GetThunkSize(); - - return ret; -} - -size_t SmartSidestepResolverThunk::GetThunkSize() const { - return GetInternalThunkSize() + kSizeOfSidestepStub + - offsetof(SmartThunk, sidestep); -} - -// This code must basically either call the intended interceptor or skip the -// call and invoke instead the original function. In any case, we are saving -// the registers that may be trashed by our c++ code. -// -// This function is called with a first parameter inserted by us, that points -// to our SmartThunk. When we call the interceptor we have to replace this -// parameter with the one expected by that function (stored inside our -// structure); on the other hand, when we skip the interceptor we have to remove -// that extra argument before calling the original function. -// -// When we skip the interceptor, the transformation of the stack looks like: -// On Entry: On Use: On Exit: -// [param 2] = first real argument [param 2] (esp+1c) [param 2] -// [param 1] = our SmartThunk [param 1] (esp+18) [ret address] -// [ret address] = real caller [ret address] (esp+14) [xxx] -// [xxx] [addr to jump to] (esp+10) [xxx] -// [xxx] [saved eax] [xxx] -// [xxx] [saved ebx] [xxx] -// [xxx] [saved ecx] [xxx] -// [xxx] [saved edx] [xxx] -__declspec(naked) -void SmartSidestepResolverThunk::SmartStub() { - __asm { - push eax // Space for the jump. - push eax // Save registers. - push ebx - push ecx - push edx - mov ebx, [esp + 0x18] // First parameter = SmartThunk. - mov edx, [esp + 0x14] // Get the return address. - mov eax, [ebx]SmartThunk.module_base - push edx - push eax - call SmartSidestepResolverThunk::IsInternalCall - add esp, 8 - - test eax, eax - lea edx, [ebx]SmartThunk.sidestep // The original function. - jz call_interceptor - - // Skip this call - mov ecx, [esp + 0x14] // Return address. - mov [esp + 0x18], ecx // Remove first parameter. - mov [esp + 0x10], edx - pop edx // Restore registers. - pop ecx - pop ebx - pop eax - ret 4 // Jump to original function. - - call_interceptor: - mov ecx, [ebx]SmartThunk.interceptor - mov [esp + 0x18], edx // Replace first parameter. - mov [esp + 0x10], ecx - pop edx // Restore registers. - pop ecx - pop ebx - pop eax - ret // Jump to original function. - } -} - -bool SmartSidestepResolverThunk::IsInternalCall(const void* base, - void* return_address) { - DCHECK_NT(base); - DCHECK_NT(return_address); - - base::win::PEImage pe(base); - if (pe.GetImageSectionFromAddr(return_address)) - return true; - return false; -} - -} // namespace sandbox diff --git a/chromium/sandbox/win/src/sidestep_resolver.h b/chromium/sandbox/win/src/sidestep_resolver.h deleted file mode 100644 index d06655031d6..00000000000 --- a/chromium/sandbox/win/src/sidestep_resolver.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SANDBOX_WIN_SRC_SIDESTEP_RESOLVER_H_ -#define SANDBOX_WIN_SRC_SIDESTEP_RESOLVER_H_ - -#include - -#include "sandbox/win/src/nt_internals.h" -#include "sandbox/win/src/resolver.h" - -namespace sandbox { - -// This is the concrete resolver used to perform sidestep interceptions. -class SidestepResolverThunk : public ResolverThunk { - public: - SidestepResolverThunk() {} - - SidestepResolverThunk(const SidestepResolverThunk&) = delete; - SidestepResolverThunk& operator=(const SidestepResolverThunk&) = delete; - - ~SidestepResolverThunk() override {} - - // Implementation of Resolver::Setup. - NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) override; - - // Implementation of Resolver::GetThunkSize. - size_t GetThunkSize() const override; -}; - -// This is the concrete resolver used to perform smart sidestep interceptions. -// This means basically a sidestep interception that skips the interceptor when -// the caller resides on the same dll being intercepted. It is intended as -// a helper only, because that determination is not infallible. -class SmartSidestepResolverThunk : public SidestepResolverThunk { - public: - SmartSidestepResolverThunk() {} - - SmartSidestepResolverThunk(const SmartSidestepResolverThunk&) = delete; - SmartSidestepResolverThunk& operator=(const SmartSidestepResolverThunk&) = - delete; - - ~SmartSidestepResolverThunk() override {} - - // Implementation of Resolver::Setup. - NTSTATUS Setup(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes, - size_t* storage_used) override; - - // Implementation of Resolver::GetThunkSize. - size_t GetThunkSize() const override; - - private: - // Performs the actual call to the interceptor if the conditions are correct - // (as determined by IsInternalCall). - static void SmartStub(); - - // Returns true if return_address is inside the module loaded at base. - static bool IsInternalCall(const void* base, void* return_address); -}; - -} // namespace sandbox - -#endif // SANDBOX_WIN_SRC_SIDESTEP_RESOLVER_H_ diff --git a/chromium/sandbox/win/src/signed_policy.cc b/chromium/sandbox/win/src/signed_policy.cc index f446fa668be..eb32734abec 100644 --- a/chromium/sandbox/win/src/signed_policy.cc +++ b/chromium/sandbox/win/src/signed_policy.cc @@ -11,6 +11,7 @@ #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_nt_util.h" #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/win_utils.h" @@ -49,20 +50,17 @@ NTSTATUS SignedPolicy::CreateSectionAction( 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()); + NTSTATUS status = GetNtExports()->CreateSection( + &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; diff --git a/chromium/sandbox/win/src/startup_information_helper.cc b/chromium/sandbox/win/src/startup_information_helper.cc index 7251205d8b3..d85c1ff65a3 100644 --- a/chromium/sandbox/win/src/startup_information_helper.cc +++ b/chromium/sandbox/win/src/startup_information_helper.cc @@ -68,7 +68,7 @@ void StartupInformationHelper::AddInheritedHandle(HANDLE handle) { } void StartupInformationHelper::SetAppContainer( - scoped_refptr container) { + scoped_refptr container) { // Only supported for Windows 8+. DCHECK(base::win::GetVersion() >= base::win::Version::WIN8); // LowPrivilegeAppContainer only supported for Windows 10+ diff --git a/chromium/sandbox/win/src/startup_information_helper.h b/chromium/sandbox/win/src/startup_information_helper.h index 87034d2ec0d..166f2fc4238 100644 --- a/chromium/sandbox/win/src/startup_information_helper.h +++ b/chromium/sandbox/win/src/startup_information_helper.h @@ -43,7 +43,7 @@ class StartupInformationHelper { // Create PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES and // PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY // based on |container|. |container| should be valid. - void SetAppContainer(scoped_refptr container); + void SetAppContainer(scoped_refptr container); // Creates PROC_THREAD_ATTRIBUTE_JOB_LIST with |job_handle|. Not valid before // Windows 10. void AddJobToAssociate(HANDLE job_handle); @@ -69,7 +69,7 @@ class StartupInformationHelper { int CountAttributes(); // Fields that are not passed into CreateProcessAsUserW(). - scoped_refptr app_container_; + scoped_refptr app_container_; bool restrict_child_process_creation_ = false; HANDLE stdout_handle_ = INVALID_HANDLE_VALUE; HANDLE stderr_handle_ = INVALID_HANDLE_VALUE; diff --git a/chromium/sandbox/win/src/target_interceptions.cc b/chromium/sandbox/win/src/target_interceptions.cc index 1b467814c6c..d44a2651369 100644 --- a/chromium/sandbox/win/src/target_interceptions.cc +++ b/chromium/sandbox/win/src/target_interceptions.cc @@ -11,8 +11,6 @@ namespace sandbox { -SANDBOX_INTERCEPT NtExports g_nt; - const char KERNEL32_DLL_NAME[] = "kernel32.dll"; enum SectionLoadState { @@ -60,14 +58,16 @@ TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection, // indicates Application Verifier is enabled and we should wait until // the next module is loaded. if (ansi_module_name && - (g_nt._strnicmp( + (GetNtExports()->_strnicmp( ansi_module_name, base::win::kApplicationVerifierDllName, - g_nt.strlen(base::win::kApplicationVerifierDllName) + 1) == 0)) + GetNtExports()->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)) { + (GetNtExports()->_strnicmp(ansi_module_name, KERNEL32_DLL_NAME, + sizeof(KERNEL32_DLL_NAME)) == 0)) { s_state = kAfterKernel32; } } __except (EXCEPTION_EXECUTE_HANDLER) { @@ -96,7 +96,7 @@ TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection, if (agent) { if (!agent->OnDllLoad(file_name, module_name, *base)) { // Interception agent is demanding to un-map the module. - g_nt.UnmapViewOfSection(process, *base); + GetNtExports()->UnmapViewOfSection(process, *base); *base = nullptr; ret = STATUS_UNSUCCESSFUL; } diff --git a/chromium/sandbox/win/src/target_process.cc b/chromium/sandbox/win/src/target_process.cc index d8fb75899d9..7a7aaf013c2 100644 --- a/chromium/sandbox/win/src/target_process.cc +++ b/chromium/sandbox/win/src/target_process.cc @@ -23,6 +23,7 @@ #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_nt_util.h" #include "sandbox/win/src/sandbox_types.h" #include "sandbox/win/src/security_capabilities.h" #include "sandbox/win/src/sharedmem_ipc_server.h" @@ -205,16 +206,17 @@ ResultCode TargetProcess::Create( } ResultCode TargetProcess::TransferVariable(const char* name, - void* address, + const void* address, size_t size) { if (!sandbox_process_info_.IsValid()) return SBOX_ERROR_UNEXPECTED_CALL; SIZE_T written; - if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), address, - address, size, &written)) + if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), + const_cast(address), address, size, + &written)) { return SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE; - + } if (written != size) return SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE; @@ -319,12 +321,9 @@ ResultCode TargetProcess::AssignLowBoxToken( PROCESS_ACCESS_TOKEN process_access_token = {}; process_access_token.token = token.Get(); - NtSetInformationProcess SetInformationProcess = nullptr; - ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess); - - NTSTATUS status = SetInformationProcess( + NTSTATUS status = GetNtExports()->SetInformationProcess( sandbox_process_info_.process_handle(), - static_cast(NtProcessInformationAccessToken), + static_cast(NtProcessInformationAccessToken), &process_access_token, sizeof(process_access_token)); if (!NT_SUCCESS(status)) { ::SetLastError(GetLastErrorFromNtStatus(status)); diff --git a/chromium/sandbox/win/src/target_process.h b/chromium/sandbox/win/src/target_process.h index 082d92e3ec2..060016e4543 100644 --- a/chromium/sandbox/win/src/target_process.h +++ b/chromium/sandbox/win/src/target_process.h @@ -90,7 +90,9 @@ class TargetProcess { 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); + ResultCode TransferVariable(const char* name, + const void* address, + size_t size); private: // Details of the target process. diff --git a/chromium/sandbox/win/src/unload_dll_test.cc b/chromium/sandbox/win/src/unload_dll_test.cc index fc85c0cd179..2067f777ebb 100644 --- a/chromium/sandbox/win/src/unload_dll_test.cc +++ b/chromium/sandbox/win/src/unload_dll_test.cc @@ -42,6 +42,17 @@ SBOX_TESTS_COMMAND int OpenExecutablePath(int argc, wchar_t** argv) { return SBOX_TEST_SUCCEEDED; } +std::unique_ptr BaselineAvicapRunner() { + auto runner = std::make_unique(); + runner->SetTestState(BEFORE_REVERT); + runner->SetTimeout(2000); + // Add a registry rule, because that ensures that the interception agent has + // more than one item in its internal table. + runner->AddRule(TargetPolicy::SUBSYS_FILES, TargetPolicy::FILES_ALLOW_QUERY, + L"\\??\\*.exe"); + return runner; +} + // Fails on Windows ARM64: https://crbug.com/905526 #if defined(ARCH_CPU_ARM64) #define MAYBE_BaselineAvicapDll DISABLED_BaselineAvicapDll @@ -49,49 +60,51 @@ SBOX_TESTS_COMMAND int OpenExecutablePath(int argc, wchar_t** argv) { #define MAYBE_BaselineAvicapDll BaselineAvicapDll #endif TEST(UnloadDllTest, MAYBE_BaselineAvicapDll) { - TestRunner runner; - runner.SetTestState(BEFORE_REVERT); - runner.SetTimeout(2000); - // Add a registry rule, because that ensures that the interception agent has - // more than one item in its internal table. - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_FILES, - TargetPolicy::FILES_ALLOW_QUERY, L"\\??\\*.exe")); - + auto runner = BaselineAvicapRunner(); // 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")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"UseOneDLL L avicap32.dll")); + runner = BaselineAvicapRunner(); + 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")); +std::unique_ptr UnloadAvicapNoPatchingRunner() { + auto runner = std::make_unique(); + runner->SetTestState(BEFORE_REVERT); + runner->SetTimeout(2000); + runner->GetPolicy()->AddDllToUnload(L"avicap32.dll"); + return runner; } -TEST(UnloadDllTest, UnloadAviCapDllWithPatching) { - TestRunner runner; - runner.SetTimeout(2000); - runner.SetTestState(BEFORE_REVERT); - sandbox::TargetPolicy* policy = runner.GetPolicy(); - policy->AddDllToUnload(L"avicap32.dll"); +TEST(UnloadDllTest, UnloadAviCapDllNoPatching) { + auto runner = UnloadAvicapNoPatchingRunner(); + EXPECT_EQ(SBOX_TEST_FAILED, runner->RunTest(L"UseOneDLL L avicap32.dll")); + runner = UnloadAvicapNoPatchingRunner(); + EXPECT_EQ(SBOX_TEST_FAILED, runner->RunTest(L"UseOneDLL B avicap32.dll")); +} +std::unique_ptr UnloadAvicapWithPatchingRunner() { + auto runner = std::make_unique(); + runner->SetTestState(BEFORE_REVERT); + runner->SetTimeout(2000); + runner->GetPolicy()->AddDllToUnload(L"avicap32.dll"); // 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_FILES, - TargetPolicy::FILES_ALLOW_QUERY, L"\\??\\*.exe")); - EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_FILES, - TargetPolicy::FILES_ALLOW_QUERY, L"\\??\\*.log")); + runner->AddRule(TargetPolicy::SUBSYS_FILES, TargetPolicy::FILES_ALLOW_QUERY, + L"\\??\\*.exe"); + runner->AddRule(TargetPolicy::SUBSYS_FILES, TargetPolicy::FILES_ALLOW_QUERY, + L"\\??\\*.log"); + return runner; +} - EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll")); +TEST(UnloadDllTest, UnloadAviCapDllWithPatching) { + auto runner = UnloadAvicapWithPatchingRunner(); + EXPECT_EQ(SBOX_TEST_FAILED, runner->RunTest(L"UseOneDLL L avicap32.dll")); - runner.SetTestState(AFTER_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"OpenExecutablePath")); + runner = UnloadAvicapWithPatchingRunner(); + runner->SetTestState(AFTER_REVERT); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner->RunTest(L"OpenExecutablePath")); } } // namespace sandbox diff --git a/chromium/sandbox/win/src/win_utils.cc b/chromium/sandbox/win/src/win_utils.cc index c8d0702652e..0769fd82376 100644 --- a/chromium/sandbox/win/src/win_utils.cc +++ b/chromium/sandbox/win/src/win_utils.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,8 @@ #include "base/numerics/safe_math.h" #include "base/strings/string_util.h" #include "base/win/pe_image.h" +#include "base/win/scoped_handle.h" +#include "base/win/win_util.h" #include "sandbox/win/src/internal_types.h" #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/sandbox_nt_util.h" @@ -147,6 +150,21 @@ void RemoveImpliedDevice(std::wstring* path) { *path = path->substr(kNTDotPrefixLen); } +bool QueryObjectInformation(HANDLE handle, + OBJECT_INFORMATION_CLASS info_class, + std::vector& buffer) { + NtQueryObjectFunction NtQueryObject = sandbox::GetNtExports()->QueryObject; + ULONG size = static_cast(buffer.size()); + __try { + return NT_SUCCESS( + NtQueryObject(handle, info_class, buffer.data(), size, &size)); + } __except (GetExceptionCode() == STATUS_INVALID_HANDLE + ? EXCEPTION_EXECUTE_HANDLER + : EXCEPTION_CONTINUE_SEARCH) { + return false; + } +} + } // namespace namespace sandbox { @@ -411,44 +429,39 @@ bool ConvertToLongPath(std::wstring* native_path, } 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 name_ptr; - if (size) { - name_ptr.reset(new BYTE[size]); - name = reinterpret_cast(name_ptr.get()); - - // Query the name information a second time to get the name of the - // object referenced by the handle. - status = NtQueryObject(handle, ObjectNameInformation, name, size, &size); - } - - if (STATUS_SUCCESS != status) + using LengthType = decltype(OBJECT_NAME_INFORMATION::ObjectName.Length); + std::vector buffer(sizeof(OBJECT_NAME_INFORMATION) + + std::numeric_limits::max()); + if (!QueryObjectInformation(handle, ObjectNameInformation, buffer)) return false; - + OBJECT_NAME_INFORMATION* name = + reinterpret_cast(buffer.data()); 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( + base::win::ScopedHandle 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) + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr)); + if (!file.IsValid()) + return false; + return GetPathFromHandle(file.Get(), nt_path); +} + +bool GetTypeNameFromHandle(HANDLE handle, std::wstring* type_name) { + // No typename is currently longer than 32 characters on Windows 11, so use an + // upper bound of 128 characters. + std::vector buffer(sizeof(OBJECT_TYPE_INFORMATION) + + 128 * sizeof(WCHAR)); + if (!QueryObjectInformation(handle, ObjectTypeInformation, buffer)) return false; - bool rv = GetPathFromHandle(file, nt_path); - ::CloseHandle(file); - return rv; + OBJECT_TYPE_INFORMATION* name = + reinterpret_cast(buffer.data()); + type_name->assign(name->Name.Buffer, + name->Name.Length / sizeof(name->Name.Buffer[0])); + return true; } bool WriteProtectedChildMemory(HANDLE child_process, @@ -504,20 +517,14 @@ bool CopyToChildMemory(HANDLE child, } DWORD GetLastErrorFromNtStatus(NTSTATUS status) { - RtlNtStatusToDosErrorFunction NtStatusToDosError = nullptr; - ResolveNTFunctionPtr("RtlNtStatusToDosError", &NtStatusToDosError); - return NtStatusToDosError(status); + return GetNtExports()->RtlNtStatusToDosError(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( + NTSTATUS status = GetNtExports()->QueryInformationProcess( process, ProcessBasicInformation, &process_basic_info, sizeof(process_basic_info), nullptr); if (STATUS_SUCCESS != status) @@ -545,6 +552,67 @@ void* GetProcessBaseAddress(HANDLE process) { return base_address; } +absl::optional GetCurrentProcessHandles() { + DWORD handle_count; + if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) + return absl::nullopt; + + // The system call will return only handles up to the buffer size so add a + // margin of error of an additional 1000 handles. + std::vector buffer((handle_count + 1000) * sizeof(uint32_t)); + DWORD return_length; + NTSTATUS status = GetNtExports()->QueryInformationProcess( + ::GetCurrentProcess(), ProcessHandleTable, buffer.data(), + static_cast(buffer.size()), &return_length); + + if (!NT_SUCCESS(status)) { + ::SetLastError(GetLastErrorFromNtStatus(status)); + return absl::nullopt; + } + DCHECK(buffer.size() >= return_length); + DCHECK((buffer.size() % sizeof(uint32_t)) == 0); + ProcessHandleMap handle_map; + const uint32_t* handle_values = reinterpret_cast(buffer.data()); + size_t count = return_length / sizeof(uint32_t); + for (size_t index = 0; index < count; ++index) { + HANDLE handle = base::win::Uint32ToHandle(handle_values[index]); + std::wstring type_name; + if (GetTypeNameFromHandle(handle, &type_name)) + handle_map[type_name].push_back(handle); + } + return handle_map; +} + +absl::optional GetCurrentProcessHandlesWin7() { + 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 absl::nullopt; + ProcessHandleMap handle_map; + + uint32_t handle_value = 0; + 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) { + handle_value += kHandleOffset; + HANDLE handle = base::win::Uint32ToHandle(handle_value); + std::wstring type_name; + if (!GetTypeNameFromHandle(handle, &type_name)) { + ++invalid_count; + continue; + } + + --handle_count; + handle_map[type_name].push_back(handle); + } + return handle_map; +} + } // namespace sandbox void ResolveNTFunctionPtr(const char* name, void* ptr) { diff --git a/chromium/sandbox/win/src/win_utils.h b/chromium/sandbox/win/src/win_utils.h index 6f54d200499..2acbe034b24 100644 --- a/chromium/sandbox/win/src/win_utils.h +++ b/chromium/sandbox/win/src/win_utils.h @@ -7,11 +7,14 @@ #include +#include #include #include +#include #include "base/cxx17_backports.h" #include "base/win/windows_types.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace sandbox { @@ -22,6 +25,9 @@ const size_t kNTPrefixLen = base::size(kNTPrefix) - 1; const wchar_t kNTDevicePrefix[] = L"\\Device\\"; const size_t kNTDevicePrefixLen = base::size(kNTDevicePrefix) - 1; +// List of handles mapped to their kernel object type name. +using ProcessHandleMap = std::map>; + // Basic implementation of a singleton which calls the destructor // when the exe is shutting down or the DLL is being unloaded. template @@ -69,9 +75,12 @@ bool SameObject(HANDLE handle, const wchar_t* full_path); 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. +// exist. Returns true if the translation was successful. bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path); +// Resolves a handle to its type name. Returns true if successful. +bool GetTypeNameFromHandle(HANDLE handle, std::wstring* type_name); + // 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. @@ -113,6 +122,18 @@ DWORD GetLastErrorFromNtStatus(NTSTATUS status); // the base address. This should only be called on new, suspended processes. void* GetProcessBaseAddress(HANDLE process); +// Returns a map of handles open in the current process. The call will only +// works on Windows 8+. The map is keyed by the kernel object type name. If +// querying the handles fails an empty optional value is returned. Note that +// unless all threads are suspended in the process the valid handles could +// change between the return of the list and when you use them. +absl::optional GetCurrentProcessHandles(); + +// Fallback function for GetCurrentProcessHandles. Should only be needed on +// Windows 7 which doesn't support the API to query all process handles. This +// uses a brute force method to get the process handles. +absl::optional GetCurrentProcessHandlesWin7(); + } // namespace sandbox // Resolves a function name in NTDLL to a function pointer. The second parameter diff --git a/chromium/sandbox/win/src/win_utils_unittest.cc b/chromium/sandbox/win/src/win_utils_unittest.cc index da82ec17949..4469da4c18c 100644 --- a/chromium/sandbox/win/src/win_utils_unittest.cc +++ b/chromium/sandbox/win/src/win_utils_unittest.cc @@ -8,14 +8,20 @@ #include +#include #include #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/numerics/safe_conversions.h" #include "base/path_service.h" +#include "base/rand_util.h" +#include "base/strings/string_util_win.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/nt_internals.h" #include "sandbox/win/tests/common/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -57,11 +63,58 @@ bool GetModuleList(HANDLE process, std::vector* result) { return false; } +std::wstring GetRandomName() { + return base::StringPrintf(L"chrome_%08X%08X", base::RandUint64(), + base::RandUint64()); +} + +void CompareHandlePath(const base::win::ScopedHandle& handle, + const std::wstring& expected_path) { + std::wstring path; + ASSERT_TRUE(GetPathFromHandle(handle.Get(), &path)); + EXPECT_TRUE(base::EqualsCaseInsensitiveASCII(path, expected_path)); +} + +void CompareHandleType(const base::win::ScopedHandle& handle, + const std::wstring& expected_type) { + std::wstring type_name; + ASSERT_TRUE(GetTypeNameFromHandle(handle.Get(), &type_name)); + EXPECT_TRUE(base::EqualsCaseInsensitiveASCII(type_name, expected_type)); +} + +void FindHandle(const ProcessHandleMap& handle_map, + const wchar_t* type_name, + const base::win::ScopedHandle& handle) { + ProcessHandleMap::const_iterator entry = handle_map.find(type_name); + ASSERT_NE(handle_map.end(), entry); + const std::vector& handles = entry->second; + EXPECT_NE(handles.cend(), + std::find(handles.cbegin(), handles.cend(), handle.Get())); +} + +void TestCurrentProcessHandles(absl::optional (*func)()) { + std::wstring random_name = GetRandomName(); + ASSERT_FALSE(random_name.empty()); + base::win::ScopedHandle event_handle( + ::CreateEvent(nullptr, FALSE, FALSE, random_name.c_str())); + ASSERT_TRUE(event_handle.IsValid()); + std::wstring pipe_name = L"\\\\.\\pipe\\" + random_name; + base::win::ScopedHandle pipe_handle(::CreateNamedPipe( + pipe_name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, nullptr)); + ASSERT_TRUE(pipe_handle.IsValid()); + + absl::optional handle_map = func(); + ASSERT_TRUE(handle_map); + EXPECT_LE(2U, handle_map->size()); + FindHandle(*handle_map, L"Event", event_handle); + FindHandle(*handle_map, L"File", pipe_handle); +} + } // 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]; @@ -84,7 +137,7 @@ TEST(WinUtils, IsReparsePoint) { IsReparsePoint(new_file)); // Replace the directory with a reparse point to %temp%. - HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS, + HANDLE dir = ::CreateFile(my_folder, FILE_WRITE_DATA, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); EXPECT_NE(INVALID_HANDLE_VALUE, dir); @@ -207,19 +260,20 @@ TEST(WinUtils, ConvertToLongPath) { 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"); + base::ScopedTempDir temp_dir; + base::FilePath base_path; + ASSERT_TRUE(base::PathService::Get(base::DIR_COMMON_APP_DATA, &base_path)); + ASSERT_TRUE(temp_dir.CreateUniqueTempDirUnderPath(base_path)); + base::FilePath temp_path = temp_dir.GetPath().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" + // WIN32 long path: "C:\ProgramData\%TEMP%\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" + // WIN32 short path: "C:\PROGRA~3\%TEMP%\TEST_C~1.exe" // Sanity check that we actually got a short path above! Small chance // it was disabled in the filesystem setup. @@ -228,13 +282,13 @@ TEST(WinUtils, ConvertToLongPath) { 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" + // NT short path: "\Device\HarddiskVolume4\PROGRA~3\%TEMP%\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" + // Expected result: "C:\ProgramData\%TEMP%\test_calc.exe" // Test 2: convert native short path to long: std::wstring drive_letter = temp_path.value().substr(0, 3); @@ -247,10 +301,40 @@ TEST(WinUtils, ConvertToLongPath) { 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" + // Expected result: "\Device\HarddiskVolumeX\ProgramData\%TEMP%\test_calc.exe" +} - // clean up - EXPECT_TRUE(base::DeleteFile(temp_path)); +TEST(WinUtils, GetPathAndTypeFromHandle) { + std::wstring invalid_handle; + EXPECT_FALSE(GetPathFromHandle(nullptr, &invalid_handle)); + EXPECT_TRUE(invalid_handle.empty()); + EXPECT_FALSE(GetTypeNameFromHandle(nullptr, &invalid_handle)); + EXPECT_TRUE(invalid_handle.empty()); + std::wstring random_name = GetRandomName(); + ASSERT_FALSE(random_name.empty()); + std::wstring event_name = L"Global\\" + random_name; + base::win::ScopedHandle event_handle( + ::CreateEvent(nullptr, FALSE, FALSE, event_name.c_str())); + ASSERT_TRUE(event_handle.IsValid()); + CompareHandlePath(event_handle, L"\\BaseNamedObjects\\" + random_name); + CompareHandleType(event_handle, L"Event"); + std::wstring pipe_name = L"\\\\.\\pipe\\" + random_name; + base::win::ScopedHandle pipe_handle(::CreateNamedPipe( + pipe_name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, nullptr)); + ASSERT_TRUE(pipe_handle.IsValid()); + CompareHandlePath(pipe_handle, L"\\Device\\NamedPipe\\" + random_name); + CompareHandleType(pipe_handle, L"File"); +} + +TEST(WinUtils, GetCurrentProcessHandles) { + if (base::win::GetVersion() < base::win::Version::WIN8) { + ASSERT_FALSE(GetCurrentProcessHandles()); + EXPECT_EQ(DWORD{ERROR_INVALID_PARAMETER}, ::GetLastError()); + } else { + TestCurrentProcessHandles(GetCurrentProcessHandles); + } + TestCurrentProcessHandles(GetCurrentProcessHandlesWin7); } } // namespace sandbox -- cgit v1.2.1