Linus 震怒!内核整数溢出“安全”之争:从华为案例看 Linux Kernel 的硬核防御演进
2026/5/4 23:57:27 网站建设 项目流程

前言

在 C 语言的世界里,整数溢出就像一个潜伏在暗处的幽灵。你以为 $2^{31}-1 + 1$ 会变成一个巨大的正数,结果它却变成了一个负数。这种“数学奇点”在内核空间往往意味着系统权限的彻底丧失。

最近,内核社区围绕“陷阱整数”展开了一场长达一年的技术博弈。Linus Torvalds 的亲自下场开喷,让原本枯燥的补丁讨论变成了关于“什么才是真正的安全”的哲学思辨。


一、 深度复盘:华为 HSO 驱动漏洞(CVE-2021-39290)

作为 Linux 内核的重要贡献者,华为在追求极致性能的同时,也时刻在与底层的算术错误作斗争。这个著名的漏洞生动地展示了一个简单的加法是如何毁掉整个系统防御的

1. 漏洞成因:消失的边界检查

在华为维护的hso网络驱动中,处理数据包(skb)时需要计算缓冲区的总长度。

// 简化后的逻辑 int len = header_len + payload_len; if (len > MAX_BUFFER_SIZE) { return -EINVAL; } unsigned char *buf = kmalloc(len, GFP_KERNEL);

致命点:攻击者可以从用户态构造一个极大的payload_len(例如0xFFFFFFF0)。当它与header_len相加时,结果发生了整数回绕,变成了一个极小的正数(如15)。这个小数字顺利通过了MAX_BUFFER_SIZE的检查,导致kmalloc只分配了一个极小的内存块。

2. 连锁反应:堆溢出与 Panic

接下来的memcpycopy_from_user依然会按照原始巨大的payload_len进行拷贝。结果就是:小小的缓冲区被瞬间撑破,相邻的内核对象被恶意覆盖。攻击者借此可以篡改函数指针,甚至直接获取 Root 权限。

3. 华为的解决方案

华为工程师与社区协作,迅速推动了补丁。核心逻辑是引入了内核安全算术库:

  • 强制校验:严禁直接使用+*计算内存大小。

  • 引入check_add_overflow使用编译器内置的溢出检测机制,在加法发生的瞬间捕捉异常。

  • 代码审计:华为内部以此为契机,对网络协议栈中所有涉及size计算的逻辑进行了地毯式扫描。


二、 举一反三:这些整数风险你避开了吗?

除了华为遇到的缓冲区分配问题,整数溢出在内核中还有以下变种:

  1. 参考计数溢出(Reference Count Overflow):

    当一个内核对象的引用计数(refcount)被非法增加到溢出并回绕至 0 时,内核会误认为该对象已不再使用并将其释放(Free)。然而,系统中可能仍有指针指向它,这就造成了Use-After-Free (UAF)漏洞,这是目前内核中最易被利用的漏洞类型。

  2. 循环计数器陷阱:

    for (i = 0; i < count; i++) { ... }

    如果count来自用户态且由于溢出变成了一个负数(对于有符号整型),循环可能完全不执行;或者如果i溢出导致条件永远为真,则会引发内核死循环,导致 CPU 100% 占用,系统瞬间宕机。

  3. 数组索引越界:

    计算数组下标时发生溢出,可能导致指针访问到数组边界之外的地址。即便溢出后的偏移量很小,也可能修改到内核的关键全局变量。


三、 社区大地震:Kees Cook 与 Linus 的“生死战”

面对这些层出不穷的漏洞,安全专家 Kees Cook 提出了他的“终极武器”:属性标注系统

  • __ob_wrap:官方认证的“回绕”。告诉工具:我知道这里会溢出,我是故意的(比如哈希算法)。

  • __ob_trap:一旦溢出,直接让 CPU 报错,停止运行。

Linus 的怒火:死掉的机器不是安全的

Linus Torvalds 对此大发雷霆。他的观点非常务实:“一个直接崩溃(Oops)的系统,对用户来说和被黑了同样糟糕。”

Linus 认为,安全不应该以“自杀”为代价。他提出,如果发生了溢出,系统应该优雅地跳出当前逻辑,清理现场,然后报错返回,而不是简单粗暴地让整个内核崩掉。


四、 最终共识:带标签的跳转机制

在 Linus 的坚持下,社区达成了一个折中且精妙的方案:“溢出标签跳转”

int __overflow_label(out_of_bounds) process_data(u32 a, u32 b) { // 标注 count 为“陷阱类型” u32 __attribute__((overflow_behavior(trap))) count; // 执行逻辑 count = a + b; // 如果这里溢出,自动跳转到 out_of_bounds return 0; out_of_bounds: // 优雅清理资源并报错,Linus 觉得这很 Cool pr_err("Detect integer overflow! Recovering...\n"); return -ERANGE; }

五、 结语

从华为的补丁实战到内核社区的架构之争,我们看到 Linux 内核正在从“防御 Bug”向“改变语言规范”演进。

作为内核开发者或底层驱动开发者,我们要记住:数学不会骗人,但 C 语言会。永远不要相信用户输入的长度,永远优先使用check_add_overflowarray_size等安全宏。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询