You would expect a printf call with a user-controlled format string to be, at worst, an arbitrary read. Thanks to %n, it can be a write as well.
If the user can control the formatting string, they can write to pointers stored on the stack. It's important to use printf("%s", str) instead of printf(str).
Useless use of printf; what's wrong with "puts(str)"?
puts() adds a newline at the end. gcc will happily turn printf("%s\n", str) into puts(str), though.
I've never tested to see if printf("%s", str) becomes the equivalent fputs(str, stdout)