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) <[email protected]> --- 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
