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)
{
    ...
}