Linux内核中的sys_execve()系统调用是否可以接收绝对或相对路径?

时间:2015-11-22 07:43:36

标签: linux linux-kernel

内核级代码中的HTML: <body> <p> <pre>test</pre> </p> </body> CSS: .test { color: red; font-weight: bold; background: black; } pre { font-family: monospace; font-size: 1.25em; text-align: left; line-height: 0px; border: 1px solid #000; height: 50px; background-color:#DDFFDD; padding: 10px 0 0 10px; } 是否会收到sys_execve()参数的绝对或相对路径?

1 个答案:

答案 0 :(得分:1)

sys_execve可以采用绝对或相对路径

让我们通过以下方式验证它:

  • 使用原始系统调用进行实验
  • 阅读内核源代码
  • 在内核+ QEMU上运行GDB以验证我们的源分析

<强>实验

<强> a.c

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>

int main(void) {
    syscall(__NR_execve, "../b.out", NULL, NULL);
}

<强> b.c

#include <stdio.h>

int main(void) {
    puts("hello");
}

然后:

gcc a.c -o a.out
gcc b.c -o ../b.out
./a.out

输出:

hello

在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。设置有点粗糙,但一旦完成它就是纯粹的幸福

首先按照How to debug the Linux kernel with GDB and QEMU?

中的说明进行设置

现在,我们将使用两个程序:

<强> init.c

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>

int main(void) {
    chdir("d");
    syscall(__NR_execve, "../b.out", NULL, NULL);
}

<强> b.c

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

然后解决了这个问题:".."不是正在进行的检查,这挫败了我们以前的蠢事!

相反,两个点被单独检查(因为.是一个子例子。)