Commit 6ff2d39b91aec3dcae951afa982059e3dd9b49dc

Authored by Manfred Spraul
Committed by Linus Torvalds
1 parent 1d678f365d

lib/idr.c: fix rcu related race with idr_find

2nd part of the fixes needed for
http://bugzilla.kernel.org/show_bug.cgi?id=11796.

When the idr tree is either grown or shrunk, then the update to the number
of layers and the top pointer were not atomic.  This race caused crashes.

The attached patch fixes that by replicating the layers counter in each
layer, thus idr_find doesn't need idp->layers anymore.

Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
Cc: Clement Calmels <cboulte@gmail.com>
Cc: Nadia Derbey <Nadia.Derbey@bull.net>
Cc: Pierre Peiffer <peifferp@gmail.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 2 changed files with 14 additions and 3 deletions Side-by-side Diff

... ... @@ -52,13 +52,14 @@
52 52 unsigned long bitmap; /* A zero bit means "space here" */
53 53 struct idr_layer *ary[1<<IDR_BITS];
54 54 int count; /* When zero, we can release it */
  55 + int layer; /* distance from leaf */
55 56 struct rcu_head rcu_head;
56 57 };
57 58  
58 59 struct idr {
59 60 struct idr_layer *top;
60 61 struct idr_layer *id_free;
61   - int layers;
  62 + int layers; /* only valid without concurrent changes */
62 63 int id_free_cnt;
63 64 spinlock_t lock;
64 65 };
... ... @@ -185,6 +185,7 @@
185 185 new = get_from_free_list(idp);
186 186 if (!new)
187 187 return -1;
  188 + new->layer = l-1;
188 189 rcu_assign_pointer(p->ary[m], new);
189 190 p->count++;
190 191 }
... ... @@ -210,6 +211,7 @@
210 211 if (unlikely(!p)) {
211 212 if (!(p = get_from_free_list(idp)))
212 213 return -1;
  214 + p->layer = 0;
213 215 layers = 1;
214 216 }
215 217 /*
... ... @@ -237,6 +239,7 @@
237 239 }
238 240 new->ary[0] = p;
239 241 new->count = 1;
  242 + new->layer = layers-1;
240 243 if (p->bitmap == IDR_FULL)
241 244 __set_bit(0, &new->bitmap);
242 245 p = new;
243 246  
244 247  
245 248  
... ... @@ -493,17 +496,21 @@
493 496 int n;
494 497 struct idr_layer *p;
495 498  
496   - n = idp->layers * IDR_BITS;
497 499 p = rcu_dereference(idp->top);
  500 + if (!p)
  501 + return NULL;
  502 + n = (p->layer+1) * IDR_BITS;
498 503  
499 504 /* Mask off upper bits we don't use for the search. */
500 505 id &= MAX_ID_MASK;
501 506  
502 507 if (id >= (1 << n))
503 508 return NULL;
  509 + BUG_ON(n == 0);
504 510  
505 511 while (n > 0 && p) {
506 512 n -= IDR_BITS;
  513 + BUG_ON(n != p->layer*IDR_BITS);
507 514 p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
508 515 }
509 516 return((void *)p);
510 517  
... ... @@ -582,8 +589,11 @@
582 589 int n;
583 590 struct idr_layer *p, *old_p;
584 591  
585   - n = idp->layers * IDR_BITS;
586 592 p = idp->top;
  593 + if (!p)
  594 + return ERR_PTR(-EINVAL);
  595 +
  596 + n = (p->layer+1) * IDR_BITS;
587 597  
588 598 id &= MAX_ID_MASK;
589 599