bugprone-unsafe-functions

检查具有更安全、更可靠的替换函数,或因设计缺陷而被视为已弃用的函数。该检查严重依赖于 C11 的 **附录 K.** “边界检查接口” 中的函数。

该检查实现了 CERT C 编码标准中的以下规则:

cert-msc24-ccert-msc33-c 作为此检查的别名重定向到这里。

不安全的函数

如果启用了 ReportDefaultFunctions,则会报告以下函数。

如果 *附录 K.* 可用,则建议使用 *附录 K.* 中的替换函数替换以下函数

asctime, asctime_r, bsearch, ctime, fopen, fprintf, freopen, fscanf, fwprintf, fwscanf, getenv, gets, gmtime, localtime, mbsrtowcs, mbstowcs, memcpy, memmove, memset, printf, qsort, scanf, snprintf, sprintf, sscanf, strcat, strcpy, strerror, strlen, strncat, strncpy, strtok, swprintf, swscanf, vfprintf, vfscanf, vfwprintf, vfwscanf, vprintf, vscanf, vsnprintf, vsprintf, vsscanf, vswprintf, vswscanf, vwprintf, vwscanf, wcrtomb, wcscat, wcscpy, wcslen, wcsncat, wcsncpy, wcsrtombs, wcstok, wcstombs, wctomb, wmemcpy, wmemmove, wprintf, wscanf.

如果 *附录 K.* 不可用,则仅建议使用以下函数替换上述列表中的函数

  • asctime, asctime_r, 建议替换函数: strftime

  • gets, 建议替换函数: fgets

以下函数始终会被检查,无论 *附录 K* 是否可用

  • rewind, 建议替换函数: fseek

  • setbuf, 建议替换函数: setvbuf

如果启用了 ReportMoreUnsafeFunctions,则还会检查以下函数

  • bcmp, 建议替换函数: memcmp

  • bcopy, 建议替换函数: 如果 *附录 K* 可用,则为 memcpy_s,否则为 memcpy

  • bzero, 建议替换函数: 如果 *附录 K* 可用,则为 memset_s,否则为 memset

  • getpw, 建议替换函数: getpwuid

  • vfork, 建议替换函数: posix_spawn

虽然在相关的 CERT 规则中提到,但以下函数会被此检查 **忽略**

atof, atoi, atol, atoll, tmpfile.

*附录 K* 的可用性是根据以下宏确定的

  • __STDC_LIB_EXT1__: 功能宏,指示库实现中是否存在 *附录 K. “边界检查接口”*

  • __STDC_WANT_LIB_EXT1__: 用户定义宏,指示用户请求定义 *附录 K.* 中的函数。

这两个宏都必须定义才能建议使用 *附录 K.* 中的替换函数。 __STDC_LIB_EXT1__ 由库实现定义,并且 __STDC_WANT_LIB_EXT1__ 必须由用户定义为 1,**在**包含任何系统头文件之前。

自定义函数

选项 CustomFunctions 允许用户定义要检查的自定义函数。格式如下,不包含换行符

bugprone-unsafe-functions.CustomFunctions="
  functionRegex1[, replacement1[, reason1]];
  functionRegex2[, replacement2[, reason2]];
  ...
"

这些函数使用 POSIX 扩展正则表达式进行匹配。(注意:正则表达式不支持负向 (?!) 匹配。)

*reason* 是可选的,用于提供有关替换原因的额外信息。默认原因是 *被标记为不安全*。

如果 *replacement* 为空,则将显示 *它不应该被使用* 而不是替换建议。

例如,配置 *^original$, replacement, is deprecated;* 将产生以下诊断消息。

original(); // warning: function 'original' is deprecated; 'replacement' should be used instead.
::std::original(); // no-warning
original_function(); // no-warning

如果正则表达式包含字符 *:*,则它将与限定名称(即 std::original)匹配,否则正则表达式将与非限定名称(original)匹配。如果正则表达式以 *::* (或 *^::*)开头,则它将与完全限定名称(::std::original)匹配。

选项

ReportMoreUnsafeFunctions

如果为 *true*,则会将来自广泛使用的 API(如 POSIX)的更多函数添加到要报告的函数列表中。有关此选项启用了哪些函数的完整列表,请参阅检查的主要文档。默认值为 *true*。

ReportDefaultFunctions

如果为 *true*,则检查会报告默认的函数集。如果您只想查看通过 自定义函数 匹配的自定义函数,请考虑将此设置更改为 false。默认值为 *true*。

CustomFunctions

要匹配的自定义函数的用分号分隔的列表。匹配的函数包含一个正则表达式、一个可选的替换函数名称和一个可选的原因,用逗号分隔。有关更多信息,请参阅 自定义函数

示例

#ifndef __STDC_LIB_EXT1__
#error "Annex K is not supported by the current standard library implementation."
#endif

#define __STDC_WANT_LIB_EXT1__ 1

#include <string.h> // Defines functions from Annex K.
#include <stdio.h>

enum { BUFSIZE = 32 };

void Unsafe(const char *Msg) {
  static const char Prefix[] = "Error: ";
  static const char Suffix[] = "\n";
  char Buf[BUFSIZE] = {0};

  strcpy(Buf, Prefix); // warning: function 'strcpy' is not bounds-checking; 'strcpy_s' should be used instead.
  strcat(Buf, Msg);    // warning: function 'strcat' is not bounds-checking; 'strcat_s' should be used instead.
  strcat(Buf, Suffix); // warning: function 'strcat' is not bounds-checking; 'strcat_s' should be used instead.
  if (fputs(buf, stderr) < 0) {
    // error handling
    return;
  }
}

void UsingSafeFunctions(const char *Msg) {
  static const char Prefix[] = "Error: ";
  static const char Suffix[] = "\n";
  char Buf[BUFSIZE] = {0};

  if (strcpy_s(Buf, BUFSIZE, Prefix) != 0) {
    // error handling
    return;
  }

  if (strcat_s(Buf, BUFSIZE, Msg) != 0) {
    // error handling
    return;
  }

  if (strcat_s(Buf, BUFSIZE, Suffix) != 0) {
    // error handling
    return;
  }

  if (fputs(Buf, stderr) < 0) {
    // error handling
    return;
  }
}