编程 nginx 补完 CVE-2026-42533 后随机 500:volatile map 嵌套脚本覆盖了全局 buffer end

2026-11-09 20:01:18

nginx 补完 CVE-2026-42533 后随机 500:volatile map 嵌套脚本覆盖了全局 buffer end

Andrew Lukoshko,AlmaLinux Lead Architect,2026 年 11 月 4 日

最近针对 CVE-2026-42533 的 nginx 安全更新引入了一个回归问题:在某些配置下,nginx 会随机对一小部分请求返回 HTTP 500,并记录 [alert] ... no buffer space in script copy。用户更新后报告了这个问题,我们在所有收到该更新的 AlmaLinux 版本上都复现了它。根因已经定位,修复后的 nginx 包也已构建完成,既移除了回归,又完整保留了 CVE-2026-42533 的修复。这些包目前位于 AlmaLinux testing 仓库。

现象

更新到修复 CVE-2026-42533 的 nginx 包后,部分用户开始在 /var/log/nginx/error.log 中看到这样的错误:

[alert] 12345#0: *678 no buffer space in script copy, client: 192.0.2.10, server: example.com, request: "GET /some/path HTTP/1.1"

每一条 alert 都意味着该请求以 HTTP 500 失败。失败是间歇性的:同一个 URL 可能这次成功、下次失败。在某个受影响的站点上,单个 location 有三分之一到二分之一的请求失败。

当 set 或 rewrite 指令使用了来自标记为 volatile 的 map 的变量,并且这个 map 的值本身又包含其他变量时,问题就会出现。例如:

map $uri $cache_key {
    volatile;
    default "$scheme://$host$uri$is_args$args";
}

server {
    location /purge/ {
        set $key $cache_key;
        ...
    }
}

这类配置在 CVE-2026-42533 更新之前工作正常。

根因

CVE-2026-42533 是 nginx script 引擎中的缓冲区溢出。script 引擎负责为 set、rewrite、return、proxy_pass 这类指令从变量构建字符串。上游修复增加了一项检查:在把数据拷贝进结果缓冲区之前,nginx 要确保不会写到缓冲区末尾之后。为此,上游把缓冲区末尾保存在每个 script 引擎实例中。

在 script 引擎结构体中增加字段会改变 nginx 的 ABI,导致基于当前包构建的第三方动态模块无法使用。为了避免这个问题,回移到 CentOS Stream 和 RHEL 包(因而也进入 AlmaLinux)的修复把缓冲区末尾放在一个全局变量里。只要同一时间只有一个 script 在运行,这样做没问题。

但 script 可以嵌套。当 set $key $cache_key 拷贝一个 volatile map 变量的值时,nginx 会立刻求值这个 map,map 运行自己的脚本去构建 "$scheme://$host$uri..."。内层脚本用自己缓冲区的末尾覆盖了全局缓冲区末尾。控制权回到外层 set 时,它的边界检查比较的是错误的缓冲区。检查是否失败取决于两个缓冲区恰好落在内存的什么位置,这就是错误随机出现的原因。

不使用 volatile 时,map 的值在外层 script 开始前就已计算并缓存,没有嵌套,也就没有这个错误。

修复做法

修复很小。每个执行 script 的函数(ngx_http_complex_value()、ngx_http_script_run() 以及 stream 模块中的对应函数)现在会在开始前保存当前全局缓冲区末尾,在结束时恢复。嵌套脚本仍然可以独占全局变量处理自己的缓冲区,内层返回后外层立即拿回自己的正确值。效果与上游的 per-engine 缓冲区末尾一致:

  • CVE-2026-42533 的防护完整保留。安全修复新增的每个边界检查都还在,且检查的是正确的缓冲区。触发原始溢出的请求仍然会被拒绝,返回 HTTP 500 并记录同样的 alert。
  • ABI 不变,现有第三方动态模块继续可用。
  • 嵌套脚本恢复正常。

实测数据

针对示例配置发起 2000 次请求:

包修复前修复后
AlmaLinux 9, nginx 1.2026 × 5000
AlmaLinux 9, nginx:1.24 stream20 × 5000
AlmaLinux 10, nginx 1.2619 × 5000

我们也已经把问题和修复报告给 Red Hat,编号 RHEL-300689,以便在 CentOS Stream 和 RHEL 中一并修复。

测试仓库中的包

发行版nginx包
AlmaLinux 8nginx:1.24 module streamnginx-1.24.0-3.module_el8.10.0+4351+6f6e4e3e.5.alma.2
AlmaLinux 9默认包nginx-1.20.1-28.el9_8.6.alma.2
AlmaLinux 9nginx:1.24 module streamnginx-1.24.0-7.module_el9.8.0+335+c30a9238.5.alma.2
AlmaLinux 10默认包nginx-1.26.3-6.el10_2.7.alma.1

如果使用 module stream,testing 仓库只会为已启用的 stream 提供更新。用 dnf module list nginx 确认当前启用的是哪个 stream。

测试步骤

安装 testing 仓库:

sudo dnf install -y almalinux-release-testing

更新 nginx 以及已安装的 nginx 模块:

sudo dnf upgrade 'nginx*' --enablerepo=almalinux-testing

确认版本:

rpm -q nginx

检查配置并重启 nginx(包更新不会自动重启 nginx):

sudo nginx -t && sudo systemctl restart nginx

检查错误日志:

sudo grep 'no buffer space in script copy' /var/log/nginx/error.log

注意事项

更新完包后,不建议继续启用 testing 仓库,除非是在真正的非生产环境中操作。如果这是生产环境,禁用该仓库:

sudo dnf config-manager --disable almalinux-testing

测试结果可以在 Mattermost 的 Development 频道、bugs.almalinux.org 上反馈,或发送邮件至 packager@almalinux.org。

信源:https://almalinux.org/zh-hans/blog/2026-11-04-nginx-script-copy-fix-call-for-testing/

复制全文 生成海报 nginx 运维 漏洞修复 Linux

推荐文章

程序员茄子在线接单