How curl was accidentally Turing-complete for nine months
Translated by a model; the original is in Russian.
curl is famous for doing nearly everything related to moving data over a network. As Nicolas Seriot found, for a while it could, at least in theory, do everything. From version 8.12 through 8.16, curl's config files formed a Turing-complete system.
Where the completeness came from
Two features combined. One let you slice a variable's value by byte range. The other let a config file include other config files with no explicit depth cap. Together they are enough to simulate any two-counter Minsky program. Under the usual idealization of unbounded strings and resources, such a machine is Turing-complete.
The construction works like this:
- a counter value n is stored as n copies of
x; - increment appends another
x, and decrement drops the first byte with the[1-]slice; - a conditional jump takes the first byte with
[0-0], which is eitherxor an empty string, and usesexpand-configto load eitherstate_.conforstate_x.conf; - an unconditional jump is simply including the right file, and halting means including nothing further.
File names act as labels, so any two-counter program can be "compiled" into a set of curl configs. As proof, the author provides four files that add 2 and 3. Build curl 8.12.0, run it with -K main.conf, and it prints A=xxxxx B=. No network is required.
Real-world bounds
In practice, of course, nothing was infinite. Each expansion is capped at 10,000,000 bytes, a limit built into curl itself. The construction also has no loop. Every step is a nested --config call that returns only at halt, so each step consumes a stack frame and keeps a file open. A minimal countdown loop managed 922 steps on an 8 MB stack before segfaulting. Reassigning a variable also keeps its old versions until cleanup, so memory use grows with every step.
Timeline
Variables arrived in curl 8.3 in September 2023, and byte-range assignment followed in 8.12 in February 2025. In 8.17, released in November 2025, a five-level include cap broke the reduction. According to the pull request, the cap stops a config file from including itself indefinitely, and the regression test checks exactly that. The public explanation says nothing about Turing completeness or why the limit is five.
Wrapping curl in a Bash while loop restores unbounded computation: the loop invokes a config once per step and carries the counters and the instruction label between invocations. But then Bash provides the repetition, so this is a composition of two programs rather than Turing completeness in curl alone.
The author sums up the security impact in a single word: none.
Source: Lobsters
Comments
Sign in, to comment.