首页 > 解决方案 > 英特尔 CPU 上的推测执行是否可能导致 EXC_BAD_INSTRUCTION (SIGILL)

问题描述

我有一个假设,即 Intel Nehalem(第 1 代)上的推测性执行会导致崩溃。有可能还是我完全错了?如果这是可能的,我能做些什么来防止这种情况发生?也许只对一个函数或整个翻译单元禁用推测执行?

对于有问题代码的 cpp 文件的编译,clang 与标志一起使用 -mavx2 -mxsave 所有其他没有这些标志编译的文件。此代码适用于任何可用的当代 Mac 书和 Windows 笔记本电脑/台式机。

Testers mac book 有 Intel(R) Core(TM) i5 CPU 760。这个 CPU 不支持 AVX2 指令集。有一个代码检查是否支持 AVX2,如果不支持则不执行。我无法直接访问此设备进行调试,以了解导致崩溃的确切代码。但现在我有两个假设:

我已经替换/“修复”了检查代码作为主要假设,但测试人员仍然报告崩溃。所以我不确定那isAvx2Supported是假的。

检查是否支持 AVX2 的代码

void cpuid(int info[4], int InfoType) noexcept
{
#ifdef _WIN32
  __cpuidex(info, InfoType, 0);
#else
  __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]);
#endif
}

bool check_xcr0_ymm() noexcept
{
  uint32_t xcr0;
#if defined(_MSC_VER)
  xcr0 = (uint32_t)_xgetbv(0);
#else
  __asm__ __volatile__("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx");
#endif
  // checking if xmm and ymm state are enabled in XCR0
  return (xcr0 & 6) == 6;
}

bool check_4th_gen_intel_core_features() noexcept
{
  // see original article
  // https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
  int cpuInfo[4] = {};

  cpuid(cpuInfo, 1);
  //    CPUID.(EAX=01H, ECX=0H):ECX.FMA[bit 12]==1
  // && CPUID.(EAX=01H, ECX=0H):ECX.MOVBE[bit 22]==1
  // && CPUID.(EAX=01H, ECX=0H):ECX.OSXSAVE[bit 27]==1
  constexpr uint32_t fma_movbe_osxsave_mask = ((1 << 12) | (1 << 22) | (1 << 27));
  if((cpuInfo[2] & fma_movbe_osxsave_mask) != fma_movbe_osxsave_mask)
    return false;

  if(!check_xcr0_ymm())
    return false;

  cpuid(cpuInfo, 7);
  //    CPUID.(EAX=07H, ECX=0H):EBX.AVX2[bit 5]==1
  // && CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]==1
  // && CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]==1
  constexpr uint32_t avx2_bmi12_mask = (1 << 5) | (1 << 3) | (1 << 8);
  if((cpuInfo[1] & avx2_bmi12_mask) != avx2_bmi12_mask)
    return false;

  cpuid(cpuInfo, 0x80000001);
  // CPUID.(EAX=80000001H):ECX.LZCNT[bit 5]==1
  if((cpuInfo[2] & (1 << 5)) == 0)
    return false;

  return true;
}

const auto isAvx2Supported = check_4th_gen_intel_core_features();

使用 AVX2 的实际代码

int findCharFast(const char* data, size_t dataSize, char c, unsigned int& offset)
{
  if(isAvx2Supported)
  {
    const auto mask = _mm256_set1_epi8(c);
    auto it = data + offset;
    for(const auto end = data + dataSize - 31; it < end; it += 32)
    {
      if(const auto result = _mm256_movemask_epi8(_mm256_cmpeq_epi8(mask, _mm256_loadu_si256(reinterpret_cast<const __m256i*>(it)))))
      {
        return it - data + get_first_bit_set(result);
      }
    }
    offset = it - data;
  }
  return -1;
}

崩溃报告说

崩溃的线程:43 队列(0x60400005b270)[16]

异常类型:EXC_BAD_INSTRUCTION (SIGILL) 异常代码:0x0000000000000001、0x0000000000000000

线程 43 崩溃:: Queue(0x60400005b270)[16] 0 0x000000010d54b06b findCharFast(char const*, unsigned long, char, unsigned int&) + 91

Thread 43 crashed with X86 Thread State (64-bit): rax: 0x00000000ffffffff rbx: 0x0000000000000000 rcx: 0x000070000982bbd0 rdx: 0x0000000000000000 rdi: 0x00007f9ac044fa0b rsi: 0x000000000000000d rbp: 0x000070000982bc00 rsp: 0x000070000982bbc8 r8: 0x0000000000000001 r9: 0x0000000000000001 r10: 0x000060c000334030 r11: 0xfffffffffffc0fde r12 : 0x0000000000000001 r13: 0x0000600000016fb0 r14: 0x00007f9ac044fa0b r15: 0x00007f9ac044fa18 rip: 0x000000010d54b06b rfl: 0x0000000000010246 cr2: 0x000000010d529cf0

标签: speculative-execution

解决方案


AVX2 支持检测代码中存在错误。描述如何做到这一点的英特尔文章基本上是错误的。在调用xgetbv实现之前必须检查ECX.XSAVE[bit 26]==1. 仅检查 OSXSAVE 标志是不够的。


推荐阅读