From owner-freebsd-bugs Mon Apr 28 21:00:04 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.5/8.8.5) id VAA22048 for bugs-outgoing; Mon, 28 Apr 1997 21:00:04 -0700 (PDT) Received: (from gnats@localhost) by hub.freebsd.org (8.8.5/8.8.5) id VAA22024; Mon, 28 Apr 1997 21:00:02 -0700 (PDT) Resent-Date: Mon, 28 Apr 1997 21:00:02 -0700 (PDT) Resent-Message-Id: <199704290400.VAA22024@hub.freebsd.org> Resent-From: gnats (GNATS Management) Resent-To: freebsd-bugs Resent-Reply-To: FreeBSD-gnats@freefall.FreeBSD.ORG, pavlin@catarina.usc.edu Received: from rumi.usc.edu (rumi.usc.edu [128.125.51.41]) by hub.freebsd.org (8.8.5/8.8.5) with ESMTP id UAA21661 for ; Mon, 28 Apr 1997 20:53:25 -0700 (PDT) Received: (from pavlin@localhost) by rumi.usc.edu (8.8.5/8.8.5) id UAA09668; Mon, 28 Apr 1997 20:52:08 -0700 (PDT) Message-Id: <199704290352.UAA09668@rumi.usc.edu> Date: Mon, 28 Apr 1997 20:52:08 -0700 (PDT) From: pavlin@catarina.usc.edu Reply-To: pavlin@catarina.usc.edu To: FreeBSD-gnats-submit@freebsd.org Cc: pavlin@catarina.usc.edu X-Send-Pr-Version: 3.2 Subject: kern/3410: Raw multicast packets panic Sender: owner-bugs@freebsd.org X-Loop: FreeBSD.org Precedence: bulk >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 #include #include #include #include #include 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: