写了一个函数:创建一个节点,并将id 全部克隆到新创建的节点,代码如下
- static itree_node_t * itree_new2(id_sets_t * id_sets,unsigned int min,unsigned int max)
- {
- itree_node_t * new_node = NULL;
-
- new_node = calloc(1,sizeof(itree_node_t));
- assert(new_node);
-
- new_node->max = max;
- new_node->min = min;
-
- new_node->id_sets.id_num = id_sets->id_num;
- new_node->id_sets.id_array_num = ID_PREPARE_MALLOC_SIZE;
-
- new_node->id_sets.id_array = calloc(new_node->id_sets.id_array_num,sizeof(unsigned int));
- assert(new_node->id_sets.id_array);
-
- for(int i = 0 ; i < id_sets->id_num;i++)
- {
- new_node->id_sets.id_array[i] = id_sets->id_array[i];
- }
-
- return new_node;
- }
不适用任何特殊编译选项编译这段代码:
gcc -g -O0 -Wall $(DEFINES) $(INCLUDE)
运行后出现下面的结果:
- itree init...
- realloc(): invalid next size ][4%][|]
- 已放弃 (核心已转储)
使用gdb 看堆栈:
- #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
- #1 0x00007ffff7de0859 in __GI_abort () at abort.c:79
- #2 0x00007ffff7e4b26e in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7f75298 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
- #3 0x00007ffff7e532fc in malloc_printerr (str=str@entry=0x7ffff7f735bb "realloc(): invalid next size") at malloc.c:5347
- #4 0x00007ffff7e56fac in _int_realloc (av=av@entry=0x7ffff7faab80 <main_arena>, oldp=oldp@entry=0x424b40, oldsize=oldsize@entry=416, nb=816) at malloc.c:4564
- #5 0x00007ffff7e58fb6 in __GI___libc_realloc (oldmem=0x424b50, bytes=800) at malloc.c:3226
- #6 0x000000000040235d in itree_node_id_clone (node=0x414510, id_sets=0x4139d8) at ../idps_itree.c:77
- #7 0x0000000000401a78 in _itree_insert (tree=0x7fffffffdfa0, pbase=0x42a070, node=0x4139d0) at ../idps_itree.c:281
可以看到出错的位置在itree_node_id_clone 中realloc 失败了。
但是检查代码,这里的代码并没有问题,使用malloc,calloc 替换后情况类似。
于是我们借助clang的sanitizer 来定位问题,使用如下编译选项:
clang -g -O0 -Wall $(DEFINES) $(INCLUDE) -fsanitize=address -fno-omit-frame-pointer
后重新编译运行,结果如下:
- zison@ubuntu:~/work/idps/car_idps/idps_engine$ ./test/itree_test 5000 100
- itree init...
- ================================================================= ][3%][\]
- ==55229==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140002559d0 at pc 0x0000004c7e5a bp 0x7ffde91cf5c0 sp 0x7ffde91cf5b8
- WRITE of size 4 at 0x6140002559d0 thread T0
- #0 0x4c7e59 in itree_new2 /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:53:39
- #1 0x4c4dbe in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:211:35
- #2 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #3 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #4 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #5 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #6 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #7 0x4c65e1 in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:350:16
- #8 0x4c3499 in itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:424:12
- #9 0x4c8ea5 in main /home/zison/work/idps/car_idps/idps_engine/test/itree_test.c:59:9
- #10 0x7f7267e1d082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
- #11 0x41b31d in _start (/home/zison/work/idps/car_idps/idps_engine/test/itree_test+0x41b31d)
-
- 0x6140002559d0 is located 0 bytes to the right of 400-byte region [0x614000255840,0x6140002559d0)
- allocated by thread T0 here:
- #0 0x493bd2 in calloc (/home/zison/work/idps/car_idps/idps_engine/test/itree_test+0x493bd2)
- #1 0x4c7bc2 in itree_new2 /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:48:34
- #2 0x4c4dbe in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:211:35
- #3 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #4 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #5 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #6 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #7 0x4c36ac in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:110:16
- #8 0x4c65e1 in _itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:350:16
- #9 0x4c3499 in itree_insert /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:424:12
- #10 0x4c8ea5 in main /home/zison/work/idps/car_idps/idps_engine/test/itree_test.c:59:9
- #11 0x7f7267e1d082 in __libc_start_main /build/glibc-SzIz7B/glibc-2.31/csu/../csu/libc-start.c:308:16
-
- SUMMARY: AddressSanitizer: heap-buffer-overflow /home/zison/work/idps/car_idps/idps_engine/test/../idps_itree.c:53:39 in itree_new2
- Shadow bytes around the buggy address:
- 0x0c2880042ae0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
- 0x0c2880042af0: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa
- 0x0c2880042b00: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
- 0x0c2880042b10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- 0x0c2880042b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- =>0x0c2880042b30: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
- 0x0c2880042b40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
- 0x0c2880042b50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
- 0x0c2880042b60: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
- 0x0c2880042b70: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
- 0x0c2880042b80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
- Shadow byte legend (one shadow byte represents 8 application bytes):
观察结果,发现idps_itree.c:53
这里溢出了,这里才是真正引起问题的地方。检查代码 ,发现在id_sets->id_num 大于ID_PREPARE_MALLOC_SIZE 的情况下,就会出现溢出new_node->id_sets.id_array 的情况。
如果不使用clang 的sanitizer ,程序报错往往是在其他的malloc,calloc,realloc 的地方,出现类似“realloc(): invalid next size”的问题,往往根据报错比较难找到真正出问题的地方。
clang 的sanitizer 不仅能方便的查找内存溢出,还能方便的检查线程死锁等等。有兴趣的可以自行google ,baidu 加深了解。