/*
 * DF-0672 PoC — userspace structural demonstration of uninitialized
 * struct sockaddr_in in nbssn_rq_request retarget branch.
 *
 * The vulnerable code lives in sys/netproto/smb/smb_trantcp.c:206-263
 * and is built into the smbfs.ko module (loaded via `kldload smbfs`).
 * Triggering the bug LIVE requires a malicious NBSSN server that
 * responds with NB_SSN_RTGRESP and a victim user running mount_smbfs
 * (or otherwise opening a kernel SMB session); see VERDICT.md for why
 * that's beyond a minimal PoC.
 *
 * This harness reproduces the STRUCTURAL bug at the C level: it
 * declares `struct sockaddr_in sin;` on the stack with NO init (as
 * the kernel does), fills only sin.sin_addr (4B) and sin.sin_port
 * (2B) as the retarget branch does, then prints the full struct. The
 * sin_len, sin_family, and sin_zero[8] fields contain stack garbage
 * in every run — exactly the kernel-side defect.
 *
 * Build: cc -O -pipe -o df0672_uninit_sin df0672_uninit_sin.c
 * Run:   ./df0672_uninit_sin
 *
 * Expected: sin.sin_len and sin.sin_family are non-deterministic
 * (stack garbage), and sin_zero[8] contains residue. Each run varies.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>

int main(void)
{
    int i;
    /* Mirror sys/netproto/smb/smb_trantcp.c:206:
     *   struct sockaddr_in sin;   <-- NO initialization
     * The kernel stack contains residue from prior call frames. */
    struct sockaddr_in sin;       /* uninitialized, like the kernel */

    /* Prime the stack with garbage so the demo is reproducible. */
    for (i = 0; i < 16; i++) {
        volatile unsigned char garbage[64];
        memset((void *)garbage, 0xa0 + i, sizeof garbage);
    }

    /* Re-declare sin AFTER priming to land it on top of the residue. */
    {
        struct sockaddr_in sin2;
        sin = sin2;   /* copy residue-filled stack */
    }

    /* Retarget branch (smb_trantcp.c:258-260) fills only these fields: */
    {
        unsigned char addr[4] = { 192, 168, 1, 100 };
        unsigned short port_net = htons(139);
        memcpy(&sin.sin_addr, addr, 4);    /* md_get_mem 4B */
        memcpy(&sin.sin_port, &port_net, 2); /* md_get_uint16 2B */
    }

    /* in_pcbladdr_find (netinet/in_pcb.c:954-957) then validates:
     *   if (nam->sa_len != sizeof *sin)  return EINVAL;
     *   if (sin->sin_family != AF_INET) return EAFNOSUPPORT;
     * With uninit sin_len and sin_family, behavior is UB: usually the
     * connect returns EINVAL/EAFNOSUPPORT silently, occasionally
     * (stack residue matches by chance) the connect proceeds with
     * attacker-chosen addr/port via a corrupted sockaddr. */
    printf("Run result (sin on stack, only sin_addr/sin_port set):\n");
    printf("  sin.sin_len    = 0x%02x  (expect 0x10=16; needs AF_INET path)\n",
           (unsigned)sin.sin_len);
    printf("  sin.sin_family = 0x%04x (expect 0x0002=AF_INET)\n",
           (unsigned)sin.sin_family);
    printf("  sin.sin_port   = 0x%04x (set by retarget branch)\n",
           (unsigned)sin.sin_port);
    printf("  sin.sin_addr   = 0x%08x (set by retarget branch)\n",
           (unsigned)sin.sin_addr.s_addr);
    printf("  sin.sin_zero   = ");
    for (i = 0; i < 8; i++) printf("%02x", (unsigned)sin.sin_zero[i]);
    printf(" (UNINIT residue)\n");

    if (sin.sin_len == 16 && sin.sin_family == AF_INET) {
        printf("  -> By chance sin_len=16 and family=AF_INET this run:\n"
               "     the connect would PROCEED with attacker-chosen target!\n");
    } else {
        printf("  -> sin_len or sin_family mismatched this run:\n"
               "     in_pcbladdr_find would return EINVAL/EAFNOSUPPORT.\n");
    }
    return 0;
}
