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
的值(如代码所示)。
如果您感到好奇,这是一个实际的客户要求(嵌入式域名)。
答案 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;
}