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?
Showing the changes to kernel (1 of 4 files). Show all
Unified · Split
| @@ -194,9 +194,9 @@ static int sysrq_sysctl_handler(ctl_table *table, int write, · 193 unmodified lines | |||
| 194 | static struct ctl_table root_table[]; | 194 | static struct ctl_table root_table[]; |
| 195 | static struct ctl_table_root sysctl_table_root; | 195 | static struct ctl_table_root sysctl_table_root; |
| 196 | static struct ctl_table_header root_table_header = { | 196 | static 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 | ||
| 1570 | static void free_head(struct rcu_head *rcu) | ||
| 1571 | { | ||
| 1572 | kfree(container_of(rcu, struct ctl_table_header, rcu)); | ||
| 1573 | } | ||
| 1574 | |||
| 1570 | void sysctl_head_put(struct ctl_table_header *head) | 1575 | void 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 | ||