diff --git a/contrib/gcc/config/rs6000/freebsd.h b/contrib/gcc/config/rs6000/freebsd.h index 34c93b8..fe8e0b6 100644 --- a/contrib/gcc/config/rs6000/freebsd.h +++ b/contrib/gcc/config/rs6000/freebsd.h @@ -242,3 +242,5 @@ #define PROFILE_HOOK(LABEL) \ do { if (TARGET_64BIT) output_profile_hook (LABEL); } while (0) +#undef NEED_INDICATE_EXEC_STACK +#define NEED_INDICATE_EXEC_STACK 1 diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index f9abab5..32cf92c 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -453,6 +453,7 @@ FBSDprivate_1.0 { _rtld_atfork_pre; _rtld_atfork_post; _rtld_error; /* for private use */ + _rtld_get_stack_prot; _rtld_thread_init; /* for private use */ __elf_phdr_match_addr; _err; @@ -499,4 +500,5 @@ FBSDprivate_1.0 { _libc_sem_getvalue_compat; __elf_aux_vector; + __pthread_map_stacks_exec; }; diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index 930ebc1..b109cc9 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); /* * Linkage to services provided by the dynamic linker. */ +#include #include #include #include @@ -165,3 +166,12 @@ _rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info) return (0); } + +#pragma weak _rtld_get_stack_prot +int +_rtld_get_stack_prot(void) +{ + + return (PROT_EXEC | PROT_READ | PROT_WRITE); +} + diff --git a/lib/libc/gen/elf_utils.c b/lib/libc/gen/elf_utils.c index 9ee5f41..7bd7511 100644 --- a/lib/libc/gen/elf_utils.c +++ b/lib/libc/gen/elf_utils.c @@ -26,7 +26,12 @@ * $FreeBSD$ */ +#include +#include +#include +#include #include +#include int __elf_phdr_match_addr(struct dl_phdr_info *phdr_info, void *addr) @@ -45,3 +50,25 @@ __elf_phdr_match_addr(struct dl_phdr_info *phdr_info, void *addr) } return (i != phdr_info->dlpi_phnum); } + +#pragma weak __pthread_map_stacks_exec +void +__pthread_map_stacks_exec(void) +{ + int mib[2]; + struct rlimit rlim; + u_long usrstack; + size_t len; + + mib[0] = CTL_KERN; + mib[1] = KERN_USRSTACK; + len = sizeof(usrstack); + if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &usrstack, &len, NULL, 0) + == -1) + return; + if (getrlimit(RLIMIT_STACK, &rlim) == -1) + return; + mprotect((void *)(uintptr_t)(usrstack - rlim.rlim_cur), + rlim.rlim_cur, _rtld_get_stack_prot()); +} + diff --git a/lib/libthr/pthread.map b/lib/libthr/pthread.map index 5e36239..9f82a1b 100644 --- a/lib/libthr/pthread.map +++ b/lib/libthr/pthread.map @@ -382,6 +382,8 @@ FBSDprivate_1.0 { _thread_size_key; _thread_state_running; _thread_state_zoombie; + + __pthread_map_stacks_exec; }; FBSD_1.1 { diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index f0af20b..a41b33f 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr, sigset_t set, oset; cpuset_t *cpusetp = NULL; int cpusetsize = 0; + int old_stack_prot; _thr_check_init(); @@ -96,6 +98,7 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->tid = TID_TERMINATED; + old_stack_prot = _rtld_get_stack_prot(); if (create_stack(&new_thread->attr) != 0) { /* Insufficient memory to create a stack: */ _thr_free(curthread, new_thread); @@ -130,6 +133,14 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Add the new thread. */ new_thread->refcount = 1; _thr_link(curthread, new_thread); + + /* + * Handle the race between __pthread_map_stacks_exec and + * thread linkage. + */ + if (old_stack_prot != _rtld_get_stack_prot()) + _thr_stack_fix_protection(new_thread); + /* Return thread pointer eariler so that new thread can use it. */ (*thread) = new_thread; if (SHOULD_REPORT_EVENT(curthread, TD_CREATE) || cpusetp != NULL) { diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index 9df97aa..9b9227b 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -898,6 +898,7 @@ struct dl_phdr_info; void __pthread_cxa_finalize(struct dl_phdr_info *phdr_info); void _thr_tsd_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_sigact_unload(struct dl_phdr_info *phdr_info) __hidden; +void _thr_stack_fix_protection(struct pthread *thrd); __END_DECLS diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c index e6af702..1821859 100644 --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -31,6 +31,7 @@ * A lockless rwlock for rtld. */ #include +#include #include #include @@ -194,6 +195,8 @@ _thr_rtld_init(void) /* force to resolve memcpy PLT */ memcpy(&dummy, &dummy, sizeof(dummy)); + mprotect(NULL, 0, 0); + li.lock_create = _thr_rtld_lock_create; li.lock_destroy = _thr_rtld_lock_destroy; li.rlock_acquire = _thr_rtld_rlock_acquire; diff --git a/lib/libthr/thread/thr_stack.c b/lib/libthr/thread/thr_stack.c index 7684953..3ff4afe 100644 --- a/lib/libthr/thread/thr_stack.c +++ b/lib/libthr/thread/thr_stack.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "thr_private.h" @@ -128,6 +129,36 @@ round_up(size_t size) return size; } +void +_thr_stack_fix_protection(struct pthread *thrd) +{ + + mprotect((char *)thrd->attr.stackaddr_attr + + round_up(thrd->attr.guardsize_attr), + round_up(thrd->attr.stacksize_attr), + _rtld_get_stack_prot()); +} + +void __pthread_map_stacks_exec(void); +void +__pthread_map_stacks_exec(void) +{ + struct pthread *curthread, *thrd; + struct stack *st; + + curthread = _get_curthread(); + THREAD_LIST_RDLOCK(curthread); + LIST_FOREACH(st, &mstackq, qe) + mprotect((char *)st->stackaddr + st->guardsize, st->stacksize, + _rtld_get_stack_prot()); + LIST_FOREACH(st, &dstackq, qe) + mprotect((char *)st->stackaddr + st->guardsize, st->stacksize, + _rtld_get_stack_prot()); + TAILQ_FOREACH(thrd, &_thread_list, tle) + _thr_stack_fix_protection(thrd); + THREAD_LIST_UNLOCK(curthread); +} + int _thr_stack_alloc(struct pthread_attr *attr) { @@ -210,7 +241,7 @@ _thr_stack_alloc(struct pthread_attr *attr) /* Map the stack and guard page together, and split guard page from allocated space: */ if ((stackaddr = mmap(stackaddr, stacksize+guardsize, - PROT_READ | PROT_WRITE, MAP_STACK, + _rtld_get_stack_prot(), MAP_STACK, -1, 0)) != MAP_FAILED && (guardsize == 0 || mprotect(stackaddr, guardsize, PROT_NONE) == 0)) { diff --git a/libexec/rtld-elf/Symbol.map b/libexec/rtld-elf/Symbol.map index 7a8273f..6a416a7 100644 --- a/libexec/rtld-elf/Symbol.map +++ b/libexec/rtld-elf/Symbol.map @@ -28,4 +28,5 @@ FBSDprivate_1.0 { _rtld_atfork_pre; _rtld_atfork_post; _rtld_addr_phdr; + _rtld_get_stack_prot; }; diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index d231830..3f69e9b 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -83,6 +83,7 @@ map_object(int fd, const char *path, const struct stat *sb) Elf_Addr bss_vaddr; Elf_Addr bss_vlimit; caddr_t bss_addr; + Elf_Word stack_flags; hdr = get_elf_header(fd, path); if (hdr == NULL) @@ -100,6 +101,7 @@ map_object(int fd, const char *path, const struct stat *sb) phdyn = phinterp = phtls = NULL; phdr_vaddr = 0; segs = alloca(sizeof(segs[0]) * hdr->e_phnum); + stack_flags = PF_X | PF_R | PF_W; while (phdr < phlimit) { switch (phdr->p_type) { @@ -128,6 +130,10 @@ map_object(int fd, const char *path, const struct stat *sb) case PT_TLS: phtls = phdr; break; + + case PT_GNU_STACK: + stack_flags = phdr->p_flags; + break; } ++phdr; @@ -261,6 +267,7 @@ map_object(int fd, const char *path, const struct stat *sb) obj->tlsinitsize = phtls->p_filesz; obj->tlsinit = mapbase + phtls->p_vaddr; } + obj->stack_flags = stack_flags; return obj; } diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index ee3985b..2e39b67 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -103,6 +103,7 @@ static void unload_filtees(Obj_Entry *); static int load_needed_objects(Obj_Entry *, int); static int load_preload_objects(void); static Obj_Entry *load_object(const char *, const Obj_Entry *, int); +static void map_stacks_exec(void); static Obj_Entry *obj_from_addr(const void *); static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); static void objlist_call_init(Objlist *, RtldLockState *); @@ -188,6 +189,9 @@ extern Elf_Dyn _DYNAMIC; int osreldate, pagesize; +static int stack_prot = PROT_READ | PROT_WRITE | PROT_EXEC; +static int max_stack_flags; + /* * Global declarations normally provided by crt1. The dynamic linker is * not built with crt1, so we have to provide them ourselves. @@ -382,6 +386,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) close(fd); if (obj_main == NULL) die(); + max_stack_flags = obj->stack_flags; } else { /* Main program already loaded. */ const Elf_Phdr *phdr; int phnum; @@ -421,6 +426,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) dbg("obj_main path %s", obj_main->path); obj_main->mainprog = true; + if (aux_info[AT_STACKPROT] != NULL && + aux_info[AT_STACKPROT]->a_un.a_val != 0) + stack_prot = aux_info[AT_STACKPROT]->a_un.a_val; + /* * Get the actual dynamic linker pathname from the executable if * possible. (It should always be possible.) That ensures that @@ -519,6 +528,8 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */ + map_stacks_exec(); + wlock_acquire(rtld_bind_lock, &lockstate); objlist_call_init(&initlist, &lockstate); objlist_clear(&initlist); @@ -1031,6 +1042,8 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path) break; } + obj->stack_flags = PF_X | PF_R | PF_W; + for (ph = phdr; ph < phlimit; ph++) { switch (ph->p_type) { @@ -1062,6 +1075,10 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path) obj->tlsinitsize = ph->p_filesz; obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase); break; + + case PT_GNU_STACK: + obj->stack_flags = ph->p_flags; + break; } } if (nsegs < 1) { @@ -1679,6 +1696,7 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp, obj_count++; obj_loads++; linkmap_add(obj); /* for GDB & dlinfo() */ + max_stack_flags |= obj->stack_flags; dbg(" %p .. %p: %s", obj->mapbase, obj->mapbase + obj->mapsize - 1, obj->path); @@ -2202,6 +2220,8 @@ dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode) name); GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL); + map_stacks_exec(); + /* Call the init functions. */ objlist_call_init(&initlist, &lockstate); objlist_clear(&initlist); @@ -3872,6 +3892,28 @@ fetch_ventry(const Obj_Entry *obj, unsigned long symnum) return NULL; } +int +_rtld_get_stack_prot(void) +{ + + return (stack_prot); +} + +static void +map_stacks_exec(void) +{ + void (*thr_map_stacks_exec)(void); + + if ((max_stack_flags & PF_X) == 0 || (stack_prot & PROT_EXEC) != 0) + return; + thr_map_stacks_exec = (void (*)(void))(uintptr_t) + get_program_var_addr("__pthread_map_stacks_exec"); + if (thr_map_stacks_exec != NULL) { + stack_prot |= PROT_EXEC; + thr_map_stacks_exec(); + } +} + void symlook_init(SymLook *dst, const char *name) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index b70e8b6..8941d29 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -157,6 +157,7 @@ typedef struct Struct_Obj_Entry { const Elf_Phdr *phdr; /* Program header if it is mapped, else NULL */ size_t phsize; /* Size of program header in bytes */ const char *interp; /* Pathname of the interpreter, if any */ + Elf_Word stack_flags; /* TLS information */ int tlsindex; /* Index in DTV for this module */ diff --git a/sys/amd64/amd64/elf_machdep.c b/sys/amd64/amd64/elf_machdep.c index 6472d55..4344731 100644 --- a/sys/amd64/amd64/elf_machdep.c +++ b/sys/amd64/amd64/elf_machdep.c @@ -75,11 +75,14 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, - .sv_flags = SV_ABI_FREEBSD | SV_LP64, + .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, + .sv_shared_page_base = SHAREDPAGE, + .sv_shared_page_len = PAGE_SIZE, }; +INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, @@ -129,7 +132,6 @@ SYSINIT(kelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &kfreebsd_brand_info); - void elf64_dump_thread(struct thread *td __unused, void *dst __unused, size_t *off __unused) diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c index 6c946e6..72e0fd7 100644 --- a/sys/amd64/amd64/machdep.c +++ b/sys/amd64/amd64/machdep.c @@ -386,7 +386,7 @@ sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (long)sfp; - regs->tf_rip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); + regs->tf_rip = p->p_sysent->sv_sigcode_base; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c index 808790b..91884d2 100644 --- a/sys/amd64/ia32/ia32_signal.c +++ b/sys/amd64/ia32/ia32_signal.c @@ -393,7 +393,8 @@ freebsd4_ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (uintptr_t)sfp; - regs->tf_rip = p->p_sysent->sv_psstrings - sz_freebsd4_ia32_sigcode; + regs->tf_rip = p->p_sysent->sv_sigcode_base + sz_ia32_sigcode - + sz_freebsd4_ia32_sigcode; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucode32sel; regs->tf_ss = _udatasel; @@ -514,7 +515,7 @@ ia32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) } regs->tf_rsp = (uintptr_t)sfp; - regs->tf_rip = p->p_sysent->sv_psstrings - *(p->p_sysent->sv_szsigcode); + regs->tf_rip = p->p_sysent->sv_sigcode_base; regs->tf_rflags &= ~(PSL_T | PSL_D); regs->tf_cs = _ucode32sel; regs->tf_ss = _udatasel; diff --git a/sys/amd64/include/vmparam.h b/sys/amd64/include/vmparam.h index 7a50c59..88d68b9 100644 --- a/sys/amd64/include/vmparam.h +++ b/sys/amd64/include/vmparam.h @@ -186,7 +186,8 @@ #define VM_MAXUSER_ADDRESS UVADDR(NUPML4E, 0, 0, 0) -#define USRSTACK VM_MAXUSER_ADDRESS +#define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE) +#define USRSTACK SHAREDPAGE #define VM_MAX_ADDRESS UPT_MAX_ADDRESS #define VM_MIN_ADDRESS (0) diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c index 326cf031..cf3af87 100644 --- a/sys/compat/freebsd32/freebsd32_misc.c +++ b/sys/compat/freebsd32/freebsd32_misc.c @@ -2546,7 +2546,10 @@ freebsd32_copyout_strings(struct image_params *imgp) execpath_len = 0; arginfo = (struct freebsd32_ps_strings *)curproc->p_sysent-> sv_psstrings; - szsigcode = *(imgp->proc->p_sysent->sv_szsigcode); + if (imgp->proc->p_sysent->sv_sigcode_base == 0) + szsigcode = *(imgp->proc->p_sysent->sv_szsigcode); + else + szsigcode = 0; destp = (caddr_t)arginfo - szsigcode - SPARE_USRSPACE - roundup(execpath_len, sizeof(char *)) - roundup(sizeof(canary), sizeof(char *)) - @@ -2556,7 +2559,7 @@ freebsd32_copyout_strings(struct image_params *imgp) /* * install sigcode */ - if (szsigcode) + if (szsigcode != 0) copyout(imgp->proc->p_sysent->sv_sigcode, ((caddr_t)arginfo - szsigcode), szsigcode); diff --git a/sys/compat/ia32/ia32_sysvec.c b/sys/compat/ia32/ia32_sysvec.c index d7ac5d0..058e739 100644 --- a/sys/compat/ia32/ia32_sysvec.c +++ b/sys/compat/ia32/ia32_sysvec.c @@ -129,7 +129,7 @@ struct sysentvec ia32_freebsd_sysvec = { .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = IA32_PAGE_SIZE, .sv_minuser = 0, - .sv_maxuser = FREEBSD32_USRSTACK, + .sv_maxuser = FREEBSD32_MAXUSER, .sv_usrstack = FREEBSD32_USRSTACK, .sv_psstrings = FREEBSD32_PS_STRINGS, .sv_stackprot = VM_PROT_ALL, @@ -137,12 +137,20 @@ struct sysentvec ia32_freebsd_sysvec = { .sv_setregs = ia32_setregs, .sv_fixlimit = ia32_fixlimit, .sv_maxssiz = &ia32_maxssiz, - .sv_flags = SV_ABI_FREEBSD | SV_IA32 | SV_ILP32, + .sv_flags = SV_ABI_FREEBSD | SV_IA32 | SV_ILP32 | +#ifdef __amd64__ + SV_SHP +#else + 0 +#endif + , .sv_set_syscall_retval = ia32_set_syscall_retval, .sv_fetch_syscall_args = ia32_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, + .sv_shared_page_base = FREEBSD32_SHAREDPAGE, + .sv_shared_page_len = PAGE_SIZE, }; - +INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec); static Elf32_Brandinfo ia32_brand_info = { .brand = ELFOSABI_FREEBSD, @@ -191,7 +199,6 @@ SYSINIT(kia32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &kia32_brand_info); - void elf32_dump_thread(struct thread *td __unused, void *dst __unused, size_t *off __unused) diff --git a/sys/compat/ia32/ia32_util.h b/sys/compat/ia32/ia32_util.h index f1cc6ed..4355daa 100644 --- a/sys/compat/ia32/ia32_util.h +++ b/sys/compat/ia32/ia32_util.h @@ -41,9 +41,13 @@ #include #ifdef __ia64__ -#define FREEBSD32_USRSTACK ((1ul << 32) - IA32_PAGE_SIZE * 2) +#define FREEBSD32_MAXUSER ((1ul << 32) - IA32_PAGE_SIZE * 2) +#define FREEBSD32_SHAREDPAGE 0 +#define FREEBSD32_USRSTACK FREEBSD32_MAXUSER #else -#define FREEBSD32_USRSTACK ((1ul << 32) - IA32_PAGE_SIZE) +#define FREEBSD32_MAXUSER ((1ul << 32) - IA32_PAGE_SIZE) +#define FREEBSD32_SHAREDPAGE (FREEBSD32_MAXUSER - IA32_PAGE_SIZE) +#define FREEBSD32_USRSTACK FREEBSD32_SHAREDPAGE #endif #define IA32_PAGE_SIZE 4096 diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 42f5573..b7dba05 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -92,6 +92,8 @@ static boolean_t __elfN(freebsd_trans_osrel)(const Elf_Note *note, static boolean_t kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel); static boolean_t __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel); +static vm_prot_t __elfN(trans_prot)(Elf_Word); +static Elf_Word __elfN(untrans_prot)(vm_prot_t); SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0, ""); @@ -113,6 +115,16 @@ static int elf_legacy_coredump = 0; SYSCTL_INT(_debug, OID_AUTO, __elfN(legacy_coredump), CTLFLAG_RW, &elf_legacy_coredump, 0, ""); +static int __elfN(nxstack) = +#ifdef __amd64__ /* both 64 and 32 bit */ + 1; +#else + 0; +#endif +SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, + nxstack, CTLFLAG_RW, &__elfN(nxstack), 0, + __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable non-executable stack"); + static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; #define trunc_page_ps(va, ps) ((va) & ~(ps - 1)) @@ -644,14 +656,7 @@ __elfN(load_file)(struct proc *p, const char *file, u_long *addr, for (i = 0, numsegs = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_LOAD && phdr[i].p_memsz != 0) { /* Loadable segment */ - prot = 0; - 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; - + prot = __elfN(trans_prot)(phdr[i].p_flags); if ((error = __elfN(load_section)(vmspace, imgp->object, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + rbase, @@ -729,19 +734,24 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) n = 0; baddr = 0; for (i = 0; i < hdr->e_phnum; i++) { - if (phdr[i].p_type == PT_LOAD) { + switch (phdr[i].p_type) { + case PT_LOAD: if (n == 0) baddr = phdr[i].p_vaddr; n++; - continue; - } - if (phdr[i].p_type == PT_INTERP) { + break; + case PT_INTERP: /* Path to interpreter */ if (phdr[i].p_filesz > MAXPATHLEN || phdr[i].p_offset + phdr[i].p_filesz > PAGE_SIZE) return (ENOEXEC); interp = imgp->image_header + phdr[i].p_offset; - continue; + break; + case PT_GNU_STACK: + if (__elfN(nxstack)) + imgp->stack_prot = + __elfN(trans_prot)(phdr[i].p_flags); + break; } } @@ -792,13 +802,7 @@ __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) case PT_LOAD: /* Loadable segment */ if (phdr[i].p_memsz == 0) break; - prot = 0; - 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; + prot = __elfN(trans_prot)(phdr[i].p_flags); #if defined(__ia64__) && __ELF_WORD_SIZE == 32 && defined(IA32_ME_HARDER) /* @@ -983,6 +987,8 @@ __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) AUXARGS_ENTRY(pos, AT_PAGESIZES, imgp->pagesizes); AUXARGS_ENTRY(pos, AT_PAGESIZESLEN, imgp->pagesizeslen); } + AUXARGS_ENTRY(pos, AT_STACKPROT, imgp->stack_prot != 0 ? + imgp->stack_prot : imgp->sysent->sv_stackprot); AUXARGS_ENTRY(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); @@ -1173,12 +1179,7 @@ cb_put_phdr(entry, closure) 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; + phdr->p_flags = __elfN(untrans_prot)(entry->protection); phc->offset += phdr->p_filesz; phc->phdr++; @@ -1633,3 +1634,33 @@ compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, return (error); } #endif /* COMPRESS_USER_CORES */ + +static vm_prot_t +__elfN(trans_prot)(Elf_Word flags) +{ + vm_prot_t prot; + + prot = 0; + if (flags & PF_X) + prot |= VM_PROT_EXECUTE; + if (flags & PF_W) + prot |= VM_PROT_WRITE; + if (flags & PF_R) + prot |= VM_PROT_READ; + return (prot); +} + +static Elf_Word +__elfN(untrans_prot)(vm_prot_t prot) +{ + vm_prot_t flags; + + flags = 0; + if (prot & VM_PROT_EXECUTE) + flags |= PF_X; + if (prot & VM_PROT_READ) + flags |= PF_R; + if (prot & VM_PROT_WRITE) + flags |= PF_W; + return (flags); +} diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 1e4d690..6cf77f7 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -389,6 +389,7 @@ do_execve(td, args, mac_p) imgp->canarylen = 0; imgp->pagesizes = 0; imgp->pagesizeslen = 0; + imgp->stack_prot = 0; #ifdef MAC error = mac_execve_enter(imgp, mac_p); @@ -996,6 +997,7 @@ exec_new_vmspace(imgp, sv) int error; struct proc *p = imgp->proc; struct vmspace *vmspace = p->p_vmspace; + vm_object_t obj; vm_offset_t sv_minuser, stack_addr; vm_map_t map; u_long ssiz; @@ -1029,6 +1031,20 @@ exec_new_vmspace(imgp, sv) map = &vmspace->vm_map; } + /* Map a shared page */ + obj = sv->sv_shared_page_obj; + if (obj != NULL) { + vm_object_reference(obj); + error = vm_map_fixed(map, obj, 0, + sv->sv_shared_page_base, sv->sv_shared_page_len, + VM_PROT_READ | VM_PROT_EXECUTE, VM_PROT_ALL, + MAP_COPY_ON_WRITE | MAP_ACC_NO_CHARGE); + if (error) { + vm_object_deallocate(obj); + return (error); + } + } + /* Allocate a new stack */ if (sv->sv_maxssiz != NULL) ssiz = *sv->sv_maxssiz; @@ -1036,7 +1052,9 @@ exec_new_vmspace(imgp, sv) ssiz = maxssiz; stack_addr = sv->sv_usrstack - ssiz; error = vm_map_stack(map, stack_addr, (vm_size_t)ssiz, - sv->sv_stackprot, VM_PROT_ALL, MAP_STACK_GROWS_DOWN); + obj != NULL && imgp->stack_prot != 0 ? imgp->stack_prot : + sv->sv_stackprot, + VM_PROT_ALL, MAP_STACK_GROWS_DOWN); if (error) return (error); @@ -1208,8 +1226,10 @@ exec_copyout_strings(imgp) p = imgp->proc; szsigcode = 0; arginfo = (struct ps_strings *)p->p_sysent->sv_psstrings; - if (p->p_sysent->sv_szsigcode != NULL) - szsigcode = *(p->p_sysent->sv_szsigcode); + if (p->p_sysent->sv_sigcode_base == 0) { + if (p->p_sysent->sv_szsigcode != NULL) + szsigcode = *(p->p_sysent->sv_szsigcode); + } destp = (caddr_t)arginfo - szsigcode - SPARE_USRSPACE - roundup(execpath_len, sizeof(char *)) - roundup(sizeof(canary), sizeof(char *)) - @@ -1219,7 +1239,7 @@ exec_copyout_strings(imgp) /* * install sigcode */ - if (szsigcode) + if (szsigcode != 0) copyout(p->p_sysent->sv_sigcode, ((caddr_t)arginfo - szsigcode), szsigcode); @@ -1459,3 +1479,64 @@ exec_unregister(execsw_arg) execsw = newexecsw; return (0); } + +static vm_object_t shared_page_obj; +static int shared_page_free; + +int +shared_page_fill(int size, int align, const char *data) +{ + vm_page_t m; + struct sf_buf *s; + vm_offset_t sk; + int res; + + VM_OBJECT_LOCK(shared_page_obj); + m = vm_page_grab(shared_page_obj, 0, VM_ALLOC_RETRY); + res = roundup(shared_page_free, align); + if (res + size >= IDX_TO_OFF(shared_page_obj->size)) + res = -1; + else { + VM_OBJECT_UNLOCK(shared_page_obj); + s = sf_buf_alloc(m, SFB_DEFAULT); + sk = sf_buf_kva(s); + bcopy(data, (void *)(sk + res), size); + shared_page_free = res + size; + sf_buf_free(s); + VM_OBJECT_LOCK(shared_page_obj); + } + vm_page_wakeup(m); + VM_OBJECT_UNLOCK(shared_page_obj); + return (res); +} + +static void +shared_page_init(void *dummy __unused) +{ + vm_page_t m; + + shared_page_obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE, + VM_PROT_DEFAULT, 0, NULL); + VM_OBJECT_LOCK(shared_page_obj); + m = vm_page_grab(shared_page_obj, 0, VM_ALLOC_RETRY | VM_ALLOC_NOBUSY | + VM_ALLOC_ZERO); + m->valid = VM_PAGE_BITS_ALL; + VM_OBJECT_UNLOCK(shared_page_obj); +} + +SYSINIT(shp, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)shared_page_init, + NULL); + +void +exec_sysvec_init(void *param) +{ + struct sysentvec *sv; + + sv = (struct sysentvec *)param; + + if ((sv->sv_flags & SV_SHP) == 0) + return; + sv->sv_shared_page_obj = shared_page_obj; + sv->sv_sigcode_base = sv->sv_shared_page_base + + shared_page_fill(*(sv->sv_szsigcode), 16, sv->sv_sigcode); +} diff --git a/sys/powerpc/include/vmparam.h b/sys/powerpc/include/vmparam.h index d98aea5..5b21fe4 100644 --- a/sys/powerpc/include/vmparam.h +++ b/sys/powerpc/include/vmparam.h @@ -35,7 +35,7 @@ #ifndef _MACHINE_VMPARAM_H_ #define _MACHINE_VMPARAM_H_ -#define USRSTACK VM_MAXUSER_ADDRESS +#define USRSTACK SHAREDPAGE #ifndef MAXTSIZ #define MAXTSIZ (64*1024*1024) /* max text size */ @@ -75,10 +75,12 @@ #ifdef __powerpc64__ #define VM_MIN_ADDRESS (0x0000000000000000UL) #define VM_MAXUSER_ADDRESS (0x7ffffffffffff000UL) +#define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE) #define VM_MAX_ADDRESS (0xffffffffffffffffUL) #else #define VM_MIN_ADDRESS ((vm_offset_t)0) #define VM_MAXUSER_ADDRESS ((vm_offset_t)0x7ffff000) +#define SHAREDPAGE (VM_MAXUSER_ADDRESS - PAGE_SIZE) #define VM_MAX_ADDRESS VM_MAXUSER_ADDRESS #endif #else /* LOCORE */ @@ -88,8 +90,8 @@ #endif #endif /* LOCORE */ -#define FREEBSD32_USRSTACK 0x7ffff000 - +#define FREEBSD32_SHAREDPAGE 0x7ffff000 +#define FREEBSD32_USRSTACK (FREEBSD32_SHAREDPAGE - PAGE_SIZE) #ifdef AIM #define KERNBASE 0x00100000UL /* start of kernel virtual */ diff --git a/sys/powerpc/powerpc/elf32_machdep.c b/sys/powerpc/powerpc/elf32_machdep.c index 1761215..e35bd19 100644 --- a/sys/powerpc/powerpc/elf32_machdep.c +++ b/sys/powerpc/powerpc/elf32_machdep.c @@ -101,10 +101,13 @@ struct sysentvec elf32_freebsd_sysvec = { #endif .sv_fixlimit = NULL, .sv_maxssiz = NULL, - .sv_flags = SV_ABI_FREEBSD | SV_ILP32, + .sv_flags = SV_ABI_FREEBSD | SV_ILP32 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, + .sv_shared_page_base = FREEBSD32_SHAREDPAGE, + .sv_shared_page_len = PAGE_SIZE, }; +INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, diff --git a/sys/powerpc/powerpc/elf64_machdep.c b/sys/powerpc/powerpc/elf64_machdep.c index 1b51920..af5b12d 100644 --- a/sys/powerpc/powerpc/elf64_machdep.c +++ b/sys/powerpc/powerpc/elf64_machdep.c @@ -76,11 +76,14 @@ struct sysentvec elf64_freebsd_sysvec = { .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, - .sv_flags = SV_ABI_FREEBSD | SV_LP64, + .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, + .sv_shared_page_base = SHAREDPAGE, + .sv_shared_page_len = PAGE_SIZE, }; +INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, @@ -114,7 +117,6 @@ SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_oinfo); - void elf64_dump_thread(struct thread *td __unused, void *dst __unused, size_t *off __unused) diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h index b54815f..17cfcc2 100644 --- a/sys/sys/imgact.h +++ b/sys/sys/imgact.h @@ -34,6 +34,8 @@ #include +#include + #define MAXSHELLCMDLEN PAGE_SIZE struct image_args { @@ -75,6 +77,7 @@ struct image_params { int canarylen; unsigned long pagesizes; int pagesizeslen; + vm_prot_t stack_prot; }; #ifdef _KERNEL diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h index 93fe1de..45b9430 100644 --- a/sys/sys/link_elf.h +++ b/sys/sys/link_elf.h @@ -93,6 +93,7 @@ __BEGIN_DECLS typedef int (*__dl_iterate_hdr_callback)(struct dl_phdr_info *, size_t, void *); extern int dl_iterate_phdr(__dl_iterate_hdr_callback, void *); int _rtld_addr_phdr(const void *, struct dl_phdr_info *); +int _rtld_get_stack_prot(void); __END_DECLS diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index e726fce..55cac2b 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -116,12 +116,17 @@ struct sysentvec { int (*sv_fetch_syscall_args)(struct thread *, struct syscall_args *); const char **sv_syscallnames; + vm_offset_t sv_shared_page_base; + vm_offset_t sv_shared_page_len; + vm_offset_t sv_sigcode_base; + void *sv_shared_page_obj; }; #define SV_ILP32 0x000100 #define SV_LP64 0x000200 #define SV_IA32 0x004000 #define SV_AOUT 0x008000 +#define SV_SHP 0x010000 #define SV_ABI_MASK 0xff #define SV_CURPROC_FLAG(x) (curproc->p_sysent->sv_flags & (x)) @@ -222,6 +227,13 @@ int lkmressys(struct thread *, struct nosys_args *); int syscall_thread_enter(struct thread *td, struct sysent *se); void syscall_thread_exit(struct thread *td, struct sysent *se); +int shared_page_fill(int size, int align, const char *data); +void exec_sysvec_init(void *param); + +#define INIT_SYSENTVEC(name, sv) \ + SYSINIT(name, SI_SUB_EXEC, SI_ORDER_ANY, \ + (sysinit_cfunc_t)exec_sysvec_init, sv); + #endif /* _KERNEL */ #endif /* !_SYS_SYSENT_H_ */