当一个可执行文件已经为write而open时,此时的可执行文件是不允许被执行的。反过来,一个文件正在执行时,它也是不允许同时被write模式而open的。这个约束很好理解,因为文件执行和文件被写应该需要同步保护,因此内核会保证这种同步。

  那么内核是如何实现该机制的呢?

  Inode结点中包含一个数据项,叫做i_writecount,很明显是用于记录文件被写的个数的,用于同步的,其类型也是atomic_t. 内核中有两个我们需要了解的函数,与write操作有关,分别是:

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;
}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);
}

  这两个函数都很简单,get_write_acess作用就和名称一致,同样deny_write_access也是。如果一个文件被执行了,要保证它在执行的过程中不能被写,那么在开始执行前应该调用deny_write_access 来关闭写的权限。那就来检查execve系统调用有没有这么做。

  Sys_execve中调用do_execve,然后又调用函数open_exec,看一下open_exec的代码:

struct file *open_exec(const char *name)
{struct file *file;int err;file = do_filp_open(AT_FDCWD, name,O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0,MAY_EXEC | MAY_OPEN);if (IS_ERR(file))goto out;err = -EACCES;if (!S_ISREG(file->f_path.dentry->d_inode->i_mode))goto exit;if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)goto exit;fsnotify_open(file->f_path.dentry);err = deny_write_access(file);//调用if (err)goto exit;out:return file;exit:fput(file);return ERR_PTR(err);
}

  明显看到了deny_write_access的调用,和预想的完全一致。在open的调用里,应该有get_write_access的调用。在open调用相关的__dentry_open函数中就包含了对该函数的调用,

if (f->f_mode & FMODE_WRITE) {error = __get_file_write_access(inode, mnt);if (error)goto cleanup_file;if (!special_file(inode->i_mode))file_take_write(f);
}

  其中__get_file_write_access(inode, mnt)封装了get_write_access.

  那么内核又是如何保证一个正在被写的文件是不允许被执行的呢?这个同样也很简单,当一个文件已经为write而open时,它对应的inode的i_writecount会变成1,因此在执行execve时同样会调用deny_write_access 中读取到i_writecount>0之后就会返回失败,因此execve也就会失败返回。

  这里是写文件与i_writecount相关的场景:
  写打开一个文件时,在函数dentry_open中:

if (f->f_mode & FMODE_WRITE) { error = get_write_access(inode); if (error) goto cleanup_file;
}

  当然在文件关闭时,会将i_writecount--;关闭时会执行代码:

if (file->f_mode & FMODE_WRITE) put_write_access(inode);

  put_write_access 代码很简单:

static inline void put_write_access(struct inode * inode)
{ atomic_dec(&inode->i_writecount);
}

  于是乎自己写了个简单的代码,一个空循环,文件在执行的时候,在bash中,echo 111 >>可执行文件,结果预期之中,返回失败,并提示信息 text file busy.

  那么该机制是否同样适用于映射机制呢,在执行可执行文件时,会mmap一些关联的动态链接库,这些动态链接库是否被mmap之后就不允许被写以及正在写时不允许mmap呢?这个是需要考虑的,因为它关系到安全的问题。因为库文件也是可执行的代码,被篡改同样会引起安全问题。

  Mmap在调用mmap_region的函数里,有一个相关的检查:

if (vm_flags & VM_DENYWRITE) {          error = deny_write_access(file);if (error)goto free_vma;correct_wcount = 1;
}

  其中,mmap调用中的flags参数会被正确的赋值给vm_flags,对应关系是MAP_DENYWRIRE被设置了,那么VM_DENYWRITE就对应的也被设置。下面写了个简单的代码,做一下测试:

#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{int fd;void *src = NULL;fd = open("test.txt",O_RDONLY);if (fd != 0){if ((src = mmap(0,5,PROT_READ|PROT_EXEC  ,MAP_PRIVATE|        MAP_DENYWRITE,fd,0))== MAP_FAILED){printf("MMAP error\n");printf("%s\n",strerror(errno));}else{printf("%x\n",src);}}FILE * fd_t = fopen("test.txt","w");if( !fd_t){printf("open for write error\n");printf("%s\n",strerror(errno));return 0;}if (fwrite("0000",sizeof(char),4,fd_t) != 4){printf("fwrite error \n");}fclose(fd_t);close(fd);return 1;
}

  最后的test.txt被写成了”0000”,很奇怪,貌似MAP_DENTWRITE不起作用了。于是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模式映射,会导致正常程序写文件失败。不过VM_DENYWRITE在内核里还是有使用的,在mmap中还是有对deny_write_access的调用, 但是对它的调用已经不是由mmap中的flag参数的MAP_DENYWRITE驱动的了。

  那与可执行文件相关的动态链接库文件就悲剧了,大家都知道动态链接库使用的也是mmap,这也导致动态链接库在运行时可以被更改。其实我这就是为了确认这点。这也导致我需要自己写同步控制代码了。我们可以使用inode中的i_security以及file结构的f_secutiry变量来写自己的同步逻辑,就是麻烦了不少,还要写内核模块,哎,工作量又增加了啊。安全问题是个麻烦的问题...

转载于:https://www.cnblogs.com/liushaodong/p/3381310.html

linux 可执行文件与写操作的同步问题相关推荐

  1. linux写文件操作同步,linux 可执行文件与写操作的同步问题(文件读写操作产生的锁机制)...

    当一个可执行文件已经为write而open时,此时的可执行文件是不允许被执行的.反过来,一个文件正在执行时,它也是不允许同时被write模式而open的.这个约束很好理解,因为文件执行和文件被写应该需 ...

  2. linux c open flush,ctrl+c以及写操作失败和flush

    .kill命令可以带信号号码选项,也可以不带.如果没有信号号码,kill命令就会发出终止信号(15),这个信号可以被进程捕获,使得进程在退出之前可以清理并释放资源.也可以用kill向进程发送特定的信号 ...

  3. 关于异步IO与同步IO的写操作区别

    最近这两天都在看IO相关的知识点.一开始太凌乱,太杂,不过终于整理清楚了.觉得杂乱是因为一开始以为异步IO等于非阻塞IO,这完全是两个概念, LINUX下的异步IO有两类,一类为glibc AIO,这 ...

  4. NoSQL生态系统——事务机制,行锁,LSM,缓存多次写操作,RWN

    13.2.4 事务机制 NoSQL系统通常注重性能和扩展性,而非事务机制. 传统的SQL数据库的事务通常都是支持ACID的强事务机制.要保证数据的一致性,通常多个事务是不可能交叉执行的,这样就导致了可 ...

  5. Linux C编程--线程操作2--线程同步详解

    linux线程同步之互斥 在windows中,为了让多个线程达到同步的目的,在对于全局变量等大家都要用的资源的使用上,通常得保证同时只能由一个线程在用,一个线程没有宣布对它的释放之前,不能够给其他线程 ...

  6. Linux管道的原子性,管道的原子性 linux写操作原子性

    从本质上说,管道也是一种文件,但他又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题 限制管道的大小.实际上,管道是一个固定大小的缓冲区.在Linux中该换冲区的大小为一页,4k 使得他的 ...

  7. Linux数据写操作改进

    Linux的IO操作中数据的写函数int nwrite = write(int fd,void* buf ,int len)表示向fd文件描述符写入len个字节长度的数据报文,但是这并不能保证真正向内 ...

  8. linux下分区ntfs,简易教程:Linux下NTFS分区的写操作

    Linux下NTFS分区的写操作只需通过简单点击即可完成. 在你正常的工作中,假如你装的是双系统,其中一个是Winodws系统,而你又在Linux环境下办公,需要用到Windows分区中的某文档资料或 ...

  9. 常用的linux的命令行操作

    2019独角兽企业重金招聘Python工程师标准>>> 系统信息  arch 显示机器的处理器架构(1)  uname -m 显示机器的处理器架构(2)  uname -r 显示正在 ...

最新文章

  1. javascript 代码_代码简介:2016年JavaScript的现状
  2. Logstash 参考指南(使用Filebeat Modules配置示例)
  3. python flask 路由_python框架之Flask(2)-路由和视图Session
  4. Mysql分库分表(Mycat)
  5. MAT之NSL:CPK_NN神经网络实现预测哪个样本与哪个样本处在同一层,从而科学规避我国煤矿突水灾难
  6. Unieap3.5-Grid编辑列中数字与下拉改变
  7. Jquery实现可拖动进度条
  8. 【Win10自带录屏工具】
  9. 拓扑排序和关键路径算法----关键路径算法 (C语言实现)
  10. 安卓按键命令库教程(紫猫版)
  11. 微信小程序--加载动画:旋转方块
  12. 塑料壳上下扣合的卡扣设计_卡扣结构设计常识,结构设计中扣合量如何留
  13. 编程运维的一些小Tips
  14. 屏蔽网站、网页和弹窗的方法
  15. android仿华为系统相册之智能相册开发
  16. mysql盲注_Mysql 布尔型盲注手工注入详解
  17. I Need Some Sleep / Eels
  18. word文档如何插入公式编号并对齐
  19. 淘宝/天猫平台商品详情API接口调用说明
  20. php 获取pr,获取网站 百度权重 搜狗 谷歌PR

热门文章

  1. html5添加到安卓桌面图标,Android向桌面添加快捷方式,使其指向特定的网页
  2. python数据组织存在维度吗_用Python将统计数据不存在的记录按维度对应指标补齐...
  3. 1688 复杂业务场景下的 Serverless 提效实践
  4. Argo 项目入驻 CNCF,一文解析 Kubernetes 原生工作流
  5. Mysql多表查询(案例2)
  6. 计算机领域认知个人陈述,计算机专业个人陈述十九
  7. linux 跟踪链接 超时,Linux:跟踪netstat -s“连接尝试失败”的来源
  8. mysql主从同步_utf8_MySQL主从同步
  9. python awk 读文件_测试python awk sed 读取文件指定位置时的性能
  10. 【AI产品】一键时光穿梭表情互动,这款FaceApp你知否?