我在C中编写了一个简单的程序,它为字符串向量分配内存,然后打印出来。
#include <stdio.h>
#include <string.h>
int main() {
char str_a[20];
strcpy(str_a, "Hello, world!\n");
printf(str_a);
}
使用编译器gcc
,这会产生编译错误:
char_array2.c:8:12: warning: format string is not a string literal
(potentially insecure) [-Wformat-security]
printf(str_a);
1 warning generated.
我不明白为什么我会收到警告。有人可以向我解释一下吗?
答案 0 :(得分:15)
使用:
printf("%s", str_a);
在启用-Wformat-security
时删除警告。
诊断信息可以避免format string vulnerability。例如:
strcpy(str_a, "%x%x%x%x");
printf(str_a);
相当于:
printf("%x%x%x%x");
缺少必需的参数,攻击者可以使用它来转储堆栈(假设str_a
处于用户控制之下,这在您的程序中并非如此,但gcc
并不聪明足以说明。)
答案 1 :(得分:6)
考虑3个printf()
语句。如果存在格式不匹配,编译器可以检测哪个?
void foo(const char *str_a,int x) {
printf("Hello %d\n", x); // Compiler sees this is good
printf("Hello %d\n"); // Compiler sees this is bad --> warning/error
printf(str_a, x); // Compiler cannot tell - thus the warning
}
答案 2 :(得分:3)
这不是错误,而是警告。使用printf打印字符串时,需要printf("%s", str_a)
之类的字符串。每当你在引号中有一个字符串(&#34;&#34;)时它就是一个字符串文字,这就是警告所说的不是字符串文字的含义。我不完全确定为什么你需要一个字符串文字,但通常最好遵循编译器;其他人可以概括地澄清字符串文字的必要性。
答案 3 :(得分:0)
如果你要做这样的事情:
#include <stdio.h>
#include <string.h>
int main() {
char str_a[20];
fgets(str_a, 20, stdin);
printf(str_a);
}
用户输入A %s bad %n string
,用户将能够崩溃您的程序,并可能启动一个shell。
通过此输入,用户可以有效地执行此操作:
printf("A %s bad %n string");
%s
说明符导致函数从无效地址读取,而%n
说明符导致写到无效地址。
答案 4 :(得分:0)
发出警告是因为优秀的编译器将格式字符串中的格式说明符(如%d
)与格式后面的类型(此处为int
)进行比较。这显然只适用于编译器可以为说明符分析的字符串文字。变量中的指向char的指针会破坏对类型的完整性检查。
请注意,不匹配的格式说明符(或参数类型)会导致未定义的行为,并且通常会导致垃圾值被转换或崩溃。想想当%s
与积分值相关联时会发生什么。