]> www.infradead.org Git - users/griffoul/linux.git/commitdiff
net: rose: include node references in rose_neigh refcount
authorTakamitsu Iwai <takamitz@amazon.co.jp>
Sat, 23 Aug 2025 08:58:57 +0000 (17:58 +0900)
committerJakub Kicinski <kuba@kernel.org>
Wed, 27 Aug 2025 14:43:09 +0000 (07:43 -0700)
Current implementation maintains two separate reference counting
mechanisms: the 'count' field in struct rose_neigh tracks references from
rose_node structures, while the 'use' field (now refcount_t) tracks
references from rose_sock.

This patch merges these two reference counting systems using 'use' field
for proper reference management. Specifically, this patch adds incrementing
and decrementing of rose_neigh->use when rose_neigh->count is incremented
or decremented.

This patch also modifies rose_rt_free(), rose_rt_device_down() and
rose_clear_route() to properly release references to rose_neigh objects
before freeing a rose_node through rose_remove_node().

These changes ensure rose_neigh structures are properly freed only when
all references, including those from rose_node structures, are released.
As a result, this resolves a slab-use-after-free issue reported by Syzbot.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+942297eecf7d2d61d1f1@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=942297eecf7d2d61d1f1
Signed-off-by: Takamitsu Iwai <takamitz@amazon.co.jp>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250823085857.47674-4-takamitz@amazon.co.jp
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/rose/rose_route.c

index 8efb9033c057c39b76818ebc521b4b61fd102053..1adee1fbc2ed1d04345c6340b717eae1f6a0305c 100644 (file)
@@ -178,6 +178,7 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
                        }
                }
                rose_neigh->count++;
+               rose_neigh_hold(rose_neigh);
 
                goto out;
        }
@@ -187,6 +188,7 @@ static int __must_check rose_add_node(struct rose_route_struct *rose_route,
                rose_node->neighbour[rose_node->count] = rose_neigh;
                rose_node->count++;
                rose_neigh->count++;
+               rose_neigh_hold(rose_neigh);
        }
 
 out:
@@ -322,6 +324,7 @@ static int rose_del_node(struct rose_route_struct *rose_route,
        for (i = 0; i < rose_node->count; i++) {
                if (rose_node->neighbour[i] == rose_neigh) {
                        rose_neigh->count--;
+                       rose_neigh_put(rose_neigh);
 
                        if (rose_neigh->count == 0) {
                                rose_remove_neigh(rose_neigh);
@@ -430,6 +433,7 @@ int rose_add_loopback_node(const rose_address *address)
        rose_node_list  = rose_node;
 
        rose_loopback_neigh->count++;
+       rose_neigh_hold(rose_loopback_neigh);
 
 out:
        spin_unlock_bh(&rose_node_list_lock);
@@ -461,6 +465,7 @@ void rose_del_loopback_node(const rose_address *address)
        rose_remove_node(rose_node);
 
        rose_loopback_neigh->count--;
+       rose_neigh_put(rose_loopback_neigh);
 
 out:
        spin_unlock_bh(&rose_node_list_lock);
@@ -500,6 +505,7 @@ void rose_rt_device_down(struct net_device *dev)
                                memmove(&t->neighbour[i], &t->neighbour[i + 1],
                                        sizeof(t->neighbour[0]) *
                                                (t->count - i));
+                               rose_neigh_put(s);
                        }
 
                        if (t->count <= 0)
@@ -543,6 +549,7 @@ static int rose_clear_routes(void)
 {
        struct rose_neigh *s, *rose_neigh;
        struct rose_node  *t, *rose_node;
+       int i;
 
        spin_lock_bh(&rose_node_list_lock);
        spin_lock_bh(&rose_neigh_list_lock);
@@ -553,8 +560,12 @@ static int rose_clear_routes(void)
        while (rose_node != NULL) {
                t         = rose_node;
                rose_node = rose_node->next;
-               if (!t->loopback)
+
+               if (!t->loopback) {
+                       for (i = 0; i < rose_node->count; i++)
+                               rose_neigh_put(t->neighbour[i]);
                        rose_remove_node(t);
+               }
        }
 
        while (rose_neigh != NULL) {
@@ -1189,7 +1200,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,
-                          refcount_read(&rose_neigh->use) - 1,
+                          refcount_read(&rose_neigh->use) - rose_neigh->count - 1,
                           (rose_neigh->dce_mode) ? "DCE" : "DTE",
                           (rose_neigh->restarted) ? "yes" : "no",
                           ax25_display_timer(&rose_neigh->t0timer) / HZ,
@@ -1294,6 +1305,7 @@ void __exit rose_rt_free(void)
        struct rose_neigh *s, *rose_neigh = rose_neigh_list;
        struct rose_node  *t, *rose_node  = rose_node_list;
        struct rose_route *u, *rose_route = rose_route_list;
+       int i;
 
        while (rose_neigh != NULL) {
                s          = rose_neigh;
@@ -1307,6 +1319,8 @@ void __exit rose_rt_free(void)
                t         = rose_node;
                rose_node = rose_node->next;
 
+               for (i = 0; i < t->count; i++)
+                       rose_neigh_put(t->neighbour[i]);
                rose_remove_node(t);
        }