* [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh
@ 2025-08-23 8:58 Takamitsu Iwai
2025-08-23 8:58 ` [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh() Takamitsu Iwai
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Takamitsu Iwai @ 2025-08-23 8:58 UTC (permalink / raw)
To: linux-hams, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Takamitsu Iwai, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich,
Kuniyuki Iwashima
The current implementation of rose_neigh uses 'use' and 'count' field of
type unsigned short as a reference count. This approach lacks atomicity,
leading to potential race conditions. As a result, syzbot has reported
slab-use-after-free errors due to unintended removals.
This series introduces refcount_t for reference counting to ensure
atomicity and prevent race conditions. The patches are structured as
follows:
1. Refactor rose_remove_neigh() to separate removal and freeing operations
2. Convert 'use' field to refcount_t for appropriate reference counting
3. Include references from rose_node to 'use' field
These changes should resolve the reported slab-use-after-free issues and
improve the overall stability of the ROSE network layer.
Changes:
v2:
- Added rose_neigh_put() in error paths of rose_connect() to prevent
reference count leaks that could occur after moving the reference
count increment to rose_get_neigh().
- Added rose_neigh_put() at the end of rose_route_frame() to properly
release the temporary reference held by new_neigh variable when
the function completes.
- Added rose_neigh_hold() in the second for loop of rose_get_neigh()
to maintain consistent reference counting behavior between both loops.
v1:
https://lore.kernel.org/all/20250820174707.83372-1-takamitz@amazon.co.jp/
Takamitsu Iwai (3):
net: rose: split remove and free operations in rose_remove_neigh()
net: rose: convert 'use' field to refcount_t
net: rose: include node references in rose_neigh refcount
include/net/rose.h | 18 ++++++++++++-
net/rose/af_rose.c | 13 ++++-----
net/rose/rose_in.c | 12 ++++-----
net/rose/rose_route.c | 62 ++++++++++++++++++++++++++-----------------
net/rose/rose_timer.c | 2 +-
5 files changed, 69 insertions(+), 38 deletions(-)
--
2.39.5 (Apple Git-154)
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh()
2025-08-23 8:58 [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh Takamitsu Iwai
@ 2025-08-23 8:58 ` Takamitsu Iwai
2025-08-27 6:21 ` Kuniyuki Iwashima
2025-08-23 8:58 ` [PATCH v2 net 2/3] net: rose: convert 'use' field to refcount_t Takamitsu Iwai
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Takamitsu Iwai @ 2025-08-23 8:58 UTC (permalink / raw)
To: linux-hams, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Takamitsu Iwai, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich,
Kuniyuki Iwashima
The current rose_remove_neigh() performs two distinct operations:
1. Removes rose_neigh from rose_neigh_list
2. Frees the rose_neigh structure
Split these operations into separate functions to improve maintainability
and prepare for upcoming refcount_t conversion. The timer cleanup remains
in rose_remove_neigh() because free operations can be called from timer
itself.
This patch introduce rose_neigh_put() to handle the freeing of rose_neigh
structures and modify rose_remove_neigh() to handle removal only.
Signed-off-by: Takamitsu Iwai <takamitz@amazon.co.jp>
---
include/net/rose.h | 8 ++++++++
net/rose/rose_route.c | 15 ++++++---------
2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/include/net/rose.h b/include/net/rose.h
index 23267b4efcfa..174b4f605d84 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -151,6 +151,14 @@ struct rose_sock {
#define rose_sk(sk) ((struct rose_sock *)(sk))
+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);
+}
+
/* af_rose.c */
extern ax25_address rose_callsign;
extern int sysctl_rose_restart_request_timeout;
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index b72bf8a08d48..0c44c416f485 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -234,20 +234,12 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
if ((s = rose_neigh_list) == rose_neigh) {
rose_neigh_list = rose_neigh->next;
- if (rose_neigh->ax25)
- ax25_cb_put(rose_neigh->ax25);
- kfree(rose_neigh->digipeat);
- kfree(rose_neigh);
return;
}
while (s != NULL && s->next != NULL) {
if (s->next == rose_neigh) {
s->next = rose_neigh->next;
- if (rose_neigh->ax25)
- ax25_cb_put(rose_neigh->ax25);
- kfree(rose_neigh->digipeat);
- kfree(rose_neigh);
return;
}
@@ -331,8 +323,10 @@ 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_neigh->use == 0) {
rose_remove_neigh(rose_neigh);
+ rose_neigh_put(rose_neigh);
+ }
rose_node->count--;
@@ -513,6 +507,7 @@ void rose_rt_device_down(struct net_device *dev)
}
rose_remove_neigh(s);
+ rose_neigh_put(s);
}
spin_unlock_bh(&rose_neigh_list_lock);
spin_unlock_bh(&rose_node_list_lock);
@@ -569,6 +564,7 @@ static int rose_clear_routes(void)
if (s->use == 0 && !s->loopback) {
s->count = 0;
rose_remove_neigh(s);
+ rose_neigh_put(s);
}
}
@@ -1301,6 +1297,7 @@ void __exit rose_rt_free(void)
rose_neigh = rose_neigh->next;
rose_remove_neigh(s);
+ rose_neigh_put(s);
}
while (rose_node != NULL) {
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 net 2/3] net: rose: convert 'use' field to refcount_t
2025-08-23 8:58 [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh Takamitsu Iwai
2025-08-23 8:58 ` [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh() Takamitsu Iwai
@ 2025-08-23 8:58 ` Takamitsu Iwai
2025-08-27 6:47 ` Kuniyuki Iwashima
2025-08-23 8:58 ` [PATCH v2 net 3/3] net: rose: include node references in rose_neigh refcount Takamitsu Iwai
2025-08-27 14:50 ` [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh patchwork-bot+netdevbpf
3 siblings, 1 reply; 10+ messages in thread
From: Takamitsu Iwai @ 2025-08-23 8:58 UTC (permalink / raw)
To: linux-hams, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Takamitsu Iwai, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich,
Kuniyuki Iwashima
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>
---
include/net/rose.h | 18 +++++++++++++-----
net/rose/af_rose.c | 13 +++++++------
net/rose/rose_in.c | 12 ++++++------
net/rose/rose_route.c | 33 ++++++++++++++++++---------------
net/rose/rose_timer.c | 2 +-
5 files changed, 45 insertions(+), 33 deletions(-)
diff --git a/include/net/rose.h b/include/net/rose.h
index 174b4f605d84..2b5491bbf39a 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -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 */
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 4e72b636a46a..543f9e8ebb69 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -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 @@ static void rose_kill_by_device(struct net_device *dev)
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;
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 3e99181e759f..0276b393f0e5 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -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;
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 0c44c416f485..8efb9033c057 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -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,
diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c
index 020369c49587..bb60a1654d61 100644
--- a/net/rose/rose_timer.c
+++ b/net/rose/rose_timer.c
@@ -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;
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH v2 net 3/3] net: rose: include node references in rose_neigh refcount
2025-08-23 8:58 [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh Takamitsu Iwai
2025-08-23 8:58 ` [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh() Takamitsu Iwai
2025-08-23 8:58 ` [PATCH v2 net 2/3] net: rose: convert 'use' field to refcount_t Takamitsu Iwai
@ 2025-08-23 8:58 ` Takamitsu Iwai
2025-08-27 6:48 ` Kuniyuki Iwashima
2025-08-27 14:50 ` [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh patchwork-bot+netdevbpf
3 siblings, 1 reply; 10+ messages in thread
From: Takamitsu Iwai @ 2025-08-23 8:58 UTC (permalink / raw)
To: linux-hams, netdev
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Takamitsu Iwai, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich,
Kuniyuki Iwashima, syzbot+942297eecf7d2d61d1f1
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>
---
net/rose/rose_route.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 8efb9033c057..1adee1fbc2ed 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -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);
}
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh()
2025-08-23 8:58 ` [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh() Takamitsu Iwai
@ 2025-08-27 6:21 ` Kuniyuki Iwashima
0 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-08-27 6:21 UTC (permalink / raw)
To: Takamitsu Iwai
Cc: linux-hams, netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich
On Sat, Aug 23, 2025 at 2:00 AM Takamitsu Iwai <takamitz@amazon.co.jp> wrote:
>
> The current rose_remove_neigh() performs two distinct operations:
> 1. Removes rose_neigh from rose_neigh_list
> 2. Frees the rose_neigh structure
>
> Split these operations into separate functions to improve maintainability
> and prepare for upcoming refcount_t conversion. The timer cleanup remains
> in rose_remove_neigh() because free operations can be called from timer
> itself.
>
> This patch introduce rose_neigh_put() to handle the freeing of rose_neigh
> structures and modify rose_remove_neigh() to handle removal only.
>
> Signed-off-by: Takamitsu Iwai <takamitz@amazon.co.jp>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 net 2/3] net: rose: convert 'use' field to refcount_t
2025-08-23 8:58 ` [PATCH v2 net 2/3] net: rose: convert 'use' field to refcount_t Takamitsu Iwai
@ 2025-08-27 6:47 ` Kuniyuki Iwashima
0 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-08-27 6:47 UTC (permalink / raw)
To: Takamitsu Iwai
Cc: linux-hams, netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich
On Sat, Aug 23, 2025 at 2:01 AM Takamitsu Iwai <takamitz@amazon.co.jp> wrote:
>
> 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>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 net 3/3] net: rose: include node references in rose_neigh refcount
2025-08-23 8:58 ` [PATCH v2 net 3/3] net: rose: include node references in rose_neigh refcount Takamitsu Iwai
@ 2025-08-27 6:48 ` Kuniyuki Iwashima
0 siblings, 0 replies; 10+ messages in thread
From: Kuniyuki Iwashima @ 2025-08-27 6:48 UTC (permalink / raw)
To: Takamitsu Iwai
Cc: linux-hams, netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Kohei Enju, Ingo Molnar,
Thomas Gleixner, Jesper Dangaard Brouer, Nikita Zhandarovich,
syzbot+942297eecf7d2d61d1f1
On Sat, Aug 23, 2025 at 2:01 AM Takamitsu Iwai <takamitz@amazon.co.jp> wrote:
>
> 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>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh
2025-08-23 8:58 [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh Takamitsu Iwai
` (2 preceding siblings ...)
2025-08-23 8:58 ` [PATCH v2 net 3/3] net: rose: include node references in rose_neigh refcount Takamitsu Iwai
@ 2025-08-27 14:50 ` patchwork-bot+netdevbpf
2025-09-02 17:18 ` F6BVP
3 siblings, 1 reply; 10+ messages in thread
From: patchwork-bot+netdevbpf @ 2025-08-27 14:50 UTC (permalink / raw)
To: Takamitsu Iwai
Cc: linux-hams, netdev, davem, edumazet, kuba, pabeni, horms, enjuk,
mingo, tglx, hawk, n.zhandarovich, kuniyu
Hello:
This series was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Sat, 23 Aug 2025 17:58:54 +0900 you wrote:
> The current implementation of rose_neigh uses 'use' and 'count' field of
> type unsigned short as a reference count. This approach lacks atomicity,
> leading to potential race conditions. As a result, syzbot has reported
> slab-use-after-free errors due to unintended removals.
>
> This series introduces refcount_t for reference counting to ensure
> atomicity and prevent race conditions. The patches are structured as
> follows:
>
> [...]
Here is the summary with links:
- [v2,net,1/3] net: rose: split remove and free operations in rose_remove_neigh()
https://git.kernel.org/netdev/net/c/dcb34659028f
- [v2,net,2/3] net: rose: convert 'use' field to refcount_t
https://git.kernel.org/netdev/net/c/d860d1faa6b2
- [v2,net,3/3] net: rose: include node references in rose_neigh refcount
https://git.kernel.org/netdev/net/c/da9c9c877597
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh
2025-08-27 14:50 ` [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh patchwork-bot+netdevbpf
@ 2025-09-02 17:18 ` F6BVP
2025-09-02 17:23 ` Eric Dumazet
0 siblings, 1 reply; 10+ messages in thread
From: F6BVP @ 2025-09-02 17:18 UTC (permalink / raw)
To: Takamitsu Iwai
Cc: linux-hams, netdev, davem, edumazet, kuba, pabeni, horms, enjuk,
mingo, tglx, hawk, n.zhandarovich, kuniyu
[-- Attachment #1: Type: text/plain, Size: 1686 bytes --]
Hi,
I am facing an issue while trying to apply refcount rose patchs to
latest stable release 6.16.4
In rose_in.c the call to sk_filter_trim_cap function is using an extra
argument that is not declared in 6.16.4 ~/include/linux/filter.h but
appears in 6.17.0-rc.
As a result I had to apply the following patch in order to be able to
build kernel 6.16.4 with refcount patches.
Otherwise ROSE module refcount patchs would prevent building rose module
in stable kernel
Is there any other solution ?
Regards,
Bernard Pidoux,
F6BVP
Le 27/08/2025 à 16:50, patchwork-bot+netdevbpf@kernel.org a écrit :
> Hello:
>
> This series was applied to netdev/net.git (main)
> by Jakub Kicinski <kuba@kernel.org>:
>
> On Sat, 23 Aug 2025 17:58:54 +0900 you wrote:
>> The current implementation of rose_neigh uses 'use' and 'count' field of
>> type unsigned short as a reference count. This approach lacks atomicity,
>> leading to potential race conditions. As a result, syzbot has reported
>> slab-use-after-free errors due to unintended removals.
>>
>> This series introduces refcount_t for reference counting to ensure
>> atomicity and prevent race conditions. The patches are structured as
>> follows:
>>
>> [...]
>
> Here is the summary with links:
> - [v2,net,1/3] net: rose: split remove and free operations in rose_remove_neigh()
> https://git.kernel.org/netdev/net/c/dcb34659028f
> - [v2,net,2/3] net: rose: convert 'use' field to refcount_t
> https://git.kernel.org/netdev/net/c/d860d1faa6b2
> - [v2,net,3/3] net: rose: include node references in rose_neigh refcount
> https://git.kernel.org/netdev/net/c/da9c9c877597
>
> You are awesome, thank you!
[-- Attachment #2: rose_in_reason_dr_ignored.patch --]
[-- Type: text/plain, Size: 925 bytes --]
diff --git a/b/net/rose/rose_in.c b/a/net/rose/rose_in.c
index 0276b39..517231b 100644
--- a/b/net/rose/rose_in.c
+++ b/a/net/rose/rose_in.c
@@ -101,7 +101,7 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
*/
static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
{
- enum skb_drop_reason dr; /* ignored */
+// enum skb_drop_reason dr; /* ignored */
struct rose_sock *rose = rose_sk(sk);
int queued = 0;
@@ -163,7 +163,7 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
rose_frames_acked(sk, nr);
if (ns == rose->vr) {
rose_start_idletimer(sk);
- if (!sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN, &dr) &&
+ if (!sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) &&
__sock_queue_rcv_skb(sk, skb) == 0) {
rose->vr = (rose->vr + 1) % ROSE_MODULUS;
queued = 1;
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh
2025-09-02 17:18 ` F6BVP
@ 2025-09-02 17:23 ` Eric Dumazet
0 siblings, 0 replies; 10+ messages in thread
From: Eric Dumazet @ 2025-09-02 17:23 UTC (permalink / raw)
To: F6BVP
Cc: Takamitsu Iwai, linux-hams, netdev, davem, kuba, pabeni, horms,
enjuk, mingo, tglx, hawk, n.zhandarovich, kuniyu
On Tue, Sep 2, 2025 at 10:19 AM F6BVP <f6bvp@free.fr> wrote:
>
> Hi,
>
> I am facing an issue while trying to apply refcount rose patchs to
> latest stable release 6.16.4
>
> In rose_in.c the call to sk_filter_trim_cap function is using an extra
> argument that is not declared in 6.16.4 ~/include/linux/filter.h but
> appears in 6.17.0-rc.
>
> As a result I had to apply the following patch in order to be able to
> build kernel 6.16.4 with refcount patches.
>
> Otherwise ROSE module refcount patchs would prevent building rose module
> in stable kernel
>
> Is there any other solution ?
>
Note that these patches have ongoing syzbot reports.
If I was you, I would wait a bit.
ODEBUG: free active (active state 0) object: ffff88804fb25890 object
type: timer_list hint: rose_t0timer_expiry+0x0/0x150
include/linux/skbuff.h:2880
WARNING: CPU: 1 PID: 16472 at lib/debugobjects.c:612
debug_print_object+0x1a2/0x2b0 lib/debugobjects.c:612
Modules linked in:
CPU: 1 UID: 0 PID: 16472 Comm: syz.1.2858 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine,
BIOS Google 07/12/2025
RIP: 0010:debug_print_object+0x1a2/0x2b0 lib/debugobjects.c:612
Code: fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 75 54 41 56 48 8b 14
dd e0 40 16 8c 4c 89 e6 48 c7 c7 60 35 16 8c e8 0f 46 91 fc 90 <0f> 0b
90 90 58 83 05 86 d0 c2 0b 01 48 83 c4 18 5b 5d 41 5c 41 5d
RSP: 0018:ffffc90000a08a28 EFLAGS: 00010286
RAX: 0000000000000000 RBX: 0000000000000003 RCX: ffffffff817a3358
RDX: ffff888031ae9e00 RSI: ffffffff817a3365 RDI: 0000000000000001
RBP: 0000000000000001 R08: 0000000000000001 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffffffff8c163c00
R13: ffffffff8bafed40 R14: ffffffff8a7fa2b0 R15: ffffc90000a08b28
FS: 00007f10b4f3c6c0(0000) GS:ffff8881247b9000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000110c3dff8d CR3: 00000000325a7000 CR4: 0000000000350ef0
Call Trace:
<IRQ>
__debug_check_no_obj_freed lib/debugobjects.c:1099 [inline]
debug_check_no_obj_freed+0x4b7/0x600 lib/debugobjects.c:1129
slab_free_hook mm/slub.c:2348 [inline]
slab_free mm/slub.c:4680 [inline]
kfree+0x28f/0x4d0 mm/slub.c:4879
rose_neigh_put include/net/rose.h:166 [inline]
rose_timer_expiry+0x53f/0x630 net/rose/rose_timer.c:183
call_timer_fn+0x19a/0x620 kernel/time/timer.c:1747
expire_timers kernel/time/timer.c:1798 [inline]
__run_timers+0x6ef/0x960 kernel/time/timer.c:2372
__run_timer_base kernel/time/timer.c:2384 [inline]
__run_timer_base kernel/time/timer.c:2376 [inline]
run_timer_base+0x114/0x190 kernel/time/timer.c:2393
run_timer_softirq+0x1a/0x40 kernel/time/timer.c:2403
handle_softirqs+0x219/0x8e0 kernel/softirq.c:579
__do_softirq kernel/softirq.c:613 [inline]
invoke_softirq kernel/softirq.c:453 [inline]
__irq_exit_rcu+0x109/0x170 kernel/softirq.c:680
irq_exit_rcu+0x9/0x30 kernel/softirq.c:696
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1050 [inline]
sysvec_apic_timer_interrupt+0xa4/0xc0 arch/x86/kernel/apic/apic.c:1050
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:702
RIP: 0010:lock_is_held_type+0x107/0x150 kernel/locking/lockdep.c:5945
Code: 00 00 b8 ff ff ff ff 65 0f c1 05 dc a0 44 08 83 f8 01 75 2d 9c
58 f6 c4 02 75 43 48 f7 04 24 00 02 00 00 74 01 fb 48 83 c4 08 <44> 89
e8 5b 5d 41 5c 41 5d 41 5e 41 5f e9 f2 2f 7e f5 45 31 ed eb
RSP: 0018:ffffc9000eb1f978 EFLAGS: 00000286
RAX: 0000000000000046 RBX: 1ffff92001d63f38 RCX: 0000000000000001
RDX: 0000000000000000 RSI: ffffffff8de299c8 RDI: ffffffff8c163000
RBP: ffffffff8e5c11c0 R08: 0000000000000005 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: ffff888031ae9e00
R13: 0000000000000000 R14: 00000000ffffffff R15: 0000000000000000
lock_is_held include/linux/lockdep.h:249 [inline]
> Regards,
>
> Bernard Pidoux,
> F6BVP
>
>
> Le 27/08/2025 à 16:50, patchwork-bot+netdevbpf@kernel.org a écrit :
> > Hello:
> >
> > This series was applied to netdev/net.git (main)
> > by Jakub Kicinski <kuba@kernel.org>:
> >
> > On Sat, 23 Aug 2025 17:58:54 +0900 you wrote:
> >> The current implementation of rose_neigh uses 'use' and 'count' field of
> >> type unsigned short as a reference count. This approach lacks atomicity,
> >> leading to potential race conditions. As a result, syzbot has reported
> >> slab-use-after-free errors due to unintended removals.
> >>
> >> This series introduces refcount_t for reference counting to ensure
> >> atomicity and prevent race conditions. The patches are structured as
> >> follows:
> >>
> >> [...]
> >
> > Here is the summary with links:
> > - [v2,net,1/3] net: rose: split remove and free operations in rose_remove_neigh()
> > https://git.kernel.org/netdev/net/c/dcb34659028f
> > - [v2,net,2/3] net: rose: convert 'use' field to refcount_t
> > https://git.kernel.org/netdev/net/c/d860d1faa6b2
> > - [v2,net,3/3] net: rose: include node references in rose_neigh refcount
> > https://git.kernel.org/netdev/net/c/da9c9c877597
> >
> > You are awesome, thank you!
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-09-02 17:23 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-23 8:58 [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh Takamitsu Iwai
2025-08-23 8:58 ` [PATCH v2 net 1/3] net: rose: split remove and free operations in rose_remove_neigh() Takamitsu Iwai
2025-08-27 6:21 ` Kuniyuki Iwashima
2025-08-23 8:58 ` [PATCH v2 net 2/3] net: rose: convert 'use' field to refcount_t Takamitsu Iwai
2025-08-27 6:47 ` Kuniyuki Iwashima
2025-08-23 8:58 ` [PATCH v2 net 3/3] net: rose: include node references in rose_neigh refcount Takamitsu Iwai
2025-08-27 6:48 ` Kuniyuki Iwashima
2025-08-27 14:50 ` [PATCH v2 net 0/3] Introduce refcount_t for reference counting of rose_neigh patchwork-bot+netdevbpf
2025-09-02 17:18 ` F6BVP
2025-09-02 17:23 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).