From 94ca866cc3f31be99fe6317e855a2f3054a04172 Mon Sep 17 00:00:00 2001 From: Jeroen Vreeken Date: Thu, 14 Aug 2003 07:10:20 -0700 Subject: [PATCH] [NETROM]: Lock sockets while doing protocol processing. --- net/netrom/af_netrom.c | 208 ++++++++++++++++++++++++++++++----------- net/netrom/nr_in.c | 6 ++ net/netrom/nr_timer.c | 6 ++ 3 files changed, 168 insertions(+), 52 deletions(-) diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 38c0e1a02643d..d08a1d14ca8bd 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -147,8 +147,10 @@ static struct sock *nr_find_listener(ax25_address *addr) spin_lock_bh(&nr_list_lock); sk_for_each(s, node, &nr_list) if (!ax25cmp(&nr_sk(s)->source_addr, addr) && - s->sk_state == TCP_LISTEN) + s->sk_state == TCP_LISTEN) { + bh_lock_sock(s); goto found; + } s = NULL; found: spin_unlock_bh(&nr_list_lock); @@ -167,8 +169,10 @@ static struct sock *nr_find_socket(unsigned char index, unsigned char id) sk_for_each(s, node, &nr_list) { nr_cb *nr = nr_sk(s); - if (nr->my_index == index && nr->my_id == id) + if (nr->my_index == index && nr->my_id == id) { + bh_lock_sock(s); goto found; + } } s = NULL; found: @@ -190,8 +194,10 @@ static struct sock *nr_find_peer(unsigned char index, unsigned char id, nr_cb *nr = nr_sk(s); if (nr->your_index == index && nr->your_id == id && - !ax25cmp(&nr->dest_addr, dest)) + !ax25cmp(&nr->dest_addr, dest)) { + bh_lock_sock(s); goto found; + } } s = NULL; found: @@ -206,14 +212,17 @@ static unsigned short nr_find_next_circuit(void) { unsigned short id = circuit; unsigned char i, j; + struct sock *sk; for (;;) { i = id / 256; j = id % 256; - if (i != 0 && j != 0) - if (nr_find_socket(i, j) == NULL) + if (i != 0 && j != 0) { + if ((sk=nr_find_socket(i, j)) == NULL) break; + bh_unlock_sock(sk); + } id++; } @@ -231,7 +240,12 @@ void nr_destroy_socket(struct sock *); */ static void nr_destroy_timer(unsigned long data) { - nr_destroy_socket((struct sock *)data); + struct sock *sk=(struct sock *)data; + bh_lock_sock(sk); + sock_hold(sk); + nr_destroy_socket(sk); + bh_unlock_sock(sk); + sock_put(sk); } /* @@ -277,7 +291,7 @@ void nr_destroy_socket(struct sock *sk) sk->sk_timer.data = (unsigned long)sk; add_timer(&sk->sk_timer); } else - sk_free(sk); + sock_put(sk); } /* @@ -391,12 +405,15 @@ static int nr_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; + lock_sock(sk); if (sk->sk_state != TCP_LISTEN) { memset(&nr_sk(sk)->user_addr, 0, AX25_ADDR_LEN); sk->sk_max_ack_backlog = backlog; sk->sk_state = TCP_LISTEN; + release_sock(sk); return 0; } + release_sock(sk); return -EOPNOTSUPP; } @@ -498,6 +515,7 @@ static int nr_release(struct socket *sock) if (sk == NULL) return 0; + lock_sock(sk); nr = nr_sk(sk); switch (nr->state) { @@ -531,6 +549,7 @@ static int nr_release(struct socket *sock) } sock->sk = NULL; + release_sock(sk); return 0; } @@ -543,21 +562,26 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct net_device *dev; ax25_address *user, *source; - if (!sk->sk_zapped) + lock_sock(sk); + if (!sk->sk_zapped) { + release_sock(sk); return -EINVAL; - - if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct -full_sockaddr_ax25)) + } + if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) { + release_sock(sk); return -EINVAL; - - if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) + } + if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) { + release_sock(sk); return -EINVAL; - - if (addr->fsa_ax25.sax25_family != AF_NETROM) + } + if (addr->fsa_ax25.sax25_family != AF_NETROM) { + release_sock(sk); return -EINVAL; - + } if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) { SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n"); + release_sock(sk); return -EADDRNOTAVAIL; } @@ -565,16 +589,22 @@ full_sockaddr_ax25)) * Only the super user can set an arbitrary user callsign. */ if (addr->fsa_ax25.sax25_ndigis == 1) { - if (!capable(CAP_NET_BIND_SERVICE)) + if (!capable(CAP_NET_BIND_SERVICE)) { + dev_put(dev); + release_sock(sk); return -EACCES; + } nr->user_addr = addr->fsa_digipeater[0]; nr->source_addr = addr->fsa_ax25.sax25_call; } else { source = &addr->fsa_ax25.sax25_call; if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) + if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { + release_sock(sk); + dev_put(dev); return -EPERM; + } user = source; } @@ -586,6 +616,8 @@ full_sockaddr_ax25)) nr_insert_socket(sk); sk->sk_zapped = 0; + dev_put(dev); + release_sock(sk); SOCK_DEBUG(sk, "NET/ROM: socket is bound\n"); return 0; } @@ -599,39 +631,50 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, ax25_address *user, *source = NULL; struct net_device *dev; + lock_sock(sk); if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { sock->state = SS_CONNECTED; + release_sock(sk); return 0; /* Connect completed during a ERESTARTSYS event */ } if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { sock->state = SS_UNCONNECTED; + release_sock(sk); return -ECONNREFUSED; } - if (sk->sk_state == TCP_ESTABLISHED) + if (sk->sk_state == TCP_ESTABLISHED) { + release_sock(sk); return -EISCONN; /* No reconnect on a seqpacket socket */ + } sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; - if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) + if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25)) { + release_sock(sk); return -EINVAL; - - if (addr->sax25_family != AF_NETROM) + } + if (addr->sax25_family != AF_NETROM) { + release_sock(sk); return -EINVAL; - + } if (sk->sk_zapped) { /* Must bind first - autobinding in this may or may not work */ sk->sk_zapped = 0; - if ((dev = nr_dev_first()) == NULL) + if ((dev = nr_dev_first()) == NULL) { + release_sock(sk); return -ENETUNREACH; - + } source = (ax25_address *)dev->dev_addr; if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) + if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) { + dev_put(dev); + release_sock(sk); return -EPERM; + } user = source; } @@ -639,12 +682,15 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, nr->source_addr = *source; nr->device = dev; + dev_put(dev); nr_insert_socket(sk); /* Finish the bind */ } nr->dest_addr = addr->sax25_call; + release_sock(sk); circuit = nr_find_next_circuit(); + lock_sock(sk); nr->my_index = circuit / 256; nr->my_id = circuit % 256; @@ -662,8 +708,10 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, nr_start_heartbeat(sk); /* Now the loop */ - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) + if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) { + release_sock(sk); return -EINPROGRESS; + } /* * A Connect Ack with Choke or timeout or failed routing will go to @@ -678,8 +726,10 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, set_current_state(TASK_INTERRUPTIBLE); if (sk->sk_state != TCP_SYN_SENT) break; + release_sock(sk); if (!signal_pending(tsk)) { schedule(); + lock_sock(sk); continue; } return -ERESTARTSYS; @@ -690,10 +740,12 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, if (sk->sk_state != TCP_ESTABLISHED) { sock->state = SS_UNCONNECTED; + release_sock(sk); return sock_error(sk); /* Always set at this point */ } sock->state = SS_CONNECTED; + release_sock(sk); return 0; } @@ -756,6 +808,7 @@ static int nr_accept(struct socket *sock, struct socket *newsock, int flags) newsock->sk = newsk; out: + release_sock(sk); return err; } @@ -766,9 +819,12 @@ static int nr_getname(struct socket *sock, struct sockaddr *uaddr, struct sock *sk = sock->sk; nr_cb *nr = nr_sk(sk); + lock_sock(sk); if (peer != 0) { - if (sk->sk_state != TCP_ESTABLISHED) + if (sk->sk_state != TCP_ESTABLISHED) { + release_sock(sk); return -ENOTCONN; + } sax->fsa_ax25.sax25_family = AF_NETROM; sax->fsa_ax25.sax25_ndigis = 1; sax->fsa_ax25.sax25_call = nr->user_addr; @@ -780,6 +836,7 @@ static int nr_getname(struct socket *sock, struct sockaddr *uaddr, sax->fsa_ax25.sax25_call = nr->source_addr; *uaddr_len = sizeof(struct sockaddr_ax25); } + release_sock(sk); return 0; } @@ -793,6 +850,7 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) unsigned short circuit_index, circuit_id; unsigned short peer_circuit_index, peer_circuit_id; unsigned short frametype, flags, window, timeout; + int ret; skb->sk = NULL; /* Initially we don't know who it's for */ @@ -850,7 +908,9 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) else nr_sk(sk)->bpqext = 0; - return nr_process_rx_frame(sk, skb); + ret = nr_process_rx_frame(sk, skb); + bh_unlock_sock(sk); + return ret; } /* @@ -880,6 +940,8 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) if (!sk || sk->sk_ack_backlog == sk->sk_max_ack_backlog || (make = nr_make_new(sk)) == NULL) { nr_transmit_refusal(skb, 0); + if (sk) + bh_unlock_sock(sk); return 0; } @@ -897,7 +959,9 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) nr_make->your_index = circuit_index; nr_make->your_id = circuit_id; + bh_unlock_sock(sk); circuit = nr_find_next_circuit(); + bh_lock_sock(sk); nr_make->my_index = circuit / 256; nr_make->my_id = circuit % 256; @@ -939,6 +1003,7 @@ int nr_rx_frame(struct sk_buff *skb, struct net_device *dev) if (!sock_flag(sk, SOCK_DEAD)) sk->sk_data_ready(sk, skb->len); + bh_unlock_sock(sk); return 1; } @@ -957,28 +1022,42 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock, if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) return -EINVAL; - if (sk->sk_zapped) - return -EADDRNOTAVAIL; + lock_sock(sk); + if (sk->sk_zapped) { + err = -EADDRNOTAVAIL; + goto out; + } if (sk->sk_shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); - return -EPIPE; + err = -EPIPE; + goto out; } - if (nr->device == NULL) - return -ENETUNREACH; + if (nr->device == NULL) { + err = -ENETUNREACH; + goto out; + } if (usax) { - if (msg->msg_namelen < sizeof(sax)) - return -EINVAL; + if (msg->msg_namelen < sizeof(sax)) { + err = -EINVAL; + goto out; + } sax = *usax; - if (ax25cmp(&nr->dest_addr, &sax.sax25_call) != 0) - return -EISCONN; - if (sax.sax25_family != AF_NETROM) - return -EINVAL; + if (ax25cmp(&nr->dest_addr, &sax.sax25_call) != 0) { + err = -EISCONN; + goto out; + } + if (sax.sax25_family != AF_NETROM) { + err = -EINVAL; + goto out; + } } else { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) { + err = -ENOTCONN; + goto out; + } sax.sax25_family = AF_NETROM; sax.sax25_call = nr->dest_addr; } @@ -987,10 +1066,10 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock, /* Build a packet */ SOCK_DEBUG(sk, "NET/ROM: sendto: building packet.\n"); - size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN; + size = len + NR_NETWORK_LEN + NR_TRANSPORT_LEN; if ((skb = sock_alloc_send_skb(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) - return err; + goto out; skb_reserve(skb, size - len); @@ -1025,12 +1104,16 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_state != TCP_ESTABLISHED) { kfree_skb(skb); - return -ENOTCONN; + err = -ENOTCONN; + goto out; } nr_output(sk, skb); /* Shove it onto the queue */ - return len; + err = len; +out: + release_sock(sk); + return err; } static int nr_recvmsg(struct kiocb *iocb, struct socket *sock, @@ -1047,12 +1130,17 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock, * us! We do one quick check first though */ - if (sk->sk_state != TCP_ESTABLISHED) + lock_sock(sk); + if (sk->sk_state != TCP_ESTABLISHED) { + release_sock(sk); return -ENOTCONN; + } /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) { + release_sock(sk); return er; + } skb->h.raw = skb->data; copied = skb->len; @@ -1073,6 +1161,7 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock, skb_free_datagram(sk, skb); + release_sock(sk); return copied; } @@ -1080,13 +1169,16 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock, static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; + int ret; + lock_sock(sk); switch (cmd) { case TIOCOUTQ: { long amount; amount = sk->sk_sndbuf - atomic_read(&sk->sk_wmem_alloc); if (amount < 0) amount = 0; + release_sock(sk); return put_user(amount, (int *)arg); } @@ -1096,15 +1188,21 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) amount = skb->len; + release_sock(sk); return put_user(amount, (int *)arg); } case SIOCGSTAMP: if (sk != NULL) { - if (!sk->sk_stamp.tv_sec) + if (!sk->sk_stamp.tv_sec) { + release_sock(sk); return -ENOENT; - return copy_to_user((void *)arg, &sk->sk_stamp, sizeof(struct timeval)) ? -EFAULT : 0; + } + ret = copy_to_user((void *)arg, &sk->sk_stamp, sizeof(struct timeval)) ? -EFAULT : 0; + release_sock(sk); + return ret; } + release_sock(sk); return -EINVAL; case SIOCGIFADDR: @@ -1117,17 +1215,21 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC: + release_sock(sk); return -EINVAL; case SIOCADDRT: case SIOCDELRT: case SIOCNRDECOBS: + release_sock(sk); if (!capable(CAP_NET_ADMIN)) return -EPERM; return nr_rt_ioctl(cmd, (void *)arg); default: + release_sock(sk); return dev_ioctl(cmd, (void *)arg); } + release_sock(sk); return 0; } @@ -1147,7 +1249,9 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length) len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q inode\n"); sk_for_each(s, node, &nr_list) { - nr_cb *nr = nr_sk(s); + nr_cb *nr; + bh_lock_sock(s); + nr = nr_sk(s); if ((dev = nr->device) == NULL) devname = "???"; @@ -1190,7 +1294,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length) len = 0; begin = pos; } - + bh_unlock_sock(s); if (pos > offset + length) break; } diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index 669aea370be04..e272aff648bd5 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -74,6 +74,7 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more) static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + bh_lock_sock(sk); switch (frametype) { case NR_CONNACK: { nr_cb *nr = nr_sk(sk); @@ -102,6 +103,7 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, default: break; } + bh_unlock_sock(sk); return 0; } @@ -114,6 +116,7 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype) { + bh_lock_sock(sk); switch (frametype) { case NR_CONNACK | NR_CHOKE_FLAG: nr_disconnect(sk, ECONNRESET); @@ -129,6 +132,7 @@ static int nr_state2_machine(struct sock *sk, struct sk_buff *skb, default: break; } + bh_unlock_sock(sk); return 0; } @@ -150,6 +154,7 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype nr = skb->data[18]; ns = skb->data[17]; + bh_lock_sock(sk); switch (frametype) { case NR_CONNREQ: nr_write_internal(sk, NR_CONNACK); @@ -260,6 +265,7 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype default: break; } + bh_unlock_sock(sk); return queued; } diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c index 2b92715670687..47570b2024d2b 100644 --- a/net/netrom/nr_timer.c +++ b/net/netrom/nr_timer.c @@ -143,7 +143,10 @@ static void nr_heartbeat_expiry(unsigned long param) is accepted() it isn't 'dead' so doesn't get removed. */ if (sock_flag(sk, SOCK_DESTROY) || (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) { + sock_hold(sk); nr_destroy_socket(sk); + bh_unlock_sock(sk); + sock_put(sk); return; } break; @@ -227,6 +230,7 @@ static void nr_t1timer_expiry(unsigned long param) case NR_STATE_1: if (nr->n2count == nr->n2) { nr_disconnect(sk, ETIMEDOUT); + bh_unlock_sock(sk); return; } else { nr->n2count++; @@ -237,6 +241,7 @@ static void nr_t1timer_expiry(unsigned long param) case NR_STATE_2: if (nr->n2count == nr->n2) { nr_disconnect(sk, ETIMEDOUT); + bh_unlock_sock(sk); return; } else { nr->n2count++; @@ -247,6 +252,7 @@ static void nr_t1timer_expiry(unsigned long param) case NR_STATE_3: if (nr->n2count == nr->n2) { nr_disconnect(sk, ETIMEDOUT); + bh_unlock_sock(sk); return; } else { nr->n2count++; -- 2.53.0