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 | 194 | static struct ctl_table root_table[]; |
| 195 | 195 | static struct ctl_table_root sysctl_table_root; |
| 196 | 196 | static struct ctl_table_header root_table_header = { |
| 197 | − .count = 1, | |
| 197 | + {{.count = 1, | |
| 198 | 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 | 200 | .root = &sysctl_table_root, |
| 201 | 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 | 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 | 1575 | void sysctl_head_put(struct ctl_table_header *head) |
| 1571 | 1576 | { |
| 1572 | 1577 | spin_lock(&sysctl_lock); |
| 1573 | 1578 | if (!--head->count) |
| 1574 | − kfree(head); | |
| 1579 | + call_rcu(&head->rcu, free_head); | |
| 1575 | 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 | 1953 | start_unregistering(header); |
| 1949 | 1954 | if (!--header->parent->count) { |
| 1950 | 1955 | WARN_ON(1); |
| 1951 | − kfree(header->parent); | |
| 1956 | + call_rcu(&header->parent->rcu, free_head); | |
| 1952 | 1957 | } |
| 1953 | 1958 | if (!--header->count) |
| 1954 | − kfree(header); | |
| 1959 | + call_rcu(&header->rcu, free_head); | |
| 1955 | 1960 | spin_unlock(&sysctl_lock); |
| 1956 | 1961 | } |
| 1957 | 1962 | |