%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
问题无关。