DF-2972 / fix.diff
--- a/sys/kern/imgact_shell.c +++ b/sys/kern/imgact_shell.c @@ -30,6 +30,7 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/sysmsg.h> +#include <sys/malloc.h> #include <sys/exec.h> #include <sys/imgact.h> #include <sys/kernel.h> @@ -49,6 +50,7 @@ { const char *image_header = imgp->image_header; const char *ihp; + char *snap; /* stable snapshot of the first page */ size_t length, offset; int error; @@ -66,6 +68,19 @@ imgp->interpreted = 1; /* + * Snapshot the first page ONCE. The mapped first page is the + * file's live page-cache page and can be modified concurrently + * (e.g. via a MAP_SHARED mapping that outlives close(), which + * also evades the ETXTBSY writecount check in + * exec_check_permissions()). Scanning the page twice allowed + * the accounting pass below and the copy pass further down to + * observe two different interpreter lines. + */ + snap = kmalloc(PAGE_SIZE, M_TEMP, M_WAITOK); + bcopy(image_header, snap, PAGE_SIZE); + image_header = snap; + + /* * Figure out the number of bytes that need to be reserved in the * argument string to copy the contents of the interpreter's command * line into the argument string. @@ -96,15 +111,19 @@ } /* If the script gives a null line as the interpreter, we bail */ - if (offset == 0) + if (offset == 0) { + kfree(snap, M_TEMP); return (ENOEXEC); + } /* It should not be possible for offset to exceed PAGE_SIZE */ KKASSERT(offset <= PAGE_SIZE); /* Check that we aren't too big */ - if (ihp == &image_header[PAGE_SIZE]) + if (ihp == &image_header[PAGE_SIZE]) { + kfree(snap, M_TEMP); return (ENAMETOOLONG); + } /* * The full path name of the original script file must be tagged @@ -117,8 +136,10 @@ offset += strlen(imgp->args->fname) + 1; /* add fname */ length = strlen(imgp->args->begin_argv) + 1; /* bytes to delete */ - if (offset > imgp->args->space + length) + if (offset > imgp->args->space + length) { + kfree(snap, M_TEMP); return (E2BIG); + } bcopy(imgp->args->begin_argv + length, imgp->args->begin_argv + offset, imgp->args->endp - (imgp->args->begin_argv + length)); @@ -173,6 +194,7 @@ error = copystr(imgp->args->begin_argv, imgp->interpreter_name, MAXSHELLCMDLEN, &length); } + kfree(snap, M_TEMP); return (error); } |