Skip to content

Commit 9ee6edd

Browse files
committed
mm: fix follow_pfnmap API lockdep assert
jira LE-3557 Rebuild_History Non-Buildable kernel-5.14.0-570.26.1.el9_6 commit-author Linus Torvalds <[email protected]> commit b1b4675 Empty-Commit: Cherry-Pick Conflicts during history rebuild. Will be included in final tarball splat. Ref for failed cherry-pick at: ciq/ciq_backports/kernel-5.14.0-570.26.1.el9_6/b1b46751.failed The lockdep asserts for the new follow_pfnmap() API "knows" that a pfnmap always has a vma->vm_file, since that's the only way to create such a mapping. And that's actually true for all the normal cases. But not for the mmap failure case, where the incomplete mapping is torn down and we have cleared vma->vm_file because the failure occured before the file was linked to the vma. So this codepath does actually need to check for vm_file being NULL. Reported-by: Jann Horn <[email protected]> Fixes: 6da8e96 ("mm: new follow_pfnmap API") Cc: Peter Xu <[email protected]> Cc: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]> (cherry picked from commit b1b4675) Signed-off-by: Jonathan Maple <[email protected]> # Conflicts: # mm/memory.c
1 parent 1117214 commit 9ee6edd

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
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+
Cc: Peter Xu <[email protected]>
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

Comments
 (0)