问题复现场景
目前,ubuntu稳定出现,centos环境不一定复现,启动pg为主He3DB为备,使用psql连接He3DB查询,每次退出时,日志出现无效指针释放问题。
日志:
退出客户端:
日志:
问题分析
Ubuntu环境He3DB日志在每次psql客户端退出时日志都会打印free函数出现无效指针,同时出现core文件。
#0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=140190718969344) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6, threadid=140190718969344) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=140190718969344, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3 0x00007f80b1642476 in __GI_raise (sig=sig@entry=6) at …/sysdeps/posix/raise.c:26
#4 0x00007f80b16287f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007f80b1689676 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7f80b17dbb8c “%s\n”) at …/sysdeps/posix/libc_fatal.c:155
#6 0x00007f80b16a0cfc in malloc_printerr (str=str@entry=0x7f80b17d9764 “free(): invalid pointer”) at ./malloc/malloc.c:5664
#7 0x00007f80b16a2a44 in _int_free (av=, p=, have_lock=0) at ./malloc/malloc.c:4439
#8 0x00007f80b16a5453 in __GI___libc_free (mem=) at ./malloc/malloc.c:3391
#9 0x00007f80b395954a in ?? () from /lib/x86_64-linux-gnu/libtasn1.so.6
#10 0x00007f80b3959658 in ?? () from /lib/x86_64-linux-gnu/libtasn1.so.6
#11 0x00007f80b1c7d1a2 in ?? () from /lib/x86_64-linux-gnu/libgnutls.so.30
#12 0x00007f80b3b9624e in _dl_fini () at ./elf/dl-fini.c:142
#13 0x00007f80b1645495 in __run_exit_handlers (status=0, listp=0x7f80b1819838 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:113
#14 0x00007f80b1645610 in __GI_exit (status=) at ./stdlib/exit.c:143
proc_exit退出时,调用系统exit函数报错,报错的core文件很多信息都打印不出来都是optimized out。因此,要打印最后一个出错的指针,只有在线调试,出现这种报错大概率内存地址被污染了。
he3db bug定位
定位步骤:
-
找到最后一个指针释放出错的地方。
-
psql 连接到he3db 执行psql -p5433
-
ps -ef|grep postgres
- gdb -p 150360设置断点为free的地方,因为每次退出都core,我先看下出错时是不是每次都是同一个地址访问
- \q退出客户端
- 查找最后一个出错的指针。内存越界访问,正常情况下很难定位,而现在每次core的地址都一样相对比较好定位些,现在唯一能做的事情就是看看这地址上存的什么数据,发现是”pagedata”字符串说明代码在存pagedata字符串那块应该有问题
向前打印一些字符串,这就是本地盘目录
- 查找代码He3DB唯一用到pagedata的地方就是创建本地盘目录数据的地方。
filePath是申请空间的指针,GetPageDataFile函数给空间赋值的地方
sprintf是给空间赋值的地方,该函数会在字符串结尾加上\0,因为“%s/%s”多了一个/字符,结尾再多一个\0,那length申请长度不够,这个空间就超了一个字符。
下面举例看下sprintf
虽然没有出现core,但sprintf对内存占用完全没有限制,我理解当内存布局别的地方需要使用越界空间时就会出问题,我们这边先用个临时方案将空间扩大一个,后期需要使用snprintf来限制空间越界。
- 编译重新启动,验证psql连主机退出,发现已经没有再报错。
- 另外,还有问题InitPageFile()函数是在ServerLoop函数里面,ServerLoop函数在startup进程启动之后的,如果startup进程调用InitPageFile里面全局变量filePath时,filePath可能还没有初始化的情况,或者目录还没有建的情况,存在一定风险。
小结
一般内存越界问题后期找很难查找,所以预防内存越界在编写代码时需要有严格的边界场景考虑,以免后期无法定位,这次比较幸运的地方打印p (char*)地址时能打印出相关字符串,如果是个随机字符串,那基本很难定位了。




