由mmap引发的SIGBUS(续)

关于之前的《由mmap引发的SIGBUS》问题,这几天又做了一些较深入的探索,整理如下。

SIGBUS的必要性

为什么内核在上述情况下要抛出SIGBUS信号呢?
原来这是POSIX的规定,引用一段:

The mmap() function can be used to map a region of memory that is larger than the current size of the object. Memory access within the mapping but beyond the current end of the underlying objects may result in SIGBUS signals being sent to the process. The reason for this is that the size of the object can be manipulated by other processes and can change at any moment. The implementation should tell the application that a memory reference is outside the object where this can be detected; otherwise, written data may be lost and read data may not reflect actual data in the object.

参阅mmap文档:http://www.opengroup.org/onlinepubs/000095399/functions/mmap.html

捕获异常的绝招

这个问题用户程序还有什么招吗?
发现还有一招,那就是使用异常处理的方法将这个错误catch住。在C下,我们可以使用sigsetjmp - siglongjmp来实现。(关于setjmp/longjmp,可参阅:http://www.yuanma.org/data/2007/0110/article_2084.htm

把之前的代码改造如下(类似的方法也可以用来捕获内存访问越界段错误等问题):

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h>
#include <unistd.h>
#include <setjmp.h>
#define FILESIZE 8192
sigjmp_buf env;
void handle_sigbus(int sig)
{
printf("SIGBUS!\n");
siglongjmp(env, 1);
}
void main()
{
int i;
char *p, tmp;
int fd = open("tmp.ttt", O_RDWR);
p = (char*)mmap(NULL, FILESIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
signal(SIGBUS, handle_sigbus);
getchar();
if (!sigsetjmp(env, 1)) {
for (i=0; i<FILESIZE; i++) {
tmp = p[i];
}
}
else {
printf("fault cached when i=%d\n", i);
}
printf("ok\n");
}

注意env参数的传递,看起来似乎有些奇怪。env是一个保存执行上下文(栈指针、指令指针、等)的结构,setjmp函数会在这个结构中填入当前的上下文信息。然而,调用setjmp时传递的居然不是&env,而是env!这是怎么回事呢?C可不支持引用传递的喔~
在libc里面,jmp_buf(env其类型)有个很奇怪的定义:typedef struct __jmp_buf_tag jmp_buf[1];
知道原因了吧,原来env是一个数组的名字呀~

按照同样的流程,先执行程序、再将文件缩小、再进行内存访问。得到的输出结果如下:

kouu@kouu-one:~/test$ ./a.out

SIGBUS!
fault cached when i=4096
ok

参阅一段邮件列表:http://lkml.indiana.edu/hypermail/linux/kernel/0205.1/0525.html

像可执行文件那样“text busy”?

在CU论坛上与网友讨论中(见: http://linux.chinaunix.net/bbs/thread-1162037-1-1.html),又引出一个问题:进程所执行的可执行文件也是通过mmap进行映射的(可以通过cat /proc/$pid/maps来看到这些映射)。那么如果我们在进程的执行期间将文件改小,是不是进程也会收到SIGBUS而崩溃呢?

如果你有办法将文件改小的话,的确会这样。但是你会发现,当你重写或者拷贝覆盖一个正在执行的文件时,控制台会给出“text busy”的提示。linux内核保证了这个文件不可写。

那么这是怎么做到的呢?mmap映射普通文件时是否可以借鉴?
这是通过建立映射时的MAP_DENYWRITE选项来实现的。这个选项在mmap的过程中会被处理:

mmap_region()
......
if (file) {
......
if (vm_flags & VM_DENYWRITE) {
error = deny_write_access(file);
......
}
......

MAP_DENYWRITE选项会被转成vma上的标记VM_DENYWRITE。mmap时遇到这个标记会调用deny_write_access。

deny_write_access()
int deny_write_access(struct file * file)
{
struct inode *inode = file->f_path.dentry->d_inode;

spin_lock(&inode->i_lock);
if (atomic_read(&inode->i_writecount) > 0) {
spin_unlock(&inode->i_lock);
return -ETXTBSY;
}
atomic_dec(&inode->i_writecount);
spin_unlock(&inode->i_lock);

return 0;
}

如果文件正在被写(inode->i_writecount大于0,可能存在多个写者),则映射失败。因为现在要做的是禁止别人写,但是别人先到一步,这就没办法了。
否则(inode->i_writecount小于等于0),让inode->i_writecount自减1。inode->i_writecount的值小于0时表示文件已被“deny write”。而inode->i_writecount还可能小于-1,因为有多个进程同时让它“deny write”。只有等它们都解除禁止时,文件才能够被写。

当一个文件被“deny write”之后,其他进程若想修改它,则在open这个文件的时候就会因为无法通过“deny write”的检查,而得到相应的错误码。
检查函数get_write_access跟deny_write_access正好是反过来的:

get_write_access()
int get_write_access(struct inode * inode)
{
spin_lock(&inode->i_lock);
if (atomic_read(&inode->i_writecount) < 0) {
spin_unlock(&inode->i_lock);
return -ETXTBSY;
}
atomic_inc(&inode->i_writecount);
spin_unlock(&inode->i_lock);

return 0;
}

这样,试图以写模式打开一个已经被“deny write”的文件,就将会被阻止。文件既然不能被打开,也就不能被写了。

然而,不幸的是,MAP_DENYWRITE选项在mmap系统调用里面是会被忽略的,只有在内核内部使用do_mmap时才能被使用(比如exec系列的系统调用中,在加载可执行文件时,就会调用do_mmap,并使用MAP_DENYWRITE选项)。
就连动态链接库也没法幸免(它们也是由库函数通过系统调用mmap来映射的。奇怪的是,为什么不用uselib系统调用呢?),搜到一篇康神的文章在说这个事情: http://blog.kangkang.org/index.php/archives/49
那么为什么要忽略mmap系统调用时传递的MAP_DENYWRITE选项呢?man mmap,可以看到这么一段:
MAP_DENYWRITE
This flag is ignored. (Long ago, it signaled that attempts to write to the underlying file should fail with ETXTBUSY. But this was a source of denial-of-service attacks.)

指定MAP_DENYWRITE选项可能引起一些Dos,这里指的是:一个普通用户可以使整个系统在某些方面拒绝服务。典型的做法是:用户以MAP_DENYWRITE选项mmap某个日志文件,于是需要写这个日志文件的应用程序将无法正常工作。
比如,login程序在用户登录时会写utmp日志(一般在/var/run/utmp),如果这个文件被某个用户“deny write”,那么其他用户就没法登录了。

时间: 2024-11-06 02:55:13

由mmap引发的SIGBUS(续)的相关文章

由mmap引发的SIGBUS

一直以来都觉得使用mmap读文件是非常高效.非常优雅的做法(参见<从"read"看系统调用的耗时>).mmap之后,就可以通过内存访问的方式访问到文件里的内容,省去了read这样的系统调用. 却不曾想过,mmap以后,如果读文件出错会发生什么-- 今晚看到一篇介绍apache bug的文章,里面说到,apache使用mmap来实现对静态文件的访问.在读文件之前,apache使用stat系统调用得知了文件的长度,然后按照此长度读取已经被映射在某个内存区间上的文件. 然而如果在

由IsPostBack引发的思考(续)

上篇文章中讲了关于Page类的IsPostBack属性的相关知识,并引发了思考.本文中,将借助实例,来帮助大家更好的理解WebForm中的这些知识,相信读者会有意外的收获. WebForm技术,看起来简单,做起来也看似很简单,但你真正深入去思考,去探索,会发现其非常非常的复杂. 好了,首先借助一个实例,来探索当发生"跨页投递"的这种情况时候,当前"响应页面"的IsPostBack和"请求页面"的IsPostBack值. 先建立两个新的aspx(W

认真分析mmap:是什么 为什么 怎么用【转】

转自:http://www.cnblogs.com/huxiao-tee/p/4660352.html?utm_source=tuicool&utm_medium=referral   阅读目录 mmap基础概念 mmap内存映射原理 mmap和常规文件操作的区别 mmap优点总结 mmap相关函数 mmap使用细节 回到顶部 mmap基础概念 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.实现这样

Linux内存管理之mmap详解 【转】

转自:http://blog.chinaunix.net/uid-26669729-id-3077015.html 一. mmap系统调用 1. mmap系统调用         mmap将一个文件或者其它对象映射进内存.文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零.munmap执行相反的操作,删除特定地址区域的对象映射. 当使用mmap映射文件到进程后,就可以直接操作这段虚拟地址进行文件的读写等操作,不必再调用read,write等系统调用.但需注

神州泰岳面临诸多考验,飞信续约依旧谈判之中

曾经凭借着飞信业务超募12亿元,并一度创下两市最高价个股的神州泰岳如今似乎面临着诸多考验.因为主要利润贡献点飞信业务合约到期能否续签依旧在谈判之中,而公司副总经理却又在此时违规买卖股票,且上市公司方面迟迟未予公告,引发了诸多投资者的不满. 年报披露前的短线交易 深交所网站披露的短线交易情况数据显示,3月22日神州泰岳副总经理许芃以每股18.96元的价格卖出25万股神州泰岳的股票,同日还以每股18.99元的价格买入7350股.但是截至<第一财经日报>记者发稿,神州泰岳都未就此事发布相关公告. 按

华东电脑前高管暴富探秘 4倍高溢价续写挪移史

4月26日下午,上海北京东路668号科技京城东楼16楼会议室,华东电脑39名股东代表被"临时召集"起来,举手表决了刚刚通过国资委审批的"华讯网络注入华东电脑的方案". 尽管华东电脑对此次"重大资产重组"非常审慎,称"尚需经证监会核准":但公司高管并不掩饰"可以望见的交易时间"-5月底华讯网络将成为华东电脑全资子公司. 4倍高溢价"续写"挪移史 截至评估基准日,华讯网络的净资产约为4.09

曹可凡谈选手退赛西单女孩晋级续写“天使传奇”

曹可凡 经过三场激烈的"大逃杀",前晚东方卫视<加油!东方天使>终于决出全国十二强.其中"西单女孩"任月丽作为5位复活选手之一,凭借努力顺利晋级. 而主持人曹可凡当场斥责要退赛的选手林菀则引发热议.有观众表示,不应对年轻选手如此苛刻,而曹可凡则表示,"'天使'的舞台容不得做戏." 复活选手临时欲退赛遭批 作为本次大逃杀的网络复活选手之一,来自沈阳赛区的林菀再次重返舞台让人颇为期待.然而令人大感意外的是,这位复活选手刚上舞台,还未展示才

一张电路图引发的网络营销变革

眼下说到网络营销,不论是沿海发达地区企业,还是内地企业,都已经不陌生.早在四年之前,中国就已经号称有10万网络营销从业大军.随着中国互联网用户即将突破3亿,互联网这个巨大的市场吸引着无数企业蠢蠢欲动,谁不想在互联网经济中分一杯羹? 网络营销通俗的说就是网上做生意.然而网上生意究竟如何做,不同的企业有不同的做法.有时即使是同样的网络营销方法,效果却大相径庭.不断有企业对网络营销失去信心,同时又有更多企业雄心勃勃加入网络营销行列中来,这样的事情屡见不鲜. 从2008年下半年开始,在江浙沪地区,陆续在

站长网播报:盗版高清门户遭查封续 PPS整合进爱奇艺

中介交易 SEO诊断淘宝客 站长团购 云主机 技术大厅 1. 锐观察:一万个地方网站App正在袭来 BAT一桩桩巨额收购战背景下,站长,曾经中国互联网草根创业者的代名词,正变成腐朽的代名词,连象征着"荣耀"的站长大会也要改名创业者大会,那么在移动互联网时代,站长们真的毫无作为吗? 在5月11日的站长大会上,我采访了不少为站长们开发网站工具的互联网公司,并特别关注了下午的新媒体论坛,忽然惊觉:一万个地方网站App正在奔腾而来.这背后则是地方网站转型的条件已经成熟,用中国传统的分析方法就是