mcheck 函数使用(glibc-3-内存)
1-函数格式:
#include <mcheck.h>
int mcheck(void (*abortfunc)(enum mcheck_status mstatus));
调用该函数后,后续的内存分配、释放都将进行内存的连续性检查,并在内存连续性检查失败后,调用 abortfunc
函数,如果abortfunc
使用NULL
,则使用默认行为:打印错误信息并调用abort()
退出。
2-范例:
#include <stdio.h>
#include <stdlib.h>
#include <mcheck.h> // mcheck 功能函数头文件
#include <errno.h>
#include <string.h>
/** @brief: 检查到内存不一致之后,程序退出之前被调用 */
void abortfun(enum mcheck_status mstatus)
{
fprintf(stderr, "abortfun called with %d\n", mstatus);
}
int main(int argc, char *argv[])
{
char *p = NULL;
if (mcheck(abortfun) != 0) // 注册 mcheck 内存检查功能
{
fprintf(stderr, "mcheck:%s\n", strerror(errno));
return -1;
}
p = malloc(10);
free(p);
printf("1st free finished.\n");
free(p); // 第二次,p 指向的内存已经被释放,会被检查到,将会调用 \abortfunc
printf("2nd free\n");
return 0;
}
编译:
$ gcc mcheck.c
运行结果:
$ ./a.out
1st free finished.
abortfun called with 1
*** Error in `./a.out': double free or corruption (fasttop): 0x00000000025df030 ***
======= Backtrace: =========
...
./a.out[0x400669]
======= Memory map: ========
...
已放弃(吐核)
3-主动探测 mprobe
函数格式:
#include <mcheck.h>
enum mcheck_status mprobe(void *ptr);
返回值:
/* Return values for `mprobe': these are the kinds of inconsistencies that
`mcheck' enables detection of. */
enum mcheck_status
{
MCHECK_DISABLED = -1, /* Consistency checking is not turned on. */
MCHECK_OK, /* Block is fine. */
MCHECK_FREE, /* Block freed twice. */
MCHECK_HEAD, /* Memory before the block was clobbered. */
MCHECK_TAIL /* Memory after the block was clobbered. */
};
范例:
#include <stdio.h>
#include <stdlib.h>
#include <mcheck.h>
#include <errno.h>
#include <string.h>
void abortfun(enum mcheck_status mstatus)
{
fprintf(stderr, "abortfun called with %d\n", mstatus);
}
int main(int argc, char **argv)
{
char *p = NULL;
enum mcheck_status mstatus;
if (mcheck(abortfun) != 0)
{
fprintf(stderr, "mcheck:%s\n", strerror(errno));
return -1;
}
p = malloc(10);
mstatus = mprobe(p);
printf("status:%d\n", mstatus);
mstatus = mprobe(p + 1);
printf("status:%d\n", mstatus);
return 0;
}
编译:
$ gcc mcheck.c
运行结果:
$ ./a.out
status:0
abortfun called with 2
status:2
abortfun called with 2
status:2
从运行结果来看,每次检查到 mprobe 检查到异常,并在返回前,都会调用 mcheck 注册的 abortfun。
4-自动开启检查 -lmcheck
对于已经有的代码,没有 mcheck 调用,期望使用 mcheck 功能,可以在编译的时候,直接连接 mcheck 自动开启内存连续性检查。
范例:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *p = NULL;
p = malloc(10);
free(p);
free(p);
return 0;
}
编译:
$ gcc mcheck.c -lmcheck
运行结果:
$ ./a.out
block freed twice
已放弃(吐核)
5-小陷阱
奇怪的想法:如果我调用了 mcheck 函数,链接的时候又添加了 -lmcheck 选项,结果或怎样?
示例代码(之前的 mprobe 代码):
#include <stdio.h>
#include <stdlib.h>
#include <mcheck.h>
#include <errno.h>
#include <string.h>
void abortfun(enum mcheck_status mstatus)
{
fprintf(stderr, "abortfun called with %d\n", mstatus);
}
int main(int argc, char **argv)
{
char *p = NULL;
enum mcheck_status mstatus;
if (mcheck(abortfun) != 0)
{
fprintf(stderr, "mcheck:%s\n", strerror(errno));
return -1;
}
p = malloc(10);
mstatus = mprobe(p);
printf("status:%d\n", mstatus);
mstatus = mprobe(p + 1);
printf("status:%d\n", mstatus);
return 0;
}
编译:
$ gcc mcheck.c -lmcheck
运行(卡很久):
$ ./a.out
段错误(吐核)
挂了,追踪可以发现,挂在 malloc,而卡很久是因为不断调用 mallochook。从此信息可以推断:mcheck 也是通过使用设置 malloc 等的回调函数来进行内存检查的,而内存是由 glibc 维护的,所以它能检查出来这个指针释放合法。