%n格式说明符程序在不同的编译器上给出不同的输出。 为什么?

在这个问题上,我正在阅读C中的%n格式说明符。 但是当我在不同的C ++编译器上尝试以下程序时,它给了我不同的输出。

为什么? 是什么原因? 是否存在未定义或实现定义的行为?

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

输出:

代码块13.12 :(正确输出)

 geeks for geeks 10 

Borland / CodeGear / Embarcadero C ++ :(正确输出)

 geeks for geeks 10 

Orwell Dev C ++:

 geeks -1 

Microsoft Visual Studio 2010:

 Debug assertion failed ("'n' format specifier disabled",0) 

正如问题中所述, Code Blocks确实是正确的,而Orwell Dev C++是不正确的。 另一方面,Visual Studio不符合要求。

cppreferences printf的C文档说:

返回此函数调用到目前为止写入的字符数。

我没有在草案标准中看到任何使这个可选的内容,C ++指的是关于printf的C标准。 MSDN记录了这一点并说:

由于%n格式本质上是不安全的,因此默认情况下禁用它。 如果在格式字符串中遇到%n,则调用无效参数处理程序,如参数validation中所述。 要启用%n支持,请参阅_set_printf_count_output。

为什么%n格式固有的不安全?

我假设他们认为它不安全,因为安全问题,如格式字符串漏洞文档中概述的一个可能的方法来利用它。 它取决于由用户输入控制的格式字符串。 本文给出了以下示例:

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

退休忍者链接到Bugtraqpost ,它展示了这样一个错误的真实例子,最终出现在proftpd 1.2.0pre6的漏洞利用中:

  • ftp来主持
  • 登录(匿名或否)

(这应该是一行,没有空格)

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%u%653300u%n

(用连续的ascii值为0xdc,0x4f,0x07,0x08的字符替换X)

很多其他的恶作剧可以很容易地完成。 由于proftpd会将用户输入数据传递给snprintf,因此参数攻击很容易。

Visual Studios方法的问题在于它破坏了可移植性。 其他方法包括使用gcc使用的Wformat-security之类的标志,结合-WError会使其成为错误,但您可以选择此作为构建过程的一部分。

代码块输出正确。

Orwell Dev C ++输出不正确。 默认情况下,该实现不是不符合要求(阅读文档以查看是否有一种方法可以使其正常运行),或者它有一个错误。

默认情况下,Microsoft的实现不符合要求。 它禁用标准%n格式说明符以防止一些可能的安全问题(尽管您的问题中的代码中没有此类问题)。 显然有一些方法可以重新启用它; 见Shafik Yaghmour的回答 。

我在程序中看到的唯一潜在问题是它不会在输出结束时打印换行符,但这与%n问题无关。