最近遇到了一个奔溃问题,程序在执行到某个点后,瞬间干干净净的退出,也没有dmp文件生成。这个奔溃在指定场景下出现,于是用Windbg执行程序,准备在奔溃点进行分析。
想法很好,但是在奔溃点,看不到堆栈信息。于是通过日志及问题出现场景,确定了怀疑点。但是在怀疑点,并没有看出问题。因为,我先入为主的以为,strncpy_s会做边界检测,不会越界访问及复制。
根据Windbg给出的诊断信息以及咨询同事,在TerminateProcess函数上加断点,运行程序,程序在TerminateProcess上中断下来,堆栈显示程序终止确实是由strncpy_s引起。
于是写了简单的Demo,进行问题分析及验证,堆栈如下:
Figure 1
问题出在strncpy_s上,奔溃原因是_invalid_parameter_noinfo函数中调用了TerminateProcess函数,看意思是参数有问题。
strncpy_s在C11引入,声明如下:
errno_t strncpy_s(char *restrict dest, rsize_t destsz,
const char *restrict src, rsize_t count)
其有如下特点:
-
As corrected by the post-C11 DR 468, strncpy_s, unlike strcpy_s, is only allowed to clobber the remainder of the destination array if an error occurs.
-
Unlike strncpy, strncpy_s does not pad the destination array with zeroes, This is a common source of errors when converting existing code to the bounds-checked version.
-
Although truncation to fit the destination buffer is a security risk and therefore a runtime constraints violation for strncpy_s, it is possible to get the truncating behavior by specifying count equal to the size of the destination array minus one: it will copy the first count bytes and append the null terminator as always: strncpy_s(dst, sizeof dst, src, (sizeof dst)-1)
大致意思如下:
-
不像strcpy_s,strncpy_s只会在出错时,才会填充目标缓冲区中余下的区域
-
不像strncpy,strncpy_s并不会将目标缓冲区填0,这是将当前代码转换到边界检查版本后常见的错误
-
为了适应目标缓冲区而进行截断有安全风险,违反了strncpy_s的运行时约束,但可以通过指定源缓冲区待复制长度为目标缓冲区长度减一来实现截断,这样,目标缓冲区会以’\0’结尾:strncpy_s(dst, sizeof dst, src, (s