Alan Cox discovered that users or admins can get the kernel to execute through a NULL pointer due to missing checks in the tty code.
Proposed patch from Alan: diff -u --new-file --recursive a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c --- a/drivers/net/hamradio/6pack.c 2008-06-25 16:52:02.000000000 +0100 +++ b/drivers/net/hamradio/6pack.c 2008-06-27 12:05:05.000000000 +0100 @@ -601,6 +601,8 @@ if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (!tty->driver->write) + return -EOPNOTSUPP; dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup); if (!dev) { diff -u --new-file --recursive a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c --- a/drivers/net/hamradio/mkiss.c 2008-06-25 16:52:02.000000000 +0100 +++ b/drivers/net/hamradio/mkiss.c 2008-06-27 12:06:51.000000000 +0100 @@ -530,6 +530,7 @@ static int ax_xmit(struct sk_buff *skb, struct net_device *dev) { struct mkiss *ax = netdev_priv(dev); + int cib = 0; if (!netif_running(dev)) { printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); @@ -545,10 +546,11 @@ /* 20 sec timeout not reached */ return 1; } + if (ax->tty->drivers->chars_in_buffer) + cib = ax->tty->chars_in_buffer(ax->tty); printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (ax->tty->driver->chars_in_buffer(ax->tty) || ax->xleft) ? - "bad line quality" : "driver error"); + cib || ax->xleft) ? "bad line quality" : "driver error"); ax->xleft = 0; clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags); @@ -736,6 +738,8 @@ if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (!tty->driver->write) + return -EOPNOTSUPP; dev = alloc_netdev(sizeof(struct mkiss), "ax%d", ax_setup); if (!dev) { diff -u --new-file --recursive a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c --- a/drivers/net/irda/irtty-sir.c 2008-06-25 16:52:02.000000000 +0100 +++ b/drivers/net/irda/irtty-sir.c 2008-06-27 12:09:29.000000000 +0100 @@ -64,7 +64,9 @@ IRDA_ASSERT(priv != NULL, return -1;); IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); - return priv->tty->driver->chars_in_buffer(priv->tty); + if (priv->tty->drivers->chars_in_buffer) + return priv->tty->driver->chars_in_buffer(priv->tty); + return 0; } /* Wait (sleep) until underlaying hardware finished transmission diff -u --new-file --recursive a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c --- a/drivers/net/ppp_async.c 2008-06-25 16:52:00.000000000 +0100 +++ b/drivers/net/ppp_async.c 2008-06-27 12:04:08.000000000 +0100 @@ -157,6 +157,9 @@ { struct asyncppp *ap; int err; + + if (!tty->driver->write) + return -EOPNOTSUPP; err = -ENOMEM; ap = kmalloc(sizeof(*ap), GFP_KERNEL); diff -u --new-file --recursive a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c --- a/drivers/net/ppp_synctty.c 2008-06-25 16:52:00.000000000 +0100 +++ b/drivers/net/ppp_synctty.c 2008-06-27 12:04:22.000000000 +0100 @@ -207,6 +207,9 @@ struct syncppp *ap; int err; + if (!tty->driver->write) + return -EOPNOTSUPP; + ap = kmalloc(sizeof(*ap), GFP_KERNEL); err = -ENOMEM; if (ap == 0) diff -u --new-file --recursive a/drivers/net/slip.c b/drivers/net/slip.c --- a/drivers/net/slip.c 2008-06-25 16:52:02.000000000 +0100 +++ b/drivers/net/slip.c 2008-06-27 12:03:40.000000000 +0100 @@ -463,9 +463,14 @@ /* 20 sec timeout not reached */ goto out; } - printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, - (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) ? - "bad line quality" : "driver error"); + { + int cib = 0; + if (sl->tty->driver->chars_in_buffer) + cib = sl->tty->driver->chars_in_buffer(sl->tty); + printk(KERN_WARNING "%s: transmit timed out, %s?\n", + dev->name, (cib || sl->xleft) ? + "bad line quality" : "driver error"); + } sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); sl_unlock(sl); @@ -836,7 +841,9 @@ if(!capable(CAP_NET_ADMIN)) return -EPERM; - + if (!tty->driver->write) + return -EOPNOTSUPP; + /* RTnetlink lock is misused here to serialize concurrent opens of slip channels. There are better ways, but it is the simplest one. diff -u --new-file --recursive a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c --- a/drivers/net/wan/x25_asy.c 2008-06-25 16:52:01.000000000 +0100 +++ b/drivers/net/wan/x25_asy.c 2008-06-27 12:08:53.000000000 +0100 @@ -283,6 +283,10 @@ static void x25_asy_timeout(struct net_device *dev) { struct x25_asy *sl = (struct x25_asy*)(dev->priv); + int cib = 0; + + if (sl->tty->driver->chars_in_buffer) + cib = sl->tty->driver->chars_in_buffer(sl->tty); spin_lock(&sl->lock); if (netif_queue_stopped(dev)) { @@ -290,8 +294,7 @@ * 14 Oct 1994 Dmitry Gorodchanin. */ printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, - (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) ? - "bad line quality" : "driver error"); + (cib || sl->xleft) ? "bad line quality" : "driver error"); sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); x25_asy_unlock(sl); @@ -561,6 +564,9 @@ return -EEXIST; } + if (!tty->driver->write) + return -EOPNOTSUPP; + /* OK. Find a free X.25 channel to use. */ if ((sl = x25_asy_alloc()) == NULL) { return -ENFILE; diff -u --new-file --recursive a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c --- a/drivers/net/wireless/strip.c 2008-06-25 16:52:01.000000000 +0100 +++ b/drivers/net/wireless/strip.c 2008-06-27 12:01:04.000000000 +0100 @@ -801,7 +801,8 @@ struct termios old_termios = *(tty->termios); tty->termios->c_cflag &= ~CBAUD; /* Clear the old baud setting */ tty->termios->c_cflag |= baudcode; /* Set the new baud setting */ - tty->driver->set_termios(tty, &old_termios); + if (tty->driver->set_termios) + tty->driver->set_termios(tty, &old_termios); } /*
Proposed upstream strip driver commit from Alan: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blobdiff;f=drivers/net/wireless/strip.c;h=883af891ebfb4c54d19daff25495af0a063734f4;hp=5dd23c93497db90163365b84a7e62917571cd122;hb=79f999d0aa264f72f5491be14b4bf60137a3d3a9;hpb=492c2e476eac010962850006c49df326919b284c
Proposed upstream patch for the rest of the fixes: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=f34d7a5b7010b82fe97da95496b9971435530062
This was addressed via: Red Hat Enterprise Linux version 5 (RHSA-2008:0612) Red Hat Enterprise Linux version 4 (RHSA-2008:0665) Red Hat Enterprise Linux version 3 (RHSA-2008:0973)