如何安全地将std::array清零?

5

我正在尝试在类析构函数中安全地将 std::array 归零。在这里,安全地 意味着我想确保编译器永远不会优化这个归零操作。下面是我的代码:

template<size_t SZ>
struct Buf {
    ~Buf() {
        auto ptr = static_cast<volatile uint8_t*>(buf_.data());
        std::fill(ptr, ptr + buf_.size(), 0);
    }

    std::array<uint8_t, SZ> buf_{};
};

这段代码会按预期工作吗?volatile关键字是否会在任何情况下防止编译器进行优化?


4
为什么你想要那个? - Raildex
2
为了安全地擦除内存,您需要使用一些特定于平台/编译器的函数,专门用于此目的。标准C ++无法保证它。您对哪个平台/编译器感兴趣? - user17732522
2
这个回答解决了你的问题吗?如何编写一个安全的密码类? - Suma
7
我认为C++标准并没有保证这一点。实际上,即使您确保清零某些RAM,操作系统仍有可能在交换存储器中留下一份副本。 - aschepler
4
易变类型转换确保计算机实际执行写操作并清零内存。它是否提供任何安全性取决于您的威胁没有明确说明,但我的猜测坚定地倾向于“不会”。 - n. m.
显示剩余17条评论
1个回答

4
C++标准本身并不做出明确的保证。它说:
[dcl.type.cv] 通过易失性glvalue进行访问的语义是实现定义的...
注5:volatile是给实现的一个提示,要避免涉及对象的激进优化,因为对象的值可能会通过一些无法被实现检测到的手段而改变。 此外,对于某些实现,volatile可能表示需要特殊的硬件指令才能访问对象。 有关详细语义,请参见[intro.execution]。 一般来说,volatile的语义旨在在C ++中与C中的语义相同。 ——注释结束
尽管C ++标准没有保证,但通过指向易失性的指针覆盖内存是某些加密库清除内存的一种方式--至少作为系统特定函数不可用时的备选方法。
附言:我建议使用const_cast,以避免意外地将类型强制转换为不同类型而不是限定符不同的相同类型。
auto ptr = const_cast<volatile std::uint8_t*>(buf_.data());

隐式转换也能起作用:

volatile std::uint8_t* ptr = buf_.data();

针对此目的的系统特定函数有以下几个:SecureZeroMemory(Windows)、explicit_bzero(一些BSD和glibc)。

C11标准提供了一个可选的函数memset_s,在C++中也可能可用,但不能保证一定可用。

提案P1315致力于将类似的函数引入C++标准。


请注意,安全擦除并非防止泄露敏感数据所需考虑的唯一因素。例如,操作系统可能会将内存交换到永久存储器中,除非明确禁止。在C++中没有标准方法可以这样做。POSIX中有mlock,Windows中有VirtualLock


memset_s 似乎不保证与此相关的任何内容。 - aschepler
2
@aschepler 为什么您不认为与memset不同,对memset_s函数的任何调用都必须严格按照(5.1.2.3)中描述的抽象机器规则进行评估。也就是说,对memset_s函数的任何调用都应该假定s和n所指示的内存在未来可能是可访问的,因此必须包含c所指示的值。是与此相关的保证? - eerorika
@eerorika,有趣的是cppref在这里的注释中指出,一种选项是使用带有volatile指针的std::fill:https://en.cppreference.com/w/cpp/string/byte/memset - Afshin
1
哎呀,我只看了 https://en.cppreference.com/w/c/string/byte/memset 中的函数描述,没有讲得很清楚。严格评估是在后面的注释中解释的。 - aschepler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接