内核级代码中的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()
参数的绝对或相对路径?
答案 0 :(得分:1)
sys_execve
可以采用绝对或相对路径
让我们通过以下方式验证它:
<强>实验强>
<强> 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);
}
getname
在fs/namei.c
中定义,这是上述".."
引用来自的文件。
我并不打算遵循完整的通话路径,但我敢打赌getname
它最终会..
解决。
follow_dotdot
在同一个文件中看起来特别有希望。
GDB + QEMU
阅读源代码很棒,但我们永远无法确定代码路径是否实际使用过。
有两种方法可以做到:
printk
,重新编译,printk
,重新编译首先按照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;
}
那么将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;
然后解决了这个问题:".."
不是正在进行的检查,这挫败了我们以前的蠢事!
相反,两个点被单独检查(因为.
是一个子例子。)