aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/file.c3
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/mm_types.h2
-rw-r--r--kernel/fork.c56
4 files changed, 40 insertions, 22 deletions
diff --git a/fs/file.c b/fs/file.c
index ee738ea028fa..93c5f89c248b 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -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) 534static 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}
698EXPORT_SYMBOL_GPL(mmput); 707EXPORT_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 */
700void set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file) 718void 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 */
709struct file *get_mm_exe_file(struct mm_struct *mm) 737struct 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
722static 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;