Fix a bug which could cause programs with user threads packages to
lock against themselves, causing infinite spinning. Brian Feldman found this problem when testing with Mozilla and supplied the fix, which I have revised slightly. Here is the failure scenario. A thread calls dlopen() and acquires the writer lock. While the thread still holds the lock, a signal is delivered and caught. The signal handler tries to call a function which hasn't been bound yet. It thus enters the dynamic linker and tries to acquire the reader lock. Since the writer lock is already held, it will spin forever in the signal handler. The thread holding the lock won't be able to progress and release the lock. The solution is to block almost all signals while holding the exclusive lock. A similar problem could conceivably occur in the opposite order. Namely, a thread is holding the reader lock and then a signal handler calls dlopen() or dlclose() and spins waiting for the writer lock. We deal with this administratively by proclaiming that signal handlers aren't allowed to call dlopen() or dlclose(). Actually we don't have to proclaim a thing, since signal handlers aren't allowed to call any system functions except those which are explicitly permitted. Submitted by: Brian Fundakowski Feldman <green>
This commit is contained in:
parent
15602c8dd8
commit
cf98e66403
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=63316
@ -46,6 +46,7 @@
|
||||
* them to make some progress.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
@ -70,6 +71,7 @@ typedef struct Struct_Lock {
|
||||
} Lock;
|
||||
|
||||
static const struct timespec usec = { 0, 1000 }; /* 1 usec. */
|
||||
static sigset_t fullsigmask, oldsigmask;
|
||||
|
||||
static void *
|
||||
lock_create(void *context)
|
||||
@ -123,9 +125,16 @@ static void
|
||||
wlock_acquire(void *lock)
|
||||
{
|
||||
Lock *l = (Lock *)lock;
|
||||
sigset_t tmp_oldsigmask;
|
||||
|
||||
while (cmp0_and_store_int(&l->lock, WAFLAG) != 0)
|
||||
for ( ; ; ) {
|
||||
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
|
||||
if (cmp0_and_store_int(&l->lock, WAFLAG) == 0)
|
||||
break;
|
||||
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
|
||||
nanosleep(&usec, NULL);
|
||||
}
|
||||
oldsigmask = tmp_oldsigmask;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -142,6 +151,7 @@ wlock_release(void *lock)
|
||||
Lock *l = (Lock *)lock;
|
||||
|
||||
atomic_add_int(&l->lock, -WAFLAG);
|
||||
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -155,4 +165,17 @@ lockdflt_init(LockInfo *li)
|
||||
li->wlock_release = wlock_release;
|
||||
li->lock_destroy = lock_destroy;
|
||||
li->context_destroy = NULL;
|
||||
/*
|
||||
* Construct a mask to block all signals except traps which might
|
||||
* conceivably be generated within the dynamic linker itself.
|
||||
*/
|
||||
sigfillset(&fullsigmask);
|
||||
sigdelset(&fullsigmask, SIGILL);
|
||||
sigdelset(&fullsigmask, SIGTRAP);
|
||||
sigdelset(&fullsigmask, SIGABRT);
|
||||
sigdelset(&fullsigmask, SIGEMT);
|
||||
sigdelset(&fullsigmask, SIGFPE);
|
||||
sigdelset(&fullsigmask, SIGBUS);
|
||||
sigdelset(&fullsigmask, SIGSEGV);
|
||||
sigdelset(&fullsigmask, SIGSYS);
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ typedef struct Struct_Lock {
|
||||
} Lock;
|
||||
|
||||
static const struct timespec usec = { 0, 1000 }; /* 1 usec. */
|
||||
static sigset_t fullsigmask, oldsigmask;
|
||||
|
||||
static inline int
|
||||
cmpxchgl(int old, int new, volatile int *m)
|
||||
@ -144,10 +145,17 @@ static void
|
||||
lock80386_acquire(void *lock)
|
||||
{
|
||||
Lock *l = (Lock *)lock;
|
||||
sigset_t tmp_oldsigmask;
|
||||
|
||||
while (xchgl(1, &l->lock) != 0)
|
||||
for ( ; ; ) {
|
||||
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
|
||||
if (xchgl(1, &l->lock) == 0)
|
||||
break;
|
||||
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
|
||||
while (l->lock != 0)
|
||||
nanosleep(&usec, NULL);
|
||||
}
|
||||
oldsigmask = tmp_oldsigmask;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -156,6 +164,7 @@ lock80386_release(void *lock)
|
||||
Lock *l = (Lock *)lock;
|
||||
|
||||
l->lock = 0;
|
||||
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -175,9 +184,16 @@ static void
|
||||
wlock_acquire(void *lock)
|
||||
{
|
||||
Lock *l = (Lock *)lock;
|
||||
sigset_t tmp_oldsigmask;
|
||||
|
||||
while (cmpxchgl(0, WAFLAG, &l->lock) != 0)
|
||||
for ( ; ; ) {
|
||||
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
|
||||
if (cmpxchgl(0, WAFLAG, &l->lock) == 0)
|
||||
break;
|
||||
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
|
||||
nanosleep(&usec, NULL);
|
||||
}
|
||||
oldsigmask = tmp_oldsigmask;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -194,6 +210,7 @@ wlock_release(void *lock)
|
||||
Lock *l = (Lock *)lock;
|
||||
|
||||
atomic_add_int(&l->lock, -WAFLAG);
|
||||
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -250,4 +267,17 @@ lockdflt_init(LockInfo *li)
|
||||
li->rlock_acquire = li->wlock_acquire = lock80386_acquire;
|
||||
li->rlock_release = li->wlock_release = lock80386_release;
|
||||
}
|
||||
/*
|
||||
* Construct a mask to block all signals except traps which might
|
||||
* conceivably be generated within the dynamic linker itself.
|
||||
*/
|
||||
sigfillset(&fullsigmask);
|
||||
sigdelset(&fullsigmask, SIGILL);
|
||||
sigdelset(&fullsigmask, SIGTRAP);
|
||||
sigdelset(&fullsigmask, SIGABRT);
|
||||
sigdelset(&fullsigmask, SIGEMT);
|
||||
sigdelset(&fullsigmask, SIGFPE);
|
||||
sigdelset(&fullsigmask, SIGBUS);
|
||||
sigdelset(&fullsigmask, SIGSEGV);
|
||||
sigdelset(&fullsigmask, SIGSYS);
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ typedef struct Struct_Lock {
|
||||
} Lock;
|
||||
|
||||
static const struct timespec usec = { 0, 1000 }; /* 1 usec. */
|
||||
static sigset_t fullsigmask, oldsigmask;
|
||||
|
||||
static inline int
|
||||
cmpxchgl(int old, int new, volatile int *m)
|
||||
@ -144,10 +145,17 @@ static void
|
||||
lock80386_acquire(void *lock)
|
||||
{
|
||||
Lock *l = (Lock *)lock;
|
||||
sigset_t tmp_oldsigmask;
|
||||
|
||||
while (xchgl(1, &l->lock) != 0)
|
||||
for ( ; ; ) {
|
||||
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
|
||||
if (xchgl(1, &l->lock) == 0)
|
||||
break;
|
||||
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
|
||||
while (l->lock != 0)
|
||||
nanosleep(&usec, NULL);
|
||||
}
|
||||
oldsigmask = tmp_oldsigmask;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -156,6 +164,7 @@ lock80386_release(void *lock)
|
||||
Lock *l = (Lock *)lock;
|
||||
|
||||
l->lock = 0;
|
||||
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -175,9 +184,16 @@ static void
|
||||
wlock_acquire(void *lock)
|
||||
{
|
||||
Lock *l = (Lock *)lock;
|
||||
sigset_t tmp_oldsigmask;
|
||||
|
||||
while (cmpxchgl(0, WAFLAG, &l->lock) != 0)
|
||||
for ( ; ; ) {
|
||||
sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask);
|
||||
if (cmpxchgl(0, WAFLAG, &l->lock) == 0)
|
||||
break;
|
||||
sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL);
|
||||
nanosleep(&usec, NULL);
|
||||
}
|
||||
oldsigmask = tmp_oldsigmask;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -194,6 +210,7 @@ wlock_release(void *lock)
|
||||
Lock *l = (Lock *)lock;
|
||||
|
||||
atomic_add_int(&l->lock, -WAFLAG);
|
||||
sigprocmask(SIG_SETMASK, &oldsigmask, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -250,4 +267,17 @@ lockdflt_init(LockInfo *li)
|
||||
li->rlock_acquire = li->wlock_acquire = lock80386_acquire;
|
||||
li->rlock_release = li->wlock_release = lock80386_release;
|
||||
}
|
||||
/*
|
||||
* Construct a mask to block all signals except traps which might
|
||||
* conceivably be generated within the dynamic linker itself.
|
||||
*/
|
||||
sigfillset(&fullsigmask);
|
||||
sigdelset(&fullsigmask, SIGILL);
|
||||
sigdelset(&fullsigmask, SIGTRAP);
|
||||
sigdelset(&fullsigmask, SIGABRT);
|
||||
sigdelset(&fullsigmask, SIGEMT);
|
||||
sigdelset(&fullsigmask, SIGFPE);
|
||||
sigdelset(&fullsigmask, SIGBUS);
|
||||
sigdelset(&fullsigmask, SIGSEGV);
|
||||
sigdelset(&fullsigmask, SIGSYS);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user