日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 可执行文件与写操作的同步问题

發布時間:2025/3/20 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 可执行文件与写操作的同步问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  當一個可執行文件已經為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

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的linux 可执行文件与写操作的同步问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。