DragonFlyBSD Kernel Audit
DF-0907 / harness.c
← back to finding ↓ download raw
/*
 * DF-0907 deterministic arithmetic harness.
 *
 * Transcribes the EXACT pointer arithmetic of smbfs_vfsops.c:165-178 into a
 * userspace model so the signed underflow is provable without the kernel.
 * The model uses the real MNAMELEN (80) and a real-sized username buffer.
 *
 * Output proves:
 *   - pe-pc-2 == -1  (as ptrdiff_t) at the second strncpy
 *   - which becomes SIZE_MAX when coerced to size_t (strncpy's 3rd arg)
 *
 * Build:  cc -o harness harness.c
 * Run:    ./harness
 */
#include <stdio.h>
#include <stddef.h>
#include <string.h>

#define MNAMELEN 80   /* sys/sys/mount.h: #define MNAMELEN 80 */

int main(void)
{
    char buf[MNAMELEN];          /* stand-in for f_mntfromname */
    char *pc, *pe;
    const char *vc_username;
    /* SMB_MAXUSERNAMELEN-1 = 127 chars accepted; we use 80 (>= 76). */
    char userbuf[128];
    size_t bound1, bound2_signed_view;
    long diff;

    memset(userbuf, 'A', 80);
    userbuf[80] = '\0';
    vc_username = userbuf;

    pc = buf;
    pe = pc + sizeof(buf);          /* pe = buf + 80            */
    memset(pc, 0, MNAMELEN);        /* bzero(pc, MNAMELEN);     */
    *pc++ = '/';                    /* buf[0]='/', pc=buf+1     */
    *pc++ = '/';                    /* buf[1]='/', pc=buf+2     */

    /* line 170: strncpy(pc, vc_username, pe-pc-2) */
    diff = (pe - pc - 2);
    printf("line 170: pe-pc-2 = (buf+80)-(buf+2)-2 = %ld  (bound)\n", diff);
    /* strncpy bound = 78-2 = 76 */
    bound1 = (size_t)diff;
    printf("           -> size_t bound = %zu\n", bound1);
    /* emulate strncpy: writes min(76, 80)=76 bytes, NO NUL terminator */
    {
        size_t n = bound1;
        size_t srclen = strlen(vc_username);
        size_t tocopy = (srclen < n) ? srclen : n;
        memcpy(pc, vc_username, tocopy);
        if (srclen < n) {
            /* strncpy zero-pads; here we don't since srclen>=n */
        }
        /* buf[2..77] = 'A'*76, buf[78..79] still 0 from memset */
    }
    /* index(pc, 0): scan past the 76 'A's, find buf[78]=0 */
    {
        char *p = pc;
        while (*p != 0) p++;
        pc = p;
    }
    printf("           after index: pc = buf+%ld (NUL at buf+78)\n", (long)(pc - buf));

    /* line 171: if (pc < pe-1) */
    if (pc < pe - 1) {              /* buf+78 < buf+79 -> TRUE */
        printf("line 171: pc < pe-1  ->  TRUE\n");
        /* line 172: *(pc++)='@'; overwrites buf[78]=0, pc=buf+79 */
        *pc++ = '@';
        printf("line 172: wrote '@' at buf+%ld, pc now buf+%ld\n",
               (long)(pc - 1 - buf), (long)(pc - buf));

        /* line 173: strncpy(pc, vc_srvname, pe-pc-2) */
        diff = (pe - pc - 2);
        printf("line 173: pe-pc-2 = (buf+80)-(buf+79)-2 = %ld  (ptrdiff_t)\n", diff);
        bound2_signed_view = (size_t)diff;
        printf("           coerced to size_t (strncpy 3rd arg) = %zu\n",
               bound2_signed_view);
        printf("           SIZE_MAX = %zu\n", (size_t)-1);
        if (bound2_signed_view == (size_t)-1) {
            printf("*** CONFIRMED: bound is (size_t)-1 == SIZE_MAX ***\n");
            printf("*** strncpy would write vc_srvname + ~SIZE_MAX zero-padding ***\n");
            printf("*** => heap overflow past f_mntfromname into struct statfs/mount ***\n");
            return 0;
        } else {
            printf("UNEXPECTED: bound is not SIZE_MAX\n");
            return 1;
        }
    } else {
        printf("line 171: pc < pe-1 -> FALSE (no overflow)\n");
        return 1;
    }
}