• 记一次HEAP CORRUPTION DETECTED问题及解决


    最近接手了一个同事的代码,主要是实现对C++ SDK的封装,从而提供给Unity客户调用,我们姑且称之为C# SDK。

    该C# SDK提供了一个示例demo,大概就是从摄像头获取数据,然后在其上绘制人体关键点,并渲染出来。该demo分为Unity和Winform两个版本,功能相同,细节略有不同。

    问题

    问题来了,同样的代码,在Winform中表现正常,在Unity中很容易出现下面这个问题:

    在这里插入图片描述

    排查

    经过一番努力之后,大概定位到这部分代码:

    //获取图像,通过new unsigned char[buf_sz]
    IntPtr pRgb = NativeMethods.Raw16ToRgb24(fg.foregroundData, nw, nh);
    ...
    //在RGB图像上绘制小矩形
    NativeMethods.Rgb24DrawRect(pRgb, nw, nh, rect.x, rect.y, 5, 5, 255, 255, 255, 0);
    ...
    //释放图像,通过delete [] p
    NativeMethods.DeleteMemPointer(pRgb);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    该代码调用C++ SDK中的一个绘制函数,在RGB图像上画一个5x5像素的矩形。

    起初走了弯路,因为Winform中表现OK,所以认为是Unity平台的特性导致,感觉很头大,无从下手。
    后来检索搜索引擎,看到这样一个类似的问题:
    What does “CRT detected that the application wrote to memory after end of heap buffer” mean?
    注意到其中的一个答案:
    在这里插入图片描述

    这似乎解释了上面的两种不同表现,并且类似的是同样在释放内存出现的错误。带着这两个假设,决定跳过C#的部分,直接看C++代码,写了这样一个测试snippet:

    	void debug_heap_corrupt(){
    		const auto w = 320;
    		const auto h = 240;
    		unsigned char buf[] = { 0, 0, 0 };
    		for (int i = 0; i < w; i++) {
    			for (int j = 0; j < h; j++)
    			{
    				unsigned char* pRgb = new unsigned char[w * h * 3];
    				Rgb24DrawRect_(pRgb, w, h, i, j, 5, 5, buf, 0);
    				delete[] pRgb;
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    幸运的是,在VS2017/x64中,得到了相同的现象:
    在这里插入图片描述
    并且在VS的Debug模式帮助下,得到了此时传递给绘制函数的值:i=316, j=235。这里有一部分值组合可以触发。

    解决

    下面就是解决这个问题了,此时断定就是内存越界修改。看Rgb24DrawRect_函数,注意到其中对内存的修改,有这么两个循环。每个循环都是将特定位置的像素,修改为指定的值:

    int Rgb24DrawRect_(unsigned char* pRgb, int nw, int nh, int nx, int ny, int nWRect, int nHRect, unsigned char* pColorBuf, int nAlign){
    	//loop1
    	for (j = 0, k = 0; j < m; j++, k += 3)
    	{
    		memcpy(pPos + k, pColorBuf, 3);
    	}
    	
    	//loop2
    	for (i = 1; i < n; i++)
    	{
    		memcpy(pCur, pPos, k);
    		pCur += cbLine;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我这里为了快速解决,就采用简单的方式处理了,在修改内存之前,对目的地址进行判断,确保其落在整个RGB图像unsigned char数组范围内,下面是修改后的代码:

    int Rgb24DrawRect_(unsigned char* pRgb, int nw, int nh, int nx, int ny, int nWRect, int nHRect, unsigned char* pColorBuf, int nAlign){
    
    #ifdef PTR_SAFETY_CHECK
    	//计算RGB图像的安全范围
    	const auto * const pRgbEnd = pRgb + nw * nh * 3;
    #endif
    
    	//loop1
    	for (j = 0, k = 0; j < m; j++, k += 3)
    	{
    #ifdef PTR_SAFETY_CHECK
    		if(pPos + k + 3 <= pRgbEnd )
    #endif
    		memcpy(pPos + k, pColorBuf, 3);
    	}
    	
    	//loop2
    	for (i = 1; i < n; i++)
    	{
    #ifdef PTR_SAFETY_CHECK
    		if (pCur + k <= pRgbEnd)
    #endif
    		memcpy(pCur, pPos, k);
    		pCur += cbLine;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    附:不同平台运行时表现

    Unity(Windows)WinformVS2017(C++)g++ 7.4.0NDK r11c
    报错无报错报错无报错无报错
  • 相关阅读:
    【C++编程语言】之C++对象模型和this指针
    理解SpringBoot的自动装配
    代码随想录二刷day42
    Java到底能干什么?
    Linux| jq命令对JSON格式数据操作
    root权限无法创建文件夹解决
    艾美捷QuickTiter 逆转录病毒定量试剂盒的制备方案
    Python线程和进程
    Kubernetes - pod详解
    抽象方法与设计模式
  • 原文地址:https://blog.csdn.net/liuzonrze/article/details/126406905