翻译《有关编程、重构及其他的终极问题?》——17.使用专门的函数清除专有数据

翻译《有关编程、重构及其他的终极问题?》——17.使用专门的函数清除专有数据

标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年02月15日


17.使用专门的函数清除专有数据

下面的带来摘自Apache HTTP Server项目。PVS-Studio诊断的错误描述为:V597 The compiler could delete the ‘memset’ function call, which is used to flush ‘x’ buffer. The RtlSecureZeroMemory() function should be used to erase the private data(译者注:大意是说编译器可能会把无效的memset函数调用清除,RtlSecureZeroMemory函数应该被用来清除专有数据)。

static void MD4Transform(
  apr_uint32_t state[4], const unsigned char block[64])
{
  apr_uint32_t a = state[0], b = state[1],
               c = state[2], d = state[3],
               x[APR_MD4_DIGESTSIZE];
  ....
  /* Zeroize sensitive information. */
  memset(x, 0, sizeof(x));
}

解释
在这段代码中,程序员使用了memset函数来清除专有数据。但其实这不是最好的方式,因为这些数据很可能实际上没有被清除。更近一步来说,这些数据是否被清除得依赖于编译器,以及其设置,还有一些运气吧(译者注:原文这里是Moon phase,即月相)。

让我们尝试使用编译器的视角来审视这段代码。它一直尽全力让你的代码尽可能的高效,所以它会使用一些列的优化手段。有种优化手段就是去除一些不会影响程序行为的函数调用,在这里我们就特别指的是从C/C++语言的视角。这里,上面代码中的memeset函数就是我们要说的例子。是的,这个函数会改变‘x’缓存,但这个缓存在那之后没有再被其他地方使用,这就意味着似乎memeset函数能或者应该被删除掉。

重要!我要告诉你的这种情况并非理论上的编译器行为,但它确确实实在真实的编译器上存在(译者注:C/C++编译器)。在此类的情况中,编译器会删除对memset函数的调用。你可以自己做一些使用去确认。对于有关这个问题的更多细节和例子,请参考下面的文章:
1. 安全,安全!但你真的测试过了吗?
2. 安全的清除专有数据
3. V597。编译器会删除那个被用来清空Foo缓存的memset函数调用。但其实这里应该使用RtlSecureZeroMemory函数才能真的清除专有数据。
4. 零或者遗忘——C语言中清空内存的警告(也可以查看这篇文章的讨论
5. MSC06-C:清醒认识编译器的优化项

这个难以捉摸的移除memset函数调用的错误是非常难跟踪的。当你使用debug方式时,你调式的代码一般都是没有被优化的,这样的话,memset函数调用还在那儿。你只能直接在汇编符号表中发现这个错误,因为这是在生成优化后的应用后生成的。

部分程序员相信这个错误是编译器本身应该处理的bug,并且认为编译器没有权利把类似memset这么重要的函数弃而不用。但其实并非如此,或多或少,这个函数一点也不比其他函数重要,所以编译器有权利优化相关的代码,毕竟类似代码很有可能是多余的。

正确的代码

memset_s(x, sizeof(x), 0, sizeof(x));

或者

RtlSecureZeroMemory(x, sizeof(x));

建议
你应该使用那些专用的内存清理函数,因为这样编译器就不会被允许因为优化的原因而取消这些函数的调用。

比如Visual Studio提供了RtlSecureZeroMemory函数;并且自从C11开始,你能使用memset_s函数了。如果有可能,你甚至可以创建自己的安全清楚函数——在网上有很多现成的例子。这里我列出一些:

版本No.1

errno_t memset_s(void *v, rsize_t smax, int c, rsize_t n) {
  if (v == NULL) return EINVAL;
  if (smax > RSIZE_MAX) return EINVAL;
  if (n > smax) return EINVAL;
  volatile unsigned char *p = v;
  while (smax-- && n--) {
    *p++ = c;
  }
  return 0;
}

版本No.2

void secure_zero(void *s, size_t n)
{
    volatile char *p = s;
    while (n--) *p++ = 0;
}

一些程序员甚至走的更远,他们实现了一些函数可以用伪随机值来填充数组,这样就可以在不同时间运行这些函数来更好防护时间段的攻击。你同样可以在互联网上找到这些函数的实现。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值