Clang 5.0 中出现 vsprintf 和 vsnprintf [-Wformat-nonliteral] 警告

3
我有这段代码。
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;
}

当我使用Clang 5.0.0并开启-Weverything参数编译它时,会出现以下警告:

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)时也会出现相同的情况。

我该如何解决这个警告?

谢谢


你应该将#else子句更改为简单地包含#error。因为现在,在缺少vsnprintf的破损系统上,你有一个非常严重的安全漏洞。或者,你可以在备用情况下使用tmpfilevfprintf - R.. GitHub STOP HELPING ICE
1个回答

11

显然解决方案是告诉Clang,你的vsnprintf函数在实现printf家族函数的行为时被调用。你可以使用一个属性来做到这一点,具体请参考这里描述的内容

__attribute__((__format__ (__printf__, 3, 0)))
static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
    ...
}

这个检查确定格式字符串是否是调用函数上的文本。由于 err_doit 已经使用了 va_list,因此您还应该在调用它的函数中指定格式。第二个数字(这里为0)应该是可变参数 ... 的参数索引。参见此讨论

函数属性是gcc的非标准扩展,也适用于Clang。如果你想使代码与编译器无关,则应将属性封装在一个宏中,以便对于不支持属性的编译器隐藏它。

(免责声明:我没能用Clang检查这个问题,但是它适用于gcc。编辑:修正了示例中错误的参数索引。)


谢谢 :) 这对我有用,但根据我在你的答案中链接的文档中的解释,正确的方式是 attribute((format (printf, 3, 0))),因为格式是 const char *fmt(第三个参数)。 - fitzbutz
是的,我也遇到了“不是字符串参数”的错误,但是忘记更新帖子了。抱歉,我会更新的。 - M Oehm
以上解决方案可行,但我并不理解其背后的概念。有人可以解释一下吗? - Anand Sonawane
@AnandSonawane:Gcc和Clang的属性可以向编译器提供更多信息。在这种情况下,它将向函数的用户传达类似于printf的语法被使用,因此会警告格式不匹配。它还告诉实现在此处使用格式文本是可以的,因为vprintf不会直接调用,只能通过给定的函数调用,其中fmt应该是一个字面量。 - M Oehm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接