From 408f22e81ea2fcf96c80e85ee12d20ef5148bf7c Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Sat, 16 Jun 2007 14:03:45 -0400 Subject: [PATCH 1/6] SCTP: update sctp_getsockopt helpers to allow oversized buffers I noted the other day while looking at a bug that was ostensibly in some perl networking library, that we strictly avoid allowing getsockopt operations to complete if we pass in oversized buffers. This seems to make libraries like Perl::NET malfunction since it seems to allocate oversized buffers for use in several operations. It also seems to be out of line with the way udp, tcp and ip getsockopt routines handle buffer input (since the *optlen pointer in both an input and an output and gets set to the length of the data that we copy into the buffer). This patch brings our getsockopt helpers into line with other protocols, and allows us to accept oversized buffers for our getsockopt operations. Tested by me with good results. Signed-off-by: Neil Horman Acked-by: Sridhar Samudrala Signed-off-by: Vlad Yasevich --- net/sctp/socket.c | 108 +++++++++++++++++++++++++++++++--------------- 1 file changed, 74 insertions(+), 34 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 6edaaa009d62..c1f239ac12b9 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3375,12 +3375,13 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, sctp_assoc_t associd; int retval = 0; - if (len != sizeof(status)) { + if (len < sizeof(status)) { retval = -EINVAL; goto out; } - if (copy_from_user(&status, optval, sizeof(status))) { + len = sizeof(status); + if (copy_from_user(&status, optval, len)) { retval = -EFAULT; goto out; } @@ -3452,12 +3453,13 @@ static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len, struct sctp_transport *transport; int retval = 0; - if (len != sizeof(pinfo)) { + if (len < sizeof(pinfo)) { retval = -EINVAL; goto out; } - if (copy_from_user(&pinfo, optval, sizeof(pinfo))) { + len = sizeof(pinfo); + if (copy_from_user(&pinfo, optval, len)) { retval = -EFAULT; goto out; } @@ -3523,8 +3525,11 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len, static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval, int __user *optlen) { - if (len != sizeof(struct sctp_event_subscribe)) + if (len < sizeof(struct sctp_event_subscribe)) return -EINVAL; + len = sizeof(struct sctp_event_subscribe); + if (put_user(len, optlen)) + return -EFAULT; if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len)) return -EFAULT; return 0; @@ -3546,9 +3551,12 @@ static int sctp_getsockopt_autoclose(struct sock *sk, int len, char __user *optv /* Applicable to UDP-style socket only */ if (sctp_style(sk, TCP)) return -EOPNOTSUPP; - if (len != sizeof(int)) + if (len < sizeof(int)) return -EINVAL; - if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len)) + len = sizeof(int); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &sctp_sk(sk)->autoclose, sizeof(int))) return -EFAULT; return 0; } @@ -3599,8 +3607,9 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval int retval = 0; struct sctp_association *asoc; - if (len != sizeof(sctp_peeloff_arg_t)) + if (len < sizeof(sctp_peeloff_arg_t)) return -EINVAL; + len = sizeof(sctp_peeloff_arg_t); if (copy_from_user(&peeloff, optval, len)) return -EFAULT; @@ -3628,6 +3637,8 @@ static int sctp_getsockopt_peeloff(struct sock *sk, int len, char __user *optval /* Return the fd mapped to the new socket. */ peeloff.sd = retval; + if (put_user(len, optlen)) + return -EFAULT; if (copy_to_user(optval, &peeloff, len)) retval = -EFAULT; @@ -3736,9 +3747,9 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, struct sctp_association *asoc = NULL; struct sctp_sock *sp = sctp_sk(sk); - if (len != sizeof(struct sctp_paddrparams)) + if (len < sizeof(struct sctp_paddrparams)) return -EINVAL; - + len = sizeof(struct sctp_paddrparams); if (copy_from_user(¶ms, optval, len)) return -EFAULT; @@ -3837,9 +3848,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len, struct sctp_association *asoc = NULL; struct sctp_sock *sp = sctp_sk(sk); - if (len != sizeof(struct sctp_assoc_value)) + if (len < sizeof(struct sctp_assoc_value)) return - EINVAL; + len = sizeof(struct sctp_assoc_value); + if (copy_from_user(¶ms, optval, len)) return -EFAULT; @@ -3888,8 +3901,11 @@ static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len, */ static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval, int __user *optlen) { - if (len != sizeof(struct sctp_initmsg)) + if (len < sizeof(struct sctp_initmsg)) return -EINVAL; + len = sizeof(struct sctp_initmsg); + if (put_user(len, optlen)) + return -EFAULT; if (copy_to_user(optval, &sctp_sk(sk)->initmsg, len)) return -EFAULT; return 0; @@ -3904,7 +3920,7 @@ static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len, struct list_head *pos; int cnt = 0; - if (len != sizeof(sctp_assoc_t)) + if (len < sizeof(sctp_assoc_t)) return -EINVAL; if (copy_from_user(&id, optval, sizeof(sctp_assoc_t))) @@ -3940,10 +3956,12 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len, struct sctp_sock *sp = sctp_sk(sk); int addrlen; - if (len != sizeof(struct sctp_getaddrs_old)) + if (len < sizeof(struct sctp_getaddrs_old)) return -EINVAL; - if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old))) + len = sizeof(struct sctp_getaddrs_old); + + if (copy_from_user(&getaddrs, optval, len)) return -EFAULT; if (getaddrs.addr_num <= 0) return -EINVAL; @@ -3966,7 +3984,9 @@ static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len, if (cnt >= getaddrs.addr_num) break; } getaddrs.addr_num = cnt; - if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &getaddrs, len)) return -EFAULT; return 0; @@ -4037,7 +4057,7 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, rwlock_t *addr_lock; int cnt = 0; - if (len != sizeof(sctp_assoc_t)) + if (len < sizeof(sctp_assoc_t)) return -EINVAL; if (copy_from_user(&id, optval, sizeof(sctp_assoc_t))) @@ -4179,10 +4199,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, void *buf; int bytes_copied = 0; - if (len != sizeof(struct sctp_getaddrs_old)) + if (len < sizeof(struct sctp_getaddrs_old)) return -EINVAL; - if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old))) + len = sizeof(struct sctp_getaddrs_old); + if (copy_from_user(&getaddrs, optval, len)) return -EFAULT; if (getaddrs.addr_num <= 0) return -EINVAL; @@ -4254,7 +4275,7 @@ copy_getaddrs: /* copy the leading structure back to user */ getaddrs.addr_num = cnt; - if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) + if (copy_to_user(optval, &getaddrs, len)) err = -EFAULT; error: @@ -4282,7 +4303,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, void *addrs; void *buf; - if (len <= sizeof(struct sctp_getaddrs)) + if (len < sizeof(struct sctp_getaddrs)) return -EINVAL; if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs))) @@ -4379,10 +4400,12 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len, struct sctp_association *asoc; struct sctp_sock *sp = sctp_sk(sk); - if (len != sizeof(struct sctp_prim)) + if (len < sizeof(struct sctp_prim)) return -EINVAL; - if (copy_from_user(&prim, optval, sizeof(struct sctp_prim))) + len = sizeof(struct sctp_prim); + + if (copy_from_user(&prim, optval, len)) return -EFAULT; asoc = sctp_id2assoc(sk, prim.ssp_assoc_id); @@ -4398,7 +4421,9 @@ static int sctp_getsockopt_primary_addr(struct sock *sk, int len, sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, (union sctp_addr *)&prim.ssp_addr); - if (copy_to_user(optval, &prim, sizeof(struct sctp_prim))) + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &prim, len)) return -EFAULT; return 0; @@ -4415,10 +4440,15 @@ static int sctp_getsockopt_adaptation_layer(struct sock *sk, int len, { struct sctp_setadaptation adaptation; - if (len != sizeof(struct sctp_setadaptation)) + if (len < sizeof(struct sctp_setadaptation)) return -EINVAL; + len = sizeof(struct sctp_setadaptation); + adaptation.ssb_adaptation_ind = sctp_sk(sk)->adaptation_ind; + + if (put_user(len, optlen)) + return -EFAULT; if (copy_to_user(optval, &adaptation, len)) return -EFAULT; @@ -4452,9 +4482,12 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, struct sctp_association *asoc; struct sctp_sock *sp = sctp_sk(sk); - if (len != sizeof(struct sctp_sndrcvinfo)) + if (len < sizeof(struct sctp_sndrcvinfo)) return -EINVAL; - if (copy_from_user(&info, optval, sizeof(struct sctp_sndrcvinfo))) + + len = sizeof(struct sctp_sndrcvinfo); + + if (copy_from_user(&info, optval, len)) return -EFAULT; asoc = sctp_id2assoc(sk, info.sinfo_assoc_id); @@ -4475,7 +4508,9 @@ static int sctp_getsockopt_default_send_param(struct sock *sk, info.sinfo_timetolive = sp->default_timetolive; } - if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo))) + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &info, len)) return -EFAULT; return 0; @@ -4526,10 +4561,12 @@ static int sctp_getsockopt_rtoinfo(struct sock *sk, int len, struct sctp_rtoinfo rtoinfo; struct sctp_association *asoc; - if (len != sizeof (struct sctp_rtoinfo)) + if (len < sizeof (struct sctp_rtoinfo)) return -EINVAL; - if (copy_from_user(&rtoinfo, optval, sizeof (struct sctp_rtoinfo))) + len = sizeof(struct sctp_rtoinfo); + + if (copy_from_user(&rtoinfo, optval, len)) return -EFAULT; asoc = sctp_id2assoc(sk, rtoinfo.srto_assoc_id); @@ -4581,11 +4618,12 @@ static int sctp_getsockopt_associnfo(struct sock *sk, int len, struct list_head *pos; int cnt = 0; - if (len != sizeof (struct sctp_assocparams)) + if (len < sizeof (struct sctp_assocparams)) return -EINVAL; - if (copy_from_user(&assocparams, optval, - sizeof (struct sctp_assocparams))) + len = sizeof(struct sctp_assocparams); + + if (copy_from_user(&assocparams, optval, len)) return -EFAULT; asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id); @@ -4671,9 +4709,11 @@ static int sctp_getsockopt_context(struct sock *sk, int len, struct sctp_sock *sp; struct sctp_association *asoc; - if (len != sizeof(struct sctp_assoc_value)) + if (len < sizeof(struct sctp_assoc_value)) return -EINVAL; + len = sizeof(struct sctp_assoc_value); + if (copy_from_user(¶ms, optval, len)) return -EFAULT; From 186e234358ba29a4094d0c8c0d3ea00f84d32a3e Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 18 Jun 2007 19:59:16 -0400 Subject: [PATCH 2/6] SCTP: Fix sctp_getsockopt_get_peer_addrs This is the split out of the patch that we agreed I should split out from my last patch. It changes space_left to be computed in the same way the to variable is. I know we talked about changing space_left to an int, but I think size_t is more appropriate, since we should never have negative space in our buffer, and computing using offsetof means space_left should now never drop below zero. Signed-off-by: Neil Horman Acked-by: Sridhar Samudrala Signed-off-by: Vlad Yasevich --- net/sctp/socket.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c1f239ac12b9..2fc036699d48 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4019,8 +4019,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, return -EINVAL; to = optval + offsetof(struct sctp_getaddrs,addrs); - space_left = len - sizeof(struct sctp_getaddrs) - - offsetof(struct sctp_getaddrs,addrs); + space_left = len - offsetof(struct sctp_getaddrs,addrs); list_for_each(pos, &asoc->peer.transport_addr_list) { from = list_entry(pos, struct sctp_transport, transports); @@ -4327,8 +4326,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, } to = optval + offsetof(struct sctp_getaddrs,addrs); - space_left = len - sizeof(struct sctp_getaddrs) - - offsetof(struct sctp_getaddrs,addrs); + space_left = len - offsetof(struct sctp_getaddrs,addrs); + addrs = kmalloc(space_left, GFP_KERNEL); if (!addrs) return -ENOMEM; From 5131a184a3458d9ac47d9eba032cf4c4d3295afd Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Fri, 22 Jun 2007 15:14:46 -0700 Subject: [PATCH 3/6] SCTP: lock_sock_nested in sctp_sock_migrate sctp_sock_migrate() grabs the socket lock on a newly allocated socket while holding the socket lock on an old socket. lockdep worries that this might be a recursive lock attempt. task/3026 is trying to acquire lock: (sk_lock-AF_INET){--..}, at: [] sctp_sock_migrate+0x2e3/0x327 [sctp] but task is already holding lock: (sk_lock-AF_INET){--..}, at: [] sctp_accept+0xdf/0x1e3 [sctp] This patch tells lockdep that this locking is safe by using lock_sock_nested(). Signed-off-by: Zach Brown Signed-off-by: Vlad Yasevich --- net/sctp/socket.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2fc036699d48..67861a8f00cb 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -6123,8 +6123,11 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, * queued to the backlog. This prevents a potential race between * backlog processing on the old socket and new-packet processing * on the new socket. + * + * The caller has just allocated newsk so we can guarantee that other + * paths won't try to lock it and then oldsk. */ - sctp_lock_sock(newsk); + lock_sock_nested(newsk, SINGLE_DEPTH_NESTING); sctp_assoc_migrate(assoc, newsk); /* If the association on the newsk is already closed before accept() From 0db3dc73f7a3a73b0dc725b6a991253f5652c905 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Wed, 27 Jun 2007 00:39:42 -0700 Subject: [PATCH 4/6] [NETPOLL]: tx lock deadlock fix If sky2 device poll routine is called from netpoll_send_skb, it would deadlock. The netpoll_send_skb held the netif_tx_lock, and the poll routine could acquire it to clean up skb's. Other drivers might use same locking model. The driver is correct, netpoll should not introduce more locking problems than it causes already. So change the code to drop lock before calling poll handler. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/netpoll.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 758dafe284c0..f8e74e511ce6 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -250,22 +250,23 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) unsigned long flags; local_irq_save(flags); - if (netif_tx_trylock(dev)) { - /* try until next clock tick */ - for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; - tries > 0; --tries) { + /* try until next clock tick */ + for (tries = jiffies_to_usecs(1)/USEC_PER_POLL; + tries > 0; --tries) { + if (netif_tx_trylock(dev)) { if (!netif_queue_stopped(dev)) status = dev->hard_start_xmit(skb, dev); + netif_tx_unlock(dev); if (status == NETDEV_TX_OK) break; - /* tickle device maybe there is some cleanup */ - netpoll_poll(np); - - udelay(USEC_PER_POLL); } - netif_tx_unlock(dev); + + /* tickle device maybe there is some cleanup */ + netpoll_poll(np); + + udelay(USEC_PER_POLL); } local_irq_restore(flags); } From 17200811cf539b9107a99a39bf71ba3567966285 Mon Sep 17 00:00:00 2001 From: Jarek Poplawski Date: Thu, 28 Jun 2007 22:11:47 -0700 Subject: [PATCH 5/6] [NETPOLL] netconsole: fix soft lockup when removing module #1 Until kernel ver. 2.6.21 (including) cancel_rearming_delayed_work() required a work function should always (unconditionally) rearm with delay > 0 - otherwise it would endlessly loop. This patch replaces this function with cancel_delayed_work(). Later kernel versions don't require this, so here it's only for uniformity. #2 After deleting a timer in cancel_[rearming_]delayed_work() there could stay a last skb queued in npinfo->txq causing a memory leak after kfree(npinfo). Initial patch & testing by: Jason Wessel Signed-off-by: Jarek Poplawski Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- net/core/netpoll.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/core/netpoll.c b/net/core/netpoll.c index f8e74e511ce6..cf40ff91ac01 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -72,7 +72,8 @@ static void queue_process(struct work_struct *work) netif_tx_unlock(dev); local_irq_restore(flags); - schedule_delayed_work(&npinfo->tx_work, HZ/10); + if (atomic_read(&npinfo->refcnt)) + schedule_delayed_work(&npinfo->tx_work, HZ/10); return; } netif_tx_unlock(dev); @@ -785,9 +786,15 @@ void netpoll_cleanup(struct netpoll *np) if (atomic_dec_and_test(&npinfo->refcnt)) { skb_queue_purge(&npinfo->arp_tx); skb_queue_purge(&npinfo->txq); - cancel_rearming_delayed_work(&npinfo->tx_work); + cancel_delayed_work(&npinfo->tx_work); flush_scheduled_work(); + /* clean after last, unfinished work */ + if (!skb_queue_empty(&npinfo->txq)) { + struct sk_buff *skb; + skb = __skb_dequeue(&npinfo->txq); + kfree_skb(skb); + } kfree(npinfo); } } From 5f0212174db33828f8bddcff501b73d2bc734f72 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 28 Jun 2007 22:40:23 -0700 Subject: [PATCH 6/6] [IRDA]: fix printk format Fix printk format warning: drivers/net/irda/irport.c:512: warning: format '%d' expects type 'int', but argument 5 has type 'long int' Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: David S. Miller --- drivers/net/irda/irport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index 3098960dc2a1..3078c419cb02 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -509,7 +509,7 @@ static void irport_timeout(struct net_device *dev) IRDA_DEBUG(0, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", __FUNCTION__, iir, lsr, iobase); - IRDA_DEBUG(0, "%s(), transmitting=%d, remain=%d, done=%d\n", + IRDA_DEBUG(0, "%s(), transmitting=%d, remain=%d, done=%td\n", __FUNCTION__, self->transmitting, self->tx_buff.len, self->tx_buff.data - self->tx_buff.head);