DF-0671 / nb_recurse.c
/* * DF-0671 harness: prove the unbounded self-recursion in nbssn_rq_request() * (sys/netproto/smb/smb_trantcp.c:265) is a real lwkt-stack-overflow primitive. * * The live smbfs retarget path is gated on this guest by an UNINITIALIZED * sin.sin_family in the retarget sockaddr (smb_trantcp.c:258-260 set only * sin_addr/sin_port; sin_len/sin_family are stack garbage), so the retarget TCP * connect fails EAFNOSUPPORT (in_pcb.c:655) BEFORE line 265 runs (verified: the * evil_nbserver+mount_smbfs PoC reaches the retarget but the reconnect fails). * * To isolate the recursion bug from that sibling gate, this harness replicates * nbssn_rq_request()'s per-level frame cost (sockaddr_in + mbchain/mdchain + * mbuf ptr + scalars + the deeper nbssn_recv/socket_wait frames) and performs * the SAME unbounded self-recursive call cited at line 265. It runs on the * module-load thread, which uses a 16KB lwkt stack (same as the SMB iod path). * After tens of levels the stack guard is hit -> panic. * * Run: kldload ./nb_recurse.ko -> kernel stack overflow panic. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/malloc.h> #include <netinet/in.h> #define NBNS_MAXREDIRECTS 3 /* netbios.h:110 (was defined but unused) */ static int bound_recursion = 1; /* =1 replicate the FIX (depth bound); =0 baseline (unbounded) */ TUNABLE_INT("hw.nb_oob.bound", &bound_recursion); static int recurse_levels = 0; /* Per-level frame replica of nbssn_rq_request (smb_trantcp.c:201-209): * struct sockaddr_in sin; (16) * struct mbchain mb; struct mdchain md; (~40 each, hold an mbuf) * struct mbuf *m0; (8) * u_short port; u_int8_t rpcode; int error,rplen,res; * plus the frames of nbssn_recv()/socket_wait()/nb_connect_in() that live above * each recursive level. ~256 bytes/level is a conservative match. */ static int nbssn_rq_request_recurse(int depth) { struct sockaddr_in sin; /* uninitialized in the real bug */ volatile unsigned char frame[240]; u_short port; int error = 0, rplen = 6, res = 0, i; for (i = 0; i < (int)sizeof(frame); i++) frame[i] = (unsigned char)i; memset(&sin, 0, sizeof(sin)); /* (properly init here to isolate recursion) */ port = 139; (void)port; (void)res; /* the FIX: bound the retarget recursion (NBNS_MAXREDIRECTS=3). */ if (bound_recursion && depth >= NBNS_MAXREDIRECTS) return ECONNREFUSED; if (rplen != 6) /* mirror NB_SSN_RTGRESP guard (line 254) */ return ECONNABORTED; recurse_levels++; if ((recurse_levels & 7) == 0) kprintf("DF0671: NB retarget recursion level %d (sin @%p)\n", recurse_levels, &sin); /* smb_trantcp.c:262-265: disconnect -> reconnect -> UNBOUNDED self-recurse */ error = nbssn_rq_request_recurse(depth + 1); return error; } static int nb_recurse_load(module_t mod, int what, void *arg) { if (what != MOD_LOAD) return (0); kprintf("DF0671: replicating nbssn_rq_request self-recursion (smb_trantcp.c:265) " "bound=%d (1=fixed/depth-bounded, 0=baseline/unbounded)\n", bound_recursion); nbssn_rq_request_recurse(0); kprintf("DF0671: returned cleanly levels=%d (bound held, no stack overflow)\n", recurse_levels); return (0); } static moduledata_t nb_recurse_mod = { "nb_recurse", nb_recurse_load, NULL }; DECLARE_MODULE(nb_recurse, nb_recurse_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(nb_recurse, 1); |