/*
 * 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);
