]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.6.2/mempolicy-remove-mempolicy-sharing.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.6.2 / mempolicy-remove-mempolicy-sharing.patch
1 From 869833f2c5c6e4dd09a5378cfc665ffb4615e5d2 Mon Sep 17 00:00:00 2001
2 From: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
3 Date: Mon, 8 Oct 2012 16:29:16 -0700
4 Subject: mempolicy: remove mempolicy sharing
5
6 From: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
7
8 commit 869833f2c5c6e4dd09a5378cfc665ffb4615e5d2 upstream.
9
10 Dave Jones' system call fuzz testing tool "trinity" triggered the
11 following bug error with slab debugging enabled
12
13 =============================================================================
14 BUG numa_policy (Not tainted): Poison overwritten
15 -----------------------------------------------------------------------------
16
17 INFO: 0xffff880146498250-0xffff880146498250. First byte 0x6a instead of 0x6b
18 INFO: Allocated in mpol_new+0xa3/0x140 age=46310 cpu=6 pid=32154
19 __slab_alloc+0x3d3/0x445
20 kmem_cache_alloc+0x29d/0x2b0
21 mpol_new+0xa3/0x140
22 sys_mbind+0x142/0x620
23 system_call_fastpath+0x16/0x1b
24
25 INFO: Freed in __mpol_put+0x27/0x30 age=46268 cpu=6 pid=32154
26 __slab_free+0x2e/0x1de
27 kmem_cache_free+0x25a/0x260
28 __mpol_put+0x27/0x30
29 remove_vma+0x68/0x90
30 exit_mmap+0x118/0x140
31 mmput+0x73/0x110
32 exit_mm+0x108/0x130
33 do_exit+0x162/0xb90
34 do_group_exit+0x4f/0xc0
35 sys_exit_group+0x17/0x20
36 system_call_fastpath+0x16/0x1b
37
38 INFO: Slab 0xffffea0005192600 objects=27 used=27 fp=0x (null) flags=0x20000000004080
39 INFO: Object 0xffff880146498250 @offset=592 fp=0xffff88014649b9d0
40
41 The problem is that the structure is being prematurely freed due to a
42 reference count imbalance. In the following case mbind(addr, len) should
43 replace the memory policies of both vma1 and vma2 and thus they will
44 become to share the same mempolicy and the new mempolicy will have the
45 MPOL_F_SHARED flag.
46
47 +-------------------+-------------------+
48 | vma1 | vma2(shmem) |
49 +-------------------+-------------------+
50 | |
51 addr addr+len
52
53 alloc_pages_vma() uses get_vma_policy() and mpol_cond_put() pair for
54 maintaining the mempolicy reference count. The current rule is that
55 get_vma_policy() only increments refcount for shmem VMA and
56 mpol_conf_put() only decrements refcount if the policy has
57 MPOL_F_SHARED.
58
59 In above case, vma1 is not shmem vma and vma->policy has MPOL_F_SHARED!
60 The reference count will be decreased even though was not increased
61 whenever alloc_page_vma() is called. This has been broken since commit
62 [52cd3b07: mempolicy: rework mempolicy Reference Counting] in 2008.
63
64 There is another serious bug with the sharing of memory policies.
65 Currently, mempolicy rebind logic (it is called from cpuset rebinding)
66 ignores a refcount of mempolicy and override it forcibly. Thus, any
67 mempolicy sharing may cause mempolicy corruption. The bug was
68 introduced by commit [68860ec1: cpusets: automatic numa mempolicy
69 rebinding].
70
71 Ideally, the shared policy handling would be rewritten to either
72 properly handle COW of the policy structures or at least reference count
73 MPOL_F_SHARED based exclusively on information within the policy.
74 However, this patch takes the easier approach of disabling any policy
75 sharing between VMAs. Each new range allocated with sp_alloc will
76 allocate a new policy, set the reference count to 1 and drop the
77 reference count of the old policy. This increases the memory footprint
78 but is not expected to be a major problem as mbind() is unlikely to be
79 used for fine-grained ranges. It is also inefficient because it means
80 we allocate a new policy even in cases where mbind_range() could use the
81 new_policy passed to it. However, it is more straight-forward and the
82 change should be invisible to the user.
83
84 [mgorman@suse.de: Edited changelog]
85 Reported-by: Dave Jones <davej@redhat.com>,
86 Cc: Christoph Lameter <cl@linux.com>,
87 Reviewed-by: Christoph Lameter <cl@linux.com>
88 Signed-off-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
89 Signed-off-by: Mel Gorman <mgorman@suse.de>
90 Cc: Josh Boyer <jwboyer@gmail.com>
91 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
92 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
93 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
94
95 ---
96 mm/mempolicy.c | 52 ++++++++++++++++++++++++++++++++++++++--------------
97 1 file changed, 38 insertions(+), 14 deletions(-)
98
99 --- a/mm/mempolicy.c
100 +++ b/mm/mempolicy.c
101 @@ -607,24 +607,39 @@ check_range(struct mm_struct *mm, unsign
102 return first;
103 }
104
105 -/* Apply policy to a single VMA */
106 -static int policy_vma(struct vm_area_struct *vma, struct mempolicy *new)
107 +/*
108 + * Apply policy to a single VMA
109 + * This must be called with the mmap_sem held for writing.
110 + */
111 +static int vma_replace_policy(struct vm_area_struct *vma,
112 + struct mempolicy *pol)
113 {
114 - int err = 0;
115 - struct mempolicy *old = vma->vm_policy;
116 + int err;
117 + struct mempolicy *old;
118 + struct mempolicy *new;
119
120 pr_debug("vma %lx-%lx/%lx vm_ops %p vm_file %p set_policy %p\n",
121 vma->vm_start, vma->vm_end, vma->vm_pgoff,
122 vma->vm_ops, vma->vm_file,
123 vma->vm_ops ? vma->vm_ops->set_policy : NULL);
124
125 - if (vma->vm_ops && vma->vm_ops->set_policy)
126 + new = mpol_dup(pol);
127 + if (IS_ERR(new))
128 + return PTR_ERR(new);
129 +
130 + if (vma->vm_ops && vma->vm_ops->set_policy) {
131 err = vma->vm_ops->set_policy(vma, new);
132 - if (!err) {
133 - mpol_get(new);
134 - vma->vm_policy = new;
135 - mpol_put(old);
136 + if (err)
137 + goto err_out;
138 }
139 +
140 + old = vma->vm_policy;
141 + vma->vm_policy = new; /* protected by mmap_sem */
142 + mpol_put(old);
143 +
144 + return 0;
145 + err_out:
146 + mpol_put(new);
147 return err;
148 }
149
150 @@ -676,7 +691,7 @@ static int mbind_range(struct mm_struct
151 if (err)
152 goto out;
153 }
154 - err = policy_vma(vma, new_pol);
155 + err = vma_replace_policy(vma, new_pol);
156 if (err)
157 goto out;
158 }
159 @@ -2153,15 +2168,24 @@ static void sp_delete(struct shared_poli
160 static struct sp_node *sp_alloc(unsigned long start, unsigned long end,
161 struct mempolicy *pol)
162 {
163 - struct sp_node *n = kmem_cache_alloc(sn_cache, GFP_KERNEL);
164 + struct sp_node *n;
165 + struct mempolicy *newpol;
166
167 + n = kmem_cache_alloc(sn_cache, GFP_KERNEL);
168 if (!n)
169 return NULL;
170 +
171 + newpol = mpol_dup(pol);
172 + if (IS_ERR(newpol)) {
173 + kmem_cache_free(sn_cache, n);
174 + return NULL;
175 + }
176 + newpol->flags |= MPOL_F_SHARED;
177 +
178 n->start = start;
179 n->end = end;
180 - mpol_get(pol);
181 - pol->flags |= MPOL_F_SHARED; /* for unref */
182 - n->policy = pol;
183 + n->policy = newpol;
184 +
185 return n;
186 }
187