簡體   English   中英

Linux內核中的sys_execve()系統調用可以接收絕對路徑還是相對路徑?

[英]Can the sys_execve() system call in the Linux kernel receive both absolute or relative paths?

內核級代碼中的sys_execve()應接收filename參數的絕對路徑還是相對路徑?

sys_execve可以采用絕對或相對路徑

下面我們來驗證一下:

  • 嘗試原始系統調用
  • 閱讀內核源碼
  • 在 kernel + QEMU 上運行 GDB 來驗證我們的源碼分析

實驗

主文件

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

getnamefs/namei.c定義,這是上面".."引用的來源文件。

我沒有費心去遵循完整的調用路徑,但我敢打賭getname它最終會做..解析。

同一個文件中的follow_dotdot看起來特別有前途。

GDB + QEMU

閱讀源代碼很棒,但我們永遠無法確定實際使用了代碼路徑。

有兩種方法可以做到這一點:

  • printk , 重新編譯, printk , 重新編譯
  • GDB + QEMU。 設置有點粗糙,但一旦完成,它就是純粹的幸福

首先按照以下說明進行設置: 如何使用 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;
}

那么是什么將這種typend->last_type )設置為LAST_DOTDOT呢?

好吧,搜索= LAST_DOTDOT的來源,我們發現link_path_walk正在這樣做。

更好的是: btlink_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM