mirror of
https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable.git
synced 2025-09-13 11:07:46 +10:00
net: rose: convert 'use' field to refcount_t
[ Upstream commitd860d1faa6
] 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:1da177e4c3
("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>
This commit is contained in:
parent
8e88504a28
commit
0085b250fc
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
@ -120,7 +120,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:
|
||||
@ -233,7 +233,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:
|
||||
@ -253,7 +253,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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user