Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/imgact_elf.c
Show First 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, | ||||
__XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable non-executable stack"); | __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable non-executable stack"); | ||||
#if __ELF_WORD_SIZE == 32 && (defined(__amd64__) || defined(__i386__)) | #if __ELF_WORD_SIZE == 32 && (defined(__amd64__) || defined(__i386__)) | ||||
int i386_read_exec = 0; | int i386_read_exec = 0; | ||||
SYSCTL_INT(_kern_elf32, OID_AUTO, read_exec, CTLFLAG_RW, &i386_read_exec, 0, | SYSCTL_INT(_kern_elf32, OID_AUTO, read_exec, CTLFLAG_RW, &i386_read_exec, 0, | ||||
"enable execution from readable segments"); | "enable execution from readable segments"); | ||||
#endif | #endif | ||||
SYSCTL_NODE(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, aslr, CTLFLAG_RW, 0, | |||||
""); | |||||
#define ASLR_NODE_OID __CONCAT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), _aslr) | |||||
static int __elfN(aslr_enabled) = 0; | |||||
SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, enable, CTLFLAG_RWTUN, | |||||
&__elfN(aslr_enabled), 0, | |||||
__XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) | |||||
": enable address map randomization"); | |||||
static int __elfN(pie_aslr_enabled) = 0; | |||||
SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, pie_enable, CTLFLAG_RWTUN, | |||||
&__elfN(pie_aslr_enabled), 0, | |||||
__XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) | |||||
": enable address map randomization for PIE binaries"); | |||||
static int __elfN(aslr_honor_sbrk) = 1; | |||||
SYSCTL_INT(ASLR_NODE_OID, OID_AUTO, honor_sbrk, CTLFLAG_RW, | |||||
&__elfN(aslr_honor_sbrk), 0, | |||||
__XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": assume sbrk is used"); | |||||
static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; | static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; | ||||
#define trunc_page_ps(va, ps) rounddown2(va, ps) | #define trunc_page_ps(va, ps) rounddown2(va, ps) | ||||
#define round_page_ps(va, ps) roundup2(va, ps) | #define round_page_ps(va, ps) roundup2(va, ps) | ||||
#define aligned(a, t) (trunc_page_ps((u_long)(a), sizeof(t)) == (u_long)(a)) | #define aligned(a, t) (trunc_page_ps((u_long)(a), sizeof(t)) == (u_long)(a)) | ||||
static const char FREEBSD_ABI_VENDOR[] = "FreeBSD"; | static const char FREEBSD_ABI_VENDOR[] = "FreeBSD"; | ||||
▲ Show 20 Lines • Show All 621 Lines • ▼ Show 20 Lines | fail: | ||||
if (nd->ni_vp) | if (nd->ni_vp) | ||||
vput(nd->ni_vp); | vput(nd->ni_vp); | ||||
free(tempdata, M_TEMP); | free(tempdata, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
static u_long | |||||
__CONCAT(rnd_, __elfN(base))(vm_map_t map __unused, u_long minv, u_long maxv, | |||||
u_int align) | |||||
{ | |||||
u_long rbase, res; | |||||
MPASS(vm_map_min(map) <= minv); | |||||
MPASS(maxv <= vm_map_max(map)); | |||||
MPASS(minv < maxv); | |||||
MPASS(minv + align < maxv); | |||||
arc4rand(&rbase, sizeof(rbase), 0); | |||||
res = roundup(minv, (u_long)align) + rbase % (maxv - minv); | |||||
res &= ~((u_long)align - 1); | |||||
if (res >= maxv) | |||||
res -= align; | |||||
KASSERT(res >= minv, | |||||
("res %#lx < minv %#lx, maxv %#lx rbase %#lx", | |||||
res, minv, maxv, rbase)); | |||||
KASSERT(res < maxv, | |||||
("res %#lx > maxv %#lx, minv %#lx rbase %#lx", | |||||
res, maxv, minv, rbase)); | |||||
return (res); | |||||
} | |||||
/* | |||||
* Impossible et_dyn_addr initial value indicating that the real base | |||||
* must be calculated later with some randomization applied. | |||||
*/ | |||||
#define ET_DYN_ADDR_RAND 1 | |||||
static int | static int | ||||
__CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) | __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) | ||||
{ | { | ||||
struct thread *td; | struct thread *td; | ||||
const Elf_Ehdr *hdr; | const Elf_Ehdr *hdr; | ||||
const Elf_Phdr *phdr; | const Elf_Phdr *phdr; | ||||
Elf_Auxargs *elf_auxargs; | Elf_Auxargs *elf_auxargs; | ||||
struct vmspace *vmspace; | struct vmspace *vmspace; | ||||
vm_map_t map; | |||||
const char *err_str, *newinterp; | const char *err_str, *newinterp; | ||||
char *interp, *interp_buf, *path; | char *interp, *interp_buf, *path; | ||||
Elf_Brandinfo *brand_info; | Elf_Brandinfo *brand_info; | ||||
struct sysentvec *sv; | struct sysentvec *sv; | ||||
vm_prot_t prot; | vm_prot_t prot; | ||||
u_long text_size, data_size, total_size, text_addr, data_addr; | u_long text_size, data_size, total_size, text_addr, data_addr; | ||||
u_long seg_size, seg_addr, addr, baddr, et_dyn_addr, entry, proghdr; | u_long seg_size, seg_addr, addr, baddr, et_dyn_addr, entry, proghdr; | ||||
u_long maxalign, mapsz, maxv, maxv1; | |||||
uint32_t fctl0; | uint32_t fctl0; | ||||
int32_t osrel; | int32_t osrel; | ||||
int error, i, n, interp_name_len, have_interp; | int error, i, n, interp_name_len, have_interp; | ||||
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 ? | ||||
Show All 27 Lines | __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) | ||||
osrel = 0; | osrel = 0; | ||||
fctl0 = 0; | fctl0 = 0; | ||||
text_size = data_size = total_size = text_addr = data_addr = 0; | text_size = data_size = total_size = text_addr = data_addr = 0; | ||||
entry = proghdr = 0; | entry = proghdr = 0; | ||||
interp_name_len = 0; | interp_name_len = 0; | ||||
err_str = newinterp = NULL; | err_str = newinterp = NULL; | ||||
interp = interp_buf = NULL; | interp = interp_buf = NULL; | ||||
td = curthread; | 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) | |||||
maxalign = phdr[i].p_align; | |||||
mapsz += phdr[i].p_memsz; | |||||
n++; | n++; | ||||
break; | break; | ||||
case PT_INTERP: | case PT_INTERP: | ||||
/* Path to interpreter */ | /* Path to interpreter */ | ||||
if (phdr[i].p_filesz < 2 || | if (phdr[i].p_filesz < 2 || | ||||
phdr[i].p_filesz > MAXPATHLEN) { | phdr[i].p_filesz > MAXPATHLEN) { | ||||
uprintf("Invalid PT_INTERP\n"); | uprintf("Invalid PT_INTERP\n"); | ||||
error = ENOEXEC; | error = ENOEXEC; | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) | ||||
brand_info = __elfN(get_brandinfo)(imgp, interp, interp_name_len, | brand_info = __elfN(get_brandinfo)(imgp, interp, interp_name_len, | ||||
&osrel, &fctl0); | &osrel, &fctl0); | ||||
if (brand_info == NULL) { | if (brand_info == NULL) { | ||||
uprintf("ELF binary type \"%u\" not known.\n", | uprintf("ELF binary type \"%u\" not known.\n", | ||||
hdr->e_ident[EI_OSABI]); | hdr->e_ident[EI_OSABI]); | ||||
error = ENOEXEC; | error = ENOEXEC; | ||||
goto ret; | goto ret; | ||||
} | } | ||||
sv = brand_info->sysvec; | |||||
et_dyn_addr = 0; | et_dyn_addr = 0; | ||||
if (hdr->e_type == ET_DYN) { | if (hdr->e_type == ET_DYN) { | ||||
if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) { | if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) { | ||||
uprintf("Cannot execute shared object\n"); | uprintf("Cannot execute shared object\n"); | ||||
error = ENOEXEC; | error = ENOEXEC; | ||||
goto ret; | goto ret; | ||||
} | } | ||||
/* | /* | ||||
* Honour the base load address from the dso if it is | * Honour the base load address from the dso if it is | ||||
* non-zero for some reason. | * non-zero for some reason. | ||||
*/ | */ | ||||
if (baddr == 0) | if (baddr == 0) { | ||||
if ((sv->sv_flags & SV_ASLR) == 0 || | |||||
(fctl0 & NT_FREEBSD_FCTL_ASLR_DISABLE) != 0) | |||||
et_dyn_addr = ET_DYN_LOAD_ADDR; | et_dyn_addr = ET_DYN_LOAD_ADDR; | ||||
else if ((__elfN(pie_aslr_enabled) && | |||||
(imgp->proc->p_flag2 & P2_ASLR_DISABLE) == 0) || | |||||
(imgp->proc->p_flag2 & P2_ASLR_ENABLE) != 0) | |||||
et_dyn_addr = ET_DYN_ADDR_RAND; | |||||
else | |||||
et_dyn_addr = ET_DYN_LOAD_ADDR; | |||||
} | } | ||||
sv = brand_info->sysvec; | } | ||||
if (interp != NULL && brand_info->interp_newpath != NULL) | if (interp != NULL && brand_info->interp_newpath != NULL) | ||||
newinterp = brand_info->interp_newpath; | newinterp = brand_info->interp_newpath; | ||||
/* | /* | ||||
* Avoid a possible deadlock if the current address space is destroyed | * Avoid a possible deadlock if the current address space is destroyed | ||||
* and that address space maps the locked vnode. In the common case, | * and that address space maps the locked vnode. In the common case, | ||||
* the locked vnode's v_usecount is decremented but remains greater | * the locked vnode's v_usecount is decremented but remains greater | ||||
* than zero. Consequently, the vnode lock is not needed by vrele(). | * than zero. Consequently, the vnode lock is not needed by vrele(). | ||||
* However, in cases where the vnode lock is external, such as nullfs, | * However, in cases where the vnode lock is external, such as nullfs, | ||||
* v_usecount may become zero. | * v_usecount may become zero. | ||||
* | * | ||||
* The VV_TEXT flag prevents modifications to the executable while | * The VV_TEXT flag prevents modifications to the executable while | ||||
* the vnode is unlocked. | * the vnode is unlocked. | ||||
*/ | */ | ||||
VOP_UNLOCK(imgp->vp, 0); | 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 | |||||
* ABI, by user preferences, and make special treatment for | |||||
* PIE binaries. | |||||
*/ | |||||
if (imgp->credential_setid) { | |||||
PROC_LOCK(imgp->proc); | |||||
imgp->proc->p_flag2 &= ~(P2_ASLR_ENABLE | P2_ASLR_DISABLE); | |||||
PROC_UNLOCK(imgp->proc); | |||||
} | |||||
if ((sv->sv_flags & SV_ASLR) == 0 || | |||||
(imgp->proc->p_flag2 & P2_ASLR_DISABLE) != 0 || | |||||
(fctl0 & NT_FREEBSD_FCTL_ASLR_DISABLE) != 0) { | |||||
KASSERT(et_dyn_addr != ET_DYN_ADDR_RAND, | |||||
("et_dyn_addr == RAND and !ASLR")); | |||||
} else if ((imgp->proc->p_flag2 & P2_ASLR_ENABLE) != 0 || | |||||
(__elfN(aslr_enabled) && hdr->e_type == ET_EXEC) || | |||||
et_dyn_addr == ET_DYN_ADDR_RAND) { | |||||
imgp->map_flags |= MAP_ASLR; | |||||
/* | |||||
* If user does not care about sbrk, utilize the bss | |||||
* grow region for mappings as well. We can select | |||||
* the base for the image anywere and still not suffer | |||||
* from the fragmentation. | |||||
*/ | |||||
if (!__elfN(aslr_honor_sbrk) || | |||||
(imgp->proc->p_flag2 & P2_ASLR_IGNSTART) != 0) | |||||
imgp->map_flags |= MAP_ASLR_IGNSTART; | |||||
} | |||||
error = exec_new_vmspace(imgp, sv); | error = exec_new_vmspace(imgp, sv); | ||||
vmspace = imgp->proc->p_vmspace; | |||||
map = &vmspace->vm_map; | |||||
imgp->proc->p_sysent = sv; | imgp->proc->p_sysent = sv; | ||||
maxv = vm_map_max(map) - lim_max(td, RLIMIT_STACK); | |||||
if (et_dyn_addr == ET_DYN_ADDR_RAND) { | |||||
KASSERT((map->flags & MAP_ASLR) != 0, | |||||
("ET_DYN_ADDR_RAND but !MAP_ASLR")); | |||||
et_dyn_addr = __CONCAT(rnd_, __elfN(base))(map, | |||||
vm_map_min(map) + mapsz + lim_max(td, RLIMIT_DATA), | |||||
/* reserve half of the address space to interpreter */ | |||||
maxv / 2, 1UL << flsl(maxalign)); | |||||
} | |||||
vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); | vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); | ||||
if (error != 0) | if (error != 0) | ||||
goto ret; | goto ret; | ||||
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: /* Loadable segment */ | case PT_LOAD: /* Loadable segment */ | ||||
if (phdr[i].p_memsz == 0) | if (phdr[i].p_memsz == 0) | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | else if (racct_set(imgp->proc, RACCT_VMEM, total_size) != 0) | ||||
err_str = "Total segment size exceeds resource limit"; | err_str = "Total segment size exceeds resource limit"; | ||||
if (err_str != NULL) { | if (err_str != NULL) { | ||||
PROC_UNLOCK(imgp->proc); | PROC_UNLOCK(imgp->proc); | ||||
uprintf("%s\n", err_str); | uprintf("%s\n", err_str); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto ret; | goto ret; | ||||
} | } | ||||
vmspace = imgp->proc->p_vmspace; | |||||
vmspace->vm_tsize = text_size >> PAGE_SHIFT; | vmspace->vm_tsize = text_size >> PAGE_SHIFT; | ||||
vmspace->vm_taddr = (caddr_t)(uintptr_t)text_addr; | vmspace->vm_taddr = (caddr_t)(uintptr_t)text_addr; | ||||
vmspace->vm_dsize = data_size >> PAGE_SHIFT; | vmspace->vm_dsize = data_size >> PAGE_SHIFT; | ||||
vmspace->vm_daddr = (caddr_t)(uintptr_t)data_addr; | vmspace->vm_daddr = (caddr_t)(uintptr_t)data_addr; | ||||
/* | /* | ||||
* We load the dynamic linker where a userland call | * We load the dynamic linker where a userland call | ||||
* to mmap(0, ...) would put it. The rationale behind this | * to mmap(0, ...) would put it. The rationale behind this | ||||
* calculation is that it leaves room for the heap to grow to | * calculation is that it leaves room for the heap to grow to | ||||
* its maximum allowed size. | * its maximum allowed size. | ||||
*/ | */ | ||||
addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(td, | addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(td, | ||||
RLIMIT_DATA)); | RLIMIT_DATA)); | ||||
if ((map->flags & MAP_ASLR) != 0) { | |||||
maxv1 = maxv / 2 + addr / 2; | |||||
MPASS(maxv1 >= addr); /* No overflow */ | |||||
map->anon_loc = __CONCAT(rnd_, __elfN(base))(map, addr, maxv1, | |||||
MAXPAGESIZES > 1 ? pagesizes[1] : pagesizes[0]); | |||||
} else { | |||||
map->anon_loc = addr; | |||||
} | |||||
PROC_UNLOCK(imgp->proc); | PROC_UNLOCK(imgp->proc); | ||||
imgp->entry_addr = entry; | imgp->entry_addr = entry; | ||||
if (interp != NULL) { | if (interp != NULL) { | ||||
have_interp = FALSE; | have_interp = FALSE; | ||||
VOP_UNLOCK(imgp->vp, 0); | 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; | |||||
MPASS(maxv1 >= addr); /* No overflow */ | |||||
addr = __CONCAT(rnd_, __elfN(base))(map, addr, | |||||
maxv1, PAGE_SIZE); | |||||
} | |||||
if (brand_info->emul_path != NULL && | if (brand_info->emul_path != NULL && | ||||
brand_info->emul_path[0] != '\0') { | brand_info->emul_path[0] != '\0') { | ||||
path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); | path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); | ||||
snprintf(path, MAXPATHLEN, "%s%s", | snprintf(path, MAXPATHLEN, "%s%s", | ||||
brand_info->emul_path, interp); | brand_info->emul_path, interp); | ||||
error = __elfN(load_file)(imgp->proc, path, &addr, | error = __elfN(load_file)(imgp->proc, path, &addr, | ||||
&imgp->entry_addr, sv->sv_pagesize); | &imgp->entry_addr, sv->sv_pagesize); | ||||
free(path, M_TEMP); | free(path, M_TEMP); | ||||
▲ Show 20 Lines • Show All 1,484 Lines • Show Last 20 Lines |