#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
#include <setjmp.h>
char *buf;
struct stat statbuf;
void handler(int signo)
{
printf("permission denied/n");
/*
if(mprotect(buf, statbuf.st_size, PROT_READ|PROT_WRITE)==-1) //改变内存映射区访问权限
{
perror("fail to alter permission");
exit(1);
}
printf("modify/n");
*/
}
int main(void)
{
int fd;
if((signal(SIGSEGV, handler)) == SIG_ERR) //设置SIGSEGV信号处理程序
{
printf("can't set handler for SIGALRM");
exit(0);
}
if(stat("test.txt", &statbuf) == -1) //得到文件状态信息
{
perror("fail to get stat");
exit(1);
}
fd = open("test.txt", O_RDWR); //以读写方式打开文件
if(fd == -1)
{
perror("fail to open");
exit(1);
}
buf = (char *)mmap(NULL,statbuf.st_size,PROT_READ,MAP_SHARED,fd,0); //建立内存映射区,访问权限为只读
printf("try to write/n");
memcpy(buf, "china/n", 6); // 企图写一个只读的映射区,造成SIGSEGV错误
if(mprotect(buf, statbuf.st_size, PROT_READ|PROT_WRITE)==-1) //改变内存映射区访问权限
{
perror("fail to alter permission");
exit(1);
}
printf("write again/n");
memcpy(buf, "china/n", 6); //再次写内存映射区
if(munmap(buf, statbuf.st_size) == -1) //撤销内存映射
{
perror("fail to munmap");
exit(1);
}
close(fd);
return 0;
}
程序打印结果:
try to write
permission denied
permission denied
permission denied
permission denied
permission denied
permission denied
permission denied
permission denied
分析:
程序本意:该程序演示越权访问一个内存映射区。首先创建一个只读的内存映射区,之后对其进行写操作。该越权访问造成程序收到SIGSEGV信号。应用程序接收该信号,并输出提示信息不能关切输出提示信息。接下来程序调用mprotect函数改变映射区的访问权限为可读写。此时,再次向映射区进行写操作就可以成功了。
但是:当程序捕捉到SIGSEGV信号时,本意是希望捕获信号输出提示信息后,程序能继续运行。但是,中断返回点仍旧在非法访问处,如此则进入一个“非法访问--信号--处理--非法访问”的死循环中。
改进方法:
把红色的代码注掉,将蓝色的代码放开(把mprotect(...)的调用放在信号处理函数中)
打印结果:
try to write
permission denied
modify
write again
此时紫色的代码可以去掉,因为signal信号处理函数,会返回到memcpy函数调用处执行