在C/C++中防止缓冲区溢出

3

我经常遇到缓冲区溢出的问题。

int y[10][10][10];

...

y[0][15][3] = 8;

我该如何防止这个问题的发生?有没有好用的工具可以帮助我?


1
如果您确定使用的编程语言,您将获得更好的答案。C和C ++是非常不同的编程语言。 - avakar
我希望我能够编辑评论...当然应该是“你的想法”。 - avakar
@avakar:只需删除您的评论,然后重新添加即可。 - RichieHindle
3
在您的示例中没有发生溢出。10x10x10 = 1000,因此您有一个包含1000个整数的数组。索引[0][15][3]是第154个整数(15*10 + (3+1) = 154),因此您在边界内。 - qrdl
我同时使用C和C++。问题出在原始的C数组上,就像这个例子一样。你说得对,这不是整个多维数组的缓冲区溢出,但如果我将一个数组定义为10x10x10,我不想超过数组的单个维度。谢谢。最好的问候。 - luca_c
是的,说真的,我在这里没有看到缓冲区溢出! - Steve Obbayi
5个回答

10

Neil的回答在一般情况下更好,但如果你有使用普通数组的理由,你可以使用函数来获取和设置值,并检查是否在数组边界内:

#define MAX_INDEX 10

int y[MAX_INDEX][MAX_INDEX][MAX_INDEX];

int get_y(int a, int b, int c)
{
    ASSERT(a >= 0 && a < MAX_INDEX);
    ASSERT(b >= 0 && b < MAX_INDEX);
    ASSERT(c >= 0 && c < MAX_INDEX);
    return y[a][b][c];
}

void set_y(int a, int b, int c, int value)
{
    ASSERT(a >= 0 && a < MAX_INDEX);
    ASSERT(b >= 0 && b < MAX_INDEX);
    ASSERT(c >= 0 && c < MAX_INDEX);
    y[a][b][c] = value;
}

最好将所有内容都封装在一个类中。


挑刺一下,除非你已经为它们编写了特定的代码,否则 ASSERT 在嵌入式系统中不会按你期望的方式工作。但在大多数情况下我同意。 - Paul Nathan

10
不要使用原始的C风格数组。相反,使用C++容器类,如std::vector,它们具有检查无效访问并在发生时引发异常的能力。
此外,您所描述的实际上并不是缓冲区溢出。

很多时候我编写的是嵌入式系统代码,只能使用原始数组。谢谢。 - luca_c

1

除了其他评论之外,您还可以查看此线程中提出的建议,该线程涉及静态代码分析工具:

C / C ++ 免费替代Lint?


1

代码层面的解决方案

在C++中,一个解决方案是永远不要使用数组,而是使用C++容器。例如,如果您使用at而不是[]进行索引,则向量具有越界检测功能。

在C语言中,您应该始终设计函数,使您提供数组的指针和维数,没有其他方法。

工具层面的解决方案

用于检查越界访问的好工具是valgrind。它通过运行未更改的二进制文件来工作,并且如果您使用调试信息编译,则可以给出发生错误的精确行号。Valgrind适用于许多Unix系统,包括Mac OS X。

请注意,valgrind并不总是能够检测到这些错误访问(在您的示例中,假设它是真正的越界访问,则由于变量位于堆栈上而不是堆上,因此valgrind将无法发现它)。


即使在堆上,Valgrind也无法找到这个问题,因为访问是在整个数组内部(正如@qrdl在对问题的评论中指出的那样)。 - RichieHindle
那是“假设它是真正的越界访问”的意思。 - David Cournapeau

0
你可以动态地从堆中分配数据成员,而不是通过静态分配的数组。前一种方法在使用fgets()时有优点和缺点,后一种方法。虽然前者避免了缓冲区溢出,但编程困难,并且需要精确的精度来避免内存泄漏。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接