]> www.infradead.org Git - users/griffoul/linux.git/commitdiff
net: rose: convert 'use' field to refcount_t
authorTakamitsu Iwai <takamitz@amazon.co.jp>
Sat, 23 Aug 2025 08:58:56 +0000 (17:58 +0900)
committerJakub Kicinski <kuba@kernel.org>
Wed, 27 Aug 2025 14:43:08 +0000 (07:43 -0700)
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>
include/net/rose.h
net/rose/af_rose.c
net/rose/rose_in.c
net/rose/rose_route.c
net/rose/rose_timer.c

index 174b4f605d849ba13bb58f7daf9216bc6ab4c8f0..2b5491bbf39ab5ba666caf92010d7432e876931a 100644 (file)
@@ -8,6 +8,7 @@
 #ifndef _ROSE_H
 #define _ROSE_H 
 
+#include <linux/refcount.h>
 #include <linux/rose.h>
 #include <net/ax25.h>
 #include <net/sock.h>
@@ -96,7 +97,7 @@ struct rose_neigh {
        ax25_cb                 *ax25;
        struct net_device               *dev;
        unsigned short          count;
-       unsigned short          use;
+       refcount_t              use;
        unsigned int            number;
        char                    restarted;
        char                    dce_mode;
@@ -151,12 +152,19 @@ struct rose_sock {
 
 #define rose_sk(sk) ((struct rose_sock *)(sk))
 
+static inline void rose_neigh_hold(struct rose_neigh *rose_neigh)
+{
+       refcount_inc(&rose_neigh->use);
+}
+
 static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
 {
-       if (rose_neigh->ax25)
-               ax25_cb_put(rose_neigh->ax25);
-       kfree(rose_neigh->digipeat);
-       kfree(rose_neigh);
+       if (refcount_dec_and_test(&rose_neigh->use)) {
+               if (rose_neigh->ax25)
+                       ax25_cb_put(rose_neigh->ax25);
+               kfree(rose_neigh->digipeat);
+               kfree(rose_neigh);
+       }
 }
 
 /* af_rose.c */
index 4e72b636a46a5fed572d31dc1355079d7c38c3b8..543f9e8ebb6937102fa1d228b120f177d37f20d1 100644 (file)
@@ -170,7 +170,7 @@ void rose_kill_by_neigh(struct rose_neigh *neigh)
 
                if (rose->neighbour == neigh) {
                        rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
-                       rose->neighbour->use--;
+                       rose_neigh_put(rose->neighbour);
                        rose->neighbour = NULL;
                }
        }
@@ -212,7 +212,7 @@ start:
                if (rose->device == dev) {
                        rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
                        if (rose->neighbour)
-                               rose->neighbour->use--;
+                               rose_neigh_put(rose->neighbour);
                        netdev_put(rose->device, &rose->dev_tracker);
                        rose->device = NULL;
                }
@@ -655,7 +655,7 @@ static int rose_release(struct socket *sock)
                break;
 
        case ROSE_STATE_2:
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                release_sock(sk);
                rose_disconnect(sk, 0, -1, -1);
                lock_sock(sk);
@@ -823,6 +823,7 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
        rose->lci = rose_new_lci(rose->neighbour);
        if (!rose->lci) {
                err = -ENETUNREACH;
+               rose_neigh_put(rose->neighbour);
                goto out_release;
        }
 
@@ -834,12 +835,14 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
                dev = rose_dev_first();
                if (!dev) {
                        err = -ENETUNREACH;
+                       rose_neigh_put(rose->neighbour);
                        goto out_release;
                }
 
                user = ax25_findbyuid(current_euid());
                if (!user) {
                        err = -EINVAL;
+                       rose_neigh_put(rose->neighbour);
                        dev_put(dev);
                        goto out_release;
                }
@@ -874,8 +877,6 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
 
        rose->state = ROSE_STATE_1;
 
-       rose->neighbour->use++;
-
        rose_write_internal(sk, ROSE_CALL_REQUEST);
        rose_start_heartbeat(sk);
        rose_start_t1timer(sk);
@@ -1077,7 +1078,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros
                             GFP_ATOMIC);
        make_rose->facilities    = facilities;
 
-       make_rose->neighbour->use++;
+       rose_neigh_hold(make_rose->neighbour);
 
        if (rose_sk(sk)->defer) {
                make_rose->state = ROSE_STATE_5;
index 3e99181e759f983d3f53f127c5d956863e926c8d..0276b393f0e530ea2d8f689a3dd95364849910ac 100644 (file)
@@ -56,7 +56,7 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety
        case ROSE_CLEAR_REQUEST:
                rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
                rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                break;
 
        default:
@@ -79,12 +79,12 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
        case ROSE_CLEAR_REQUEST:
                rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
                rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                break;
 
        case ROSE_CLEAR_CONFIRMATION:
                rose_disconnect(sk, 0, -1, -1);
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                break;
 
        default:
@@ -121,7 +121,7 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
        case ROSE_CLEAR_REQUEST:
                rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
                rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                break;
 
        case ROSE_RR:
@@ -234,7 +234,7 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
        case ROSE_CLEAR_REQUEST:
                rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
                rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                break;
 
        default:
@@ -254,7 +254,7 @@ static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int framety
        if (frametype == ROSE_CLEAR_REQUEST) {
                rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
                rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
-               rose_sk(sk)->neighbour->use--;
+               rose_neigh_put(rose_sk(sk)->neighbour);
        }
 
        return 0;
index 0c44c416f4853960b84d20eb57ebd3f153b8c181..8efb9033c057c39b76818ebc521b4b61fd102053 100644 (file)
@@ -93,11 +93,11 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
                rose_neigh->ax25      = NULL;
                rose_neigh->dev       = dev;
                rose_neigh->count     = 0;
-               rose_neigh->use       = 0;
                rose_neigh->dce_mode  = 0;
                rose_neigh->loopback  = 0;
                rose_neigh->number    = rose_neigh_no++;
                rose_neigh->restarted = 0;
+               refcount_set(&rose_neigh->use, 1);
 
                skb_queue_head_init(&rose_neigh->queue);
 
@@ -255,10 +255,10 @@ static void rose_remove_route(struct rose_route *rose_route)
        struct rose_route *s;
 
        if (rose_route->neigh1 != NULL)
-               rose_route->neigh1->use--;
+               rose_neigh_put(rose_route->neigh1);
 
        if (rose_route->neigh2 != NULL)
-               rose_route->neigh2->use--;
+               rose_neigh_put(rose_route->neigh2);
 
        if ((s = rose_route_list) == rose_route) {
                rose_route_list = rose_route->next;
@@ -323,7 +323,7 @@ static int rose_del_node(struct rose_route_struct *rose_route,
                if (rose_node->neighbour[i] == rose_neigh) {
                        rose_neigh->count--;
 
-                       if (rose_neigh->count == 0 && rose_neigh->use == 0) {
+                       if (rose_neigh->count == 0) {
                                rose_remove_neigh(rose_neigh);
                                rose_neigh_put(rose_neigh);
                        }
@@ -375,11 +375,11 @@ void rose_add_loopback_neigh(void)
        sn->ax25      = NULL;
        sn->dev       = NULL;
        sn->count     = 0;
-       sn->use       = 0;
        sn->dce_mode  = 1;
        sn->loopback  = 1;
        sn->number    = rose_neigh_no++;
        sn->restarted = 1;
+       refcount_set(&sn->use, 1);
 
        skb_queue_head_init(&sn->queue);
 
@@ -561,8 +561,7 @@ static int rose_clear_routes(void)
                s          = rose_neigh;
                rose_neigh = rose_neigh->next;
 
-               if (s->use == 0 && !s->loopback) {
-                       s->count = 0;
+               if (!s->loopback) {
                        rose_remove_neigh(s);
                        rose_neigh_put(s);
                }
@@ -680,6 +679,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
                        for (i = 0; i < node->count; i++) {
                                if (node->neighbour[i]->restarted) {
                                        res = node->neighbour[i];
+                                       rose_neigh_hold(node->neighbour[i]);
                                        goto out;
                                }
                        }
@@ -691,6 +691,7 @@ struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause,
                                for (i = 0; i < node->count; i++) {
                                        if (!rose_ftimer_running(node->neighbour[i])) {
                                                res = node->neighbour[i];
+                                               rose_neigh_hold(node->neighbour[i]);
                                                goto out;
                                        }
                                        failed = 1;
@@ -780,13 +781,13 @@ static void rose_del_route_by_neigh(struct rose_neigh *rose_neigh)
                }
 
                if (rose_route->neigh1 == rose_neigh) {
-                       rose_route->neigh1->use--;
+                       rose_neigh_put(rose_route->neigh1);
                        rose_route->neigh1 = NULL;
                        rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0);
                }
 
                if (rose_route->neigh2 == rose_neigh) {
-                       rose_route->neigh2->use--;
+                       rose_neigh_put(rose_route->neigh2);
                        rose_route->neigh2 = NULL;
                        rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0);
                }
@@ -915,7 +916,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
                        rose_clear_queues(sk);
                        rose->cause      = ROSE_NETWORK_CONGESTION;
                        rose->diagnostic = 0;
-                       rose->neighbour->use--;
+                       rose_neigh_put(rose->neighbour);
                        rose->neighbour  = NULL;
                        rose->lci        = 0;
                        rose->state      = ROSE_STATE_0;
@@ -1040,12 +1041,12 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
 
        if ((new_lci = rose_new_lci(new_neigh)) == 0) {
                rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71);
-               goto out;
+               goto put_neigh;
        }
 
        if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
                rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120);
-               goto out;
+               goto put_neigh;
        }
 
        rose_route->lci1      = lci;
@@ -1058,8 +1059,8 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
        rose_route->lci2      = new_lci;
        rose_route->neigh2    = new_neigh;
 
-       rose_route->neigh1->use++;
-       rose_route->neigh2->use++;
+       rose_neigh_hold(rose_route->neigh1);
+       rose_neigh_hold(rose_route->neigh2);
 
        rose_route->next = rose_route_list;
        rose_route_list  = rose_route;
@@ -1071,6 +1072,8 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
        rose_transmit_link(skb, rose_route->neigh2);
        res = 1;
 
+put_neigh:
+       rose_neigh_put(new_neigh);
 out:
        spin_unlock_bh(&rose_route_list_lock);
        spin_unlock_bh(&rose_neigh_list_lock);
@@ -1186,7 +1189,7 @@ static int rose_neigh_show(struct seq_file *seq, void *v)
                           (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(buf, &rose_neigh->callsign),
                           rose_neigh->dev ? rose_neigh->dev->name : "???",
                           rose_neigh->count,
-                          rose_neigh->use,
+                          refcount_read(&rose_neigh->use) - 1,
                           (rose_neigh->dce_mode) ? "DCE" : "DTE",
                           (rose_neigh->restarted) ? "yes" : "no",
                           ax25_display_timer(&rose_neigh->t0timer) / HZ,
index 020369c49587f123d5db22159c8270b20f8c6875..bb60a1654d61252810af5a5b9f4e1d671839ba5b 100644 (file)
@@ -180,7 +180,7 @@ static void rose_timer_expiry(struct timer_list *t)
                break;
 
        case ROSE_STATE_2:      /* T3 */
-               rose->neighbour->use--;
+               rose_neigh_put(rose->neighbour);
                rose_disconnect(sk, ETIMEDOUT, -1, -1);
                break;