[英]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.