History

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.

Manfred Spraul committed 17 years ago

Commit 6ff2d391 parent 1d678f32 files · +14 −3Browse files

Unified · Split

2 files
  1. include/linux/idr.h +2 −1
  2. lib/idr.c +12 −2
include/linux/idr.h+2−1
@@ -52,13 +52,14 @@ struct idr_layer { · 51 unmodified lines
5252 unsigned long bitmap; /* A zero bit means "space here" */
5353 struct idr_layer *ary[1<<IDR_BITS];
5454 int count; /* When zero, we can release it */
55+ int layer; /* distance from leaf */
5556 struct rcu_head rcu_head;
5657 };
5758
5859 struct idr {
5960 struct idr_layer *top;
6061 struct idr_layer *id_free;
61− int layers;
62+ int layers; /* only valid without concurrent changes */
6263 int id_free_cnt;
6364 spinlock_t lock;
6465 };
lib/idr.c+12−2
@@ -185,6 +185,7 @@ static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) · 184 unmodified lines
185185 new = get_from_free_list(idp);
186186 if (!new)
187187 return -1;
188+ new->layer = l-1;
188189 rcu_assign_pointer(p->ary[m], new);
189190 p->count++;
190191 }
@@ -210,6 +211,7 @@ build_up: · 19 unmodified lines
210211 if (unlikely(!p)) {
211212 if (!(p = get_from_free_list(idp)))
212213 return -1;
214+ p->layer = 0;
213215 layers = 1;
214216 }
215217 /*
@@ -237,6 +239,7 @@ build_up: · 21 unmodified lines
237239 }
238240 new->ary[0] = p;
239241 new->count = 1;
242+ new->layer = layers-1;
240243 if (p->bitmap == IDR_FULL)
241244 __set_bit(0, &new->bitmap);
242245 p = new;
@@ -493,17 +496,21 @@ void *idr_find(struct idr *idp, int id) · 250 unmodified lines
493496 int n;
494497 struct idr_layer *p;
495498
496− n = idp->layers * IDR_BITS;
497499 p = rcu_dereference(idp->top);
500+ if (!p)
501+ return NULL;
502+ n = (p->layer+1) * IDR_BITS;
498503
499504 /* Mask off upper bits we don't use for the search. */
500505 id &= MAX_ID_MASK;
501506
502507 if (id >= (1 << n))
503508 return NULL;
509+ BUG_ON(n == 0);
504510
505511 while (n > 0 && p) {
506512 n -= IDR_BITS;
513+ BUG_ON(n != p->layer*IDR_BITS);
507514 p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
508515 }
509516 return((void *)p);
@@ -582,8 +589,11 @@ void *idr_replace(struct idr *idp, void *ptr, int id) · 72 unmodified lines
582589 int n;
583590 struct idr_layer *p, *old_p;
584591
585− n = idp->layers * IDR_BITS;
586592 p = idp->top;
593+ if (!p)
594+ return ERR_PTR(-EINVAL);
595+
596+ n = (p->layer+1) * IDR_BITS;
587597
588598 id &= MAX_ID_MASK;
589599

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t