暴雨天,写着玩。

野指针是令人讨厌的,很容易导致莫名其妙的踩内存错误,那么如何判断一个指针是不是野指针呢?

一般而言,用户使用的内存在MMAP_THRESHOLD(一般为128k)两边,分别由malloc和mmap管理:

* malloc:C库的内存管理机制,一般用于小块内存管理。
* mmap:操作系统提供的内存管理机制,一般用于大块内存管理。
这里不谈mmap,因为操作系统提供的机制太容易检测了。难的是C库的机制:

* 内存在操作系统管理之外,完全自治,无法享受操作系统的段错误提示…


只好通过C库malloc元数据来detect咯,比方说你可以man -k
malloc,获取很多malloc内存管理内部的一些信息,但是那没意思,照本宣科而已,不如自己手工来一点点地发现。

给出一个弱爆了的代码:
#include <stdlib.h> #include <stdio.h> int main(int argc, char **argv) { char *
p1, *p2, *p3, *p4; int i, j; unsigned char *p; p1 = calloc(1, 32); memset(p1,
'c', 32); p2 = calloc(1, 32); //
如果在这里alloc一个128的,那么由于切断了相同size的block,在free之后,就会同时出现prev,next两个指针。 memset(p2, 'c'
, 32); p3 = calloc(1, 32); memset(p3, 'c', 32); p4 = calloc(1, 32); memset(p4,
'c', 32); // 观察内存地址的规律,发现间隔是32+16 printf("%p %p %p %p\n", p1, p2, p3, p4); p =
p1; // 猜测base-16处存有元数据 p -= 16; // 打印探测究竟 for (i = 0; i < 48*4; ) { for (j = 0;
j< 16; j++) { printf("%02x ", p[i++]); } printf("\n"); } printf("\n"); printf(
"\n"); free(p1); free(p2); free(p3); free(p4); // 查看free之后的相同内存区域,观测元数据的变化 for (
i= 0; i < 48*4;) { for (j = 0; j < 16; j++) { printf("%02x ", p[i++]); } printf(
"\n"); } printf("\n"); // 重来一遍 p1 = calloc(1, 32); memset(p1, 'c', 32); p2 =
calloc(1, 32); memset(p2, 'c', 32); p3 = calloc(1, 32); memset(p3, 'c', 32); p4
= calloc(1, 32); memset(p4, 'c', 32); // 哦,原来是栈式分配,和内核的slab一样 printf("%p %p %p
%p\n", p1, p2, p3, p4); for (i = 0; i < 48*4; ) { for (j = 0; j < 16; j++) {
printf("%02x ", p[i++]); } printf("\n"); } free(p1); free(p2); free(p3); free(p4
); // 分配不同大小的block p1 = calloc(1, 32); memset(p1, 'c', 32); p2 = calloc(1, 64);
memset(p2, 'd', 64); p3 = calloc(1, 128); memset(p3, 'e', 128); p = p1; p -= 16;
printf("%p %p %p \n", p1, p2, p3); for (i = 0; i < p3 + 128 - p1 + 16; ) { for (
j= 0; j < 16; j++) { printf("%02x ", p[i++]); } printf("\n"); } printf("\n");
free(p1); free(p2); free(p3); // 再来一遍 p1 = calloc(1, 32); memset(p1, 'c', 32);
p2= calloc(1, 64); memset(p2, 'd', 64); p3 = calloc(1, 128); memset(p3, 'e', 128
); p4 = calloc(1, 128); memset(p4, 'e', 128); p = p1; p -= 16; printf("%p %p %p
%p\n", p1, p2, p3, p4); for (i = 0; i < p4 + 128 - p1 + 16; ) { for (j = 0; j <
16; j++) { printf("%02x ", p[i++]); } printf("\n"); } }
执行一遍:
[root@localhost check]# ./a.out 0x1a0a010 0x1a0a040 0x1a0a070 0x1a0a0a0 #
元数据第一个16字节中的0x31应该就是block的大小。 # 48 = 32 + 16 = 0x30 00 00 00 00 00 00 00 00 31
00 00 00 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00
00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00
00 00 00 00 00 31 00 00 00 00 00 00 00# free内存块占用了至少1个8字节存放元数据。 # 貌似是next or
上一个可用块的地址:0x01a0a000。 # 这个地址是包括了元数据的。 00 a0 a0 01 00 00 00 00 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00
31 00 00 00 00 00 00 00 30 a0 a0 01 00 00 00 00 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00
00 00 00 00 60 a0 a0 01 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 0x1a0a0a0 0x1a0a070 0x1a0a040 0x1a0a010 00 00 00
00 00 00 00 00 31 00 00 00 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00
00 31 00 00 00 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00
00 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00
00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 0x1a0a010 0x1a0a0d0 0x1a0a120 00 00 00 00 00 00 00 00 31
00 00 00 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00
00 00 00 60 a0 a0 01 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 90
a0 a0 01 00 00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 63 63 63 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00 64 64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
64 64 64 00 00 00 00 00 00 00 00 91 00 00 00 00 00 00 00 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 0x1a0a010 0x1a0a040
0x1a0a090 0x1a0a120 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 63 63 63 63
63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63
63 63 00 00 00 00 00 00 00 00 51 00 00 00 00 00 00 00 64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
64 64 64 64 00 00 00 00 00 00 00 00 91 00 00 00 00 00 00 00 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 00 00 00 00 00 00 00 00
91 00 00 00 00 00 00 00 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65 65
65 65 65 65 65 65
很容易的一次探索,如果你面对一块内存,发现它的前16个字节包含至少一个附近的指针,而你根本就没有写过一个地址在内存块里,那么它超大概率就是
已经被free的被写入了free ptr的野指针 了!



接下来其实还有比较好玩的,照着这个思路,可以不参考任何文档,不参考源代码,直到把malloc的freelist管理方法全部dump出来,不断探测它的行为即可。

当然,我肯定知道内存管理有很多方法,malloc就有很多,本文我列举的这个是最low的,现在没啥人用这个了,高端的都看不起这种,线程不安全没有考虑,怎么怎么地的,所以说,然后呢?

不较真儿,本来就是玩嘛。

浙江温州皮鞋湿,下雨进水不会胖!

技术
©2019-2020 Toolsou All rights reserved,
css基础2:flex 多行均分有间隙布局习题11-5 指定位置输出字符串指定位置输出字符串(详细解析)(精华)2020年8月13日 C#基础知识点 windform实现双色球mybatis系列之返回结果映射(精华)2020年6月29日 C#类库 接口签名校验Java分布式系统高并发解决方案小结基于STM32红外避障小车的设计(有代码)vue vue-element-admin项目踩坑小结线上问题排查之HTTP状态码——415和406