Linux Kernel drivers/char/n_tty.c驱动空指针引用拒绝服务漏洞

来源:岁月联盟 编辑:zhuzhu 时间:2009-12-05
Linux Kernel drivers/char/n_tty.c驱动空指针引用拒绝服务漏洞 影响版本:
Linux kernel 2.6.31.5漏洞描述:
BUGTRAQ  ID: 37147

Linux Kernel是开放源码操作系统Linux所使用的内核。

Linux Kernel所使用的drivers/char/n_tty.c驱动中存在空指针引用漏洞:

/**
*    n_tty_close        -    close the ldisc for this tty
*    @tty: device
*
*    Called from the terminal layer when this line discipline is
*    being shut down, either because of a close or becsuse of a
*    discipline change. The function will not be called while other
*    ldisc methods are in progress.
*/

static void n_tty_close(struct tty_struct *tty)
{
    n_tty_flush_buffer(tty);
    if (tty->read_buf) {
        kfree(tty->read_buf);
        tty->read_buf = NULL;
    }
    if (tty->echo_buf) {
        kfree(tty->echo_buf);
        tty->echo_buf = NULL;
    }
}

这个例程的参数是以指向tty_struct结构的指针形式传送的TTY,首先调用n_tty_flush_buffer()然后释放所分配的read_buf和echo_buf缓冲区,并将其设置为NULL。在这里所调用的第一个函数为:

/**
*    n_tty_flush_buffer    -    clean input queue
*    @tty:    terminal device
*
*    Flush the input buffer. Called when the line discipline is
*    being closed, when the tty layer wants the buffer flushed (eg
*    at hangup) or when the N_TTY line discipline internally has to
*    clean the pending queue (for example some signals).
*
*    Locking: ctrl_lock, read_lock.
*/

static void n_tty_flush_buffer(struct tty_struct *tty)
{
    unsigned long flags;
    /* clear everything and unthrottle the driver */
    reset_buffer_flags(tty);

    if (!tty->link)
        return;

    spin_lock_irqsave(&tty->ctrl_lock, flags);
    if (tty->link->packet) {
        tty->ctrl_status |= TIOCPKT_FLUSHREAD;
        wake_up_interruptible(&tty->link->read_wait);
    }
    spin_unlock_irqrestore(&tty->ctrl_lock, flags);
}

上面所保持的唯一一个锁是n_tty_flush_buffer()中的自旋锁。由于这个过程中没有其他的锁,这可能导致竞争条件。具体来讲,在释放缓冲区和kfree()指针期间n_tty_close()没有锁定,如果TTY接收到了字符,代码执行就会到达以下函数:

static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
{
    unsigned long flags;
    int parmrk;

    if (tty->raw) {
        put_tty_queue(c, tty);
        return;
    }
   ...
    put_tty_queue(c, tty);
}

结果会是:

static void put_tty_queue(unsigned char c, struct tty_struct *tty)
{
    unsigned long flags;
    /*
     *    The problem of stomping on the buffers ends here.
     *    Why didn’t anyone see this one coming? --AJK
    */
    spin_lock_irqsave(&tty->read_lock, flags);
    put_tty_queue_nolock(c, tty);
    spin_unlock_irqrestore(&tty->read_lock, flags);
}

这会锁定TTY并调用执行以下内容的相应非锁定例程:

static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty)
{
    if (tty->read_cnt < N_TTY_BUF_SIZE) {
        tty->read_buf[tty->read_head] = c;
        tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
        tty->read_cnt++;
    }
}

由于这时tty->read_buf可能已被释放并在n_tty_close()中设置为NULL,分配新的字符“c”可能导致试图访问并写入空指针,之后触发空指针引用。<*参考 
Kyle Bader (kyle.bader@gmail.com)

链接:http://bugzilla.kernel.org/show_bug.cgi?format=multiple&id=14605
http://xorl.wordpress.com/2009/11/30/linux-kernel-tty-null-pointer-dereference-race-condition/
*>
安全建议:
厂商补丁:

Linux
-----
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://www.kernel.org/

图片内容