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