unfuck proc_sysctl ->d_compare()

a) struct inode is not going to be freed under ->d_compare();
however, the thing PROC_I(inode)->sysctl points to just might.
Fortunately, it's enough to make freeing that sucker delayed,
provided that we don't step on its ->unregistering, clear
the pointer to it in PROC_I(inode) before dropping the reference
and check if it's NULL in ->d_compare().

b) I'm not sure that we *can* walk into NULL inode here (we recheck
dentry->seq between verifying that it's still hashed / fetching
dentry->d_inode and passing it to ->d_compare() and there's no
negative hashed dentries in /proc/sys/*), but if we can walk into
that, we really should not have ->d_compare() return 0 on it!
Said that, I really suspect that this check can be simply killed.
Nick?

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro
2011-03-08 01:25:28 -05:00
parent 1858efd471
commit dfef6dcd35
4 changed files with 31 additions and 13 deletions

View File

@@ -25,6 +25,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/compiler.h>
#include <linux/rcupdate.h>
struct completion;
@@ -1037,10 +1038,15 @@ struct ctl_table_root {
struct ctl_table trees. */
struct ctl_table_header
{
struct ctl_table *ctl_table;
struct list_head ctl_entry;
int used;
int count;
union {
struct {
struct ctl_table *ctl_table;
struct list_head ctl_entry;
int used;
int count;
};
struct rcu_head rcu;
};
struct completion *unregistering;
struct ctl_table *ctl_table_arg;
struct ctl_table_root *root;