【问题标题】:passing (size_t)-1 to snprintf_s将 (size_t)-1 传递给 snprintf_s
【发布时间】:2018-09-14 06:57:59
【问题描述】:

这里有一个关于 C++ Stackwalker 的问题: https://github.com/JochenKalmbach/StackWalker

#define _TRUNCATE ((size_t)-1)

enum
{
    STACKWALK_MAX_NAMELEN = 1024
}; 

void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
{
    CHAR   buffer[STACKWALK_MAX_NAMELEN];
    size_t maxLen = STACKWALK_MAX_NAMELEN;
#if _MSC_VER >= 1400
    maxLen = _TRUNCATE;
#endif
    _snprintf_s(buffer, maxLen, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n",
        szSearchPath, symOptions, szUserName);
}

maxlen 首先设置为 1024,然后如果 VC++ 版本 >= 1400(它是)则设置为 (unsigned)-1,换句话说,至少为 UINT_MAX。

然后将maxlen传递给_snprintf_s,所以如果字符串大于1024个字符会再次导致缓冲区溢出。

我在这里遗漏了什么吗?将 maxlen 设置为 UINT_MAX 的原因可能是什么?使用安全的字符串函数但没有安全性?

【问题讨论】:

  • 注意,代码不使用(unsigned)-1,而是(size_t)-1,即SIZE_MAX
  • 很确定 size_t 总是无符号的
  • 如果你想要UINT_MAX,请使用UINT_MAX!不能保证(size_t)-1 == UINT_MAX.
  • @ServeLaurijssen size_t 是一些 unsigned 类型。它可能与unsignedunsigned long 或其他相同。
  • 没错,SIZE_MAX./UINT_MAX 是一个附带问题,但您可以修改帖子以不包括分心并提高清晰度。

标签: c++ c visual-c++


【解决方案1】:

_snprintf_s(Visual Studio 特定函数)的重载只能在 C++ 中使用,前提是可以从第一个参数推导出缓冲区长度。

请参阅documentationRemarks 下的第二段,了解_TRUNCATE 魔法值及其作用。

但请注意,对于早期版本的VS,代码使用_snprintf实现这个功能,它对宏_TRUNCATE一无所知:

#if _MSC_VER < 1400
// ...
#define _snprintf_s _snprintf

为了能够使用_snprintf,他必须安排要通过的缓冲区长度。 snprintf 没有可以推断字符缓冲区参数长度的模板化重载。另外,他需要#define _TRUNCATE,因为在那些相同的旧版本中,它在stdlib.h 中不存在。

总而言之,对于一个不必要的复杂的非标准库函数,这让我觉得这是一个非常笨拙的解决方法。

【讨论】:

  • 我明白了,_snprintf 实际上是一个模板函数,它已经知道传入的缓冲区大小! _TRUNCATE 的意思是“dontcare”,你已经知道大小了。奇怪的 C/C++ 组合,确实很笨重。感谢您指出!
猜你喜欢
  • 1970-01-01
  • 2019-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-28
  • 2015-02-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多