linux 覆盖可执行文件的问题
Posted 安庆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux 覆盖可执行文件的问题相关的知识,希望对你有一定的参考价值。
测试环境是3.10.0 内核。
有一次操作中,发现cp -f A B执行的时候,行为不一样:
当B没被打开,则正常覆盖B。
当B是被打开,但没有被执行,则能覆盖,
当B被打开,且被执行,则不能直接覆盖,而是创建一个同名文件,然后写这个文件,同时B的inode在os中用lsof看的话,是delete。
问题是:为什么被执行的文件不能覆盖,它通过什么机制保护的?
通过strace,发现cp -f的时候,如果目标文件正在被执行,那么返回的是 ETXTBSY,为什么会返回busy,以及这个busy是怎么设置的呢?
stap 一下:
probe kernel.function("do_dentry_open").return { if($return == -26)---------这个就是busy的错误码 { print_backtrace(); exit(); } }
通过搜索并stap内核代码,发现流程如下:
Returning from: 0xffffffff812062d0 : do_dentry_open+0x0/0x2e0 [kernel] Returning to : 0xffffffff8120664a : vfs_open+0x5a/0xb0 [kernel] 0xffffffff812179ad : do_last+0x1ed/0x12c0 [kernel] 0xffffffff816be459 : kretprobe_trampoline+0x0/0x57 [kernel] 0xffff88557427ffd8 0xffffffff8121b0eb : do_filp_open+0x4b/0xb0 [kernel] (inexact) 0xffffffff81125a87 : __audit_getname+0x97/0xb0 [kernel] (inexact) 0xffffffff8122840a : __alloc_fd+0x8a/0x130 [kernel] (inexact) 0xffffffff81207a13 : do_sys_open+0xf3/0x1f0 [kernel] (inexact) 0xffffffff81207b2e : sys_open+0x1e/0x20 [kernel] (inexact) 0xffffffff816c5991 : tracesys+0x9d/0xc3 [kernel] (inexact)
static int do_dentry_open(struct file *f, struct inode *inode, int (*open)(struct inode *, struct file *), const struct cred *cred) { static const struct file_operations empty_fops = {}; int error; f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE; if (unlikely(f->f_flags & O_PATH)) f->f_mode = FMODE_PATH; path_get(&f->f_path); f->f_inode = inode; if (f->f_mode & FMODE_WRITE) { error = __get_file_write_access(inode, f->f_path.mnt);
static inline int __get_file_write_access(struct inode *inode, struct vfsmount *mnt) { int error; error = get_write_access(inode);
报错的最终函数就是get_write_access:
static inline int get_write_access(struct inode *inode) { return atomic_inc_unless_negative(&inode->i_writecount) ? 0 : -ETXTBSY; }
看来,文件在被执行的时候,会将 inode->i_writecount 值会被设置为负值?查看代码,调用链是:
stub_execve -->sys_execve-->do_execve_common-->do_open_exec-->deny_write_access
我照样stap一下:
一个gdb程序用来打开一个文件,然后gdb断住:
(gdb) n 6 FILE *pFile=NULL; (gdb) 7 char * Mode="a+"; (gdb) 8 int ret=0; (gdb) 9 pFile = fopen("main.o", Mode); (gdb) 10 if(!pFile) (gdb) 15 ret=fputs("abcd", pFile);
另外一边,使用stap进行跟踪:
probe kernel.function("do_open_exec").return { if($return == -26) { print_backtrace(); exit(); } }
然后第三个窗口执行./main.o:
strace ./main.o execve("./main.o", ["./main.o"], [/* 38 vars */]) = -1 ETXTBSY (Text file busy) write(2, "strace: exec: Text file busy ", 29strace: exec: Text file busy ) = 29 exit_group(1) = ? +++ exited with 1 +++
发现确实报错的是busy,也就是 deny_write_access 返回busy。
stap的结果是:
[[email protected] code]# stap cp_fail.stp System Call Monitoring Started (10 seconds)... Returning from: 0xffffffff8120ffe0 : do_open_exec+0x0/0x100 [kernel] Returning to : 0xffffffff81210f73 : do_execve_common.isra.24+0x203/0x6c0 [kernel] 0xffffffff812116c9 : sys_execve+0x29/0x30 [kernel] 0xffffffff816c5c98 : stub_execve+0x48/0x80 [kernel]
当然,如果执行在前,而open再写入在后,则报busy的是后面的open。
总结一下:
对于文件的写入,内核需要判断当前文件是否正在被执行,如果在的话,则拒绝写入,相关函数如下。
static inline int get_write_access(struct inode *inode) { return atomic_inc_unless_negative(&inode->i_writecount) ? 0 : -ETXTBSY; } static inline int deny_write_access(struct file *file) { struct inode *inode = file_inode(file); return atomic_dec_unless_positive(&inode->i_writecount) ? 0 : -ETXTBSY; } static inline void put_write_access(struct inode * inode) { atomic_dec(&inode->i_writecount); } static inline void allow_write_access(struct file *file) { if (file) atomic_inc(&file_inode(file)->i_writecount); } static inline bool inode_is_open_for_write(const struct inode *inode) { return atomic_read(&inode->i_writecount) > 0; }
以上是关于linux 覆盖可执行文件的问题的主要内容,如果未能解决你的问题,请参考以下文章
linux 静态库和动态库(共享库)的制作与使用(注意覆盖问题)
Android 逆向Linux 文件权限 ( Linux 权限简介 | 系统权限 | 用户权限 | 匿名用户权限 | 读 | 写 | 执行 | 更改组 | 更改用户 | 粘滞 )(代码片段
是否可以将来自两个可执行文件的覆盖率数据与 gcov/gcovr 合并?