LangChain numexpr.evaluate 执行 LLM 生成代码
一句话
据 NVD,LangChain 0.0.245 的数学链路可让 LLM 生成内容经 numexpr.evaluate 进入代码执行 sink,属于 ToLO-Exec。
怎么读这个案例
这个案例适合用来理解”换一个执行器不等于风险消失”。
numexpr.evaluate 不是 Python 内置 eval,但它仍然会解释执行表达式。只要表达式来自 LLM 输出,就仍要问:
- 表达式语言能做什么?
- 上下文里有哪些对象?
- 版本是否修复了已知问题?
它和 CVE-2023-29374 LLMMathChain 的关系是: LLMMathChain 修复时采用了 numexpr,但 numexpr 自己历史上也有 RCE bug。这个案例就是 numexpr 一次安全问题被映射回 LangChain 的 ToLO 路径。
影响组件
影响组件是 LangChain 的数学表达式执行能力及其可选依赖 numexpr。
| 来源 | affected_versions | fixed_in |
|---|---|---|
| NVD | langchain 0.0.245 | — |
| GHSA | langchain <0.0.308,numexpr <2.8.5 | — |
| LangChain PR | — | langchain 0.0.308,把 numexpr 依赖下限提升到 ^2.8.6 |
| numexpr issue #442 | 涉及 ^USER_FUNCTIONS$ 等被滥用接口 | — |
LangChain 修复 PR 把 numexpr 依赖下限提高到 2.8.6,因此本站暂记为 pending(fixed_in 表述差异)。
ToLO 路径
| 列 | 内容 |
|---|---|
| Source | S_LLM^framework — LLMMathChain 生成的表达式文本 |
| Transform | 框架把模型输出整理为待求值表达式 |
| Sink | numexpr.evaluate(expr, ...)(ToLO-Exec,但 sink 不是 Python eval) |
| Guard 缺失 | 缺少与代码执行匹配的 C_SAFE^allowlist、C_SAFE^capability 或隔离执行边界 |
这里的 sink 与普通 exec 不同,但 ToLO 判断相同:LLM 输出决定了求值引擎要解释的表达式。若表达式语言、函数集合或上下文对象可被滥用,就落入执行边界风险。
教学骨架
import numexpr
# 修复前:LLMMathChain 调用expr = llm_output_text # ← S_LLM^framework
# numexpr 可识别一些函数(sin, cos, exp 等)和算术运算。# 历史 CVE 涉及 numexpr 内部 ^USER_FUNCTIONS^ 解析处理某些畸形 payload,# 在特定版本下可能跨过表达式边界访问 Python 上下文。result = numexpr.evaluate(expr)⚠ 这里不展开具体 PoC payload。重点是:numexpr 不等于安全求值器,要看版本。
修复方式
据 LangChain PR #11302 和 commit 801ddb12856c32a1c2d2f50e4c6638c57894d0c7,上游在 v0.0.308 将可选依赖 numexpr 的最低版本从 ^2.8.4 提高到 ^2.8.6,依赖新版输入收紧来降低执行风险。
具体来说:
- 不是 LangChain 自己加了 AST 校验。
- 不是 LangChain 改用其他求值器。
- 而是 升级
numexpr依赖版本,让 numexpr 本身的新版 fix 生效。
修复属于哪一类 C_SAFE?
这种修复很难直接归入 C_SAFE 五类。它最接近:
- 依赖版本约束 —— 不在
C_SAFE五类里,而是一种间接修复。 - 静态分析里不容易识别这种修复为 sanitizer,除非规则同时理解依赖版本和库语义。
更彻底的修复应当是:
- 把 numexpr 包装在
C_SAFE^capability(进程级 sandbox)中,即使 numexpr 出新 bug,也限制后果范围。 - 给 numexpr 显式传
global_dict={}, local_dict={...}(LLMMathChain 修复 PR #2943 已经这样做)。
对 ToLO 分类的启发
该案例支持 ToLO-Exec:问题不在传统用户输入直接到 eval,而在 LLM 输出被框架当作可信中间结果后进入执行 sink。它不需要扩展 taxonomy,但提示数学/代码解释器类工具需要能力门控或白名单,而不只是类型检查。
它也提醒案例复盘要区分”sink API 名称”和”sink 语义”:
numexpr.evaluate不是 Python 内置eval。- 但它仍然是解释执行 LLM 生成表达式的边界。
更广泛地说:任何对外承诺”安全求值”的库都要看版本和实现细节。它们不是自动 C_SAFE^safe-codec:
| 库 | 安全级别 | 注意事项 |
|---|---|---|
ast.literal_eval | 高 | 只接字面量,不接表达式;DoS 可能(超大嵌套) |
numexpr.evaluate | 中 | 表达式引擎;历史 CVE;需 global_dict={} |
sympy.sympify | 低-中 | 默认支持很多函数,可能解析时执行;需 evaluate=False 等约束 |
RestrictedPython | 低-中 | 多次绕过 CVE |
eval | 无 | 永远不要在 untrusted 上下文用 |
学习者要带走什么
- sink 的名字不一定叫
eval,只要它解释执行 LLM 生成内容,就要按执行边界看。 - 依赖升级可以修复已知风险,但静态分析里不容易把版本约束识别为 sanitizer。
- 表达式求值器适合配合变量 allowlist、函数 allowlist 和最小权限上下文。
- CodeQL 等工具检测
ToLO-Exec时,sink 集合应包含所有”解释执行”风格的 API,而不只是eval/exec字面调用。
读完检查
为什么这个案例仍归为 ToLO-Exec,而不是新增一个 ToLO-Numexpr?
→ 因为 taxonomy 按 sink 语义分组,numexpr.evaluate 的核心语义仍是解释执行表达式。开新子类需要新的 sink 语义和新的 sanitizer 适配,这里都没有。
公开来源
- https://nvd.nist.gov/vuln/detail/CVE-2023-39631
- https://github.com/advisories/GHSA-f73w-4m7g-ch9x
- https://github.com/langchain-ai/langchain/issues/8363
- https://github.com/langchain-ai/langchain/pull/11302
- https://github.com/langchain-ai/langchain/commit/801ddb12856c32a1c2d2f50e4c6638c57894d0c7
- https://github.com/langchain-ai/langchain/releases/tag/v0.0.308
- https://github.com/pydata/numexpr/issues/442
下一步阅读
- CVE-2023-29374 LLMMathChain:LLMMathChain 修复时采用了 numexpr。
- Defensive Patterns §
C_SAFE^safe-codec:安全求值器的工程要点和版本注意。