【问题标题】:Mac OS X equivalent of SecureZeroMemory / RtlSecureZeroMemory?Mac OS X 相当于 SecureZeroMemory / RtlSecureZeroMemory?
【发布时间】:2012-10-29 06:17:30
【问题描述】:

是否有一个 Mac OS X 等效于 RtlSecureZeroMemory / SecureZeroMemory 的函数,它会将内存块归零,但编译器不会优化调用?

【问题讨论】:

  • FWIW,如果您使用的是 OpenSSL,它会提供 OPENSSL_cleanse 函数,该函数可以用伪随机数据安全地覆盖一块内存。
  • 考虑到 OpenSSL 确实(和 AFAIK,仍然)使用堆栈上未初始化的对象作为熵的来源,我会犹豫将其用于像这样的严重安全性。 .. 作者不了解 C 的安全使用。(当 Debian 修复他们的错误时,这个问题导致熵生成密钥的灾难性丢失。)
  • R,你说的并不完全正确。 Debian 删除了堆栈变量 的一些其他有效熵源的使用,剩下的只是进程 pid。这不是 OpenSSL 的错,使用未初始化的变量作为 附加 熵源非常好。

标签: objective-c c macos security memset


【解决方案1】:

编写你自己的函数:

void secure_zero(void *s, size_t n)
{
    volatile char *p = s;

    while (n--) *p++ = 0;
}

编辑: 对于 cmets 中的问题,为什么不 memset?如果数组对象不再被访问,memset 函数调用可以被编译器优化掉。

请注意,C11 添加了(可选)函数memset_s,标准保证函数调用不能被优化掉:

(C11, K.3.7.4.1p4) "[...] 与 memset 不同,对 memset_s 函数的任何调用都应严格按照 (5.1.2.3) 中描述的抽象机规则进行评估。那也就是说,对 memset_s 函数的任何调用都应假定 s 和 n 指示的内存将来可以访问,因此必须包含 c 指示的值。”

【讨论】:

  • 看起来非常复杂和臃肿。为什么不用 memset?
  • @Daij-Djan why not memset :因为如果不再访问数组对象,编译器可以优化调用。 看起来非常复杂和臃肿:这是编写memset 函数的经典方式,我觉得它优雅简洁。
  • 请注意,即使您将缓冲区归零,它的痕迹仍有可能以您无法安全或便携地消除的方式存在于堆栈中。我见过的处理此问题的最佳方法之一是在使用来自同一调用者的处理敏感数据的结果后,使用虚拟数据而不是敏感数据重新运行整个操作。虽然没有严格的要求,但这几乎肯定会导致从第一次运行时保留在堆栈中的任何内容都被破坏。我已经在加密哈希函数中看到了这种技术。
  • +1 我不知道memset_s。一旦可用,这听起来就是答案。
  • @DanielTrebbien:不要屏住呼吸。大多数人认为附件 K 是垃圾(它是基于 Microsoft 接口添加的,这些接口被发明用于破坏可移植 C 并宣传标准 C 函数“不安全”的信念)并且在大多数系统上实现它几乎没有兴趣。跨度>
【解决方案2】:

是否有与 RtlSecureZeroMemory / SecureZeroMemory 等效的 Mac OS X 函数,该函数将内存块归零,但编译器不会优化调用?

在 C 运行时的更新版本中,您拥有memset_s。它保证不会被优化掉。

#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
errno_t memset_s(void * restrict s, rsize_t smax, int c, rsize_t n)

OS X 还包括bzero 函数。但是bzero(3) man pages 不要声明它的会被优化器删除。

避免使用 volatile 限定符的技巧,因为它不可移植。它在 Windows 上按预期工作,但 GCC 人员将 volatile 解释为由硬件支持的 I/O 内存。所以你不应该使用volatile 来驯服优化器。


这是您可以使用的内联汇编实现。奇怪的是,ASM 语句和块上的__volatile__ 是可以的。它在 OS X 上运行良好(这是最初编写的地方)。

// g++ -Og -g3 -m64 wipe.cpp -o wipe.exe
// g++ -Og -g3 -m32 wipe.cpp -o wipe.exe    
// g++ -Os -g2 -S -m64 wipe.cpp -o wipe.exe.S
// g++ -Os -g2 -S -m32 wipe.cpp -o wipe.exe.S

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

int main(int argc, char* argv[])
{
    string s("Hello world");
    cout << "S: " << s << endl;

    char* ptr = &s[0];
    size_t size = s.length();

    if(ptr && size)
    {
        /* Needed because we can't just say to GCC, */
        /*   "give me a register that you choose".  */
        void* dummy;

        __asm__ __volatile__
        (
         "%=:\n\t"                /* generate a unique label for TOP */

#if (__WORDSIZE == 64)
         "subq $1, %2\n\t"        /* 0-based index */
#elif (__WORDSIZE == 32)
         "subl $1, %2\n\t"        /* 0-based index */
#elif (__WORDSIZE == 16)
         "subw $1, %2\n\t"        /* 0-based index */
#else
# error Unknown machine word size
#endif

         "lea (%1, %2), %0\n\t"   /* calcualte ptr[idx] */
         "movb $0, (%0)\n\t"      /* 0 -> ptr[size - 1] .. ptr[0] */
         "jnz %=b\n\t"            /* Back to TOP if non-zero */

         : "=&r" (dummy)
         :  "r" (ptr), "r" (size)
         : "0", "1", "2", "cc"
         );
    }

#if 0
    cout.setf(ios::hex, ios::basefield);
    cout.fill('0');

    for(size_t i = 0; i < s.length(); i++)
        cout << "0x" << setw(2) << ((int)s[i] & 0xff) << " ";

    cout << endl;
#endif

    cout << "S: " << s << endl;

    return 0;
}

【讨论】:

    猜你喜欢
    • 2014-03-27
    • 2014-01-04
    • 1970-01-01
    • 2010-09-20
    • 2012-12-13
    • 2015-07-29
    • 2011-06-02
    • 1970-01-01
    • 2014-06-25
    相关资源
    最近更新 更多