Clang 5.0上的vsprintf和vsnprintf[-Wformat nonliteral]警告
我有这段代码Clang 5.0上的vsprintf和vsnprintf[-Wformat nonliteral]警告,c,network-programming,clang,printf,C,Network Programming,Clang,Printf,我有这段代码 static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) { int errno_save; unsigned long n; char buf[MAXLINE]; errno_save = errno; #ifdef HAVE_VSNPRINTF vsnprintf(buf, sizeof(buf), fmt, ap); /*
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save;
unsigned long n;
char buf[MAXLINE];
errno_save = errno;
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, sizeof(buf), fmt, ap); /* this is safe */
#else
vsprintf(buf ,fmt, ap); /* this is not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, sizeof(buf) - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level,"%s", buf);
} else {
fflush(stdout);
fputs(buf, stderr);
fflush(stderr);
}
return;
}
当我编译它(带有-Weverything的Clang 5.0.0)时,我得到了以下警告:
Building C object lib/netutils/CMakeFiles/netutils.dir/error.c.o
/Users/User/Desktop/project.cmake/lib/netutils/error.c:98:16: warning: format string is not a string literal [-Wformat-nonliteral]
vsprintf(buf ,fmt, ap); /* this is not safe */
^~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/secure/_stdio.h:66:57: note: expanded from
macro 'vsprintf'__builtin___vsprintf_chk (str, 0, __darwin_obsz(str), format, ap)
^
另一个函数vsnprintf(buf、sizeof(buf)、fmt、ap)也会发生同样的情况
如何修复此警告
感谢显然,解决方案是告诉Clang在实现函数族的行为的函数中调用您的
vsnprintf
,方法是调用vsnprintf
。您可以使用一个属性来执行此操作:
这将检查格式字符串是否是调用函数上的文本。由于err\u doit
已经使用了va\u列表
,因此也应该在调用它的函数上指定格式。第二个数字,这里是0,应该是可变参数的参数索引,…
。看
函数属性是gcc的一个非标准扩展,它也是为Clang实现的。如果要保持代码编译器的独立性,则应将属性包装在宏中,以隐藏不知道属性的编译器的属性
(免责声明:我无法用Clang检查这一点,但它适用于gcc。编辑:修复了示例中错误的参数索引)您应该将
\else
子句更改为只包含\erro
。事实上,在缺少vsnprintf
的坏系统上有一个非常严重的安全漏洞。或者,您可以在回退案例中使用tmpfile
和vfprintf
。谢谢:)这对我很有效,但根据我在您的答案中链接的文档中的解释,正确的方式是attribute_uuuu(uuu格式(printf,3,0)),因为格式是const char*fmt(第三个参数)。是的,我得到了“不是字符串参数“也有错误,但忘了更新帖子。很抱歉,我会更新它。上面的解决方案有效,但我不理解它背后的概念。有人能解释一下吗?@AnandSonawane:Gcc和Clang的属性可以向编译器提供更多信息。在这种情况下,它会向函数用户传达使用了类似printf的语法,因此会警告格式不匹配。它告诉实现可以在这里使用格式文本,因为vprintf
不会直接调用,只能通过给定的函数调用,其中fmt
应该是文本。
__attribute__((__format__ (__printf__, 3, 0)))
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
...
}