Commit 1252ecf63f77ea147bd40f5462c7d9e3d3ae2815
Committed by
David S. Miller
1 parent
00181fc946
Exists in
master
and in
7 other branches
[ATM]: fix possible recursive locking in skb_migrate()
ok this is a real potential deadlock in a way, it takes two locks of 2 skbuffs without doing any kind of lock ordering; I think the following patch should fix it. Just sort the lock taking order by address of the skb.. it's not pretty but it's the best this can do in a minimally invasive way. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Chas Williams <chas@cmf.nrl.navy.mil> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 1 changed file with 11 additions and 6 deletions Inline Diff
net/atm/ipcommon.c
1 | /* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */ | 1 | /* net/atm/ipcommon.c - Common items for all ways of doing IP over ATM */ |
2 | 2 | ||
3 | /* Written 1996-2000 by Werner Almesberger, EPFL LRC/ICA */ | 3 | /* Written 1996-2000 by Werner Almesberger, EPFL LRC/ICA */ |
4 | 4 | ||
5 | 5 | ||
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
7 | #include <linux/string.h> | 7 | #include <linux/string.h> |
8 | #include <linux/skbuff.h> | 8 | #include <linux/skbuff.h> |
9 | #include <linux/netdevice.h> | 9 | #include <linux/netdevice.h> |
10 | #include <linux/in.h> | 10 | #include <linux/in.h> |
11 | #include <linux/atmdev.h> | 11 | #include <linux/atmdev.h> |
12 | #include <linux/atmclip.h> | 12 | #include <linux/atmclip.h> |
13 | 13 | ||
14 | #include "common.h" | 14 | #include "common.h" |
15 | #include "ipcommon.h" | 15 | #include "ipcommon.h" |
16 | 16 | ||
17 | 17 | ||
18 | #if 0 | 18 | #if 0 |
19 | #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) | 19 | #define DPRINTK(format,args...) printk(KERN_DEBUG format,##args) |
20 | #else | 20 | #else |
21 | #define DPRINTK(format,args...) | 21 | #define DPRINTK(format,args...) |
22 | #endif | 22 | #endif |
23 | 23 | ||
24 | 24 | ||
25 | /* | 25 | /* |
26 | * skb_migrate appends the list at "from" to "to", emptying "from" in the | 26 | * skb_migrate appends the list at "from" to "to", emptying "from" in the |
27 | * process. skb_migrate is atomic with respect to all other skb operations on | 27 | * process. skb_migrate is atomic with respect to all other skb operations on |
28 | * "from" and "to". Note that it locks both lists at the same time, so beware | 28 | * "from" and "to". Note that it locks both lists at the same time, so to deal |
29 | * of potential deadlocks. | 29 | * with the lock ordering, the locks are taken in address order. |
30 | * | 30 | * |
31 | * This function should live in skbuff.c or skbuff.h. | 31 | * This function should live in skbuff.c or skbuff.h. |
32 | */ | 32 | */ |
33 | 33 | ||
34 | 34 | ||
35 | void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to) | 35 | void skb_migrate(struct sk_buff_head *from, struct sk_buff_head *to) |
36 | { | 36 | { |
37 | unsigned long flags; | 37 | unsigned long flags; |
38 | struct sk_buff *skb_from = (struct sk_buff *) from; | 38 | struct sk_buff *skb_from = (struct sk_buff *) from; |
39 | struct sk_buff *skb_to = (struct sk_buff *) to; | 39 | struct sk_buff *skb_to = (struct sk_buff *) to; |
40 | struct sk_buff *prev; | 40 | struct sk_buff *prev; |
41 | 41 | ||
42 | spin_lock_irqsave(&from->lock,flags); | 42 | if ((unsigned long) from < (unsigned long) to) { |
43 | spin_lock(&to->lock); | 43 | spin_lock_irqsave(&from->lock, flags); |
44 | spin_lock_nested(&to->lock, SINGLE_DEPTH_NESTING); | ||
45 | } else { | ||
46 | spin_lock_irqsave(&to->lock, flags); | ||
47 | spin_lock_nested(&from->lock, SINGLE_DEPTH_NESTING); | ||
48 | } | ||
44 | prev = from->prev; | 49 | prev = from->prev; |
45 | from->next->prev = to->prev; | 50 | from->next->prev = to->prev; |
46 | prev->next = skb_to; | 51 | prev->next = skb_to; |
47 | to->prev->next = from->next; | 52 | to->prev->next = from->next; |
48 | to->prev = from->prev; | 53 | to->prev = from->prev; |
49 | to->qlen += from->qlen; | 54 | to->qlen += from->qlen; |
50 | spin_unlock(&to->lock); | 55 | spin_unlock(&to->lock); |
51 | from->prev = skb_from; | 56 | from->prev = skb_from; |
52 | from->next = skb_from; | 57 | from->next = skb_from; |
53 | from->qlen = 0; | 58 | from->qlen = 0; |
54 | spin_unlock_irqrestore(&from->lock,flags); | 59 | spin_unlock_irqrestore(&from->lock, flags); |
55 | } | 60 | } |
56 | 61 | ||
57 | 62 | ||
58 | EXPORT_SYMBOL(skb_migrate); | 63 | EXPORT_SYMBOL(skb_migrate); |
59 | 64 |