bugprone-not-null-terminated-result

查找可能导致非空终止结果的函数调用。通常,字符串的正确长度为 strlen(src) + 1 或该表达式的等长,因为空终止符需要额外的空间。没有空终止符会导致在读取字符串时出现未定义行为。

以下及其相应的 wchar_t 基函数将被检查

memcpy, memcpy_s, memchr, memmove, memmove_s, strerror_s, strncmp, strxfrm

以下是一个现实世界的例子,程序员忘记增加传递的第三个参数,即 size_t length。这就是分配的内存长度不足以容纳空终止符的原因。

static char *stringCpy(const std::string &str) {
  char *result = reinterpret_cast<char *>(malloc(str.size()));
  memcpy(result, str.data(), str.size());
  return result;
}

除了发出警告之外,修复还将重写所有必要的代码。它还尝试调整目标数组的容量

static char *stringCpy(const std::string &str) {
  char *result = reinterpret_cast<char *>(malloc(str.size() + 1));
  strcpy(result, str.data());
  return result;
}

注意:它不能保证重写所有路径敏感的内存分配。

‘memcpy()’ 的转换规则

可以将 memcpy()memcpy_s() 调用重写为以下四个函数: strcpy(), strncpy(), strcpy_s(), strncpy_s(),其中后两个是前两个的更安全版本。它分别重写了基于 wchar_t 的内存处理函数。

基于目标数组的重写

  • 如果复制到目标数组不会溢出 [1],则新函数应该是旧的复制函数(以 cpy 结尾),因为它比安全版本更高效。

  • 如果复制到目标数组可能溢出 [1],并且 WantToUseSafeFunctions 设置为 true,并且可以获取目标数组的容量,则新函数可以是安全版本(以 cpy_s 结尾)。

  • 如果新函数可能是安全版本,并且分析的是 C++ 文件,并且目标数组是普通的 char/wchar_t,没有 un/signed,则可以省略目标数组的长度。

  • 如果新函数可能是安全版本,并且目标数组是 un/signed,则需要将其强制转换为普通的 char */wchar_t *

[1] 可能溢出
  • 如果目标数组的容量未知。

  • 如果给定长度等于目标数组的容量。

基于源字符串长度的重写

  • 如果给定长度为 strlen(source) 或该表达式的等长,则新函数应该是旧的复制函数(以 cpy 结尾),因为它比安全版本(以 cpy_s 结尾)更高效。

  • 否则,我们假设程序员希望复制 'N' 个字符,因此新函数是类似 ncpy 的,它复制 'N' 个字符。

使用 ‘strlen()’ 或该表达式的等长的转换

它分别转换了基于 wchar_t 的内存和字符串处理函数(其中只有 strerror_s 没有基于 wchar_t 的别名)。

内存处理函数

memcpy 请访问 ‘memcpy()’ 的转换规则 部分。

memchr 通常有一个 C 样式的强制转换,需要将其删除,因为新函数 strchr 的返回类型是正确的。给定的长度将被删除。

memmove 如果有安全函数可用,新函数是 memmove_s,它有一个新的第二个参数,它是目标数组的长度,它被调整,并且源字符串的长度增加 1。如果安全函数不可用,则给定长度增加 1。

memmove_s 给定的长度增加 1。

字符串处理函数

strerror_s 给定的长度增加 1。

strncmp 如果第三个参数是第一个或第二个参数的 length + 1,则必须将其截断,不进行 + 1 操作。

strxfrm 给定的长度增加 1。

选项

WantToUseSafeFunctions

true 指定目标环境被认为实现了以 ‘_s’ 为后缀的内存和字符串处理函数,这些函数比旧版本(例如 ‘memcpy_s()’)更安全。默认值为 true