内存写坏问题
c++内存写坏问题,有很多原因造成,包括野指针、指针越界、并发场景等,出现很难排查,有很多内存分析工具可以使用,比如Valgrind1等很多方式
本文总结一种容易排查定位的方法:
mprotect+信号量处理【比如利用backtrace打印堆栈调用方式】,从而定位到哪里写坏内存
mprotect
mprotect是一种系统调用,mprotect
简单而言,mprotec可以指定一块内存保护为指定的模式,比如只读模式,这样当有场景破坏该模式的时候,就可以发送信号量SIGSEGV,这时候程序中捕获SIGSEGV,就可以获取到相关信息
函数原型
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
其中addr和len指定保护的内存区域,其中prot是模式,比如
PROT_NONE The memory cannot be accessed at all.
PROT_READ The memory can be read.
PROT_WRITE The memory can be modified.
PROT_EXEC The memory can be executed.
注意事项
其中需要注意的一点,mprotect要求保护的addr需要内存地址页对齐。因此如果保护的地址不是内存地址页对齐,需要改造比如填充保证对齐,或者使用mmap分配内存,保证一定是对齐的
mmap
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。具体可以参考认真分析mmap:是什么 为什么 怎么用
函数原型
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
例子
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static int alloc_size;
static char* memory;
void segv_handler(int signal_number)
{
printf ("memory accessed!\n");
mprotect (memory, alloc_size, PROT_READ | PROT_WRITE);
}
int main ()
{
int fd;
struct sigaction sa;
// 初始化segv_handler为SIGSEGV的句柄
memset (&sa, 0, sizeof (sa));
sa.sa_handler = &segv_handler;
sigaction (SIGSEGV, &sa, NULL);
// 使用映射/dev/zero分配内存页。最初映射的内存为只写
alloc_size = getpagesize();
fd = open ("/dev/zero", O_RDONLY);
// 分配内存
memory = static_cast<char *>(mmap (NULL, alloc_size, PROT_WRITE, MAP_PRIVATE, fd, 0));
close (fd);
// 写页来获得一个私有复制
memory[0] = 0;
// 使内存为不可写
mprotect (memory, alloc_size, PROT_READ);
// 写分配内存区域
memory[0] = 1;
// 释放内存
printf ("free mem\n");
munmap (memory, alloc_size);
return 0;
}
g++ main.cpp -o main
执行后,./main
./main
memory accessed!
free mem