Message ID | 87d195f9m7.fsf@xmission.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Jul 13, 2017 at 2:45 AM, Eric W. Biederman <ebiederm@xmission.com> wrote: > > Linus, > > Please pull the for-linus branch from the git tree: > > git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-linus > > HEAD: 2fd1d2c4ceb2248a727696962cf3370dc9f5a0a4 proc: Fix proc_sys_prune_dcache to hold a sb reference > > A rather embarassing and hard to hit bug was merged into 4.11-rc1. > Andrei Vagin tracked this bug now and after some starting at the > code I came up with a fix. > > Eric > > From 2fd1d2c4ceb2248a727696962cf3370dc9f5a0a4 Mon Sep 17 00:00:00 2001 > From: "Eric W. Biederman" <ebiederm@xmission.com> > Date: Thu, 6 Jul 2017 08:41:06 -0500 > Subject: [PATCH] proc: Fix proc_sys_prune_dcache to hold a sb reference > > Andrei Vagin writes: > FYI: This bug has been reproduced on 4.11.7 [...] > Cc: stable@vger.kernel.org Please add first affected Linux-kernel release. Cc: stable@vger.kernel.org # 4.11 - Sedat - > Reported-by: Andrei Vagin <avagin@gmail.com> > Tested-by: Andrei Vagin <avagin@openvz.org> > Fixes: ace0c791e6c3 ("proc/sysctl: Don't grab i_lock under sysctl_lock.") > Fixes: d6cffbbe9a7e ("proc/sysctl: prune stale dentries during unregistering") > Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Sedat Dilek <sedat.dilek@gmail.com> writes: > On Thu, Jul 13, 2017 at 2:45 AM, Eric W. Biederman > <ebiederm@xmission.com> wrote: >> >> Linus, >> >> Please pull the for-linus branch from the git tree: >> >> git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git for-linus >> >> HEAD: 2fd1d2c4ceb2248a727696962cf3370dc9f5a0a4 proc: Fix proc_sys_prune_dcache to hold a sb reference >> >> A rather embarassing and hard to hit bug was merged into 4.11-rc1. >> Andrei Vagin tracked this bug now and after some starting at the >> code I came up with a fix. >> >> Eric >> >> From 2fd1d2c4ceb2248a727696962cf3370dc9f5a0a4 Mon Sep 17 00:00:00 2001 >> From: "Eric W. Biederman" <ebiederm@xmission.com> >> Date: Thu, 6 Jul 2017 08:41:06 -0500 >> Subject: [PATCH] proc: Fix proc_sys_prune_dcache to hold a sb reference >> >> Andrei Vagin writes: >> FYI: This bug has been reproduced on 4.11.7 > [...] > >> Cc: stable@vger.kernel.org > > Please add first affected Linux-kernel release. > > Cc: stable@vger.kernel.org # 4.11 Please look at the fixes tags below. Especially when changes are backported that seems to be a much better indicator of what is affected than some arbitrary kernel number. I have included a Fixes tags for the originally buggy commit and a Fixes tag for the incomplete fix for the originally buggy commit. As a rule I only include a kernel version number if the originally affected kernel is not in Linus's git tree, and not in Thomas's conversion of the original bitkeeper tree to git. > > - Sedat - > >> Reported-by: Andrei Vagin <avagin@gmail.com> >> Tested-by: Andrei Vagin <avagin@openvz.org> >> Fixes: ace0c791e6c3 ("proc/sysctl: Don't grab i_lock under sysctl_lock.") >> Fixes: d6cffbbe9a7e ("proc/sysctl: prune stale dentries during unregistering") >> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Eric
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index c5ae09b6c726..18694598bebf 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -67,7 +67,7 @@ struct proc_inode { struct proc_dir_entry *pde; struct ctl_table_header *sysctl; struct ctl_table *sysctl_entry; - struct list_head sysctl_inodes; + struct hlist_node sysctl_inodes; const struct proc_ns_operations *ns_ops; struct inode vfs_inode; }; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 67985a7233c2..9bf06e2b1284 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -191,7 +191,7 @@ static void init_header(struct ctl_table_header *head, head->set = set; head->parent = NULL; head->node = node; - INIT_LIST_HEAD(&head->inodes); + INIT_HLIST_HEAD(&head->inodes); if (node) { struct ctl_table *entry; for (entry = table; entry->procname; entry++, node++) @@ -261,25 +261,42 @@ static void unuse_table(struct ctl_table_header *p) complete(p->unregistering); } -/* called under sysctl_lock */ static void proc_sys_prune_dcache(struct ctl_table_header *head) { - struct inode *inode, *prev = NULL; + struct inode *inode; struct proc_inode *ei; + struct hlist_node *node; + struct super_block *sb; rcu_read_lock(); - list_for_each_entry_rcu(ei, &head->inodes, sysctl_inodes) { - inode = igrab(&ei->vfs_inode); - if (inode) { - rcu_read_unlock(); - iput(prev); - prev = inode; - d_prune_aliases(inode); + for (;;) { + node = hlist_first_rcu(&head->inodes); + if (!node) + break; + ei = hlist_entry(node, struct proc_inode, sysctl_inodes); + spin_lock(&sysctl_lock); + hlist_del_init_rcu(&ei->sysctl_inodes); + spin_unlock(&sysctl_lock); + + inode = &ei->vfs_inode; + sb = inode->i_sb; + if (!atomic_inc_not_zero(&sb->s_active)) + continue; + inode = igrab(inode); + rcu_read_unlock(); + if (unlikely(!inode)) { + deactivate_super(sb); rcu_read_lock(); + continue; } + + d_prune_aliases(inode); + iput(inode); + deactivate_super(sb); + + rcu_read_lock(); } rcu_read_unlock(); - iput(prev); } /* called under sysctl_lock, will reacquire if has to wait */ @@ -461,7 +478,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, } ei->sysctl = head; ei->sysctl_entry = table; - list_add_rcu(&ei->sysctl_inodes, &head->inodes); + hlist_add_head_rcu(&ei->sysctl_inodes, &head->inodes); head->count++; spin_unlock(&sysctl_lock); @@ -489,7 +506,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb, void proc_sys_evict_inode(struct inode *inode, struct ctl_table_header *head) { spin_lock(&sysctl_lock); - list_del_rcu(&PROC_I(inode)->sysctl_inodes); + hlist_del_init_rcu(&PROC_I(inode)->sysctl_inodes); if (!--head->count) kfree_rcu(head, rcu); spin_unlock(&sysctl_lock); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 80d07816def0..1c04a26bfd2f 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -143,7 +143,7 @@ struct ctl_table_header struct ctl_table_set *set; struct ctl_dir *parent; struct ctl_node *node; - struct list_head inodes; /* head for proc_inode->sysctl_inodes */ + struct hlist_head inodes; /* head for proc_inode->sysctl_inodes */ }; struct ctl_dir {