summaryrefslogtreecommitdiff
path: root/mm/shmem.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2022-12-22 11:11:48 +0300
committerTakashi Iwai <tiwai@suse.de>2022-12-22 11:11:48 +0300
commit2d78eb0342dd2c9c5cde9ae9ada1d33f189a858b (patch)
treef711bc9cab45f4963e4883ef15ff4c54a6cbc12e /mm/shmem.c
parentb47068b4aa53a57552398e3f60d0ed1918700c2b (diff)
parentee0b089d660021792e4ab4dda191b097ce1e964f (diff)
downloadlinux-2d78eb0342dd2c9c5cde9ae9ada1d33f189a858b.tar.xz
Merge branch 'for-next' into for-linus
Diffstat (limited to 'mm/shmem.c')
-rw-r--r--mm/shmem.c17
1 files changed, 17 insertions, 0 deletions
diff --git a/mm/shmem.c b/mm/shmem.c
index 8280a5cb48df..c1d8b8a1aa3b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2424,9 +2424,26 @@ int shmem_mfill_atomic_pte(struct mm_struct *dst_mm,
if (!zeropage) { /* COPY */
page_kaddr = kmap_local_folio(folio, 0);
+ /*
+ * The read mmap_lock is held here. Despite the
+ * mmap_lock being read recursive a deadlock is still
+ * possible if a writer has taken a lock. For example:
+ *
+ * process A thread 1 takes read lock on own mmap_lock
+ * process A thread 2 calls mmap, blocks taking write lock
+ * process B thread 1 takes page fault, read lock on own mmap lock
+ * process B thread 2 calls mmap, blocks taking write lock
+ * process A thread 1 blocks taking read lock on process B
+ * process B thread 1 blocks taking read lock on process A
+ *
+ * Disable page faults to prevent potential deadlock
+ * and retry the copy outside the mmap_lock.
+ */
+ pagefault_disable();
ret = copy_from_user(page_kaddr,
(const void __user *)src_addr,
PAGE_SIZE);
+ pagefault_enable();
kunmap_local(page_kaddr);
/* fallback to copy_from_user outside mmap_lock */