2011-02-11 15:08:07

by Lino Sanfilippo

[permalink] [raw]
Subject: [PATCH] inotify: fix race when adding a new watch


In inotify_new_watch() the number of watches for a group is compared against
the max number of allowed watches and increased afterwards.
The check and incrementation is not done atomically, so it is possible for
multiple concurrent threads to pass the check and increment the number of
marks above the allowed max.
This patch uses a groups mark_lock to ensure that both check and incrementation
are done atomic.
With this lock held it is also not longer necessary to check for an already existing
mark after inotify_new_watch() has returned, since mark lookup and addition are also
synchronized now.

Signed-off-by: Lino Sanfilippo <[email protected]>
---
This patch depends on the the patch series
"fsnotify: use a groups mark_mutex to synchronize access to its mark list" sent
earlier this day.

fs/notify/inotify/inotify_user.c | 13 ++++---------
1 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 1637820..8e8b236 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -656,7 +656,8 @@ static int inotify_new_watch(struct fsnotify_group *group,
goto out_err;

/* we are on the idr, now get on the inode */
- ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, NULL, 0);
+ ret = fsnotify_add_mark_locked(&tmp_i_mark->fsn_mark, group, inode,
+ NULL, 0);
if (ret) {
/* we failed to get on the inode, get off the idr */
inotify_remove_from_idr(group, tmp_i_mark);
@@ -680,19 +681,13 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod
{
int ret = 0;

-retry:
+ mutex_lock(&group->mark_mutex);
/* try to update and existing watch with the new arg */
ret = inotify_update_existing_watch(group, inode, arg);
/* no mark present, try to add a new one */
if (ret == -ENOENT)
ret = inotify_new_watch(group, inode, arg);
- /*
- * inotify_new_watch could race with another thread which did an
- * inotify_new_watch between the update_existing and the add watch
- * here, go back and try to update an existing mark again.
- */
- if (ret == -EEXIST)
- goto retry;
+ mutex_unlock(&group->mark_mutex);

return ret;
}
--
1.5.6.5