proc_sys_make_inode() checks under sysctl_lock that @head isn't being
unregistered, lets go of the lock, and then reads the mode of @table
and calls ->set_ownership() with @head. That's fine as long as the
caller holds @head in use, which keeps unregistering from getting past
that check. The next patch has a caller that only pins the memory of
@head. For that one the table, and whatever the owner of the header
keeps behind it, is only there for as long as nobody unregisters the
header, which happens under sysctl_lock. So read the mode and set the
ownership before letting go of the lock. The ->set_ownership()
callbacks only look up ids in a user namespace.
No functional changes.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/proc/proc_sysctl.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 2150cf842919..a1242247f492 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -500,6 +500,7 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
struct ctl_table_root *root = head->root;
struct inode *inode;
struct proc_inode *ei;
+ umode_t mode;
inode = new_inode(sb);
if (!inode)
@@ -509,12 +510,22 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
ei = PROC_I(inode);
+ inode->i_uid = GLOBAL_ROOT_UID;
+ inode->i_gid = GLOBAL_ROOT_GID;
spin_lock(&sysctl_lock);
if (unlikely(head->unregistering)) {
spin_unlock(&sysctl_lock);
iput(inode);
return ERR_PTR(-ENOENT);
}
+ /*
+ * The table, and whatever the owner of the header keeps behind it,
+ * is only there for as long as nobody unregisters the header, which
+ * happens under sysctl_lock. So look at them before letting go of it.
+ */
+ mode = table->mode;
+ if (root->set_ownership)
+ root->set_ownership(head, &inode->i_uid, &inode->i_gid);
ei->sysctl = head;
ei->sysctl_entry = table;
hlist_add_head_rcu(&ei->sibling_inodes, &head->inodes);@@ -522,8 +533,8 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
spin_unlock(&sysctl_lock);
simple_inode_init_ts(inode);
- inode->i_mode = table->mode;
- if (!S_ISDIR(table->mode)) {
+ inode->i_mode = mode;
+ if (!S_ISDIR(mode)) {
inode->i_mode |= S_IFREG;
inode->i_op = &proc_sys_inode_operations;
inode->i_fop = &proc_sys_file_operations;@@ -535,11 +546,6 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
make_empty_dir_inode(inode);
}
- inode->i_uid = GLOBAL_ROOT_UID;
- inode->i_gid = GLOBAL_ROOT_GID;
- if (root->set_ownership)
- root->set_ownership(head, &inode->i_uid, &inode->i_gid);
-
return inode;
}
--
2.53.0