[prev in list] [next in list] [prev in thread] [next in thread] 

List:       linux-kernel
Subject:    Re: Serious BUG in scm_send() [NET] causes total freeze
From:       kuznet () ms2 ! inr ! ac ! ru
Date:       1999-04-16 19:07:27
[Download RAW message or body]

Hello!

> it would happily overwrite our stack, provided the user mem is
> readable) and _negative_ cmsg_len 

No, it is protected against this. This problem is that you have zero cmsg_len
and it takes infinite time to "parse" such messsage 8)8)

The patch is enclosed (this problem is in core/scm.c, ipv4/ip_sockglue.c
and ipv6/datagram.c)

Alexey


diff -ur --new-file ../orig/linux/include/net/dst.h linux/include/net/dst.h
--- ../orig/linux/include/net/dst.h	Sun Mar 21 17:28:11 1999
+++ linux/include/net/dst.h	Fri Apr 16 22:07:59 1999
@@ -16,11 +16,7 @@
  * 1 - rare events and bugs (default)
  * 2 - trace mode.
  */
-#ifdef  NO_ANK_FIX
 #define RT_CACHE_DEBUG		0
-#else
-#define RT_CACHE_DEBUG		1
-#endif
 
 #define DST_GC_MIN	(1*HZ)
 #define DST_GC_INC	(5*HZ)
diff -ur --new-file ../orig/linux/include/net/pkt_sched.h linux/include/net/pkt_sched.h
--- ../orig/linux/include/net/pkt_sched.h	Sun Mar 21 17:28:20 1999
+++ linux/include/net/pkt_sched.h	Mon Apr  5 21:23:33 1999
@@ -215,6 +215,8 @@
    (stamp) = __cur>>psched_clock_scale; \
 })
 
+#define PSCHED_EXPORTLIST_1
+
 #elif defined (__alpha__)
 
 #define PSCHED_WATCHER u32
diff -ur --new-file ../orig/linux/include/net/route.h linux/include/net/route.h
--- ../orig/linux/include/net/route.h	Sun Mar 21 17:28:26 1999
+++ linux/include/net/route.h	Sat Apr 10 21:42:07 1999
@@ -114,6 +114,7 @@
 extern int		ip_route_output(struct rtable **, u32 dst, u32 src, u32 tos, int oif);
 extern int		ip_route_input(struct sk_buff*, u32 dst, u32 src, u8 tos, struct device *devin);
 extern unsigned short	ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu);
+extern void		ip_rt_update_pmtu(struct dst_entry *dst, unsigned mtu);
 extern void		ip_rt_send_redirect(struct sk_buff *skb);
 
 extern unsigned		inet_addr_type(u32 addr);
diff -ur --new-file ../orig/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- ../orig/linux/net/core/neighbour.c	Mon Apr  5 19:35:50 1999
+++ linux/net/core/neighbour.c	Thu Apr 15 19:43:39 1999
@@ -1170,7 +1170,7 @@
 	for (h=0; h <= NEIGH_HASHMASK; h++) {
 		if (h < s_h) continue;
 		if (h > s_h)
-			memset(&cb->args[2], 0, sizeof(cb->args) - 2*sizeof(cb->args[0]));
+			s_idx = 0;
 		start_bh_atomic();
 		for (n = tbl->hash_buckets[h], idx = 0; n;
 		     n = n->next, idx++) {
@@ -1179,12 +1179,14 @@
 			if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).pid,
 					    cb->nlh->nlmsg_seq, RTM_NEWNEIGH) <= 0) {
 				end_bh_atomic();
-				goto done;
+				cb->args[1] = h;
+				cb->args[2] = idx;
+				return -1;
 			}
 		}
 		end_bh_atomic();
 	}
-done:
+
 	cb->args[1] = h;
 	cb->args[2] = idx;
 	return skb->len;
@@ -1355,10 +1357,10 @@
 		t->neigh_dev[0].ctl_name = dev->ifindex;
 		memset(&t->neigh_vars[12], 0, sizeof(ctl_table));
 	} else {
-		t->neigh_vars[12].data = (&p->locktime) + 1;
-		t->neigh_vars[13].data = (&p->locktime) + 2;
-		t->neigh_vars[14].data = (&p->locktime) + 3;
-		t->neigh_vars[15].data = (&p->locktime) + 4;
+		t->neigh_vars[12].data = (int*)(p+1);
+		t->neigh_vars[13].data = (int*)(p+1) + 1;
+		t->neigh_vars[14].data = (int*)(p+1) + 2;
+		t->neigh_vars[15].data = (int*)(p+1) + 3;
 	}
 	t->neigh_neigh_dir[0].ctl_name = pdev_id;
 
diff -ur --new-file ../orig/linux/net/core/scm.c linux/net/core/scm.c
--- ../orig/linux/net/core/scm.c	Tue Mar  9 22:35:01 1999
+++ linux/net/core/scm.c	Fri Apr 16 22:02:24 1999
@@ -122,7 +122,15 @@
 		err = -EINVAL;
 
 		/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
-		if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+		/* The first check was omitted in <= 2.2.5. The reasoning was
+		   that parser checks cmsg_len in any case, so that
+		   additional check would be work duplication.
+		   But if cmsg_level is not SOL_SOCKET, we do not check 
+		   for too short ancillary data object at all! Oops.
+		   OK, let's add it...
+		 */
+		if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+		    (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
 				    + cmsg->cmsg_len) > msg->msg_controllen)
 			goto error;
 
diff -ur --new-file ../orig/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- ../orig/linux/net/ipv4/af_inet.c	Mon Apr  5 19:35:51 1999
+++ linux/net/ipv4/af_inet.c	Thu Apr 15 21:03:39 1999
@@ -513,17 +513,6 @@
 	    (sk->num != 0))
 		return -EINVAL;
 		
-	snum = ntohs(addr->sin_port);
-#ifdef CONFIG_IP_MASQUERADE
-	/* The kernel masquerader needs some ports. */
-	if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
-		return -EADDRINUSE;
-#endif		 
-	if (snum == 0) 
-		snum = sk->prot->good_socknum();
-	if (snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
-		return(-EACCES);
-	
 	chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
 	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != RTN_LOCAL &&
 	    chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST) {
@@ -545,6 +534,17 @@
 	if(chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
 		sk->saddr = 0;  /* Use device */
 
+	snum = ntohs(addr->sin_port);
+#ifdef CONFIG_IP_MASQUERADE
+	/* The kernel masquerader needs some ports. */
+	if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
+		return -EADDRINUSE;
+#endif		 
+	if (snum == 0) 
+		snum = sk->prot->good_socknum();
+	if (snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+		return(-EACCES);
+	
 	/* Make sure we are allowed to bind here. */
 	if(sk->prot->verify_bind(sk, snum))
 		return -EADDRINUSE;
diff -ur --new-file ../orig/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- ../orig/linux/net/ipv4/ip_sockglue.c	Mon Apr  5 19:35:52 1999
+++ linux/net/ipv4/ip_sockglue.c	Fri Apr 16 22:04:36 1999
@@ -150,7 +150,8 @@
 	struct cmsghdr *cmsg;
 
 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-		if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+		if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+		    (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
 				    + cmsg->cmsg_len) > msg->msg_controllen) {
 			return -EINVAL;
 		}
diff -ur --new-file ../orig/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- ../orig/linux/net/ipv4/route.c	Mon Apr  5 19:35:53 1999
+++ linux/net/ipv4/route.c	Thu Apr 15 19:43:39 1999
@@ -281,6 +281,9 @@
 	if (atomic_read(&rth->u.dst.use))
 		return 0;
 
+	if (rth->u.dst.expires && (long)(rth->u.dst.expires - jiffies) <= 0)
+		return 1;
+
 	age = jiffies - rth->u.dst.lastuse;
 	if (age <= tmo1 && !rt_fast_clean(rth))
 		return 0;
@@ -305,7 +308,7 @@
 		while ((rth = *rthp) != NULL) {
 			if (rth->u.dst.expires) {
 				/* Entrie is expired even if it is in use */
-				if ((long)(now - rth->u.dst.expires) < tmo) {
+				if ((long)(now - rth->u.dst.expires) <= 0) {
 					tmo >>= 1;
 					rthp = &rth->u.rt_next;
 					continue;
@@ -564,8 +567,11 @@
 			 */
 			if (attempts-- > 0) {
 				int saved_elasticity = ip_rt_gc_elasticity;
+				int saved_int = ip_rt_gc_min_interval;
 				ip_rt_gc_elasticity = 1;
+				ip_rt_gc_min_interval = 0;
 				rt_garbage_collect();
+				ip_rt_gc_min_interval = saved_int;
 				ip_rt_gc_elasticity = saved_elasticity;
 				goto restart;
 			}
@@ -885,7 +891,16 @@
 			}
 		}
 	}
-	return est_mtu;
+	return est_mtu ? : new_mtu;
+}
+
+void ip_rt_update_pmtu(struct dst_entry *dst, unsigned mtu)
+{
+	if (dst->pmtu > mtu && mtu >= 68 &&
+	    !(dst->mxlock&(1<<RTAX_MTU))) {
+		dst->pmtu = mtu;
+		dst_set_expires(dst, ip_rt_mtu_expires);
+	}
 }
 
 static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32 cookie)
@@ -1850,7 +1865,7 @@
 	for (h=0; h < RT_HASH_DIVISOR; h++) {
 		if (h < s_h) continue;
 		if (h > s_h)
-			memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(cb->args[0]));
+			s_idx = 0;
 		start_bh_atomic();
 		for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
 			if (idx < s_idx)
diff -ur --new-file ../orig/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- ../orig/linux/net/ipv4/tcp_ipv4.c	Mon Apr  5 19:35:54 1999
+++ linux/net/ipv4/tcp_ipv4.c	Thu Apr 15 22:43:53 1999
@@ -629,6 +629,7 @@
 
 	if (!tcp_v4_unique_address(sk)) {
 		kfree_skb(buff);
+		sk->daddr = 0;
 		return -EADDRNOTAVAIL;
 	}
 
@@ -722,7 +723,7 @@
 /* 
  * This routine does path mtu discovery as defined in RFC1191.
  */
-static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip)
+static inline void do_pmtu_discovery(struct sock *sk, struct iphdr *ip, unsigned mtu)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
@@ -742,8 +743,10 @@
      	 * There is a small race when the user changes this flag in the
 	 * route, but I think that's acceptable.
 	 */
-	if (sk->dst_cache &&
-	    sk->ip_pmtudisc != IP_PMTUDISC_DONT &&
+	if (sk->dst_cache == NULL)
+		return;
+	ip_rt_update_pmtu(sk->dst_cache, mtu);
+	if (sk->ip_pmtudisc != IP_PMTUDISC_DONT &&
 	    tp->pmtu_cookie > sk->dst_cache->pmtu) {
 		tcp_sync_mss(sk, sk->dst_cache->pmtu);
 
@@ -830,7 +833,7 @@
 			return;
 
 		if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
-			do_pmtu_discovery(sk, iph); 
+			do_pmtu_discovery(sk, iph, ntohs(skb->h.icmph->un.frag.mtu));
 			return;
 		}
 
@@ -1595,6 +1598,7 @@
 		 * the new socket..
 		 */
 		if (atomic_read(&nsk->sock_readers)) {
+			skb_orphan(skb);
 			__skb_queue_tail(&nsk->back_log, skb);
 			return 0;
 		}
diff -ur --new-file ../orig/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- ../orig/linux/net/ipv6/af_inet6.c	Sat Jan 30 18:27:58 1999
+++ linux/net/ipv6/af_inet6.c	Thu Apr 15 21:03:39 1999
@@ -190,7 +190,7 @@
 	struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
 	struct sock *sk = sock->sk;
 	__u32 v4addr = 0;
-	unsigned short snum = 0;
+	unsigned short snum;
 	int addr_type = 0;
 
 	/* If the socket has its own bind function then use it. */
@@ -203,12 +203,6 @@
 	    (sk->num != 0))
 		return -EINVAL;
 		
-	snum = ntohs(addr->sin6_port);
-	if (snum == 0) 
-		snum = sk->prot->good_socknum();
-	if (snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
-		return(-EACCES);
-	
 	addr_type = ipv6_addr_type(&addr->sin6_addr);
 	if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
 		return(-EINVAL);
@@ -241,6 +235,12 @@
 		memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, 
 		       sizeof(struct in6_addr));
 
+	snum = ntohs(addr->sin6_port);
+	if (snum == 0) 
+		snum = sk->prot->good_socknum();
+	if (snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+		return(-EACCES);
+
 	/* Make sure we are allowed to bind here. */
 	if(sk->prot->verify_bind(sk, snum))
 		return -EADDRINUSE;
diff -ur --new-file ../orig/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- ../orig/linux/net/ipv6/datagram.c	Sat Oct  3 20:17:46 1998
+++ linux/net/ipv6/datagram.c	Fri Apr 16 22:04:49 1999
@@ -235,17 +243,15 @@
 
 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
 
-		if ((unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+		if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
+		    (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
 				    + cmsg->cmsg_len) > msg->msg_controllen) {
 			err = -EINVAL;
 			goto exit_f;
 		}
 
-		if (cmsg->cmsg_level != SOL_IPV6) {
-			if (net_ratelimit())
-				printk(KERN_DEBUG "invalid cmsg_level %d\n", cmsg->cmsg_level);
+		if (cmsg->cmsg_level != SOL_IPV6)
 			continue;
-		}
 
 		switch (cmsg->cmsg_type) {
  		case IPV6_PKTINFO:
diff -ur --new-file ../orig/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- ../orig/linux/net/ipv6/ip6_fib.c	Sun Mar 21 17:33:49 1999
+++ linux/net/ipv6/ip6_fib.c	Sun Apr 11 22:20:47 1999
@@ -890,9 +890,6 @@
 
 	RT6_TRACE("fib6_del_route\n");
 
-	if (!(rt->rt6i_flags&RTF_CACHE))
-		fib6_prune_clones(fn, rt);
-
 	/* Unlink it */
 	*rtp = rt->u.next;
 	rt->rt6i_node = NULL;
@@ -938,6 +935,9 @@
 		return -ENOENT;
 
 	BUG_TRAP(fn->fn_flags&RTN_RTINFO);
+
+	if (!(rt->rt6i_flags&RTF_CACHE))
+		fib6_prune_clones(fn, rt);
 
 	/*
 	 *	Walk the leaf entries looking for ourself
diff -ur --new-file ../orig/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- ../orig/linux/net/ipv6/ndisc.c	Sun Mar 21 17:33:56 1999
+++ linux/net/ipv6/ndisc.c	Sun Apr 11 20:51:45 1999
@@ -813,7 +813,7 @@
 		}
 	}
 
-	rd_len = min(IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, ntohs(skb->nh.ipv6h->payload_len) + 8);
+	rd_len = min(IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);
 	rd_len &= ~0x7;
 	len += rd_len;
 
@@ -873,7 +873,7 @@
 	*(opt++) = (rd_len >> 3);
 	opt += 6;
 
-	memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
+	memcpy(opt, skb->nh.ipv6h, rd_len - 8);
 
 	icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
 					     len, IPPROTO_ICMPV6,
diff -ur --new-file ../orig/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- ../orig/linux/net/ipv6/tcp_ipv6.c	Mon Apr  5 19:35:57 1999
+++ linux/net/ipv6/tcp_ipv6.c	Thu Apr 15 22:47:24 1999
@@ -445,6 +459,7 @@
 			tp->ext_header_len = exthdrlen;
 			sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
 			sk->backlog_rcv = tcp_v6_do_rcv;
+			goto failure;
 		} else {
 			ipv6_addr_set(&np->saddr, 0, 0, __constant_htonl(0x0000FFFF),
 				      sk->saddr);
@@ -474,7 +489,7 @@
 
 	if ((err = dst->error) != 0) {
 		dst_release(dst);
-		return err;
+		goto failure;
 	}
 
 	if (fl.oif == 0 && addr_type&IPV6_ADDR_LINKLOCAL) {
@@ -491,7 +506,7 @@
 	if (saddr == NULL) {
 		err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf);
 		if (err)
-			return err;
+			goto failure;
 
 		saddr = &saddr_buf;
 	}
@@ -506,17 +521,19 @@
 	/* Reset mss clamp */
 	tp->mss_clamp = ~0;
 
+	err = -ENOBUFS;
 	buff = sock_wmalloc(sk, (MAX_HEADER + sk->prot->max_header),
 			    0, GFP_KERNEL);
 
 	if (buff == NULL)
-		return -ENOBUFS;
+		goto failure;
 
 	sk->dport = usin->sin6_port;
 
 	if (!tcp_v6_unique_address(sk)) {
 		kfree_skb(buff);
-		return -EADDRNOTAVAIL;
+		err = -EADDRNOTAVAIL;
+		goto failure;
 	}
 
 	/*
@@ -530,6 +547,12 @@
 	tcp_connect(sk, buff, dst->pmtu);
 
 	return 0;
+
+failure:
+	dst_release(xchg(&sk->dst_cache, NULL));
+	memcpy(&np->daddr, 0, sizeof(struct in6_addr));
+	sk->daddr = 0;
+	return err;
 }
 
 static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
@@ -1256,6 +1286,7 @@
 		 * the new socket..
 		 */
 		if (atomic_read(&nsk->sock_readers)) {
+			skb_orphan(skb);
 			__skb_queue_tail(&nsk->back_log, skb);
 			return 0;
 		}
diff -ur --new-file ../orig/linux/net/sched/cls_fw.c linux/net/sched/cls_fw.c
--- ../orig/linux/net/sched/cls_fw.c	Mon Apr  5 19:35:59 1999
+++ linux/net/sched/cls_fw.c	Thu Apr 15 19:36:32 1999
@@ -7,6 +7,10 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_walk off by one
+ * Karlis Peisenieks <karlis@mt.lv> : 990415 : fw_delete killed all the filter (and kernel).
  */
 
 #include <linux/config.h>
@@ -146,7 +150,7 @@
 
 static int fw_delete(struct tcf_proto *tp, unsigned long arg)
 {
-	struct fw_head *head = (struct fw_head*)xchg(&tp->root, NULL);
+	struct fw_head *head = (struct fw_head*)tp->root;
 	struct fw_filter *f = (struct fw_filter*)arg;
 	struct fw_filter **fp;
 
@@ -273,7 +277,7 @@
 	if (arg->stop)
 		return;
 
-	for (h = 0; h <= 256; h++) {
+	for (h = 0; h < 256; h++) {
 		struct fw_filter *f;
 
 		for (f = head->ht[h]; f; f = f->next) {
diff -ur --new-file ../orig/linux/net/sched/cls_rsvp.h linux/net/sched/cls_rsvp.h
--- ../orig/linux/net/sched/cls_rsvp.h	Mon Apr  5 19:36:00 1999
+++ linux/net/sched/cls_rsvp.h	Wed Apr  7 19:14:29 1999
@@ -193,23 +193,23 @@
 				    && src[2] == f->src[2]
 #endif
 				    ) {
+					*res = f->res;
 
 					RSVP_POLICE();
 
 matched:
-					if (f->tunnelhdr == 0) {
-						*res = f->res;
+					if (f->tunnelhdr == 0)
 						return 0;
-					} else {
-						tunnelid = f->res.classid;
-						nhptr = (void*)(xprt + f->tunnelhdr - sizeof(*nhptr));
-						goto restart;
-					}
+
+					tunnelid = f->res.classid;
+					nhptr = (void*)(xprt + f->tunnelhdr - sizeof(*nhptr));
+					goto restart;
 				}
 			}
 
 			/* And wildcard bucket... */
 			for (f = s->ht[16]; f; f = f->next) {
+				*res = f->res;
 				RSVP_POLICE();
 				goto matched;
 			}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic