遗留MUD代码free_mem函数魔数校验疑问及内存回收报错排查
MUD内存管理器报错与校验机制解析
问题背景
我正在维护一个遗留MUD代码库,运行时触发如下报错:
Attempt to recycle invalid memory of size 24.
对应内存释放函数代码如下,我有两个疑问:
- 该代码如何确保
pMem指向的内存值始终为MAGIC_NUM? - 内存地址不是随机的吗,这种校验机制为何能正常工作?
/* * Give a GP block back to the memory manager for recycling. */ void free_mem(void *pMem, size_t sMem) { int iList; #ifdef MAGIC_CHECKING int *magic; pMem = (void *)(((char *)pMem) - sizeof(*magic)); magic = (int *)pMem; #ifndef MAGIC_NUM #define MAGIC_NUM 52571214 #endif if (*magic != MAGIC_NUM) { bug("Attempt to recyle invalid memory of size %d.", sMem); bug((char *)pMem + sizeof(*magic), 0); return; } *magic = 0; sMem += sizeof(*magic); #endif for (iList = 0; iList < MAX_MEM_LIST; iList++) if (sMem <= rgSizeList[iList]) break; if (iList == MAX_MEM_LIST) { bug("Free_mem: size %d too large.", sMem); exit(EXIT_FAILURE); } *((void **)pMem) = rgFreeList[iList]; rgFreeList[iList] = pMem; return; }
解答
1. 如何保证校验位始终为MAGIC_NUM?
这个校验逻辑完全依赖配套内存分配函数(比如alloc_mem)的实现:
- 当用户申请
sMem大小的内存时,分配器实际会申请sMem + sizeof(int)的内存块 - 在内存块的起始位置(也就是用户最终拿到的指针往前偏移
sizeof(int)的位置)写入MAGIC_NUM - 最后返回给用户的指针是「内存块起始地址 + sizeof(int)」,相当于跳过了这个校验位
free_mem里的pMem = (void *)(((char *)pMem) - sizeof(*magic));就是把用户传入的指针往回偏移,找到这个预存的校验位。只有通过这套分配器申请的内存,才会在这个固定位置有MAGIC_NUM,以此确保要释放的是合法内存块。
2. 内存地址随机为何不影响校验?
内存地址随机(ASLR)是指程序虚拟地址空间的起始地址随机化,但这个校验机制和绝对地址无关:
- 校验位的位置是相对于用户传入指针的固定偏移(往前一个
int长度),不管整个内存块的绝对地址在哪,这个相对位置永远固定 - 校验时只需要检查这个相对位置的值,和内存块的绝对地址没有关系,所以地址随机化不会干扰校验逻辑
报错的常见原因
出现这个错误通常是以下情况之一:
- 传入了非本内存管理器分配的指针(比如用标准库
malloc分配的内存,却调用free_mem释放) - 内存块被重复释放:第一次释放时,代码会把校验位设为
0(*magic = 0;),第二次释放时就会触发校验失败 - 内存越界写入:用户拿到内存指针后,往指针前面的区域写数据,破坏了预存的
MAGIC_NUM - 使用了野指针:传入的指针指向已经被回收或从未分配的内存区域
内容的提问来源于stack exchange,提问作者BrainFRZ
相关产品推荐
相关产品推荐

