[PATCH] inotify: fix race between the kernel and user space
authorJohn McCutchan <ttb@tentacle.dhs.org>
Mon, 1 Aug 2005 15:00:45 +0000 (11:00 -0400)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 1 Aug 2005 16:16:53 +0000 (09:16 -0700)
When you rm a watch, an IN_IGNORED event is sent down the event queue
with the watch descriptor that you just rm'd.

If you then add a watch you could get the ignored watch's wd and if you
haven't read the entire event queue, user space will think that it's
newly created watch was just ignored.

To avoid this problem we just use idr_get_new_above instead of
idr_get_new.

Signed-off-by: John McCutchan <ttb@tentacle.dhs.org>
Signed-off-by: Robert Love <rml@novell.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/inotify.c

index a8a714e481405f37c3886c1761dd078a9e1fef37..27ebcac5e07ff8f67afe7594d25aa5d384814951 100644 (file)
@@ -90,6 +90,7 @@ struct inotify_device {
        unsigned int            queue_size;     /* size of the queue (bytes) */
        unsigned int            event_count;    /* number of pending events */
        unsigned int            max_events;     /* maximum number of events */
+       u32                     last_wd;        /* the last wd allocated */
 };
 
 /*
@@ -352,7 +353,7 @@ static int inotify_dev_get_wd(struct inotify_device *dev,
        do {
                if (unlikely(!idr_pre_get(&dev->idr, GFP_KERNEL)))
                        return -ENOSPC;
-               ret = idr_get_new(&dev->idr, watch, &watch->wd);
+               ret = idr_get_new_above(&dev->idr, watch, dev->last_wd, &watch->wd);
        } while (ret == -EAGAIN);
 
        return ret;
@@ -401,6 +402,7 @@ static struct inotify_watch *create_watch(struct inotify_device *dev,
                return ERR_PTR(ret);
        }
 
+       dev->last_wd = ret;
        watch->mask = mask;
        atomic_set(&watch->count, 0);
        INIT_LIST_HEAD(&watch->d_list);
@@ -899,6 +901,7 @@ asmlinkage long sys_inotify_init(void)
        dev->queue_size = 0;
        dev->max_events = inotify_max_queued_events;
        dev->user = user;
+       dev->last_wd = 0;
        atomic_set(&dev->count, 0);
 
        get_inotify_dev(dev);