Hi, in the ELF format, the PT_INTERP segment contains the path of the interpreter which must be loaded. For this segment, the kernel looks at these values in the program header:
p_offset: offset of the path string p_filesz: size of the path string, including the \0 The path string must be a valid string, or the binary won't be loaded. The kernel actually doesn't check the string, it just reads p_filesz bytes from p_offset, and later it checks if it can be loaded. Malformed binaries which have paths like: "aaaaaaaaaaaaaaaaaaaaa" or "aaa\0aaa\0aaa\0aaa\0" are parsed, loaded, and then the kernel figures out that the path is not valid, and aborts. When the kernel reads the path from the file, it should check directly the string to ensure it is at least a valid string. Here is a patch for this. For pp->p_filesz, I could have put if (pp->p_filesz == 0) but values of 1 also don't make sense; "\0" can't be a valid path. Index: exec_elf.c =================================================================== RCS file: /cvs/src/sys/kern/exec_elf.c,v retrieving revision 1.93 diff -u -p -r1.93 exec_elf.c --- exec_elf.c 4 Jul 2013 17:37:05 -0000 1.93 +++ exec_elf.c 27 Aug 2013 22:59:21 -0000 @@ -513,7 +513,7 @@ ELFNAME2(exec,makecmds)(struct proc *p, Elf_Ehdr *eh = epp->ep_hdr; Elf_Phdr *ph, *pp, *base_ph = NULL; Elf_Addr phdr = 0, exe_base = 0; - int error, i; + int error, i, n; char *interp = NULL; u_long pos = 0, phsize; size_t randomizequota = ELF_RANDOMIZE_LIMIT; @@ -552,11 +552,21 @@ ELFNAME2(exec,makecmds)(struct proc *p, for (i = 0, pp = ph; i < eh->e_phnum; i++, pp++) { if (pp->p_type == PT_INTERP && !interp) { - if (pp->p_filesz >= MAXPATHLEN) + if (pp->p_filesz >= MAXPATHLEN || + pp->p_filesz < 2) goto bad; interp = pool_get(&namei_pool, PR_WAITOK); if ((error = ELFNAME(read_from)(p, epp->ep_vp, pp->p_offset, interp, pp->p_filesz)) != 0) { + goto bad; + } + /* Ensure interp is a valid, NULL-terminated string */ + for (n = 0; n < pp->p_filesz; n++) { + if (interp[n] == '\0') + break; + } + if (n != pp->p_filesz - 1) { + error = ENOEXEC; goto bad; } } else if (pp->p_type == PT_LOAD) { If you want to test * Before patching take whatever binary you have, open it with a text/hex editor, at the beginning of the file there should be a string like "/usr/libexec/ld.so", just add a character to it, then run the binary with execv(): you will get abort traps. * After patching execv() now returns -1, and errno is set to ENOEXEC. There should not be any regression; if there is, it means that the binary is not spec compliant. Ok/Comments?