/*
 * DF-0269 PoC: sppp_print_bytes VLA stack buffer overflow.
 *
 * sys/net/sppp/if_spppsubr.c:5287-5293:
 *
 *   static void sppp_print_bytes(const u_char *p, u_short len) {
 *       char hexstr[len];                                    // <-- VLA: len bytes
 *       if (len)
 *           log(-1, " %s", hexncpy(p, len, hexstr, HEX_NCPYLEN(len), "-"));
 *   }
 *
 * HEX_NCPYLEN(s) = (s * 3)   [sys/sys/libkern.h:67]
 * hexncpy() (sys/libkern/hexncpy.c:56-61) writes 3 bytes per input byte:
 *   for (; inlen > 0 && outlen >= 3; --inlen, outlen -= 3) {
 *       *outb++ = hexdigit[*inb >> 4];
 *       *outb++ = hexdigit[*inb++ & 0xf];
 *       *outb++ = *sep;            // separator
 *   }
 *
 * So hexncpy writes 3*len bytes into hexstr[len] -> 2*len bytes stack overflow.
 *
 * Reachability (pre-auth): sppp_cp_input() (if_spppsubr.c:1399-1410):
 *   if (debug) {                              // debug = ifp->if_flags & IFF_DEBUG
 *       printlen = ntohs(h->len);
 *       ...
 *       if (printlen > 4)
 *           sppp_print_bytes((u_char*)(h+1), printlen - 4);
 *   }
 * This runs in PHASE_ESTABLISH (LCP negotiation, BEFORE PHASE_AUTHENTICATE).
 * A peer sending an LCP frame to a sppp interface with IFF_DEBUG set triggers
 * the overflow. For a ~1500-byte frame, printlen ~= 1496 -> ~2992 bytes overflow.
 *
 * This PoC is a CODE-PATH CONFIRMATION harness.  A live trigger requires a
 * sppp-based interface (PPPoE/sync-serial) with IFF_DEBUG — the sppp module
 * is a framework, not directly cloneable (ifconfig sppp0 create fails).
 *
 * Build:  cc -o sppp_vla sppp_vla.c
 * Run:    ./sppp_vla
 */
#include <stdio.h>

#define HEX_NCPYLEN(s) (s * 3)

int
main(void)
{
	/* Simulate the VLA overflow to demonstrate the size mismatch */
	unsigned short len = 100;  /* e.g., a 100-byte LCP options payload */
	char hexstr_len[len];      /* what the kernel allocates: 100 bytes  */
	int needed = HEX_NCPYLEN(len); /* what hexncpy writes: 300 bytes    */

	printf("DF-0269: sppp_print_bytes VLA stack overflow\n");
	printf("============================================\n\n");
	printf("sppp_print_bytes (if_spppsubr.c:5290):\n");
	printf("  char hexstr[len];             // allocates %u bytes\n", len);
	printf("  hexncpy(p, len, hexstr, HEX_NCPYLEN(len), \"-\");\n");
	printf("  HEX_NCPYLEN(%u) = %u          // hexncpy writes %u bytes\n",
	    len, needed, needed);
	printf("  OVERFLOW = %u - %u = %u bytes past the VLA buffer\n\n",
	    needed, len, needed - len);

	printf("For a 1500-byte PPP frame (printlen-4 ~= 1496):\n");
	printf("  VLA size:     1496 bytes\n");
	printf("  hexncpy writes: 4488 bytes\n");
	printf("  OVERFLOW:      2992 bytes of kernel stack smashed\n\n");

	printf("Pre-auth reachability:\n");
	printf("  sppp_cp_input (if_spppsubr.c:1399-1410) runs in PHASE_ESTABLISH\n");
	printf("  (LCP negotiation) BEFORE PHASE_AUTHENTICATE.\n");
	printf("  Triggered by: IFF_DEBUG set + peer sends LCP frame with len>4.\n\n");

	printf("sppp is not directly cloneable (ifconfig sppp0 create -> EINVAL).\n");
	printf("Requires PPPoE/sync-serial interface using the sppp framework.\n");
	printf("Fix: char hexstr[HEX_NCPYLEN(len)] (see fix.diff).\n");
	return 0;
}
