Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: rose: convert 'use' field to refcount_t

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: 1da177e4c3f4 ("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>

authored by

Takamitsu Iwai and committed by
Jakub Kicinski
d860d1fa dcb34659

+45 -33
+13 -5
include/net/rose.h
··· 8 8 #ifndef _ROSE_H 9 9 #define _ROSE_H 10 10 11 + #include <linux/refcount.h> 11 12 #include <linux/rose.h> 12 13 #include <net/ax25.h> 13 14 #include <net/sock.h> ··· 97 96 ax25_cb *ax25; 98 97 struct net_device *dev; 99 98 unsigned short count; 100 - unsigned short use; 99 + refcount_t use; 101 100 unsigned int number; 102 101 char restarted; 103 102 char dce_mode; ··· 152 151 153 152 #define rose_sk(sk) ((struct rose_sock *)(sk)) 154 153 154 + static inline void rose_neigh_hold(struct rose_neigh *rose_neigh) 155 + { 156 + refcount_inc(&rose_neigh->use); 157 + } 158 + 155 159 static inline void rose_neigh_put(struct rose_neigh *rose_neigh) 156 160 { 157 - if (rose_neigh->ax25) 158 - ax25_cb_put(rose_neigh->ax25); 159 - kfree(rose_neigh->digipeat); 160 - kfree(rose_neigh); 161 + if (refcount_dec_and_test(&rose_neigh->use)) { 162 + if (rose_neigh->ax25) 163 + ax25_cb_put(rose_neigh->ax25); 164 + kfree(rose_neigh->digipeat); 165 + kfree(rose_neigh); 166 + } 161 167 } 162 168 163 169 /* af_rose.c */
+7 -6
net/rose/af_rose.c
··· 170 170 171 171 if (rose->neighbour == neigh) { 172 172 rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); 173 - rose->neighbour->use--; 173 + rose_neigh_put(rose->neighbour); 174 174 rose->neighbour = NULL; 175 175 } 176 176 } ··· 212 212 if (rose->device == dev) { 213 213 rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0); 214 214 if (rose->neighbour) 215 - rose->neighbour->use--; 215 + rose_neigh_put(rose->neighbour); 216 216 netdev_put(rose->device, &rose->dev_tracker); 217 217 rose->device = NULL; 218 218 } ··· 655 655 break; 656 656 657 657 case ROSE_STATE_2: 658 - rose->neighbour->use--; 658 + rose_neigh_put(rose->neighbour); 659 659 release_sock(sk); 660 660 rose_disconnect(sk, 0, -1, -1); 661 661 lock_sock(sk); ··· 823 823 rose->lci = rose_new_lci(rose->neighbour); 824 824 if (!rose->lci) { 825 825 err = -ENETUNREACH; 826 + rose_neigh_put(rose->neighbour); 826 827 goto out_release; 827 828 } 828 829 ··· 835 834 dev = rose_dev_first(); 836 835 if (!dev) { 837 836 err = -ENETUNREACH; 837 + rose_neigh_put(rose->neighbour); 838 838 goto out_release; 839 839 } 840 840 841 841 user = ax25_findbyuid(current_euid()); 842 842 if (!user) { 843 843 err = -EINVAL; 844 + rose_neigh_put(rose->neighbour); 844 845 dev_put(dev); 845 846 goto out_release; 846 847 } ··· 876 873 sk->sk_state = TCP_SYN_SENT; 877 874 878 875 rose->state = ROSE_STATE_1; 879 - 880 - rose->neighbour->use++; 881 876 882 877 rose_write_internal(sk, ROSE_CALL_REQUEST); 883 878 rose_start_heartbeat(sk); ··· 1078 1077 GFP_ATOMIC); 1079 1078 make_rose->facilities = facilities; 1080 1079 1081 - make_rose->neighbour->use++; 1080 + rose_neigh_hold(make_rose->neighbour); 1082 1081 1083 1082 if (rose_sk(sk)->defer) { 1084 1083 make_rose->state = ROSE_STATE_5;
+6 -6
net/rose/rose_in.c
··· 56 56 case ROSE_CLEAR_REQUEST: 57 57 rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); 58 58 rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]); 59 - rose->neighbour->use--; 59 + rose_neigh_put(rose->neighbour); 60 60 break; 61 61 62 62 default: ··· 79 79 case ROSE_CLEAR_REQUEST: 80 80 rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); 81 81 rose_disconnect(sk, 0, skb->data[3], skb->data[4]); 82 - rose->neighbour->use--; 82 + rose_neigh_put(rose->neighbour); 83 83 break; 84 84 85 85 case ROSE_CLEAR_CONFIRMATION: 86 86 rose_disconnect(sk, 0, -1, -1); 87 - rose->neighbour->use--; 87 + rose_neigh_put(rose->neighbour); 88 88 break; 89 89 90 90 default: ··· 121 121 case ROSE_CLEAR_REQUEST: 122 122 rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); 123 123 rose_disconnect(sk, 0, skb->data[3], skb->data[4]); 124 - rose->neighbour->use--; 124 + rose_neigh_put(rose->neighbour); 125 125 break; 126 126 127 127 case ROSE_RR: ··· 234 234 case ROSE_CLEAR_REQUEST: 235 235 rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); 236 236 rose_disconnect(sk, 0, skb->data[3], skb->data[4]); 237 - rose->neighbour->use--; 237 + rose_neigh_put(rose->neighbour); 238 238 break; 239 239 240 240 default: ··· 254 254 if (frametype == ROSE_CLEAR_REQUEST) { 255 255 rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION); 256 256 rose_disconnect(sk, 0, skb->data[3], skb->data[4]); 257 - rose_sk(sk)->neighbour->use--; 257 + rose_neigh_put(rose_sk(sk)->neighbour); 258 258 } 259 259 260 260 return 0;
+18 -15
net/rose/rose_route.c
··· 93 93 rose_neigh->ax25 = NULL; 94 94 rose_neigh->dev = dev; 95 95 rose_neigh->count = 0; 96 - rose_neigh->use = 0; 97 96 rose_neigh->dce_mode = 0; 98 97 rose_neigh->loopback = 0; 99 98 rose_neigh->number = rose_neigh_no++; 100 99 rose_neigh->restarted = 0; 100 + refcount_set(&rose_neigh->use, 1); 101 101 102 102 skb_queue_head_init(&rose_neigh->queue); 103 103 ··· 255 255 struct rose_route *s; 256 256 257 257 if (rose_route->neigh1 != NULL) 258 - rose_route->neigh1->use--; 258 + rose_neigh_put(rose_route->neigh1); 259 259 260 260 if (rose_route->neigh2 != NULL) 261 - rose_route->neigh2->use--; 261 + rose_neigh_put(rose_route->neigh2); 262 262 263 263 if ((s = rose_route_list) == rose_route) { 264 264 rose_route_list = rose_route->next; ··· 323 323 if (rose_node->neighbour[i] == rose_neigh) { 324 324 rose_neigh->count--; 325 325 326 - if (rose_neigh->count == 0 && rose_neigh->use == 0) { 326 + if (rose_neigh->count == 0) { 327 327 rose_remove_neigh(rose_neigh); 328 328 rose_neigh_put(rose_neigh); 329 329 } ··· 375 375 sn->ax25 = NULL; 376 376 sn->dev = NULL; 377 377 sn->count = 0; 378 - sn->use = 0; 379 378 sn->dce_mode = 1; 380 379 sn->loopback = 1; 381 380 sn->number = rose_neigh_no++; 382 381 sn->restarted = 1; 382 + refcount_set(&sn->use, 1); 383 383 384 384 skb_queue_head_init(&sn->queue); 385 385 ··· 561 561 s = rose_neigh; 562 562 rose_neigh = rose_neigh->next; 563 563 564 - if (s->use == 0 && !s->loopback) { 565 - s->count = 0; 564 + if (!s->loopback) { 566 565 rose_remove_neigh(s); 567 566 rose_neigh_put(s); 568 567 } ··· 679 680 for (i = 0; i < node->count; i++) { 680 681 if (node->neighbour[i]->restarted) { 681 682 res = node->neighbour[i]; 683 + rose_neigh_hold(node->neighbour[i]); 682 684 goto out; 683 685 } 684 686 } ··· 691 691 for (i = 0; i < node->count; i++) { 692 692 if (!rose_ftimer_running(node->neighbour[i])) { 693 693 res = node->neighbour[i]; 694 + rose_neigh_hold(node->neighbour[i]); 694 695 goto out; 695 696 } 696 697 failed = 1; ··· 781 780 } 782 781 783 782 if (rose_route->neigh1 == rose_neigh) { 784 - rose_route->neigh1->use--; 783 + rose_neigh_put(rose_route->neigh1); 785 784 rose_route->neigh1 = NULL; 786 785 rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0); 787 786 } 788 787 789 788 if (rose_route->neigh2 == rose_neigh) { 790 - rose_route->neigh2->use--; 789 + rose_neigh_put(rose_route->neigh2); 791 790 rose_route->neigh2 = NULL; 792 791 rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0); 793 792 } ··· 916 915 rose_clear_queues(sk); 917 916 rose->cause = ROSE_NETWORK_CONGESTION; 918 917 rose->diagnostic = 0; 919 - rose->neighbour->use--; 918 + rose_neigh_put(rose->neighbour); 920 919 rose->neighbour = NULL; 921 920 rose->lci = 0; 922 921 rose->state = ROSE_STATE_0; ··· 1041 1040 1042 1041 if ((new_lci = rose_new_lci(new_neigh)) == 0) { 1043 1042 rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71); 1044 - goto out; 1043 + goto put_neigh; 1045 1044 } 1046 1045 1047 1046 if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) { 1048 1047 rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120); 1049 - goto out; 1048 + goto put_neigh; 1050 1049 } 1051 1050 1052 1051 rose_route->lci1 = lci; ··· 1059 1058 rose_route->lci2 = new_lci; 1060 1059 rose_route->neigh2 = new_neigh; 1061 1060 1062 - rose_route->neigh1->use++; 1063 - rose_route->neigh2->use++; 1061 + rose_neigh_hold(rose_route->neigh1); 1062 + rose_neigh_hold(rose_route->neigh2); 1064 1063 1065 1064 rose_route->next = rose_route_list; 1066 1065 rose_route_list = rose_route; ··· 1072 1071 rose_transmit_link(skb, rose_route->neigh2); 1073 1072 res = 1; 1074 1073 1074 + put_neigh: 1075 + rose_neigh_put(new_neigh); 1075 1076 out: 1076 1077 spin_unlock_bh(&rose_route_list_lock); 1077 1078 spin_unlock_bh(&rose_neigh_list_lock); ··· 1189 1186 (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(buf, &rose_neigh->callsign), 1190 1187 rose_neigh->dev ? rose_neigh->dev->name : "???", 1191 1188 rose_neigh->count, 1192 - rose_neigh->use, 1189 + refcount_read(&rose_neigh->use) - 1, 1193 1190 (rose_neigh->dce_mode) ? "DCE" : "DTE", 1194 1191 (rose_neigh->restarted) ? "yes" : "no", 1195 1192 ax25_display_timer(&rose_neigh->t0timer) / HZ,
+1 -1
net/rose/rose_timer.c
··· 180 180 break; 181 181 182 182 case ROSE_STATE_2: /* T3 */ 183 - rose->neighbour->use--; 183 + rose_neigh_put(rose->neighbour); 184 184 rose_disconnect(sk, ETIMEDOUT, -1, -1); 185 185 break; 186 186