How curl was accidentally Turing-complete for nine months

How curl was accidentally Turing-complete for nine months

Nicolas Seriot Computation > How curl was accidentally Turing-complete for nine months 29th October 2026 From curl 8.12 through 8.16, config files could slice variable values and include other config files without an explicit depth cap. Together, these features let curl simulate any two-counter Minsky program. Under the usual idealization of unbounded strings and resources, this construction was Turing-complete. Curl 8.17.0 added a five-level include cap that breaks this reduction.

Nicolas Seriot 计算 > curl 如何在九个月内意外实现图灵完备 2026 年 10 月 29 日。从 curl 8.12 到 8.16 版本,配置文件可以对变量值进行切片并包含其他配置文件,且没有明确的深度限制。这些功能共同使得 curl 能够模拟任何双计数器 Minsky 程序。在通常对无界字符串和资源的理想化假设下,这种结构是图灵完备的。Curl 8.17.0 增加了一个五级包含上限,从而打破了这种归约。

Update 2026-10-30: Jonathan Tremesaygues wrote me: I couldn’t help but implement “99 Bottles of Beer on the Wall” 99_bottles.tar.gz Minsky 2-Counter Reduction A counter value n is represented by n copies of x. Each instruction state uses one or more config files, with file names acting as labels, and expand-config selecting the next file. Syntax: {{b}} expands the current value [0-0] keeps only the first byte (x or empty) [1-] keeps everything except the first byte empty slices yield empty strings (doc) Minsky Curl config Register R Variable r (R is the number of x bytes) INC(R) expand-variable = “r={{r}}x” DEC(R) expand-variable = “r[1-]={{r}}” JZ(R, _, x) expand-variable = “t[0-0]={{r}}“expand-config = “state{{t}}.conf” JMP(S) expand-config = “S.conf” Halt No further expand-config With that, we can compile any two-counter program into curl config files.

更新 2026-10-30:Jonathan Tremesaygues 写信给我:我忍不住实现了“墙上的 99 瓶啤酒” 99_bottles.tar.gz。Minsky 双计数器归约:计数器值 n 由 n 个 x 的副本表示。每个指令状态使用一个或多个配置文件,文件名充当标签,expand-config 选择下一个文件。语法:{{b}} 展开当前值,[0-0] 仅保留第一个字节(x 或空),[1-] 保留除第一个字节外的所有内容,空切片产生空字符串(文档)。Minsky 与 Curl 配置对应关系:寄存器 R 对应变量 r(R 是 x 字节的数量);INC(R) 对应 expand-variable = “r={{r}}x”;DEC(R) 对应 expand-variable = “r[1-]={{r}}“;JZ(R, _, x) 对应 expand-variable = “t[0-0]={{r}}” 和 expand-config = “state{{t}}.conf”;JMP(S) 对应 expand-config = “S.conf”;Halt 对应不再有 expand-config。有了这些,我们就可以将任何双计数器程序编译成 curl 配置文件。

PoC: Adding 2 + 3 Four files in curl_addition.zip compute 2 + 3. Build a local curl 8.12.0 binary if needed: mkdir -p /tmp/curl-tc-build cd /tmp/curl-tc-build curl -fsSLO https://curl.se/download/curl-8.12.0.tar.gz tar -xzf curl-8.12.0.tar.gz cd curl-8.12.0 ./configure —without-ssl —disable-shared —disable-manual —disable-docs make -j4 Then, from the addition/ directory, run: /tmp/curl-tc-build/curl-8.12.0/src/curl -q -s -K main.conf Expected output: A=xxxxx B=

概念验证:计算 2 + 3。curl_addition.zip 中的四个文件用于计算 2 + 3。如果需要,构建一个本地的 curl 8.12.0 二进制文件:mkdir -p /tmp/curl-tc-build,cd /tmp/curl-tc-build,curl -fsSLO https://curl.se/download/curl-8.12.0.tar.gz,tar -xzf curl-8.12.0.tar.gz,cd curl-8.12.0,./configure —without-ssl —disable-shared —disable-manual —disable-docs,make -j4。然后,在 addition/ 目录下运行:/tmp/curl-tc-build/curl-8.12.0/src/curl -q -s -K main.conf。预期输出:A=xxxxx B=

Runtime Bounds The reduction assumes unbounded counters and unbounded steps. The real binary has neither: Expanded content (curl’s own limit). Each expansion is capped at MAX_EXPAND_CONTENT = 10,000,000 bytes (src/var.c). Step count. There is no loop: each step is a nested —config that returns only at halt. Every level uses a C stack frame and keeps its config file open until it finishes. In a minimal countdown loop on 8.12, an 8 MB stack allowed 922 steps before a segfault. Retained variable versions. Reassignment adds a new entry while retaining the old one until cleanup. Memory use and some variable lookups grow with the number of steps.

运行时限制:该归约假设计数器和步骤是无界的。但实际的二进制文件两者皆非:展开内容(curl 自身的限制)。每次展开被限制在 MAX_EXPAND_CONTENT = 10,000,000 字节(src/var.c)。步骤计数。没有循环:每一步都是嵌套的 —config,只有在停止时才会返回。每一层都使用一个 C 栈帧,并保持其配置文件打开直到完成。在 8.12 版本的一个最小倒计时循环中,8 MB 的栈在段错误前允许执行 922 步。保留的变量版本。重新赋值会添加一个新条目,同时保留旧条目直到清理。内存使用量和某些变量查找会随着步骤数量的增加而增长。

History and Version Support curl 8.3 (2023-09): Variables expansion curl 8.12 (2025-02): Byte-range assignment curl 8.17 (2025-11): five-level cap defeats the reduction The pull request says the cap prevents a config file from including itself indefinitely. Its regression test does exactly that and expects an error. The public explanation does not discuss Turing completeness or why the cap is five. A Bash while loop can complement bounded curl by invoking a config once per step and carrying the counters and instruction label between invocations. The repetition then comes from Bash, so this is a composition rather than curl-alone Turing completeness. Security impact None.

历史与版本支持:curl 8.3 (2023-09):变量展开;curl 8.12 (2025-02):字节范围赋值;curl 8.17 (2025-11):五级上限打破了归约。拉取请求称该上限防止了配置文件无限包含自身。其回归测试正是这样做的并预期会报错。公开解释中并未讨论图灵完备性或为什么上限是五。Bash while 循环可以通过每步调用一次配置并在调用之间携带计数器和指令标签来补充有界 curl。重复性来自 Bash,因此这是一种组合,而不是 curl 单独的图灵完备性。安全影响:无。