diff options
author | Todd Kjos <tkjos@android.com> | 2017-11-27 12:32:33 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-12-18 09:47:12 -0500 |
commit | 7f3dc0088b98533f17128058fac73cd8b2752ef1 (patch) | |
tree | 4bfb1d5f8b5356832a99e4c1abf96c05a668e638 /drivers/android/binder.c | |
parent | 869b5567e12f63ea7407f81728ca87f8c0abbfdb (diff) |
binder: fix proc->files use-after-free
proc->files cleanup is initiated by binder_vma_close. Therefore
a reference on the binder_proc is not enough to prevent the
files_struct from being released while the binder_proc still has
a reference. This can lead to an attempt to dereference the
stale pointer obtained from proc->files prior to proc->files
cleanup. This has been seen once in task_get_unused_fd_flags()
when __alloc_fd() is called with a stale "files".
The fix is to protect proc->files with a mutex to prevent cleanup
while in use.
Signed-off-by: Todd Kjos <tkjos@google.com>
Cc: stable <stable@vger.kernel.org> # 4.14
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/android/binder.c')
-rw-r--r-- | drivers/android/binder.c | 44 |
1 files changed, 31 insertions, 13 deletions
diff --git a/drivers/android/binder.c b/drivers/android/binder.c index bccec9de0533..a7ecfde66b7b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c | |||
@@ -482,7 +482,8 @@ enum binder_deferred_state { | |||
482 | * @tsk task_struct for group_leader of process | 482 | * @tsk task_struct for group_leader of process |
483 | * (invariant after initialized) | 483 | * (invariant after initialized) |
484 | * @files files_struct for process | 484 | * @files files_struct for process |
485 | * (invariant after initialized) | 485 | * (protected by @files_lock) |
486 | * @files_lock mutex to protect @files | ||
486 | * @deferred_work_node: element for binder_deferred_list | 487 | * @deferred_work_node: element for binder_deferred_list |
487 | * (protected by binder_deferred_lock) | 488 | * (protected by binder_deferred_lock) |
488 | * @deferred_work: bitmap of deferred work to perform | 489 | * @deferred_work: bitmap of deferred work to perform |
@@ -530,6 +531,7 @@ struct binder_proc { | |||
530 | int pid; | 531 | int pid; |
531 | struct task_struct *tsk; | 532 | struct task_struct *tsk; |
532 | struct files_struct *files; | 533 | struct files_struct *files; |
534 | struct mutex files_lock; | ||
533 | struct hlist_node deferred_work_node; | 535 | struct hlist_node deferred_work_node; |
534 | int deferred_work; | 536 | int deferred_work; |
535 | bool is_dead; | 537 | bool is_dead; |
@@ -877,20 +879,26 @@ static void binder_inc_node_tmpref_ilocked(struct binder_node *node); | |||
877 | 879 | ||
878 | static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) | 880 | static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) |
879 | { | 881 | { |
880 | struct files_struct *files = proc->files; | ||
881 | unsigned long rlim_cur; | 882 | unsigned long rlim_cur; |
882 | unsigned long irqs; | 883 | unsigned long irqs; |
884 | int ret; | ||
883 | 885 | ||
884 | if (files == NULL) | 886 | mutex_lock(&proc->files_lock); |
885 | return -ESRCH; | 887 | if (proc->files == NULL) { |
886 | 888 | ret = -ESRCH; | |
887 | if (!lock_task_sighand(proc->tsk, &irqs)) | 889 | goto err; |
888 | return -EMFILE; | 890 | } |
889 | 891 | if (!lock_task_sighand(proc->tsk, &irqs)) { | |
892 | ret = -EMFILE; | ||
893 | goto err; | ||
894 | } | ||
890 | rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); | 895 | rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); |
891 | unlock_task_sighand(proc->tsk, &irqs); | 896 | unlock_task_sighand(proc->tsk, &irqs); |
892 | 897 | ||
893 | return __alloc_fd(files, 0, rlim_cur, flags); | 898 | ret = __alloc_fd(proc->files, 0, rlim_cur, flags); |
899 | err: | ||
900 | mutex_unlock(&proc->files_lock); | ||
901 | return ret; | ||
894 | } | 902 | } |
895 | 903 | ||
896 | /* | 904 | /* |
@@ -899,8 +907,10 @@ static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) | |||
899 | static void task_fd_install( | 907 | static void task_fd_install( |
900 | struct binder_proc *proc, unsigned int fd, struct file *file) | 908 | struct binder_proc *proc, unsigned int fd, struct file *file) |
901 | { | 909 | { |
910 | mutex_lock(&proc->files_lock); | ||
902 | if (proc->files) | 911 | if (proc->files) |
903 | __fd_install(proc->files, fd, file); | 912 | __fd_install(proc->files, fd, file); |
913 | mutex_unlock(&proc->files_lock); | ||
904 | } | 914 | } |
905 | 915 | ||
906 | /* | 916 | /* |
@@ -910,9 +920,11 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) | |||
910 | { | 920 | { |
911 | int retval; | 921 | int retval; |
912 | 922 | ||
913 | if (proc->files == NULL) | 923 | mutex_lock(&proc->files_lock); |
914 | return -ESRCH; | 924 | if (proc->files == NULL) { |
915 | 925 | retval = -ESRCH; | |
926 | goto err; | ||
927 | } | ||
916 | retval = __close_fd(proc->files, fd); | 928 | retval = __close_fd(proc->files, fd); |
917 | /* can't restart close syscall because file table entry was cleared */ | 929 | /* can't restart close syscall because file table entry was cleared */ |
918 | if (unlikely(retval == -ERESTARTSYS || | 930 | if (unlikely(retval == -ERESTARTSYS || |
@@ -920,7 +932,8 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) | |||
920 | retval == -ERESTARTNOHAND || | 932 | retval == -ERESTARTNOHAND || |
921 | retval == -ERESTART_RESTARTBLOCK)) | 933 | retval == -ERESTART_RESTARTBLOCK)) |
922 | retval = -EINTR; | 934 | retval = -EINTR; |
923 | 935 | err: | |
936 | mutex_unlock(&proc->files_lock); | ||
924 | return retval; | 937 | return retval; |
925 | } | 938 | } |
926 | 939 | ||
@@ -4627,7 +4640,9 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) | |||
4627 | ret = binder_alloc_mmap_handler(&proc->alloc, vma); | 4640 | ret = binder_alloc_mmap_handler(&proc->alloc, vma); |
4628 | if (ret) | 4641 | if (ret) |
4629 | return ret; | 4642 | return ret; |
4643 | mutex_lock(&proc->files_lock); | ||
4630 | proc->files = get_files_struct(current); | 4644 | proc->files = get_files_struct(current); |
4645 | mutex_unlock(&proc->files_lock); | ||
4631 | return 0; | 4646 | return 0; |
4632 | 4647 | ||
4633 | err_bad_arg: | 4648 | err_bad_arg: |
@@ -4651,6 +4666,7 @@ static int binder_open(struct inode *nodp, struct file *filp) | |||
4651 | spin_lock_init(&proc->outer_lock); | 4666 | spin_lock_init(&proc->outer_lock); |
4652 | get_task_struct(current->group_leader); | 4667 | get_task_struct(current->group_leader); |
4653 | proc->tsk = current->group_leader; | 4668 | proc->tsk = current->group_leader; |
4669 | mutex_init(&proc->files_lock); | ||
4654 | INIT_LIST_HEAD(&proc->todo); | 4670 | INIT_LIST_HEAD(&proc->todo); |
4655 | proc->default_priority = task_nice(current); | 4671 | proc->default_priority = task_nice(current); |
4656 | binder_dev = container_of(filp->private_data, struct binder_device, | 4672 | binder_dev = container_of(filp->private_data, struct binder_device, |
@@ -4903,9 +4919,11 @@ static void binder_deferred_func(struct work_struct *work) | |||
4903 | 4919 | ||
4904 | files = NULL; | 4920 | files = NULL; |
4905 | if (defer & BINDER_DEFERRED_PUT_FILES) { | 4921 | if (defer & BINDER_DEFERRED_PUT_FILES) { |
4922 | mutex_lock(&proc->files_lock); | ||
4906 | files = proc->files; | 4923 | files = proc->files; |
4907 | if (files) | 4924 | if (files) |
4908 | proc->files = NULL; | 4925 | proc->files = NULL; |
4926 | mutex_unlock(&proc->files_lock); | ||
4909 | } | 4927 | } |
4910 | 4928 | ||
4911 | if (defer & BINDER_DEFERRED_FLUSH) | 4929 | if (defer & BINDER_DEFERRED_FLUSH) |