18

I was reading about %n format specifier in C at this question. But when I tried the following program on different C++ compilers, it gave me different outputs.

Why? What is the reason? Is there undefined or implementation defined behavior occurring?

#include<stdio.h>

int main()
{
  int c = -1;
  printf("geeks for %ngeeks ", &c);
  printf("%d", c);
  getchar();
  return 0;
}

Output:

Code blocks 13.12: (correct output)

geeks for geeks 10

Borland/CodeGear/Embarcadero C++: (correct output)

geeks for geeks 10

Orwell Dev C++:

geeks -1

Microsoft Visual Studio 2010:

Debug assertion failed ("'n' format specifier disabled",0) 
Community
  • 1
  • 1
Destructor
  • 14,123
  • 11
  • 61
  • 126
  • Because the exact implementation is left over being compiler specific? – πάντα ῥεῖ Mar 23 '15 at 19:38
  • _"Orwell Dev C++"_ Boils down to some MinGW GCC version, correct? – πάντα ῥεῖ Mar 23 '15 at 19:44
  • 6
    You should initialize `c` to something like 0 so you can tell if it was touched or not. 34 might be the uninitialized value. – Retired Ninja Mar 23 '15 at 19:45
  • 2
    You may want to initialize `c` with some value, say, `-1`, and try the experiment with `Orwell Dev C++` again. See if you get `-1` back (in which case you would know that `%n` is not supported). – Sergey Kalinichenko Mar 23 '15 at 19:46
  • 1
    @dasblinkenlight: yes, i got -1 back as output in Orwell Dev C++. – Destructor Mar 23 '15 at 19:47
  • @πάνταῥεῖ: It looks like there is a mingw option and a gcc option for Orwell Dev C++. From the context clues, I would guess that it is using mingw. – Bill Lynch Mar 23 '15 at 19:52
  • @BillLynch How do they differ exactly? – πάντα ῥεῖ Mar 23 '15 at 19:58
  • 2
    @BillLynch: Hmm. MinGW uses gcc as its compiler, so I'm not sure having a mingw option vs. a gcc option makes sense, but it probably would with more context. But the use of gcc shouldn't be relevant; `printf` is part of the C runtime library, not the compiler. MinGW uses gcc as its compiler and the Microsoft runtime library (which dislikes `%n`). – Keith Thompson Mar 23 '15 at 20:00
  • 1
    Some of the answers at that other question already address why Microsoft disabled `%n`. – jamesdlin Mar 23 '15 at 20:00
  • @jamesdlin, that's pretty weak justification for intentionally being non-conforming. Since an essential requirement for a program to be open to a format string exploit is that it uses user-controlled data as a format string, a far better solution would be to warn about variable format strings, as some other compilers do. I can think of only unflattering reasons why MS would approach the problem as it did. – John Bollinger Mar 23 '15 at 20:13
  • FWIW, the Borland/Embarcadero C++ compiler also supports `%n` and outputs the correct value. – Remy Lebeau Mar 23 '15 at 20:27
  • 1
    @JohnBollinger I'm just the messenger. That said, I do think that Microsoft took a practical approach: variable format strings are much more common than using `%n`. Furthermore, *compile-time* warnings are useless for existing binaries, whereas disabling `%n` at runtime would address (albeit potentially break) programs that dynamically linked to the CRT. – jamesdlin Mar 23 '15 at 20:40
  • @KeithThompson although MinGW and gcc have the same compiler, they have different standard library implementations. minGW defers `printf` to MSVCRTxxx.dll by default, whereas Linux/gcc uses glibc. IDK what "orwell dev-c++" does. – M.M Mar 24 '15 at 04:31
  • @meet CodeBlocks is an IDE. You can use any compiler with it. I'd suggest changing your post to include the compiler name and version instead. – M.M Mar 24 '15 at 04:33
  • @MattMcNabb: gcc itself has no standard library; implementations combine the gcc compiler with some library. Supposedly Orwell Dev C++ has a MinGW option and a gcc option. Since MinGW uses gcc, that didn't make much sense. – Keith Thompson Mar 24 '15 at 04:41

2 Answers2

10

As noted in the question Code Blocks is indeed correct while Orwell Dev C++ is incorrect. Visual Studio on the other hand is non-conforming.

cppreferences C documentation for printf says says:

returns the number of characters written so far by this call to the function.

I don't see anything in the draft standard that makes this optional, C++ refers to the C standard with respect to printf. MSDN documents this and says:

Because the %n format is inherently insecure, it is disabled by default. If %n is encountered in a format string, the invalid parameter handler is invoked, as described in Parameter Validation. To enable %n support, see _set_printf_count_output.

Why is the %n format is inherently insecure?

I am assuming that they consider it unsafe because of security issues such as those outlined in Format String Vulnerability documents one possible way to exploit this. It is predicated on the format string being controlled by user input. The paper gives the following example:

char user_input[100];
scanf("%s", user_input); 
printf(user_input); 

Retired Ninja linked to Bugtraq post which demonstrates an real example of such a bug ending up in an exploit in proftpd 1.2.0pre6:

  • ftp to host
  • login (anonymous or no)

(this should be all on one line, no spaces)

ftp> ls aaaXXXX%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u %u%u%u%u%u%u%u%u%u%653300u%n

(replace the X's with the characters with ascii values 0xdc,0x4f,0x07,0x08 consecutively)

Lots of other nasties can easily be easily done with this. Since proftpd will pass on user input data to snprintf, argument attacks are easy.

The problem with Visual Studios approach is that it breaks portability. Other approaches include using flags like Wformat-security used by gcc which combined with -WError can make it an error but you can choose this as part of your build process.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Why it is inherently insecure? What about other compilers like code blocks, dev C++, clang etc? How they handles this? – Destructor Mar 23 '15 at 19:44
  • 7
    @meet MS views it as inherently insecure. That does not mean it is inherently insecure, just that at least one significant vendor thinks so. – chux - Reinstate Monica Mar 23 '15 at 19:49
  • 2
    @chux: It also means that Microsoft's implementation is non-conforming by default (which is not news). – Keith Thompson Mar 23 '15 at 19:52
  • http://en.wikipedia.org/wiki/Uncontrolled_format_string It has been used for exploits in the past. http://seclists.org/bugtraq/1999/Sep/328 – Retired Ninja Mar 23 '15 at 19:54
  • 4
    @Keith Thompson Should MS be consider a leader in this for disallowing `"%n"`by default, yet allowing with a certain compiler switch? Or is this [Embrace, extend, extinguish](http://en.wikipedia.org/wiki/Embrace,_extend_and_extinguish)? Hmmmm – chux - Reinstate Monica Mar 23 '15 at 20:07
  • @chux: It's not even a compiler switch. Apparently you have to *call* `_set_printf_count_output(1)` before calling `printf`. – Keith Thompson Mar 23 '15 at 20:12
  • @chux the problem is that this breaks portability. – Shafik Yaghmour Mar 23 '15 at 20:12
  • @RetiredNinja that bugtraq post is an excellent example, thanks – Shafik Yaghmour Mar 23 '15 at 20:20
  • 1
    GCC/glibc and `_FORTIFY_SOURCE > 1` are a bit smarter (can actually be said about fortification vs. Secure CRT (at least in plain C) in general): `%n` is only enabled for read-only format strings (like in the example). – cremno Mar 23 '15 at 20:25
  • So don't user unchecked user input as a `printf` format string. – Keith Thompson Mar 23 '15 at 21:15
4

The Code Blocks output is correct.

The Orwell Dev C++ output is incorrect. Either that implementation is not non-conforming by default (read the documentation to see if there's a way to make it behave correctly), or it has a bug.

Microsoft's implementation is non-conforming by default. It disables the standard %n format specifier to prevent some possible security problems (though there are no such problems in the code in your question). Apparently there are ways to re-enable it; see Shafik Yaghmour's answer.

The only potential problem I see with your program is that it doesn't print a newline at the end of its output, but that's not relevant to the %n issue.

Community
  • 1
  • 1
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631