关于C#:使用安全的_vsnprintf_s获取所需的缓冲区长度

Getting required buffer length with secure _vsnprintf_s

我正在尝试更新一些"旧版"代码以符合对MSVC的最新安全更新,并且在从_vsnprintf迁移到_vsnprintf_s时遇到了一些麻烦。

尤其是,我在调用_vsnprintf时使用空缓冲区,计数/长度为零,获取结果,分配所需大小的缓冲区(return value + 1),然后再次使用_vsnprintf调用新分配的缓冲区,并且已知正确的大小:

1
2
3
size_t length = _vsntprintf(nullptr, 0, mask, params);
TCHAR *final = new TCHAR [length + 1];
_vsntprintf(final, length + 1, mask, params);

此行为记录在MSDN上:

If the buffer size specified by count is not sufficiently large to contain the output specified by format and argptr, the return value of vsnprintf is the number of characters that would be written if count were sufficiently large. If the return value is greater than count - 1, the output has been truncated.

我正在尝试对_vsnprintf_s执行相同的操作,但是其文档中没有相同的内容。而是说

If the storage required to store the data and a terminating null exceeds sizeOfBuffer, the invalid parameter handler is invoked, as described in Parameter Validation, unless count is _TRUNCATE, in which case as much of the string as will fit in buffer is written and -1 returned.

尝试使用以下方法进行尝试:

1
size_t length = _vsntprintf_s(nullptr, 0, 0, mask, params);

这导致" length "为零。如果改为传递_TRUNCATE(-1)作为计数,则以下断言将失败:

Expression: buffer != nullptr && buffer_count > 0

我认为可以覆盖_set_invalid_parameter_handler并以某种方式找出长度,但是必须有一种更简单的方法吗?


1
2
3
size_t length = _vscprintf(mask, va_list);
TCHAR *final = new TCHAR [length + 1];
_vsntprintf_s(final, length, _TRUNCATE, mask, va_list);


如何滚动自己的vsnprintf变体,该变体不"违反规则"以获取长度:

1
2
3
4
5
6
7
8
9
10
int
printf_size(const char *fmt,int count,va_list ap)
{
    char buf[2000000];
    int len;

    len = vsnprintf_s(buf,sizeof(buf),count,fmt,ap);

    return len;
}

由于返回的值[最有可能]小于sizeof(buf),所以应该没问题。

或者,执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int
printf_size(const char *fmt,int count,va_list ap)
{
    char *buf;
    int siz;
    int len;

    for (siz = 2000000;  ;  siz <<= 1) {
        buf = malloc(siz);
        len = vsnprintf_s(buf,siz,count,fmt,ap);
        free(buf);
        if (len < siz)
            break;
    }

    return len;
}

或者,执行一站式服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int
sprintf_secure(char **buf,const char *fmt,int count,va_list ap)
{
    char *bp;
    int siz;
    int len;

    for (siz = 2000000;  ;  siz <<= 1) {
        bp = malloc(siz);
        len = vsnprintf_s(bp,siz,count,fmt,ap);
        if (len < siz)
            break;
    }

    bp = realloc(bp,len + 1);

    *buf = bp;

    return len;
}