1996-03-10 08:42:54 +00:00
|
|
|
|
/*-
|
2001-02-24 22:20:11 +00:00
|
|
|
|
* Copyright (c) 2000 David O'Brien
|
1996-03-10 08:42:54 +00:00
|
|
|
|
* Copyright (c) 1995-1996 S<EFBFBD>ren Schmidt
|
1996-03-10 22:37:34 +00:00
|
|
|
|
* Copyright (c) 1996 Peter Wemm
|
1996-03-10 08:42:54 +00:00
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
|
* are met:
|
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer
|
|
|
|
|
* in this position and unchanged.
|
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
|
* 3. The name of the author may not be used to endorse or promote products
|
2002-06-02 20:05:59 +00:00
|
|
|
|
* derived from this software without specific prior written permission
|
1996-03-10 08:42:54 +00:00
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
*
|
1999-08-28 01:08:13 +00:00
|
|
|
|
* $FreeBSD$
|
1996-03-10 08:42:54 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
#include <sys/exec.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/fcntl.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
#include <sys/imgact.h>
|
|
|
|
|
#include <sys/imgact_elf.h>
|
|
|
|
|
#include <sys/kernel.h>
|
2001-03-28 09:17:56 +00:00
|
|
|
|
#include <sys/lock.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
#include <sys/malloc.h>
|
2000-10-20 07:58:15 +00:00
|
|
|
|
#include <sys/mutex.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/mman.h>
|
1996-05-01 02:43:13 +00:00
|
|
|
|
#include <sys/namei.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/pioctl.h>
|
1996-05-01 02:43:13 +00:00
|
|
|
|
#include <sys/proc.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/procfs.h>
|
|
|
|
|
#include <sys/resourcevar.h>
|
2000-09-10 13:54:52 +00:00
|
|
|
|
#include <sys/systm.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
#include <sys/signalvar.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/stat.h>
|
2001-03-28 11:52:56 +00:00
|
|
|
|
#include <sys/sx.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/syscall.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
#include <sys/sysctl.h>
|
1998-09-14 22:46:08 +00:00
|
|
|
|
#include <sys/sysent.h>
|
1996-05-01 02:43:13 +00:00
|
|
|
|
#include <sys/vnode.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
#include <vm/vm.h>
|
|
|
|
|
#include <vm/vm_kern.h>
|
|
|
|
|
#include <vm/vm_param.h>
|
|
|
|
|
#include <vm/pmap.h>
|
|
|
|
|
#include <vm/vm_map.h>
|
1998-09-16 02:04:05 +00:00
|
|
|
|
#include <vm/vm_object.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
#include <vm/vm_extern.h>
|
|
|
|
|
|
1998-10-18 15:55:12 +00:00
|
|
|
|
#include <machine/elf.h>
|
1996-03-10 08:42:54 +00:00
|
|
|
|
#include <machine/md_var.h>
|
|
|
|
|
|
2000-04-18 02:39:26 +00:00
|
|
|
|
#define OLD_EI_BRAND 8
|
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
static int __elfN(check_header)(const Elf_Ehdr *hdr);
|
2002-09-02 04:50:57 +00:00
|
|
|
|
static Elf_Brandinfo *__elfN(get_brandinfo)(const Elf_Ehdr *hdr,
|
|
|
|
|
const char *interp);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr,
|
|
|
|
|
u_long *entry, size_t pagesize);
|
|
|
|
|
static int __elfN(load_section)(struct proc *p,
|
2002-07-06 07:00:01 +00:00
|
|
|
|
struct vmspace *vmspace, struct vnode *vp, vm_object_t object,
|
1998-09-14 22:46:08 +00:00
|
|
|
|
vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz,
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_prot_t prot, size_t pagesize);
|
|
|
|
|
static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2003-01-04 22:07:48 +00:00
|
|
|
|
SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0,
|
|
|
|
|
"");
|
|
|
|
|
|
2003-01-05 03:48:14 +00:00
|
|
|
|
int __elfN(fallback_brand) = -1;
|
|
|
|
|
SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO,
|
|
|
|
|
fallback_brand, CTLFLAG_RW, &__elfN(fallback_brand), 0,
|
2003-01-04 22:07:48 +00:00
|
|
|
|
__XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) " brand of last resort");
|
|
|
|
|
TUNABLE_INT("kern.elf" __XSTRING(__ELF_WORD_SIZE) ".fallback_brand",
|
2003-01-05 03:48:14 +00:00
|
|
|
|
&__elfN(fallback_brand));
|
2003-01-04 22:07:48 +00:00
|
|
|
|
|
1997-04-01 10:41:48 +00:00
|
|
|
|
static int elf_trace = 0;
|
2003-01-04 22:07:48 +00:00
|
|
|
|
SYSCTL_INT(_debug, OID_AUTO, __elfN(trace), CTLFLAG_RW, &elf_trace, 0, "");
|
|
|
|
|
|
2002-12-21 01:15:39 +00:00
|
|
|
|
static int elf_legacy_coredump = 0;
|
2003-01-04 22:07:48 +00:00
|
|
|
|
SYSCTL_INT(_debug, OID_AUTO, __elfN(legacy_coredump), CTLFLAG_RW,
|
2002-12-16 19:24:43 +00:00
|
|
|
|
&elf_legacy_coredump, 0, "");
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
static Elf_Brandinfo *elf_brand_list[MAX_BRANDS];
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(insert_brand_entry)(Elf_Brandinfo *entry)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2002-08-24 22:55:16 +00:00
|
|
|
|
for (i = 0; i < MAX_BRANDS; i++) {
|
1996-10-16 17:51:08 +00:00
|
|
|
|
if (elf_brand_list[i] == NULL) {
|
|
|
|
|
elf_brand_list[i] = entry;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-10-16 17:51:08 +00:00
|
|
|
|
if (i == MAX_BRANDS)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (-1);
|
|
|
|
|
return (0);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(remove_brand_entry)(Elf_Brandinfo *entry)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
2002-08-24 22:55:16 +00:00
|
|
|
|
for (i = 0; i < MAX_BRANDS; i++) {
|
1996-10-16 17:51:08 +00:00
|
|
|
|
if (elf_brand_list[i] == entry) {
|
|
|
|
|
elf_brand_list[i] = NULL;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
1996-10-16 17:51:08 +00:00
|
|
|
|
if (i == MAX_BRANDS)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (-1);
|
|
|
|
|
return (0);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-02-04 12:42:39 +00:00
|
|
|
|
int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(brand_inuse)(Elf_Brandinfo *entry)
|
1999-02-04 12:42:39 +00:00
|
|
|
|
{
|
|
|
|
|
struct proc *p;
|
2000-11-22 07:42:04 +00:00
|
|
|
|
int rval = FALSE;
|
1999-02-04 12:42:39 +00:00
|
|
|
|
|
2001-03-28 11:52:56 +00:00
|
|
|
|
sx_slock(&allproc_lock);
|
1999-11-16 10:56:05 +00:00
|
|
|
|
LIST_FOREACH(p, &allproc, p_list) {
|
2000-11-22 07:42:04 +00:00
|
|
|
|
if (p->p_sysent == entry->sysvec) {
|
|
|
|
|
rval = TRUE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
1999-02-04 12:42:39 +00:00
|
|
|
|
}
|
2001-03-28 11:52:56 +00:00
|
|
|
|
sx_sunlock(&allproc_lock);
|
1999-02-04 12:42:39 +00:00
|
|
|
|
|
2000-11-22 07:42:04 +00:00
|
|
|
|
return (rval);
|
1999-02-04 12:42:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-09-02 04:50:57 +00:00
|
|
|
|
static Elf_Brandinfo *
|
|
|
|
|
__elfN(get_brandinfo)(const Elf_Ehdr *hdr, const char *interp)
|
|
|
|
|
{
|
|
|
|
|
Elf_Brandinfo *bi;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We support three types of branding -- (1) the ELF EI_OSABI field
|
|
|
|
|
* that SCO added to the ELF spec, (2) FreeBSD 3.x's traditional string
|
|
|
|
|
* branding w/in the ELF header, and (3) path of the `interp_path'
|
|
|
|
|
* field. We should also look for an ".note.ABI-tag" ELF section now
|
|
|
|
|
* in all Linux ELF binaries, FreeBSD 4.1+, and some NetBSD ones.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* If the executable has a brand, search for it in the brand list. */
|
|
|
|
|
for (i = 0; i < MAX_BRANDS; i++) {
|
|
|
|
|
bi = elf_brand_list[i];
|
|
|
|
|
if (bi != NULL && hdr->e_machine == bi->machine &&
|
|
|
|
|
(hdr->e_ident[EI_OSABI] == bi->brand ||
|
|
|
|
|
strncmp((const char *)&hdr->e_ident[OLD_EI_BRAND],
|
|
|
|
|
bi->compat_3_brand, strlen(bi->compat_3_brand)) == 0))
|
|
|
|
|
return (bi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lacking a known brand, search for a recognized interpreter. */
|
|
|
|
|
if (interp != NULL) {
|
|
|
|
|
for (i = 0; i < MAX_BRANDS; i++) {
|
|
|
|
|
bi = elf_brand_list[i];
|
|
|
|
|
if (bi != NULL && hdr->e_machine == bi->machine &&
|
|
|
|
|
strcmp(interp, bi->interp_path) == 0)
|
|
|
|
|
return (bi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lacking a recognized interpreter, try the default brand */
|
|
|
|
|
for (i = 0; i < MAX_BRANDS; i++) {
|
|
|
|
|
bi = elf_brand_list[i];
|
|
|
|
|
if (bi != NULL && hdr->e_machine == bi->machine &&
|
2003-01-05 03:48:14 +00:00
|
|
|
|
__elfN(fallback_brand) == bi->brand)
|
2002-09-02 04:50:57 +00:00
|
|
|
|
return (bi);
|
|
|
|
|
}
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
static int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(check_header)(const Elf_Ehdr *hdr)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
2002-09-08 02:17:44 +00:00
|
|
|
|
Elf_Brandinfo *bi;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
int i;
|
|
|
|
|
|
1998-10-18 15:55:12 +00:00
|
|
|
|
if (!IS_ELF(*hdr) ||
|
|
|
|
|
hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
|
|
|
|
|
hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
|
|
|
|
|
hdr->e_ident[EI_VERSION] != EV_CURRENT)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (ENOEXEC);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
/*
|
|
|
|
|
* Make sure we have at least one brand for this machine.
|
|
|
|
|
*/
|
|
|
|
|
|
2002-08-24 22:55:16 +00:00
|
|
|
|
for (i = 0; i < MAX_BRANDS; i++) {
|
2002-09-08 02:17:44 +00:00
|
|
|
|
bi = elf_brand_list[i];
|
|
|
|
|
if (bi != NULL && bi->machine == hdr->e_machine)
|
2002-07-20 02:56:12 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (i == MAX_BRANDS)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (ENOEXEC);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1999-02-20 23:52:34 +00:00
|
|
|
|
if (hdr->e_version != ELF_TARG_VER)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (ENOEXEC);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (0);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(map_partial)(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
|
|
|
|
|
vm_offset_t start, vm_offset_t end, vm_prot_t prot,
|
|
|
|
|
vm_prot_t max)
|
|
|
|
|
{
|
|
|
|
|
int error, rv;
|
|
|
|
|
vm_offset_t off;
|
|
|
|
|
vm_offset_t data_buf = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Create the page if it doesn't exist yet. Ignore errors.
|
|
|
|
|
*/
|
|
|
|
|
vm_map_lock(map);
|
2002-08-25 22:36:52 +00:00
|
|
|
|
vm_map_insert(map, NULL, 0, trunc_page(start), round_page(end), max,
|
|
|
|
|
max, 0);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_map_unlock(map);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find the page from the underlying object.
|
|
|
|
|
*/
|
|
|
|
|
if (object) {
|
|
|
|
|
vm_object_reference(object);
|
|
|
|
|
rv = vm_map_find(exec_map,
|
|
|
|
|
object,
|
|
|
|
|
trunc_page(offset),
|
|
|
|
|
&data_buf,
|
|
|
|
|
PAGE_SIZE,
|
|
|
|
|
TRUE,
|
|
|
|
|
VM_PROT_READ,
|
|
|
|
|
VM_PROT_ALL,
|
|
|
|
|
MAP_COPY_ON_WRITE | MAP_PREFAULT_PARTIAL);
|
|
|
|
|
if (rv != KERN_SUCCESS) {
|
|
|
|
|
vm_object_deallocate(object);
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (rv);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
off = offset - trunc_page(offset);
|
2002-08-25 20:48:45 +00:00
|
|
|
|
error = copyout((caddr_t)data_buf + off, (caddr_t)start,
|
|
|
|
|
end - start);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_map_remove(exec_map, data_buf, data_buf + PAGE_SIZE);
|
|
|
|
|
if (error) {
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (KERN_FAILURE);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (KERN_SUCCESS);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
__elfN(map_insert)(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
|
|
|
|
|
vm_offset_t start, vm_offset_t end, vm_prot_t prot,
|
|
|
|
|
vm_prot_t max, int cow)
|
|
|
|
|
{
|
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
|
|
if (start != trunc_page(start)) {
|
2002-08-25 22:36:52 +00:00
|
|
|
|
rv = __elfN(map_partial)(map, object, offset, start,
|
|
|
|
|
round_page(start), prot, max);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if (rv)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (rv);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
offset += round_page(start) - start;
|
|
|
|
|
start = round_page(start);
|
|
|
|
|
}
|
|
|
|
|
if (end != round_page(end)) {
|
2002-08-25 22:36:52 +00:00
|
|
|
|
rv = __elfN(map_partial)(map, object, offset +
|
|
|
|
|
trunc_page(end) - start, trunc_page(end), end, prot, max);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if (rv)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (rv);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
end = trunc_page(end);
|
|
|
|
|
}
|
|
|
|
|
if (end > start) {
|
|
|
|
|
if (offset & PAGE_MASK) {
|
|
|
|
|
vm_offset_t data_buf, off;
|
|
|
|
|
vm_size_t sz;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The mapping is not page aligned. This means we have
|
|
|
|
|
* to copy the data. Sigh.
|
|
|
|
|
*/
|
2002-08-25 22:36:52 +00:00
|
|
|
|
rv = vm_map_find(map, 0, 0, &start, end - start,
|
|
|
|
|
FALSE, prot, max, 0);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if (rv)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (rv);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
while (start < end) {
|
|
|
|
|
vm_object_reference(object);
|
|
|
|
|
rv = vm_map_find(exec_map,
|
|
|
|
|
object,
|
|
|
|
|
trunc_page(offset),
|
|
|
|
|
&data_buf,
|
2002-08-24 22:55:16 +00:00
|
|
|
|
2 * PAGE_SIZE,
|
2002-07-20 02:56:12 +00:00
|
|
|
|
TRUE,
|
|
|
|
|
VM_PROT_READ,
|
|
|
|
|
VM_PROT_ALL,
|
|
|
|
|
(MAP_COPY_ON_WRITE
|
|
|
|
|
| MAP_PREFAULT_PARTIAL));
|
|
|
|
|
if (rv != KERN_SUCCESS) {
|
|
|
|
|
vm_object_deallocate(object);
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (rv);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
|
|
|
|
off = offset - trunc_page(offset);
|
|
|
|
|
sz = end - start;
|
|
|
|
|
if (sz > PAGE_SIZE)
|
|
|
|
|
sz = PAGE_SIZE;
|
2002-08-24 22:55:16 +00:00
|
|
|
|
error = copyout((caddr_t)data_buf + off,
|
2002-08-25 22:36:52 +00:00
|
|
|
|
(caddr_t)start, sz);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_map_remove(exec_map, data_buf,
|
2002-08-25 22:36:52 +00:00
|
|
|
|
data_buf + 2 * PAGE_SIZE);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if (error) {
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (KERN_FAILURE);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
|
|
|
|
start += sz;
|
|
|
|
|
}
|
|
|
|
|
rv = KERN_SUCCESS;
|
|
|
|
|
} else {
|
|
|
|
|
vm_map_lock(map);
|
2002-08-24 22:55:16 +00:00
|
|
|
|
rv = vm_map_insert(map, object, offset, start, end,
|
2002-08-25 22:36:52 +00:00
|
|
|
|
prot, max, cow);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_map_unlock(map);
|
|
|
|
|
}
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (rv);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
} else {
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (KERN_SUCCESS);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
__elfN(load_section)(struct proc *p, struct vmspace *vmspace,
|
|
|
|
|
struct vnode *vp, vm_object_t object, vm_offset_t offset,
|
|
|
|
|
caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot,
|
|
|
|
|
size_t pagesize)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
|
|
|
|
size_t map_len;
|
|
|
|
|
vm_offset_t map_addr;
|
2002-12-16 19:24:43 +00:00
|
|
|
|
int error, rv, cow;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
size_t copy_len;
|
1998-10-18 15:55:12 +00:00
|
|
|
|
vm_offset_t file_addr;
|
|
|
|
|
vm_offset_t data_buf = 0;
|
|
|
|
|
|
2001-07-04 16:20:28 +00:00
|
|
|
|
GIANT_REQUIRED;
|
|
|
|
|
|
1998-10-18 15:55:12 +00:00
|
|
|
|
error = 0;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2000-07-23 06:49:46 +00:00
|
|
|
|
/*
|
|
|
|
|
* It's necessary to fail if the filsz + offset taken from the
|
|
|
|
|
* header is greater than the actual file pager object's size.
|
|
|
|
|
* If we were to allow this, then the vm_map_find() below would
|
|
|
|
|
* walk right off the end of the file object and into the ether.
|
|
|
|
|
*
|
|
|
|
|
* While I'm here, might as well check for something else that
|
|
|
|
|
* is invalid: filsz cannot be greater than memsz.
|
|
|
|
|
*/
|
|
|
|
|
if ((off_t)filsz + offset > object->un_pager.vnp.vnp_size ||
|
|
|
|
|
filsz > memsz) {
|
|
|
|
|
uprintf("elf_load_section: truncated ELF file\n");
|
|
|
|
|
return (ENOEXEC);
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
#define trunc_page_ps(va, ps) ((va) & ~(ps - 1))
|
|
|
|
|
#define round_page_ps(va, ps) (((va) + (ps - 1)) & ~(ps - 1))
|
|
|
|
|
|
|
|
|
|
map_addr = trunc_page_ps((vm_offset_t)vmaddr, pagesize);
|
|
|
|
|
file_addr = trunc_page_ps(offset, pagesize);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1998-10-18 15:55:12 +00:00
|
|
|
|
/*
|
|
|
|
|
* We have two choices. We can either clear the data in the last page
|
|
|
|
|
* of an oversized mapping, or we can start the anon mapping a page
|
|
|
|
|
* early and copy the initialized data into that first page. We
|
|
|
|
|
* choose the second..
|
|
|
|
|
*/
|
1996-03-10 08:42:54 +00:00
|
|
|
|
if (memsz > filsz)
|
2002-08-24 22:55:16 +00:00
|
|
|
|
map_len = trunc_page_ps(offset + filsz, pagesize) - file_addr;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
else
|
2002-08-24 22:55:16 +00:00
|
|
|
|
map_len = round_page_ps(offset + filsz, pagesize) - file_addr;
|
1998-10-18 15:55:12 +00:00
|
|
|
|
|
|
|
|
|
if (map_len != 0) {
|
|
|
|
|
vm_object_reference(object);
|
2002-12-16 19:24:43 +00:00
|
|
|
|
|
|
|
|
|
/* cow flags: don't dump readonly sections in core */
|
|
|
|
|
cow = MAP_COPY_ON_WRITE | MAP_PREFAULT |
|
|
|
|
|
(prot & VM_PROT_WRITE ? 0 : MAP_DISABLE_COREDUMP);
|
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
rv = __elfN(map_insert)(&vmspace->vm_map,
|
1998-10-18 15:55:12 +00:00
|
|
|
|
object,
|
|
|
|
|
file_addr, /* file offset */
|
|
|
|
|
map_addr, /* virtual start */
|
|
|
|
|
map_addr + map_len,/* virtual end */
|
|
|
|
|
prot,
|
|
|
|
|
VM_PROT_ALL,
|
2002-12-16 19:24:43 +00:00
|
|
|
|
cow);
|
1999-11-20 03:03:14 +00:00
|
|
|
|
if (rv != KERN_SUCCESS) {
|
|
|
|
|
vm_object_deallocate(object);
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (EINVAL);
|
1999-11-20 03:03:14 +00:00
|
|
|
|
}
|
1998-10-18 15:55:12 +00:00
|
|
|
|
|
|
|
|
|
/* we can stop now if we've covered it all */
|
2001-05-19 01:28:09 +00:00
|
|
|
|
if (memsz == filsz) {
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (0);
|
2001-05-19 01:28:09 +00:00
|
|
|
|
}
|
1998-10-18 15:55:12 +00:00
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
1998-10-18 15:55:12 +00:00
|
|
|
|
* We have to get the remaining bit of the file into the first part
|
|
|
|
|
* of the oversized map segment. This is normally because the .data
|
|
|
|
|
* segment in the file is extended to provide bss. It's a neat idea
|
|
|
|
|
* to try and save a page, but it's a pain in the behind to implement.
|
1996-03-10 08:42:54 +00:00
|
|
|
|
*/
|
2002-07-20 02:56:12 +00:00
|
|
|
|
copy_len = (offset + filsz) - trunc_page_ps(offset + filsz, pagesize);
|
|
|
|
|
map_addr = trunc_page_ps((vm_offset_t)vmaddr + filsz, pagesize);
|
2002-08-25 20:48:45 +00:00
|
|
|
|
map_len = round_page_ps((vm_offset_t)vmaddr + memsz, pagesize) -
|
|
|
|
|
map_addr;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1998-10-18 15:55:12 +00:00
|
|
|
|
/* This had damn well better be true! */
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if (map_len != 0) {
|
2002-08-25 22:36:52 +00:00
|
|
|
|
rv = __elfN(map_insert)(&vmspace->vm_map, NULL, 0, map_addr,
|
|
|
|
|
map_addr + map_len, VM_PROT_ALL, VM_PROT_ALL, 0);
|
2001-05-19 01:28:09 +00:00
|
|
|
|
if (rv != KERN_SUCCESS) {
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (EINVAL);
|
2001-12-16 17:21:16 +00:00
|
|
|
|
}
|
1996-03-10 22:37:34 +00:00
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1998-10-18 15:55:12 +00:00
|
|
|
|
if (copy_len != 0) {
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_offset_t off;
|
1998-10-18 15:55:12 +00:00
|
|
|
|
vm_object_reference(object);
|
|
|
|
|
rv = vm_map_find(exec_map,
|
2002-07-20 02:56:12 +00:00
|
|
|
|
object,
|
1998-10-18 15:55:12 +00:00
|
|
|
|
trunc_page(offset + filsz),
|
|
|
|
|
&data_buf,
|
|
|
|
|
PAGE_SIZE,
|
|
|
|
|
TRUE,
|
|
|
|
|
VM_PROT_READ,
|
|
|
|
|
VM_PROT_ALL,
|
1999-05-17 00:53:56 +00:00
|
|
|
|
MAP_COPY_ON_WRITE | MAP_PREFAULT_PARTIAL);
|
1998-10-18 15:55:12 +00:00
|
|
|
|
if (rv != KERN_SUCCESS) {
|
|
|
|
|
vm_object_deallocate(object);
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (EINVAL);
|
1998-10-18 15:55:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send the page fragment to user space */
|
2002-08-25 22:36:52 +00:00
|
|
|
|
off = trunc_page_ps(offset + filsz, pagesize) -
|
|
|
|
|
trunc_page(offset + filsz);
|
2002-08-24 22:55:16 +00:00
|
|
|
|
error = copyout((caddr_t)data_buf + off, (caddr_t)map_addr,
|
2002-08-25 22:36:52 +00:00
|
|
|
|
copy_len);
|
1998-10-18 15:55:12 +00:00
|
|
|
|
vm_map_remove(exec_map, data_buf, data_buf + PAGE_SIZE);
|
2001-05-19 01:28:09 +00:00
|
|
|
|
if (error) {
|
1998-10-18 15:55:12 +00:00
|
|
|
|
return (error);
|
2001-05-19 01:28:09 +00:00
|
|
|
|
}
|
1998-10-18 15:55:12 +00:00
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-07-20 02:56:12 +00:00
|
|
|
|
* set it to the specified protection.
|
|
|
|
|
* XXX had better undo the damage from pasting over the cracks here!
|
1996-03-10 08:42:54 +00:00
|
|
|
|
*/
|
2002-07-20 02:56:12 +00:00
|
|
|
|
vm_map_protect(&vmspace->vm_map, trunc_page(map_addr),
|
|
|
|
|
round_page(map_addr + map_len), prot, FALSE);
|
1996-03-10 22:37:34 +00:00
|
|
|
|
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (error);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-02-20 23:52:34 +00:00
|
|
|
|
/*
|
|
|
|
|
* Load the file "file" into memory. It may be either a shared object
|
|
|
|
|
* or an executable.
|
|
|
|
|
*
|
|
|
|
|
* The "addr" reference parameter is in/out. On entry, it specifies
|
|
|
|
|
* the address where a shared object should be loaded. If the file is
|
|
|
|
|
* an executable, this value is ignored. On exit, "addr" specifies
|
|
|
|
|
* where the file was actually loaded.
|
|
|
|
|
*
|
|
|
|
|
* The "entry" reference parameter is out only. On exit, it specifies
|
|
|
|
|
* the entry point for the loaded file.
|
|
|
|
|
*/
|
1996-03-10 08:42:54 +00:00
|
|
|
|
static int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(load_file)(struct proc *p, const char *file, u_long *addr,
|
|
|
|
|
u_long *entry, size_t pagesize)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
2001-08-16 16:14:26 +00:00
|
|
|
|
struct {
|
|
|
|
|
struct nameidata nd;
|
|
|
|
|
struct vattr attr;
|
|
|
|
|
struct image_params image_params;
|
|
|
|
|
} *tempdata;
|
1999-01-27 21:50:00 +00:00
|
|
|
|
const Elf_Ehdr *hdr = NULL;
|
|
|
|
|
const Elf_Phdr *phdr = NULL;
|
2001-08-16 16:14:26 +00:00
|
|
|
|
struct nameidata *nd;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
struct vmspace *vmspace = p->p_vmspace;
|
2001-08-16 16:14:26 +00:00
|
|
|
|
struct vattr *attr;
|
|
|
|
|
struct image_params *imgp;
|
1998-10-18 15:55:12 +00:00
|
|
|
|
vm_prot_t prot;
|
1999-02-20 23:52:34 +00:00
|
|
|
|
u_long rbase;
|
|
|
|
|
u_long base_addr = 0;
|
|
|
|
|
int error, i, numsegs;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2001-09-12 08:38:13 +00:00
|
|
|
|
if (curthread->td_proc != p)
|
|
|
|
|
panic("elf_load_file - thread"); /* XXXKSE DIAGNOSTIC */
|
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
|
tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK);
|
2001-08-16 16:14:26 +00:00
|
|
|
|
nd = &tempdata->nd;
|
|
|
|
|
attr = &tempdata->attr;
|
|
|
|
|
imgp = &tempdata->image_params;
|
|
|
|
|
|
1998-03-02 05:47:58 +00:00
|
|
|
|
/*
|
|
|
|
|
* Initialize part of the common data
|
|
|
|
|
*/
|
|
|
|
|
imgp->proc = p;
|
Remove reference to struct execve_args from struct imgact, which
describes an image activation instance. Instead, make use of the
existing fname structure entry, and introduce two new entries,
userspace_argv, and userspace_envv. With the addition of
mac_execve(), this divorces the image structure from the specifics
of the execve() system call, removes a redundant pointer, etc.
No semantic change from current behavior, but it means that the
structure doesn't depend on syscalls.master-generated includes.
There seems to be some redundant initialization of imgact entries,
which I have maintained, but which could probably use some cleaning
up at some point.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, Network Associates Laboratories
2002-11-05 01:59:56 +00:00
|
|
|
|
imgp->userspace_argv = NULL;
|
|
|
|
|
imgp->userspace_envv = NULL;
|
2001-08-16 16:14:26 +00:00
|
|
|
|
imgp->attr = attr;
|
1998-03-02 05:47:58 +00:00
|
|
|
|
imgp->firstpage = NULL;
|
|
|
|
|
imgp->image_header = (char *)kmem_alloc_wait(exec_map, PAGE_SIZE);
|
2002-07-06 07:00:01 +00:00
|
|
|
|
imgp->object = NULL;
|
2002-11-08 20:49:50 +00:00
|
|
|
|
imgp->execlabel = NULL;
|
1998-03-02 05:47:58 +00:00
|
|
|
|
|
|
|
|
|
if (imgp->image_header == NULL) {
|
2001-08-16 16:14:26 +00:00
|
|
|
|
nd->ni_vp = NULL;
|
1998-03-02 05:47:58 +00:00
|
|
|
|
error = ENOMEM;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2001-09-12 08:38:13 +00:00
|
|
|
|
/* XXXKSE */
|
2002-07-20 02:56:12 +00:00
|
|
|
|
NDINIT(nd, LOOKUP, LOCKLEAF|FOLLOW, UIO_SYSSPACE, file, curthread);
|
|
|
|
|
|
2001-08-16 16:14:26 +00:00
|
|
|
|
if ((error = namei(nd)) != 0) {
|
|
|
|
|
nd->ni_vp = NULL;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2001-08-16 16:14:26 +00:00
|
|
|
|
NDFREE(nd, NDF_ONLY_PNBUF);
|
|
|
|
|
imgp->vp = nd->ni_vp;
|
1998-03-02 05:47:58 +00:00
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
/*
|
|
|
|
|
* Check permissions, modes, uid, etc on the file, and "open" it.
|
|
|
|
|
*/
|
1998-03-02 05:47:58 +00:00
|
|
|
|
error = exec_check_permissions(imgp);
|
|
|
|
|
if (error) {
|
2001-09-12 08:38:13 +00:00
|
|
|
|
VOP_UNLOCK(nd->ni_vp, 0, curthread); /* XXXKSE */
|
1998-03-02 05:47:58 +00:00
|
|
|
|
goto fail;
|
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1998-03-02 05:47:58 +00:00
|
|
|
|
error = exec_map_first_page(imgp);
|
2000-07-23 06:49:46 +00:00
|
|
|
|
/*
|
|
|
|
|
* Also make certain that the interpreter stays the same, so set
|
2002-08-04 10:29:36 +00:00
|
|
|
|
* its VV_TEXT flag, too.
|
2000-07-23 06:49:46 +00:00
|
|
|
|
*/
|
|
|
|
|
if (error == 0)
|
2002-08-04 10:29:36 +00:00
|
|
|
|
nd->ni_vp->v_vflag |= VV_TEXT;
|
|
|
|
|
|
2002-07-06 07:00:01 +00:00
|
|
|
|
VOP_GETVOBJECT(nd->ni_vp, &imgp->object);
|
|
|
|
|
vm_object_reference(imgp->object);
|
|
|
|
|
|
2001-09-12 08:38:13 +00:00
|
|
|
|
VOP_UNLOCK(nd->ni_vp, 0, curthread); /* XXXKSE */
|
1996-03-10 08:42:54 +00:00
|
|
|
|
if (error)
|
2002-07-20 02:56:12 +00:00
|
|
|
|
goto fail;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1999-01-27 21:50:00 +00:00
|
|
|
|
hdr = (const Elf_Ehdr *)imgp->image_header;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if ((error = __elfN(check_header)(hdr)) != 0)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
goto fail;
|
1999-02-20 23:52:34 +00:00
|
|
|
|
if (hdr->e_type == ET_DYN)
|
|
|
|
|
rbase = *addr;
|
|
|
|
|
else if (hdr->e_type == ET_EXEC)
|
|
|
|
|
rbase = 0;
|
|
|
|
|
else {
|
|
|
|
|
error = ENOEXEC;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1998-03-02 05:47:58 +00:00
|
|
|
|
/* Only support headers that fit within first page for now */
|
1998-10-18 15:55:12 +00:00
|
|
|
|
if ((hdr->e_phoff > PAGE_SIZE) ||
|
|
|
|
|
(hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) {
|
1998-03-02 05:47:58 +00:00
|
|
|
|
error = ENOEXEC;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
1999-01-27 21:50:00 +00:00
|
|
|
|
phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1999-02-20 23:52:34 +00:00
|
|
|
|
for (i = 0, numsegs = 0; i < hdr->e_phnum; i++) {
|
1998-10-18 15:55:12 +00:00
|
|
|
|
if (phdr[i].p_type == PT_LOAD) { /* Loadable segment */
|
|
|
|
|
prot = 0;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
if (phdr[i].p_flags & PF_X)
|
|
|
|
|
prot |= VM_PROT_EXECUTE;
|
|
|
|
|
if (phdr[i].p_flags & PF_W)
|
|
|
|
|
prot |= VM_PROT_WRITE;
|
|
|
|
|
if (phdr[i].p_flags & PF_R)
|
|
|
|
|
prot |= VM_PROT_READ;
|
|
|
|
|
|
2002-08-25 22:36:52 +00:00
|
|
|
|
if ((error = __elfN(load_section)(p, vmspace,
|
|
|
|
|
nd->ni_vp, imgp->object, phdr[i].p_offset,
|
|
|
|
|
(caddr_t)(uintptr_t)phdr[i].p_vaddr + rbase,
|
|
|
|
|
phdr[i].p_memsz, phdr[i].p_filesz, prot,
|
|
|
|
|
pagesize)) != 0)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
goto fail;
|
|
|
|
|
/*
|
1999-02-20 23:52:34 +00:00
|
|
|
|
* Establish the base address if this is the
|
|
|
|
|
* first segment.
|
1996-03-10 08:42:54 +00:00
|
|
|
|
*/
|
1999-02-20 23:52:34 +00:00
|
|
|
|
if (numsegs == 0)
|
2002-08-25 20:48:45 +00:00
|
|
|
|
base_addr = trunc_page(phdr[i].p_vaddr +
|
|
|
|
|
rbase);
|
1999-02-20 23:52:34 +00:00
|
|
|
|
numsegs++;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-02-20 23:52:34 +00:00
|
|
|
|
*addr = base_addr;
|
2002-08-24 22:55:16 +00:00
|
|
|
|
*entry = (unsigned long)hdr->e_entry + rbase;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
fail:
|
1998-03-02 05:47:58 +00:00
|
|
|
|
if (imgp->firstpage)
|
|
|
|
|
exec_unmap_first_page(imgp);
|
|
|
|
|
if (imgp->image_header)
|
|
|
|
|
kmem_free_wakeup(exec_map, (vm_offset_t)imgp->image_header,
|
2002-08-25 22:36:52 +00:00
|
|
|
|
PAGE_SIZE);
|
2002-07-06 07:00:01 +00:00
|
|
|
|
if (imgp->object)
|
|
|
|
|
vm_object_deallocate(imgp->object);
|
|
|
|
|
|
2001-08-16 16:14:26 +00:00
|
|
|
|
if (nd->ni_vp)
|
|
|
|
|
vrele(nd->ni_vp);
|
|
|
|
|
|
|
|
|
|
free(tempdata, M_TEMP);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (error);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-02-09 06:11:36 +00:00
|
|
|
|
static int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__CONCAT(exec_, __elfN(imgact))(struct image_params *imgp)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
2002-08-24 22:55:16 +00:00
|
|
|
|
const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header;
|
1998-10-25 17:44:59 +00:00
|
|
|
|
const Elf_Phdr *phdr;
|
1998-06-07 17:13:14 +00:00
|
|
|
|
Elf_Auxargs *elf_auxargs = NULL;
|
1997-04-13 01:48:35 +00:00
|
|
|
|
struct vmspace *vmspace;
|
1998-10-18 15:55:12 +00:00
|
|
|
|
vm_prot_t prot;
|
2002-09-04 04:42:12 +00:00
|
|
|
|
u_long text_size = 0, data_size = 0, total_size = 0;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
u_long text_addr = 0, data_addr = 0;
|
2002-08-30 18:09:46 +00:00
|
|
|
|
u_long seg_size, seg_addr;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
u_long addr, entry = 0, proghdr = 0;
|
1998-10-18 15:55:12 +00:00
|
|
|
|
int error, i;
|
1998-03-02 05:47:58 +00:00
|
|
|
|
const char *interp = NULL;
|
1998-10-11 19:22:07 +00:00
|
|
|
|
Elf_Brandinfo *brand_info;
|
2001-08-16 16:14:26 +00:00
|
|
|
|
char *path;
|
2002-08-13 06:55:28 +00:00
|
|
|
|
struct thread *td = curthread;
|
2002-09-02 04:50:57 +00:00
|
|
|
|
struct sysentvec *sv;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2001-07-04 16:20:28 +00:00
|
|
|
|
GIANT_REQUIRED;
|
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
/*
|
|
|
|
|
* Do we have a valid ELF header ?
|
|
|
|
|
*/
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if (__elfN(check_header)(hdr) != 0 || hdr->e_type != ET_EXEC)
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (-1);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* From here on down, we return an errno, not -1, as we've
|
|
|
|
|
* detected an ELF file.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if ((hdr->e_phoff > PAGE_SIZE) ||
|
1998-10-18 15:55:12 +00:00
|
|
|
|
(hdr->e_phoff + hdr->e_phentsize * hdr->e_phnum) > PAGE_SIZE) {
|
1998-03-02 05:47:58 +00:00
|
|
|
|
/* Only support headers in first page for now */
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (ENOEXEC);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
2002-08-24 22:55:16 +00:00
|
|
|
|
phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
/*
|
|
|
|
|
* From this point on, we may have resources that need to be freed.
|
|
|
|
|
*/
|
2000-07-23 06:49:46 +00:00
|
|
|
|
|
2002-08-13 06:55:28 +00:00
|
|
|
|
VOP_UNLOCK(imgp->vp, 0, td);
|
2000-07-23 06:49:46 +00:00
|
|
|
|
|
2002-09-02 04:50:57 +00:00
|
|
|
|
for (i = 0; i < hdr->e_phnum; i++) {
|
|
|
|
|
switch (phdr[i].p_type) {
|
|
|
|
|
case PT_INTERP: /* Path to interpreter */
|
|
|
|
|
if (phdr[i].p_filesz > MAXPATHLEN ||
|
|
|
|
|
phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) {
|
|
|
|
|
error = ENOEXEC;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
interp = imgp->image_header + phdr[i].p_offset;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2002-07-20 02:56:12 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-02 04:50:57 +00:00
|
|
|
|
brand_info = __elfN(get_brandinfo)(hdr, interp);
|
|
|
|
|
if (brand_info == NULL) {
|
|
|
|
|
uprintf("ELF binary type \"%u\" not known.\n",
|
|
|
|
|
hdr->e_ident[EI_OSABI]);
|
|
|
|
|
error = ENOEXEC;
|
|
|
|
|
goto fail;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
2002-09-02 04:50:57 +00:00
|
|
|
|
sv = brand_info->sysvec;
|
|
|
|
|
|
|
|
|
|
if ((error = exec_extract_strings(imgp)) != 0)
|
|
|
|
|
goto fail;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
|
2002-09-21 22:07:17 +00:00
|
|
|
|
exec_new_vmspace(imgp, sv);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1997-04-13 01:48:35 +00:00
|
|
|
|
vmspace = imgp->proc->p_vmspace;
|
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
for (i = 0; i < hdr->e_phnum; i++) {
|
2002-08-24 22:55:16 +00:00
|
|
|
|
switch (phdr[i].p_type) {
|
1996-03-10 08:42:54 +00:00
|
|
|
|
case PT_LOAD: /* Loadable segment */
|
1998-10-18 15:55:12 +00:00
|
|
|
|
prot = 0;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
if (phdr[i].p_flags & PF_X)
|
|
|
|
|
prot |= VM_PROT_EXECUTE;
|
|
|
|
|
if (phdr[i].p_flags & PF_W)
|
|
|
|
|
prot |= VM_PROT_WRITE;
|
|
|
|
|
if (phdr[i].p_flags & PF_R)
|
|
|
|
|
prot |= VM_PROT_READ;
|
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
#if defined(__ia64__) && __ELF_WORD_SIZE == 32 && defined(IA32_ME_HARDER)
|
|
|
|
|
/*
|
|
|
|
|
* Some x86 binaries assume read == executable,
|
|
|
|
|
* notably the M3 runtime and therefore cvsup
|
|
|
|
|
*/
|
|
|
|
|
if (prot & VM_PROT_READ)
|
|
|
|
|
prot |= VM_PROT_EXECUTE;
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-08-25 22:36:52 +00:00
|
|
|
|
if ((error = __elfN(load_section)(imgp->proc, vmspace,
|
|
|
|
|
imgp->vp, imgp->object, phdr[i].p_offset,
|
|
|
|
|
(caddr_t)(uintptr_t)phdr[i].p_vaddr,
|
|
|
|
|
phdr[i].p_memsz, phdr[i].p_filesz, prot,
|
2002-09-02 04:50:57 +00:00
|
|
|
|
sv->sv_pagesize)) != 0)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
goto fail;
|
|
|
|
|
|
2002-08-30 18:09:46 +00:00
|
|
|
|
seg_addr = trunc_page(phdr[i].p_vaddr);
|
|
|
|
|
seg_size = round_page(phdr[i].p_memsz +
|
2002-09-02 02:41:26 +00:00
|
|
|
|
phdr[i].p_vaddr - seg_addr);
|
2002-08-30 18:09:46 +00:00
|
|
|
|
|
|
|
|
|
/*
|
2002-09-04 04:42:12 +00:00
|
|
|
|
* Is this .text or .data? We can't use
|
|
|
|
|
* VM_PROT_WRITE or VM_PROT_EXEC, it breaks the
|
|
|
|
|
* alpha terribly and possibly does other bad
|
|
|
|
|
* things so we stick to the old way of figuring
|
|
|
|
|
* it out: If the segment contains the program
|
|
|
|
|
* entry point, it's a text segment, otherwise it
|
|
|
|
|
* is a data segment.
|
|
|
|
|
*
|
|
|
|
|
* Note that obreak() assumes that data_addr +
|
|
|
|
|
* data_size == end of data load area, and the ELF
|
|
|
|
|
* file format expects segments to be sorted by
|
|
|
|
|
* address. If multiple data segments exist, the
|
|
|
|
|
* last one will be used.
|
2002-08-30 18:09:46 +00:00
|
|
|
|
*/
|
2002-09-03 21:18:17 +00:00
|
|
|
|
if (hdr->e_entry >= phdr[i].p_vaddr &&
|
|
|
|
|
hdr->e_entry < (phdr[i].p_vaddr +
|
|
|
|
|
phdr[i].p_memsz)) {
|
|
|
|
|
text_size = seg_size;
|
|
|
|
|
text_addr = seg_addr;
|
|
|
|
|
entry = (u_long)hdr->e_entry;
|
|
|
|
|
} else {
|
2002-09-04 04:42:12 +00:00
|
|
|
|
data_size = seg_size;
|
|
|
|
|
data_addr = seg_addr;
|
2002-08-30 18:09:46 +00:00
|
|
|
|
}
|
2002-09-04 04:42:12 +00:00
|
|
|
|
total_size += seg_size;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
break;
|
|
|
|
|
case PT_PHDR: /* Program header table info */
|
|
|
|
|
proghdr = phdr[i].p_vaddr;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
1998-10-18 15:55:12 +00:00
|
|
|
|
break;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-23 01:57:39 +00:00
|
|
|
|
|
|
|
|
|
if (data_addr == 0 && data_size == 0) {
|
|
|
|
|
data_addr = text_addr;
|
|
|
|
|
data_size = text_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Check limits. It should be safe to check the
|
|
|
|
|
* limits after loading the segments since we do
|
|
|
|
|
* not actually fault in all the segments pages.
|
|
|
|
|
*/
|
|
|
|
|
if (data_size >
|
|
|
|
|
imgp->proc->p_rlimit[RLIMIT_DATA].rlim_cur ||
|
|
|
|
|
text_size > maxtsiz ||
|
|
|
|
|
total_size >
|
|
|
|
|
imgp->proc->p_rlimit[RLIMIT_VMEM].rlim_cur) {
|
|
|
|
|
error = ENOMEM;
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
vmspace->vm_tsize = text_size >> PAGE_SHIFT;
|
1998-07-15 05:00:26 +00:00
|
|
|
|
vmspace->vm_taddr = (caddr_t)(uintptr_t)text_addr;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
vmspace->vm_dsize = data_size >> PAGE_SHIFT;
|
1998-07-15 05:00:26 +00:00
|
|
|
|
vmspace->vm_daddr = (caddr_t)(uintptr_t)data_addr;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1999-02-07 23:49:56 +00:00
|
|
|
|
addr = ELF_RTLD_ADDR(vmspace);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1996-10-16 17:51:08 +00:00
|
|
|
|
imgp->entry_addr = entry;
|
|
|
|
|
|
2002-09-02 04:50:57 +00:00
|
|
|
|
imgp->proc->p_sysent = sv;
|
1998-10-11 19:22:07 +00:00
|
|
|
|
if (interp != NULL) {
|
2003-02-19 05:47:46 +00:00
|
|
|
|
path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
|
2002-09-02 02:41:26 +00:00
|
|
|
|
snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path,
|
|
|
|
|
interp);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
if ((error = __elfN(load_file)(imgp->proc, path, &addr,
|
2002-09-02 04:50:57 +00:00
|
|
|
|
&imgp->entry_addr, sv->sv_pagesize)) != 0) {
|
2002-08-25 22:36:52 +00:00
|
|
|
|
if ((error = __elfN(load_file)(imgp->proc, interp,
|
2002-09-02 04:50:57 +00:00
|
|
|
|
&addr, &imgp->entry_addr, sv->sv_pagesize)) != 0) {
|
2002-08-25 20:48:45 +00:00
|
|
|
|
uprintf("ELF interpreter %s not found\n",
|
|
|
|
|
path);
|
2001-08-16 16:14:26 +00:00
|
|
|
|
free(path, M_TEMP);
|
1999-07-05 18:38:29 +00:00
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
2001-08-16 16:14:26 +00:00
|
|
|
|
free(path, M_TEMP);
|
1996-10-16 17:51:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
/*
|
|
|
|
|
* Construct auxargs table (used by the fixup routine)
|
|
|
|
|
*/
|
2003-02-19 05:47:46 +00:00
|
|
|
|
elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
elf_auxargs->execfd = -1;
|
|
|
|
|
elf_auxargs->phdr = proghdr;
|
|
|
|
|
elf_auxargs->phent = hdr->e_phentsize;
|
|
|
|
|
elf_auxargs->phnum = hdr->e_phnum;
|
|
|
|
|
elf_auxargs->pagesz = PAGE_SIZE;
|
|
|
|
|
elf_auxargs->base = addr;
|
|
|
|
|
elf_auxargs->flags = 0;
|
|
|
|
|
elf_auxargs->entry = entry;
|
|
|
|
|
elf_auxargs->trace = elf_trace;
|
|
|
|
|
|
|
|
|
|
imgp->auxargs = elf_auxargs;
|
|
|
|
|
imgp->interpreted = 0;
|
|
|
|
|
|
|
|
|
|
fail:
|
2002-08-13 06:55:28 +00:00
|
|
|
|
vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY, td);
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (error);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-01-04 22:07:48 +00:00
|
|
|
|
#define suword __CONCAT(suword, __ELF_WORD_SIZE)
|
2002-07-20 02:56:12 +00:00
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
__elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp)
|
1996-03-10 08:42:54 +00:00
|
|
|
|
{
|
1998-06-07 17:13:14 +00:00
|
|
|
|
Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs;
|
2003-01-04 22:07:48 +00:00
|
|
|
|
Elf_Addr *base;
|
|
|
|
|
Elf_Addr *pos;
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
2003-01-04 22:07:48 +00:00
|
|
|
|
base = (Elf_Addr *)*stack_base;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
pos = base + (imgp->argc + imgp->envc + 2);
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
|
|
|
|
if (args->trace) {
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_DEBUG, 1);
|
|
|
|
|
}
|
|
|
|
|
if (args->execfd != -1) {
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd);
|
|
|
|
|
}
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_PHDR, args->phdr);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_PHENT, args->phent);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_FLAGS, args->flags);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_ENTRY, args->entry);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_BASE, args->base);
|
|
|
|
|
AUXARGS_ENTRY(pos, AT_NULL, 0);
|
|
|
|
|
|
|
|
|
|
free(imgp->auxargs, M_TEMP);
|
|
|
|
|
imgp->auxargs = NULL;
|
|
|
|
|
|
2002-07-20 02:56:12 +00:00
|
|
|
|
base--;
|
2002-08-24 22:55:16 +00:00
|
|
|
|
suword(base, (long)imgp->argc);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
*stack_base = (register_t *)base;
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (0);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
}
|
1996-03-10 08:42:54 +00:00
|
|
|
|
|
1998-09-14 22:46:08 +00:00
|
|
|
|
/*
|
|
|
|
|
* Code for generating ELF core dumps.
|
|
|
|
|
*/
|
|
|
|
|
|
2002-03-19 21:25:46 +00:00
|
|
|
|
typedef void (*segment_callback)(vm_map_entry_t, void *);
|
1998-09-16 02:04:05 +00:00
|
|
|
|
|
|
|
|
|
/* Closure for cb_put_phdr(). */
|
|
|
|
|
struct phdr_closure {
|
|
|
|
|
Elf_Phdr *phdr; /* Program header to fill in */
|
|
|
|
|
Elf_Off offset; /* Offset of segment in core file */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Closure for cb_size_segment(). */
|
|
|
|
|
struct sseg_closure {
|
|
|
|
|
int count; /* Count of writable segments. */
|
|
|
|
|
size_t size; /* Total size of all writable segments. */
|
|
|
|
|
};
|
|
|
|
|
|
2002-03-19 21:25:46 +00:00
|
|
|
|
static void cb_put_phdr(vm_map_entry_t, void *);
|
|
|
|
|
static void cb_size_segment(vm_map_entry_t, void *);
|
|
|
|
|
static void each_writable_segment(struct proc *, segment_callback, void *);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *,
|
2002-03-19 21:25:46 +00:00
|
|
|
|
int, void *, size_t);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
static void __elfN(puthdr)(struct proc *, void *, size_t *,
|
2002-03-19 21:25:46 +00:00
|
|
|
|
const prstatus_t *, const prfpregset_t *, const prpsinfo_t *, int);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
static void __elfN(putnote)(void *, size_t *, const char *, int,
|
2002-03-19 21:25:46 +00:00
|
|
|
|
const void *, size_t);
|
1998-09-14 22:46:08 +00:00
|
|
|
|
|
|
|
|
|
extern int osreldate;
|
|
|
|
|
|
|
|
|
|
int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(coredump)(td, vp, limit)
|
2001-09-12 08:38:13 +00:00
|
|
|
|
struct thread *td;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
register struct vnode *vp;
|
1999-09-01 00:29:56 +00:00
|
|
|
|
off_t limit;
|
|
|
|
|
{
|
2001-09-12 08:38:13 +00:00
|
|
|
|
register struct proc *p = td->td_proc;
|
2002-02-27 18:32:23 +00:00
|
|
|
|
register struct ucred *cred = td->td_ucred;
|
1999-09-01 00:29:56 +00:00
|
|
|
|
int error = 0;
|
1998-09-16 02:04:05 +00:00
|
|
|
|
struct sseg_closure seginfo;
|
|
|
|
|
void *hdr;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
size_t hdrsize;
|
|
|
|
|
|
1998-09-16 02:04:05 +00:00
|
|
|
|
/* Size the program segments. */
|
|
|
|
|
seginfo.count = 0;
|
|
|
|
|
seginfo.size = 0;
|
|
|
|
|
each_writable_segment(p, cb_size_segment, &seginfo);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Calculate the size of the core file header area by making
|
|
|
|
|
* a dry run of generating it. Nothing is written, but the
|
|
|
|
|
* size is calculated.
|
|
|
|
|
*/
|
|
|
|
|
hdrsize = 0;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(puthdr)((struct proc *)NULL, (void *)NULL, &hdrsize,
|
1998-09-16 02:04:05 +00:00
|
|
|
|
(const prstatus_t *)NULL, (const prfpregset_t *)NULL,
|
|
|
|
|
(const prpsinfo_t *)NULL, seginfo.count);
|
|
|
|
|
|
1999-09-01 00:29:56 +00:00
|
|
|
|
if (hdrsize + seginfo.size >= limit)
|
1998-09-14 22:46:08 +00:00
|
|
|
|
return (EFAULT);
|
1998-09-16 02:04:05 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Allocate memory for building the header, fill it up,
|
|
|
|
|
* and write it out.
|
|
|
|
|
*/
|
2003-02-19 05:47:46 +00:00
|
|
|
|
hdr = malloc(hdrsize, M_TEMP, M_WAITOK);
|
1998-09-16 02:04:05 +00:00
|
|
|
|
if (hdr == NULL) {
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (EINVAL);
|
1998-09-16 02:04:05 +00:00
|
|
|
|
}
|
2002-07-20 02:56:12 +00:00
|
|
|
|
error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize);
|
1998-09-16 02:04:05 +00:00
|
|
|
|
|
|
|
|
|
/* Write the contents of all of the writable segments. */
|
|
|
|
|
if (error == 0) {
|
|
|
|
|
Elf_Phdr *php;
|
|
|
|
|
off_t offset;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1;
|
|
|
|
|
offset = hdrsize;
|
2002-08-24 22:55:16 +00:00
|
|
|
|
for (i = 0; i < seginfo.count; i++) {
|
2002-07-20 02:56:12 +00:00
|
|
|
|
error = vn_rdwr_inchunks(UIO_WRITE, vp,
|
|
|
|
|
(caddr_t)(uintptr_t)php->p_vaddr,
|
1998-09-16 02:04:05 +00:00
|
|
|
|
php->p_filesz, offset, UIO_USERSPACE,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
|
IO_UNIT | IO_DIRECT, cred, NOCRED, (int *)NULL,
|
|
|
|
|
curthread); /* XXXKSE */
|
1998-09-16 02:04:05 +00:00
|
|
|
|
if (error != 0)
|
|
|
|
|
break;
|
|
|
|
|
offset += php->p_filesz;
|
|
|
|
|
php++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(hdr, M_TEMP);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (error);
|
1998-09-14 22:46:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-16 02:04:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* A callback for each_writable_segment() to write out the segment's
|
|
|
|
|
* program header entry.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
cb_put_phdr(entry, closure)
|
|
|
|
|
vm_map_entry_t entry;
|
|
|
|
|
void *closure;
|
|
|
|
|
{
|
|
|
|
|
struct phdr_closure *phc = (struct phdr_closure *)closure;
|
|
|
|
|
Elf_Phdr *phdr = phc->phdr;
|
|
|
|
|
|
|
|
|
|
phc->offset = round_page(phc->offset);
|
|
|
|
|
|
|
|
|
|
phdr->p_type = PT_LOAD;
|
|
|
|
|
phdr->p_offset = phc->offset;
|
|
|
|
|
phdr->p_vaddr = entry->start;
|
|
|
|
|
phdr->p_paddr = 0;
|
|
|
|
|
phdr->p_filesz = phdr->p_memsz = entry->end - entry->start;
|
|
|
|
|
phdr->p_align = PAGE_SIZE;
|
|
|
|
|
phdr->p_flags = 0;
|
|
|
|
|
if (entry->protection & VM_PROT_READ)
|
|
|
|
|
phdr->p_flags |= PF_R;
|
|
|
|
|
if (entry->protection & VM_PROT_WRITE)
|
|
|
|
|
phdr->p_flags |= PF_W;
|
|
|
|
|
if (entry->protection & VM_PROT_EXECUTE)
|
|
|
|
|
phdr->p_flags |= PF_X;
|
|
|
|
|
|
|
|
|
|
phc->offset += phdr->p_filesz;
|
|
|
|
|
phc->phdr++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* A callback for each_writable_segment() to gather information about
|
|
|
|
|
* the number of segments and their total size.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
cb_size_segment(entry, closure)
|
|
|
|
|
vm_map_entry_t entry;
|
|
|
|
|
void *closure;
|
|
|
|
|
{
|
|
|
|
|
struct sseg_closure *ssc = (struct sseg_closure *)closure;
|
|
|
|
|
|
|
|
|
|
ssc->count++;
|
|
|
|
|
ssc->size += entry->end - entry->start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* For each writable segment in the process's memory map, call the given
|
|
|
|
|
* function with a pointer to the map entry and some arbitrary
|
|
|
|
|
* caller-supplied data.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
each_writable_segment(p, func, closure)
|
|
|
|
|
struct proc *p;
|
|
|
|
|
segment_callback func;
|
|
|
|
|
void *closure;
|
|
|
|
|
{
|
|
|
|
|
vm_map_t map = &p->p_vmspace->vm_map;
|
|
|
|
|
vm_map_entry_t entry;
|
|
|
|
|
|
2002-08-24 22:55:16 +00:00
|
|
|
|
for (entry = map->header.next; entry != &map->header;
|
1998-09-16 02:04:05 +00:00
|
|
|
|
entry = entry->next) {
|
|
|
|
|
vm_object_t obj;
|
|
|
|
|
|
2002-12-16 19:24:43 +00:00
|
|
|
|
/*
|
|
|
|
|
* Don't dump inaccessible mappings, deal with legacy
|
|
|
|
|
* coredump mode.
|
|
|
|
|
*
|
|
|
|
|
* Note that read-only segments related to the elf binary
|
|
|
|
|
* are marked MAP_ENTRY_NOCOREDUMP now so we no longer
|
|
|
|
|
* need to arbitrarily ignore such segments.
|
|
|
|
|
*/
|
|
|
|
|
if (elf_legacy_coredump) {
|
|
|
|
|
if ((entry->protection & VM_PROT_RW) != VM_PROT_RW)
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
if ((entry->protection & VM_PROT_ALL) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1998-09-16 02:04:05 +00:00
|
|
|
|
|
2000-02-28 04:10:35 +00:00
|
|
|
|
/*
|
2002-12-16 19:24:43 +00:00
|
|
|
|
* Dont include memory segment in the coredump if
|
|
|
|
|
* MAP_NOCORE is set in mmap(2) or MADV_NOCORE in
|
|
|
|
|
* madvise(2). Do not dump submaps (i.e. parts of the
|
|
|
|
|
* kernel map).
|
|
|
|
|
*/
|
|
|
|
|
if (entry->eflags & (MAP_ENTRY_NOCOREDUMP|MAP_ENTRY_IS_SUB_MAP))
|
2000-02-28 04:10:35 +00:00
|
|
|
|
continue;
|
|
|
|
|
|
1998-09-16 02:04:05 +00:00
|
|
|
|
if ((obj = entry->object.vm_object) == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Find the deepest backing object. */
|
|
|
|
|
while (obj->backing_object != NULL)
|
|
|
|
|
obj = obj->backing_object;
|
|
|
|
|
|
|
|
|
|
/* Ignore memory-mapped devices and such things. */
|
|
|
|
|
if (obj->type != OBJT_DEFAULT &&
|
|
|
|
|
obj->type != OBJT_SWAP &&
|
|
|
|
|
obj->type != OBJT_VNODE)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
(*func)(entry, closure);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Write the core file header to the file, including padding up to
|
|
|
|
|
* the page boundary.
|
|
|
|
|
*/
|
1998-09-14 22:46:08 +00:00
|
|
|
|
static int
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(corehdr)(td, vp, cred, numsegs, hdr, hdrsize)
|
2001-09-12 08:38:13 +00:00
|
|
|
|
struct thread *td;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
struct vnode *vp;
|
|
|
|
|
struct ucred *cred;
|
1998-09-16 02:04:05 +00:00
|
|
|
|
int numsegs;
|
|
|
|
|
size_t hdrsize;
|
|
|
|
|
void *hdr;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
{
|
2001-08-16 16:14:26 +00:00
|
|
|
|
struct {
|
|
|
|
|
prstatus_t status;
|
|
|
|
|
prfpregset_t fpregset;
|
|
|
|
|
prpsinfo_t psinfo;
|
|
|
|
|
} *tempdata;
|
2001-09-12 08:38:13 +00:00
|
|
|
|
struct proc *p = td->td_proc;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
size_t off;
|
2001-08-16 16:14:26 +00:00
|
|
|
|
prstatus_t *status;
|
|
|
|
|
prfpregset_t *fpregset;
|
|
|
|
|
prpsinfo_t *psinfo;
|
|
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
|
tempdata = malloc(sizeof(*tempdata), M_TEMP, M_ZERO | M_WAITOK);
|
2001-08-16 16:14:26 +00:00
|
|
|
|
status = &tempdata->status;
|
|
|
|
|
fpregset = &tempdata->fpregset;
|
|
|
|
|
psinfo = &tempdata->psinfo;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
|
|
|
|
|
/* Gather the information for the header. */
|
2001-08-16 16:14:26 +00:00
|
|
|
|
status->pr_version = PRSTATUS_VERSION;
|
|
|
|
|
status->pr_statussz = sizeof(prstatus_t);
|
|
|
|
|
status->pr_gregsetsz = sizeof(gregset_t);
|
|
|
|
|
status->pr_fpregsetsz = sizeof(fpregset_t);
|
|
|
|
|
status->pr_osreldate = osreldate;
|
|
|
|
|
status->pr_cursig = p->p_sig;
|
|
|
|
|
status->pr_pid = p->p_pid;
|
2001-09-12 08:38:13 +00:00
|
|
|
|
fill_regs(td, &status->pr_reg);
|
2001-08-16 16:14:26 +00:00
|
|
|
|
|
2001-09-12 08:38:13 +00:00
|
|
|
|
fill_fpregs(td, fpregset);
|
2001-08-16 16:14:26 +00:00
|
|
|
|
|
|
|
|
|
psinfo->pr_version = PRPSINFO_VERSION;
|
|
|
|
|
psinfo->pr_psinfosz = sizeof(prpsinfo_t);
|
2002-10-17 20:03:38 +00:00
|
|
|
|
strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname));
|
2001-08-16 16:14:26 +00:00
|
|
|
|
|
1998-09-16 02:04:05 +00:00
|
|
|
|
/* XXX - We don't fill in the command line arguments properly yet. */
|
2002-10-17 20:03:38 +00:00
|
|
|
|
strlcpy(psinfo->pr_psargs, p->p_comm, sizeof(psinfo->pr_psargs));
|
1998-09-14 22:46:08 +00:00
|
|
|
|
|
|
|
|
|
/* Fill in the header. */
|
1998-09-16 02:04:05 +00:00
|
|
|
|
bzero(hdr, hdrsize);
|
1998-09-14 22:46:08 +00:00
|
|
|
|
off = 0;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(puthdr)(p, hdr, &off, status, fpregset, psinfo, numsegs);
|
2001-08-16 16:14:26 +00:00
|
|
|
|
|
|
|
|
|
free(tempdata, M_TEMP);
|
1998-09-14 22:46:08 +00:00
|
|
|
|
|
|
|
|
|
/* Write it to the core file. */
|
2002-08-24 22:01:40 +00:00
|
|
|
|
return (vn_rdwr_inchunks(UIO_WRITE, vp, hdr, hdrsize, (off_t)0,
|
In order to better support flexible and extensible access control,
make a series of modifications to the credential arguments relating
to file read and write operations to cliarfy which credential is
used for what:
- Change fo_read() and fo_write() to accept "active_cred" instead of
"cred", and change the semantics of consumers of fo_read() and
fo_write() to pass the active credential of the thread requesting
an operation rather than the cached file cred. The cached file
cred is still available in fo_read() and fo_write() consumers
via fp->f_cred. These changes largely in sys_generic.c.
For each implementation of fo_read() and fo_write(), update cred
usage to reflect this change and maintain current semantics:
- badfo_readwrite() unchanged
- kqueue_read/write() unchanged
pipe_read/write() now authorize MAC using active_cred rather
than td->td_ucred
- soo_read/write() unchanged
- vn_read/write() now authorize MAC using active_cred but
VOP_READ/WRITE() with fp->f_cred
Modify vn_rdwr() to accept two credential arguments instead of a
single credential: active_cred and file_cred. Use active_cred
for MAC authorization, and select a credential for use in
VOP_READ/WRITE() based on whether file_cred is NULL or not. If
file_cred is provided, authorize the VOP using that cred,
otherwise the active credential, matching current semantics.
Modify current vn_rdwr() consumers to pass a file_cred if used
in the context of a struct file, and to always pass active_cred.
When vn_rdwr() is used without a file_cred, pass NOCRED.
These changes should maintain current semantics for read/write,
but avoid a redundant passing of fp->f_cred, as well as making
it more clear what the origin of each credential is in file
descriptor read/write operations.
Follow-up commits will make similar changes to other file descriptor
operations, and modify the MAC framework to pass both credentials
to MAC policy modules so they can implement either semantic for
revocation.
Obtained from: TrustedBSD Project
Sponsored by: DARPA, NAI Labs
2002-08-15 20:55:08 +00:00
|
|
|
|
UIO_SYSSPACE, IO_UNIT | IO_DIRECT, cred, NOCRED, NULL,
|
2002-08-24 22:01:40 +00:00
|
|
|
|
td)); /* XXXKSE */
|
1998-09-15 22:23:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-09-14 22:46:08 +00:00
|
|
|
|
static void
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(puthdr)(struct proc *p, void *dst, size_t *off, const prstatus_t *status,
|
1998-09-16 02:04:05 +00:00
|
|
|
|
const prfpregset_t *fpregset, const prpsinfo_t *psinfo, int numsegs)
|
1998-09-14 22:46:08 +00:00
|
|
|
|
{
|
|
|
|
|
size_t ehoff;
|
|
|
|
|
size_t phoff;
|
|
|
|
|
size_t noteoff;
|
|
|
|
|
size_t notesz;
|
|
|
|
|
|
|
|
|
|
ehoff = *off;
|
|
|
|
|
*off += sizeof(Elf_Ehdr);
|
|
|
|
|
|
|
|
|
|
phoff = *off;
|
1998-09-16 02:04:05 +00:00
|
|
|
|
*off += (numsegs + 1) * sizeof(Elf_Phdr);
|
1998-09-14 22:46:08 +00:00
|
|
|
|
|
|
|
|
|
noteoff = *off;
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(putnote)(dst, off, "FreeBSD", NT_PRSTATUS, status,
|
1998-09-14 22:46:08 +00:00
|
|
|
|
sizeof *status);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(putnote)(dst, off, "FreeBSD", NT_FPREGSET, fpregset,
|
1998-09-14 22:46:08 +00:00
|
|
|
|
sizeof *fpregset);
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(putnote)(dst, off, "FreeBSD", NT_PRPSINFO, psinfo,
|
1998-09-14 22:46:08 +00:00
|
|
|
|
sizeof *psinfo);
|
|
|
|
|
notesz = *off - noteoff;
|
|
|
|
|
|
1998-09-16 02:04:05 +00:00
|
|
|
|
/* Align up to a page boundary for the program segments. */
|
1998-09-14 22:46:08 +00:00
|
|
|
|
*off = round_page(*off);
|
|
|
|
|
|
|
|
|
|
if (dst != NULL) {
|
|
|
|
|
Elf_Ehdr *ehdr;
|
|
|
|
|
Elf_Phdr *phdr;
|
1998-09-16 02:04:05 +00:00
|
|
|
|
struct phdr_closure phc;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Fill in the ELF header.
|
|
|
|
|
*/
|
|
|
|
|
ehdr = (Elf_Ehdr *)((char *)dst + ehoff);
|
|
|
|
|
ehdr->e_ident[EI_MAG0] = ELFMAG0;
|
|
|
|
|
ehdr->e_ident[EI_MAG1] = ELFMAG1;
|
|
|
|
|
ehdr->e_ident[EI_MAG2] = ELFMAG2;
|
|
|
|
|
ehdr->e_ident[EI_MAG3] = ELFMAG3;
|
|
|
|
|
ehdr->e_ident[EI_CLASS] = ELF_CLASS;
|
|
|
|
|
ehdr->e_ident[EI_DATA] = ELF_DATA;
|
|
|
|
|
ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
2000-04-18 02:39:26 +00:00
|
|
|
|
ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
|
|
|
|
|
ehdr->e_ident[EI_ABIVERSION] = 0;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
ehdr->e_ident[EI_PAD] = 0;
|
|
|
|
|
ehdr->e_type = ET_CORE;
|
|
|
|
|
ehdr->e_machine = ELF_ARCH;
|
|
|
|
|
ehdr->e_version = EV_CURRENT;
|
|
|
|
|
ehdr->e_entry = 0;
|
|
|
|
|
ehdr->e_phoff = phoff;
|
|
|
|
|
ehdr->e_flags = 0;
|
|
|
|
|
ehdr->e_ehsize = sizeof(Elf_Ehdr);
|
|
|
|
|
ehdr->e_phentsize = sizeof(Elf_Phdr);
|
1998-09-16 02:04:05 +00:00
|
|
|
|
ehdr->e_phnum = numsegs + 1;
|
1998-09-14 22:46:08 +00:00
|
|
|
|
ehdr->e_shentsize = sizeof(Elf_Shdr);
|
|
|
|
|
ehdr->e_shnum = 0;
|
|
|
|
|
ehdr->e_shstrndx = SHN_UNDEF;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Fill in the program header entries.
|
|
|
|
|
*/
|
|
|
|
|
phdr = (Elf_Phdr *)((char *)dst + phoff);
|
|
|
|
|
|
|
|
|
|
/* The note segement. */
|
|
|
|
|
phdr->p_type = PT_NOTE;
|
|
|
|
|
phdr->p_offset = noteoff;
|
|
|
|
|
phdr->p_vaddr = 0;
|
|
|
|
|
phdr->p_paddr = 0;
|
|
|
|
|
phdr->p_filesz = notesz;
|
|
|
|
|
phdr->p_memsz = 0;
|
|
|
|
|
phdr->p_flags = 0;
|
|
|
|
|
phdr->p_align = 0;
|
|
|
|
|
phdr++;
|
|
|
|
|
|
1998-09-16 02:04:05 +00:00
|
|
|
|
/* All the writable segments from the program. */
|
|
|
|
|
phc.phdr = phdr;
|
|
|
|
|
phc.offset = *off;
|
|
|
|
|
each_writable_segment(p, cb_put_phdr, &phc);
|
1998-09-14 22:46:08 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2002-07-20 02:56:12 +00:00
|
|
|
|
__elfN(putnote)(void *dst, size_t *off, const char *name, int type,
|
1998-09-14 22:46:08 +00:00
|
|
|
|
const void *desc, size_t descsz)
|
|
|
|
|
{
|
|
|
|
|
Elf_Note note;
|
|
|
|
|
|
|
|
|
|
note.n_namesz = strlen(name) + 1;
|
|
|
|
|
note.n_descsz = descsz;
|
|
|
|
|
note.n_type = type;
|
|
|
|
|
if (dst != NULL)
|
|
|
|
|
bcopy(¬e, (char *)dst + *off, sizeof note);
|
|
|
|
|
*off += sizeof note;
|
|
|
|
|
if (dst != NULL)
|
|
|
|
|
bcopy(name, (char *)dst + *off, note.n_namesz);
|
|
|
|
|
*off += roundup2(note.n_namesz, sizeof(Elf_Size));
|
|
|
|
|
if (dst != NULL)
|
|
|
|
|
bcopy(desc, (char *)dst + *off, note.n_descsz);
|
|
|
|
|
*off += roundup2(note.n_descsz, sizeof(Elf_Size));
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-10 08:42:54 +00:00
|
|
|
|
/*
|
|
|
|
|
* Tell kern_execve.c about it, with a little help from the linker.
|
|
|
|
|
*/
|
2003-01-04 22:07:48 +00:00
|
|
|
|
static struct execsw __elfN(execsw) = {
|
|
|
|
|
__CONCAT(exec_, __elfN(imgact)),
|
|
|
|
|
__XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE))
|
|
|
|
|
};
|
|
|
|
|
EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw));
|