Commit b22d127a39ddd10d93deee3d96e643657ad53a49

Authored by Mel Gorman
Committed by Linus Torvalds
1 parent 869833f2c5

mempolicy: fix a race in shared_policy_replace()

shared_policy_replace() use of sp_alloc() is unsafe.  1) sp_node cannot
be dereferenced if sp->lock is not held and 2) another thread can modify
sp_node between spin_unlock for allocating a new sp node and next
spin_lock.  The bug was introduced before 2.6.12-rc2.

Kosaki's original patch for this problem was to allocate an sp node and
policy within shared_policy_replace and initialise it when the lock is
reacquired.  I was not keen on this approach because it partially
duplicates sp_alloc().  As the paths were sp->lock is taken are not that
performance critical this patch converts sp->lock to sp->mutex so it can
sleep when calling sp_alloc().

[kosaki.motohiro@jp.fujitsu.com: Original patch]
Signed-off-by: Mel Gorman <mgorman@suse.de>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Christoph Lameter <cl@linux.com>
Cc: Josh Boyer <jwboyer@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 2 changed files with 17 additions and 22 deletions Side-by-side Diff

include/linux/mempolicy.h
... ... @@ -188,7 +188,7 @@
188 188  
189 189 struct shared_policy {
190 190 struct rb_root root;
191   - spinlock_t lock;
  191 + struct mutex mutex;
192 192 };
193 193  
194 194 void mpol_shared_policy_init(struct shared_policy *sp, struct mempolicy *mpol);
... ... @@ -2083,7 +2083,7 @@
2083 2083 */
2084 2084  
2085 2085 /* lookup first element intersecting start-end */
2086   -/* Caller holds sp->lock */
  2086 +/* Caller holds sp->mutex */
2087 2087 static struct sp_node *
2088 2088 sp_lookup(struct shared_policy *sp, unsigned long start, unsigned long end)
2089 2089 {
2090 2090  
... ... @@ -2147,13 +2147,13 @@
2147 2147  
2148 2148 if (!sp->root.rb_node)
2149 2149 return NULL;
2150   - spin_lock(&sp->lock);
  2150 + mutex_lock(&sp->mutex);
2151 2151 sn = sp_lookup(sp, idx, idx+1);
2152 2152 if (sn) {
2153 2153 mpol_get(sn->policy);
2154 2154 pol = sn->policy;
2155 2155 }
2156   - spin_unlock(&sp->lock);
  2156 + mutex_unlock(&sp->mutex);
2157 2157 return pol;
2158 2158 }
2159 2159  
2160 2160  
... ... @@ -2193,10 +2193,10 @@
2193 2193 static int shared_policy_replace(struct shared_policy *sp, unsigned long start,
2194 2194 unsigned long end, struct sp_node *new)
2195 2195 {
2196   - struct sp_node *n, *new2 = NULL;
  2196 + struct sp_node *n;
  2197 + int ret = 0;
2197 2198  
2198   -restart:
2199   - spin_lock(&sp->lock);
  2199 + mutex_lock(&sp->mutex);
2200 2200 n = sp_lookup(sp, start, end);
2201 2201 /* Take care of old policies in the same range. */
2202 2202 while (n && n->start < end) {
2203 2203  
2204 2204  
... ... @@ -2209,16 +2209,14 @@
2209 2209 } else {
2210 2210 /* Old policy spanning whole new range. */
2211 2211 if (n->end > end) {
  2212 + struct sp_node *new2;
  2213 + new2 = sp_alloc(end, n->end, n->policy);
2212 2214 if (!new2) {
2213   - spin_unlock(&sp->lock);
2214   - new2 = sp_alloc(end, n->end, n->policy);
2215   - if (!new2)
2216   - return -ENOMEM;
2217   - goto restart;
  2215 + ret = -ENOMEM;
  2216 + goto out;
2218 2217 }
2219 2218 n->end = start;
2220 2219 sp_insert(sp, new2);
2221   - new2 = NULL;
2222 2220 break;
2223 2221 } else
2224 2222 n->end = start;
... ... @@ -2229,12 +2227,9 @@
2229 2227 }
2230 2228 if (new)
2231 2229 sp_insert(sp, new);
2232   - spin_unlock(&sp->lock);
2233   - if (new2) {
2234   - mpol_put(new2->policy);
2235   - kmem_cache_free(sn_cache, new2);
2236   - }
2237   - return 0;
  2230 +out:
  2231 + mutex_unlock(&sp->mutex);
  2232 + return ret;
2238 2233 }
2239 2234  
2240 2235 /**
... ... @@ -2252,7 +2247,7 @@
2252 2247 int ret;
2253 2248  
2254 2249 sp->root = RB_ROOT; /* empty tree == default mempolicy */
2255   - spin_lock_init(&sp->lock);
  2250 + mutex_init(&sp->mutex);
2256 2251  
2257 2252 if (mpol) {
2258 2253 struct vm_area_struct pvma;
... ... @@ -2318,7 +2313,7 @@
2318 2313  
2319 2314 if (!p->root.rb_node)
2320 2315 return;
2321   - spin_lock(&p->lock);
  2316 + mutex_lock(&p->mutex);
2322 2317 next = rb_first(&p->root);
2323 2318 while (next) {
2324 2319 n = rb_entry(next, struct sp_node, nd);
... ... @@ -2327,7 +2322,7 @@
2327 2322 mpol_put(n->policy);
2328 2323 kmem_cache_free(sn_cache, n);
2329 2324 }
2330   - spin_unlock(&p->lock);
  2325 + mutex_unlock(&p->mutex);
2331 2326 }
2332 2327  
2333 2328 /* assumes fs == KERNEL_DS */