aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarek Poplawski <jarkao2@gmail.com>2010-01-16 04:04:04 -0500
committerDavid S. Miller <davem@davemloft.net>2010-01-16 04:04:04 -0500
commitd00c362f1b0ff54161e0a42b4554ac621a9ef92d (patch)
tree33ffeef90727309ad67690b2b7b63e1161b052ec
parent2a04cd4c7d41c4549764734dcf5a883d304e3229 (diff)
ax25: netrom: rose: Fix timer oopses
Wrong ax25_cb refcounting in ax25_send_frame() and by its callers can cause timer oopses (first reported with 2.6.29.6 kernel). Fixes: http://bugzilla.kernel.org/show_bug.cgi?id=14905 Reported-by: Bernard Pidoux <bpidoux@free.fr> Tested-by: Bernard Pidoux <bpidoux@free.fr> Signed-off-by: Jarek Poplawski <jarkao2@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/netrom.h2
-rw-r--r--net/ax25/ax25_out.c6
-rw-r--r--net/netrom/nr_route.c11
-rw-r--r--net/rose/rose_link.c8
-rw-r--r--net/rose/rose_route.c5
5 files changed, 27 insertions, 5 deletions
diff --git a/include/net/netrom.h b/include/net/netrom.h
index 15696b1fd30f..ab170a60e7d3 100644
--- a/include/net/netrom.h
+++ b/include/net/netrom.h
@@ -132,6 +132,8 @@ static __inline__ void nr_node_put(struct nr_node *nr_node)
132static __inline__ void nr_neigh_put(struct nr_neigh *nr_neigh) 132static __inline__ void nr_neigh_put(struct nr_neigh *nr_neigh)
133{ 133{
134 if (atomic_dec_and_test(&nr_neigh->refcount)) { 134 if (atomic_dec_and_test(&nr_neigh->refcount)) {
135 if (nr_neigh->ax25)
136 ax25_cb_put(nr_neigh->ax25);
135 kfree(nr_neigh->digipeat); 137 kfree(nr_neigh->digipeat);
136 kfree(nr_neigh); 138 kfree(nr_neigh);
137 } 139 }
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index bf706f83a5c9..14912600ec57 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -92,6 +92,12 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax2
92#endif 92#endif
93 } 93 }
94 94
95 /*
96 * There is one ref for the state machine; a caller needs
97 * one more to put it back, just like with the existing one.
98 */
99 ax25_cb_hold(ax25);
100
95 ax25_cb_add(ax25); 101 ax25_cb_add(ax25);
96 102
97 ax25->state = AX25_STATE_1; 103 ax25->state = AX25_STATE_1;
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index aacba76070fc..e2e2d33cafdf 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -843,12 +843,13 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
843 dptr = skb_push(skb, 1); 843 dptr = skb_push(skb, 1);
844 *dptr = AX25_P_NETROM; 844 *dptr = AX25_P_NETROM;
845 845
846 ax25s = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); 846 ax25s = nr_neigh->ax25;
847 if (nr_neigh->ax25 && ax25s) { 847 nr_neigh->ax25 = ax25_send_frame(skb, 256,
848 /* We were already holding this ax25_cb */ 848 (ax25_address *)dev->dev_addr,
849 &nr_neigh->callsign,
850 nr_neigh->digipeat, nr_neigh->dev);
851 if (ax25s)
849 ax25_cb_put(ax25s); 852 ax25_cb_put(ax25s);
850 }
851 nr_neigh->ax25 = ax25s;
852 853
853 dev_put(dev); 854 dev_put(dev);
854 ret = (nr_neigh->ax25 != NULL); 855 ret = (nr_neigh->ax25 != NULL);
diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c
index bd86a63960ce..5ef5f6988a2e 100644
--- a/net/rose/rose_link.c
+++ b/net/rose/rose_link.c
@@ -101,13 +101,17 @@ static void rose_t0timer_expiry(unsigned long param)
101static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) 101static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
102{ 102{
103 ax25_address *rose_call; 103 ax25_address *rose_call;
104 ax25_cb *ax25s;
104 105
105 if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) 106 if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
106 rose_call = (ax25_address *)neigh->dev->dev_addr; 107 rose_call = (ax25_address *)neigh->dev->dev_addr;
107 else 108 else
108 rose_call = &rose_callsign; 109 rose_call = &rose_callsign;
109 110
111 ax25s = neigh->ax25;
110 neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); 112 neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
113 if (ax25s)
114 ax25_cb_put(ax25s);
111 115
112 return (neigh->ax25 != NULL); 116 return (neigh->ax25 != NULL);
113} 117}
@@ -120,13 +124,17 @@ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
120static int rose_link_up(struct rose_neigh *neigh) 124static int rose_link_up(struct rose_neigh *neigh)
121{ 125{
122 ax25_address *rose_call; 126 ax25_address *rose_call;
127 ax25_cb *ax25s;
123 128
124 if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) 129 if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
125 rose_call = (ax25_address *)neigh->dev->dev_addr; 130 rose_call = (ax25_address *)neigh->dev->dev_addr;
126 else 131 else
127 rose_call = &rose_callsign; 132 rose_call = &rose_callsign;
128 133
134 ax25s = neigh->ax25;
129 neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); 135 neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
136 if (ax25s)
137 ax25_cb_put(ax25s);
130 138
131 return (neigh->ax25 != NULL); 139 return (neigh->ax25 != NULL);
132} 140}
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 795c4b025e31..70a0b3b4b4d2 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -235,6 +235,8 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
235 235
236 if ((s = rose_neigh_list) == rose_neigh) { 236 if ((s = rose_neigh_list) == rose_neigh) {
237 rose_neigh_list = rose_neigh->next; 237 rose_neigh_list = rose_neigh->next;
238 if (rose_neigh->ax25)
239 ax25_cb_put(rose_neigh->ax25);
238 kfree(rose_neigh->digipeat); 240 kfree(rose_neigh->digipeat);
239 kfree(rose_neigh); 241 kfree(rose_neigh);
240 return; 242 return;
@@ -243,6 +245,8 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
243 while (s != NULL && s->next != NULL) { 245 while (s != NULL && s->next != NULL) {
244 if (s->next == rose_neigh) { 246 if (s->next == rose_neigh) {
245 s->next = rose_neigh->next; 247 s->next = rose_neigh->next;
248 if (rose_neigh->ax25)
249 ax25_cb_put(rose_neigh->ax25);
246 kfree(rose_neigh->digipeat); 250 kfree(rose_neigh->digipeat);
247 kfree(rose_neigh); 251 kfree(rose_neigh);
248 return; 252 return;
@@ -812,6 +816,7 @@ void rose_link_failed(ax25_cb *ax25, int reason)
812 816
813 if (rose_neigh != NULL) { 817 if (rose_neigh != NULL) {
814 rose_neigh->ax25 = NULL; 818 rose_neigh->ax25 = NULL;
819 ax25_cb_put(ax25);
815 820
816 rose_del_route_by_neigh(rose_neigh); 821 rose_del_route_by_neigh(rose_neigh);
817 rose_kill_by_neigh(rose_neigh); 822 rose_kill_by_neigh(rose_neigh);