Subject: Re: [PATCH v3 net] ipv6: Fix dangling pointer when ipv6 fragment

From: kbuild test robot <[email-masked]>
Date: Tue, 2 Apr 2019 18:49:03 +0800

Hi hujunwei,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net/master]

url:    https://github.com/0day-ci/linux/commits/hujunwei/ipv6-Fix-dangling-pointer-when-ipv6-fragment/20190402-175602
config: i386-randconfig-x005-201913 (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All warnings (new ones prefixed by >>):

   net//ipv6/ip6_output.c: In function 'ip6_fragment':
>> net//ipv6/ip6_output.c:609:27: warning: 'prevhdr' is used uninitialized in this function [-Wuninitialized]
     nexthdr_offset = prevhdr - skb_network_header(skb);
                      ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~

vim +/prevhdr +609 net//ipv6/ip6_output.c

   594	
   595	int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
   596			 int (*output)(struct net *, struct sock *, struct sk_buff *))
   597	{
   598		struct sk_buff *frag;
   599		struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
   600		struct ipv6_pinfo *np = skb->sk && !dev_recursion_level() ?
   601					inet6_sk(skb->sk) : NULL;
   602		struct ipv6hdr *tmp_hdr;
   603		struct frag_hdr *fh;
   604		unsigned int mtu, hlen, left, len, nexthdr_offset;
   605		int hroom, troom;
   606		__be32 frag_id;
   607		int ptr, offset = 0, err = 0;
   608		u8 *prevhdr, nexthdr = 0;
 > 609		nexthdr_offset = prevhdr - skb_network_header(skb);
   610	
   611		err = ip6_find_1stfragopt(skb, &prevhdr);
   612		if (err < 0)
   613			goto fail;
   614		hlen = err;
   615		nexthdr = *prevhdr;
   616	
   617		mtu = ip6_skb_dst_mtu(skb);
   618	
   619		/* We must not fragment if the socket is set to force MTU discovery
   620		 * or if the skb it not generated by a local socket.
   621		 */
   622		if (unlikely(!skb->ignore_df && skb->len > mtu))
   623			goto fail_toobig;
   624	
   625		if (IP6CB(skb)->frag_max_size) {
   626			if (IP6CB(skb)->frag_max_size > mtu)
   627				goto fail_toobig;
   628	
   629			/* don't send fragments larger than what we received */
   630			mtu = IP6CB(skb)->frag_max_size;
   631			if (mtu < IPV6_MIN_MTU)
   632				mtu = IPV6_MIN_MTU;
   633		}
   634	
   635		if (np && np->frag_size < mtu) {
   636			if (np->frag_size)
   637				mtu = np->frag_size;
   638		}
   639		if (mtu < hlen + sizeof(struct frag_hdr) + 8)
   640			goto fail_toobig;
   641		mtu -= hlen + sizeof(struct frag_hdr);
   642	
   643		frag_id = ipv6_select_ident(net, &ipv6_hdr(skb)->daddr,
   644					    &ipv6_hdr(skb)->saddr);
   645	
   646		if (skb->ip_summed == CHECKSUM_PARTIAL &&
   647		    (err = skb_checksum_help(skb)))
   648			goto fail;
   649	
   650		prevhdr = skb_network_header(skb) + nexthdr_offset;
   651		hroom = LL_RESERVED_SPACE(rt->dst.dev);
   652		if (skb_has_frag_list(skb)) {
   653			unsigned int first_len = skb_pagelen(skb);
   654			struct sk_buff *frag2;
   655	
   656			if (first_len - hlen > mtu ||
   657			    ((first_len - hlen) & 7) ||
   658			    skb_cloned(skb) ||
   659			    skb_headroom(skb) < (hroom + sizeof(struct frag_hdr)))
   660				goto slow_path;
   661	
   662			skb_walk_frags(skb, frag) {
   663				/* Correct geometry. */
   664				if (frag->len > mtu ||
   665				    ((frag->len & 7) && frag->next) ||
   666				    skb_headroom(frag) < (hlen + hroom + sizeof(struct frag_hdr)))
   667					goto slow_path_clean;
   668	
   669				/* Partially cloned skb? */
   670				if (skb_shared(frag))
   671					goto slow_path_clean;
   672	
   673				BUG_ON(frag->sk);
   674				if (skb->sk) {
   675					frag->sk = skb->sk;
   676					frag->destructor = sock_wfree;
   677				}
   678				skb->truesize -= frag->truesize;
   679			}
   680	
   681			err = 0;
   682			offset = 0;
   683			/* BUILD HEADER */
   684	
   685			*prevhdr = NEXTHDR_FRAGMENT;
   686			tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
   687			if (!tmp_hdr) {
   688				err = -ENOMEM;
   689				goto fail;
   690			}
   691			frag = skb_shinfo(skb)->frag_list;
   692			skb_frag_list_init(skb);
   693	
   694			__skb_pull(skb, hlen);
   695			fh = __skb_push(skb, sizeof(struct frag_hdr));
   696			__skb_push(skb, hlen);
   697			skb_reset_network_header(skb);
   698			memcpy(skb_network_header(skb), tmp_hdr, hlen);
   699	
   700			fh->nexthdr = nexthdr;
   701			fh->reserved = 0;
   702			fh->frag_off = htons(IP6_MF);
   703			fh->identification = frag_id;
   704	
   705			first_len = skb_pagelen(skb);
   706			skb->data_len = first_len - skb_headlen(skb);
   707			skb->len = first_len;
   708			ipv6_hdr(skb)->payload_len = htons(first_len -
   709							   sizeof(struct ipv6hdr));
   710	
   711			for (;;) {
   712				/* Prepare header of the next frame,
   713				 * before previous one went down. */
   714				if (frag) {
   715					frag->ip_summed = CHECKSUM_NONE;
   716					skb_reset_transport_header(frag);
   717					fh = __skb_push(frag, sizeof(struct frag_hdr));
   718					__skb_push(frag, hlen);
   719					skb_reset_network_header(frag);
   720					memcpy(skb_network_header(frag), tmp_hdr,
   721					       hlen);
   722					offset += skb->len - hlen - sizeof(struct frag_hdr);
   723					fh->nexthdr = nexthdr;
   724					fh->reserved = 0;
   725					fh->frag_off = htons(offset);
   726					if (frag->next)
   727						fh->frag_off |= htons(IP6_MF);
   728					fh->identification = frag_id;
   729					ipv6_hdr(frag)->payload_len =
   730							htons(frag->len -
   731							      sizeof(struct ipv6hdr));
   732					ip6_copy_metadata(frag, skb);
   733				}
   734	
   735				err = output(net, sk, skb);
   736				if (!err)
   737					IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
   738						      IPSTATS_MIB_FRAGCREATES);
   739	
   740				if (err || !frag)
   741					break;
   742	
   743				skb = frag;
   744				frag = skb->next;
   745				skb_mark_not_on_list(skb);
   746			}
   747	
   748			kfree(tmp_hdr);
   749	
   750			if (err == 0) {
   751				IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
   752					      IPSTATS_MIB_FRAGOKS);
   753				return 0;
   754			}
   755	
   756			kfree_skb_list(frag);
   757	
   758			IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
   759				      IPSTATS_MIB_FRAGFAILS);
   760			return err;
   761	
   762	slow_path_clean:
   763			skb_walk_frags(skb, frag2) {
   764				if (frag2 == frag)
   765					break;
   766				frag2->sk = NULL;
   767				frag2->destructor = NULL;
   768				skb->truesize += frag2->truesize;
   769			}
   770		}
   771	
   772	slow_path:
   773		left = skb->len - hlen;		/* Space per frame */
   774		ptr = hlen;			/* Where to start from */
   775	
   776		/*
   777		 *	Fragment the datagram.
   778		 */
   779	
   780		troom = rt->dst.dev->needed_tailroom;
   781	
   782		/*
   783		 *	Keep copying data until we run out.
   784		 */
   785		while (left > 0)	{
   786			u8 *fragnexthdr_offset;
   787	
   788			len = left;
   789			/* IF: it doesn't fit, use 'mtu' - the data space left */
   790			if (len > mtu)
   791				len = mtu;
   792			/* IF: we are not sending up to and including the packet end
   793			   then align the next start on an eight byte boundary */
   794			if (len < left)	{
   795				len &= ~7;
   796			}
   797	
   798			/* Allocate buffer */
   799			frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
   800					 hroom + troom, GFP_ATOMIC);
   801			if (!frag) {
   802				err = -ENOMEM;
   803				goto fail;
   804			}
   805	
   806			/*
   807			 *	Set up data on packet
   808			 */
   809	
   810			ip6_copy_metadata(frag, skb);
   811			skb_reserve(frag, hroom);
   812			skb_put(frag, len + hlen + sizeof(struct frag_hdr));
   813			skb_reset_network_header(frag);
   814			fh = (struct frag_hdr *)(skb_network_header(frag) + hlen);
   815			frag->transport_header = (frag->network_header + hlen +
   816						  sizeof(struct frag_hdr));
   817	
   818			/*
   819			 *	Charge the memory for the fragment to any owner
   820			 *	it might possess
   821			 */
   822			if (skb->sk)
   823				skb_set_owner_w(frag, skb->sk);
   824	
   825			/*
   826			 *	Copy the packet header into the new buffer.
   827			 */
   828			skb_copy_from_linear_data(skb, skb_network_header(frag), hlen);
   829	
   830			fragnexthdr_offset = skb_network_header(frag);
   831			fragnexthdr_offset += prevhdr - skb_network_header(skb);
   832			*fragnexthdr_offset = NEXTHDR_FRAGMENT;
   833	
   834			/*
   835			 *	Build fragment header.
   836			 */
   837			fh->nexthdr = nexthdr;
   838			fh->reserved = 0;
   839			fh->identification = frag_id;
   840	
   841			/*
   842			 *	Copy a block of the IP datagram.
   843			 */
   844			BUG_ON(skb_copy_bits(skb, ptr, skb_transport_header(frag),
   845					     len));
   846			left -= len;
   847	
   848			fh->frag_off = htons(offset);
   849			if (left > 0)
   850				fh->frag_off |= htons(IP6_MF);
   851			ipv6_hdr(frag)->payload_len = htons(frag->len -
   852							    sizeof(struct ipv6hdr));
   853	
   854			ptr += len;
   855			offset += len;
   856	
   857			/*
   858			 *	Put this fragment into the sending queue.
   859			 */
   860			err = output(net, sk, frag);
   861			if (err)
   862				goto fail;
   863	
   864			IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
   865				      IPSTATS_MIB_FRAGCREATES);
   866		}
   867		IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
   868			      IPSTATS_MIB_FRAGOKS);
   869		consume_skb(skb);
   870		return err;
   871	
   872	fail_toobig:
   873		if (skb->sk && dst_allfrag(skb_dst(skb)))
   874			sk_nocaps_add(skb->sk, NETIF_F_GSO_MASK);
   875	
   876		icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
   877		err = -EMSGSIZE;
   878	
   879	fail:
   880		IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
   881			      IPSTATS_MIB_FRAGFAILS);
   882		kfree_skb(skb);
   883		return err;
   884	}
   885	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation