Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Printf-Tac-Toe (github.com/carlini)
117 points by carlos-menezes 16 hours ago | hide | past | favorite | 12 comments
 help



[2020], and written for IOCCC: The International Obfuscated C Code Contest.

This was awarded "Best of Show - abuse of libc" at the time[0]. See also the judges' remarks[1]:

This program consists of a single printf(3) statement wrapped in a while loop. You would not think that this would amount to much, but you would be very, very wrong. A clue to what is happening and how this works is encoded in the ASCII art of the program source.

[0] https://www.ioccc.org/2020/index.html

[1] https://www.ioccc.org/2020/carlini/index.html


Contestant: "I'll take My Dirty Programming Secrets for 100, Alex."

Alex: "Its primary purpose is to serve as The One True Debugger."

(It has certainly served me well.)


This is both impressive and slightly terrifying. Format strings are way more powerful than most people realize.

How did we end up with printf - within a loop - being Turing-complete? Was it designed that way from the beginning? Were new features added over time until we got there?

Having something Turing-complete is surprisingly easy, and it hides everywhere. The repository have a small document that explains how you can use printf() as a computer : it can performs additions, logical union and negation, which is enough.

It was unintentional, but Ken Thompson being Ken Thompson, can't be 100% sure.


List of examples: https://gwern.net/turing-complete

It was probably unintentional, yeah, I don't recall any mentions of early printf being overloaded to do stuff, nor is it clear why you would do that since you're using it in a much more convenient Turing-complete language already (C).


So there was no extension of the functionality over time, all the formats have been supported from day one?

The key features that is used here is the '%n' format specifier, that fetches a pointer as the next argument, and writes a character count back.

There is actually an interesting question here: was '%n' always in printf, or was it added at one point?

I took a cursory look at some old Unix source archives at TUHS: https://www.tuhs.org/cgi-bin/utree.pl

As far as I can tell from the PDP-11 assembly, Version 7 research Unix (relevant file: /usr/src/libc/stdio/doprnt.s) does not appear to implement it.

The 4.1BSD version of that file even explicitly throws an error, treating it as an invalid format specifier.

The implementation in a System III archive looks suspiciously similar to the BSD one, also throwing an error.

Only in a System V R4 archive (relevant file: svr4/ucblib/libc/port/stdio/doprnt.c) I found an implementation of "%n" that works as expected.

I guess it was added at some point to System V and through that eventually made it into POSIX?


I think it was first introduced in 4.3 BSD Tahoe (released June 15, 1988): https://www.tuhs.org/cgi-bin/utree.pl?file=4.3BSD-Tahoe/usr/...

This was an update to the earlier 4.3 BSD (1986) which still implemented printf() in VAX assembly instead, and doesn't support the %n feature.

So %n may have originally been implemented in 4.3 BSD Tahoe and made its way into SVR4 subsequently.


No surprise Log4J attack was that big. Amount of logic one can fit into text formatting is immense.

To be fair, this is actually `scanf` and `printf` in a loop. The `scanf` is buried in the `arg` define.

That's the content why I check HN! :)



Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: