diff options
author | jdp <jdp@FreeBSD.org> | 2000-07-17 17:18:13 +0000 |
---|---|---|
committer | jdp <jdp@FreeBSD.org> | 2000-07-17 17:18:13 +0000 |
commit | d8dcadaeb6502770e772e80b4e14b0afb3fcb9d5 (patch) | |
tree | ff66dc70ea8da8bce0cc93925af492df17fd5c32 /libexec | |
parent | 5b9b8deed9b95d05509ec5d0c088b0190029e6bc (diff) | |
download | FreeBSD-src-d8dcadaeb6502770e772e80b4e14b0afb3fcb9d5.zip FreeBSD-src-d8dcadaeb6502770e772e80b4e14b0afb3fcb9d5.tar.gz |
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>
Diffstat (limited to 'libexec')
-rw-r--r-- | libexec/rtld-elf/alpha/lockdflt.c | 25 | ||||
-rw-r--r-- | libexec/rtld-elf/amd64/lockdflt.c | 34 | ||||
-rw-r--r-- | libexec/rtld-elf/i386/lockdflt.c | 34 |
3 files changed, 88 insertions, 5 deletions
diff --git a/libexec/rtld-elf/alpha/lockdflt.c b/libexec/rtld-elf/alpha/lockdflt.c index 65900a6..5847abb 100644 --- a/libexec/rtld-elf/alpha/lockdflt.c +++ b/libexec/rtld-elf/alpha/lockdflt.c @@ -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); } diff --git a/libexec/rtld-elf/amd64/lockdflt.c b/libexec/rtld-elf/amd64/lockdflt.c index b2ca9a5..c1c23c1 100644 --- a/libexec/rtld-elf/amd64/lockdflt.c +++ b/libexec/rtld-elf/amd64/lockdflt.c @@ -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); } diff --git a/libexec/rtld-elf/i386/lockdflt.c b/libexec/rtld-elf/i386/lockdflt.c index b2ca9a5..c1c23c1 100644 --- a/libexec/rtld-elf/i386/lockdflt.c +++ b/libexec/rtld-elf/i386/lockdflt.c @@ -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); } |