Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/imgact_elf.c
Show First 20 Lines • Show All 938 Lines • ▼ Show 20 Lines | __elfN(get_interp)(struct image_params *imgp, const Elf_Phdr *phdr, | ||||
char **interpp, bool *free_interpp) | char **interpp, bool *free_interpp) | ||||
{ | { | ||||
struct thread *td; | struct thread *td; | ||||
char *interp; | char *interp; | ||||
int error, interp_name_len; | int error, interp_name_len; | ||||
KASSERT(phdr->p_type == PT_INTERP, | KASSERT(phdr->p_type == PT_INTERP, | ||||
("%s: p_type %u != PT_INTERP", __func__, phdr->p_type)); | ("%s: p_type %u != PT_INTERP", __func__, phdr->p_type)); | ||||
ASSERT_VOP_LOCKED(imgp->vp, __func__); | |||||
td = curthread; | td = curthread; | ||||
/* Path to interpreter */ | /* Path to interpreter */ | ||||
if (phdr->p_filesz < 2 || phdr->p_filesz > MAXPATHLEN) { | if (phdr->p_filesz < 2 || phdr->p_filesz > MAXPATHLEN) { | ||||
uprintf("Invalid PT_INTERP\n"); | uprintf("Invalid PT_INTERP\n"); | ||||
return (ENOEXEC); | return (ENOEXEC); | ||||
} | } | ||||
interp_name_len = phdr->p_filesz; | interp_name_len = phdr->p_filesz; | ||||
if (phdr->p_offset > PAGE_SIZE || | if (phdr->p_offset > PAGE_SIZE || | ||||
interp_name_len > PAGE_SIZE - phdr->p_offset) { | interp_name_len > PAGE_SIZE - phdr->p_offset) { | ||||
VOP_UNLOCK(imgp->vp, 0); | |||||
interp = malloc(interp_name_len + 1, M_TEMP, M_WAITOK); | 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, | error = vn_rdwr(UIO_READ, imgp->vp, interp, | ||||
interp_name_len, phdr->p_offset, | interp_name_len, phdr->p_offset, | ||||
UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, | UIO_SYSSPACE, IO_RANGELOCKED, td->td_ucred, | ||||
NOCRED, NULL, td); | NOCRED, NULL, td); | ||||
if (error != 0) { | if (error != 0) { | ||||
free(interp, M_TEMP); | free(interp, M_TEMP); | ||||
uprintf("i/o error PT_INTERP %d\n", error); | uprintf("i/o error PT_INTERP %d\n", error); | ||||
return (error); | return (error); | ||||
} | } | ||||
interp[interp_name_len] = '\0'; | interp[interp_name_len] = '\0'; | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) | ||||
vm_map_t map; | vm_map_t map; | ||||
char *interp; | char *interp; | ||||
Elf_Brandinfo *brand_info; | Elf_Brandinfo *brand_info; | ||||
struct sysentvec *sv; | struct sysentvec *sv; | ||||
u_long addr, baddr, et_dyn_addr, entry, proghdr; | u_long addr, baddr, et_dyn_addr, entry, proghdr; | ||||
u_long maxalign, mapsz, maxv, maxv1; | u_long maxalign, mapsz, maxv, maxv1; | ||||
uint32_t fctl0; | uint32_t fctl0; | ||||
int32_t osrel; | int32_t osrel; | ||||
bool free_interp; | bool free_interp, locked; | ||||
int error, i, n; | int error, i, n; | ||||
hdr = (const Elf_Ehdr *)imgp->image_header; | 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 | * Only allow ET_EXEC & ET_DYN here, reject ET_DYN later | ||||
* if particular brand doesn't support it. | * if particular brand doesn't support it. | ||||
*/ | */ | ||||
if (__elfN(check_header)(hdr) != 0 || | if (__elfN(check_header)(hdr) != 0 || | ||||
(hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)) | (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)) | ||||
return (-1); | 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 | * From here on down, we return an errno, not -1, as we've | ||||
* detected an ELF file. | * detected an ELF file. | ||||
*/ | */ | ||||
if ((hdr->e_phoff > PAGE_SIZE) || | if ((hdr->e_phoff > PAGE_SIZE) || | ||||
(u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { | (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { | ||||
/* Only support headers in first page for now */ | /* Only support headers in first page for now */ | ||||
uprintf("Program headers not in the first page\n"); | 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); | phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); | ||||
if (!aligned(phdr, Elf_Addr)) { | if (!aligned(phdr, Elf_Addr)) { | ||||
uprintf("Unaligned program headers\n"); | 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++) { | for (i = 0; i < hdr->e_phnum; i++) { | ||||
switch (phdr[i].p_type) { | switch (phdr[i].p_type) { | ||||
case PT_LOAD: | case PT_LOAD: | ||||
if (n == 0) | if (n == 0) | ||||
baddr = phdr[i].p_vaddr; | baddr = phdr[i].p_vaddr; | ||||
if (phdr[i].p_align > maxalign) | if (phdr[i].p_align > maxalign) | ||||
maxalign = phdr[i].p_align; | maxalign = phdr[i].p_align; | ||||
mapsz += phdr[i].p_memsz; | mapsz += phdr[i].p_memsz; | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | if (baddr == 0) { | ||||
(imgp->proc->p_flag2 & P2_ASLR_ENABLE) != 0) | (imgp->proc->p_flag2 & P2_ASLR_ENABLE) != 0) | ||||
et_dyn_addr = ET_DYN_ADDR_RAND; | et_dyn_addr = ET_DYN_ADDR_RAND; | ||||
else | else | ||||
et_dyn_addr = ET_DYN_LOAD_ADDR; | et_dyn_addr = ET_DYN_LOAD_ADDR; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* 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. | * Decide whether to enable randomization of user mappings. | ||||
* First, reset user preferences for the setid binaries. | * First, reset user preferences for the setid binaries. | ||||
* Then, account for the support of the randomization by the | * Then, account for the support of the randomization by the | ||||
* ABI, by user preferences, and make special treatment for | * ABI, by user preferences, and make special treatment for | ||||
* PIE binaries. | * PIE binaries. | ||||
*/ | */ | ||||
if (imgp->credential_setid) { | if (imgp->credential_setid) { | ||||
PROC_LOCK(imgp->proc); | PROC_LOCK(imgp->proc); | ||||
Show All 16 Lines | if ((sv->sv_flags & SV_ASLR) == 0 || | ||||
* from the fragmentation. | * from the fragmentation. | ||||
*/ | */ | ||||
if (!__elfN(aslr_honor_sbrk) || | if (!__elfN(aslr_honor_sbrk) || | ||||
(imgp->proc->p_flag2 & P2_ASLR_IGNSTART) != 0) | (imgp->proc->p_flag2 & P2_ASLR_IGNSTART) != 0) | ||||
imgp->map_flags |= MAP_ASLR_IGNSTART; | imgp->map_flags |= MAP_ASLR_IGNSTART; | ||||
} | } | ||||
error = exec_new_vmspace(imgp, sv); | error = exec_new_vmspace(imgp, sv); | ||||
if (error != 0) | |||||
goto ret; | |||||
vmspace = imgp->proc->p_vmspace; | vmspace = imgp->proc->p_vmspace; | ||||
map = &vmspace->vm_map; | map = &vmspace->vm_map; | ||||
imgp->proc->p_sysent = sv; | imgp->proc->p_sysent = sv; | ||||
maxv = vm_map_max(map) - lim_max(td, RLIMIT_STACK); | maxv = vm_map_max(map) - lim_max(td, RLIMIT_STACK); | ||||
if (et_dyn_addr == ET_DYN_ADDR_RAND) { | if (et_dyn_addr == ET_DYN_ADDR_RAND) { | ||||
KASSERT((map->flags & MAP_ASLR) != 0, | KASSERT((map->flags & MAP_ASLR) != 0, | ||||
("ET_DYN_ADDR_RAND but !MAP_ASLR")); | ("ET_DYN_ADDR_RAND but !MAP_ASLR")); | ||||
et_dyn_addr = __CONCAT(rnd_, __elfN(base))(map, | et_dyn_addr = __CONCAT(rnd_, __elfN(base))(map, | ||||
vm_map_min(map) + mapsz + lim_max(td, RLIMIT_DATA), | vm_map_min(map) + mapsz + lim_max(td, RLIMIT_DATA), | ||||
/* reserve half of the address space to interpreter */ | /* reserve half of the address space to interpreter */ | ||||
maxv / 2, 1UL << flsl(maxalign)); | 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); | error = __elfN(enforce_limits)(imgp, hdr, phdr, et_dyn_addr); | ||||
if (error != 0) | if (error != 0) | ||||
goto ret; | goto ret; | ||||
entry = (u_long)hdr->e_entry + et_dyn_addr; | entry = (u_long)hdr->e_entry + et_dyn_addr; | ||||
/* | /* | ||||
* We load the dynamic linker where a userland call | * We load the dynamic linker where a userland call | ||||
Show All 10 Lines | map->anon_loc = __CONCAT(rnd_, __elfN(base))(map, addr, maxv1, | ||||
MAXPAGESIZES > 1 ? pagesizes[1] : pagesizes[0]); | MAXPAGESIZES > 1 ? pagesizes[1] : pagesizes[0]); | ||||
} else { | } else { | ||||
map->anon_loc = addr; | map->anon_loc = addr; | ||||
} | } | ||||
imgp->entry_addr = entry; | imgp->entry_addr = entry; | ||||
if (interp != NULL) { | if (interp != NULL) { | ||||
VOP_UNLOCK(imgp->vp, 0); | |||||
if ((map->flags & MAP_ASLR) != 0) { | if ((map->flags & MAP_ASLR) != 0) { | ||||
/* Assume that interpeter fits into 1/4 of AS */ | /* Assume that interpeter fits into 1/4 of AS */ | ||||
maxv1 = maxv / 2 + addr / 2; | maxv1 = maxv / 2 + addr / 2; | ||||
MPASS(maxv1 >= addr); /* No overflow */ | MPASS(maxv1 >= addr); /* No overflow */ | ||||
addr = __CONCAT(rnd_, __elfN(base))(map, addr, | addr = __CONCAT(rnd_, __elfN(base))(map, addr, | ||||
maxv1, PAGE_SIZE); | maxv1, PAGE_SIZE); | ||||
} | } | ||||
error = __elfN(load_interp)(imgp, brand_info, interp, &addr, | error = __elfN(load_interp)(imgp, brand_info, interp, &addr, | ||||
&imgp->entry_addr); | &imgp->entry_addr); | ||||
vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); | |||||
if (error != 0) | if (error != 0) | ||||
goto ret; | goto ret; | ||||
} else | } else | ||||
addr = et_dyn_addr; | addr = et_dyn_addr; | ||||
/* | /* | ||||
* Construct auxargs table (used by the fixup routine) | * Construct auxargs table (used by the fixup routine) | ||||
*/ | */ | ||||
elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); | elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); | ||||
elf_auxargs->execfd = -1; | elf_auxargs->execfd = -1; | ||||
elf_auxargs->phdr = proghdr + et_dyn_addr; | elf_auxargs->phdr = proghdr + et_dyn_addr; | ||||
elf_auxargs->phent = hdr->e_phentsize; | elf_auxargs->phent = hdr->e_phentsize; | ||||
elf_auxargs->phnum = hdr->e_phnum; | elf_auxargs->phnum = hdr->e_phnum; | ||||
elf_auxargs->pagesz = PAGE_SIZE; | elf_auxargs->pagesz = PAGE_SIZE; | ||||
elf_auxargs->base = addr; | elf_auxargs->base = addr; | ||||
elf_auxargs->flags = 0; | elf_auxargs->flags = 0; | ||||
elf_auxargs->entry = entry; | elf_auxargs->entry = entry; | ||||
elf_auxargs->hdr_eflags = hdr->e_flags; | 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->auxargs = elf_auxargs; | ||||
imgp->interpreted = 0; | imgp->interpreted = 0; | ||||
imgp->reloc_base = addr; | imgp->reloc_base = addr; | ||||
imgp->proc->p_osrel = osrel; | imgp->proc->p_osrel = osrel; | ||||
imgp->proc->p_fctl0 = fctl0; | imgp->proc->p_fctl0 = fctl0; | ||||
imgp->proc->p_elf_machine = hdr->e_machine; | imgp->proc->p_elf_machine = hdr->e_machine; | ||||
imgp->proc->p_elf_flags = hdr->e_flags; | imgp->proc->p_elf_flags = hdr->e_flags; | ||||
ret: | ret: | ||||
if (free_interp) | if (free_interp) | ||||
free(interp, M_TEMP); | free(interp, M_TEMP); | ||||
if (!locked) | |||||
vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); | |||||
return (error); | return (error); | ||||
} | } | ||||
#define suword __CONCAT(suword, __ELF_WORD_SIZE) | #define suword __CONCAT(suword, __ELF_WORD_SIZE) | ||||
int | int | ||||
__elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) | __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 1,239 Lines • ▼ Show 20 Lines | __elfN(parse_notes)(struct image_params *imgp, Elf_Note *checknote, | ||||
const char *note_name; | const char *note_name; | ||||
char *buf; | char *buf; | ||||
int i, error; | int i, error; | ||||
boolean_t res; | boolean_t res; | ||||
/* We need some limit, might as well use PAGE_SIZE. */ | /* We need some limit, might as well use PAGE_SIZE. */ | ||||
if (pnote == NULL || pnote->p_filesz > PAGE_SIZE) | if (pnote == NULL || pnote->p_filesz > PAGE_SIZE) | ||||
return (FALSE); | return (FALSE); | ||||
ASSERT_VOP_LOCKED(imgp->vp, "parse_notes"); | |||||
if (pnote->p_offset > PAGE_SIZE || | if (pnote->p_offset > PAGE_SIZE || | ||||
pnote->p_filesz > PAGE_SIZE - pnote->p_offset) { | pnote->p_filesz > PAGE_SIZE - pnote->p_offset) { | ||||
VOP_UNLOCK(imgp->vp, 0); | |||||
buf = malloc(pnote->p_filesz, M_TEMP, M_WAITOK); | 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, | 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); | curthread->td_ucred, NOCRED, NULL, curthread); | ||||
if (error != 0) { | if (error != 0) { | ||||
uprintf("i/o error PT_NOTE\n"); | uprintf("i/o error PT_NOTE\n"); | ||||
goto retf; | goto retf; | ||||
} | } | ||||
note = note0 = (const Elf_Note *)buf; | note = note0 = (const Elf_Note *)buf; | ||||
note_end = (const Elf_Note *)(buf + pnote->p_filesz); | note_end = (const Elf_Note *)(buf + pnote->p_filesz); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 165 Lines • Show Last 20 Lines |