[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Kernel panic if raw multicast packet (>208 bytes) loopback
>Originator: Pavlin Radoslavov
>Organization: University of Southern California
>Confidential: yes
>Synopsis: Kernel panic if raw multicast packet (>208 bytes)
>loopback
>Severity: serious
>Priority: high
>Category: kern
>Release: OpenBSD 2.6
This bug is common to all *BSD distribution. It was reported to and
fixed in FreeBSD more than 2 years ago (around
FreeBSD-2.2.2?). Note that I have not verified it on OpenBSD. Below
is part of the original report.
>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/ip_output.c (in
ip_mloopback()). Note that I have not verified this patch.
--- ip_output.c.org Sat Dec 18 01:12:46 1999
+++ ip_output.c Sat Dec 18 01:14:34 1999
@@ -1702,6 +1702,8 @@
struct mbuf *copym;
copym = m_copy(m, 0, M_COPYALL);
+ if (copym != NULL && (copym->m_flags & M_EXT || copym->m_len < (ip->ip_hl << 2)))
+ copym = m_pullup(copym, ip->ip_hl << 2);
if (copym != NULL) {
/*
* We don't bother to fragment if the IP length is greater
Pavlin Radoslavov
pavlin@catarina.usc.edu