diff options
Diffstat (limited to 'test/syscalls/linux/32bit.cc')
-rw-r--r-- | test/syscalls/linux/32bit.cc | 248 |
1 files changed, 0 insertions, 248 deletions
diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc deleted file mode 100644 index 9883aef61..000000000 --- a/test/syscalls/linux/32bit.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <string.h> -#include <sys/mman.h> - -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "test/util/memory_util.h" -#include "test/util/platform_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -#ifndef __x86_64__ -#error "This test is x86-64 specific." -#endif - -namespace gvisor { -namespace testing { - -namespace { - -constexpr char kInt3 = '\xcc'; -constexpr char kInt80[2] = {'\xcd', '\x80'}; -constexpr char kSyscall[2] = {'\x0f', '\x05'}; -constexpr char kSysenter[2] = {'\x0f', '\x34'}; - -void ExitGroup32(const char instruction[2], int code) { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0)); - - // Fill with INT 3 in case we execute too far. - memset(m.ptr(), kInt3, m.len()); - - // Copy in the actual instruction. - memcpy(m.ptr(), instruction, 2); - - // We're playing *extremely* fast-and-loose with the various syscall ABIs - // here, which we can more-or-less get away with since exit_group doesn't - // return. - // - // SYSENTER expects the user stack in (%ebp) and arg6 in 0(%ebp). The kernel - // will unconditionally dereference %ebp for arg6, so we must pass a valid - // address or it will return EFAULT. - // - // SYSENTER also unconditionally returns to thread_info->sysenter_return which - // is ostensibly a stub in the 32-bit VDSO. But a 64-bit binary doesn't have - // the 32-bit VDSO mapped, so sysenter_return will simply be the value - // inherited from the most recent 32-bit ancestor, or NULL if there is none. - // As a result, return would not return from SYSENTER. - asm volatile( - "movl $252, %%eax\n" // exit_group - "movl %[code], %%ebx\n" // code - "movl %%edx, %%ebp\n" // SYSENTER: user stack (use IP as a valid addr) - "leaq -20(%%rsp), %%rsp\n" - "movl $0x2b, 16(%%rsp)\n" // SS = CPL3 data segment - "movl $0,12(%%rsp)\n" // ESP = nullptr (unused) - "movl $0, 8(%%rsp)\n" // EFLAGS - "movl $0x23, 4(%%rsp)\n" // CS = CPL3 32-bit code segment - "movl %%edx, 0(%%rsp)\n" // EIP - "iretl\n" - "int $3\n" - : - : [ code ] "m"(code), [ ip ] "d"(m.ptr()) - : "rax", "rbx", "rsp"); -} - -constexpr int kExitCode = 42; - -TEST(Syscall32Bit, Int80) { - switch (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - case PlatformSupport::Segfault: - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), - ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Ignored: - // Since the call is ignored, we'll hit the int3 trap. - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), - ::testing::KilledBySignal(SIGTRAP), ""); - break; - - case PlatformSupport::Allowed: - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42), - ""); - break; - } -} - -TEST(Syscall32Bit, Sysenter) { - if ((PlatformSupport32Bit() == PlatformSupport::Allowed || - PlatformSupport32Bit() == PlatformSupport::Ignored) && - GetCPUVendor() == CPUVendor::kAMD) { - // SYSENTER is an illegal instruction in compatibility mode on AMD. - EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), - ::testing::KilledBySignal(SIGILL), ""); - return; - } - - switch (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - - case PlatformSupport::Segfault: - EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), - ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Ignored: - // See above, except expected code is SIGSEGV. - EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), - ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Allowed: - EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), - ::testing::ExitedWithCode(42), ""); - break; - } -} - -TEST(Syscall32Bit, Syscall) { - if ((PlatformSupport32Bit() == PlatformSupport::Allowed || - PlatformSupport32Bit() == PlatformSupport::Ignored) && - GetCPUVendor() == CPUVendor::kIntel) { - // SYSCALL is an illegal instruction in compatibility mode on Intel. - EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), - ::testing::KilledBySignal(SIGILL), ""); - return; - } - - switch (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - - case PlatformSupport::Segfault: - EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), - ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Ignored: - // See above. - EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), - ::testing::KilledBySignal(SIGILL), ""); - break; - - case PlatformSupport::Allowed: - EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), - ::testing::ExitedWithCode(42), ""); - break; - } -} - -// Far call code called below. -// -// Input stack layout: -// -// %esp+12 lcall segment -// %esp+8 lcall address offset -// %esp+0 return address -// -// The lcall will enter compatibility mode and jump to the call address (the -// address of the lret). The lret will return to 64-bit mode at the retq, which -// will return to the external caller of this function. -// -// Since this enters compatibility mode, it must be mapped in a 32-bit region of -// address space and have a 32-bit stack pointer. -constexpr char kFarCall[] = { - '\x67', '\xff', '\x5c', '\x24', '\x08', // lcall *8(%esp) - '\xc3', // retq - '\xcb', // lret -}; - -void FarCall32() { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0)); - - // Fill with INT 3 in case we execute too far. - memset(m.ptr(), kInt3, m.len()); - - // 32-bit code. - memcpy(m.ptr(), kFarCall, sizeof(kFarCall)); - - // Use the end of the code page as its stack. - uintptr_t stack = m.endaddr(); - - uintptr_t lcall = m.addr(); - uintptr_t lret = m.addr() + sizeof(kFarCall) - 1; - - // N.B. We must save and restore RSP manually. GCC can do so automatically - // with an "rsp" clobber, but clang cannot. - asm volatile( - // Place the address of lret (%edx) and the 32-bit code segment (0x23) on - // the 32-bit stack for lcall. - "subl $0x8, %%ecx\n" - "movl $0x23, 4(%%ecx)\n" - "movl %%edx, 0(%%ecx)\n" - - // Save the current stack and switch to 32-bit stack. - "pushq %%rbp\n" - "movq %%rsp, %%rbp\n" - "movq %%rcx, %%rsp\n" - - // Run the lcall code. - "callq *%%rbx\n" - - // Restore the old stack. - "leaveq\n" - : "+c"(stack) - : "b"(lcall), "d"(lret)); -} - -TEST(Call32Bit, Disallowed) { - switch (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - - case PlatformSupport::Segfault: - EXPECT_EXIT(FarCall32(), ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Ignored: - ABSL_FALLTHROUGH_INTENDED; - case PlatformSupport::Allowed: - // Shouldn't crash. - FarCall32(); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor |