[英]How to properly write asnprintf without format string compiler warnings?
I'd like to write a asnprintf
function -- which is a wrapper around snprintf, but it mallocs the string according to its output size.我想写一个asnprintf
function——它是 snprintf 的包装器,但它根据 output 的大小对字符串进行 malloc。 Unfortunately when I compile I get a warning (promoted to error on my system) format string is not a string literal [-Werror,-Wformat-nonliteral]
.不幸的是,当我编译时,我收到一条警告(在我的系统上升级为错误) format string is not a string literal [-Werror,-Wformat-nonliteral]
。
I looked up the warning and apparently there are security concerns with passing a non-literal to printf
functions, but in my case, I need to take in a format pointer, and pass that on.我查看了警告,显然将非文字传递给printf
函数存在安全问题,但就我而言,我需要采用格式指针并将其传递。
Is there a good way around this that does not expose the same security vulnerability?有没有解决这个问题的好方法,不会暴露相同的安全漏洞?
My function as is is as follows:我的function原样如下:
int
asnprintf(char **strp, int max_len, const char *fmt, ...)
{
int len;
va_list ap,ap2;
va_start(ap, fmt);
va_copy(ap2, ap);
len = vsnprintf(NULL, 0, fmt, ap);
if ( len > max_len)
len = max_len;
*strp = malloc(len+1);
if (*strp == NULL)
return -1;
len = vsnprintf(*strp, len+1, fmt, ap2);
va_end(ap2);
va_end(ap);
return len;
}
If you are targeting only GCC and Clang as compilers, you can get around this pretty easily by temporarily disabling the warning for that specific function:如果您只将 GCC 和 Clang 作为编译器,您可以通过暂时禁用该特定 function 的警告来轻松解决此问题:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wformat-security"
// Function definition here...
#pragma GCC diagnostic pop
Clang should recognize #pragma GCC
too. Clang 也应该识别#pragma GCC
。 You may also need to ignore -Wformat-security
as I did above depending on your compiler flags.您可能还需要像我上面所做的那样忽略-Wformat-security
,具体取决于您的编译器标志。
Godbolt link to working example with Clang 11 . Godbolt 链接到 Clang 11 的工作示例。
I'm wondering if it's possible for example to require that my function take only string literals我想知道是否有可能要求我的 function 只接受字符串文字
As Craig Estey suggests above , you could make use of the format
function attribute to make the compiler perform this check for you: 正如 Craig Estey 在上面建议的那样,您可以使用format
function 属性让编译器为您执行此检查:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wformat-security"
int __attribute__((format(printf, 3, 4))) asnprintf(char **strp, int max_len, const char *fmt, ...) {
// ... implementation ...
}
#pragma GCC diagnostic pop
char global_fmt[100];
int main(void) {
char *res;
asnprintf(&res, 100, "asd"); // will compile
asnprintf(&res, 100, global_fmt); // will NOT compile
return 0;
}
You could also do this, with a bit of trickery, using a macro and some compiler built-ins:您也可以使用宏和一些编译器内置函数来执行此操作,但需要一些技巧:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wformat-security"
int internal_asnprintf(char **strp, int max_len, const char *fmt, ...) {
return printf(fmt);
}
#pragma GCC diagnostic pop
#define asnprintf(strp, maxlen, fmt, ...) ({ \
_Static_assert(__builtin_constant_p(fmt), "format string is not a constant"); \
internal_asnprintf(strp, maxlen, fmt, __VA_ARGS__); \
})
char global_fmt[100];
int main(void) {
char *res;
asnprintf(&res, 100, "asd"); // will compile
asnprintf(&res, 100, global_fmt); // will NOT compile
return 0;
}
Note that the above code makes use of statement expressions ( ({...})
) which are a non-standard extension and may or may not be available depending on your compiler flags.请注意,上面的代码使用了语句表达式 ( ({...})
),它们是非标准扩展,可能可用也可能不可用,具体取决于您的编译器标志。
From my top comments...从我的热门评论...
Just add __attribute__((__format__(__printf__,3,4)))
to your asnprintf
declaration and/or definition.只需将__attribute__((__format__(__printf__,3,4)))
添加到您的asnprintf
声明和/或定义中。
This will cue the compiler to not complain.这将提示编译器不要抱怨。
And, the further benefit is that it will check the variadic arguments passed to asnprintf
against the format string.而且,进一步的好处是它将根据格式字符串检查传递给asnprintf
的可变参数 arguments。
So:所以:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
// put this in a .h file!?
int __attribute__((__format__(__printf__,3,4)))
asnprintf(char **strp, int max_len, const char *fmt, ...);
int
asnprintf(char **strp, int max_len, const char *fmt, ...)
{
int len;
va_list ap, ap2;
va_start(ap, fmt);
va_copy(ap2, ap);
len = vsnprintf(NULL, 0, fmt, ap);
if (len > max_len)
len = max_len;
*strp = malloc(len + 1);
if (*strp == NULL)
return -1;
len = vsnprintf(*strp, len + 1, fmt, ap2);
va_end(ap2);
va_end(ap);
return len;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.