钩子(hook)想必大家并不陌生,但是写一个好的钩子却并不是想像中那么简单,尤其是将钩子函数的具体实现放在模块中的时候,在钩子函数注销时很有可能发生竞争,严重的甚至可能导致内核的崩溃。
我们从一个错误的例子开始,假设我们需要在内核的某个地方设置一个钩子函数sample_hook:
function_ptr sample_hook = 0;
EXPORT_SYMBOL(sample_hook);
...
if (sample_hook)
retval = sample_hook(...);
...
|
我们在一个内核模块里面实现这个hook:
static ... function(...) {
....
}
static int __init init(void)
{
if (sample_hook != NULL)
return -EEXIST;
sample_hook = &function;
return 0;
}
static void __exit exit(void)
{
sample_hook = NULL;
}
module_init(init);
module_exit(exit);
|
以上代码很简单,并且大多时候它都能正常的工作,但是这并不代表它完美无暇,实际上它里面存在很多竞争条件,请耐心看我分析。
先分析模块初始化函数init,它能否保证比较和赋值操作的原子性呢?很抱歉不能。在它得出sample_hook为空的结论后到sample_hook被赋值成function之间,不能排除其它的一些模块完成了sample_hook的赋值操作,这样就会造成其间的sample_hook值被覆盖。
接下来,我们分析模块的清除函数exit,区区的一个赋空操作也会产生问题?赋值操作确实不会产生什么问题,但是因为模块卸载后要清除自身内存,而这时如果有另外一个执行体正在sample_hook的实现function中还未出来,那么function所在的那块内存因为已经被释放而变成了非法的,在接下来的function执行中将会产生Oops信息,或者是直接导致内核崩溃。
好的,现在问题分析完了,如何解答呢?其实这个问题本质也是对共享变量(包括sample_hook具体实现)的保护问题,可以简答地用读写锁(rwlock_t)解决,下面是我实现的一组函数,可以简化hook的相关编程,其中xx_bh和xx_irq分别对应于有可能在软中断或者是硬中断中调用的hook。虽然我只实现了单个参数的hook函数的情行,但是仿照它炮制多参数的实现应该也不难。
File: genhook.h
#ifndef _GENHOOK_H_
#define _GENHOOK_H_
#include <linux/kernel.h>
#include <linux/interrupt.h>
typedef void* (genhook_func_t)(void* args);
typedef struct genhook
{
rwlock_t lock;
genhook_func_t* func;
} genhook_t;
#define DEFINE_GENHOOK(h, f) genhook_t hook = { \
.lock = RW_LOCK_UNLOCKED, \
.func = f \
}
static inline void genhook_init(genhook_t *hook, genhook_func_t func)
{
rwlock_init(&hook->lock);
hook->func = func;
}
static inline void* genhook_call(genhook_t *hook, void* args)
{
void *retval = NULL;
read_lock(&hook->lock);
if (hook->func)
retval = hook->func(args);
read_unlock(&hook->lock);
return retval;
}
static inline int genhook_register(genhook_t *hook, genhook_func_t *func)
{
int retval = 0;
write_lock(&hook->lock);
if (hook->func)
retval = -EEXIST;
else
hook->func = func;
write_unlock(&hook->lock);
return retval;
}
static inline int genhook_unregister(genhook_t *hook, genhook_func_t *func)
{
int retval = 0;
write_lock(&hook->lock);
if (hook->func == NULL || hook->func != func)
retval = -EFAULT;
else
hook->func = NULL;
write_unlock(&hook->lock);
return retval;
}
static inline void* genhook_call_bh(genhook_t *hook, void *args)
{
void* retval;
local_bh_disable();
retval = genhook_call(hook, args);
local_bh_enable();
return retval;
}
static inline int genhook_register_bh(genhook_t *hook, genhook_func_t *func)
{
int retval;
local_bh_disable();
retval = genhook_register(hook, func);
local_bh_enable();
return retval;
}
static inline int genhook_unregister_bh(genhook_t *hook, genhook_func_t *func)
{
int retval;
local_bh_disable();
retval = genhook_unregister(hook, func);
local_bh_enable();
return retval;
}
static inline void* genhook_call_irq(genhook_t *hook, void *args)
{
void* retval;
unsigned long flags;
local_irq_save(flags);
retval = genhook_call(hook, args);
local_irq_restore(flags);
return retval;
}
static inline int genhook_register_irq(genhook_t *hook, genhook_func_t *func)
{
int retval;
unsigned long flags;
local_irq_save(flags);
retval = genhook_register(hook, func);
local_irq_restore(flags);
return retval;
}
static inline int genhook_unregister_irq(genhook_t *hook, genhook_func_t *func)
{
int retval;
unsigned long flags;
local_irq_save(flags);
retval = genhook_unregister(hook, func);
local_irq_restore(flags);
return retval;
}
#endif /* _GENHOOK_H_ */
|
阅读(2273) | 评论(3) | 转发(0) |