Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2005701
  • 博文数量: 369
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 4271
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-21 00:59
文章分类

全部博文(369)

文章存档

2013年(1)

2011年(2)

2010年(10)

2009年(16)

2008年(33)

2007年(146)

2006年(160)

2005年(1)

分类: LINUX

2007-06-15 23:38:08

钩子(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_ */

阅读(2235) | 评论(3) | 转发(0) |
0

上一篇:情绪化

下一篇:端午节

给主人留下些什么吧!~~

xiaosuo2010-05-24 10:40:09

不幸的是,内核并没有解决,实际上也没办法很多解决。不过,我上面的解法有些低效,可以改成RCU机制。

chinaunix网友2010-05-21 10:48:26

内核应该已经很好的解决了你所说的卸载时可能会的情况,

chinaunix网友2010-03-08 13:01:42

难道内核中的模块管理代码没有把module_init里的危险动作原子化?我觉得应该是把诸如替换系统调用、多个模块修改共同数的代码自动原子化才对啊。。。