访问检查通过监视每个读取、写入、分配和释放操作来检查程序是否正确访问内存。
程序可能会以各种方式错误读取或写入内存,这些都称为内存访问错误。例如,程序可能会引用已通过 free() 调用针对堆块释放的内存块。另外,函数可能会将指针返回给局部变量,这样,访问该指针时,便会出现错误。访问错误可能会导致程序中指针混乱,并可导致程序行为异常,包括输出错误和段违规。某些类型的内存访问错误很难跟踪。
运行时检查保存有一个跟踪程序使用的每个内存块状态的表。运行时检查会将每个内存操作与其涉及的内存块的状态进行对照,然后确定相应操作是否有效。可能的内存状态包括:
未分配,初始状态。尚未分配内存。读取、写入或释放此内存是非法操作,因为它不归程序所有。
已分配,但尚未初始化。已为程序分配了内存,但尚未初始化此内存。写入或释放此内存是合法操作,但读取是非法操作,因为尚未初始化此内存。例如,输入函数时,已分配局部变量的栈内存,但未初始化此内存。
只读。读取只读内存是合法操作,但写入或释放只读内存是非法操作。
已分配且已初始化。读取、写入或释放已分配且已初始化的内存是合法操作。
使用运行时检查来查找内存访问错误与使用编译器查找程序中的语法错误没有什么不同。在这两种情况下,都会生成错误列表,并提供与每个错误对应的错误消息,说明出错原因和程序中的出错位置。在这两种情况下,应该从错误列表的顶部开始依次向下修复程序中的错误。在链锁反应下,一个错误可导致其他错误发生。因此,链中的第一个错误是“首要原因”,修复该错误后,便可能会修复一些后续的错误。
例如,从未初始化的内存区进行读取会创建不正确的指针,这样,取消其引用时,便会导致出现其他无效的读取或写入,而这又会导致出现另一个错误。
|
以下示例显示的是一个典型的访问错误。
Read from uninitialized (rui): Attempting to read 4 bytes at address 0xefffee50 which is 96 bytes above the current stack pointer Variable is ”j’ Current function is rui 12 i = j;
rui(请参见从未初始化的内存中读 (rui) 错误)
rua(请参见从未分配的内存中读 (rua) 错误)
rob(请参见从数组越界中读 (rob) 错误)
wua(请参见写入到未分配内存 (wua) 错误)
wro(请参见写入到只读内存 (wro) 错误)
wob(请参见写入到数组越界内存 (wob) 错误)
mar(请参见未对齐读 (mar) 错误)
maw(请参见未对齐写 (maw) 错误)
duf(请参见重复释放 (duf) 错误)
baf(请参见错误释放 (baf) 错误)
maf(请参见未对齐释放 (maf) 错误)
oom(请参见内存不足 (oom) 错误)
注 - 在 SPARC 平台上,运行时检查不执行数组边界检查,因此不会将数组边界违规按访问错误来报告。