Skip to content

Commit 203e4f4

Browse files
Takamitsu Iwaigregkh
authored andcommitted
net: rose: convert 'use' field to refcount_t
[ Upstream commit d860d1f ] The 'use' field in struct rose_neigh is used as a reference counter but lacks atomicity. This can lead to race conditions where a rose_neigh structure is freed while still being referenced by other code paths. For example, when rose_neigh->use becomes zero during an ioctl operation via rose_rt_ioctl(), the structure may be removed while its timer is still active, potentially causing use-after-free issues. This patch changes the type of 'use' from unsigned short to refcount_t and updates all code paths to use rose_neigh_hold() and rose_neigh_put() which operate reference counts atomically. Fixes: 1da177e ("Linux-2.6.12-rc2") Signed-off-by: Takamitsu Iwai <takamitz@amazon.co.jp> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20250823085857.47674-3-takamitz@amazon.co.jp Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 188ec77 commit 203e4f4

File tree

5 files changed

+45
-33
lines changed

5 files changed

+45
-33
lines changed

include/net/rose.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#ifndef _ROSE_H
99
#define _ROSE_H
1010

11+
#include <linux/refcount.h>
1112
#include <linux/rose.h>
1213
#include <net/ax25.h>
1314
#include <net/sock.h>
@@ -96,7 +97,7 @@ struct rose_neigh {
9697
ax25_cb *ax25;
9798
struct net_device *dev;
9899
unsigned short count;
99-
unsigned short use;
100+
refcount_t use;
100101
unsigned int number;
101102
char restarted;
102103
char dce_mode;
@@ -151,12 +152,19 @@ struct rose_sock {
151152

152153
#define rose_sk(sk) ((struct rose_sock *)(sk))
153154

155+
static inline void rose_neigh_hold(struct rose_neigh *rose_neigh)
156+
{
157+
refcount_inc(&rose_neigh->use);
158+
}
159+
154160
static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
155161
{
156-
if (rose_neigh->ax25)
157-
ax25_cb_put(rose_neigh->ax25);
158-
kfree(rose_neigh->digipeat);
159-
kfree(rose_neigh);
162+
if (refcount_dec_and_test(&rose_neigh->use)) {
163+
if (rose_neigh->ax25)
164+
ax25_cb_put(rose_neigh->ax25);
165+
kfree(rose_neigh->digipeat);
166+
kfree(rose_neigh);
167+
}
160168
}
161169

162170
/* af_rose.c */

net/rose/af_rose.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh)
170170

171171
if (rose->neighbour == neigh) {
172172
rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
173-
rose->neighbour->use--;
173+
rose_neigh_put(rose->neighbour);
174174
rose->neighbour = NULL;
175175
}
176176
}
@@ -212,7 +212,7 @@ static void rose_kill_by_device(struct net_device *dev)
212212
if (rose->device == dev) {
213213
rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
214214
if (rose->neighbour)
215-
rose->neighbour->use--;
215+
rose_neigh_put(rose->neighbour);
216216
netdev_put(rose->device, &rose->dev_tracker);
217217
rose->device = NULL;
218218
}
@@ -655,7 +655,7 @@ static int rose_release(struct socket *sock)
655655
break;
656656

657657
case ROSE_STATE_2:
658-
rose->neighbour->use--;
658+
rose_neigh_put(rose->neighbour);
659659
release_sock(sk);
660660
rose_disconnect(sk, 0, -1, -1);
661661
lock_sock(sk);
@@ -823,6 +823,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
823823
rose->lci = rose_new_lci(rose->neighbour);
824824
if (!rose->lci) {
825825
err = -ENETUNREACH;
826+
rose_neigh_put(rose->neighbour);
826827
goto out_release;
827828
}
828829

@@ -834,12 +835,14 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
834835
dev = rose_dev_first();
835836
if (!dev) {
836837
err = -ENETUNREACH;
838+
rose_neigh_put(rose->neighbour);
837839
goto out_release;
838840
}
839841

840842
user = ax25_findbyuid(current_euid());
841843
if (!user) {
842844
err = -EINVAL;
845+
rose_neigh_put(rose->neighbour);
843846
dev_put(dev);
844847
goto out_release;
845848
}
@@ -874,8 +877,6 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
874877

875878
rose->state = ROSE_STATE_1;
876879

877-
rose->neighbour->use++;
878-
879880
rose_write_internal(sk, ROSE_CALL_REQUEST);
880881
rose_start_heartbeat(sk);
881882
rose_start_t1timer(sk);
@@ -1077,7 +1078,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros
10771078
GFP_ATOMIC);
10781079
make_rose->facilities = facilities;
10791080

1080-
make_rose->neighbour->use++;
1081+
rose_neigh_hold(make_rose->neighbour);
10811082

10821083
if (rose_sk(sk)->defer) {
10831084
make_rose->state = ROSE_STATE_5;

net/rose/rose_in.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety
5656
case ROSE_CLEAR_REQUEST:
5757
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
5858
rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
59-
rose->neighbour->use--;
59+
rose_neigh_put(rose->neighbour);
6060
break;
6161

6262
default:
@@ -79,12 +79,12 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
7979
case ROSE_CLEAR_REQUEST:
8080
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
8181
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
82-
rose->neighbour->use--;
82+
rose_neigh_put(rose->neighbour);
8383
break;
8484

8585
case ROSE_CLEAR_CONFIRMATION:
8686
rose_disconnect(sk, 0, -1, -1);
87-
rose->neighbour->use--;
87+
rose_neigh_put(rose->neighbour);
8888
break;
8989

9090
default:
@@ -120,7 +120,7 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
120120
case ROSE_CLEAR_REQUEST:
121121
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
122122
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
123-
rose->neighbour->use--;
123+
rose_neigh_put(rose->neighbour);
124124
break;
125125

126126
case ROSE_RR:
@@ -233,7 +233,7 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
233233
case ROSE_CLEAR_REQUEST:
234234
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
235235
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
236-
rose->neighbour->use--;
236+
rose_neigh_put(rose->neighbour);
237237
break;
238238

239239
default:
@@ -253,7 +253,7 @@ static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int framety
253253
if (frametype == ROSE_CLEAR_REQUEST) {
254254
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
255255
rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
256-
rose_sk(sk)->neighbour->use--;
256+
rose_neigh_put(rose_sk(sk)->neighbour);
257257
}
258258

259259
return 0;

net/rose/rose_route.c

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
9393
rose_neigh->ax25 = NULL;
9494
rose_neigh->dev = dev;
9595
rose_neigh->count = 0;
96-
rose_neigh->use = 0;
9796
rose_neigh->dce_mode = 0;
9897
rose_neigh->loopback = 0;
9998
rose_neigh->number = rose_neigh_no++;
10099
rose_neigh->restarted = 0;
100+
refcount_set(&rose_neigh->use, 1);
101101

102102
skb_queue_head_init(&rose_neigh->queue);
103103

@@ -255,10 +255,10 @@ static void rose_remove_route(struct rose_route *rose_route)
255255
struct rose_route *s;
256256

257257
if (rose_route->neigh1 != NULL)
258-
rose_route->neigh1->use--;
258+
rose_neigh_put(rose_route->neigh1);
259259

260260
if (rose_route->neigh2 != NULL)
261-
rose_route->neigh2->use--;
261+
rose_neigh_put(rose_route->neigh2);
262262

263263
if ((s = rose_route_list) == rose_route) {
264264
rose_route_list = rose_route->next;
@@ -323,7 +323,7 @@ static int rose_del_node(struct rose_route_struct *rose_route,
323323
if (rose_node->neighbour[i] == rose_neigh) {
324324
rose_neigh->count--;
325325

326-
if (rose_neigh->count == 0 && rose_neigh->use == 0) {
326+
if (rose_neigh->count == 0) {
327327
rose_remove_neigh(rose_neigh);
328328
rose_neigh_put(rose_neigh);
329329
}
@@ -375,11 +375,11 @@ void rose_add_loopback_neigh(void)
375375
sn->ax25 = NULL;
376376
sn->dev = NULL;
377377
sn->count = 0;
378-
sn->use = 0;
379378
sn->dce_mode = 1;
380379
sn->loopback = 1;
381380
sn->number = rose_neigh_no++;
382381
sn->restarted = 1;
382+
refcount_set(&sn->use, 1);
383383

384384
skb_queue_head_init(&sn->queue);
385385

@@ -561,8 +561,7 @@ static int rose_clear_routes(void)
561561
s = rose_neigh;
562562
rose_neigh = rose_neigh->next;
563563

564-
if (s->use == 0 && !s->loopback) {
565-
s->count = 0;
564+
if (!s->loopback) {
566565
rose_remove_neigh(s);
567566
rose_neigh_put(s);
568567
}
@@ -680,6 +679,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
680679
for (i = 0; i < node->count; i++) {
681680
if (node->neighbour[i]->restarted) {
682681
res = node->neighbour[i];
682+
rose_neigh_hold(node->neighbour[i]);
683683
goto out;
684684
}
685685
}
@@ -691,6 +691,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
691691
for (i = 0; i < node->count; i++) {
692692
if (!rose_ftimer_running(node->neighbour[i])) {
693693
res = node->neighbour[i];
694+
rose_neigh_hold(node->neighbour[i]);
694695
goto out;
695696
}
696697
failed = 1;
@@ -780,13 +781,13 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
780781
}
781782

782783
if (rose_route->neigh1 == rose_neigh) {
783-
rose_route->neigh1->use--;
784+
rose_neigh_put(rose_route->neigh1);
784785
rose_route->neigh1 = NULL;
785786
rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0);
786787
}
787788

788789
if (rose_route->neigh2 == rose_neigh) {
789-
rose_route->neigh2->use--;
790+
rose_neigh_put(rose_route->neigh2);
790791
rose_route->neigh2 = NULL;
791792
rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0);
792793
}
@@ -915,7 +916,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
915916
rose_clear_queues(sk);
916917
rose->cause = ROSE_NETWORK_CONGESTION;
917918
rose->diagnostic = 0;
918-
rose->neighbour->use--;
919+
rose_neigh_put(rose->neighbour);
919920
rose->neighbour = NULL;
920921
rose->lci = 0;
921922
rose->state = ROSE_STATE_0;
@@ -1040,12 +1041,12 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
10401041

10411042
if ((new_lci = rose_new_lci(new_neigh)) == 0) {
10421043
rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71);
1043-
goto out;
1044+
goto put_neigh;
10441045
}
10451046

10461047
if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
10471048
rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120);
1048-
goto out;
1049+
goto put_neigh;
10491050
}
10501051

10511052
rose_route->lci1 = lci;
@@ -1058,8 +1059,8 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
10581059
rose_route->lci2 = new_lci;
10591060
rose_route->neigh2 = new_neigh;
10601061

1061-
rose_route->neigh1->use++;
1062-
rose_route->neigh2->use++;
1062+
rose_neigh_hold(rose_route->neigh1);
1063+
rose_neigh_hold(rose_route->neigh2);
10631064

10641065
rose_route->next = rose_route_list;
10651066
rose_route_list = rose_route;
@@ -1071,6 +1072,8 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
10711072
rose_transmit_link(skb, rose_route->neigh2);
10721073
res = 1;
10731074

1075+
put_neigh:
1076+
rose_neigh_put(new_neigh);
10741077
out:
10751078
spin_unlock_bh(&rose_route_list_lock);
10761079
spin_unlock_bh(&rose_neigh_list_lock);
@@ -1186,7 +1189,7 @@ static int rose_neigh_show(struct seq_file *seq, void *v)
11861189
(rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(buf, &rose_neigh->callsign),
11871190
rose_neigh->dev ? rose_neigh->dev->name : "???",
11881191
rose_neigh->count,
1189-
rose_neigh->use,
1192+
refcount_read(&rose_neigh->use) - 1,
11901193
(rose_neigh->dce_mode) ? "DCE" : "DTE",
11911194
(rose_neigh->restarted) ? "yes" : "no",
11921195
ax25_display_timer(&rose_neigh->t0timer) / HZ,

net/rose/rose_timer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ static void rose_timer_expiry(struct timer_list *t)
180180
break;
181181

182182
case ROSE_STATE_2: /* T3 */
183-
rose->neighbour->use--;
183+
rose_neigh_put(rose->neighbour);
184184
rose_disconnect(sk, ETIMEDOUT, -1, -1);
185185
break;
186186

0 commit comments

Comments
 (0)