偶然中发现,下面的两端代码表现不一样
void main(){
void* p1 = malloc(32);
free(p1);
free(p1); // 这里会报double free 错误,程序退出
}
void main(){
void* p1 = malloc(32);
void* p2 = malloc(32);
free(p1);
free(p2);
free(p1); // 正常没有报错
free(p2);//正常,没有报错
...
}
我就开始疑惑,难道glibc malloc库对doublefree错误的检测那么傻B,只有连续两次free的指针一样才能检测出来?然后又尝试了memset溢出的情况,如下
void main(){
void* p1 = malloc(32);
memset(p1,1,64); // 这里溢出了p1
void* p2 = malloc(32); //
printf("p1=%p,p2=%p\n",p1,p2); // 打印发现,malloc没有检测 memset的溢出影响
}
经过搜索,发现了,原来glibc malloc 库有一个环境变量MALLOC_CHECK_,当其值是0的时候,对上述两种情况(不连续地free同一段虚拟内存,memset溢出)是没有检测的,当我将其值设置为1/2/3时,对上述情况都会正常报错。经过这次测试,建议各位在开发过程中,最好是将MALLOC_CHECK_设置为非0,等到发布的时候,才将其值设置为0提升速度。
下面是glibc malloc调试相关的环境变量或工具说明,是比较好的参考
(转)
一)MALLOC_CHECK_
GNU的标准库(glibc)可以通过内置的调试特性对动态内存进行调试,它就是MALLOC_CHECK_环境变量,
它在默认情况下是不设定的,在老的版本默认这个值为0,新的版本默认值为2,但有一个矛盾,如果设定为空,它将会打印出长长的跟踪信息,这比设为2更详细.
MALLOC_CHECK_有三种设定,即:
MALLOC_CHECK_=0 ----- 关闭所有检查.
MALLOC_CHECK_=1 ----- 当有错误被探测到时,在标准错误输出(stderr)上打印错误信息.
MALLOC_CHECK_=2 ----- 当有错误被探测到时,不显示错误信息,直接进行中断.
我们用下面的小程序做一下测试,源程序如下:
#include <stdio.h>
#include <stdlib.h>
int main (int argc,char *argv[])
{
int i;
char* p = (char *)malloc(10);
char* pt = p;
for (i = 0;i < 10;i++)
{
p[i] = 'z';
}
free (p);
free(pt);
return 0;
}
gcc double-free.c -o double-free
注:这个程序会释放两次指针.
echo $MALLOC_CHECK_
我们在MALLOC_CHECK_默认设定的情况下,执行test程序,输出如下的信息:
./test
*** glibc detected *** ./test: double free or corruption (fasttop): 0x0890f008 ***
======= Backtrace: =========
/lib/libc.so.6[0x175f7d]
/lib/libc.so.6(cfree+0x90)[0x1795d0]
./test[0x80483dc]
/lib/libc.so.6(__libc_start_main+0xdc)[0x125dec]
./test[0x8048301]
======= Memory map: ========
00110000-00247000 r-xp 00000000 08:01 3704502 /lib/libc-2.5.so
00247000-00249000 r-xp 00137000 08:01 3704502 /lib/libc-2.5.so
00249000-0024a000 rwxp 00139000 08:01 3704502 /lib/libc-2.5.so
0024a000-0024d000 rwxp 0024a000 00:00 0
00b51000-00b6a000 r-xp 00000000 08:01 3704501 /lib/ld-2.5.so
00b6a000-00b6b000 r-xp 00018000 08:01 3704501 /lib/ld-2.5.so
00b6b000-00b6c000 rwxp 00019000 08:01 3704501 /lib/ld-2.5.so
00bf3000-00bf4000 r-xp 00bf3000 00:00 0 [vdso]
00dab000-00db6000 r-xp 00000000 08:01 3704511 /lib/libgcc_s-4.1.1-20070105.so.1
00db6000-00db7000 rwxp 0000a000 08:01 3704511 /lib/libgcc_s-4.1.1-20070105.so.1
08048000-08049000 r-xp 00000000 08:01 327681 /root/test
08049000-0804a000 rw-p 00000000 08:01 327681 /root/test
0890f000-08930000 rw-p 0890f000 00:00 0
b7e00000-b7e21000 rw-p b7e00000 00:00 0
b7e21000-b7f00000 ---p b7e21000 00:00 0
b7f26000-b7f27000 rw-p b7f26000 00:00 0
b7f3b000-b7f3c000 rw-p b7f3b000 00:00 0
bfdcf000-bfde4000 rw-p bfdcf000 00:00 0 [stack]
Aborted
这里我们调整MALLOC_CHECK_为0,再次运行程序,如下:
export MALLOC_CHECK_=0
./test
注:我们看到程序没有任何输出.
我们将MALLOC_CHECK_调整为1,再次运行程序,如下:
export MALLOC_CHECK_=1
./test
malloc: using debugging hooks
*** glibc detected *** ./test: free(): invalid pointer: 0x0811e008 ***
注:我们看到每次运行程序都会有malloc: using debugging hooks的输出,同时程序检测到free()两次释放的问题.
我们将MALLOC_CHECK_调整为2,再次运行程序,如下:
export MALLOC_CHECK_=2
./test
Aborted
注:我们看到程序只输出了Aborted,并中断了程序的运行.
二)用mtrace查找内存泄露
mtrace是由glibc提供的一个工具,在Redhat中将它打包在glibc-utils包中.
我们安装此包,如下:
rpm -ivh /mnt/Server/glibc-utils-2.5-12.i386.rpm
mtrace的主要作用是查找内存泄露,为了应用mtrace程序,必须在代码中使用glibc提供的函数mtrace和muntrace.另外,必须设置一个文件的名字给环境变量MALLOC_TRACE,因为glibc利用它为mtrace程序存储数据.
当执行完代码后,数据将会存在这个确认的文件中,每执行一次程序,这个文件的内容都会被重写.
我们用下面的代码进行测试,如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <mcheck.h>
int main (int argc,char *argv[])
{
setenv("MALLOC_TRACE","output",1);
mtrace();
int i;
char* p = (char *)malloc(10);
char* pt = p;
for (i = 0;i < 10;i++)
{
p[i] = 'z';
}
return 0;
}
编译:
gcc test.c -o test
注:程序用setenv函数设定环境变量MALLOC_TRACE
运行程序:
./test
这时在当前目录下生成了一个名为output的文件,如下:
cat output
= Start
@ ./test:[0x80483f2] + 0x82ba438 0xa
@ /lib/libc.so.6:(clearenv+0x7c)[0xb9910c] - 0x82ba008
@ /lib/libc.so.6:(tdestroy+0x47)[0xc39b77] - 0x82ba090
@ /lib/libc.so.6:(tdestroy+0x4f)[0xc39b7f] - 0x82ba0b0
用mtrace查找内存泄露,它告诉我们memory not freed
mtrace output
- 0x082ba008 Free 3 was never alloc'd 0xb9910c
- 0x082ba090 Free 4 was never alloc'd 0xc39b77
- 0x082ba0b0 Free 5 was never alloc'd 0xc39b7f
Memory not freed:
-----------------
Address Size Caller
0x082ba438 0xa at 0x80483f2
三)使用memusage收集内存统计数据
memusage不需要在代码中做出任何指示.这个工具也来自由glibc-utils包.它以柱形显示程序占用了多少内存.它默认输出到标准输出中,用ASCII文本显示一个绘成图画似的柱形.如下:
memusage awk 'BEGIN{print "hello world"}'
hello world
Memory usage summary: heap total: 7487, heap peak: 6891, stack peak: 8624
total calls total memory failed calls
malloc| 58 7487 0
realloc| 0 0 0 (nomove:0, dec:0, free:0)
calloc| 0 0 0
free| 15 797
Histogram for block sizes:
0-15 27 46% ==================================================
16-31 7 12% ============
32-47 2 3% ===
48-63 6 10% ===========
64-79 1 1% =
80-95 1 1% =
96-111 1 1% =
112-127 4 6% =======
160-175 1 1% =
176-191 2 3% ===
192-207 1 1% =
208-223 2 3% ===
384-399 1 1% =
480-495 1 1% =
4000-4015 1 1% =
四)使用Electric Fence检测内存泄漏
Electric Fence用一些巧妙的技术来检测程序在堆内存区上的溢出,不需要用Electric Fence来修改代码,相反,它提供一个动态库,这个库有多个动态分配函数.
一个名为ef的脚本被用来处理环境变量LD_PRELOAD的设置,我们可以用ef命令来调用程序.
下面是安装Electric Fence,如下:
rpm -ivh /mnt/Server/ElectricFence-2.2.2-20.2.2.i386.rpm
我们下面用一个小程序做测试,源代码如下:
#include <string.h>
int
main (int argc, char *argv[])
{
int *ptr = new int;
memset(ptr, 0, sizeof(int) + 1);
delete ptr;
}
编译:
g++ new-corrupt.cpp -o new-corrupt
注:这个小程序会导致边界溢出.
执行程序:
./new-corrupt
注:程序没有任何指示.
我们用ef执行这个程序,如下:
ef ./new-corrupt
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
/usr/bin/ef: line 20: 4148 Segmentation fault ( export LD_PRELOAD=libefence.so.0.0; exec $* )
注:此时有输出信息,它告诉我们出现了Segmentation {敏感词}t,并且指明在哪行出现的问题.
我们也可以将electric fence和gdb联用,如下:
编译程序,同时指定-g选项
g++ -g new-corrupt.cpp -o new-corrupt
用gdb打开程序,如下:
gdb ./new-corrupt
GNU gdb Red Hat Linux (6.5-16.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) set environment LD_PRELOAD libefence.so.0.0 /*设置环境变量LD_PRELOAD为libefence.so.0.0*/
(gdb) run /*运行程序*/
Starting program: /root/new-corrupt
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Program received signal SIGSEGV, Segmentation fault.
0x0804849d in main () at new-corrupt.cpp:7 /*检查出在调用memset函数时导致越界*/
7 memset(ptr, 0, sizeof(int) + 1);
(gdb) quit
下面我们在gdb中不指定环境变量,我们看到gdb没有打印出相关的错误信息.
gdb ./new-corrupt
GNU gdb Red Hat Linux (6.5-16.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) run
Starting program: /root/new-corrupt
Program exited normally.
- linux调试工具glibc的演示分析-core dump double free【转】
转自:http://www.cnblogs.com/jiayy/p/3475544.html 偶然中发现,下面的两端代码表现不一样 void main(){ void* p1 = malloc(32) ...
- Linux调试工具
1. 使用printf调试 #ifdef DEBUG Printf(“valriable x has value = %d\n”, x) #endif 然后在编译选项中加入-DDEBUG 更复杂的调试 ...
- Linux下库打桩机制分析 function Interposition
[时间:2017-08] [状态:Open] [关键词:linux, libray,打桩,interposition,函数替换,链接器,gcc,malloc,free] 0 引言 本文主要参考< ...
- linux实践之ELF文件分析
linux实践之ELF文件分析 下面开始elf文件的分析. 我们首先编写一个简单的C代码. 编译链接生成可执行文件. 首先,查看scn15elf.o文件的详细信息. 以16进制形式查看scn15elf ...
- Linux内核--网络栈实现分析(十一)--驱动程序层(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7555870 更多请查看专栏,地 ...
- Linux内核--网络栈实现分析(七)--数据包的传递过程(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...
- linux core dump 文件 gdb分析
core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIG ...
- 网易视频云技术分享:linux软raid的bitmap分析
网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术,提供稳定流畅.低时延.高并发的视频直播.录制.存储.转码及点播等音视频的PAAS服务,在线教育.远程医疗.娱乐秀场.在线 ...
- Linux信号(signal) 机制分析
Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...
随机推荐
- HashMap的put方法返回值问题
API文档中的描述: 先看一个例子 Map<Character, Integer> map = new HashMap<Character, Integer>(); Syste ...
- 状压DP入门详解+题目推荐
在动态规划的题型中,一般叫什么DP就是怎么DP,状压DP也不例外 所谓状态压缩,一般是通过用01串表示状态,充分利用二进制数的特性,简化计算难度.举个例子,在棋盘上摆放棋子的题目中,我们可以用1表示当 ...
- MT【144】托兰定理【图论】
平面上$2n$个点$(n>1,n\in N)$,无三点共线,任意两点连线段,将其中任意$n^2+1$条线段染红色. 求证:三边都为红色的三角形至少有$\left[\dfrac{2}{3}(n+\ ...
- 破解CobaltStrike3.12(转)
0x00 概述 CobaltStrike是一款内网渗透的商业远控软件,支持自定义脚本扩展,功能非常强大.前段时间Github上有好心人放出了CobaltStrike3.12的试用版,接着Lz1y ...
- 控制对象的创建方式(禁止创建栈对象or堆对象)和创建的数量
我们知道,C++将内存划分为三个逻辑区域:堆.栈和静态存储区.既然如此,我称位于它们之中的对象分别为堆对象,栈对象以及静态对象.通常情况下,对象创建在堆上还是在栈上,创建多少个,这都是没有限制的.但是 ...
- Android之断点续传下载(转)
Android之断点续传下载http://www.cnblogs.com/zxl-jay/archive/2011/10/09/2204195.html
- 【trie树】【P4551】 最长异或路径
Description 给定 \(n\) 个点的带边权树,求一条异或和最大的简单路径 Input 第一行是点数 \(n\) 下面 \(n - 1\) 行每行三个整数描述这棵树 Output 输出一个数 ...
- HTTP状态码的含义: 200:400:403:404:408:500:503:504
http协议的状态码 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态码. 100(继续) 请求者应当继续提出请求.服务器返回此代码表示已收到请求的第一部分,正在等待其余部分. 101( ...
- 4:JAVA UUID 生成
GUID是一个128位长的数字,一般用16进制表示.算法的核心思想是结合机器的网卡.当地时间.一个随即数来生成GUID.从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义 ...
- 使用 xhprof 进行 php 的性能分析
基于本机环境(php7,macos) 1.xhprof 扩展 php7 下安装 xhprof 扩展: git clone https://github.com/longxinH/xhprof cd x ...