![](/img/trans.png)
[英]How to sprintf to write a string without warnings about restrictions?
[英]How to properly write asnprintf without format string compiler warnings?
我想寫一個asnprintf
function——它是 snprintf 的包裝器,但它根據 output 的大小對字符串進行 malloc。 不幸的是,當我編譯時,我收到一條警告(在我的系統上升級為錯誤) format string is not a string literal [-Werror,-Wformat-nonliteral]
。
我查看了警告,顯然將非文字傳遞給printf
函數存在安全問題,但就我而言,我需要采用格式指針並將其傳遞。
有沒有解決這個問題的好方法,不會暴露相同的安全漏洞?
我的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;
}
如果您只將 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 也應該識別#pragma GCC
。 您可能還需要像我上面所做的那樣忽略-Wformat-security
,具體取決於您的編譯器標志。
我想知道是否有可能要求我的 function 只接受字符串文字
正如 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;
}
您也可以使用宏和一些編譯器內置函數來執行此操作,但需要一些技巧:
#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;
}
請注意,上面的代碼使用了語句表達式 ( ({...})
),它們是非標准擴展,可能可用也可能不可用,具體取決於您的編譯器標志。
從我的熱門評論...
只需將__attribute__((__format__(__printf__,3,4)))
添加到您的asnprintf
聲明和/或定義中。
這將提示編譯器不要抱怨。
而且,進一步的好處是它將根據格式字符串檢查傳遞給asnprintf
的可變參數 arguments。
所以:
#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.