Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

binder: fix race between munmap() and direct reclaim

An munmap() on a binder device causes binder_vma_close() to be called
which clears the alloc->vma pointer.

If direct reclaim causes binder_alloc_free_page() to be called, there
is a race where alloc->vma is read into a local vma pointer and then
used later after the mm->mmap_sem is acquired. This can result in
calling zap_page_range() with an invalid vma which manifests as a
use-after-free in zap_page_range().

The fix is to check alloc->vma after acquiring the mmap_sem (which we
were acquiring anyway) and skip zap_page_range() if it has changed
to NULL.

Signed-off-by: Todd Kjos <tkjos@google.com>
Reviewed-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Todd Kjos and committed by
Greg Kroah-Hartman
5cec2d2e 5997da82

+8 -10
+8 -10
drivers/android/binder_alloc.c
··· 927 927 928 928 index = page - alloc->pages; 929 929 page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; 930 + 931 + mm = alloc->vma_vm_mm; 932 + if (!mmget_not_zero(mm)) 933 + goto err_mmget; 934 + if (!down_write_trylock(&mm->mmap_sem)) 935 + goto err_down_write_mmap_sem_failed; 930 936 vma = binder_alloc_get_vma(alloc); 931 - if (vma) { 932 - if (!mmget_not_zero(alloc->vma_vm_mm)) 933 - goto err_mmget; 934 - mm = alloc->vma_vm_mm; 935 - if (!down_read_trylock(&mm->mmap_sem)) 936 - goto err_down_write_mmap_sem_failed; 937 - } 938 937 939 938 list_lru_isolate(lru, item); 940 939 spin_unlock(lock); ··· 944 945 zap_page_range(vma, page_addr, PAGE_SIZE); 945 946 946 947 trace_binder_unmap_user_end(alloc, index); 947 - 948 - up_read(&mm->mmap_sem); 949 - mmput(mm); 950 948 } 949 + up_write(&mm->mmap_sem); 950 + mmput(mm); 951 951 952 952 trace_binder_unmap_kernel_start(alloc, index); 953 953