在函数返回之前擦除堆栈内存的选项

时间:2015-02-13 00:46:30

标签: c security stack

在编写处理敏感数据的c代码时,我听说攻击者可以在处理敏感数据的函数返回后尝试读取堆栈中剩余的内存。在函数返回之前将所有堆栈内存归零似乎是一个艰苦的过程。此外,它还需要使用-O0来确保编译器不会优化归零代码。有没有办法自动擦除堆栈内存?也许编译器标志,定义,或者可能是汇编语言内联函数,它读取堆栈大小并将其归零?

3 个答案:

答案 0 :(得分:2)

没有一般方法(我知道)可以移植。

你能做什么(并且应该工作)只是用堆栈分配的数组调用另一个函数,大到足以覆盖前一次调用的敏感区域。

之后,memset表示函数中的数组。将数组声明为volatile可能是个好主意,这样memset就不会被优化掉。

C99的示例(因为它使用可变长度数组):

void clear_stack(size_t sz)
{
    // you can try using `alloca` or a fixed-size array if not using C99
    // make sure `sz` is small enough to avoid stack overflows
    volatile char arr[sz];
    memset(arr, 0, sz);
}

答案 1 :(得分:0)

你需要使用堆栈变量吗?您可以使用堆变量并以这种方式控制内存吗?

您可以分配足够大的堆内存块并为其分配类型指针,然后在释放块之前将其memset为零。

答案 2 :(得分:0)

我认为没有完全可移植的解决方案。我之前尝试使用通用解决方案来解决这个问题但是我意识到编译器的局部变量重组使得将当前正在执行的函数的堆栈归零几乎是不可能的。

但是我确实在函数返回后找到了零堆栈内存的解决方案,尽管它依赖于操作系统的支持。在这种情况下,FreeRTOS,虽然我确信它可以适应其他简单的RTOS。这个解决方案有点像TimČas的建议,但不是希望用适当大小的数组覆盖内存,而是擦除所有内容,因为它确切知道堆栈的位置。

将以下代码添加到task.c

void *pvGetCurrentTaskStackStart(void) {
    return (void *)pxCurrentTCB->pxStack;
}

这是一个擦除当前任务堆栈中所有未使用(但很脏)的内存的函数:

void wipe_task_stack(void)
{
    int i;
    register uint32_t sp asm ("sp");
    uint32_t stack_start = (uint32_t) pvGetCurrentTaskStackStart();
    uint32_t size = sp - stack_start;

    /* Do not use memset or similar function, it would wipe its own stack! */
    for (i = i; i < size; i++) {
        ((volatile uint8_t *) stack_start)[i] = 0;
    }
}

这里有一些很好的文章,关于为什么归零内存很难:

http://www.daemonology.net/blog/2014-09-04-how-to-zero-a-buffer.html

http://www.daemonology.net/blog/2014-09-05-erratum.html

http://www.daemonology.net/blog/2014-09-06-zeroing-buffers-is-insufficient.html