github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/exceptions.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 <signal.h> 16 17 #include "gtest/gtest.h" 18 #include "test/util/logging.h" 19 #include "test/util/platform_util.h" 20 #include "test/util/signal_util.h" 21 #include "test/util/test_util.h" 22 23 namespace gvisor { 24 namespace testing { 25 26 #if defined(__x86_64__) 27 // Default value for the x87 FPU control word. See Intel SDM Vol 1, Ch 8.1.5 28 // "x87 FPU Control Word". 29 constexpr uint16_t kX87ControlWordDefault = 0x37f; 30 31 // Mask for the divide-by-zero exception. 32 constexpr uint16_t kX87ControlWordDiv0Mask = 1 << 2; 33 34 // Default value for the SSE control register (MXCSR). See Intel SDM Vol 1, Ch 35 // 11.6.4 "Initialization of SSE/SSE3 Extensions". 36 constexpr uint32_t kMXCSRDefault = 0x1f80; 37 38 // Mask for the divide-by-zero exception. 39 constexpr uint32_t kMXCSRDiv0Mask = 1 << 9; 40 41 // Flag for a pending divide-by-zero exception. 42 constexpr uint32_t kMXCSRDiv0Flag = 1 << 2; 43 44 void inline Halt() { asm("hlt\r\n"); } 45 46 void inline SetAlignmentCheck() { 47 asm("subq $128, %%rsp\r\n" // Avoid potential red zone clobber 48 "pushf\r\n" 49 "pop %%rax\r\n" 50 "or $0x40000, %%rax\r\n" 51 "push %%rax\r\n" 52 "popf\r\n" 53 "addq $128, %%rsp\r\n" 54 : 55 : 56 : "ax"); 57 } 58 59 void inline ClearAlignmentCheck() { 60 asm("subq $128, %%rsp\r\n" // Avoid potential red zone clobber 61 "pushf\r\n" 62 "pop %%rax\r\n" 63 "mov $0x40000, %%rbx\r\n" 64 "not %%rbx\r\n" 65 "and %%rbx, %%rax\r\n" 66 "push %%rax\r\n" 67 "popf\r\n" 68 "addq $128, %%rsp\r\n" 69 : 70 : 71 : "ax", "bx"); 72 } 73 74 void inline Int3Normal() { asm(".byte 0xcd, 0x03\r\n"); } 75 76 void inline Int3Compact() { asm(".byte 0xcc\r\n"); } 77 78 void InIOHelper(int width, int value) { 79 EXPECT_EXIT( 80 { 81 switch (width) { 82 case 1: 83 asm volatile("inb %%dx, %%al" ::"d"(value) : "%eax"); 84 break; 85 case 2: 86 asm volatile("inw %%dx, %%ax" ::"d"(value) : "%eax"); 87 break; 88 case 4: 89 asm volatile("inl %%dx, %%eax" ::"d"(value) : "%eax"); 90 break; 91 default: 92 FAIL() << "invalid input width, only 1, 2 or 4 is allowed"; 93 } 94 }, 95 ::testing::KilledBySignal(SIGSEGV), ""); 96 } 97 #elif defined(__aarch64__) 98 void inline Halt() { asm("hlt #0\r\n"); } 99 #endif 100 101 TEST(ExceptionTest, Halt) { 102 // In order to prevent the regular handler from messing with things (and 103 // perhaps refaulting until some other signal occurs), we reset the handler to 104 // the default action here and ensure that it dies correctly. 105 struct sigaction sa = {}; 106 sa.sa_handler = SIG_DFL; 107 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); 108 109 #if defined(__x86_64__) 110 EXPECT_EXIT(Halt(), ::testing::KilledBySignal(SIGSEGV), ""); 111 #elif defined(__aarch64__) 112 EXPECT_EXIT(Halt(), ::testing::KilledBySignal(SIGILL), ""); 113 #endif 114 } 115 116 #if defined(__x86_64__) 117 TEST(ExceptionTest, DivideByZero) { 118 // See above. 119 struct sigaction sa = {}; 120 sa.sa_handler = SIG_DFL; 121 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); 122 123 EXPECT_EXIT( 124 { 125 uint32_t remainder; 126 uint32_t quotient; 127 uint32_t divisor = 0; 128 uint64_t value = 1; 129 asm("divl 0(%2)\r\n" 130 : "=d"(remainder), "=a"(quotient) 131 : "r"(&divisor), "d"(value >> 32), "a"(value)); 132 TEST_CHECK(quotient > 0); // Force dependency. 133 }, 134 ::testing::KilledBySignal(SIGFPE), ""); 135 } 136 137 // By default, x87 exceptions are masked and simply return a default value. 138 TEST(ExceptionTest, X87DivideByZeroMasked) { 139 int32_t quotient; 140 int32_t value = 1; 141 int32_t divisor = 0; 142 asm("fildl %[value]\r\n" 143 "fidivl %[divisor]\r\n" 144 "fistpl %[quotient]\r\n" 145 : [ quotient ] "=m"(quotient) 146 : [ value ] "m"(value), [ divisor ] "m"(divisor)); 147 148 EXPECT_EQ(quotient, INT32_MIN); 149 } 150 151 // When unmasked, division by zero raises SIGFPE. 152 TEST(ExceptionTest, X87DivideByZeroUnmasked) { 153 // See above. 154 struct sigaction sa = {}; 155 sa.sa_handler = SIG_DFL; 156 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); 157 158 EXPECT_EXIT( 159 { 160 // Clear the divide by zero exception mask. 161 constexpr uint16_t kControlWord = 162 kX87ControlWordDefault & ~kX87ControlWordDiv0Mask; 163 164 int32_t quotient; 165 int32_t value = 1; 166 int32_t divisor = 0; 167 asm volatile( 168 "fldcw %[cw]\r\n" 169 "fildl %[value]\r\n" 170 "fidivl %[divisor]\r\n" 171 "fistpl %[quotient]\r\n" 172 : [ quotient ] "=m"(quotient) 173 : [ cw ] "m"(kControlWord), [ value ] "m"(value), 174 [ divisor ] "m"(divisor)); 175 }, 176 ::testing::KilledBySignal(SIGFPE), ""); 177 } 178 179 // Pending exceptions in the x87 status register are not clobbered by syscalls. 180 TEST(ExceptionTest, X87StatusClobber) { 181 // See above. 182 struct sigaction sa = {}; 183 sa.sa_handler = SIG_DFL; 184 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); 185 186 EXPECT_EXIT( 187 { 188 // Clear the divide by zero exception mask. 189 constexpr uint16_t kControlWord = 190 kX87ControlWordDefault & ~kX87ControlWordDiv0Mask; 191 192 int32_t quotient; 193 int32_t value = 1; 194 int32_t divisor = 0; 195 asm volatile( 196 "fildl %[value]\r\n" 197 "fidivl %[divisor]\r\n" 198 // Exception is masked, so it does not occur here. 199 "fistpl %[quotient]\r\n" 200 201 // SYS_getpid placed in rax by constraint. 202 "syscall\r\n" 203 204 // Unmask exception. The syscall didn't clobber the pending 205 // exception, so now it can be raised. 206 // 207 // N.B. "a floating-point exception will be generated upon execution 208 // of the *next* floating-point instruction". 209 "fldcw %[cw]\r\n" 210 "fwait\r\n" 211 : [ quotient ] "=m"(quotient) 212 : [ value ] "m"(value), [ divisor ] "m"(divisor), "a"(SYS_getpid), 213 [ cw ] "m"(kControlWord) 214 : "rcx", "r11"); 215 }, 216 ::testing::KilledBySignal(SIGFPE), ""); 217 } 218 219 // By default, SSE exceptions are masked and simply return a default value. 220 TEST(ExceptionTest, SSEDivideByZeroMasked) { 221 uint32_t status; 222 int32_t quotient; 223 int32_t value = 1; 224 int32_t divisor = 0; 225 asm("cvtsi2ssl %[value], %%xmm0\r\n" 226 "cvtsi2ssl %[divisor], %%xmm1\r\n" 227 "divss %%xmm1, %%xmm0\r\n" 228 "cvtss2sil %%xmm0, %[quotient]\r\n" 229 : [ quotient ] "=r"(quotient), [ status ] "=r"(status) 230 : [ value ] "r"(value), [ divisor ] "r"(divisor) 231 : "xmm0", "xmm1"); 232 233 EXPECT_EQ(quotient, INT32_MIN); 234 } 235 236 // When unmasked, division by zero raises SIGFPE. 237 TEST(ExceptionTest, SSEDivideByZeroUnmasked) { 238 // See above. 239 struct sigaction sa = {}; 240 sa.sa_handler = SIG_DFL; 241 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); 242 243 EXPECT_EXIT( 244 { 245 // Clear the divide by zero exception mask. 246 constexpr uint32_t kMXCSR = kMXCSRDefault & ~kMXCSRDiv0Mask; 247 248 int32_t quotient; 249 int32_t value = 1; 250 int32_t divisor = 0; 251 asm volatile( 252 "ldmxcsr %[mxcsr]\r\n" 253 "cvtsi2ssl %[value], %%xmm0\r\n" 254 "cvtsi2ssl %[divisor], %%xmm1\r\n" 255 "divss %%xmm1, %%xmm0\r\n" 256 "cvtss2sil %%xmm0, %[quotient]\r\n" 257 : [ quotient ] "=r"(quotient) 258 : [ mxcsr ] "m"(kMXCSR), [ value ] "r"(value), 259 [ divisor ] "r"(divisor) 260 : "xmm0", "xmm1"); 261 }, 262 ::testing::KilledBySignal(SIGFPE), ""); 263 } 264 265 // Pending exceptions in the SSE status register are not clobbered by syscalls. 266 TEST(ExceptionTest, SSEStatusClobber) { 267 uint32_t mxcsr; 268 int32_t quotient; 269 int32_t value = 1; 270 int32_t divisor = 0; 271 asm("cvtsi2ssl %[value], %%xmm0\r\n" 272 "cvtsi2ssl %[divisor], %%xmm1\r\n" 273 "divss %%xmm1, %%xmm0\r\n" 274 // Exception is masked, so it does not occur here. 275 "cvtss2sil %%xmm0, %[quotient]\r\n" 276 277 // SYS_getpid placed in rax by constraint. 278 "syscall\r\n" 279 280 // Intel SDM Vol 1, Ch 10.2.3.1 "SIMD Floating-Point Mask and Flag Bits": 281 // "If LDMXCSR or FXRSTOR clears a mask bit and sets the corresponding 282 // exception flag bit, a SIMD floating-point exception will not be 283 // generated as a result of this change. The unmasked exception will be 284 // generated only upon the execution of the next SSE/SSE2/SSE3 instruction 285 // that detects the unmasked exception condition." 286 // 287 // Though ambiguous, empirical evidence indicates that this means that 288 // exception flags set in the status register will never cause an 289 // exception to be raised; only a new exception condition will do so. 290 // 291 // Thus here we just check for the flag itself rather than trying to raise 292 // the exception. 293 "stmxcsr %[mxcsr]\r\n" 294 : [ quotient ] "=r"(quotient), [ mxcsr ] "+m"(mxcsr) 295 : [ value ] "r"(value), [ divisor ] "r"(divisor), "a"(SYS_getpid) 296 : "xmm0", "xmm1", "rcx", "r11"); 297 298 EXPECT_TRUE(mxcsr & kMXCSRDiv0Flag); 299 } 300 301 TEST(ExceptionTest, IOAccessFault) { 302 // See above. 303 struct sigaction sa = {}; 304 sa.sa_handler = SIG_DFL; 305 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); 306 307 InIOHelper(1, 0x0); 308 InIOHelper(2, 0x7); 309 InIOHelper(4, 0x6); 310 InIOHelper(1, 0xffff); 311 InIOHelper(2, 0xffff); 312 InIOHelper(4, 0xfffd); 313 } 314 315 TEST(ExceptionTest, Alignment) { 316 SetAlignmentCheck(); 317 ClearAlignmentCheck(); 318 } 319 320 TEST(ExceptionTest, AlignmentHalt) { 321 // See above. 322 struct sigaction sa = {}; 323 sa.sa_handler = SIG_DFL; 324 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); 325 326 // Reported upstream. We need to ensure that bad flags are cleared even in 327 // fault paths. Set the alignment flag and then generate an exception. 328 EXPECT_EXIT( 329 { 330 SetAlignmentCheck(); 331 Halt(); 332 }, 333 ::testing::KilledBySignal(SIGSEGV), ""); 334 } 335 336 TEST(ExceptionTest, AlignmentCheck) { 337 SKIP_IF(PlatformSupportAlignmentCheck() != PlatformSupport::Allowed); 338 339 // See above. 340 struct sigaction sa = {}; 341 sa.sa_handler = SIG_DFL; 342 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGBUS, sa)); 343 344 EXPECT_EXIT( 345 { 346 char array[16]; 347 SetAlignmentCheck(); 348 for (int i = 0; i < 8; i++) { 349 // At least 7/8 offsets will be unaligned here. 350 uint64_t* ptr = reinterpret_cast<uint64_t*>(&array[i]); 351 asm("mov %0, 0(%0)\r\n" : : "r"(ptr) : "ax"); 352 } 353 }, 354 ::testing::KilledBySignal(SIGBUS), ""); 355 } 356 357 TEST(ExceptionTest, Int3Normal) { 358 // See above. 359 struct sigaction sa = {}; 360 sa.sa_handler = SIG_DFL; 361 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGTRAP, sa)); 362 363 EXPECT_EXIT(Int3Normal(), ::testing::KilledBySignal(SIGTRAP), ""); 364 } 365 366 TEST(ExceptionTest, Int3Compact) { 367 // See above. 368 struct sigaction sa = {}; 369 sa.sa_handler = SIG_DFL; 370 auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGTRAP, sa)); 371 372 EXPECT_EXIT(Int3Compact(), ::testing::KilledBySignal(SIGTRAP), ""); 373 } 374 #endif 375 376 } // namespace testing 377 } // namespace gvisor