gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/32bit.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <string.h>
    16  #include <sys/mman.h>
    17  
    18  #include "gtest/gtest.h"
    19  #include "absl/base/macros.h"
    20  #include "test/util/memory_util.h"
    21  #include "test/util/platform_util.h"
    22  #include "test/util/posix_error.h"
    23  #include "test/util/test_util.h"
    24  
    25  namespace gvisor {
    26  namespace testing {
    27  
    28  namespace {
    29  
    30  #ifdef __x86_64__
    31  
    32  constexpr char kInt3 = '\xcc';
    33  constexpr char kInt80[2] = {'\xcd', '\x80'};
    34  constexpr char kSyscall[2] = {'\x0f', '\x05'};
    35  constexpr char kSysenter[2] = {'\x0f', '\x34'};
    36  
    37  void ExitGroup32(const char instruction[2], int code) {
    38    const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
    39        Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC,
    40             MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0));
    41  
    42    // Fill with INT 3 in case we execute too far.
    43    memset(m.ptr(), kInt3, m.len());
    44  
    45    // Copy in the actual instruction.
    46    memcpy(m.ptr(), instruction, 2);
    47  
    48    // We're playing *extremely* fast-and-loose with the various syscall ABIs
    49    // here, which we can more-or-less get away with since exit_group doesn't
    50    // return.
    51    //
    52    // SYSENTER expects the user stack in (%ebp) and arg6 in 0(%ebp). The kernel
    53    // will unconditionally dereference %ebp for arg6, so we must pass a valid
    54    // address or it will return EFAULT.
    55    //
    56    // SYSENTER also unconditionally returns to thread_info->sysenter_return which
    57    // is ostensibly a stub in the 32-bit VDSO. But a 64-bit binary doesn't have
    58    // the 32-bit VDSO mapped, so sysenter_return will simply be the value
    59    // inherited from the most recent 32-bit ancestor, or NULL if there is none.
    60    // As a result, return would not return from SYSENTER.
    61    asm volatile(
    62        "movl $252, %%eax\n"     // exit_group
    63        "movl %[code], %%ebx\n"  // code
    64        "movl %%edx, %%ebp\n"    // SYSENTER: user stack (use IP as a valid addr)
    65        "leaq -20(%%rsp), %%rsp\n"
    66        "movl $0x2b, 16(%%rsp)\n"  // SS = CPL3 data segment
    67        "movl $0,12(%%rsp)\n"      // ESP = nullptr (unused)
    68        "movl $0, 8(%%rsp)\n"      // EFLAGS
    69        "movl $0x23, 4(%%rsp)\n"   // CS = CPL3 32-bit code segment
    70        "movl %%edx, 0(%%rsp)\n"   // EIP
    71        "iretl\n"
    72        "int $3\n"
    73        :
    74        : [code] "m"(code), [ip] "d"(m.ptr())
    75        : "rax", "rbx");
    76  }
    77  
    78  constexpr int kExitCode = 42;
    79  
    80  TEST(Syscall32Bit, Int80) {
    81    switch (PlatformSupport32Bit()) {
    82      case PlatformSupport::NotSupported:
    83        break;
    84      case PlatformSupport::Segfault:
    85        EXPECT_EXIT(ExitGroup32(kInt80, kExitCode),
    86                    ::testing::KilledBySignal(SIGSEGV), "");
    87        break;
    88  
    89      case PlatformSupport::Ignored:
    90        // Since the call is ignored, we'll hit the int3 trap.
    91        EXPECT_EXIT(ExitGroup32(kInt80, kExitCode),
    92                    ::testing::KilledBySignal(SIGTRAP), "");
    93        break;
    94  
    95      case PlatformSupport::Allowed:
    96        EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42),
    97                    "");
    98        break;
    99    }
   100  }
   101  
   102  TEST(Syscall32Bit, Sysenter) {
   103    if ((PlatformSupport32Bit() == PlatformSupport::Allowed ||
   104         PlatformSupport32Bit() == PlatformSupport::Ignored) &&
   105        GetCPUVendor() == CPUVendor::kAMD) {
   106      // SYSENTER is an illegal instruction in compatibility mode on AMD.
   107      EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
   108                  ::testing::KilledBySignal(SIGILL), "");
   109      return;
   110    }
   111  
   112    switch (PlatformSupport32Bit()) {
   113      case PlatformSupport::NotSupported:
   114        break;
   115  
   116      case PlatformSupport::Segfault:
   117        EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
   118                    ::testing::KilledBySignal(SIGSEGV), "");
   119        break;
   120  
   121      case PlatformSupport::Ignored:
   122        // See above, except expected code is SIGSEGV.
   123        EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
   124                    ::testing::KilledBySignal(SIGSEGV), "");
   125        break;
   126  
   127      case PlatformSupport::Allowed:
   128        EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
   129                    ::testing::ExitedWithCode(42), "");
   130        break;
   131    }
   132  }
   133  
   134  class KilledByOneOfSignals {
   135   public:
   136    KilledByOneOfSignals(int signum1, int signum2)
   137        : signum1_(signum1), signum2_(signum2) {}
   138    bool operator()(int exit_status) const {
   139      if (!WIFSIGNALED(exit_status)) return false;
   140      int sig = WTERMSIG(exit_status);
   141      return sig == signum1_ || sig == signum2_;
   142    }
   143  
   144   private:
   145    const int signum1_, signum2_;
   146  };
   147  
   148  TEST(Syscall32Bit, Syscall) {
   149    if ((PlatformSupport32Bit() == PlatformSupport::Allowed ||
   150         PlatformSupport32Bit() == PlatformSupport::Ignored) &&
   151        GetCPUVendor() == CPUVendor::kIntel) {
   152      // SYSCALL is an illegal instruction in compatibility mode on Intel.
   153      EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
   154                  ::testing::KilledBySignal(SIGILL), "");
   155      return;
   156    }
   157  
   158    switch (PlatformSupport32Bit()) {
   159      case PlatformSupport::NotSupported:
   160        break;
   161  
   162      case PlatformSupport::Segfault:
   163        EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
   164                    ::testing::KilledBySignal(SIGSEGV), "");
   165        break;
   166  
   167      case PlatformSupport::Ignored:
   168        // NOTE(b/241819530): SIGSEGV was returned due to a kernel bug that has
   169        // been fixed recently. Let's continue accept SIGSEGV while bad kernels
   170        // are running in prod.
   171        EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
   172                    KilledByOneOfSignals(SIGTRAP, SIGSEGV), "");
   173        break;
   174  
   175      case PlatformSupport::Allowed:
   176        EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
   177                    ::testing::ExitedWithCode(42), "");
   178        break;
   179    }
   180  }
   181  
   182  // Far call code called below.
   183  //
   184  // Input stack layout:
   185  //
   186  // %esp+12 lcall segment
   187  // %esp+8  lcall address offset
   188  // %esp+0  return address
   189  //
   190  // The lcall will enter compatibility mode and jump to the call address (the
   191  // address of the lret). The lret will return to 64-bit mode at the retq, which
   192  // will return to the external caller of this function.
   193  //
   194  // Since this enters compatibility mode, it must be mapped in a 32-bit region of
   195  // address space and have a 32-bit stack pointer.
   196  constexpr char kFarCall[] = {
   197      '\x67', '\xff', '\x5c', '\x24', '\x08',  // lcall *8(%esp)
   198      '\xc3',                                  // retq
   199      '\xcb',                                  // lret
   200  };
   201  
   202  void FarCall32() {
   203    const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
   204        Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE | PROT_EXEC,
   205             MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0));
   206  
   207    // Fill with INT 3 in case we execute too far.
   208    memset(m.ptr(), kInt3, m.len());
   209  
   210    // 32-bit code.
   211    memcpy(m.ptr(), kFarCall, sizeof(kFarCall));
   212  
   213    // Use the end of the code page as its stack.
   214    uintptr_t stack = m.endaddr();
   215  
   216    uintptr_t lcall = m.addr();
   217    uintptr_t lret = m.addr() + sizeof(kFarCall) - 1;
   218  
   219    // N.B. We must save and restore RSP manually. GCC can do so automatically
   220    // with an "rsp" clobber, but clang cannot.
   221    asm volatile(
   222        // Place the address of lret (%edx) and the 32-bit code segment (0x23) on
   223        // the 32-bit stack for lcall.
   224        "subl $0x8, %%ecx\n"
   225        "movl $0x23, 4(%%ecx)\n"
   226        "movl %%edx, 0(%%ecx)\n"
   227  
   228        // Save the current stack and switch to 32-bit stack.
   229        "pushq %%rbp\n"
   230        "movq %%rsp, %%rbp\n"
   231        "movq %%rcx, %%rsp\n"
   232  
   233        // Run the lcall code.
   234        "callq *%%rbx\n"
   235  
   236        // Restore the old stack.
   237        "leaveq\n"
   238        : "+c"(stack)
   239        : "b"(lcall), "d"(lret));
   240  }
   241  
   242  TEST(Call32Bit, Disallowed) {
   243    switch (PlatformSupport32Bit()) {
   244      case PlatformSupport::NotSupported:
   245        break;
   246  
   247      case PlatformSupport::Segfault:
   248        EXPECT_EXIT(FarCall32(), ::testing::KilledBySignal(SIGSEGV), "");
   249        break;
   250  
   251      case PlatformSupport::Ignored:
   252        ABSL_FALLTHROUGH_INTENDED;
   253      case PlatformSupport::Allowed:
   254        // Shouldn't crash.
   255        FarCall32();
   256    }
   257  }
   258  
   259  #endif
   260  
   261  }  // namespace
   262  
   263  }  // namespace testing
   264  }  // namespace gvisor