最近我们切换到了较新的 GCC,它在优化大小时优化了整个函数并将其替换为“空指针访问”陷阱代码。查看 godbolt,在使用 进行优化时,GCC 11.1 出现了此问题。使用和-Os
进行优化效果很好,甚至使用 也是如此。-O2
-O3
-fstrict-aliasing
简化后的代码如下所示(godbolt 链接):
#include <inttypes.h>
#include <stddef.h>
#include <string.h>
typedef struct bounds_s {
uint8_t *start;
uint8_t *end;
} bounds_s_t;
static void reserve_space(bounds_s_t *bounds, size_t len, uint8_t **element)
{
if (bounds->start + len > bounds->end) {
return;
}
*element = bounds->start;
bounds->start += len;
}
void bug(uint8_t *buffer, size_t size)
{
bounds_s_t bounds;
uint32_t *initialize_this;
initialize_this = NULL;
bounds.start = buffer;
bounds.end = buffer + size;
reserve_space(&bounds, sizeof(*initialize_this), (uint8_t **)&initialize_this);
uint32_t value = 1234;
memcpy(initialize_this, &value, sizeof(*initialize_this));
}
并导致以下组装:
bug:
xor eax, eax
mov DWORD PTR ds:0, eax
ud2
什么优化会让 GCC 认为initialize_this
变量为 NULL?我唯一想到的就是违反严格的别名规则。但是,将双指针从 转换为uint32_t **
真的uint8_t **
会成为这里的问题并导致如此严重的后果吗?