Implement the __pthread_map_stacks_exec() for libthr.
Stack creation code is changed to call _rtld_get_stack_prot() to get the stack protection right. There is a race where thread is created during dlopen() of dso that requires executable stacks. Then, _rtld_get_stack_prot() may return PROT_READ | PROT_WRITE, but thread is still not linked into the thread list. In this case, the callback misses the thread stack, and rechecks the required protection afterward. Reviewed by: davidxu
This commit is contained in:
parent
1fdd33fd31
commit
da2fcff746
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=217191
@ -382,6 +382,8 @@ FBSDprivate_1.0 {
|
||||
_thread_size_key;
|
||||
_thread_state_running;
|
||||
_thread_state_zoombie;
|
||||
|
||||
__pthread_map_stacks_exec;
|
||||
};
|
||||
|
||||
FBSD_1.1 {
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <sys/rtprio.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <errno.h>
|
||||
#include <link.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -31,6 +31,8 @@
|
||||
* A lockless rwlock for rtld.
|
||||
*/
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/mman.h>
|
||||
#include <link.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -194,6 +196,9 @@ _thr_rtld_init(void)
|
||||
/* force to resolve memcpy PLT */
|
||||
memcpy(&dummy, &dummy, sizeof(dummy));
|
||||
|
||||
mprotect(NULL, 0, 0);
|
||||
_rtld_get_stack_prot();
|
||||
|
||||
li.lock_create = _thr_rtld_lock_create;
|
||||
li.lock_destroy = _thr_rtld_lock_destroy;
|
||||
li.rlock_acquire = _thr_rtld_rlock_acquire;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <sys/queue.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <link.h>
|
||||
|
||||
#include "thr_private.h"
|
||||
|
||||
@ -128,6 +129,38 @@ 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_gc_list, gcle)
|
||||
_thr_stack_fix_protection(thrd);
|
||||
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 +243,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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user