C++|VS2019: 进一步新增了C++代码分析规则( 二 )


const auto& colorInfo = ColorTable[color
;
在网上可以找到许多有关将枚举值作为数组索引的讨论 。在许多情况下 , 这确实很有意义 。
通常 , 当开发人员将枚举类型的枚举器用作数组的索引时 , 他们知道枚举类型的枚举器的值从零开始到已知的最大值 , 且增量为1 , 并且任何一对连续的行之间没有间隔 。
因此 , 大多数开发人员认为对照已知的最大值来检查枚举值将可以确保值的有效性 。
但是 , 使用枚举器作为数组索引不是很安全 。 不幸的是 , 似乎没有太多关于为什么它可能很危险的讨论 。
让我们看一个例子 。 考虑下面的枚举和一个函数指针表 , 我们要使用该枚举值作为索引:

现在 , 在源文件中 , 我们使用枚举的枚举数作为函数指针表的索引 , 定义一个从表中选择函数的工具函数:

上面的代码的逻辑看起来挺清晰的 。 为了调用者传入错误的参数 , 我们将枚举的值与FunctionId的已知最大值进行比较 , 以免导致函数访问超出其范围的表 。我们还知道 , FunctionId枚举类型的枚举值将从零开始 , 以一个增量递增 , 并以[FunctionId::FunctionCount – 1
结束 , FunctionCount是枚举中的最后一个枚举值 。
让我们继续添加更多使用这个工具函数的代码 。 我们的客户代码将具有整数值作为函数的选择器 , 并希望我们通过函数返回整数值:

如上所述 , 我们需要进行强制转换 , 以将函数表索引的整数值转换为枚举类型 , 以传递给GetFunction 。 这将确保将int值正确转换为FunctionId枚举的枚举值 。
希望到目前为止 , 一切都很好 。
现在 , 让我们考虑这样一个调用GetValue函数:

在上面的代码中 , -1是从哪里来的呢?
对于此讨论 , 这并不重要 。 假设它来自用户的输入 。 无论如何 , 这显然是错误的 。 但是 , 即使使用/ Wall编译选项 , 我们也不会从编译器那里获得任何有关此调用潜在问题的提示 。 实际上 , 考虑到所涉及的类型及其使用方式 , 没有什么是“错误的” 。 但是我们知道这是错误的 。GetFunction是否真的保护自己免受此调用的侵害?一个简单的答案是 – 不 。
问题是 , 你可以将任何int值强制转换为枚举类型 , 并且枚举的基础类型默认为int值(带符号的int) 。 对于带符号的值 , 如果您检查上限但不检查其下限 , 则最终将允许使用负值 。 在上面的示例中 , 它最终调用了危险的DoNotCallMe函数 , 该函数恰好在函数指针表之前 。 在现实生活中 , 这种错误可能导致可利用的安全漏洞 。
开发者检查索引下限的可能性较小 , 但忘记检查上限 。 但是 , 通过允许超出数组范围的访问 , 这也可能导致相同的问题 。
只是为了好玩 , 运行上面的示例会为我产生以下输出:

EnumIndex扩展EnumIndex扩展可以发现如上所示的潜在问题 , 并通过以下警告进行报告:
> C33010:未选中枚举\"enum\"的下限 , 用作索引 。
> C33011:枚举\"enum\"的未选中的上限用作索引 。
C33010警告如果检查上限(而不是下限)的值 , 则将对用作数组索引的枚举触发此警告 。
这是一个简化的例子:

还可以通过检查索引值的下限来更正这些警告:

C33011警告如果检查了下限值而不是上限值 , 则将对用作数组索引的枚举触发此警告 。
这是一个简化的例子:

通过检查索引值的上限也可以更正这些警告:

在Visual Studio中启用EnumIndex规则
通过为项目选择不同的规则集 , 可以按如下所示在Visual Studio中启用EnumIndex规则:

使用布尔值作为HRESULT尽管可能不是故意的 , 但我们已经看到了将布尔值用作HRESULT值的代码 , 反之亦然 。C/C++允许在它们之间进行隐式转换 , 并且编译器不会对这些隐式转换发出警告 。但是 , 布尔值和HRESULT具有不同的语义 , 因此不能互换使用 。


推荐阅读