/*
 * DF-0509 analysis: ng_ksocket_sockaddr_unparse stack overflow via negative pathlen
 *
 * Source: sys/netgraph/ksocket/ng_ksocket.c:323-339 (v1 — loadable)
 *         sys/netgraph7/ksocket/ng_ksocket.c:323-339 (ng7 — dead code)
 *
 * The bug in the code:
 *   ng_ksocket_sockaddr_unparse:
 *     const int pathoff = OFFSETOF(struct sockaddr_un, sun_path); // = 2
 *     const int pathlen = sun->sun_len - pathoff;  // underflows if sun_len < 2
 *     char pathbuf[SOCK_MAXADDRLEN + 1];
 *     bcopy(sun->sun_path, pathbuf, pathlen);  // pathlen negative -> huge size_t
 *
 * The ng_ksocket BIND/CONNECT sanity checks (lines 655, 717):
 *   if (msg->header.arglen < SADATA_OFFSET || msg->header.arglen < sa->sa_len)
 * do NOT check sa->sa_len >= SADATA_OFFSET, so a message with sa_len=0/1 passes.
 *
 * HOWEVER: defense-in-depth in unp_bind (sys/kern/uipc_usrreq.c:1141-1143):
 *   namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
 *   if (namelen <= 0)
 *       return EINVAL;
 * This rejects short sockaddrs BEFORE they are stored, so a ksocket bound
 * via PF_LOCAL can never have a stored sockaddr with sa_len < 3.
 *
 * CONCLUSION: The unparse function has a latent integer-underflow vulnerability,
 * but the only data path that feeds it (sobind -> unp_bind) validates the
 * length. The bug is NOT triggerable to panic/RCE on DragonFly.
 *
 * CLASSIFICATION: FALSE POSITIVE (defense-in-depth in unp_bind prevents
 * exploitation). The ng_ksocket sanity check should still be hardened.
 *
 * VERDICT: The finding's claim that "sobind accepts short sockaddr" is
 * incorrect — unp_bind at uipc_usrreq.c:1141-1143 rejects it.
 *
 * This program documents the analysis by demonstrating:
 * 1. The underflow in pathlen computation
 * 2. The unp_bind validation that blocks it
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stddef.h>

/* Simplified sockaddr_un */
struct sockaddr_un {
    uint8_t  sun_len;
    uint8_t  sun_family;
    char     sun_path[104];
};

#define SADATA_OFFSET offsetof(struct sockaddr_un, sun_path)  /* = 2 */

/* Replicates ng_ksocket_sockaddr_unparse pathlen computation */
static int compute_pathlen(uint8_t sun_len) {
    const int pathoff = SADATA_OFFSET;
    const int pathlen = sun_len - pathoff;  /* underflow if sun_len < 2 */
    return pathlen;
}

/* Replicates unp_bind validation (uipc_usrreq.c:1141-1143) */
static int unp_bind_validate(uint8_t sun_len) {
    int namelen = sun_len - SADATA_OFFSET;
    if (namelen <= 0)
        return -1;  /* EINVAL — rejected */
    return 0;  /* OK */
}

/* Replicates ng_ksocket BIND sanity check (ng_ksocket.c:655-656) */
static int ng_ksocket_sanity(int arglen, uint8_t sa_len) {
    if (arglen < SADATA_OFFSET || arglen < sa_len)
        return -1;  /* EINVAL */
    return 0;  /* passes — does NOT check sa_len >= SADATA_OFFSET */
}

int main(void) {
    printf("=== DF-0509: ng_ksocket_sockaddr_unparse pathlen underflow ===\n\n");
    printf("SADATA_OFFSET = offsetof(sockaddr_un, sun_path) = %ld\n\n",
           (long)SADATA_OFFSET);

    uint8_t test_lens[] = {0, 1, 2, 3, 5, 10};

    for (int i = 0; i < (int)(sizeof(test_lens)/sizeof(test_lens[0])); i++) {
        uint8_t sl = test_lens[i];
        int pathlen = compute_pathlen(sl);
        int ng_sanity = ng_ksocket_sanity(SADATA_OFFSET, sl); /* arglen=2 */
        int unp = unp_bind_validate(sl);

        printf("sun_len=%d:\n", sl);
        printf("  ng_ksocket sanity (arglen=2): %s\n",
               ng_sanity == 0 ? "PASSES (weak)" : "FAILS");
        printf("  pathlen = %d - %ld = %d\n", sl, (long)SADATA_OFFSET, pathlen);
        if (pathlen < 0)
            printf("  -> bcopy size = (size_t)%d = 0x%lx -> MASSIVE overflow!\n",
                   pathlen, (unsigned long)(size_t)pathlen);
        printf("  unp_bind validation: %s\n\n",
               unp == 0 ? "ACCEPTS" : "REJECTS (EINVAL)");
    }

    printf("=== CONCLUSION ===\n");
    printf("For sun_len < 2:\n");
    printf("  ng_ksocket BIND sanity PASSES (bug)\n");
    printf("  BUT unp_bind REJECTS with EINVAL (defense-in-depth)\n");
    printf("  -> sockaddr never stored -> unparse never sees it\n");
    printf("  -> NOT triggerable on DragonFly\n\n");
    printf("RECOMMENDATION: Harden ng_ksocket sanity check anyway (fix.diff).\n");
    printf("CLASSIFICATION: FALSE POSITIVE (latent bug, blocked by unp_bind).\n");

    return 0;
}
