diff options
-rw-r--r-- | fs/file.c | 3 | ||||
-rw-r--r-- | include/linux/fs.h | 1 | ||||
-rw-r--r-- | include/linux/mm_types.h | 2 | ||||
-rw-r--r-- | kernel/fork.c | 56 |
4 files changed, 40 insertions, 22 deletions
@@ -638,8 +638,7 @@ static struct file *__fget(unsigned int fd, fmode_t mask) | |||
638 | file = fcheck_files(files, fd); | 638 | file = fcheck_files(files, fd); |
639 | if (file) { | 639 | if (file) { |
640 | /* File object ref couldn't be taken */ | 640 | /* File object ref couldn't be taken */ |
641 | if ((file->f_mode & mask) || | 641 | if ((file->f_mode & mask) || !get_file_rcu(file)) |
642 | !atomic_long_inc_not_zero(&file->f_count)) | ||
643 | file = NULL; | 642 | file = NULL; |
644 | } | 643 | } |
645 | rcu_read_unlock(); | 644 | rcu_read_unlock(); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index f4fc60727b8d..6bf7ab7c1573 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -870,6 +870,7 @@ static inline struct file *get_file(struct file *f) | |||
870 | atomic_long_inc(&f->f_count); | 870 | atomic_long_inc(&f->f_count); |
871 | return f; | 871 | return f; |
872 | } | 872 | } |
873 | #define get_file_rcu(x) atomic_long_inc_not_zero(&(x)->f_count) | ||
873 | #define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1) | 874 | #define fput_atomic(x) atomic_long_add_unless(&(x)->f_count, -1, 1) |
874 | #define file_count(x) atomic_long_read(&(x)->f_count) | 875 | #define file_count(x) atomic_long_read(&(x)->f_count) |
875 | 876 | ||
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 590630eb59ba..8d37e26a1007 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h | |||
@@ -429,7 +429,7 @@ struct mm_struct { | |||
429 | #endif | 429 | #endif |
430 | 430 | ||
431 | /* store ref to file /proc/<pid>/exe symlink points to */ | 431 | /* store ref to file /proc/<pid>/exe symlink points to */ |
432 | struct file *exe_file; | 432 | struct file __rcu *exe_file; |
433 | #ifdef CONFIG_MMU_NOTIFIER | 433 | #ifdef CONFIG_MMU_NOTIFIER |
434 | struct mmu_notifier_mm *mmu_notifier_mm; | 434 | struct mmu_notifier_mm *mmu_notifier_mm; |
435 | #endif | 435 | #endif |
diff --git a/kernel/fork.c b/kernel/fork.c index 8807a129711b..259202637531 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -403,6 +403,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) | |||
403 | */ | 403 | */ |
404 | down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING); | 404 | down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING); |
405 | 405 | ||
406 | /* No ordering required: file already has been exposed. */ | ||
407 | RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); | ||
408 | |||
406 | mm->total_vm = oldmm->total_vm; | 409 | mm->total_vm = oldmm->total_vm; |
407 | mm->shared_vm = oldmm->shared_vm; | 410 | mm->shared_vm = oldmm->shared_vm; |
408 | mm->exec_vm = oldmm->exec_vm; | 411 | mm->exec_vm = oldmm->exec_vm; |
@@ -528,7 +531,13 @@ static inline void mm_free_pgd(struct mm_struct *mm) | |||
528 | pgd_free(mm, mm->pgd); | 531 | pgd_free(mm, mm->pgd); |
529 | } | 532 | } |
530 | #else | 533 | #else |
531 | #define dup_mmap(mm, oldmm) (0) | 534 | static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) |
535 | { | ||
536 | down_write(&oldmm->mmap_sem); | ||
537 | RCU_INIT_POINTER(mm->exe_file, get_mm_exe_file(oldmm)); | ||
538 | up_write(&oldmm->mmap_sem); | ||
539 | return 0; | ||
540 | } | ||
532 | #define mm_alloc_pgd(mm) (0) | 541 | #define mm_alloc_pgd(mm) (0) |
533 | #define mm_free_pgd(mm) | 542 | #define mm_free_pgd(mm) |
534 | #endif /* CONFIG_MMU */ | 543 | #endif /* CONFIG_MMU */ |
@@ -697,35 +706,46 @@ void mmput(struct mm_struct *mm) | |||
697 | } | 706 | } |
698 | EXPORT_SYMBOL_GPL(mmput); | 707 | EXPORT_SYMBOL_GPL(mmput); |
699 | 708 | ||
709 | /** | ||
710 | * set_mm_exe_file - change a reference to the mm's executable file | ||
711 | * | ||
712 | * This changes mm's executable file (shown as symlink /proc/[pid]/exe). | ||
713 | * | ||
714 | * Main users are mmput(), sys_execve() and sys_prctl(PR_SET_MM_MAP/EXE_FILE). | ||
715 | * Callers prevent concurrent invocations: in mmput() nobody alive left, | ||
716 | * in execve task is single-threaded, prctl holds mmap_sem exclusively. | ||
717 | */ | ||
700 | void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) | 718 | void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) |
701 | { | 719 | { |
720 | struct file *old_exe_file = rcu_dereference_protected(mm->exe_file, | ||
721 | !atomic_read(&mm->mm_users) || current->in_execve || | ||
722 | lockdep_is_held(&mm->mmap_sem)); | ||
723 | |||
702 | if (new_exe_file) | 724 | if (new_exe_file) |
703 | get_file(new_exe_file); | 725 | get_file(new_exe_file); |
704 | if (mm->exe_file) | 726 | rcu_assign_pointer(mm->exe_file, new_exe_file); |
705 | fput(mm->exe_file); | 727 | if (old_exe_file) |
706 | mm->exe_file = new_exe_file; | 728 | fput(old_exe_file); |
707 | } | 729 | } |
708 | 730 | ||
731 | /** | ||
732 | * get_mm_exe_file - acquire a reference to the mm's executable file | ||
733 | * | ||
734 | * Returns %NULL if mm has no associated executable file. | ||
735 | * User must release file via fput(). | ||
736 | */ | ||
709 | struct file *get_mm_exe_file(struct mm_struct *mm) | 737 | struct file *get_mm_exe_file(struct mm_struct *mm) |
710 | { | 738 | { |
711 | struct file *exe_file; | 739 | struct file *exe_file; |
712 | 740 | ||
713 | /* We need mmap_sem to protect against races with removal of exe_file */ | 741 | rcu_read_lock(); |
714 | down_read(&mm->mmap_sem); | 742 | exe_file = rcu_dereference(mm->exe_file); |
715 | exe_file = mm->exe_file; | 743 | if (exe_file && !get_file_rcu(exe_file)) |
716 | if (exe_file) | 744 | exe_file = NULL; |
717 | get_file(exe_file); | 745 | rcu_read_unlock(); |
718 | up_read(&mm->mmap_sem); | ||
719 | return exe_file; | 746 | return exe_file; |
720 | } | 747 | } |
721 | 748 | ||
722 | static void dup_mm_exe_file(struct mm_struct *oldmm, struct mm_struct *newmm) | ||
723 | { | ||
724 | /* It's safe to write the exe_file pointer without exe_file_lock because | ||
725 | * this is called during fork when the task is not yet in /proc */ | ||
726 | newmm->exe_file = get_mm_exe_file(oldmm); | ||
727 | } | ||
728 | |||
729 | /** | 749 | /** |
730 | * get_task_mm - acquire a reference to the task's mm | 750 | * get_task_mm - acquire a reference to the task's mm |
731 | * | 751 | * |
@@ -887,8 +907,6 @@ static struct mm_struct *dup_mm(struct task_struct *tsk) | |||
887 | if (!mm_init(mm, tsk)) | 907 | if (!mm_init(mm, tsk)) |
888 | goto fail_nomem; | 908 | goto fail_nomem; |
889 | 909 | ||
890 | dup_mm_exe_file(oldmm, mm); | ||
891 | |||
892 | err = dup_mmap(mm, oldmm); | 910 | err = dup_mmap(mm, oldmm); |
893 | if (err) | 911 | if (err) |
894 | goto free_pt; | 912 | goto free_pt; |