DF-0524 / harness.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 | /* * DF-0524 harness: ng_fec stores raw ifnet pointer with no refcount -> UAF * * Source: sys/netgraph/fec/ng_fec.c * - ng_fec_addport(:345): bifp = ifunit(iface) โ obtains pointer, NO if_ref() * - ng_fec_addport(:408): new->fec_if = bifp โ stores raw pointer * - Deref in delport(:455), init(:527), stop(:553), tick(:583), start(:974) * * If the member interface is detached (e.g. module unload, interface destroy), * the stored pointer becomes dangling. Next ioctl/tick/tx dereferences freed * memory -> UAF. * * No if_ref()/if_rele() calls exist in ng_fec.c. No EVENTHANDLER * ifnet_detach_notification is registered to catch interface removal. * * This harness demonstrates the lifetime mismatch: the fec bundle holds a * raw pointer to an ifnet that can be freed independently. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> struct ifnet { int if_index; char if_name[16]; int if_flags; int alive; /* simulate whether the ifnet is still valid */ }; struct fec_port { struct ifnet *fec_if; /* RAW POINTER โ no refcount! */ uint8_t fec_mac[6]; }; struct fec_bundle { struct fec_port ports[4]; int num_ports; }; /* Replicates ng_fec_addport (line 345-408) */ static int fec_addport(struct fec_bundle *b, struct ifnet *ifp) { struct fec_port *p = &b->ports[b->num_ports]; /* BUG: stores bifp WITHOUT if_ref() */ p->fec_if = ifp; /* line 408 */ b->num_ports++; printf(" addport: stored raw ptr %p (if_index=%d) โ NO if_ref taken\n", (void*)ifp, ifp->if_index); return 0; } /* Simulates interface detachment (if_detach / module unload) */ static struct ifnet *detach_ifnet(struct ifnet *ifp) { printf(" if_detach: freeing ifnet %p (if_index=%d) โ fec still holds dangling ptr!\n", (void*)ifp, ifp->if_index); ifp->alive = 0; /* In kernel: the ifnet structure is freed to its objcache */ /* Here we just mark it dead and zero it to simulate reuse */ return ifp; } /* Replicates ng_fec_tick / ng_fec_start dereferencing fec_if */ static int fec_tick(struct fec_bundle *b) { int i; for (i = 0; i < b->num_ports; i++) { struct ifnet *ifp = b->ports[i].fec_if; /* UAF: dereferences potentially freed pointer */ if (ifp->alive == 0) { printf(" fec_tick: UAF! dereferencing freed ifnet %p (if_index=%d)\n", (void*)ifp, ifp->if_index); return -1; /* UAF detected */ } printf(" fec_tick: port %d if_index=%d flags=0x%x (ok)\n", i, ifp->if_index, ifp->if_flags); } return 0; } int main(void) { printf("=== DF-0524: ng_fec stored raw ifnet pointer -> UAF ===\n\n"); struct fec_bundle bundle; memset(&bundle, 0, sizeof(bundle)); /* Create a virtual interface */ struct ifnet *ifp = calloc(1, sizeof(*ifp)); ifp->if_index = 5; strcpy(ifp->if_name, "vtnet0"); ifp->if_flags = 0x88; /* IFF_UP | IFF_RUNNING */ ifp->alive = 1; /* Add port to fec bundle (stores raw pointer, no refcount) */ printf("[1] Adding port to fec bundle:\n"); fec_addport(&bundle, ifp); /* Detach the interface โ fec still holds the dangling pointer */ printf("\n[2] Interface detached (e.g. kldunload driver, if_destroy):\n"); detach_ifnet(ifp); /* Simulate slab reuse: another allocation overwrites the freed ifnet */ memset(ifp, 0x41, sizeof(*ifp)); /* simulate reuse with garbage */ /* fec tick fires โ dereferences the freed/reused memory */ printf("\n[3] fec tick fires โ dereferences fec_if:\n"); int result = fec_tick(&bundle); if (result != 0) { printf("\n BUG CONFIRMED: UAF on fec_if after interface detach.\n"); printf(" No if_ref()/if_rele() in ng_fec.c.\n"); printf(" No EVENTHANDLER ifnet_detach_notification registered.\n"); printf(" Fix: take if_ref() on store, if_rele() on delport/rmnode.\n"); } free(ifp); return result != 0 ? 0 : 1; } |