`
java-mans
  • 浏览: 11446269 次
文章分类
社区版块
存档分类
最新评论

代码测试之内存越界

 
阅读更多

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】


内存越界是我们软件开发中经常遇到的一个问题。不经意间的复制常常导致很严重的后果。经常使用memset、memmove、strcpy、strncpy、strcat、sprintf的朋友肯定对此印象深刻,下面就是我个人在开发中实际遇到的一个开发问题,颇具典型。

#define MAX_SET_STR_LENGTH  50
#define MAX_GET_STR_LENGTH 100

int* process(char* pMem, int size)
{
	char localMemory[MAX_SET_STR_LENGTH] = {0};
	int* pData = NULL;

	/*  code process */
	memset(localMemory, 1, MAX_GET_STR_LENGTH);
	memmove(pMem, localMemory, MAX_GET_STR_LENGTH);
	return pData;
}

这段代码看上去没有什么问题。我们本意是对localMemory进行赋值,然后拷贝到pMem指向的内存中去。其实问题就出在这一句memset的大小。根据localMemory初始化定义语句,我们可以看出localMemory其实最初的申明大小只有MAX_SET_STR_LENGTH,但是我们赋值的时候,却设置成了MAX_GET_STR_LENGTH。之所以会犯这样的错误,主要是因为MAX_GET_STR_LENGTH和MAX_SET_STR_LENGTH极其相似。这段代码编译后,产生的后果是非常严重的,不断冲垮了堆栈信息,还把返回的int*设置成了非法值。

那么有没有什么好的办法来处理这样一个问题?我们可以换一个方向来看。首先我们查看,在软件中存在的数据类型主要有哪些?无非就是全局数据、堆数据、栈临时数据。搞清楚了需要控制的数据之后,我们应该怎么对这些数据进行监控呢,一个简单有效的办法就是把memset这些函数替换成我们自己的函数,在这些函数中我们严格对指针的复制、拷贝进行判断和监督。

(1)事实上,一般来说malloc的数据是不需要我们监督的,因为内存分配的时候,通常库函数会比我们要求的size多分配几个字节,这样在free的时候就可以判断内存的开头和结尾处有没有指针溢出。朋友们可以试一下下面这段代码。

void heap_memory_leak()
{
	char* pMem = (char*)malloc(100);
	pMem[-1] = 100;
	pMem[100] = 100;
	free(pMem);
}
pMem[-1] = 100是堆左溢出, pMem[100]是堆右溢出。

(2)堆全局数据和栈临时数据进行处理时,我们利用memset初始化记录全局指针或者是堆栈临时指针

a) 首先对memset处理,添加下面一句宏语句

#define memset(param, value, size) MEMORY_SET_PROCESS(__FUNCTION__, __LINE__, param, value, size)


b) 定义内存节点结构

typedef struct _MEMORY_NODE
{
	char functionName[64];
	int line;
	void* pAddress;
	int size;
	struct _MEMORY_NODE* next;

}MEMORY_NODE;

其中functionName记录了函数名称,line记录文件行数, pAddress记录了指针地址, size指向了pAddress指向的内存大小,next指向下一个结构节点。


c)记录内存节点属性

在MEMORY_SET_PROCESS处理过程中,不仅需要调用memset函数,还需要对当前内存节点进行记录和保存。可以通过使用单链表节点的方法进行记录。但是如果发现pAddress指向的内存是malloc时候分配过的,此时就不需要记录了,因为堆内存指针溢出的问题lib库已经帮我们解决了。


d)改造原有内存指针操作函数

比如对memmove等函数进行改造,不失去一般性,我们就以memmove作为范例。

添加宏语句 #define memmove(dst, src, size) MEMMOVE_PROCESS(dst, src, size)

void MEMMOVE_PROCESS(void* dst, const void* src, int size)
{
	MEMORY_NODE* pMemNode = check_node_exist(dst);
	if(NULL == pMemNode) return;

	assert(dst >= (pMemNode->pAddress));
	assert(((char*)dst + size) <= ((char*)pMemNode->pAddress + pMemNode->size));
        memmove(dst, src, size);
	return;
}


e)下面就是内存节点的删除工作。

我们知道函数是需要反复使用堆栈的。不同时间相同的堆栈地址对应的是完全不同的指针内容,这就要求我们在函数返回的时候对内存地址进行清理,把内存节点从对应的链表删除。

我们知道在函数运行后,ebp和esp之间的内存就是通常意义上临时变量的生存空间,所以下面的一段宏就可以记录函数的内存空间。

#ifdef MEMORY_LEAK_TEST
#define FUNCTION_LOCAL_SPACE_RECORD()\
{\
	int* functionBpRecord = 0;\
	int*  functionSpRecord = 0;\
}
#else
#define FUNCTION_LOCAL_SPACE_RECORD()
#endif

#ifdef MEMORY_LEAK_TEST
#define FUNCTION_LEAVE_PROCESS()\
{\
__asm { mov functionBpRecord, bp\
    mov functionSpRecord, sp}\
	FREE_MEMORY_NODE(functionBpRecord, functionSpRecord)\
}
#else
#define FUNCTION_LEAVE_PROCESS()
#endif

这两段宏代码,需要插在函数的起始位置和结束的位置,这样在函数结束的时候就可以根据ebp和esp删除堆栈空间中的所有内存,方便了堆栈的重复使用。如果是全局内存,因为函数的变化不会导致地址的变化,所以没有必要进行全局内存节点的处理。

内存溢出检查流程总结:

(1)对memset进行重新设计,记录除了malloc指针外的一切内存;

(2)对memmove, strcpy, strncpy,strcat,sprintf等全部函数进行重新设计,因为我们需要对他们的指针运行范围进行判断;

(3)在函数的开头和结尾位置添加宏处理。函数运行返回前进行节点清除。


(全文完)




分享到:
评论

相关推荐

    Linux之内存泄漏检测valgrind-memcheck. 附件测试代码.cpp

    【1】文章附件代码:https://blog.csdn.net/weixin_43780617/article/details/131805359?spm=1001.2014.3001.5501 ...3. 动态内存越界; 4. 堆内存泄漏,没有成对使用malloc/free和new/delete; 5.两次释放内存;

    Checked C是对C的扩展,它添加了检查以检测或防止常见的编程错误(例如缓冲区溢出和越界内存访问)的检查。 此仓库有一个用于Checked C,示例代码,规范和测试代码的Wiki。-C/C++开发

    Checked C Checked C向C添加了静态和动态检查,以检测或防止常见的编程错误,例如缓冲区溢出和越界内存访问。 该项目的目标是通过maki改进系统编程。Checked C Checked C向C添加了静态和动态检查,以检测或防止常见...

    嵌入式内存测试

    LINUX操作系统下有关嵌入式方面的算法“一种用于嵌入式内存检查的高效诊断算法”

    SQLite-XXTEA加密-源代码

    顺便修改了网上流传的XXTEA代码的一处内存越界的BUG。 用SQLite的一般是单机版软件比较多,有加密需求的一定不少,所以现在放出源代码与大家分享。代码的工程文件是用VC2005,如果用其它编译器,编译的时候不要忘记...

    静态代码扫描工具TscanCode.zip

    应用特性空指针检查,包含可疑的空指针,判空后解引用比如Crash等共3类subid检查数据越界,Sprintf_S越界共1类subid检查内存泄漏,分配和释放不匹配同1类subid检查逻辑错误,重复的代码分支,bool类型和INT进行比较...

    checkedc:Checked C是C的扩展,增加了检查以检测或防止常见的编程错误(例如缓冲区溢出和越界内存访问)的检查。 此仓库有一个Checked C,示例代码,规范和测试代码的Wiki。

    checkedc:Checked C是C的扩展,增加了检查以检测或防止常见的编程错误(例如缓冲区溢出和越界内存访问)的检查。 此仓库有一个Checked C,示例代码,规范和测试代码的Wiki。

    SourceInsight代码静态扫描插件

    支持空指针,越界,逻辑错误,内存泄漏,可疑代码等大类,极其对应细化规则扫描,轻松帮您发现潜在的质量风险。 3.静态集成 无需编译,直接静态扫描,集成在IDE中,轻松点击启动扫描,片刻即可查看结果。 4.高效准确 ...

    vld-2.3-资源泄漏检测

    内存泄漏、内存越界是最常见的内存问题之一。 内存泄漏如果不是很严重的话,在短时间内对程序不会造成太大的影响,而且在进程终止的时候,所有分配的内存都会释放掉。但是对于长时间运行的程序,其破坏力是惊人的,...

    C/C++源代码静态检测系统的设计和实现

    为了检测出C/C++源代码程序中常见的运行时错误,设计了一个静态检测系统。...通过检测程序对结点的属性值的分析,能够检测出C/C++源代码程序中出现的数组越界、指针错误、字符串函数错误,内存泄露等问题。

    一个动态内存管理模块的实现

    再如“写内存越界”,一种不合法的写内存操作,极可能破坏到本程序中正在使用的其它数据,严重的时候还可能对其它正在运行的程序甚至整个系统造成影响。为此,本文介绍一个增强的、可定制的动态内存管理模块

    TscanCode超好用的静态代码扫描工具,支持C++/C#/Lua语言,源码及编译好的安装包

    数据越界,Sprintf_S越界共1类subid检查 内存泄漏,分配和释放不匹配同1类subid检查 逻辑错误,重复的代码分支,bool类型和INT进行比较,表达式永远True或者false等共18类检查 可疑代码检查,if判断中含有可疑的=号...

    SQLite-XXTEA加密-DLL

    顺便修改了网上流传的XXTEA代码的一处内存越界的BUG。 用SQLite的一般是单机版软件比较多,有加密需求的一定不少,所以现在放出源代码与大家分享。代码的工程文件是用VC2005,如果用其它编译器,编译的时候不要忘记...

    内存泄漏check

    Cppcheck是一种C/C++代码缺陷静态检查工具。不同于C/C++编译器及其它分析工具,Cppcheck只检查编译器检查不出来的bug,不检查语法错误。... 能对一些常见,容易被我们忽视的内存泄露,数组越界访问等进行检测。

    C语言常见的100多条bug汇总

    1. 内存泄漏 3 2. 数组越界 3 3. 空指针解引用 4 4. 类型转换错误 4 ...35. 忽视测试与调试 22 37. 忽视代码复用 23 38. 滥用宏定义 23 39. 忽视编译器警告 23 40. 不关注代码风格一致性 24 42. 不合理的代码组织 24

    PClint安装及配置方法

    比如代码缩进格式、case语句书写规范、函数声明和布尔表达式的编写规则等,而PC-Lint则偏重于代码的逻辑分析,它能够发现代码中潜在的错误,比如数组访问越界、内存泄漏、使用未初始化变量等。本文将介绍如何安装和...

    pc-lint 经验(中文)

    静态代码检查工具PC-Lint则偏重于代码的逻辑分析,它能够发现代码中潜在的错误,比如数组访问越界、内存泄漏、使用未初始化变量等。本文将介绍如何安装和配置PC-Lint代码检查工具以及如何将PC-Lint与常见的代码编辑...

    klocwork操作步骤 以及介绍

    (详细参见附录): C/C++缺陷类型样例 空指针释放 内存管理问题(如:内存泄漏) 数组越界 未初始化数据使用 编码风格问题(如:在条件中赋值) Java 缺陷类型样例 效率错误(如:空的 finalize 方法) 可维护性...

    离线OJ判题系统

    评判结果包括:Accepted(测试通过)、Compile Error(编译失败)、Memory Limit Exceed(内存超出限制)、Presentation Error(格式错误)、Runtime Error(运行时错误,可能是数组越界,改写只读的内存,除零,栈...

    PClint9.0i程序+补丁+经验教程

    PCLINT是一种代码检查工具,能帮助你发现很多不易发现,而且编译器检测不出的...如内存越界等。包括微软、华为在内的很多大公司都用它做代码检查。 本资料包含了PCLint9.0i的程序加补丁,还有安装和使用的经验教程。

    Qualcomm平台BREW开发技术文档

     内存越界 109  内存问题的建议 109  采取的措施 109  OEM层不应该处理UI的事情 109  文件操作注意 109  不能同时对一个文件进行操作 109  树型文件夹问题 109  系统USB文件目录 110  T卡文件目录...

Global site tag (gtag.js) - Google Analytics