@@ -1583,6 +1583,24 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
1583
1583
pgtable_t pgtable = NULL ;
1584
1584
int ret = - ENOMEM ;
1585
1585
1586
+ pmd = pmdp_get_lockless (src_pmd );
1587
+ if (unlikely (pmd_special (pmd ))) {
1588
+ dst_ptl = pmd_lock (dst_mm , dst_pmd );
1589
+ src_ptl = pmd_lockptr (src_mm , src_pmd );
1590
+ spin_lock_nested (src_ptl , SINGLE_DEPTH_NESTING );
1591
+ /*
1592
+ * No need to recheck the pmd, it can't change with write
1593
+ * mmap lock held here.
1594
+ *
1595
+ * Meanwhile, making sure it's not a CoW VMA with writable
1596
+ * mapping, otherwise it means either the anon page wrongly
1597
+ * applied special bit, or we made the PRIVATE mapping be
1598
+ * able to wrongly write to the backend MMIO.
1599
+ */
1600
+ VM_WARN_ON_ONCE (is_cow_mapping (src_vma -> vm_flags ) && pmd_write (pmd ));
1601
+ goto set_pmd ;
1602
+ }
1603
+
1586
1604
/* Skip if can be re-fill on fault */
1587
1605
if (!vma_is_anonymous (dst_vma ))
1588
1606
return 0 ;
@@ -1664,7 +1682,9 @@ int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
1664
1682
pmdp_set_wrprotect (src_mm , addr , src_pmd );
1665
1683
if (!userfaultfd_wp (dst_vma ))
1666
1684
pmd = pmd_clear_uffd_wp (pmd );
1667
- pmd = pmd_mkold (pmd_wrprotect (pmd ));
1685
+ pmd = pmd_wrprotect (pmd );
1686
+ set_pmd :
1687
+ pmd = pmd_mkold (pmd );
1668
1688
set_pmd_at (dst_mm , addr , dst_pmd , pmd );
1669
1689
1670
1690
ret = 0 ;
@@ -1710,8 +1730,11 @@ int copy_huge_pud(struct mm_struct *dst_mm, struct mm_struct *src_mm,
1710
1730
* TODO: once we support anonymous pages, use
1711
1731
* folio_try_dup_anon_rmap_*() and split if duplicating fails.
1712
1732
*/
1713
- pudp_set_wrprotect (src_mm , addr , src_pud );
1714
- pud = pud_mkold (pud_wrprotect (pud ));
1733
+ if (is_cow_mapping (vma -> vm_flags ) && pud_write (pud )) {
1734
+ pudp_set_wrprotect (src_mm , addr , src_pud );
1735
+ pud = pud_wrprotect (pud );
1736
+ }
1737
+ pud = pud_mkold (pud );
1715
1738
set_pud_at (dst_mm , addr , dst_pud , pud );
1716
1739
1717
1740
ret = 0 ;
0 commit comments