On Mon, 2012-06-11 at 05:17 -0400, kosaki.motohiro@gmail.com wrote:
From: KOSAKI Motohiro <redacted>
commit cc9a6c8776 (cpuset: mm: reduce large amounts of memory barrier related
damage v3) introduced a memory corruption.
shmem_alloc_page() passes pseudo vma and it has one significant unique
combination, vma->vm_ops=NULL and (vma->policy->flags & MPOL_F_SHARED).
Now, get_vma_policy() does NOT increase a policy ref when vma->vm_ops=NULL
and mpol_cond_put() DOES decrease a policy ref when a policy has MPOL_F_SHARED.
Therefore, when alloc_pages_vma() goes 'goto retry_cpuset' path, a policy
refcount will be decreased too much and therefore it will make a memory corruption.
[...]
quoted hunk ↗ jump to hunk
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1544,18 +1544,29 @@ struct mempolicy *get_vma_policy(struct task_struct *task,
struct vm_area_struct *vma, unsigned long addr)
{
struct mempolicy *pol = task->mempolicy;
+ int got_ref;
= 0
And this should really be a bool.
if (vma) {
if (vma->vm_ops && vma->vm_ops->get_policy) {
struct mempolicy *vpol = vma->vm_ops->get_policy(vma,
addr);
- if (vpol)
+ if (vpol) {
pol = vpol;
+ got_ref = 1;
+ }
} else if (vma->vm_policy)
pol = vma->vm_policy;
}
if (!pol)
pol = &default_policy;
+
+ /*
+ * shmem_alloc_page() passes MPOL_F_SHARED policy with vma->vm_ops=NULL.
+ * Thus, we need to take additional ref for avoiding refcount imbalance.
+ */
+ if (!got_ref && mpol_needs_cond_ref(pol))
+ mpol_get(pol);
+
return pol;
}
[...]
--
Ben Hutchings
Computers are not intelligent. They only think they are.