{"id":51440666,"url":"https://github.com/daedalus/printftc","last_synced_at":"2026-07-05T11:01:47.772Z","repository":{"id":368689008,"uuid":"1286391411","full_name":"daedalus/printfTC","owner":"daedalus","description":"Proving C printf is Turing complete via the %n format specifier","archived":false,"fork":false,"pushed_at":"2026-07-01T18:26:37.000Z","size":32,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-01T20:07:28.737Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daedalus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-01T18:26:27.000Z","updated_at":"2026-07-01T18:26:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/daedalus/printfTC","commit_stats":null,"previous_names":["daedalus/printftc"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/daedalus/printfTC","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2FprintfTC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2FprintfTC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2FprintfTC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2FprintfTC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daedalus","download_url":"https://codeload.github.com/daedalus/printfTC/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daedalus%2FprintfTC/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35151638,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-05T11:01:46.944Z","updated_at":"2026-07-05T11:01:47.752Z","avatar_url":"https://github.com/daedalus.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Turing Completeness of C printf\n\nDoes `printf` have enough computational power to be Turing complete on its own?\nThe answer is nuanced, and this repository explores exactly where the boundary\nlies.\n\n## The Honest Claim\n\n**printf the format-string language is not Turing complete.** It has no loop\nconstructs, no conditionals, and no ability to call itself. A single `printf()`\ncall parses its format string left-to-right exactly once.\n\n**But `%n` changes everything.** It writes an attacker-controlled value to an\narbitrary address. When that address is a return address or function pointer,\nthe format string hijacks control flow — and suddenly the format string *is*\nthe program. This is not hypothetical; it is exactly how real exploits work.\n\nThis repository demonstrates both sides:\n\n| Claim | Status | Examples |\n|-------|--------|----------|\n| \"printf + C is TC\" | Proven (trivially — C is already TC) | #1–#15 |\n| \"printf alone is TC\" | Requires `%n` to hijack control flow | #16 |\n| \"`%n` enables arbitrary write\" | Proven — the real security finding | #1, #4, #7, #9 |\n\n## What `%n` Actually Provides\n\n```c\nint sum = 0;\nprintf(\"%*s%*s%n\", a, \"\", b, \"\", \u0026sum);  // sum = a + b\n```\n\n`%*s` prints exactly N spaces (when the string is `\"\"`), and `%n` writes the\ncumulative character count to `sum`. This is a **read-modify-write cycle**:\n\n| Primitive | Mechanism | Limitation |\n|-----------|-----------|-----------|\n| LOAD | `%*s` reads stored value as width | Only as format-string width arg |\n| STORE | `%n` writes char count to any `int*` | Can only add to running count |\n| Addition | Concatenate two `%*s` in one call | Works — pure printf |\n| Subtraction | Cannot be done by printf alone | Requires C's `-=` after LOAD |\n| Branching | Not in the format string language | Requires C's `if`/`else` |\n| Looping | Not in the format string language | Requires C's `for`/`while` |\n\nprintf's `%n` is a **memory bus**, not an ALU. It provides the read/write\nprimitives. Arithmetic and control flow come from C.\n\n## Building and Running\n\n```bash\nmake all          # compile all examples\nmake clean        # remove binaries\n\n./01_counter      # run individual example\n./16_self Interpreter\n```\n\nRequires `gcc` with C11 support. Example #16 requires a non-NX stack\n(disable ASLR and exec-stack for testing: `execstack -s ./16_self`).\n\n## Examples\n\n### Printf + C (demonstrate printf's memory primitives)\n\n| # | File | What it demonstrates |\n|---|------|----------------------|\n| 1 | `src/01_counter.c` | Basic `%n` — writing character count to memory |\n| 2 | `src/02_two_counter.c` | Minsky machine (2-counter, proven TC via C loops) |\n| 3 | `src/03_fibonacci.c` | Iterative Fibonacci via printf addition |\n| 4 | `src/04_calculator.c` | ALU: add (pure printf), sub (printf + C), mul, div, mod, pow |\n| 5 | `src/05_conditional.c` | Conditional logic: min, max, abs, sign |\n| 6 | `src/06_state_machine.c` | Finite automaton recognizing {a^n b^n | n \u003e= 0} |\n| 7 | `src/07_pointer_arithmetic.c` | Array init, sum, and 2x2 matrix multiply |\n| 8 | `src/08_self_modifying.c` | Dynamic format strings and a data-driven interpreter |\n| 9 | `src/09_memory_simulation.c` | CPU simulator: registers, RAM, instruction cycle |\n| 10 | `src/10_ackermann.c` | Ackermann function (non-primitive-recursive) |\n| 11 | `src/11_turing_machine.c` | Binary increment Turing machine |\n| 12 | `src/12_lambda_calculus.c` | Church numerals, SUCC/ADD/MULT/EXP/PRED/SUB, booleans, fib |\n| 13 | `src/13_formal_proof.c` | Brainfuck interpreter (minimal TC language) |\n| 14 | `src/14_universal_machine.c` | Universal Turing machine (transition table as data) |\n| 15 | `src/15_complex_example.c` | 4-element sorting network |\n\n### Pure format-string TC (via `%n` control-flow hijack)\n\n| # | File | What it demonstrates |\n|---|------|----------------------|\n| 16 | `src/16_self_interpreter.c` | Format string that interprets itself via `%n` return-address overwrite |\n\n## How It Works\n\n### Arithmetic via `%n`\n\n```c\n// Addition: pure printf — print a+b spaces, capture total\nint sum = 0;\nprintf(\"%*s%*s%n\", a, \"\", b, \"\", \u0026sum);\n\n// Subtraction: printf loads a via %n, C subtracts b\nint diff = 0;\nprintf(\"%*s%n\", a, \"\", \u0026diff);\ndiff -= b;\n\n// Multiplication: repeated addition\nint product = 0;\nfor (int i = 0; i \u003c b; i++)\n    product = add(product, a);\n```\n\nNote: `%*d` with value 0 prints `\"0\"` (1 character), causing an off-by-one.\nAlways use `%*s` with `\"\"` to print exactly N characters.\n\n### Memory Access via `%n`\n\n```c\nint ram[256] = {0};\nint *cell = \u0026ram[address];\nprintf(\"%*s%n\", value, \"\", cell);  // ram[address] = value\n```\n\n### The Control-Flow Hijack (what makes it truly TC)\n\nA single `printf(input)` call where `input` is attacker-controlled can use `%n`\nto overwrite the return address on the stack, jumping execution back to the\nprintf call. The format string then modifies its own arguments (or the program\nstate) before the next iteration. This creates a loop without any C `for`/`while`:\n\n```\nprintf(input)  →  %n overwrites return addr  →  jumps back to printf\n    →  %n modifies program state  →  %n overwrites return addr again  →  ...\n```\n\nThis is the mechanism behind real format-string exploits and is the only\npath to genuine Turing completeness for printf alone.\n\n### Pitfalls\n\n- **Use `%*s`, not `%*d`**: `printf(\"%*d\", 0, 0)` prints `\"0\"` (1 char), breaking zero-operand cases. `printf(\"%*s\", 0, \"\")` prints nothing (0 chars).\n- **Single call for accumulation**: `%n` captures the count of *that* printf call only. Looping separate printf calls then capturing gives the last call's count (0), not the accumulated total. Combine into one call.\n- **Negative width ≠ subtraction**: Per the C standard, a negative `%*` argument means left-justify with `abs(width)`. It does not shrink output.\n\n## Computational Hierarchy\n\n```\nprintf alone (no %n hijack)\n    |\n    | can only: load, store, add (one pass, no loops)\n    v\nFinite-state transducer  ← NOT Turing complete\n\nprintf + C control flow\n    |\n    | can: load, store, add, subtract, branch, loop\n    v\nMinsky Machine (2-counter) --- proven TC (Minsky, 1967)\n    |\n    | simulates\n    v\nTuring Machine\n    |\n    | equivalent to\n    v\nLambda Calculus\n\nprintf with %n return-address hijack\n    |\n    | can: load, store, add, branch, loop (all in format string)\n    v\nTuring complete (architecture-specific)\n```\n\n## What the 15+1 Examples Prove\n\n| Model | Example | Control flow source |\n|-------|---------|-------------------|\n| Finite automaton | #6 | C `if`/`else` |\n| Minsky machine | #2 | C `for` loop |\n| Turing machine | #11 | C `while` loop |\n| Universal TM | #14 | C loop + switch |\n| Lambda calculus | #12 | C recursion |\n| Brainfuck | #13 | C `while` loop |\n| CPU | #9 | C `switch` + loop |\n| Self-interpreter | #16 | **`%n` hijacks return address** |\n\nThe first 15 examples prove that printf's memory primitives are sufficient for\nTC *when combined with C's control flow*. Example #16 attempts to close the gap\nby eliminating C control flow entirely, using `%n` to hijack the instruction\npointer directly.\n\n## Security Implications\n\nThe `%n` write primitive is the root cause of **format string vulnerabilities**:\n\n- `%n` can write to arbitrary memory addresses\n- Attackers control the value written (character count, manipulated via width specifiers)\n- This enables return-address overwrites, GOT hijacking, and arbitrary code execution\n\nThe Turing completeness angle is not just academic — it explains *why* format\nstring bugs are so dangerous. The same mechanism that could (in theory) make\nprintf a complete computer makes it an exploitation vector.\n\n**Defense**: never pass user input as a printf format string.\n\n## Historical Context\n\n- **1967**: Minsky proved 2-counter machines are Turing complete\n- **2000s**: Security researchers discovered `%n` enables arbitrary memory writes\n- **2001**: Phrack article \"Exploiting Format String Vulnerabilities\"\n- **2020s**: Academic papers formalized printf's computational power\n\n## References\n\n1. Minsky, M. (1967). *Computation: Finite and Infinite Machines*. Prentice-Hall.\n2. Turing, A. M. (1936). \"On Computable Numbers, with an Application to the Entscheidungsproblem\". *Proceedings of the London Mathematical Society*.\n3. Phrack Magazine. (2001). \"Exploiting Format String Vulnerabilities\".\n4. Lattner, C. (2005). \"Format string vulnerabilities\". LLVM Blog.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaedalus%2Fprintftc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaedalus%2Fprintftc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaedalus%2Fprintftc/lists"}