mISDN: fix misdn_add_timer()/misdn_del_timer() race
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 15 Apr 2013 21:04:04 +0000 (17:04 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 29 Apr 2013 19:41:48 +0000 (15:41 -0400)
do add_timer() *before* unlocking dev->lock, or unpleasant things can
happen if misdn_del_timer() on another CPU finds the sucker, calls
del_timer_sync() (which does nothing, since we hadn't started the
timer yet) and frees it, just as we get around to add_timer()...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
drivers/isdn/mISDN/timerdev.c

index c00546f830db72b9f94aa4a6787b8f629ac9e3be..ddb8adcd5fbb4bbbd6d574f660c16374e99cf6d1 100644 (file)
@@ -173,7 +173,6 @@ static int
 misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
 {
        int                     id;
-       u_long                  flags;
        struct mISDNtimer       *timer;
 
        if (!timeout) {
@@ -184,19 +183,16 @@ misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
                timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
                if (!timer)
                        return -ENOMEM;
-               spin_lock_irqsave(&dev->lock, flags);
-               timer->id = dev->next_id++;
+               timer->dev = dev;
+               setup_timer(&timer->tl, dev_expire_timer, (long)timer);
+               spin_lock_irq(&dev->lock);
+               id = timer->id = dev->next_id++;
                if (dev->next_id < 0)
                        dev->next_id = 1;
                list_add_tail(&timer->list, &dev->pending);
-               spin_unlock_irqrestore(&dev->lock, flags);
-               timer->dev = dev;
-               timer->tl.data = (long)timer;
-               timer->tl.function = dev_expire_timer;
-               init_timer(&timer->tl);
                timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
                add_timer(&timer->tl);
-               id = timer->id;
+               spin_unlock_irq(&dev->lock);
        }
        return id;
 }