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
27static void proc_evict_inode(struct inode *inode)27static void proc_evict_inode(struct inode *inode)
28{28{
29 struct proc_dir_entry *de;29 struct proc_dir_entry *de;
30 struct ctl_table_header *head;
30 31
31 truncate_inode_pages(&inode->i_data, 0);32 truncate_inode_pages(&inode->i_data, 0);
32 end_writeback(inode);33 end_writeback(inode);
@@ -38,8 +39,11 @@ static void proc_evict_inode(struct inode *inode) · 5 unmodified lines
38 de = PROC_I(inode)->pde;39 de = PROC_I(inode)->pde;
39 if (de)40 if (de)
40 pde_put(de);41 pde_put(de);
41 if (PROC_I(inode)->sysctl)42 head = PROC_I(inode)->sysctl;
42 sysctl_head_put(PROC_I(inode)->sysctl);43 if (head) {
44 rcu_assign_pointer(PROC_I(inode)->sysctl, NULL);
45 sysctl_head_put(head);
46 }
43}47}
44 48
45struct vfsmount *proc_mnt;49struct 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
408 const struct dentry *dentry, const struct inode *inode,408 const struct dentry *dentry, const struct inode *inode,
409 unsigned int len, const char *str, const struct qstr *name)409 unsigned int len, const char *str, const struct qstr *name)
410{410{
411 struct ctl_table_header *head;
411 /* Although proc doesn't have negative dentries, rcu-walk means412 /* Although proc doesn't have negative dentries, rcu-walk means
412 * that inode here can be NULL */413 * that inode here can be NULL */
414 /* AV: can it, indeed? */
413 if (!inode)415 if (!inode)
414 return 0;416 return 1;
415 if (name->len != len)417 if (name->len != len)
416 return 1;418 return 1;
417 if (memcmp(name->name, str, len))419 if (memcmp(name->name, str, len))
418 return 1;420 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);
420}423}
421 424
422static const struct dentry_operations proc_sys_dentry_operations = {425static const struct dentry_operations proc_sys_dentry_operations = {
include/linux/sysctl.h+10−4
@@ -25,6 +25,7 @@ · 24 unmodified lines
25#include <linux/kernel.h>25#include <linux/kernel.h>
26#include <linux/types.h>26#include <linux/types.h>
27#include <linux/compiler.h>27#include <linux/compiler.h>
28#include <linux/rcupdate.h>
28 29
29struct completion;30struct completion;
30 31
@@ -1037,10 +1038,15 @@ struct ctl_table_root { · 1006 unmodified lines
1037 struct ctl_table trees. */1038 struct ctl_table trees. */
1038struct ctl_table_header1039struct ctl_table_header
1039{1040{
1040 struct ctl_table *ctl_table;1041 union {
1041 struct list_head ctl_entry;1042 struct {
1042 int used;1043 struct ctl_table *ctl_table;
1043 int count;1044 struct list_head ctl_entry;
1045 int used;
1046 int count;
1047 };
1048 struct rcu_head rcu;
1049 };
1044 struct completion *unregistering;1050 struct completion *unregistering;
1045 struct ctl_table *ctl_table_arg;1051 struct ctl_table *ctl_table_arg;
1046 struct ctl_table_root *root;1052 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
194static struct ctl_table root_table[];194static struct ctl_table root_table[];
195static struct ctl_table_root sysctl_table_root;195static struct ctl_table_root sysctl_table_root;
196static struct ctl_table_header root_table_header = {196static struct ctl_table_header root_table_header = {
197 .count = 1,197 {{.count = 1,
198 .ctl_table = root_table,198 .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),}},
200 .root = &sysctl_table_root,200 .root = &sysctl_table_root,
201 .set = &sysctl_table_root.default_set,201 .set = &sysctl_table_root.default_set,
202};202};
@@ -1567,11 +1567,16 @@ void sysctl_head_get(struct ctl_table_header *head) · 1364 unmodified lines
1567 spin_unlock(&sysctl_lock);1567 spin_unlock(&sysctl_lock);
1568}1568}
1569 1569
1570static void free_head(struct rcu_head *rcu)
1571{
1572 kfree(container_of(rcu, struct ctl_table_header, rcu));
1573}
1574
1570void sysctl_head_put(struct ctl_table_header *head)1575void sysctl_head_put(struct ctl_table_header *head)
1571{1576{
1572 spin_lock(&sysctl_lock);1577 spin_lock(&sysctl_lock);
1573 if (!--head->count)1578 if (!--head->count)
1574 kfree(head);1579 call_rcu(&head->rcu, free_head);
1575 spin_unlock(&sysctl_lock);1580 spin_unlock(&sysctl_lock);
1576}1581}
1577 1582
@@ -1948,10 +1953,10 @@ void unregister_sysctl_table(struct ctl_table_header * header) · 370 unmodified lines
1948 start_unregistering(header);1953 start_unregistering(header);
1949 if (!--header->parent->count) {1954 if (!--header->parent->count) {
1950 WARN_ON(1);1955 WARN_ON(1);
1951 kfree(header->parent);1956 call_rcu(&header->parent->rcu, free_head);
1952 }1957 }
1953 if (!--header->count)1958 if (!--header->count)
1954 kfree(header);1959 call_rcu(&header->rcu, free_head);
1955 spin_unlock(&sysctl_lock);1960 spin_unlock(&sysctl_lock);
1956}1961}
1957 1962