From: Kirill Korotaev Fixed race on put_files_struct on exec with proc. Restoring files on current on error path may lead to proc having a pointer to already kfree-d files_struct. ->files changing at exit.c and khtread.c are safe as exit_files() makes all things under lock. Found during OpenVZ stress testing. Signed-off-by: Pavel Emelianov Signed-off-by: Kirill Korotaev Signed-off-by: Andrew Morton --- fs/binfmt_elf.c | 6 ++---- fs/binfmt_misc.c | 6 ++---- fs/exec.c | 3 +-- include/linux/file.h | 1 + kernel/exit.c | 11 +++++++++++ 5 files changed, 17 insertions(+), 10 deletions(-) diff -puN fs/binfmt_elf.c~fix-unserialized-task-files-changing fs/binfmt_elf.c --- a/fs/binfmt_elf.c~fix-unserialized-task-files-changing +++ a/fs/binfmt_elf.c @@ -1037,10 +1037,8 @@ out_free_interp: out_free_file: sys_close(elf_exec_fileno); out_free_fh: - if (files) { - put_files_struct(current->files); - current->files = files; - } + if (files) + reset_files_struct(current, files); out_free_ph: kfree(elf_phdata); goto out; diff -puN fs/binfmt_misc.c~fix-unserialized-task-files-changing fs/binfmt_misc.c --- a/fs/binfmt_misc.c~fix-unserialized-task-files-changing +++ a/fs/binfmt_misc.c @@ -215,10 +215,8 @@ _error: bprm->interp_flags = 0; bprm->interp_data = 0; _unshare: - if (files) { - put_files_struct(current->files); - current->files = files; - } + if (files) + reset_files_struct(current, files); goto _ret; } diff -puN fs/exec.c~fix-unserialized-task-files-changing fs/exec.c --- a/fs/exec.c~fix-unserialized-task-files-changing +++ a/fs/exec.c @@ -898,8 +898,7 @@ int flush_old_exec(struct linux_binprm * return 0; mmap_failed: - put_files_struct(current->files); - current->files = files; + reset_files_struct(current, files); out: return retval; } diff -puN include/linux/file.h~fix-unserialized-task-files-changing include/linux/file.h --- a/include/linux/file.h~fix-unserialized-task-files-changing +++ a/include/linux/file.h @@ -112,5 +112,6 @@ struct task_struct; struct files_struct *get_files_struct(struct task_struct *); void FASTCALL(put_files_struct(struct files_struct *fs)); +void reset_files_struct(struct task_struct *, struct files_struct *); #endif /* __LINUX_FILE_H */ diff -puN kernel/exit.c~fix-unserialized-task-files-changing kernel/exit.c --- a/kernel/exit.c~fix-unserialized-task-files-changing +++ a/kernel/exit.c @@ -488,6 +488,17 @@ void fastcall put_files_struct(struct fi EXPORT_SYMBOL(put_files_struct); +void reset_files_struct(struct task_struct *tsk, struct files_struct *files) +{ + struct files_struct *old; + + old = tsk->files; + task_lock(tsk); + tsk->files = files; + task_unlock(tsk); + put_files_struct(old); +} + static inline void __exit_files(struct task_struct *tsk) { struct files_struct * files = tsk->files; _