2

代码片段:

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我需要在 ing之前从堆栈中清除变量的值return(如代码所示)。

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

4

4 回答 4

8

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

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

 volatile int key = get_secret();
于 2016-05-26T21:41:58.587 回答
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

附录:看看这篇文章归零缓冲区不足指出了其他问题(除了归零实际缓冲区):

稍加小心和协作编译器,我们可以缓冲区归零——但这不是我们需要的。我们需要做的是将可能存储敏感数据的每个位置归零。请记住,我们在内存中拥有敏感信息的全部原因首先是为了我们可以使用它;这种使用几乎肯定会导致敏感数据被复制到堆栈和寄存器中。

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

于 2016-05-26T22:03:03.630 回答
1

如果您使用动态分配,您可以控制擦除该内存,而不受系统对堆栈所做的事情的约束。

int secret_foo(void)
{
  int *key = malloc(sizeof(int));
  *key = get_secret();
  memset(key, 0, sizeof(int));
  // other magical things...
  return result;
}
于 2016-05-26T21:50:49.963 回答
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;
}
于 2016-05-26T21:56:40.453 回答