[英]Can the sys_execve() system call in the Linux kernel receive both absolute or relative paths?
內核級代碼中的sys_execve()
應接收filename
參數的絕對路徑還是相對路徑?
sys_execve
可以采用絕對或相對路徑
下面我們來驗證一下:
實驗
主文件
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
int main(void) {
syscall(__NR_execve, "../main2.out", NULL, NULL);
}
main2.c
#include <stdio.h>
int main(void) {
puts("hello main2");
}
編譯並運行:
gcc -o main.out main.c
gcc -o ../main2.out main2.c
./main.out
輸出:
hello main2
在 Ubuntu 16.10 中測試。
內核源碼
首先,進入內核樹
git grep '"\.\."' fs
我們關注fs
是因為我們知道execve
是在那里定義的。
這立即給出如下結果: https : //github.com/torvalds/linux/blob/v4.9/fs/namei.c#L1759 ,這清楚地表明他的內核知道..
:
/*
* "." and ".." are special - ".." especially so because it has
* to be able to know about the current root directory and
* parent relationships.
*/
然后我們查看 execve https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1869的定義,它做的第一件事就是在輸入路徑上調用getname()
:
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
return do_execve(getname(filename), argv, envp);
}
getname
在fs/namei.c
定義,這是上面".."
引用的來源文件。
我沒有費心去遵循完整的調用路徑,但我敢打賭getname
它最終會做..
解析。
同一個文件中的follow_dotdot
看起來特別有前途。
GDB + QEMU
閱讀源代碼很棒,但我們永遠無法確定實際使用了代碼路徑。
有兩種方法可以做到這一點:
printk
, 重新編譯, printk
, 重新編譯首先按照以下說明進行設置: 如何使用 GDB 和 QEMU 調試 Linux 內核?
現在,我們將使用兩個程序:
初始化程序
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
int main(void) {
chdir("d");
syscall(__NR_execve, "../b.out", NULL, NULL);
}
公元前
#include <unistd.h>
#include <stdio.h>
int main(void) {
puts("hello");
sleep(0xFFFFFFFF);
}
rootfs
文件結構應該是這樣的:
init
b.out
d/
一旦 GDB 運行,我們將執行以下操作:
b sys_execve
c
x/s filename
輸出../b.out
,所以我們知道這是正確的系統調用。
現在我們之前看到的有趣的".."
注釋是在一個名為walk_component
的函數中,所以讓我們看看它是否被調用:
b walk_component
c
是的,我們擊中了它。
如果我們仔細閱讀一下,我們會看到一個調用:
error = handle_dots(nd, nd->last_type);
這聽起來很有希望並且確實:
static inline int handle_dots(struct nameidata *nd, int type)
{
if (type == LAST_DOTDOT) {
if (!nd->root.mnt)
set_root(nd);
if (nd->flags & LOOKUP_RCU) {
return follow_dotdot_rcu(nd);
} else
return follow_dotdot(nd);
}
return 0;
}
那么是什么將這種type
( nd->last_type
)設置為LAST_DOTDOT
呢?
好吧,搜索= LAST_DOTDOT
的來源,我們發現link_path_walk
正在這樣做。
更好的是: bt
說link_path_walk
是一個調用者,所以很容易理解現在發生了什么。
在link_path_walk
,我們看到:
if (name[0] == '.') switch (hashlen_len(hash_len)) {
case 2:
if (name[1] == '.') {
type = LAST_DOTDOT;
因此謎團解決了: ".."
不是正在做的檢查,它挫敗了我們之前的greps!
相反,這兩個點被分別檢查(因為.
是一個子案例)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.