c - 调用 mremap(2) 时出现奇怪的信号处理程序行为
问题描述
首先,这是代码
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h> /* mmap() is defined in this header */
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>
#include <execinfo.h>
#define FILE_NAME "mremap_test_file.txt"
void sighandler(int signum) {
printf("Signal SIGSEGV recieved\n");
exit(EXIT_FAILURE);
}
int main (int argc, char *argv[])
{
//int fdin;
int fdout = 0;
static char stack[SIGSTKSZ];
stack_t ss = {
.ss_size = SIGSTKSZ,
.ss_sp = stack,
};
struct sigaction sigact;
sigaltstack(&ss, 0);
int ret = sigaction(SIGSEGV, NULL, &sigact);
if (ret == -1){
perror("register sighandler");
return -1;
}
sigact.sa_handler = sighandler;
sigact.sa_flags |= SA_ONSTACK | SA_RESTART;
ret = sigaction(SIGSEGV, &sigact, NULL);
if (ret == -1){
perror("register sighandler");
return -1;
}
fdout = open(FILE_NAME, O_CREAT| O_RDWR);
if( fdout < 0){
perror("open");
return -1;
}
char test_str[1024];
memset(test_str,65,1024);
write(fdout,test_str,1024);
close(fdout);
sync();
if(argc > 2)
return 0;
fdout = open(FILE_NAME, O_CREAT| O_RDWR);
if( fdout < 0){
perror("open");
return -1;
}
void * addr = NULL;
addr = mmap(NULL, 1024, PROT_EXEC |PROT_WRITE, MAP_SHARED, fdout, 0);
if(addr == MAP_FAILED ){
perror("mmap");
return -1;
}
void* remap_addr = addr;
remap_addr = mremap(remap_addr, 1024, 512, (MREMAP_FIXED | MREMAP_MAYMOVE), (void*)((unsigned long)remap_addr + 4096) );
if(remap_addr == MAP_FAILED){
perror("mremap");
return -1;
}
int a = *(int*)(0);
remove(FILE_NAME);
return 0;
}
我正在使用 gcc 版本 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12) 来编译这段代码
你可以看到里面有int a = *(int*)(0);
触发SIGSEGV的代码。奇怪的是,如果这行代码在mremap(2)
自定义 SIGSEGV 处理程序被触发之前运行;如果这行代码在mremap(2)
触发默认的 SIGSEGV 处理程序之后运行。
mremap(2)
确实返回成功没有任何问题,所以我无法理解那些这样的行为
解决方案
我没有看到这种行为,在这两种情况下,非法访问都会导致调用sighandler()
.
当然,这可能是因为我必须解决您的代码的几个问题(您提供的内容没有立即编译),特别是:
- 创建具有允许我再次打开它们的权限的文件,因为它们是使用权限掩码零创建的:
fdout = open(FILE_NAME, O_CREAT| O_RDWR, 0777);
- 基于原始地址而不是尚未存在的重新映射地址重新映射:
void *remap_addr = mremap(addr, 1024, 512, (MREMAP_FIXED | MREMAP_MAYMOVE), (void*)((unsigned long)addr + 4096) );
一旦这些问题得到解决,非法访问就会调用自定义处理程序,无论它是在mremap
. 如果这不能解决您的问题,我建议您发布导致问题的实际代码:-)
您可能要考虑的另一件事是,这printf
不是被认为可以从信号处理程序中安全调用的函数之一。此链接详细说明了可以安全使用的内容,因此这可能是由printf
自身内部的问题引起的。
顺便说一句,如果您在重新映射后安装(或重新安装)信号处理程序,看看会发生什么会很有趣。我之所以只提到这一点,是因为在其他情况下,调用某些函数会干扰最初被认为是一个单独的项目(sleep
与SIGALRM
信号之间的冲突的模糊记忆,但这是很久以前的事了)。
我唯一可以建议的另一件事是,您可以sigaction
在通话后使用它来获取信号的当前处置mremap
。如果由于某种原因它已与您的处理程序分离,那应该希望能清楚地说明这一点。
推荐阅读
- reactjs - 反应api连接错误
- sql - 来自 postgresql 的时序数据
- ios - IOS/Objective-C:在barbutton中同时显示两个图像
- c# - 在 Linux Python 中使用 .NET Core 库
- groovy - Groovy 2.5 Java 10 和 sparkjava
- hadoop - hadoop 运行示例 输入路径不存在
- docker - Docker 工具箱:来自守护进程的错误响应:无效模式:/root/docker
- javascript - 我在 AWS 上托管的 Web 应用程序的一些脚本和页面正在以某种方式被修改
- android - 长按刷新列表后取消ListView上的点击事件
- hibernate - Java spring Boot,JPA,Hibernate,持久化 OBJECT 类型的对象