execve系统调用_Linux内核中的sys_execve()系统调用可以同时接收绝对路径还是相对路径?...

本文详细探讨了Linux内核中的execve系统调用如何处理绝对路径和相对路径。通过源代码分析、实验验证以及GDB+QEMU的调试,揭示了内核如何识别和处理'..'路径。实验表明,execve可以接受绝对或相对路径,并通过getname函数进行路径解析,最终确认'..'的处理发生在walk_component函数中。
摘要由CSDN通过智能技术生成

Shall sys_execve() in kernel level code receive absolute or relative path for the filename parameter?

解决方案

sys_execve can take either absolute or relative paths

Let's verify it in the following ways:

experiment with a raw system call

read the kernel source

run GDB on kernel + QEMU to verify our source analysis

Experiment

main.c

#define _GNU_SOURCE

#include

#include

int main(void) {

syscall(__NR_execve, "../main2.out", NULL, NULL);

}

main2.c

#include

int main(void) {

puts("hello main2");

}

Compile and run:

gcc -o main.out main.c

gcc -o ../main2.out main2.c

./main.out

Output:

hello main2

Tested in Ubuntu 16.10.

Kernel source

First, just go into the kernel tree

git grep '"\.\."' fs

We focus on fs because we know that execve is defined there.

This immediately gives results like: https://github.com/torvalds/linux/blob/v4.9/fs/namei.c#L1759 which clearly indicate that he kernel knows about ..:

/*

* "." and ".." are special - ".." especially so because it has

* to be able to know about the current root directory and

* parent relationships.

*/

We then look at the definition of execve https://github.com/torvalds/linux/blob/v4.9/fs/exec.c#L1869 and the first thing it does is to call getname() on the input path:

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 is defined in fs/namei.c, which is the file where the above ".." quote came from.

I haven't bothered to follow the full call path, but I bet that getname it ends up doing .. resolution.

follow_dotdot in the same file looks specially promising.

GDB + QEMU

Reading the source is great, but we can never be sure that the code paths are actually used.

There are two ways to do that:

printk, recompile, printk, recompile

GDB + QEMU. Setup is a bit rougher, but once done it is pure bliss

Now, we will use two programs:

init.c

#define _GNU_SOURCE

#include

#include

int main(void) {

chdir("d");

syscall(__NR_execve, "../b.out", NULL, NULL);

}

b.c

#include

#include

int main(void) {

puts("hello");

sleep(0xFFFFFFFF);

}

And the rootfs file structure should be like:

init

b.out

d/

Once GDB is running, we will do:

b sys_execve

c

x/s filename

Outputs ../b.out, so we know it is the right syscall.

Now the interesting ".." comment we had seen before was in a function called walk_component, so let's see if that is called:

b walk_component

c

And yes, we hit it.

If we read a bit into it, we see a call:

error = handle_dots(nd, nd->last_type);

which sounds promising and does:

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;

}

So what is it that sets this type (nd->last_type) to LAST_DOTDOT?

Well, search the source for = LAST_DOTDOT, and we find that link_path_walk is doing it.

And even better: bt says that link_path_walk is a caller, so it will be easy to understand what is going on now.

In link_path_walk, we see:

if (name[0] == '.') switch (hashlen_len(hash_len)) {

case 2:

if (name[1] == '.') {

type = LAST_DOTDOT;

and thus the mistery is solved: ".." was not the check that was being done, which foiled our previous greps!

Instead, the two dots were being checked separately (because . is a subcase).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值