DEBUG.WIKI
OnlineSIM:在线接收短信,虚拟号码租用
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →
RackNerd;$21.99/年 · 1GB KVM

Rust bytes 在内存分配失败后重复释放,先确认 OOM 是否展开栈

bytes 1.12.0 的共享存储转换在 OOM 走 panic 展开时存在重复释放风险。1.12.1 已调整分配顺序;普通 OOM abort 与这条路径不同。

Rust 程序在内存分配失败后没有立即中止,而是进入 panic 展开,随后在 BytesMut 或 Bytes 的释放路径出现重复释放。若应用采用了允许 OOM 展开的特殊配置,要核对实际使用的 bytes 版本。

这条问题有很窄的前提。常规链接 std 的程序,默认分配错误处理会打印信息并中止进程,不执行同样的栈展开过程;不能把任意内存不足、容器退出或分配失败都认定为 bytes 重复释放。

出错时,两个所有者都可能进入 Drop

bytes 1.12.0 的 BytesMut::promote_to_shared 会先从已有缓冲区构造一个拥有底层内存的 Vec,再为包含它的共享控制结构分配 Box。若后一步分配以 panic 展开结束,临时共享结构和原来的 BytesMut 都可能尝试释放同一份缓冲区。

Bytes 的相应共享克隆路径也需要处理这种分配失败顺序。它不是应用少调用一次 clone() 就能普遍解决的问题,更不能靠删除最后一个 drop() 或泄漏缓冲区规避。

核对构建配置、分配错误处理方式和故障时是否真的发生 unwind。不要为了验证本题,临时让全局分配器从不允许展开的接口里抛 panic;那可能另外引入未定义行为,也不能代表正常 OOM 路径。

采用正式 1.12.1 的分配顺序修正

bytes 1.12.1 已正式发布相关修正。它在接管已有 Vec 的所有权之前,先完成共享结构所需的分配;如果分配在这一阶段失败,原缓冲区仍只有原来的所有者负责释放。

在依赖允许的工程中,可将 bytes 的最低要求更新到该版本,例如 Cargo.toml 中:

[dependencies]
bytes = "1.12.1"

这只是依赖声明片段,不是完整工程。已有项目可能通过其他库间接引入 bytes,应检查 Cargo.lock 中实际参与链接的条目,避免只更新一个直接依赖而仍保留另一份受影响的旧版本。采用更高版本时,也需核对兼容性与其正式实现。

1.12.1 修复的是异常路径中的所有权交接,不会增加可用内存,也不保证分配成功。若业务持续耗尽内存,仍需独立处理输入规模和容量;不能将一次升级当作已经解决全部 OOM 的证据。

不在同一个损坏进程里继续尝试

重复释放属于内存安全问题。不能用 catch_unwind 吞掉 panic 后,宣称进程里的缓冲区仍可靠。也不要在生产中通过人为耗尽内存来确认修正。

保留故障构建的锁文件、分配配置与受控诊断,按正常部署更新进程。在隔离、允许故障注入的验证中,只有确实采用 OOM unwind 配置的工程才需要覆盖该路径,同时检查正常创建、切片、克隆和释放行为;默认 abort 配置应继续按自己的进程终止契约处理。

若已经出现内存破坏,先恢复服务进程,再依据上层存储和业务记录核对结果。库升级与进程重启不能证明此前正在执行的业务写入已经成功或自动回滚。