Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dead537d authored by Eric Paris's avatar Eric Paris
Browse files

inotify: fix locking around inotify watching in the idr



The are races around the idr storage of inotify watches.  It's possible
that a watch could be found from sys_inotify_rm_watch() in the idr, but it
could be removed from the idr before that code does it's removal.  Move the
locking and the refcnt'ing so that these have to happen atomically.

Signed-off-by: default avatarEric Paris <eparis@redhat.com>
parent cf437426
Loading
Loading
Loading
Loading
+40 −10
Original line number Original line Diff line number Diff line
@@ -364,20 +364,53 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns
	return error;
	return error;
}
}


/*
 * Remove the mark from the idr (if present) and drop the reference
 * on the mark because it was in the idr.
 */
static void inotify_remove_from_idr(struct fsnotify_group *group,
static void inotify_remove_from_idr(struct fsnotify_group *group,
				    struct inotify_inode_mark_entry *ientry)
				    struct inotify_inode_mark_entry *ientry)
{
{
	struct idr *idr;
	struct idr *idr;
	struct fsnotify_mark_entry *entry;
	struct inotify_inode_mark_entry *found_ientry;
	int wd;


	spin_lock(&group->inotify_data.idr_lock);
	spin_lock(&group->inotify_data.idr_lock);
	idr = &group->inotify_data.idr;
	idr = &group->inotify_data.idr;
	idr_remove(idr, ientry->wd);
	wd = ientry->wd;
	spin_unlock(&group->inotify_data.idr_lock);

	if (wd == -1)
		goto out;

	entry = idr_find(&group->inotify_data.idr, wd);
	if (unlikely(!entry))
		goto out;

	found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry);
	if (unlikely(found_ientry != ientry)) {
		/* We found an entry in the idr with the right wd, but it's
		 * not the entry we were told to remove.  eparis seriously
		 * fucked up somewhere. */
		WARN_ON(1);
		ientry->wd = -1;
		goto out;
	}

	/* One ref for being in the idr, one ref held by the caller */
	BUG_ON(atomic_read(&entry->refcnt) < 2);

	idr_remove(idr, wd);
	ientry->wd = -1;
	ientry->wd = -1;

	/* removed from the idr, drop that ref */
	fsnotify_put_mark(entry);
out:
	spin_unlock(&group->inotify_data.idr_lock);
}
}

/*
/*
 * Send IN_IGNORED for this wd, remove this wd from the idr, and drop the
 * Send IN_IGNORED for this wd, remove this wd from the idr.
 * internal reference help on the mark because it is in the idr.
 */
 */
void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
				    struct fsnotify_group *group)
				    struct fsnotify_group *group)
@@ -417,9 +450,6 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
	/* remove this entry from the idr */
	/* remove this entry from the idr */
	inotify_remove_from_idr(group, ientry);
	inotify_remove_from_idr(group, ientry);


	/* removed from idr, drop that reference */
	fsnotify_put_mark(entry);

	atomic_dec(&group->inotify_data.user->inotify_watches);
	atomic_dec(&group->inotify_data.user->inotify_watches);
}
}


@@ -535,6 +565,9 @@ static int inotify_new_watch(struct fsnotify_group *group,
		goto out_err;
		goto out_err;
	}
	}


	/* we put the mark on the idr, take a reference */
	fsnotify_get_mark(&tmp_ientry->fsn_entry);

	/* we are on the idr, now get on the inode */
	/* we are on the idr, now get on the inode */
	ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
	ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
	if (ret) {
	if (ret) {
@@ -543,9 +576,6 @@ static int inotify_new_watch(struct fsnotify_group *group,
		goto out_err;
		goto out_err;
	}
	}


	/* we put the mark on the idr, take a reference */
	fsnotify_get_mark(&tmp_ientry->fsn_entry);

	/* update the idr hint, who cares about races, it's just a hint */
	/* update the idr hint, who cares about races, it's just a hint */
	group->inotify_data.last_wd = tmp_ientry->wd;
	group->inotify_data.last_wd = tmp_ientry->wd;