Skip to content

Commit 20261f1

Browse files
thejhcodewalkerster
authored andcommitted
mm/rmap: Fix anon_vma->degree ambiguity leading to double-reuse
commit 2555283 upstream. anon_vma->degree tracks the combined number of child anon_vmas and VMAs that use the anon_vma as their ->anon_vma. anon_vma_clone() then assumes that for any anon_vma attached to src->anon_vma_chain other than src->anon_vma, it is impossible for it to be a leaf node of the VMA tree, meaning that for such VMAs ->degree is elevated by 1 because of a child anon_vma, meaning that if ->degree equals 1 there are no VMAs that use the anon_vma as their ->anon_vma. This assumption is wrong because the ->degree optimization leads to leaf nodes being abandoned on anon_vma_clone() - an existing anon_vma is reused and no new parent-child relationship is created. So it is possible to reuse an anon_vma for one VMA while it is still tied to another VMA. This is an issue because is_mergeable_anon_vma() and its callers assume that if two VMAs have the same ->anon_vma, the list of anon_vmas attached to the VMAs is guaranteed to be the same. When this assumption is violated, vma_merge() can merge pages into a VMA that is not attached to the corresponding anon_vma, leading to dangling page->mapping pointers that will be dereferenced during rmap walks. Fix it by separately tracking the number of child anon_vmas and the number of VMAs using the anon_vma as their ->anon_vma. Fixes: 7a3ef20 ("mm: prevent endless growth of anon_vma hierarchy") Cc: [email protected] Acked-by: Michal Hocko <[email protected]> Acked-by: Vlastimil Babka <[email protected]> Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Linus Torvalds <[email protected]> [manually fixed up different indentation in stable] Signed-off-by: Jann Horn <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a0c0c25 commit 20261f1

File tree

2 files changed

+22
-16
lines changed

2 files changed

+22
-16
lines changed

include/linux/rmap.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ struct anon_vma {
3737
atomic_t refcount;
3838

3939
/*
40-
* Count of child anon_vmas and VMAs which points to this anon_vma.
40+
* Count of child anon_vmas. Equals to the count of all anon_vmas that
41+
* have ->parent pointing to this one, including itself.
4142
*
4243
* This counter is used for making decision about reusing anon_vma
4344
* instead of forking new one. See comments in function anon_vma_clone.
4445
*/
45-
unsigned degree;
46+
unsigned long num_children;
47+
/* Count of VMAs whose ->anon_vma pointer points to this object. */
48+
unsigned long num_active_vmas;
4649

4750
struct anon_vma *parent; /* Parent of this anon_vma */
4851

mm/rmap.c

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ static inline struct anon_vma *anon_vma_alloc(void)
7878
anon_vma = kmem_cache_alloc(anon_vma_cachep, GFP_KERNEL);
7979
if (anon_vma) {
8080
atomic_set(&anon_vma->refcount, 1);
81-
anon_vma->degree = 1; /* Reference for first vma */
81+
anon_vma->num_children = 0;
82+
anon_vma->num_active_vmas = 0;
8283
anon_vma->parent = anon_vma;
8384
/*
8485
* Initialise the anon_vma root to point to itself. If called
@@ -187,6 +188,7 @@ int anon_vma_prepare(struct vm_area_struct *vma)
187188
anon_vma = anon_vma_alloc();
188189
if (unlikely(!anon_vma))
189190
goto out_enomem_free_avc;
191+
anon_vma->num_children++; /* self-parent link for new root */
190192
allocated = anon_vma;
191193
}
192194

@@ -196,8 +198,7 @@ int anon_vma_prepare(struct vm_area_struct *vma)
196198
if (likely(!vma->anon_vma)) {
197199
vma->anon_vma = anon_vma;
198200
anon_vma_chain_link(vma, avc, anon_vma);
199-
/* vma reference or self-parent link for new root */
200-
anon_vma->degree++;
201+
anon_vma->num_active_vmas++;
201202
allocated = NULL;
202203
avc = NULL;
203204
}
@@ -276,19 +277,19 @@ int anon_vma_clone(struct vm_area_struct *dst, struct vm_area_struct *src)
276277
anon_vma_chain_link(dst, avc, anon_vma);
277278

278279
/*
279-
* Reuse existing anon_vma if its degree lower than two,
280-
* that means it has no vma and only one anon_vma child.
280+
* Reuse existing anon_vma if it has no vma and only one
281+
* anon_vma child.
281282
*
282-
* Do not chose parent anon_vma, otherwise first child
283-
* will always reuse it. Root anon_vma is never reused:
283+
* Root anon_vma is never reused:
284284
* it has self-parent reference and at least one child.
285285
*/
286-
if (!dst->anon_vma && anon_vma != src->anon_vma &&
287-
anon_vma->degree < 2)
286+
if (!dst->anon_vma &&
287+
anon_vma->num_children < 2 &&
288+
anon_vma->num_active_vmas == 0)
288289
dst->anon_vma = anon_vma;
289290
}
290291
if (dst->anon_vma)
291-
dst->anon_vma->degree++;
292+
dst->anon_vma->num_active_vmas++;
292293
unlock_anon_vma_root(root);
293294
return 0;
294295

@@ -338,6 +339,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
338339
anon_vma = anon_vma_alloc();
339340
if (!anon_vma)
340341
goto out_error;
342+
anon_vma->num_active_vmas++;
341343
avc = anon_vma_chain_alloc(GFP_KERNEL);
342344
if (!avc)
343345
goto out_error_free_anon_vma;
@@ -358,7 +360,7 @@ int anon_vma_fork(struct vm_area_struct *vma, struct vm_area_struct *pvma)
358360
vma->anon_vma = anon_vma;
359361
anon_vma_lock_write(anon_vma);
360362
anon_vma_chain_link(vma, avc, anon_vma);
361-
anon_vma->parent->degree++;
363+
anon_vma->parent->num_children++;
362364
anon_vma_unlock_write(anon_vma);
363365

364366
return 0;
@@ -390,15 +392,15 @@ void unlink_anon_vmas(struct vm_area_struct *vma)
390392
* to free them outside the lock.
391393
*/
392394
if (RB_EMPTY_ROOT(&anon_vma->rb_root)) {
393-
anon_vma->parent->degree--;
395+
anon_vma->parent->num_children--;
394396
continue;
395397
}
396398

397399
list_del(&avc->same_vma);
398400
anon_vma_chain_free(avc);
399401
}
400402
if (vma->anon_vma)
401-
vma->anon_vma->degree--;
403+
vma->anon_vma->num_active_vmas--;
402404
unlock_anon_vma_root(root);
403405

404406
/*
@@ -409,7 +411,8 @@ void unlink_anon_vmas(struct vm_area_struct *vma)
409411
list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) {
410412
struct anon_vma *anon_vma = avc->anon_vma;
411413

412-
VM_WARN_ON(anon_vma->degree);
414+
VM_WARN_ON(anon_vma->num_children);
415+
VM_WARN_ON(anon_vma->num_active_vmas);
413416
put_anon_vma(anon_vma);
414417

415418
list_del(&avc->same_vma);

0 commit comments

Comments
 (0)