CS.ABV.EXCEPT
尝试使用超出边界的数组索引时,发生 IndexOutOfRange 异常
与 C 和 C++ 不同,C# 语言具有内置的保护机制,可防止对在托管堆上分配的数组和容器进行超出边界的读取和写入。在采取保护的情况下,尝试读取或写入超出边界的内存将不会成功;然而,使用无效的访问索引将导致数组访问操作符引发 IndexOutOfRangeException。
CS.ABV.EXCEPT 检查器会检查索引访问操作,并且标记索引值可能无效的情况。
漏洞与风险
虽然在托管堆上分配的数组不可能发生缓冲区溢出,但是未处理或未正确处理的异常可能导致程序终止或者以意外的方式修改控制流。即使 IndexOutOfRange 异常得到正确的处理,索引值超出数组边界的可能性也强烈地表明存在可能导致不正确的程序结果的逻辑错误。
漏洞代码示例 1
namespace n {
class C
{
void foo()
{
int[] A = new int[5];
int ind = 4;
++ind;
A[ind] = 10; // CS.ABV.EXCEPT
}
}
}
在此基本示例中,分配了大小为 5 的数组 A,并使用索引 ind(在访问站点处等于 5)来引用数组元素。如果执行此代码,将在运行时引发 IndexOutRangeException。Klocwork 将此问题报告为 CS.ABV.EXCEPT 缺陷。
修正代码示例 1
namespace n {
class C
{
void foo()
{
int[] A = new int[6];
int ind = 4;
++ind;
A[ind] = 10;
}
}
}
通过验证数组访问逻辑的正确性,可以修正该问题。在以上修正代码示例中,调整了数组大小,以确保访问索引在数组边界内。
漏洞代码示例 2
namespace n
{
class C
{
static void Main(string[] args)
{
int[] E = { 1, 2, 3, 4, 5, 6, 7, 8, 9 , 10 };
int ind1 = 5;
for (int i = 0; i < args.Length; i++)
{
++ind1;
if (i == 10)
{
break;
}
}
E[ind1] = 0; // CS.ABV.EXCEPT
}
}
}
在此示例中,使用了大小为 10 的隐式类型数组来初始化 E。根据传递给访问索引的 Main 值的 args 的长度,循环中可能存在多达 11 次迭代,可导致 ind1 的值为 [5;16] 范围内的任意值。Klocwork 将此问题报告为 CS.ABV.EXCEPT 缺陷,这表示可能的访问索引范围中会导致引发 IndexOutRangeException 的部分。
修正代码示例 2
与示例 1 类似,通过仔细评估可能的索引值和数组大小,可以解决该问题。
namespace n
{
class C
{
static void Main(string[] args)
{
int[] E = { 1, 2, 3, 4, 5, 6, 7, 8, 9 , 10 };
int ind1 = 5;
for (int i = 0; i < args.Length; i++)
{
++ind1;
if (i == 3) // Fixed here in this example.
{
break;
}
}
E[ind1] = 0;
}
}
}
在此修正代码示例中,可保证 ind1 的值在数组边界内。
限制
在 Klocwork 2021.3 中,对于值类型元素的数组,CS.ABV.EXCEPT 仅支持数组访问的过程内分析。