Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 28 Apr 1997 20:52:08 -0700 (PDT)
From:      pavlin@catarina.usc.edu
To:        FreeBSD-gnats-submit@freebsd.org
Cc:        pavlin@catarina.usc.edu
Subject:   kern/3410: Raw multicast packets panic  
Message-ID:  <199704290352.UAA09668@rumi.usc.edu>
Resent-Message-ID: <199704290400.VAA22024@hub.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         3410
>Category:       kern
>Synopsis:       Kernel panic if raw multicast packet (>208 bytes) loopback
>Confidential:   yes
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Apr 28 21:00:01 PDT 1997
>Last-Modified:
>Originator:     Pavlin Ivanov Radoslavov
>Organization:
University of Southern California
>Release:        FreeBSD 2.2-RELEASE i386
>Environment:

All BSD delived systems with multicast support (FreeBSD, NetBSD, OpenBSD,
SunOS,...) Verified only on FreeBSD, but the relevant code of the other systems
looks the same. 

>Description:

If a process wants to prepare itself the IP header and then send the packet
using a raw socket, under certain conditions the kernel will panic:

1. The destination is a multicast address
2. The host is a member of the same multicast group  
3. The packet to send is at least 208 bytes
4. The host is little endian

If the packet is at least 208 bytes, an external mbuf cluster is used to
store it (including the IP header) when it is passed to rip_output() and
then to ip_output(). If the host is a member of the same multicast group,
ip_mloopback() is called to send a copy of the packet to the same host.
ip_mloopback uses m_copy() to create a copy of the packet and then uses
htons() over some fields in the header to make them network-ordered.
However, because m_copy() does not really copy the external clusters, but 
make them shared, the modification by ip_mloopback affects also the original
packet. The size of the original packet is corrupted and after ip_output()
tries to break the packet into several smaller, the difference between
the size based on the IP header and the mbuf header results in panic.   

>How-To-Repeat:
Compile and execute the following code as a root:

/*
 * BSD kernel bug demonstration program. KERNEL PANIC!!!!
 * Verified only for FreeBSD. 
 */
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in_systm.h>  
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>

void main()
{
	int sock, msock;
	int off;
	int b = 1;
	struct ip *ip;
	char buffer[300];
	struct sockaddr_in sockdst;
	struct ip_mreq imr;
	
	sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
	if (sock < 0){
		printf("Raw socket open error\n");
		exit(1);
	}

	msock = socket(AF_INET, SOCK_DGRAM, 0);
	if (msock < 0){
		printf("Multicast socket open error\n");
		exit(1);
	}
	imr.imr_multiaddr.s_addr = inet_addr("224.0.1.20");
	imr.imr_interface.s_addr = htonl(inet_addr("0.0.0.0"));
	if(setsockopt(msock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr,
	    sizeof(struct ip_mreq)) < 0){
		printf("Error join multicast group\n");
		exit(1);
	}	

	if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&b, sizeof(b)) < 0){
		printf("setsockopt HDRINCL error\n");
		exit(1);
	}

	ip = (struct ip *)buffer;
	ip->ip_hl = sizeof(struct ip) >>2;
	ip->ip_v = IPVERSION;
	ip->ip_tos = 0;
	ip->ip_off = 0;
	ip->ip_src.s_addr = inet_addr("0.0.0.0");
	ip->ip_dst.s_addr = inet_addr("224.0.1.20");
	ip->ip_p = IPPROTO_UDP;
	ip->ip_len = 240;
	ip->ip_ttl = 2;
	bzero(&sockdst, sizeof(sockdst));
	sockdst.sin_family = AF_INET;
	sockdst.sin_addr.s_addr = ip->ip_dst.s_addr;	
	if (sendto(sock, buffer, ip->ip_len, 0, (struct sockaddr *)&sockdst,
	    sizeof(sockdst)) < 0){
		printf("sendto error\n");
		exit(1);
	}
	printf("Packet send successfully\n");
	exit(0);
}

>Fix:
	
Apply the following patch to netinet/raw_ip.c


--- raw_ip.c.org	Mon Mar  3 01:24:38 1997
+++ raw_ip.c	Mon Apr 28 18:46:54 1997
@@ -206,6 +206,13 @@
 		}
 		if (ip->ip_id == 0)
 			ip->ip_id = htons(ip_id++);
+		if (m->m_flags & M_EXT)
+		  /* pullup the IP header from the external buffer */
+		   if ((m = m_pullup(m, (IP_VHL_HL(ip->ip_vhl) << 2))) == 0){
+			m_freem(m);
+			return ENOBUFS;
+		   }
+
 		/* XXX prevent ip_output from overwriting header fields */
 		flags |= IP_RAWOUTPUT;
 		ipstat.ips_rawout++;

Pavlin Radoslavov
pavlin@catarina.usc.edu

>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199704290352.UAA09668>