清除堆栈上的变量

时间:2016-05-26 21:40:56

标签: c secure-coding

代码段:

int secret_foo(void)
{
  int key = get_secret();
  /* use the key to do highly privileged  stuff */
  ....

  /* Need to clear the value of key on the stack before exit */
  key = 0;      
  /* Any half decent compiler would probably optimize out the statement above */
  /* How can I convince it not to do that? */

  return result;
}

我需要在key之前清除堆栈中变量return的值(如代码所示)。

如果您感到好奇,这是一个实际的客户要求(嵌入式域名)。

4 个答案:

答案 0 :(得分:8)

您可以使用volatile(强调我的):

  

通过volatile限定类型的左值表达式进行的每次访问(读取和写入)都被认为是出于优化目的的可观察副作用,并且严格根据抽象机器的规则进行评估(即在下一个序列点之前的某个时间完成所有写入。这意味着在单个执行线程中,相对于由易失性访问的序列点分隔的另一个可见副作用,无法优化或重新排序易失性访问。

 volatile int key = get_secret();

答案 1 :(得分:3)

volatile有时可能有些过分,因为它也会影响变量的所有其他用途。

使用memset_s(自C11起):http://en.cppreference.com/w/c/string/byte/memset

  如果此函数修改的对象在其生命周期的剩余时间内未被再次访问,则可以优化memset(在as-if规则下)。因此,此函数不能用于擦除内存(例如,用零填充存储密码的数组)。 memset_s禁止这种优化:保证执行内存写入。

int secret_foo(void)
{
  int key = get_secret();
  /* use the key to do highly privileged  stuff */
  ....

  memset_s(&key, sizeof(int), 0, sizeof(int));
  return result;
}

您可以在此处找到适用于各种平台/ C标准的其他解决方案:https://www.securecoding.cert.org/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations

附录:看看这篇文章Zeroing buffer is insufficient,它指出了其他问题(除了将实际缓冲区归零):

  

通过一些谨慎和合作编译器,我们可以将一个缓冲区归零 - 但这不是我们需要的。我们需要做的是零每个位置,可以存储敏感数据。请记住,我们首先在记忆中获得敏感信息的全部原因是我们可以使用它;并且这种使用几乎肯定会导致敏感数据被复制到堆栈和寄存器中。

您的key值可能已被编译器复制到另一个位置(如寄存器或临时堆栈/内存位置),并且您无法清除该位置。

答案 2 :(得分:1)

如果你使用动态分配,你可以控制擦除内存,而不受系统对堆栈的限制。

event_date

答案 3 :(得分:1)

一种解决方案是禁用您不希望优化的代码部分的编译器优化:

int secret_foo(void) {
     int key = get_secret();
     #pragma GCC push_options
     #pragma GCC optimize ("O0")

         key = 0;

     #pragma GCC pop_options
     return result;
}