【问题标题】:Workaround for Spectre warning MSVC C5040Spectre 警告 MSVC C5040 的解决方法
【发布时间】:2018-05-17 20:22:23
【问题描述】:

MSVC 刚刚发布了一个更新,其中添加了一个关于编译器将注入一些代码以减轻(显然是一小部分)Spectre 的新警告:

https://blogs.msdn.microsoft.com/vcblog/2018/01/15/spectre-mitigations-in-msvc/

这是从他们的“有问题的”代码示例中得出的一个小 MCVE:

#include <stdio.h>

int main(int argc, char *argv) {
    unsigned char array1[1] = {0};
    int array1_length = 1;
    unsigned char array2[1] = {99};
    int untrusted_index = 0; /* in this MCVE, I trust it, compiler doesn't */
    for (; untrusted_index < array1_length; ++untrusted_index) {
        unsigned char value = array1[untrusted_index];
        unsigned char value2 = array2[value * 64];
        printf("Picked value %d\n", value2);
    }
    return 0;
}

"在上面的例子中,代码执行了数组边界检查,以确保 untrusted_index 小于 array1 的长度。这是确保程序不会读取超出数组边界所必需的。虽然这似乎是书面的,但它没有考虑 CPU 涉及推测执行的微架构行为。”

所以你现在得到一个警告:

警告 C5045:如果指定 /Qspectre 开关,编译器将插入 Spectre 缓解内存负载

这是告诉您此代码最终可能比您希望的要慢的方式(如果编译 /Qspectre),因为它会提供一些额外的保护。

由于您似乎无法将任何事情视为理所当然,因此我怀疑做出“只是让警告消失”的更改。例如,将untrusted_index &lt; array1_length 更改为untrusted_index != array1_length 似乎可以做到这一点,对于我在这里给出的 MCVE 代码的具体实例。但这是一个可行的补丁,还是他们的警告只是不完整的——在下一次更新中,它也会抱怨这个?

我知道我可以使用 /wd5040 或其他方式禁用警告。但我有兴趣确保如果代码是使用 /Qspectre 编译的,则不会出现减速,并且如果它没有使用 /Qspectre 编译,则不会出现警告。我不想在循环条件下到处触摸将&lt; 更改为!= 的文件——或者其他任何情况——如果这只是流失的话。

所以一个更大的问题是,如果有这种基本的合法解决方法模式,为什么没有提到它们?例如,我描述的情况是我控制索引的迭代,不必担心它来自“不受信任的来源”。然而我收到了警告,从&lt; 切换到!= 让它消失了。为什么?应该有吗?

【问题讨论】:

  • 您的示例在 Spectre 问题方面被破坏了:在访问数组之前没有边界检查。请坚持引用文章中提供的示例。
  • @YannDroneaud No. (a) 你在说什么,有一个边界检查。 (b) 如果我“坚持引用文章中的示例”,它就不是 MCVE,无法复制、粘贴和编译以显示警告,然后按给定的方式修改以显示它消失。
  • 无论如何,我相信您应该默认使用 /Qspectre,只是为了不必检查您的所有代码库以查找需要击败推测执行以防止 Spectre 变体 #1 漏洞的位置
  • @YannDroneaud 问题不是“/Qspectre 好”或“我应该使用 /Qspectre”。
  • 我想知道“代码执行数组边界检查以确保 untrusted_index 小于 array1 的长度”更像是“... 数组中? IMO,如果array1_length, untrusted_index 都是无符号整数,问题应该会消失。建议size_t

标签: c visual-c++ visual-studio-2017 spectre


【解决方案1】:

来自文章本身:

需要注意的是,分析存在局限性,这一点很重要 MSVC 和编译器通常可以在尝试识别时执行 变体 1 的实例。因此,不能保证所有 变体 1 的可能实例将在 /Qspectre 下检测。

您可能遇到过 /Qspectre 的当前实现无法通过设计缓解漏洞的情况之一。这是合理的,因为过度使用 LFENCE 可能会显着降低性能。减轻出现在代码中的每一个变体 1 实例的成本太高,无法完全在软件中完成(使用 LFENCE)。

在cmets中,有人问:

您能否向开发人员描述 MSVC 的限制是什么? 更多开发人员需要做些什么来保护自己免受“变体 1”的侵害?

文章作者回复:

我们不会详细介绍 MSVC 的实现。许多 人们和公司都依赖我们的工具,所以我们会在 对我们公开讨论的内容持谨慎态度。

因此,微软似乎不想确切披露变体 1 的哪些实例不会被 /Qspectre 缓解。

【讨论】:

    【解决方案2】:

    如果您不希望出现警告,只需使用#pragma warning(disable :5040),或在项目属性页中禁用它。

    请注意,您提供的对“untrusted_index != array1_length”的更改是不够的,因为它会使整个范围大于可滥用的大小。

    请记住,此诊断只是告诉您在启用幽灵缓解后编译器会做一些与以前不同的事情,它并没有真正告诉您必须对代码执行任何操作。 p>

    【讨论】:

    • “请注意,您对“untrusted_index != array1_length”的更改是不够的,因为它会使整个范围大于可滥用的大小。” 我的示例是特别是关于此类的 for 循环迭代,我在其中控制索引......如果启用了 Qspectre(或者如果没有启用警告),则不需要缓解代码。那么 != 到底是什么导致警告没有被触发。
    猜你喜欢
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    • 2021-02-18
    • 2018-11-26
    • 2013-10-27
    • 1970-01-01
    • 2013-04-29
    相关资源
    最近更新 更多