force_rmmod...

This commit is contained in:
gatieme
2017-06-29 00:07:44 +08:00
parent 34debbb3c8
commit bb0b815e53
12 changed files with 232 additions and 97 deletions
+137 -1
View File
@@ -261,7 +261,143 @@ static int try_stop_module(struct module *mod, int flags, int *forced)
##4.1 force_rmmod外部驱动模块强制卸载异常驱动
-------
##4.2
为此设计了 `force_rmmod` 外部模块用于卸载外部的驱动.
##4.2 情形3--没有exit函数的异常模块
-------
没有 `exit` 函数的驱动是无法被卸载的, 如果我们设计的驱动没有 exit函数, 那么将导致驱动无法卸载.
示例代码 `none_hello` 如下所示 :
```cpp
// none_exit.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Gatieme");
MODULE_DESCRIPTION("none_exit world");
static int none_exit_init(void)
{
printk("none_exit init\n");
return 0;
}
module_init(none_exit_init);
#if 0
static void none_exit_exit(void)
{
printk(KERN_ERR"exit");
}
module_exit(none_exit_exit);
#endif
```
此时构建内核模块, 加载完成后, 发现内核模块 `none_exit` 加载成功, 但是却无法卸载.
```cpp
make #
sudo insmod none_exit.ko #
dmesg | tail -l # ,
sudo rmmod none_exit
```
![无法卸载没有exit函数的驱动模块](none_exit_bug.png)
`rmmod` 卸载驱动时, 提示
```cpp
rmmod: ERROR: ../libkmod/libkmod-module.c:769 kmod_module_remove_module() could not remove 'none_exit': Device or resource busy
rmmod: ERROR: could not remove module none_exit: Device or resource busy
```
如果想要正常卸载此驱动模块, 可以通过外部注册 `exit` 函数的方式, 替换掉 `none_exit` 模块的 `exit`函数
即基本流程如下
1. 查找到 `none_exit` 模块的内核模块结构 `struct moudle`, 可以通过 `find_module` 函数查找到, 也可以参照 `find_module` 函数实现.
```cpp
mod = find_module(del_mod_name)
```
2. 设计新的 `exit` 函数, 替代原来的 `exit` 函数
```cpp
// 此处为外部注册的待卸载模块的exit函数
// 用于替代模块原来的exit函数
// 注意--此函数由于需要被待删除模块引用, 因此不能声明为static
/* static */ void force_replace_exit_module_function(void)
{
/////////////////////
// 此处完善待卸载驱动的 exit/cleanup 函数
/////////////////////
printk("module %s exit SUCCESS...\n", modname);
// platform_device_unregister((struct platform_device*)(*(int*)symbol_addr));
}
/////////////////////
// 替换/重新注册驱动的exit函数
/////////////////////
mod->exit = force_replace_exit_module_function;
```
> **注意**
>
> 外部 `exit` 函数的设计依赖于原驱动模块的实现, 特别是 `init` 函数的设计, `exit` 函数必须释放掉驱动构建的数据结构和结点信息
而此例子中, 我们用于演示的 `none_exit` 仅是一个简单的实例, 它没有那么多信息, 因此只需要一个简单的 `exit` 退出函数即可.
可以使用 `force_rmmod` 驱动实现卸载 `none_exit`, 可以根据需要自行补充 `exit` 函数 `force_replace_exit_module_function` 的实现.
编译时需要添加 `CONFIG_REPLACE_EXIT_FUNCTION` 宏, 可以通过
* 在 `Makefile` 中制定 `-DCONFIG_REPLACE_EXIT_FUNCTION`
或者
* 在 force_rmmod 驱动源码中添加 `#define CONFIG_REPLACE_EXIT_FUNCTION`
卸载 `none_exit` 的具体流程如下.
```cpp
# 通过 `modname` 制定待卸载驱动的信息
sudo insmod force_rmmod.ko modname=none_exit
# 查看是否加载成功, `exit` 函数是否正常替换
dmesg | tail -l
# 卸载 `none_exit` 驱动
sudo rmmod none_exit
# 卸载 `force_rmmod` 驱动
sudo rmmod force_rmmod
```
![强制卸载](non_exit_resolve.png)
可以看到驱动 `none_exit` 被正常卸载
由于替换了 `none_exit` 驱动中的 `exit` 函数为 `force_rmmod` 驱动中的函数, 因此要首先卸载 `none_exit` 的时候, 再卸载 `force_rmmod`, 否则 `none_exit` 将同样卸载失败.
##4.3 情形4--引用计数不为0的异常情形
-------
驱动异常, 或者
#参考资料
-------
@@ -20,7 +20,7 @@ DRIVER_DESC := "Linux input module for Elo MultiTouch(MT) devices"
DRIVER_LICENSE := "Dual BSD/GPL"
MODULE_NAME := hello
MODULE_NAME := none_exit
@@ -49,27 +49,21 @@ modules_install:
insmod:
sudo insmod $(MODULE_NAME).ko
.PHNOY:
modules modules_install clean
reinsmod:
sudo rmmod $(MODULE_NAME)
sudo insmod $(MODULE_NAME).ko
github:
cd $(ROOT) && make github
rmmod:
sudo rmmod $(MODULE_NAME)
test :
sudo ../injector/memInjector -l stack -m random -t word_0 --time 1 --timeout 3 -p 1
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
rm -f modules.order Module.symvers Module.markers
.PHNOY:
modules modules_install clean
endif
@@ -0,0 +1,27 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Gatieme");
MODULE_DESCRIPTION("none_exit world");
static int none_exit_init(void)
{
printk("none_exit init\n");
return 0;
}
module_init(none_exit_init);
#if 0
static void none_exit_exit(void)
{
printk(KERN_ERR"exit");
}
module_exit(none_exit_exit);
#endif
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

@@ -1,30 +0,0 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
//#include <linux/list.h>
//#include <linux/mm.h>
//#include <linux/mm_types.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Gatieme");
MODULE_DESCRIPTION("hello world");
static int hello_init(void)
{
printk("hello init\n");
return 0;
}
#if 0
static void hello_exit(void)
{
printk(KERN_ERR"exit");
}
#endif
module_init(hello_init);
//module_exit(hello_exit);
@@ -1,47 +0,0 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
//#include <linux/list.h>
//#include <linux/mm.h>
//#include <linux/mm_types.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Gatieme");
MODULE_DESCRIPTION("hello world");
/*
* print the module information
*/
static void print_module(void)
{
struct module *mod;
printk(KERN_ALERT "this module: %p==%p\n", &__this_module, THIS_MODULE);
printk(KERN_ALERT "module state: %d\n", THIS_MODULE->state);
printk(KERN_ALERT "module name: %s\n", THIS_MODULE->name);
list_for_each_entry(mod, *(&THIS_MODULE->list.prev), list);
printk(KERN_ALERT "module name: %s\n", mod->name);
printk(KERN_ALERT "module state: %d\n", THIS_MODULE->state);
}
static int hello_init(void)
{
print_module();
printk(KERN_ALERT "run in cpu %d\n", get_cpu());
printk(KERN_ALERT "PAGE_OFFSET : 0x%lx, TASK_SIZE : 0x%lx", PAGE_OFFSET,
TASK_SIZE);
return 0;
}
static void hello_exit(void)
{
printk(KERN_ERR "exit");
}
module_init(hello_init);
module_exit(hello_exit);
Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

@@ -6,6 +6,7 @@
#include <asm-generic/local.h>
#include <linux/platform_device.h>
#include <linux/kallsyms.h>
#include <linux/sched.h>
/*
@@ -24,15 +25,20 @@ module_param(modname, charp, 0644);
MODULE_PARM_DESC(modname, "The name of module you want do clean or delete...\n");
#define CONFIG_REPLACE_EXIT_FUNCTION
#ifdef CONFIG_REPLACE_EXIT_FUNCTION
static void force_replace_exit_module_function(void)
// 此处为外部注册的待卸载模块的exit函数
// 用于替代模块原来的exit函数
// 注意--此函数由于需要被待删除模块引用, 因此不能声明为static
/* static */ void force_replace_exit_module_function(void)
{
int symbol_addr;
/////////////////////
// 此处完善待卸载驱动的 exit/cleanup 函数
/////////////////////
printk("XXXXXX, force!\n");
symbol_addr = kallsyms_lookup_name(modname);
platform_device_unregister((struct platform_device*)(*(int*)symbol_addr));
printk("module %s exit SUCCESS...\n", modname);
// platform_device_unregister((struct platform_device*)(*(int*)symbol_addr));
}
#endif // CONFIG_REPLACE_EXIT_FUNCTION
@@ -41,6 +47,13 @@ static int force_cleanup_module(char *del_mod_name)
{
struct module *mod = NULL, *relate = NULL;
int cpu;
#ifdef CONFIG_REPLACE_EXIT_FUNCTION
void *origin_exit_addr = NULL;
#endif
/////////////////////
// 找到待删除模块的内核module信息
/////////////////////
#if 0
// 方法一, 遍历内核模块树list_mod查询
struct module *list_mod = NULL;
@@ -74,7 +87,7 @@ static int force_cleanup_module(char *del_mod_name)
}
/////////////////////
// 如果有其他驱动依赖于当前驱动, 则退出卸载
// 如果有其他驱动依赖于当前驱动, 则不能强制卸载, 立刻退出
/////////////////////
/* 如果有其他模块依赖于 del_mod */
if (!list_empty(&mod->source_list))
@@ -104,13 +117,24 @@ static int force_cleanup_module(char *del_mod_name)
//per_cpu_ptr(mod->refptr, cpu)->decs;
//module_put(mod);
}
atomic_set(&mod->refcnt, 1);
//atomic_set(&mod->refcnt, 1);
#ifdef CONFIG_REPLACE_EXIT_FUNCTION
/////////////////////
// 重新注册驱动的exit函数
/////////////////////
origin_exit_addr = mod->exit;
if (origin_exit_addr == NULL)
{
printk("module %s don't have exit function...\n", mod->name);
}
else
{
printk("module %s exit function address %p\n", mod->name, origin_exit_addr);
}
mod->exit = force_replace_exit_module_function;
printk("replace module %s exit function address (%p -=> %p)\n", mod->name, origin_exit_addr, mod->exit);
#endif
printk("[after] name:%s, state:%d, refcnt:%u\n",
@@ -0,0 +1,22 @@
#!/usr/bin/stap
probe begin
{
log("begin to probe")
}
probe syscall.open
{
printf("%s(%d) open (%s)\n", execname( ), pid( ), argstr)
}
# after 4 seconds
probe timer.ms(4000)
{
exit( )
}
probe end
{
log("end to probe")
}
+9
View File
@@ -0,0 +1,9 @@
近期想做的事情
1--实验一个强制卸载内核异常驱动的外部模块
外部驱动
systemtap实现
守护进程或者proc方式实验
2--