[英]How can I Execute/Call a user-space defined function from Linux kernel space module?
[英]Execute/invoke user-space program, and get its pid, from a kernel module
我查看了內核API,第1部分:從內核調用用戶空間應用程序 ,以及從內核空間執行用戶空間函數 - Stack Overflow - 這是一個小內核模塊, callmodule.c
,演示了:
// http://people.ee.ethz.ch/~arkeller/linux/code/usermodehelper.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
static int __init callmodule_init(void)
{
int ret = 0;
char userprog[] = "/path/to/mytest";
char *argv[] = {userprog, "2", NULL };
char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
printk("callmodule: init %s\n", userprog);
/* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*/
/* returns 0 if usermode process was started successfully, errorvalue otherwise*/
/* no possiblity to get return value of usermode process*/
ret = call_usermodehelper(userprog, argv, envp, UMH_WAIT_EXEC);
if (ret != 0)
printk("error in call to usermodehelper: %i\n", ret);
else
printk("everything all right\n");
return 0;
}
static void __exit callmodule_exit(void)
{
printk("callmodule: exit\n");
}
module_init(callmodule_init);
module_exit(callmodule_exit);
MODULE_LICENSE("GPL");
...使用Makefile
:
obj-m += callmodule.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
當我通過sudo insmod ./callmodule.ko && sudo rmmod callmodule
,我進入/var/log/syslog
:
Feb 10 00:42:45 mypc kernel: [71455.260355] callmodule: init /path/to/mytest
Feb 10 00:42:45 mypc kernel: [71455.261218] everything all right
Feb 10 00:42:45 mypc kernel: [71455.286131] callmodule: exit
......這顯然意味着一切順利。 (使用Linux 2.6.38-16-通用#67-Ubuntu SMP)
我的問題是 - 如何從內核模塊中獲取進程的PID? 是否有一個類似於call_usermodehelper
進程,這將允許我在內核空間中實例化一個用戶空間進程,並獲得它的pid?
請注意,可能無法使用call_usermodehelper
並獲取實例化的進程PID:
Re:call_usermodehelper的pid? - Linux Kernel Newbies
我想在內核模塊中創建一個用戶空間進程,並能夠殺死它,向它發送信號等等......
我可以知道它的pid嗎?
不,你不能。 但是因為在實現中pid是已知的,使得它可用的補丁不會太難(注意,內核中的錯誤總是負的,並且pid是正的,限制為2 ** 16)。 您必須修改所有期望0成功的調用者。
我稍微探討了一下這些來源,似乎最終有一個調用鏈: call_usermodehelper
- > call_usermodehelper_setup
- > __call_usermodehelper
,它看起來像:
static void __call_usermodehelper(struct work_struct *work)
{
struct subprocess_info *sub_info =
container_of(work, struct subprocess_info, work);
// ...
if (wait == UMH_WAIT_PROC)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else
pid = kernel_thread(____call_usermodehelper, sub_info,
CLONE_VFORK | SIGCHLD);
...
...所以使用內核線程的PID,但它沒有保存; 另外, work_struct
和subprocess_info
都沒有pid
字段( task_struct
可以,但這里沒有任何東西似乎使用task_struct
)。 記錄這個pid需要更改內核源代碼 - 而且我想避免這種情況,這也是我對call_usermodehelper
以外的方法感興趣的原因......
我對kmod.c中實現的理解的初步答案。
如果你看一下call_usermodehelper
的代碼,你會看到它調用call_usermodehelper_setup
然后調用call_usermodehelper_exec
。
call_usermodehelper_setup
將一個init函數作為參數,該函數將在do_execve
之前do_execve
。 我相信執行init函數時current
的值將獲得用戶進程的task_struct
。
所以要獲得pid,你需要:
call_usermodehelper
的實現。 call_usermodehelper_setup
。 task_struct
,然后檢索PID。 嗯,這很乏味......下面是一個相當hacky的方法來實現這一點,至少在我的平台上,如callmodule.c
(可以使用與上面相同的Makefile)。 因為我無法相信這是應該這樣做的方式,所以仍然歡迎更合適的答案(希望,也可以使用我可以測試的代碼示例)。 但至少,它只是作為一個內核模塊的工作 - 不需要修補內核本身 - 對於2.6.38版本,這對我來說非常重要。
基本上,我復制了所有函數(用“B”后綴重命名),直到PID可用。 然后我使用帶有額外字段的subprocess_info
副本來保存它(雖然這不是絕對必要的:為了不在返回值方面弄亂函數簽名,我必須將pid保存為全局變量;離開它作為一項運動)。 現在,當我運行sudo insmod ./callmodule.ko && sudo rmmod callmodule
,在/var/log/syslog
我得到:
Feb 10 18:53:02 mypc kernel: [ 2942.891886] callmodule: > init /path/to/mytest
Feb 10 18:53:02 mypc kernel: [ 2942.891912] callmodule: symbol @ 0xc1065b60 is wait_for_helper+0x0/0xb0
Feb 10 18:53:02 mypc kernel: [ 2942.891923] callmodule: symbol @ 0xc1065ed0 is ____call_usermodehelper+0x0/0x90
Feb 10 18:53:02 mypc kernel: [ 2942.891932] callmodule:a: pid 0
Feb 10 18:53:02 mypc kernel: [ 2942.891937] callmodule:b: pid 0
Feb 10 18:53:02 mypc kernel: [ 2942.893491] callmodule: : pid 23306
Feb 10 18:53:02 mypc kernel: [ 2942.894474] callmodule:c: pid 23306
Feb 10 18:53:02 mypc kernel: [ 2942.894483] callmodule: everything all right; pid 23306
Feb 10 18:53:02 mypc kernel: [ 2942.894494] callmodule: pid task a: ec401940 c: mytest p: [23306] s: runnable
Feb 10 18:53:02 mypc kernel: [ 2942.894502] callmodule: parent task a: f40aa5e0 c: kworker/u:1 p: [14] s: stopped
Feb 10 18:53:02 mypc kernel: [ 2942.894510] callmodule: - mytest [23306]
Feb 10 18:53:02 mypc kernel: [ 2942.918500] callmodule: < exit
這里令人討厭的問題之一是,一旦你開始復制函數,在某個時候,你會到達一個使用未導出的內核函數的點,就像在這種情況下wait_for_helper
。 我所做的基本上是查看/proc/kallsyms
(記住sudo
!)來獲取例如wait_for_helper
絕對地址,然后硬編碼內核模塊中的那些作為函數指針 - 似乎工作。 另一個問題是內核源代碼中的函數引用了enum umh_wait
,它不能用作模塊的參數(需要簡單地將它們轉換為使用int
)。
因此模塊啟動用戶空間進程,獲取PID( 注意“ 內核調用的PID實際上是內核級線程ID(通常稱為TID)... ...在POSIX意義上的”進程“中被認為是PID,另一方面,在內核中稱為“線程組ID”或“TGID”。 “ )獲取相應的task_struct
及其父級,並嘗試列出父級和生成的進程本身的所有子級。 所以我可以看到kworker/u:1
通常是父級,並且除了mytest
之外沒有其他孩子 - 而且因為mytest
非常簡單(在我的情況下,只是一次寫入磁盤文件),它不會產生任何線程擁有,所以它也沒有孩子。
我遇到了幾個需要重啟的Oopses - 我認為它們現在已經解決了,但為了以防萬一,請注意。
這是callmodule.c
代碼(末尾有一些注釋/鏈接):
// callmodule.c with pid, url: https://stackoverflow.com/questions/21668727/
#include <linux/module.h>
#include <linux/slab.h> //kzalloc
#include <linux/syscalls.h> // SIGCHLD, ... sys_wait4, ...
#include <linux/kallsyms.h> // kallsyms_lookup, print_symbol
// global variable - to avoid intervening too much in the return of call_usermodehelperB:
static int callmodule_pid;
// >>>>>>>>>>>>>>>>>>>>>>
// modified kernel functions - taken from
// http://lxr.missinglinkelectronics.com/linux+v2.6.38/+save=include/linux/kmod.h
// http://lxr.linux.no/linux+v2.6.38/+save=kernel/kmod.c
// define a modified struct (with extra pid field) here:
struct subprocess_infoB {
struct work_struct work;
struct completion *complete;
char *path;
char **argv;
char **envp;
int wait; //enum umh_wait wait;
int retval;
int (*init)(struct subprocess_info *info);
void (*cleanup)(struct subprocess_info *info);
void *data;
pid_t pid;
};
// forward declare:
struct subprocess_infoB *call_usermodehelper_setupB(char *path, char **argv,
char **envp, gfp_t gfp_mask);
static inline int
call_usermodehelper_fnsB(char *path, char **argv, char **envp,
int wait, //enum umh_wait wait,
int (*init)(struct subprocess_info *info),
void (*cleanup)(struct subprocess_info *), void *data)
{
struct subprocess_info *info;
struct subprocess_infoB *infoB;
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
int ret;
populate_rootfs_wait(); // is in linux-headers-2.6.38-16-generic/include/linux/kmod.h
infoB = call_usermodehelper_setupB(path, argv, envp, gfp_mask);
printk(KBUILD_MODNAME ":a: pid %d\n", infoB->pid);
info = (struct subprocess_info *) infoB;
if (info == NULL)
return -ENOMEM;
call_usermodehelper_setfns(info, init, cleanup, data);
printk(KBUILD_MODNAME ":b: pid %d\n", infoB->pid);
// this must be called first, before infoB->pid is populated (by __call_usermodehelperB):
ret = call_usermodehelper_exec(info, wait);
// assign global pid here, so rest of the code has it:
callmodule_pid = infoB->pid;
printk(KBUILD_MODNAME ":c: pid %d\n", callmodule_pid);
return ret;
}
static inline int
call_usermodehelperB(char *path, char **argv, char **envp, int wait) //enum umh_wait wait)
{
return call_usermodehelper_fnsB(path, argv, envp, wait,
NULL, NULL, NULL);
}
/* This is run by khelper thread */
static void __call_usermodehelperB(struct work_struct *work)
{
struct subprocess_infoB *sub_infoB =
container_of(work, struct subprocess_infoB, work);
int wait = sub_infoB->wait; // enum umh_wait wait = sub_info->wait;
pid_t pid;
struct subprocess_info *sub_info;
// hack - declare function pointers, to use for wait_for_helper/____call_usermodehelper
int (*ptrwait_for_helper)(void *data);
int (*ptr____call_usermodehelper)(void *data);
// assign function pointers to verbatim addresses as obtained from /proc/kallsyms
ptrwait_for_helper = (void *)0xc1065b60;
ptr____call_usermodehelper = (void *)0xc1065ed0;
sub_info = (struct subprocess_info *)sub_infoB;
/* CLONE_VFORK: wait until the usermode helper has execve'd
* successfully We need the data structures to stay around
* until that is done. */
if (wait == UMH_WAIT_PROC)
pid = kernel_thread((*ptrwait_for_helper), sub_info, //(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else
pid = kernel_thread((*ptr____call_usermodehelper), sub_info, //(____call_usermodehelper, sub_info,
CLONE_VFORK | SIGCHLD);
printk(KBUILD_MODNAME ": : pid %d\n", pid);
// grab and save the pid here:
sub_infoB->pid = pid;
switch (wait) {
case UMH_NO_WAIT:
call_usermodehelper_freeinfo(sub_info);
break;
case UMH_WAIT_PROC:
if (pid > 0)
break;
/* FALLTHROUGH */
case UMH_WAIT_EXEC:
if (pid < 0)
sub_info->retval = pid;
complete(sub_info->complete);
}
}
/**
* call_usermodehelper_setup - prepare to call a usermode helper
*/
struct subprocess_infoB *call_usermodehelper_setupB(char *path, char **argv,
char **envp, gfp_t gfp_mask)
{
struct subprocess_infoB *sub_infoB;
sub_infoB = kzalloc(sizeof(struct subprocess_infoB), gfp_mask);
if (!sub_infoB)
goto out;
INIT_WORK(&sub_infoB->work, __call_usermodehelperB);
sub_infoB->path = path;
sub_infoB->argv = argv;
sub_infoB->envp = envp;
out:
return sub_infoB;
}
// <<<<<<<<<<<<<<<<<<<<<<
static int __init callmodule_init(void)
{
int ret = 0;
char userprog[] = "/path/to/mytest";
char *argv[] = {userprog, "2", NULL };
char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
struct task_struct *p;
struct task_struct *par;
struct task_struct *pc;
struct list_head *children_list_head;
struct list_head *cchildren_list_head;
char *state_str;
printk(KBUILD_MODNAME ": > init %s\n", userprog);
/* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*/
/* returns 0 if usermode process was started successfully, errorvalue otherwise*/
/* no possiblity to get return value of usermode process*/
// note - only one argument allowed for print_symbol
print_symbol(KBUILD_MODNAME ": symbol @ 0xc1065b60 is %s\n", 0xc1065b60); // shows wait_for_helper+0x0/0xb0
print_symbol(KBUILD_MODNAME ": symbol @ 0xc1065ed0 is %s\n", 0xc1065ed0); // shows ____call_usermodehelper+0x0/0x90
ret = call_usermodehelperB(userprog, argv, envp, UMH_WAIT_EXEC);
if (ret != 0)
printk(KBUILD_MODNAME ": error in call to usermodehelper: %i\n", ret);
else
printk(KBUILD_MODNAME ": everything all right; pid %d\n", callmodule_pid);
// find the task:
// note: sometimes p may end up being NULL here, causing kernel oops -
// just exit prematurely in that case
rcu_read_lock();
p = pid_task(find_vpid(callmodule_pid), PIDTYPE_PID);
rcu_read_unlock();
if (p == NULL) {
printk(KBUILD_MODNAME ": p is NULL - exiting\n");
return 0;
}
// p->comm should be the command/program name (as per userprog)
// (out here that task is typically in runnable state)
state_str = (p->state==-1)?"unrunnable":((p->state==0)?"runnable":"stopped");
printk(KBUILD_MODNAME ": pid task a: %p c: %s p: [%d] s: %s\n",
p, p->comm, p->pid, state_str);
// find parent task:
// parent task could typically be: c: kworker/u:1 p: [14] s: stopped
par = p->parent;
if (par == NULL) {
printk(KBUILD_MODNAME ": par is NULL - exiting\n");
return 0;
}
state_str = (par->state==-1)?"unrunnable":((par->state==0)?"runnable":"stopped");
printk(KBUILD_MODNAME ": parent task a: %p c: %s p: [%d] s: %s\n",
par, par->comm, par->pid, state_str);
// iterate through parent's (and our task's) child processes:
rcu_read_lock(); // read_lock(&tasklist_lock);
list_for_each(children_list_head, &par->children){
p = list_entry(children_list_head, struct task_struct, sibling);
printk(KBUILD_MODNAME ": - %s [%d] \n", p->comm, p->pid);
// note: trying to print "%p",p here results with oops/segfault:
// printk(KBUILD_MODNAME ": - %s [%d] %p\n", p->comm, p->pid, p);
if (p->pid == callmodule_pid) {
list_for_each(cchildren_list_head, &p->children){
pc = list_entry(cchildren_list_head, struct task_struct, sibling);
printk(KBUILD_MODNAME ": - - %s [%d] \n", pc->comm, pc->pid);
}
}
}
rcu_read_unlock(); //~ read_unlock(&tasklist_lock);
return 0;
}
static void __exit callmodule_exit(void)
{
printk(KBUILD_MODNAME ": < exit\n");
}
module_init(callmodule_init);
module_exit(callmodule_exit);
MODULE_LICENSE("GPL");
/*
NOTES:
// assign function pointers to verbatim addresses as obtained from /proc/kallsyms:
// ( cast to void* to avoid "warning: assignment makes pointer from integer without a cast",
// see also https://stackoverflow.com/questions/3941793/what-is-guaranteed-about-the-size-of-a-function-pointer )
// $ sudo grep 'wait_for_helper\|____call_usermodehelper' /proc/kallsyms
// c1065b60 t wait_for_helper
// c1065ed0 t ____call_usermodehelper
// protos:
// static int wait_for_helper(void *data)
// static int ____call_usermodehelper(void *data)
// see also:
// http://www.linuxforu.com/2012/02/function-pointers-and-callbacks-in-c-an-odyssey/
// from include/linux/kmod.h:
//~ enum umh_wait {
//~ UMH_NO_WAIT = -1, /* don't wait at all * /
//~ UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process * /
//~ UMH_WAIT_PROC = 1, /* wait for the process to complete * /
//~ };
// however, note:
// /usr/src/linux-headers-2.6.38-16-generic/include/linux/kmod.h:
// #define UMH_NO_WAIT 0 ; UMH_WAIT_EXEC 1 ; UMH_WAIT_PROC 2 ; UMH_KILLABLE 4 !
// those defines end up here, regardless of the enum definition above
// (NB: 0,1,2,4 enumeration starts from kmod.h?v=3.4 on lxr.free-electrons.com !)
// also, note, in "generic" include/, prototypes of call_usermodehelper(_fns)
// use int wait, and not enum umh_wait wait ...
// seems these cannot be used from a module, nonetheless:
//~ extern int wait_for_helper(void *data);
//~ extern int ____call_usermodehelper(void *data);
// we probably would have to (via http://www.linuxconsulting.ro/pidwatcher/)
// edit /usr/src/linux/kernel/ksyms.c and add:
//EXPORT_SYMBOL(wait_for_helper);
// but that is kernel re-compilation...
// https://stackoverflow.com/questions/19360298/triggering-user-space-with-kernel
// You should not be using PIDs to identify processes within the kernel. The process can exit and a different process re-use that PID. Instead, you should be using a pointer to the task_struct for the process (rather than storing current->pid at registration time, just store current)
# reports task name from the pid (pid_task(find_get_pid(..)):
http://tuxthink.blogspot.dk/2012/07/module-to-find-task-from-its-pid.html
// find the task:
//~ rcu_read_lock();
// uprobes uses this - but find_task_by_pid is not exported for modules:
//~ p = find_task_by_pid(callmodule_pid);
//~ if (p)
//~ get_task_struct(p);
//~ rcu_read_unlock();
// see: [http://www.gossamer-threads.com/lists/linux/kernel/1260996 find_task_by_pid() problem | Linux | Kernel]
// https://stackoverflow.com/questions/18408766/make-a-system-call-to-get-list-of-processes
// this macro loops through *all* processes; our callmodule_pid should be listed by it
//~ for_each_process(p)
//~ pr_info("%s [%d]\n", p->comm, p->pid);
// [https://lists.debian.org/debian-devel/2008/05/msg00034.html Re: problems for making kernel module]
// note - WARNING: "tasklist_lock" ... undefined; because tasklist_lock removed in 2.6.1*:
// "tasklist_lock protects the kernel internal task list. Modules have no business looking at it";
// https://stackoverflow.com/questions/13002444/list-all-threads-within-the-current-process
// "all methods that loop over the task lists need to be wrapped in rcu_read_lock(); / rcu_read_unlock(); to be correct."
// https://stackoverflow.com/questions/19208487/kernel-module-that-iterates-over-all-tasks-using-depth-first-tree
// https://stackoverflow.com/questions/5728592/how-can-i-get-the-children-process-list-in-kernel-code
// https://stackoverflow.com/questions/1446239/traversing-task-struct-children-in-linux-kernel
// https://stackoverflow.com/questions/8207160/kernel-how-to-iterate-the-children-of-the-current-process
// https://stackoverflow.com/questions/10262017/linux-kernel-list-list-head-init-vs-init-list-head
// https://stackoverflow.com/questions/16230524/explain-list-for-each-entry-and-list-for-each-entry-safe "list_entry is just an alias for container_of"
*/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.