(euid, egid) pair is snapshotted correctly from task under RCU, but writeback to inode can be done in any order. Fix by doing writeback under inode->i_lock where necessary (/proc/* , /proc/*/fd/* , /proc/*/map_files/* revalidate). Reported-by: syzbot+e392f8008a294fdf8891@xxxxxxxxxxxxxxxxxxxxxxxxx Signed-off-by: Alexey Dobriyan <adobriyan@xxxxxxxxx> --- fs/proc/base.c | 25 +++++++++++++++++++++++-- fs/proc/fd.c | 2 +- fs/proc/internal.h | 2 ++ 3 files changed, 26 insertions(+), 3 deletions(-) --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1743,6 +1743,25 @@ void task_dump_owner(struct task_struct *task, umode_t mode, *rgid = gid; } +/* use if inode is live */ +void task_dump_owner_to_inode(struct task_struct *task, umode_t mode, + struct inode *inode) +{ + kuid_t uid; + kgid_t gid; + + task_dump_owner(task, mode, &uid, &gid); + /* + * There is no atomic "change all credentials at once" system call, + * guaranteeing more than _some_ snapshot from "struct cred" ends up + * in inode is not possible. + */ + spin_lock(&inode->i_lock); + inode->i_uid = uid; + inode->i_gid = gid; + spin_unlock(&inode->i_lock); +} + struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, umode_t mode) { @@ -1769,6 +1788,7 @@ struct inode *proc_pid_make_inode(struct super_block * sb, if (!ei->pid) goto out_unlock; + /* fresh inode -- no races */ task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid); security_task_to_inode(task, inode); @@ -1802,6 +1822,7 @@ int pid_getattr(const struct path *path, struct kstat *stat, */ return -ENOENT; } + /* "struct kstat" is thread local, atomic snapshot is enough */ task_dump_owner(task, inode->i_mode, &stat->uid, &stat->gid); } rcu_read_unlock(); @@ -1815,7 +1836,7 @@ int pid_getattr(const struct path *path, struct kstat *stat, */ void pid_update_inode(struct task_struct *task, struct inode *inode) { - task_dump_owner(task, inode->i_mode, &inode->i_uid, &inode->i_gid); + task_dump_owner_to_inode(task, inode->i_mode, inode); inode->i_mode &= ~(S_ISUID | S_ISGID); security_task_to_inode(task, inode); @@ -1990,7 +2011,7 @@ static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags) mmput(mm); if (exact_vma_exists) { - task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid); + task_dump_owner_to_inode(task, 0, inode); security_task_to_inode(task, inode); status = 1; --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -101,7 +101,7 @@ static bool tid_fd_mode(struct task_struct *task, unsigned fd, fmode_t *mode) static void tid_fd_update_inode(struct task_struct *task, struct inode *inode, fmode_t f_mode) { - task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid); + task_dump_owner_to_inode(task, 0, inode); if (S_ISLNK(inode->i_mode)) { unsigned i_mode = S_IFLNK; --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -123,6 +123,8 @@ static inline struct task_struct *get_proc_task(const struct inode *inode) void task_dump_owner(struct task_struct *task, umode_t mode, kuid_t *ruid, kgid_t *rgid); +void task_dump_owner_to_inode(struct task_struct *task, umode_t mode, + struct inode *inode); unsigned name_to_int(const struct qstr *qstr); /*