One doesn't normally think of python as vulnerable to format string attacks. And it's not, at least in the security sense. But after getting odd reports of failures adding tags to MP3 files with Quod Libet, particularly one that pointed me straight at mmap, and then tracing it to a format string problem in mmap_move_method (already reported as http://bugs.debian.org/340661 and http://python.org/sf/1365916), I was concerned.
The iii:move on mmap_move_method line 5 means to pull three values from the tuple, and store them as integers in the variables passed into the variadic scanf-style function. Written and thoroughly tested on your standard 32-bit platform, all is well. However since these variables are really longs, there's a problem. On the rising AMD64 platform, a long is eight bytes, no longer the same size as a four byte int.
Furthermore, thanks to uninitialized local storage, chances are really good that /dest/, /src/, and /count/ start out as some weird value like 0xFEDCBA0987654321 instead of a pretty 0x0000000000000000. Upon the successful parse writing an int to the long's storage, we now have something like 0xFEDCBA090000000A when we wanted 0xA. And that's on a little-endian system; on a big-endian we'd end up with the equally absurd 0x0000000A87654321.
There's one bright light to this story: while python code is more
idiomatically one of exception catching, its C implementation is
necessarily Look Before You Leap.
In this case the looking catches
the absurd scenario (lines 10-14) before it causes any harm in the
memmove (line 18), so there is no segfault. Unfortunately since my code
wasn't expecting this error, it exited unexpectedly and left the files
it was modifying in a corrupted state. I have since
fixed my calling
code to work around this python bug, but I'm hoping for a day when I no
longer need this code.
This is the backstory. Next time I'll talk about what I did after finding this bad format string.