|
| 1 | +mm: fix follow_pfnmap API lockdep assert |
| 2 | + |
| 3 | +jira LE-3557 |
| 4 | +Rebuild_History Non-Buildable kernel-5.14.0-570.26.1.el9_6 |
| 5 | +commit-author Linus Torvalds < [email protected]> |
| 6 | +commit b1b46751671be5a426982f037a47ae05f37ff80b |
| 7 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 8 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 9 | +ciq/ciq_backports/kernel-5.14.0-570.26.1.el9_6/b1b46751.failed |
| 10 | + |
| 11 | +The lockdep asserts for the new follow_pfnmap() API "knows" that a |
| 12 | +pfnmap always has a vma->vm_file, since that's the only way to create |
| 13 | +such a mapping. |
| 14 | + |
| 15 | +And that's actually true for all the normal cases. But not for the mmap |
| 16 | +failure case, where the incomplete mapping is torn down and we have |
| 17 | +cleared vma->vm_file because the failure occured before the file was |
| 18 | +linked to the vma. |
| 19 | + |
| 20 | +So this codepath does actually need to check for vm_file being NULL. |
| 21 | + |
| 22 | + Reported-by: Jann Horn < [email protected]> |
| 23 | +Fixes: 6da8e9634bb7 ("mm: new follow_pfnmap API") |
| 24 | + |
| 25 | + Cc: Andrew Morton < [email protected]> |
| 26 | + Signed-off-by: Linus Torvalds < [email protected]> |
| 27 | +(cherry picked from commit b1b46751671be5a426982f037a47ae05f37ff80b) |
| 28 | + Signed-off-by: Jonathan Maple < [email protected]> |
| 29 | + |
| 30 | +# Conflicts: |
| 31 | +# mm/memory.c |
| 32 | +diff --cc mm/memory.c |
| 33 | +index e2794e3b8919,3ccee51adfbb..000000000000 |
| 34 | +--- a/mm/memory.c |
| 35 | ++++ b/mm/memory.c |
| 36 | +@@@ -5607,60 -6333,136 +5607,91 @@@ int __pmd_alloc(struct mm_struct *mm, p |
| 37 | + } |
| 38 | + #endif /* __PAGETABLE_PMD_FOLDED */ |
| 39 | + |
| 40 | +++<<<<<<< HEAD |
| 41 | +++======= |
| 42 | ++ static inline void pfnmap_args_setup(struct follow_pfnmap_args *args, |
| 43 | ++ spinlock_t *lock, pte_t *ptep, |
| 44 | ++ pgprot_t pgprot, unsigned long pfn_base, |
| 45 | ++ unsigned long addr_mask, bool writable, |
| 46 | ++ bool special) |
| 47 | ++ { |
| 48 | ++ args->lock = lock; |
| 49 | ++ args->ptep = ptep; |
| 50 | ++ args->pfn = pfn_base + ((args->address & ~addr_mask) >> PAGE_SHIFT); |
| 51 | ++ args->pgprot = pgprot; |
| 52 | ++ args->writable = writable; |
| 53 | ++ args->special = special; |
| 54 | ++ } |
| 55 | ++ |
| 56 | ++ static inline void pfnmap_lockdep_assert(struct vm_area_struct *vma) |
| 57 | ++ { |
| 58 | ++ #ifdef CONFIG_LOCKDEP |
| 59 | ++ struct file *file = vma->vm_file; |
| 60 | ++ struct address_space *mapping = file ? file->f_mapping : NULL; |
| 61 | ++ |
| 62 | ++ if (mapping) |
| 63 | ++ lockdep_assert(lockdep_is_held(&vma->vm_file->f_mapping->i_mmap_rwsem) || |
| 64 | ++ lockdep_is_held(&vma->vm_mm->mmap_lock)); |
| 65 | ++ else |
| 66 | ++ lockdep_assert(lockdep_is_held(&vma->vm_mm->mmap_lock)); |
| 67 | ++ #endif |
| 68 | ++ } |
| 69 | ++ |
| 70 | +++>>>>>>> b1b46751671b (mm: fix follow_pfnmap API lockdep assert) |
| 71 | + /** |
| 72 | + - * follow_pfnmap_start() - Look up a pfn mapping at a user virtual address |
| 73 | + - * @args: Pointer to struct @follow_pfnmap_args |
| 74 | + - * |
| 75 | + - * The caller needs to setup args->vma and args->address to point to the |
| 76 | + - * virtual address as the target of such lookup. On a successful return, |
| 77 | + - * the results will be put into other output fields. |
| 78 | + - * |
| 79 | + - * After the caller finished using the fields, the caller must invoke |
| 80 | + - * another follow_pfnmap_end() to proper releases the locks and resources |
| 81 | + - * of such look up request. |
| 82 | + - * |
| 83 | + - * During the start() and end() calls, the results in @args will be valid |
| 84 | + - * as proper locks will be held. After the end() is called, all the fields |
| 85 | + - * in @follow_pfnmap_args will be invalid to be further accessed. Further |
| 86 | + - * use of such information after end() may require proper synchronizations |
| 87 | + - * by the caller with page table updates, otherwise it can create a |
| 88 | + - * security bug. |
| 89 | + + * follow_pte - look up PTE at a user virtual address |
| 90 | + + * @mm: the mm_struct of the target address space |
| 91 | + + * @address: user virtual address |
| 92 | + + * @ptepp: location to store found PTE |
| 93 | + + * @ptlp: location to store the lock for the PTE |
| 94 | + * |
| 95 | + - * If the PTE maps a refcounted page, callers are responsible to protect |
| 96 | + - * against invalidation with MMU notifiers; otherwise access to the PFN at |
| 97 | + - * a later point in time can trigger use-after-free. |
| 98 | + + * On a successful return, the pointer to the PTE is stored in @ptepp; |
| 99 | + + * the corresponding lock is taken and its location is stored in @ptlp. |
| 100 | + + * The contents of the PTE are only stable until @ptlp is released; |
| 101 | + + * any further use, if any, must be protected against invalidation |
| 102 | + + * with MMU notifiers. |
| 103 | + * |
| 104 | + * Only IO mappings and raw PFN mappings are allowed. The mmap semaphore |
| 105 | + - * should be taken for read, and the mmap semaphore cannot be released |
| 106 | + - * before the end() is invoked. |
| 107 | + + * should be taken for read. |
| 108 | + * |
| 109 | + - * This function must not be used to modify PTE content. |
| 110 | + + * KVM uses this function. While it is arguably less bad than ``follow_pfn``, |
| 111 | + + * it is not a good general-purpose API. |
| 112 | + * |
| 113 | + - * Return: zero on success, negative otherwise. |
| 114 | + + * Return: zero on success, -ve otherwise. |
| 115 | + */ |
| 116 | + -int follow_pfnmap_start(struct follow_pfnmap_args *args) |
| 117 | + +int follow_pte(struct mm_struct *mm, unsigned long address, |
| 118 | + + pte_t **ptepp, spinlock_t **ptlp) |
| 119 | + { |
| 120 | + - struct vm_area_struct *vma = args->vma; |
| 121 | + - unsigned long address = args->address; |
| 122 | + - struct mm_struct *mm = vma->vm_mm; |
| 123 | + - spinlock_t *lock; |
| 124 | + - pgd_t *pgdp; |
| 125 | + - p4d_t *p4dp, p4d; |
| 126 | + - pud_t *pudp, pud; |
| 127 | + - pmd_t *pmdp, pmd; |
| 128 | + - pte_t *ptep, pte; |
| 129 | + - |
| 130 | + - pfnmap_lockdep_assert(vma); |
| 131 | + - |
| 132 | + - if (unlikely(address < vma->vm_start || address >= vma->vm_end)) |
| 133 | + - goto out; |
| 134 | + + pgd_t *pgd; |
| 135 | + + p4d_t *p4d; |
| 136 | + + pud_t *pud; |
| 137 | + + pmd_t *pmd; |
| 138 | + + pte_t *ptep; |
| 139 | + |
| 140 | + - if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) |
| 141 | + - goto out; |
| 142 | + -retry: |
| 143 | + - pgdp = pgd_offset(mm, address); |
| 144 | + - if (pgd_none(*pgdp) || unlikely(pgd_bad(*pgdp))) |
| 145 | + + pgd = pgd_offset(mm, address); |
| 146 | + + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) |
| 147 | + goto out; |
| 148 | + |
| 149 | + - p4dp = p4d_offset(pgdp, address); |
| 150 | + - p4d = READ_ONCE(*p4dp); |
| 151 | + - if (p4d_none(p4d) || unlikely(p4d_bad(p4d))) |
| 152 | + + p4d = p4d_offset(pgd, address); |
| 153 | + + if (p4d_none(*p4d) || unlikely(p4d_bad(*p4d))) |
| 154 | + goto out; |
| 155 | + |
| 156 | + - pudp = pud_offset(p4dp, address); |
| 157 | + - pud = READ_ONCE(*pudp); |
| 158 | + - if (pud_none(pud)) |
| 159 | + + pud = pud_offset(p4d, address); |
| 160 | + + if (pud_none(*pud) || unlikely(pud_bad(*pud))) |
| 161 | + goto out; |
| 162 | + - if (pud_leaf(pud)) { |
| 163 | + - lock = pud_lock(mm, pudp); |
| 164 | + - if (!unlikely(pud_leaf(pud))) { |
| 165 | + - spin_unlock(lock); |
| 166 | + - goto retry; |
| 167 | + - } |
| 168 | + - pfnmap_args_setup(args, lock, NULL, pud_pgprot(pud), |
| 169 | + - pud_pfn(pud), PUD_MASK, pud_write(pud), |
| 170 | + - pud_special(pud)); |
| 171 | + - return 0; |
| 172 | + - } |
| 173 | + |
| 174 | + - pmdp = pmd_offset(pudp, address); |
| 175 | + - pmd = pmdp_get_lockless(pmdp); |
| 176 | + - if (pmd_leaf(pmd)) { |
| 177 | + - lock = pmd_lock(mm, pmdp); |
| 178 | + - if (!unlikely(pmd_leaf(pmd))) { |
| 179 | + - spin_unlock(lock); |
| 180 | + - goto retry; |
| 181 | + - } |
| 182 | + - pfnmap_args_setup(args, lock, NULL, pmd_pgprot(pmd), |
| 183 | + - pmd_pfn(pmd), PMD_MASK, pmd_write(pmd), |
| 184 | + - pmd_special(pmd)); |
| 185 | + - return 0; |
| 186 | + - } |
| 187 | + + pmd = pmd_offset(pud, address); |
| 188 | + + VM_BUG_ON(pmd_trans_huge(*pmd)); |
| 189 | + |
| 190 | + - ptep = pte_offset_map_lock(mm, pmdp, address, &lock); |
| 191 | + + ptep = pte_offset_map_lock(mm, pmd, address, ptlp); |
| 192 | + if (!ptep) |
| 193 | + goto out; |
| 194 | + - pte = ptep_get(ptep); |
| 195 | + - if (!pte_present(pte)) |
| 196 | + + if (!pte_present(ptep_get(ptep))) |
| 197 | + goto unlock; |
| 198 | + - pfnmap_args_setup(args, lock, ptep, pte_pgprot(pte), |
| 199 | + - pte_pfn(pte), PAGE_MASK, pte_write(pte), |
| 200 | + - pte_special(pte)); |
| 201 | + + *ptepp = ptep; |
| 202 | + return 0; |
| 203 | + unlock: |
| 204 | + - pte_unmap_unlock(ptep, lock); |
| 205 | + + pte_unmap_unlock(ptep, *ptlp); |
| 206 | + out: |
| 207 | + return -EINVAL; |
| 208 | + } |
| 209 | +* Unmerged path mm/memory.c |
0 commit comments