From 29c5d4afba51c71cfeadd3f74f3c42e064483fb0 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Tue, 18 Sep 2007 13:07:15 -0700 Subject: [NETFILTER]: nfnetlink_log: fix sending of multipart messages The following patch fixes the handling of netlink packets containing multiple messages. As exposed during netfilter workshop, nfnetlink_log was overwritten the message type of the last message (setting it to MSG_DONE) in a multipart packet. The consequence was libnfnetlink to ignore the last message in the packet. The following patch adds a supplementary message (with type MSG_DONE) af the end of the netlink skb. Signed-off-by: Eric Leblond Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/netfilter/nfnetlink_log.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index e185a5b5591..2351533a850 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -58,7 +58,6 @@ struct nfulnl_instance { unsigned int qlen; /* number of nlmsgs in skb */ struct sk_buff *skb; /* pre-allocatd skb */ - struct nlmsghdr *lastnlh; /* netlink header of last msg in skb */ struct timer_list timer; int peer_pid; /* PID of the peer process */ @@ -345,10 +344,12 @@ static struct sk_buff *nfulnl_alloc_skb(unsigned int inst_size, static int __nfulnl_send(struct nfulnl_instance *inst) { - int status; + int status = -1; if (inst->qlen > 1) - inst->lastnlh->nlmsg_type = NLMSG_DONE; + NLMSG_PUT(inst->skb, 0, 0, + NLMSG_DONE, + sizeof(struct nfgenmsg)); status = nfnetlink_unicast(inst->skb, inst->peer_pid, MSG_DONTWAIT); if (status < 0) { @@ -358,8 +359,8 @@ __nfulnl_send(struct nfulnl_instance *inst) inst->qlen = 0; inst->skb = NULL; - inst->lastnlh = NULL; +nlmsg_failure: return status; } @@ -538,7 +539,6 @@ __build_packet_message(struct nfulnl_instance *inst, } nlh->nlmsg_len = inst->skb->tail - old_tail; - inst->lastnlh = nlh; return 0; nlmsg_failure: @@ -644,7 +644,8 @@ nfulnl_log_packet(unsigned int pf, } if (inst->qlen >= qthreshold || - (inst->skb && size > skb_tailroom(inst->skb))) { + (inst->skb && size > + skb_tailroom(inst->skb) - sizeof(struct nfgenmsg))) { /* either the queue len is too high or we don't have * enough room in the skb left. flush to userspace. */ UDEBUG("flushing old skb\n"); -- cgit v1.2.3-70-g09d2 From 5588b40d7c2bff75ee573ed42d1738c73ce24492 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Wed, 19 Sep 2007 10:42:03 -0700 Subject: [PKT_SCHED]: Fix 'SFQ qdisc crashes with limit of 2 packets' Acked-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_sfq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 95795730985..3a23e30bc79 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -270,7 +270,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) q->tail = x; } } - if (++sch->q.qlen < q->limit-1) { + if (++sch->q.qlen <= q->limit) { sch->bstats.bytes += skb->len; sch->bstats.packets++; return 0; @@ -306,7 +306,7 @@ sfq_requeue(struct sk_buff *skb, struct Qdisc* sch) q->tail = x; } } - if (++sch->q.qlen < q->limit - 1) { + if (++sch->q.qlen <= q->limit) { sch->qstats.requeues++; return 0; } @@ -391,10 +391,10 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) q->quantum = ctl->quantum ? : psched_mtu(sch->dev); q->perturb_period = ctl->perturb_period*HZ; if (ctl->limit) - q->limit = min_t(u32, ctl->limit, SFQ_DEPTH); + q->limit = min_t(u32, ctl->limit, SFQ_DEPTH - 2); qlen = sch->q.qlen; - while (sch->q.qlen >= q->limit-1) + while (sch->q.qlen > q->limit) sfq_drop(sch); qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen); @@ -423,7 +423,7 @@ static int sfq_init(struct Qdisc *sch, struct rtattr *opt) q->dep[i+SFQ_DEPTH].next = i+SFQ_DEPTH; q->dep[i+SFQ_DEPTH].prev = i+SFQ_DEPTH; } - q->limit = SFQ_DEPTH; + q->limit = SFQ_DEPTH - 2; q->max_depth = 0; q->tail = SFQ_DEPTH; if (opt == NULL) { -- cgit v1.2.3-70-g09d2 From 9db619e66503494e41159de3c76fafabe80d016b Mon Sep 17 00:00:00 2001 From: Wolfgang Walter Date: Thu, 20 Sep 2007 15:51:46 -0400 Subject: rpc: fix garbage in printk in svc_tcp_accept() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit we upgraded the kernel of a nfs-server from 2.6.17.11 to 2.6.22.6. Since then we get the message lockd: too many open TCP sockets, consider increasing the number of nfsd threads lockd: last TCP connect from ^\\236^\É^D These random characters in the second line are caused by a bug in svc_tcp_accept. (Note: there are two previous __svc_print_addr(sin, buf, sizeof(buf)) calls in this function, either of which would initialize buf correctly; but both are inside "if"'s and are not necessarily executed. This is less obvious in the second case, which is inside a dprintk(), which is a macro which expands to an if statement.) Signed-off-by: Wolfgang Walter Signed-off-by: J. Bruce Fields Signed-off-by: Linus Torvalds --- net/sunrpc/svcsock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 1a899924023..036ab520df2 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -1110,7 +1110,8 @@ svc_tcp_accept(struct svc_sock *svsk) serv->sv_name); printk(KERN_NOTICE "%s: last TCP connect from %s\n", - serv->sv_name, buf); + serv->sv_name, __svc_print_addr(sin, + buf, sizeof(buf))); } /* * Always select the oldest socket. It's not fair, -- cgit v1.2.3-70-g09d2 From efe870f9f4ad74410a18ecbf0d9ba7c14b50a0fb Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 18 Sep 2007 22:00:52 -0500 Subject: [PATCH] softmac: Fix inability to associate with WEP networks Commit 4cf92a3c was submitted as a fix for bug #8686 at bugzilla.kernel.org (http://bugzilla.kernel.org/show_bug.cgi?id=8686). Unfortunately, the fix led to a new bug, reported by Yoshifuji Hideaki, that prevented association for WEP encrypted networks that use ifconfig to control the device. This patch effectively reverts the earlier commit and does a proper fix for bug #8686. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- net/ieee80211/softmac/ieee80211softmac_assoc.c | 2 - net/ieee80211/softmac/ieee80211softmac_wx.c | 54 ++++++++++---------------- 2 files changed, 20 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index afb6c6698b2..e475f2e1be1 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -273,8 +273,6 @@ ieee80211softmac_assoc_work(struct work_struct *work) ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify_scan, NULL); if (ieee80211softmac_start_scan(mac)) { dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n"); - mac->associnfo.associating = 0; - mac->associnfo.associated = 0; } goto out; } else { diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c index d054e9224b3..442b9875f3f 100644 --- a/net/ieee80211/softmac/ieee80211softmac_wx.c +++ b/net/ieee80211/softmac/ieee80211softmac_wx.c @@ -70,44 +70,30 @@ ieee80211softmac_wx_set_essid(struct net_device *net_dev, char *extra) { struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); - struct ieee80211softmac_network *n; struct ieee80211softmac_auth_queue_item *authptr; int length = 0; check_assoc_again: mutex_lock(&sm->associnfo.mutex); - /* Check if we're already associating to this or another network - * If it's another network, cancel and start over with our new network - * If it's our network, ignore the change, we're already doing it! - */ if((sm->associnfo.associating || sm->associnfo.associated) && (data->essid.flags && data->essid.length)) { - /* Get the associating network */ - n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid); - if(n && n->essid.len == data->essid.length && - !memcmp(n->essid.data, extra, n->essid.len)) { - dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n", - MAC_ARG(sm->associnfo.bssid)); - goto out; - } else { - dprintk(KERN_INFO PFX "Canceling existing associate request!\n"); - /* Cancel assoc work */ - cancel_delayed_work(&sm->associnfo.work); - /* We don't have to do this, but it's a little cleaner */ - list_for_each_entry(authptr, &sm->auth_queue, list) - cancel_delayed_work(&authptr->work); - sm->associnfo.bssvalid = 0; - sm->associnfo.bssfixed = 0; - sm->associnfo.associating = 0; - sm->associnfo.associated = 0; - /* We must unlock to avoid deadlocks with the assoc workqueue - * on the associnfo.mutex */ - mutex_unlock(&sm->associnfo.mutex); - flush_scheduled_work(); - /* Avoid race! Check assoc status again. Maybe someone started an - * association while we flushed. */ - goto check_assoc_again; - } + dprintk(KERN_INFO PFX "Canceling existing associate request!\n"); + /* Cancel assoc work */ + cancel_delayed_work(&sm->associnfo.work); + /* We don't have to do this, but it's a little cleaner */ + list_for_each_entry(authptr, &sm->auth_queue, list) + cancel_delayed_work(&authptr->work); + sm->associnfo.bssvalid = 0; + sm->associnfo.bssfixed = 0; + sm->associnfo.associating = 0; + sm->associnfo.associated = 0; + /* We must unlock to avoid deadlocks with the assoc workqueue + * on the associnfo.mutex */ + mutex_unlock(&sm->associnfo.mutex); + flush_scheduled_work(); + /* Avoid race! Check assoc status again. Maybe someone started an + * association while we flushed. */ + goto check_assoc_again; } sm->associnfo.static_essid = 0; @@ -153,13 +139,13 @@ ieee80211softmac_wx_get_essid(struct net_device *net_dev, data->essid.length = sm->associnfo.req_essid.len; data->essid.flags = 1; /* active */ memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len); - } - + dprintk(KERN_INFO PFX "Getting essid from req_essid\n"); + } else if (sm->associnfo.associated || sm->associnfo.associating) { /* If we're associating/associated, return that */ - if (sm->associnfo.associated || sm->associnfo.associating) { data->essid.length = sm->associnfo.associate_essid.len; data->essid.flags = 1; /* active */ memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len); + dprintk(KERN_INFO PFX "Getting essid from associate_essid\n"); } mutex_unlock(&sm->associnfo.mutex); -- cgit v1.2.3-70-g09d2 From 7b5ee3a038c869e773092834abaa8174e0d77514 Mon Sep 17 00:00:00 2001 From: Satyam Sharma Date: Mon, 3 Sep 2007 01:41:34 +0530 Subject: [PATCH] net/wireless/sysfs.c: Shut up build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit net/wireless/sysfs.c:108: warning: ‘wiphy_uevent’ defined but not used when CONFIG_HOTPLUG=n is because the only usage site of this function is #ifdef'ed as such, so let's #ifdef the definition also. Signed-off-by: Satyam Sharma Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/sysfs.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 88aaacd9f82..2d5d2255a27 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -52,12 +52,14 @@ static void wiphy_dev_release(struct device *dev) cfg80211_dev_free(rdev); } +#ifdef CONFIG_HOTPLUG static int wiphy_uevent(struct device *dev, char **envp, int num_envp, char *buf, int size) { /* TODO, we probably need stuff here */ return 0; } +#endif struct class ieee80211_class = { .name = "ieee80211", -- cgit v1.2.3-70-g09d2 From 3a4624652e68ee897145ac698a85bfd2603367c3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Sep 2007 13:44:45 +0200 Subject: [PATCH] cfg80211: fix initialisation if built-in When cfg80211 is built into the kernel it needs to init earlier so that device registrations are run after it has initialised. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/core.c b/net/wireless/core.c index 7eabd55417a..9771451eae2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -213,7 +213,7 @@ out_fail_notifier: out_fail_sysfs: return err; } -module_init(cfg80211_init); +subsys_initcall(cfg80211_init); static void cfg80211_exit(void) { -- cgit v1.2.3-70-g09d2 From 136e83d6b8999f4ef680ff9cf2a67e1e1ccbd94e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Sep 2007 13:55:08 +0200 Subject: [PATCH] net/mac80211/wme.c: fix sparse warning wme.c triggers a sparse warning; it wasn't noticed before because until recently ARRAY_SIZE triggered a sparse error. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 89ce8152969..7ab82b376e1 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -424,7 +424,7 @@ static int wme_qdiscop_init(struct Qdisc *qd, struct rtattr *opt) skb_queue_head_init(&q->requeued[i]); q->queues[i] = qdisc_create_dflt(qd->dev, &pfifo_qdisc_ops, qd->handle); - if (q->queues[i] == 0) { + if (!q->queues[i]) { q->queues[i] = &noop_qdisc; printk(KERN_ERR "%s child qdisc %i creation failed", dev->name, i); } -- cgit v1.2.3-70-g09d2 From ca9938fea576ebbb8d8c4fbe8a5bcc937e49e1ca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Sep 2007 12:50:32 +0200 Subject: [PATCH] mac80211: fix initialisation when built-in When mac80211 is built into the kernel it needs to init earlier so that device registrations are run after it has initialised. The same applies to rate control algorithms. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211.c | 2 +- net/mac80211/rc80211_simple.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 7286c389a4d..ff2172ffd86 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -5259,7 +5259,7 @@ static void __exit ieee80211_exit(void) } -module_init(ieee80211_init); +subsys_initcall(ieee80211_init); module_exit(ieee80211_exit); MODULE_DESCRIPTION("IEEE 802.11 subsystem"); diff --git a/net/mac80211/rc80211_simple.c b/net/mac80211/rc80211_simple.c index f6780d63b34..17b9f46bbf2 100644 --- a/net/mac80211/rc80211_simple.c +++ b/net/mac80211/rc80211_simple.c @@ -431,7 +431,7 @@ static void __exit rate_control_simple_exit(void) } -module_init(rate_control_simple_init); +subsys_initcall(rate_control_simple_init); module_exit(rate_control_simple_exit); MODULE_DESCRIPTION("Simple rate control algorithm for ieee80211"); -- cgit v1.2.3-70-g09d2 From a09c83847b664dcd67a72613374061c900afb799 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Wed, 5 Sep 2007 15:53:58 -0400 Subject: SCTP: Validate buffer room when processing sequential chunks When we process bundled chunks, we need to make sure that the skb has the buffer for each header since we assume it's always there. Some malicious node can send us something like DATA + 2 bytes and we'll try to walk off the end refrencing potentially uninitialized memory. Signed-off-by: Vlad Yasevich --- net/sctp/inqueue.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 88aa2240754..e4ea7fdf36e 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -130,6 +130,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) /* Force chunk->skb->data to chunk->chunk_end. */ skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data); + + /* Verify that we have at least chunk headers + * worth of buffer left. + */ + if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) { + sctp_chunk_free(chunk); + chunk = queue->in_progress = NULL; + } } } -- cgit v1.2.3-70-g09d2 From 02c4e12c6400b6dccdc6b5c2c18325551e4b2dc9 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 31 Aug 2007 10:03:58 +0800 Subject: SCTP: Send ABORT chunk with correct tag in response to INIT ACK When SCTP client received an INIT ACK chunk with missing mandatory parameter such as "cookie parameter", it will send back a ABORT with T-bit not set and verification tag is set to 0. This is because before we accept this INIT ACK chunk, we do not know the peer's tag. This patch change to reflect vtag when responding to INIT ACK with missing mandatory parameter. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- net/sctp/sm_statefuns.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 177528ed3e1..385f1757158 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -5176,7 +5176,22 @@ static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc * association exists, otherwise, use the peer's vtag. */ if (asoc) { - vtag = asoc->peer.i.init_tag; + /* Special case the INIT-ACK as there is no peer's vtag + * yet. + */ + switch(chunk->chunk_hdr->type) { + case SCTP_CID_INIT_ACK: + { + sctp_initack_chunk_t *initack; + + initack = (sctp_initack_chunk_t *)chunk->chunk_hdr; + vtag = ntohl(initack->init_hdr.init_tag); + break; + } + default: + vtag = asoc->peer.i.init_tag; + break; + } } else { /* Special case the INIT and stale COOKIE_ECHO as there is no * vtag yet. -- cgit v1.2.3-70-g09d2 From d3f259687fd248aa4de477149481478c122ba48b Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 7 Sep 2007 11:47:45 -0400 Subject: SCTP: Explicitely discard OOTB chunks Explicitely discard OOTB chunks, whether the result is a SHUTDOWN COMPLETE or an ABORT. We need to discard the OOTB SHUTDOWN ACK to prevent bombing attackes since responsed MUST NOT be bundled. We also explicietely discard in the ABORT case since that function is widely used internally. Signed-off-by: Vlad Yasevich Acked-by: Neil Horman --- net/sctp/sm_statefuns.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 385f1757158..5e7fb0f54b8 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2965,6 +2965,7 @@ sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + sctp_sf_pdiscard(ep, asoc, type, arg, commands); return SCTP_DISPOSITION_CONSUME; } @@ -3148,11 +3149,9 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, } while (ch_end < skb_tail_pointer(skb)); if (ootb_shut_ack) - sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); + return sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); else - sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); - - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); } /* @@ -3218,7 +3217,11 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - return SCTP_DISPOSITION_CONSUME; + /* We need to discard the rest of the packet to prevent + * potential bomming attacks from additional bundled chunks. + * This is documented in SCTP Threats ID. + */ + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); } return SCTP_DISPOSITION_NOMEM; -- cgit v1.2.3-70-g09d2 From ece25dfa0991f65c4e1d26beb1c3c45bda4239b8 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Fri, 7 Sep 2007 16:30:54 -0400 Subject: SCTP: Clean up OOTB handling and fix infinite loop processing While processing OOTB chunks as well as chunks with an invalid length of 0, it was possible to SCTP to get wedged inside an infinite loop because we didn't catch the condition correctly, or didn't mark the packet for discard correctly. This work is based on original findings and work by Wei Yongjun Signed-off-by: Vlad Yasevich --- include/net/sctp/sm.h | 1 - net/sctp/sm_statefuns.c | 163 +++++++++++++++++++++++++++++++++++++---------- net/sctp/sm_statetable.c | 16 ++--- 3 files changed, 138 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 991c85bb9e3..cc71f366d1c 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -114,7 +114,6 @@ sctp_state_fn_t sctp_sf_do_4_C; sctp_state_fn_t sctp_sf_eat_data_6_2; sctp_state_fn_t sctp_sf_eat_data_fast_4_4; sctp_state_fn_t sctp_sf_eat_sack_6_2; -sctp_state_fn_t sctp_sf_tabort_8_4_8; sctp_state_fn_t sctp_sf_operr_notify; sctp_state_fn_t sctp_sf_t1_init_timer_expire; sctp_state_fn_t sctp_sf_t1_cookie_timer_expire; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 5e7fb0f54b8..caed19d90d0 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -90,6 +90,11 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands); +static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, @@ -98,6 +103,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, struct sctp_transport *transport); static sctp_disposition_t sctp_sf_abort_violation( + const struct sctp_endpoint *ep, const struct sctp_association *asoc, void *arg, sctp_cmd_seq_t *commands, @@ -118,6 +124,13 @@ static sctp_disposition_t sctp_sf_violation_ctsn( void *arg, sctp_cmd_seq_t *commands); +static sctp_disposition_t sctp_sf_violation_chunk( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands); + /* Small helper function that checks if the chunk length * is of the appropriate length. The 'required_length' argument * is set to be the size of a specific chunk we are testing. @@ -181,16 +194,21 @@ sctp_disposition_t sctp_sf_do_4_C(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; + if (!sctp_vtag_verify_either(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* RFC 2960 6.10 Bundling * * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. */ if (!chunk->singleton) - return SCTP_DISPOSITION_VIOLATION; + return sctp_sf_violation_chunk(ep, asoc, type, arg, commands); - if (!sctp_vtag_verify_either(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the SHUTDOWN_COMPLETE chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); /* RFC 2960 10.2 SCTP-to-ULP * @@ -450,17 +468,17 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - /* Make sure that the INIT-ACK chunk has a valid length */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t))) - return sctp_sf_violation_chunklen(ep, asoc, type, arg, - commands); /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. */ if (!chunk->singleton) - return SCTP_DISPOSITION_VIOLATION; + return sctp_sf_violation_chunk(ep, asoc, type, arg, commands); + /* Make sure that the INIT-ACK chunk has a valid length */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); /* Grab the INIT header. */ chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; @@ -585,7 +603,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, * control endpoint, respond with an ABORT. */ if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) - return sctp_sf_ootb(ep, asoc, type, arg, commands); + return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); /* Make sure that the COOKIE_ECHO chunk has a valid length. * In this case, we check that we have enough for at least a @@ -2496,6 +2514,11 @@ sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = (struct sctp_chunk *) arg; struct sctp_chunk *reply; + /* Make sure that the chunk has a valid length */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Since we are not going to really process this INIT, there * is no point in verifying chunk boundries. Just generate * the SHUTDOWN ACK. @@ -2929,7 +2952,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, +static sctp_disposition_t sctp_sf_tabort_8_4_8(const struct sctp_endpoint *ep, const struct sctp_association *asoc, const sctp_subtype_t type, void *arg, @@ -3126,14 +3149,14 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, ch = (sctp_chunkhdr_t *) chunk->chunk_hdr; do { - /* Break out if chunk length is less then minimal. */ + /* Report violation if the chunk is less then minimal */ if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t)) - break; - - ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); - if (ch_end > skb_tail_pointer(skb)) - break; + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Now that we know we at least have a chunk header, + * do things that are type appropriate. + */ if (SCTP_CID_SHUTDOWN_ACK == ch->type) ootb_shut_ack = 1; @@ -3145,6 +3168,12 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, if (SCTP_CID_ABORT == ch->type) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Report violation if chunk len overflows */ + ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); + if (ch_end > skb_tail_pointer(skb)) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + ch = (sctp_chunkhdr_t *) ch_end; } while (ch_end < skb_tail_pointer(skb)); @@ -3244,6 +3273,13 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { + struct sctp_chunk *chunk = arg; + + /* Make sure that the SHUTDOWN_ACK chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Although we do have an association in this case, it corresponds * to a restarted association. So the packet is treated as an OOTB * packet and the state function that handles OOTB SHUTDOWN_ACK is @@ -3658,6 +3694,16 @@ sctp_disposition_t sctp_sf_discard_chunk(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { + struct sctp_chunk *chunk = arg; + + /* Make sure that the chunk has a valid length. + * Since we don't know the chunk type, we use a general + * chunkhdr structure to make a comparison. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk); return SCTP_DISPOSITION_DISCARD; } @@ -3713,6 +3759,13 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, void *arg, sctp_cmd_seq_t *commands) { + struct sctp_chunk *chunk = arg; + + /* Make sure that the chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + return SCTP_DISPOSITION_VIOLATION; } @@ -3720,12 +3773,14 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, * Common function to handle a protocol violation. */ static sctp_disposition_t sctp_sf_abort_violation( + const struct sctp_endpoint *ep, const struct sctp_association *asoc, void *arg, sctp_cmd_seq_t *commands, const __u8 *payload, const size_t paylen) { + struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; struct sctp_chunk *abort = NULL; @@ -3734,30 +3789,51 @@ static sctp_disposition_t sctp_sf_abort_violation( if (!abort) goto nomem; - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); - SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + if (asoc) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); + SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); - if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) { - sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, - SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); - sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, - SCTP_ERROR(ECONNREFUSED)); - sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, - SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION)); + if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNREFUSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION)); + } else { + sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, + SCTP_ERROR(ECONNABORTED)); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION)); + SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + } } else { - sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, - SCTP_ERROR(ECONNABORTED)); - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, - SCTP_PERR(SCTP_ERROR_PROTO_VIOLATION)); - SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + packet = sctp_ootb_pkt_new(asoc, chunk); + + if (!packet) + goto nomem_pkt; + + if (sctp_test_T_bit(abort)) + packet->vtag = ntohl(chunk->sctp_hdr->vtag); + + abort->skb->sk = ep->base.sk; + + sctp_packet_append_chunk(packet, abort); + + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, + SCTP_PACKET(packet)); + + SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); } - sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); + sctp_sf_pdiscard(ep, asoc, SCTP_ST_CHUNK(0), arg, commands); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); return SCTP_DISPOSITION_ABORT; +nomem_pkt: + sctp_chunk_free(abort); nomem: return SCTP_DISPOSITION_NOMEM; } @@ -3790,7 +3866,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( { char err_str[]="The following chunk had invalid length:"; - return sctp_sf_abort_violation(asoc, arg, commands, err_str, + return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, sizeof(err_str)); } @@ -3809,10 +3885,31 @@ static sctp_disposition_t sctp_sf_violation_ctsn( { char err_str[]="The cumulative tsn ack beyond the max tsn currently sent:"; - return sctp_sf_abort_violation(asoc, arg, commands, err_str, + return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, sizeof(err_str)); } +/* Handle protocol violation of an invalid chunk bundling. For example, + * when we have an association and we recieve bundled INIT-ACK, or + * SHUDOWN-COMPLETE, our peer is clearly violationg the "MUST NOT bundle" + * statement from the specs. Additinally, there might be an attacker + * on the path and we may not want to continue this communication. + */ +static sctp_disposition_t sctp_sf_violation_chunk( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + char err_str[]="The following chunk violates protocol:"; + + if (!asoc) + return sctp_sf_violation(ep, asoc, type, arg, commands); + + return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, + sizeof(err_str)); +} /*************************************************************************** * These are the state functions for handling primitive (Section 10) events. ***************************************************************************/ diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 70a91ece3c4..ddb0ba3974b 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -110,7 +110,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -173,7 +173,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -194,7 +194,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -216,7 +216,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_violation), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -258,7 +258,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -300,7 +300,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -499,7 +499,7 @@ static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_ /* SCTP_STATE_EMPTY */ \ TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_CLOSED */ \ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \ + TYPE_SCTP_FUNC(sctp_sf_ootb), \ /* SCTP_STATE_COOKIE_WAIT */ \ TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \ /* SCTP_STATE_COOKIE_ECHOED */ \ @@ -528,7 +528,7 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { /* SCTP_STATE_EMPTY */ TYPE_SCTP_FUNC(sctp_sf_ootb), /* SCTP_STATE_CLOSED */ - TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), + TYPE_SCTP_FUNC(sctp_sf_ootb), /* SCTP_STATE_COOKIE_WAIT */ TYPE_SCTP_FUNC(sctp_sf_unk_chunk), /* SCTP_STATE_COOKIE_ECHOED */ -- cgit v1.2.3-70-g09d2 From 3c77f961b55b6060858c68a213d7f4470d7f3eb2 Mon Sep 17 00:00:00 2001 From: Vlad Yasevich Date: Mon, 17 Sep 2007 15:14:28 -0400 Subject: SCTP: Discard OOTB packetes with bundled INIT early. RFC 4460 and future RFC 4960 (2960-bis) specify that packets with bundled INIT chunks need to be dropped. We currenlty do that only after processing any leading chunks. For OOTB chunks, since we already walk the entire packet, we should discard packets with bundled INITs. There are other chunks chunks that MUST NOT be bundled, but the spec is silent on theire treatment. Thus, we'll leave their teatment alone for the moment. Signed-off-by: Vlad Yasevich Acked-by: Wei Yongjun --- net/sctp/input.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/sctp/input.c b/net/sctp/input.c index 47e56017f4c..f9a0c9276e3 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -622,6 +622,14 @@ static int sctp_rcv_ootb(struct sk_buff *skb) if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type) goto discard; + /* RFC 4460, 2.11.2 + * This will discard packets with INIT chunk bundled as + * subsequent chunks in the packet. When INIT is first, + * the normal INIT processing will discard the chunk. + */ + if (SCTP_CID_INIT == ch->type && (void *)ch != skb->data) + goto discard; + /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR * or a COOKIE ACK the SCTP Packet should be silently * discarded. -- cgit v1.2.3-70-g09d2 From 6f4c618ddb0e6b7e6d49cfc8134e694be1c0bc9b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 19 Sep 2007 17:19:52 +0800 Subject: SCTP : Add paramters validity check for ASCONF chunk If ADDIP is enabled, when an ASCONF chunk is received with ASCONF paramter length set to zero, this will cause infinite loop. By the way, if an malformed ASCONF chunk is received, will cause processing to access memory without verifying. This is because of not check the validity of parameters in ASCONF chunk. This patch fixed this. Signed-off-by: Wei Yongjun Signed-off-by: Vlad Yasevich --- include/net/sctp/sm.h | 3 +++ include/net/sctp/structs.h | 1 + net/sctp/sm_make_chunk.c | 46 ++++++++++++++++++++++++++++++++++++++++++ net/sctp/sm_statefuns.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) (limited to 'net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index cc71f366d1c..e8e3a64eb32 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -246,6 +246,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, int, __be16); struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr); +int sctp_verify_asconf(const struct sctp_association *asoc, + struct sctp_paramhdr *param_hdr, void *chunk_end, + struct sctp_paramhdr **errp); struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *asconf); int sctp_process_asconf_ack(struct sctp_association *asoc, diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c2fe2dcc9af..490a2928817 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -421,6 +421,7 @@ struct sctp_signed_cookie { * internally. */ union sctp_addr_param { + struct sctp_paramhdr p; struct sctp_ipv4addr_param v4; struct sctp_ipv6addr_param v6; }; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 2e34220d94c..23ae37ec871 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2499,6 +2499,52 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, return SCTP_ERROR_NO_ERROR; } +/* Verify the ASCONF packet before we process it. */ +int sctp_verify_asconf(const struct sctp_association *asoc, + struct sctp_paramhdr *param_hdr, void *chunk_end, + struct sctp_paramhdr **errp) { + sctp_addip_param_t *asconf_param; + union sctp_params param; + int length, plen; + + param.v = (sctp_paramhdr_t *) param_hdr; + while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) { + length = ntohs(param.p->length); + *errp = param.p; + + if (param.v > chunk_end - length || + length < sizeof(sctp_paramhdr_t)) + return 0; + + switch (param.p->type) { + case SCTP_PARAM_ADD_IP: + case SCTP_PARAM_DEL_IP: + case SCTP_PARAM_SET_PRIMARY: + asconf_param = (sctp_addip_param_t *)param.v; + plen = ntohs(asconf_param->param_hdr.length); + if (plen < sizeof(sctp_addip_param_t) + + sizeof(sctp_paramhdr_t)) + return 0; + break; + case SCTP_PARAM_SUCCESS_REPORT: + case SCTP_PARAM_ADAPTATION_LAYER_IND: + if (length != sizeof(sctp_addip_param_t)) + return 0; + + break; + default: + break; + } + + param.v += WORD_ROUND(length); + } + + if (param.v != chunk_end) + return 0; + + return 1; +} + /* Process an incoming ASCONF chunk with the next expected serial no. and * return an ASCONF_ACK chunk to be sent in response. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index caed19d90d0..a583d67cab6 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -117,6 +117,13 @@ static sctp_disposition_t sctp_sf_violation_chunklen( void *arg, sctp_cmd_seq_t *commands); +static sctp_disposition_t sctp_sf_violation_paramlen( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands); + static sctp_disposition_t sctp_sf_violation_ctsn( const struct sctp_endpoint *ep, const struct sctp_association *asoc, @@ -3296,8 +3303,11 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, { struct sctp_chunk *chunk = arg; struct sctp_chunk *asconf_ack = NULL; + struct sctp_paramhdr *err_param = NULL; sctp_addiphdr_t *hdr; + union sctp_addr_param *addr_param; __u32 serial; + int length; if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, @@ -3313,6 +3323,20 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, hdr = (sctp_addiphdr_t *)chunk->skb->data; serial = ntohl(hdr->serial); + addr_param = (union sctp_addr_param *)hdr->params; + length = ntohs(addr_param->p.length); + if (length < sizeof(sctp_paramhdr_t)) + return sctp_sf_violation_paramlen(ep, asoc, type, + (void *)addr_param, commands); + + /* Verify the ASCONF chunk before processing it. */ + if (!sctp_verify_asconf(asoc, + (sctp_paramhdr_t *)((void *)addr_param + length), + (void *)chunk->chunk_end, + &err_param)) + return sctp_sf_violation_paramlen(ep, asoc, type, + (void *)&err_param, commands); + /* ADDIP 4.2 C1) Compare the value of the serial number to the value * the endpoint stored in a new association variable * 'Peer-Serial-Number'. @@ -3367,6 +3391,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, struct sctp_chunk *asconf_ack = arg; struct sctp_chunk *last_asconf = asoc->addip_last_asconf; struct sctp_chunk *abort; + struct sctp_paramhdr *err_param = NULL; sctp_addiphdr_t *addip_hdr; __u32 sent_serial, rcvd_serial; @@ -3384,6 +3409,14 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data; rcvd_serial = ntohl(addip_hdr->serial); + /* Verify the ASCONF-ACK chunk before processing it. */ + if (!sctp_verify_asconf(asoc, + (sctp_paramhdr_t *)addip_hdr->params, + (void *)asconf_ack->chunk_end, + &err_param)) + return sctp_sf_violation_paramlen(ep, asoc, type, + (void *)&err_param, commands); + if (last_asconf) { addip_hdr = (sctp_addiphdr_t *)last_asconf->subh.addip_hdr; sent_serial = ntohl(addip_hdr->serial); @@ -3870,6 +3903,23 @@ static sctp_disposition_t sctp_sf_violation_chunklen( sizeof(err_str)); } +/* + * Handle a protocol violation when the parameter length is invalid. + * "Invalid" length is identified as smaller then the minimal length a + * given parameter can be. + */ +static sctp_disposition_t sctp_sf_violation_paramlen( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) { + char err_str[] = "The following parameter had invalid length:"; + + return sctp_sf_abort_violation(ep, asoc, arg, commands, err_str, + sizeof(err_str)); +} + /* Handle a protocol violation when the peer trying to advance the * cumulative tsn ack to a point beyond the max tsn currently sent. * -- cgit v1.2.3-70-g09d2 From 78bd8fbbcd66fc977baa40e7fd838a4461b0f727 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 26 Sep 2007 01:54:32 +0100 Subject: fix sctp_del_bind_addr() last argument type It gets pointer to fastcall function, expects a pointer to normal one and calls the sucker. Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- include/net/sctp/structs.h | 2 +- net/sctp/bind_addr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 490a2928817..baff49dfcdb 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1157,7 +1157,7 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest, int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, __u8 use_as_src, gfp_t gfp); int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *, - void (*rcu_call)(struct rcu_head *, + void fastcall (*rcu_call)(struct rcu_head *, void (*func)(struct rcu_head *))); int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, struct sctp_sock *); diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index d35cbf5aae3..dfffa94fb9f 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -181,7 +181,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, * structure. */ int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr, - void (*rcu_call)(struct rcu_head *head, + void fastcall (*rcu_call)(struct rcu_head *head, void (*func)(struct rcu_head *head))) { struct sctp_sockaddr_entry *addr, *temp; -- cgit v1.2.3-70-g09d2 From e79ad711a0108475c1b3a03815527e7237020b08 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 27 Sep 2007 13:52:00 -0700 Subject: [NET]: Zero length write() on socket should not simply return 0. This fixes kernel bugzilla #5731 It should generate an empty packet for datagram protocols when the socket is connected, for one. The check is doubly-wrong because all that a write() can be is a sendmsg() call with a NULL msg_control and a single entry iovec. No special semantics should be assigned to it, therefore the zero length check should be removed entirely. This matches the behavior of BSD and several other systems. Alan Cox notes that SuSv3 says the behavior of a zero length write on non-files is "unspecified", but that's kind of useless since BSD has defined this behavior for a quarter century and BSD is essentially what application folks code to. Based upon a patch from Stephen Hemminger. Signed-off-by: David S. Miller --- net/socket.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/socket.c b/net/socket.c index 7d44453dfae..b09eb9036a1 100644 --- a/net/socket.c +++ b/net/socket.c @@ -777,9 +777,6 @@ static ssize_t sock_aio_write(struct kiocb *iocb, const struct iovec *iov, if (pos != 0) return -ESPIPE; - if (iocb->ki_left == 0) /* Match SYS5 behaviour */ - return 0; - x = alloc_sock_iocb(iocb, &siocb); if (!x) return -ENOMEM; -- cgit v1.2.3-70-g09d2 From f8ab18d2d987a59ccbf0495032b2aef05b730037 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 28 Sep 2007 15:18:35 -0700 Subject: [TCP]: Fix MD5 signature handling on big-endian. Based upon a report and initial patch by Peter Lieven. tcp4_md5sig_key and tcp6_md5sig_key need to start with the exact same members as tcp_md5sig_key. Because they are both cast to that type by tcp_v{4,6}_md5_do_lookup(). Unfortunately tcp{4,6}_md5sig_key use a u16 for the key length instead of a u8, which is what tcp_md5sig_key uses. This just so happens to work by accident on little-endian, but on big-endian it doesn't. Instead of casting, just place tcp_md5sig_key as the first member of the address-family specific structures, adjust the access sites, and kill off the ugly casts. Signed-off-by: David S. Miller --- include/net/tcp.h | 6 ++---- net/ipv4/tcp_ipv4.c | 19 +++++++++---------- net/ipv6/tcp_ipv6.c | 18 +++++++++--------- 3 files changed, 20 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 185c7ecce4c..54053de0bdd 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1059,14 +1059,12 @@ struct tcp_md5sig_key { }; struct tcp4_md5sig_key { - u8 *key; - u16 keylen; + struct tcp_md5sig_key base; __be32 addr; }; struct tcp6_md5sig_key { - u8 *key; - u16 keylen; + struct tcp_md5sig_key base; #if 0 u32 scope_id; /* XXX */ #endif diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 9c94627c8c7..e089a978e12 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -833,8 +833,7 @@ static struct tcp_md5sig_key * return NULL; for (i = 0; i < tp->md5sig_info->entries4; i++) { if (tp->md5sig_info->keys4[i].addr == addr) - return (struct tcp_md5sig_key *) - &tp->md5sig_info->keys4[i]; + return &tp->md5sig_info->keys4[i].base; } return NULL; } @@ -865,9 +864,9 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, key = (struct tcp4_md5sig_key *)tcp_v4_md5_do_lookup(sk, addr); if (key) { /* Pre-existing entry - just update that one. */ - kfree(key->key); - key->key = newkey; - key->keylen = newkeylen; + kfree(key->base.key); + key->base.key = newkey; + key->base.keylen = newkeylen; } else { struct tcp_md5sig_info *md5sig; @@ -906,9 +905,9 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, md5sig->alloced4++; } md5sig->entries4++; - md5sig->keys4[md5sig->entries4 - 1].addr = addr; - md5sig->keys4[md5sig->entries4 - 1].key = newkey; - md5sig->keys4[md5sig->entries4 - 1].keylen = newkeylen; + md5sig->keys4[md5sig->entries4 - 1].addr = addr; + md5sig->keys4[md5sig->entries4 - 1].base.key = newkey; + md5sig->keys4[md5sig->entries4 - 1].base.keylen = newkeylen; } return 0; } @@ -930,7 +929,7 @@ int tcp_v4_md5_do_del(struct sock *sk, __be32 addr) for (i = 0; i < tp->md5sig_info->entries4; i++) { if (tp->md5sig_info->keys4[i].addr == addr) { /* Free the key */ - kfree(tp->md5sig_info->keys4[i].key); + kfree(tp->md5sig_info->keys4[i].base.key); tp->md5sig_info->entries4--; if (tp->md5sig_info->entries4 == 0) { @@ -964,7 +963,7 @@ static void tcp_v4_clear_md5_list(struct sock *sk) if (tp->md5sig_info->entries4) { int i; for (i = 0; i < tp->md5sig_info->entries4; i++) - kfree(tp->md5sig_info->keys4[i].key); + kfree(tp->md5sig_info->keys4[i].base.key); tp->md5sig_info->entries4 = 0; tcp_free_md5sig_pool(); } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 0f7defb482e..3e06799b37a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -539,7 +539,7 @@ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, for (i = 0; i < tp->md5sig_info->entries6; i++) { if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, addr) == 0) - return (struct tcp_md5sig_key *)&tp->md5sig_info->keys6[i]; + return &tp->md5sig_info->keys6[i].base; } return NULL; } @@ -567,9 +567,9 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer, key = (struct tcp6_md5sig_key*) tcp_v6_md5_do_lookup(sk, peer); if (key) { /* modify existing entry - just update that one */ - kfree(key->key); - key->key = newkey; - key->keylen = newkeylen; + kfree(key->base.key); + key->base.key = newkey; + key->base.keylen = newkeylen; } else { /* reallocate new list if current one is full. */ if (!tp->md5sig_info) { @@ -603,8 +603,8 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer, ipv6_addr_copy(&tp->md5sig_info->keys6[tp->md5sig_info->entries6].addr, peer); - tp->md5sig_info->keys6[tp->md5sig_info->entries6].key = newkey; - tp->md5sig_info->keys6[tp->md5sig_info->entries6].keylen = newkeylen; + tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.key = newkey; + tp->md5sig_info->keys6[tp->md5sig_info->entries6].base.keylen = newkeylen; tp->md5sig_info->entries6++; } @@ -626,7 +626,7 @@ static int tcp_v6_md5_do_del(struct sock *sk, struct in6_addr *peer) for (i = 0; i < tp->md5sig_info->entries6; i++) { if (ipv6_addr_cmp(&tp->md5sig_info->keys6[i].addr, peer) == 0) { /* Free the key */ - kfree(tp->md5sig_info->keys6[i].key); + kfree(tp->md5sig_info->keys6[i].base.key); tp->md5sig_info->entries6--; if (tp->md5sig_info->entries6 == 0) { @@ -657,7 +657,7 @@ static void tcp_v6_clear_md5_list (struct sock *sk) if (tp->md5sig_info->entries6) { for (i = 0; i < tp->md5sig_info->entries6; i++) - kfree(tp->md5sig_info->keys6[i].key); + kfree(tp->md5sig_info->keys6[i].base.key); tp->md5sig_info->entries6 = 0; tcp_free_md5sig_pool(); } @@ -668,7 +668,7 @@ static void tcp_v6_clear_md5_list (struct sock *sk) if (tp->md5sig_info->entries4) { for (i = 0; i < tp->md5sig_info->entries4; i++) - kfree(tp->md5sig_info->keys4[i].key); + kfree(tp->md5sig_info->keys4[i].base.key); tp->md5sig_info->entries4 = 0; tcp_free_md5sig_pool(); } -- cgit v1.2.3-70-g09d2 From 32740ddc1095e5e320bf961dda146bf97bc28adb Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Sun, 30 Sep 2007 17:51:33 -0700 Subject: [SFQ]: Remove artificial limitation for queue limit. This is followup to Patrick's patch. A little optimization to enqueue routine allows to remove artificial limitation on queue length. Plus, testing showed that hash function used by SFQ is too bad or even worse. It does not even sweep the whole range of hash values. Switched to Jenkins' hash. Signed-off-by: Alexey Kuznetsov Signed-off-by: David S. Miller --- net/sched/sch_sfq.c | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 3a23e30bc79..b542c875e15 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -95,7 +96,7 @@ struct sfq_sched_data /* Variables */ struct timer_list perturb_timer; - int perturbation; + u32 perturbation; sfq_index tail; /* Index of current slot in round */ sfq_index max_depth; /* Maximal depth */ @@ -109,12 +110,7 @@ struct sfq_sched_data static __inline__ unsigned sfq_fold_hash(struct sfq_sched_data *q, u32 h, u32 h1) { - int pert = q->perturbation; - - /* Have we any rotation primitives? If not, WHY? */ - h ^= (h1<>(0x1F - pert)); - h ^= h>>10; - return h & 0x3FF; + return jhash_2words(h, h1, q->perturbation) & (SFQ_HASH_DIVISOR - 1); } static unsigned sfq_hash(struct sfq_sched_data *q, struct sk_buff *skb) @@ -256,6 +252,13 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc* sch) q->ht[hash] = x = q->dep[SFQ_DEPTH].next; q->hash[x] = hash; } + /* If selected queue has length q->limit, this means that + * all another queues are empty and that we do simple tail drop, + * i.e. drop _this_ packet. + */ + if (q->qs[x].qlen >= q->limit) + return qdisc_drop(skb, sch); + sch->qstats.backlog += skb->len; __skb_queue_tail(&q->qs[x], skb); sfq_inc(q, x); @@ -294,6 +297,19 @@ sfq_requeue(struct sk_buff *skb, struct Qdisc* sch) } sch->qstats.backlog += skb->len; __skb_queue_head(&q->qs[x], skb); + /* If selected queue has length q->limit+1, this means that + * all another queues are empty and we do simple tail drop. + * This packet is still requeued at head of queue, tail packet + * is dropped. + */ + if (q->qs[x].qlen > q->limit) { + skb = q->qs[x].prev; + __skb_unlink(skb, &q->qs[x]); + sch->qstats.drops++; + sch->qstats.backlog -= skb->len; + kfree_skb(skb); + return NET_XMIT_CN; + } sfq_inc(q, x); if (q->qs[x].qlen == 1) { /* The flow is new */ if (q->tail == SFQ_DEPTH) { /* It is the first flow */ @@ -370,12 +386,10 @@ static void sfq_perturbation(unsigned long arg) struct Qdisc *sch = (struct Qdisc*)arg; struct sfq_sched_data *q = qdisc_priv(sch); - q->perturbation = net_random()&0x1F; + get_random_bytes(&q->perturbation, 4); - if (q->perturb_period) { - q->perturb_timer.expires = jiffies + q->perturb_period; - add_timer(&q->perturb_timer); - } + if (q->perturb_period) + mod_timer(&q->perturb_timer, jiffies + q->perturb_period); } static int sfq_change(struct Qdisc *sch, struct rtattr *opt) @@ -391,7 +405,7 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) q->quantum = ctl->quantum ? : psched_mtu(sch->dev); q->perturb_period = ctl->perturb_period*HZ; if (ctl->limit) - q->limit = min_t(u32, ctl->limit, SFQ_DEPTH - 2); + q->limit = min_t(u32, ctl->limit, SFQ_DEPTH - 1); qlen = sch->q.qlen; while (sch->q.qlen > q->limit) @@ -400,8 +414,8 @@ static int sfq_change(struct Qdisc *sch, struct rtattr *opt) del_timer(&q->perturb_timer); if (q->perturb_period) { - q->perturb_timer.expires = jiffies + q->perturb_period; - add_timer(&q->perturb_timer); + mod_timer(&q->perturb_timer, jiffies + q->perturb_period); + get_random_bytes(&q->perturbation, 4); } sch_tree_unlock(sch); return 0; @@ -423,12 +437,13 @@ static int sfq_init(struct Qdisc *sch, struct rtattr *opt) q->dep[i+SFQ_DEPTH].next = i+SFQ_DEPTH; q->dep[i+SFQ_DEPTH].prev = i+SFQ_DEPTH; } - q->limit = SFQ_DEPTH - 2; + q->limit = SFQ_DEPTH - 1; q->max_depth = 0; q->tail = SFQ_DEPTH; if (opt == NULL) { q->quantum = psched_mtu(sch->dev); q->perturb_period = 0; + get_random_bytes(&q->perturbation, 4); } else { int err = sfq_change(sch, opt); if (err) -- cgit v1.2.3-70-g09d2 From 04045f98e0457aba7d4e6736f37eed189c48a5f7 Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 1 Oct 2007 21:03:54 -0700 Subject: [IEEE80211]: avoid integer underflow for runt rx frames Reported by Chris Evans : > The summary is that an evil 80211 frame can crash out a victim's > machine. It only applies to drivers using the 80211 wireless code, and > only then to certain drivers (and even then depends on a card's > firmware not dropping a dubious packet). I must confess I'm not > keeping track of Linux wireless support, and the different protocol > stacks etc. > > Details are as follows: > > ieee80211_rx() does not explicitly check that "skb->len >= hdrlen". > There are other skb->len checks, but not enough to prevent a subtle > off-by-two error if the frame has the IEEE80211_STYPE_QOS_DATA flag > set. > > This leads to integer underflow and crash here: > > if (frag != 0) > flen -= hdrlen; > > (flen is subsequently used as a memcpy length parameter). How about this? Signed-off-by: John W. Linville Signed-off-by: David S. Miller --- net/ieee80211/ieee80211_rx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index f2de2e48b02..6284c99b456 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -366,6 +366,12 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, frag = WLAN_GET_SEQ_FRAG(sc); hdrlen = ieee80211_get_hdrlen(fc); + if (skb->len < hdrlen) { + printk(KERN_INFO "%s: invalid SKB length %d\n", + dev->name, skb->len); + goto rx_dropped; + } + /* Put this code here so that we avoid duplicating it in all * Rx paths. - Jean II */ #ifdef CONFIG_WIRELESS_EXT -- cgit v1.2.3-70-g09d2