History

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?

Al Viro committed 15 years ago

Commit dfef6dc1 parent 1858efd4 files · +31 −13Browse files

Unified · Split

4 files
  1. fs/proc/inode.c +6 −2
  2. fs/proc/proc_sysctl.c +5 −2
  3. include/linux/sysctl.h +10 −4
  4. kernel/sysctl.c +10 −5
fs/proc/inode.c+6−2
@@ -27,6 +27,7 @@ · 26 unmodified lines
2727 static void proc_evict_inode(struct inode *inode)
2828 {
2929 struct proc_dir_entry *de;
30+ struct ctl_table_header *head;
3031
3132 truncate_inode_pages(&inode->i_data, 0);
3233 end_writeback(inode);
@@ -38,8 +39,11 @@ static void proc_evict_inode(struct inode *inode) · 5 unmodified lines
3839 de = PROC_I(inode)->pde;
3940 if (de)
4041 pde_put(de);
41− if (PROC_I(inode)->sysctl)
42− sysctl_head_put(PROC_I(inode)->sysctl);
42+ head = PROC_I(inode)->sysctl;
43+ if (head) {
44+ rcu_assign_pointer(PROC_I(inode)->sysctl, NULL);
45+ sysctl_head_put(head);
46+ }
4347 }
4448
4549 struct vfsmount *proc_mnt;
fs/proc/proc_sysctl.c+5−2
@@ -408,15 +408,18 @@ static int proc_sys_compare(const struct dentry *parent, · 407 unmodified lines
408408 const struct dentry *dentry, const struct inode *inode,
409409 unsigned int len, const char *str, const struct qstr *name)
410410 {
411+ struct ctl_table_header *head;
411412 /* Although proc doesn't have negative dentries, rcu-walk means
412413 * that inode here can be NULL */
414+ /* AV: can it, indeed? */
413415 if (!inode)
414− return 0;
416+ return 1;
415417 if (name->len != len)
416418 return 1;
417419 if (memcmp(name->name, str, len))
418420 return 1;
419− return !sysctl_is_seen(PROC_I(inode)->sysctl);
421+ head = rcu_dereference(PROC_I(inode)->sysctl);
422+ return !head || !sysctl_is_seen(head);
420423 }
421424
422425 static const struct dentry_operations proc_sys_dentry_operations = {
include/linux/sysctl.h+10−4
@@ -25,6 +25,7 @@ · 24 unmodified lines
2525 #include <linux/kernel.h>
2626 #include <linux/types.h>
2727 #include <linux/compiler.h>
28+#include <linux/rcupdate.h>
2829
2930 struct completion;
3031
@@ -1037,10 +1038,15 @@ struct ctl_table_root { · 1006 unmodified lines
10371038 struct ctl_table trees. */
10381039 struct ctl_table_header
10391040 {
1040− struct ctl_table *ctl_table;
1041− struct list_head ctl_entry;
1042− int used;
1043− int count;
1041+ union {
1042+ struct {
1043+ struct ctl_table *ctl_table;
1044+ struct list_head ctl_entry;
1045+ int used;
1046+ int count;
1047+ };
1048+ struct rcu_head rcu;
1049+ };
10441050 struct completion *unregistering;
10451051 struct ctl_table *ctl_table_arg;
10461052 struct ctl_table_root *root;
kernel/sysctl.c+10−5
@@ -194,9 +194,9 @@ static int sysrq_sysctl_handler(ctl_table *table, int write, · 193 unmodified lines
194194 static struct ctl_table root_table[];
195195 static struct ctl_table_root sysctl_table_root;
196196 static struct ctl_table_header root_table_header = {
197− .count = 1,
197+ {{.count = 1,
198198 .ctl_table = root_table,
199− .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),
199+ .ctl_entry = LIST_HEAD_INIT(sysctl_table_root.default_set.list),}},
200200 .root = &sysctl_table_root,
201201 .set = &sysctl_table_root.default_set,
202202 };
@@ -1567,11 +1567,16 @@ void sysctl_head_get(struct ctl_table_header *head) · 1364 unmodified lines
15671567 spin_unlock(&sysctl_lock);
15681568 }
15691569
1570+static void free_head(struct rcu_head *rcu)
1571+{
1572+ kfree(container_of(rcu, struct ctl_table_header, rcu));
1573+}
1574+
15701575 void sysctl_head_put(struct ctl_table_header *head)
15711576 {
15721577 spin_lock(&sysctl_lock);
15731578 if (!--head->count)
1574− kfree(head);
1579+ call_rcu(&head->rcu, free_head);
15751580 spin_unlock(&sysctl_lock);
15761581 }
15771582
@@ -1948,10 +1953,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) · 370 unmodified lines
19481953 start_unregistering(header);
19491954 if (!--header->parent->count) {
19501955 WARN_ON(1);
1951− kfree(header->parent);
1956+ call_rcu(&header->parent->rcu, free_head);
19521957 }
19531958 if (!--header->count)
1954− kfree(header);
1959+ call_rcu(&header->rcu, free_head);
19551960 spin_unlock(&sysctl_lock);
19561961 }
19571962