Index: sys/kern/imgact_elf.c =================================================================== --- sys/kern/imgact_elf.c +++ sys/kern/imgact_elf.c @@ -944,7 +944,6 @@ KASSERT(phdr->p_type == PT_INTERP, ("%s: p_type %u != PT_INTERP", __func__, phdr->p_type)); - ASSERT_VOP_LOCKED(imgp->vp, __func__); td = curthread; @@ -957,12 +956,10 @@ interp_name_len = phdr->p_filesz; if (phdr->p_offset > PAGE_SIZE || interp_name_len > PAGE_SIZE - phdr->p_offset) { - VOP_UNLOCK(imgp->vp, 0); interp = malloc(interp_name_len + 1, M_TEMP, M_WAITOK); - vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); error = vn_rdwr(UIO_READ, imgp->vp, interp, interp_name_len, phdr->p_offset, - UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, + UIO_SYSSPACE, IO_RANGELOCKED, td->td_ucred, NOCRED, NULL, td); if (error != 0) { free(interp, M_TEMP); @@ -1044,13 +1041,14 @@ u_long maxalign, mapsz, maxv, maxv1; uint32_t fctl0; int32_t osrel; - bool free_interp; + bool free_interp, locked; int error, i, n; hdr = (const Elf_Ehdr *)imgp->image_header; /* - * Do we have a valid ELF header ? + * Do we have a valid ELF header? If not, return -1, + * so that do_exec() can try other activators. * * Only allow ET_EXEC & ET_DYN here, reject ET_DYN later * if particular brand doesn't support it. @@ -1060,6 +1058,32 @@ return (-1); /* + * Avoid a possible deadlock if the current address space is destroyed + * and that address space maps the locked vnode. In the common case, + * the locked vnode's v_usecount is decremented but remains greater + * than zero. Consequently, the vnode lock is not needed by vrele(). + * However, in cases where the vnode lock is external, such as nullfs, + * v_usecount may become zero. + * + * The VV_TEXT flag prevents modifications to the executable while + * the vnode is unlocked. + */ + VOP_UNLOCK(imgp->vp, 0); + locked = false; + + n = error = 0; + baddr = 0; + osrel = 0; + elf_auxargs = NULL; + fctl0 = 0; + entry = proghdr = 0; + interp = NULL; + free_interp = false; + td = curthread; + maxalign = PAGE_SIZE; + mapsz = 0; + + /* * From here on down, we return an errno, not -1, as we've * detected an ELF file. */ @@ -1068,25 +1092,16 @@ (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { /* Only support headers in first page for now */ uprintf("Program headers not in the first page\n"); - return (ENOEXEC); + error = ENOEXEC; + goto ret; } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) { uprintf("Unaligned program headers\n"); - return (ENOEXEC); + error = ENOEXEC; + goto ret; } - n = error = 0; - baddr = 0; - osrel = 0; - fctl0 = 0; - entry = proghdr = 0; - interp = NULL; - free_interp = false; - td = curthread; - maxalign = PAGE_SIZE; - mapsz = 0; - for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: @@ -1165,19 +1180,6 @@ } /* - * Avoid a possible deadlock if the current address space is destroyed - * and that address space maps the locked vnode. In the common case, - * the locked vnode's v_usecount is decremented but remains greater - * than zero. Consequently, the vnode lock is not needed by vrele(). - * However, in cases where the vnode lock is external, such as nullfs, - * v_usecount may become zero. - * - * The VV_TEXT flag prevents modifications to the executable while - * the vnode is unlocked. - */ - VOP_UNLOCK(imgp->vp, 0); - - /* * Decide whether to enable randomization of user mappings. * First, reset user preferences for the setid binaries. * Then, account for the support of the randomization by the @@ -1210,6 +1212,9 @@ } error = exec_new_vmspace(imgp, sv); + if (error != 0) + goto ret; + vmspace = imgp->proc->p_vmspace; map = &vmspace->vm_map; @@ -1225,14 +1230,6 @@ maxv / 2, 1UL << flsl(maxalign)); } - vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); - if (error != 0) - goto ret; - - error = __elfN(load_sections)(imgp, hdr, phdr, et_dyn_addr, NULL); - if (error != 0) - goto ret; - error = __elfN(enforce_limits)(imgp, hdr, phdr, et_dyn_addr); if (error != 0) goto ret; @@ -1259,7 +1256,6 @@ imgp->entry_addr = entry; if (interp != NULL) { - VOP_UNLOCK(imgp->vp, 0); if ((map->flags & MAP_ASLR) != 0) { /* Assume that interpeter fits into 1/4 of AS */ maxv1 = maxv / 2 + addr / 2; @@ -1269,7 +1265,6 @@ } error = __elfN(load_interp)(imgp, brand_info, interp, &addr, &imgp->entry_addr); - vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) goto ret; } else @@ -1289,6 +1284,15 @@ elf_auxargs->entry = entry; elf_auxargs->hdr_eflags = hdr->e_flags; + vn_lock(imgp->vp, LK_SHARED | LK_RETRY); + locked = true; + + error = __elfN(load_sections)(imgp, hdr, phdr, et_dyn_addr, NULL); + if (error != 0) { + free(elf_auxargs, M_TEMP); + goto ret; + } + imgp->auxargs = elf_auxargs; imgp->interpreted = 0; imgp->reloc_base = addr; @@ -1300,6 +1304,8 @@ ret: if (free_interp) free(interp, M_TEMP); + if (!locked) + vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); return (error); } @@ -2555,14 +2561,11 @@ /* We need some limit, might as well use PAGE_SIZE. */ if (pnote == NULL || pnote->p_filesz > PAGE_SIZE) return (FALSE); - ASSERT_VOP_LOCKED(imgp->vp, "parse_notes"); if (pnote->p_offset > PAGE_SIZE || pnote->p_filesz > PAGE_SIZE - pnote->p_offset) { - VOP_UNLOCK(imgp->vp, 0); buf = malloc(pnote->p_filesz, M_TEMP, M_WAITOK); - vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); error = vn_rdwr(UIO_READ, imgp->vp, buf, pnote->p_filesz, - pnote->p_offset, UIO_SYSSPACE, IO_NODELOCKED, + pnote->p_offset, UIO_SYSSPACE, IO_RANGELOCKED, curthread->td_ucred, NOCRED, NULL, curthread); if (error != 0) { uprintf("i/o error PT_NOTE\n"); Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c +++ sys/kern/kern_exec.c @@ -610,6 +610,9 @@ } if (error) { + if (VOP_ISLOCKED(imgp->vp) != LK_EXCLUSIVE) + vn_lock(imgp->vp, LK_UPGRADE | LK_RETRY); + if (error == -1) { if (textset == 0) VOP_UNSET_TEXT(imgp->vp);