Commit 09d25c03 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller

o X25: protect x25 sockets and list with refcnt and rwlock

parent b498a583
...@@ -65,7 +65,8 @@ int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22; ...@@ -65,7 +65,8 @@ int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22;
int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23; int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23;
int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2;
static struct sock *volatile x25_list /* = NULL initially */; static struct sock *x25_list;
static rwlock_t x25_list_lock = RW_LOCK_UNLOCKED;
static struct proto_ops x25_proto_ops; static struct proto_ops x25_proto_ops;
...@@ -152,22 +153,22 @@ int x25_addr_aton(unsigned char *p, struct x25_address *called_addr, ...@@ -152,22 +153,22 @@ int x25_addr_aton(unsigned char *p, struct x25_address *called_addr,
static void x25_remove_socket(struct sock *sk) static void x25_remove_socket(struct sock *sk)
{ {
struct sock *s; struct sock *s;
unsigned long flags;
save_flags(flags); write_lock_bh(&x25_list_lock);
cli();
if ((s = x25_list) == sk) if ((s = x25_list) == sk)
x25_list = s->next; x25_list = s->next;
else while (s && s->next) { else while (s && s->next) {
if (s->next == sk) { if (s->next == sk) {
s->next = sk->next; s->next = sk->next;
sock_put(sk);
break; break;
} }
s = s->next; s = s->next;
} }
restore_flags(flags);
write_unlock_bh(&x25_list_lock);
} }
/* /*
...@@ -177,15 +178,20 @@ static void x25_kill_by_device(struct net_device *dev) ...@@ -177,15 +178,20 @@ static void x25_kill_by_device(struct net_device *dev)
{ {
struct sock *s; struct sock *s;
write_lock_bh(&x25_list_lock);
for (s = x25_list; s; s = s->next) for (s = x25_list; s; s = s->next)
if (x25_sk(s)->neighbour && x25_sk(s)->neighbour->dev == dev) if (x25_sk(s)->neighbour && x25_sk(s)->neighbour->dev == dev)
x25_disconnect(s, ENETUNREACH, 0, 0); x25_disconnect(s, ENETUNREACH, 0, 0);
write_unlock_bh(&x25_list_lock);
} }
/* /*
* Handle device status changes. * Handle device status changes.
*/ */
static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) static int x25_device_event(struct notifier_block *this, unsigned long event,
void *ptr)
{ {
struct net_device *dev = ptr; struct net_device *dev = ptr;
struct x25_neigh *nb; struct x25_neigh *nb;
...@@ -222,15 +228,11 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo ...@@ -222,15 +228,11 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo
*/ */
static void x25_insert_socket(struct sock *sk) static void x25_insert_socket(struct sock *sk)
{ {
unsigned long flags; write_lock_bh(&x25_list_lock);
save_flags(flags);
cli();
sk->next = x25_list; sk->next = x25_list;
x25_list = sk; x25_list = sk;
sock_hold(sk);
restore_flags(flags); write_unlock_bh(&x25_list_lock);
} }
/* /*
...@@ -239,11 +241,9 @@ static void x25_insert_socket(struct sock *sk) ...@@ -239,11 +241,9 @@ static void x25_insert_socket(struct sock *sk)
*/ */
static struct sock *x25_find_listener(struct x25_address *addr) static struct sock *x25_find_listener(struct x25_address *addr)
{ {
unsigned long flags;
struct sock *s; struct sock *s;
save_flags(flags); read_lock_bh(&x25_list_lock);
cli();
for (s = x25_list; s; s = s->next) for (s = x25_list; s; s = s->next)
if ((!strcmp(addr->x25_addr, x25_sk(s)->source_addr.x25_addr) || if ((!strcmp(addr->x25_addr, x25_sk(s)->source_addr.x25_addr) ||
...@@ -251,26 +251,34 @@ static struct sock *x25_find_listener(struct x25_address *addr) ...@@ -251,26 +251,34 @@ static struct sock *x25_find_listener(struct x25_address *addr)
s->state == TCP_LISTEN) s->state == TCP_LISTEN)
break; break;
restore_flags(flags); if (s)
sock_hold(s);
read_unlock_bh(&x25_list_lock);
return s; return s;
} }
/* /*
* Find a connected X.25 socket given my LCI and neighbour. * Find a connected X.25 socket given my LCI and neighbour.
*/ */
struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb) struct sock *__x25_find_socket(unsigned int lci, struct x25_neigh *nb)
{ {
struct sock *s; struct sock *s;
unsigned long flags;
save_flags(flags);
cli();
for (s = x25_list; s; s = s->next) for (s = x25_list; s; s = s->next)
if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == nb) if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == nb)
break; break;
if (s)
sock_hold(s);
return s;
}
restore_flags(flags); struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb)
{
struct sock *s;
read_lock_bh(&x25_list_lock);
s = __x25_find_socket(lci, nb);
read_unlock_bh(&x25_list_lock);
return s; return s;
} }
...@@ -280,13 +288,19 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb) ...@@ -280,13 +288,19 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb)
unsigned int x25_new_lci(struct x25_neigh *nb) unsigned int x25_new_lci(struct x25_neigh *nb)
{ {
unsigned int lci = 1; unsigned int lci = 1;
struct sock *sk;
while (x25_find_socket(lci, nb)) read_lock_bh(&x25_list_lock);
while ((sk = __x25_find_socket(lci, nb)) != NULL) {
sock_put(sk);
if (++lci == 4096) { if (++lci == 4096) {
lci = 0; lci = 0;
break; break;
} }
}
read_unlock_bh(&x25_list_lock);
return lci; return lci;
} }
...@@ -312,11 +326,9 @@ static void x25_destroy_timer(unsigned long data) ...@@ -312,11 +326,9 @@ static void x25_destroy_timer(unsigned long data)
void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned long flags;
save_flags(flags);
cli();
sock_hold(sk);
lock_sock(sk);
x25_stop_heartbeat(sk); x25_stop_heartbeat(sk);
x25_stop_timer(sk); x25_stop_timer(sk);
...@@ -344,8 +356,8 @@ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer ...@@ -344,8 +356,8 @@ void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer
sk_free(sk); sk_free(sk);
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
} }
release_sock(sk);
restore_flags(flags); sock_put(sk);
} }
/* /*
...@@ -799,7 +811,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int ...@@ -799,7 +811,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int
* Try to reach a compromise on the requested facilities. * Try to reach a compromise on the requested facilities.
*/ */
if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1) if ((len = x25_negotiate_facilities(skb, sk, &facilities)) == -1)
goto out_clear_request; goto out_sock_put;
/* /*
* current neighbour/link might impose additional limits * current neighbour/link might impose additional limits
...@@ -813,7 +825,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int ...@@ -813,7 +825,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int
*/ */
make = x25_make_new(sk); make = x25_make_new(sk);
if (!make) if (!make)
goto out_clear_request; goto out_sock_put;
/* /*
* Remove the facilities, leaving any Call User Data. * Remove the facilities, leaving any Call User Data.
...@@ -855,8 +867,11 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int ...@@ -855,8 +867,11 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int
if (!sk->dead) if (!sk->dead)
sk->data_ready(sk, skb->len); sk->data_ready(sk, skb->len);
rc = 1; rc = 1;
sock_put(sk);
out: out:
return rc; return rc;
out_sock_put:
sock_put(sk);
out_clear_request: out_clear_request:
rc = 0; rc = 0;
x25_transmit_clear_request(nb, lci, 0x01); x25_transmit_clear_request(nb, lci, 0x01);
...@@ -1264,14 +1279,12 @@ static int x25_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1264,14 +1279,12 @@ static int x25_get_info(char *buffer, char **start, off_t offset, int length)
struct sock *s; struct sock *s;
struct net_device *dev; struct net_device *dev;
const char *devname; const char *devname;
int len;
off_t pos = 0; off_t pos = 0;
off_t begin = 0; off_t begin = 0;
int len = sprintf(buffer, "dest_addr src_addr dev lci st vs vr "
"va t t2 t21 t22 t23 Snd-Q Rcv-Q inode\n");
cli(); read_lock_bh(&x25_list_lock);
len = sprintf(buffer, "dest_addr src_addr dev lci st vs vr va "
"t t2 t21 t22 t23 Snd-Q Rcv-Q inode\n");
for (s = x25_list; s; s = s->next) { for (s = x25_list; s; s = s->next) {
struct x25_opt *x25 = x25_sk(s); struct x25_opt *x25 = x25_sk(s);
...@@ -1314,7 +1327,7 @@ static int x25_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -1314,7 +1327,7 @@ static int x25_get_info(char *buffer, char **start, off_t offset, int length)
break; break;
} }
sti(); read_unlock_bh(&x25_list_lock);
*start = buffer + (offset - begin); *start = buffer + (offset - begin);
len -= (offset - begin); len -= (offset - begin);
...@@ -1368,9 +1381,13 @@ void x25_kill_by_neigh(struct x25_neigh *nb) ...@@ -1368,9 +1381,13 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
{ {
struct sock *s; struct sock *s;
write_lock_bh(&x25_list_lock);
for (s = x25_list; s; s = s->next) for (s = x25_list; s; s = s->next)
if (x25_sk(s)->neighbour == nb) if (x25_sk(s)->neighbour == nb)
x25_disconnect(s, ENETUNREACH, 0, 0); x25_disconnect(s, ENETUNREACH, 0, 0);
write_unlock_bh(&x25_list_lock);
} }
static int __init x25_init(void) static int __init x25_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment