⬢ DragonFlyBSD Kernel Audit
DF-5700 / guard_harness.c
← back to finding ↓ download raw
/*
 * Guard-page containment rig for sys/libkern/bcmp.c (compiled verbatim as
 * df_bcmp).  Maps each buffer inside a single RW page with PROT_NONE guard
 * pages on BOTH sides, then sweeps lengths/offsets/placements/content.
 *
 * Property under test (bcmp.c:53-56,85,89,91,110-111,115,128-129):
 *   every longword load is naturally aligned (8 | page size) and starts at
 *   an address holding >= 1 in-bounds byte  =>  the overread (up to 7 bytes
 *   below start / past end) NEVER leaves the buffer's first/last valid page.
 *
 * Any violation => SIGSEGV => harness dies with the case recorded on disk.
 * Clean exit = containment empirically confirmed over the whole sweep.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>

int df_bcmp(const void *, const void *, size_t);

#define PS 4096
static unsigned char *P1, *P2;   /* usable pages, guarded both sides */

static FILE *journal;
static char jline[256];

static void jwrite(void) { fclose(journal); fprintf(stderr, "DIED IN: %s", jline); }

static uint64_t s_state = 0x243f6a8885a308d3ULL;
static uint64_t prng(void)
{
    s_state ^= s_state << 13; s_state ^= s_state >> 7; s_state ^= s_state << 17;
    return s_state;
}

static void one(int s1, int s2, size_t n, int mode)
{
    snprintf(jline, sizeof(jline), "s1=%d s2=%d n=%zu mode=%d\n", s1, s2, n, mode);
    for (int i = 0; i < PS; i++) {
        unsigned char v;
        switch (mode) {
        case 0: v = (unsigned char)prng(); break;
        case 1: v = 0; break;
        case 2: v = (unsigned char)(i * 31); break;
        default: v = (unsigned char)(i & 1 ? 0xaa : 0x55); break;
        }
        P1[i] = v; P2[i] = v;   /* keep both pages identical content */
    }
    /* mismatch variations that exercise the last byte / early exit paths */
    if (n && (mode & 1)) P1[s1 + n - 1] ^= 1;
    if (n && !(mode & 1)) P2[s2 + n - 1] ^= 1;
    volatile int r = df_bcmp(P1 + s1, P2 + s2, n);
    (void)r;
}

int main(void)
{
    /* two independent guarded arenas: [guard][usable][guard] */
    unsigned char *a1 = mmap(NULL, 3 * PS, PROT_READ | PROT_WRITE,
                             MAP_PRIVATE | MAP_ANON, -1, 0);
    unsigned char *a2 = mmap(NULL, 3 * PS, PROT_READ | PROT_WRITE,
                             MAP_PRIVATE | MAP_ANON, -1, 0);
    if (a1 == MAP_FAILED || a2 == MAP_FAILED) { perror("mmap"); return 2; }
    P1 = a1 + PS; P2 = a2 + PS;
    if (mprotect(a1, PS, PROT_NONE) || mprotect(a1 + 2 * PS, PS, PROT_NONE) ||
        mprotect(a2, PS, PROT_NONE) || mprotect(a2 + 2 * PS, PS, PROT_NONE)) {
        perror("mprotect"); return 2;
    }
    journal = fopen("journal.txt", "w");
    if (!journal) { perror("fopen"); return 2; }
    setvbuf(journal, NULL, _IOLBF, 0);

    long cases = 0;
    /* exhaustive small: n 1..96, every start offset in the low/high 16 bytes
     * of the page (page-corner coverage), 4 content modes */
    for (size_t n = 1; n <= 96; n++) {
        for (int s = 0; s < 16 && s + n <= PS; s++) {
            int s2m = PS - n - (s % 16); if (s2m < 0) s2m = 0;
            for (int mode = 0; mode < 4; mode++) {
                fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", s, s2m, n, mode);
                one(s, s2m, n, mode); cases++;
                fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", s2m, s, n, mode);
                one(s2m, s, n, mode); cases++;
            }
        }
    }
    printf("phase1 small sweep: %ld cases OK\n", cases);

    /* page-scale: lengths around PS with buffers ending exactly at the page
     * boundary and starting exactly at the page start (the two corners where
     * an uncontained overread would hit a guard page) */
    long c2 = 0;
    size_t lens[] = { PS - 8, PS - 7, PS - 3, PS - 2, PS - 1, PS,
                      PS + 1, PS + 2, PS + 7, PS + 8, 2 * PS, 4096 + 123, 8192 };
    for (size_t i = 0; i < sizeof(lens) / sizeof(lens[0]); i++) {
        size_t n = lens[i];
        if (n > PS) continue;              /* single usable page per buffer */
        int starts[] = { 0, 1, 7, 8, (int)(PS - n), (int)(PS - n > 0 ? PS - n : 0) };
        for (unsigned a = 0; a < sizeof(starts) / sizeof(starts[0]); a++) {
            int s = starts[a];
            if (s < 0 || s + n > PS) continue;
            for (int mode = 0; mode < 4; mode++) {
                fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", s, PS - n, n, mode);
                one(s, (int)(PS - n), n, mode); c2++;
                fprintf(journal, "s1=%d s2=%d n=%zu mode=%d\n", (int)(PS - n), s, n, mode);
                one((int)(PS - n), s, n, mode); c2++;
            }
        }
    }
    printf("phase2 page-corner sweep: %ld cases OK\n", c2);

    /* randomized placement across the whole page */
    for (long it = 0; it < 500000; it++) {
        size_t n = (size_t)(prng() % PS) + 1;
        int s1 = (int)(prng() % (PS - n + 1));
        int s2 = (int)(prng() % (PS - n + 1));
        fprintf(journal, "s1=%d s2=%d n=%zu mode=r\n", s1, s2, n);
        one(s1, s2, n, (int)(prng() % 4));
    }
    printf("phase3 randomized: 500000 cases OK\n");
    printf("RESULT: PASS — no load escaped the buffer's valid pages "
           "(%ld + %ld + 500000 cases)\n", cases, c2);
    fclose(journal);
    return 0;
}