当前位置:首页 > 时尚 > 漏洞struct 正文

漏洞struct

来源:烟消火灭网   作者:知识   时间:2026-10-17 05:54:47

我翻过一套老旧的漏洞C代码,里面有个结构体,漏洞定义得规规矩矩,漏洞字段排列整齐,漏洞注释也写得勤快。漏洞可就是漏洞这套代码,在某个凌晨三点,漏洞让整个服务崩了十分钟。漏洞问题不在逻辑,漏洞在struct本身——它有个洞,漏洞一个看不见的漏洞洞。

那个洞叫内存对齐。漏洞你写struct { char a; int b; },漏洞编译器不会老老实实把a放在偏移0、漏洞b放在偏移1。漏洞它会悄悄在a后面塞三个填充字节,让b对齐到4字节边界。这本来是为了CPU访问效率,可如果你把这个结构体直接write到文件里,或者通过socket发给另一台机器,那三个填充字节就成了不确定的垃圾。你以为是固定长度的协议,结果每个包都带着随机杂质。对面解析的时候,校验和永远对不上。

更阴险的是另一种洞——位域。程序员喜欢用位域来节省空间,把几个标志位塞进一个int里。看着很美,可位域的布局是implementation-defined。GCC和MSVC的排列顺序可能正好相反。你在一台机器上序列化的数据,换台机器读出来,所有标志位全部错位,像把汉字按拼音排序后发给一个按笔画查字典的人。

我自己踩过最深的坑,是结构体里的柔性数组。C99允许你写struct { int len; char data[]; },这个data不占空间,只是个占位符。我那时候年轻,以为malloc一个sizeof(struct) + len就万事大吉。结果忘了结构体本身有对齐要求,malloc出来的内存,data的起始地址可能不是对齐的。在某些架构上,访问未对齐的int指针,直接SIGBUS,进程当场暴毙。不是崩溃,是直接被操作系统拍死。

你以为这些问题只在C里?C++的struct继承了所有C的毛病,还多加了构造函数、析构函数、虚函数表指针。你memcpy一个带虚函数的对象,等于把vptr也复制了一份。如果源对象和目的对象的类型不完全一致,那个vptr可能指向一个错误的方法表。调用虚函数的时候,程序跳到一个莫名其妙的地址,然后——段错误。你查半天,发现不过是把一个struct赋值给了另一个类型相似的struct。

这些洞从来不会被编译器警告。它们藏在类型系统的缝隙里,只有当你把内存当作字节流来搬运时才会露头。现代语言里,Go的struct有明确的对齐规则,Rust干脆用repr(C)来显式控制布局,Python的__slots__也避免了字典的开销。可那些老代码还在跑,在银行、在飞机、在心脏起搏器里。它们的struct上依然开着洞,只是没人再去碰那个凌晨三点的文件了。

所以,每当你看到一个struct定义,别只盯着字段名。想想它背后有多少看不见的填充字节,多少未定义的布局规则,多少靠编译器心情决定的排列顺序。那是一个个沉默的陷阱,等着某个信任它的程序员一脚踩空。

标签:

责任编辑:探索