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