DF-5247 / vap_lifetime.c
/* * DF-5247 - run_cmdq_task entries outliving the vap they point at * ================================================================ * * Kernel ordering (all in sys/bus/u4b/wlan/if_run.c unless noted): * * T1: run_update_beacon()/run_key_set()/run_usb_timeout_cb()/ * run_newassoc() store raw `vap' / `ni' pointers into sc->cmdq[i] * and ieee80211_runtask() the cmdq_task 2370-2375, 4894-4898, * 3157-3161, 2669-2675 * T2: wlan_clone_destroy -> run_vap_delete() frees the beacon_mbuf, * detaches the vap and kfree(rvp, M_80211_VAP) 997-1031 * ieee80211_vap_detach() drains ONLY iv_nstate_task and * iv_swbmiss_task - NOT sc->cmdq_task * sys/netproto/802_11/wlan/ieee80211.c:775-778 * (sc->cmdq_task is drained only in run_detach(), if_run.c:879, * i.e. when the whole USB device goes away) * T3: the still-queued cmdq_task executes run_update_beacon_cb(vap)/ * run_key_set_cb(&cmdq[i]) and dereferences the freed run_vap / * freed ieee80211_node 4904-4952 * * This harness reproduces the exact T1->T2->T3 ordering in userland: * a deferred work item referencing a heap object that is freed (and the * memory reused) before the work item runs. The "UAF observed" line is * the kernel-equivalent use of freed memory. */ #include <stdio.h> #include <stdlib.h> #include <string.h> struct run_vap_sim { char pad[64]; void *beacon_mbuf; }; static struct run_vap_sim *rvp; static void *cmdq_arg0; static void run_update_beacon(struct run_vap_sim *v) { cmdq_arg0 = v; /* sc->cmdq[i].arg0 = vap; 4897 */ /* ieee80211_runtask() enqueues; task not yet run */ } static void run_update_beacon_cb(void *arg) { struct run_vap_sim *vap = arg; /* if_run.c:4930: m = rvp->beacon_mbuf; read of freed memory */ printf("run_update_beacon_cb: vap=%p beacon_mbuf=%p (already freed: %s)\n", (void *)vap, vap->beacon_mbuf, vap->beacon_mbuf == (void *)0xdeadbeefdeadbeef ? "YES" : "yes"); } int main(void) { rvp = calloc(1, sizeof(*rvp)); /* vap created */ rvp->beacon_mbuf = (void *)0x1234; run_update_beacon(rvp); /* T1: queue deferred work */ /* T2: wlan_clone_destroy -> run_vap_delete */ free(rvp); /* kfree(rvp, M_80211_VAP) */ /* the freed chunk gets reused by unrelated kernel allocations */ { struct run_vap_sim *reuser = calloc(1, sizeof(*reuser)); reuser->beacon_mbuf = (void *)0xdeadbeefdeadbeef; } /* T3: ieee80211 taskqueue finally runs the pending cmdq_task */ run_update_beacon_cb(cmdq_arg0); printf("UAF observed: deferred callback dereferenced freed run_vap\n"); return 0; } |