c - 当 vsnprintf 不可用时安全地格式化字符串

标签 c printf c89

我正在编写需要格式化字符串的代码,我想避免缓冲区溢出。

我知道如果 vsnprintf 可用(从 C99 开始)我们可以:

char* formatString(const char *format, ...)
{
    char* result = NULL;
    va_list ap;
    va_start(ap, format);

    /* Get the size of the formatted string by getting vsnprintf return the
     * number of remaining characters if we ask it to write 0 characters */
    int size = vsnprintf(NULL, 0, format, ap);

    if (size > 0)
    {
        /* String formatted just fine */
        result = (char *) calloc(size + 1, sizeof(char));
        vsnprintf(result, size + 1, format, ap);
    }

    va_end(ap);
    return result;
}

我想不出在 C90 中做类似事情的方法(没有 vsnprintf)。如果事实证明如果不编写极其复​​杂的逻辑就不可能实现,我很乐意为结果设置最大长度,但我不确定如何在不冒缓冲区溢出风险的情况下实现这一点。

最佳答案

C99 之前的版本没有提供简单的解决方案来格式化字符串,并具有防止缓冲区溢出的高度安全性。

正是那些讨厌的 "%s""%[]""%f" 格式说明符需要非常小心考虑他们潜在的长期输出。因而需要这样的功能。 @Jonathan Leffler

为了使用那些早期的编译器,代码必须分析format 和参数以找到所需的大小。到那时,代码几乎就可以让您拥有完整的 my_vsnprintf()。我会为此寻求现有的解决方案。 @user694733 .


即使使用 C99,*printf() 也有环境限制。

The number of characters that can be produced by any single conversion shall be at least 4095. C11dr §7.21.6.1 15

所以任何试图char buf[10000]的代码; snprintf(buf, sizeof buf, "%s", long_string); 即使有足够的 buf[]strlen(long_string) > 4095 也会出现问题.

这意味着快速而肮脏的代码可以计算 % 和格式长度,并做出所需大小不超过的合理假设:

size_t sz = 4095*percent_count + strlen(format) + 1;

当然,对说明符的进一步分析可能会导致更保守的 sz。继续往下 path我们 end编写我们自己的 my_vsnprintf()


即使使用您自己的 my_vsnprintf()安全性 也不过如此。没有运行时检查 format(可能是动态的)是否与以下参数匹配。为此需要一种新方法。

C99 解决方案厚颜无耻的 self 宣传以确保匹配说明符和参数:Formatted print without the need to specify type matching specifiers using _Generic .

关于c - 当 vsnprintf 不可用时安全地格式化字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52537188/

相关文章:

c - sprintf 导致未定义的行为

c - 在 sprintf 中转义

c - C中函数指针的递归声明

c - Socket的系统调用Bind In c

C - OpenDir() 条目数

c - 动态 C 结构中的重叠和太长的整数值

c - 具有 C99 内联函数的 OpenBSD 5.9 header

c - `long` 保证与 `size_t` 一样宽

c - 如何在 C 中的字符串中获取井号(或哈希)符号?

python - 尝试将带有 ctypes 的 numpy 数组转换为 C 会出现段错误