diff options
author | Florian Weimer <fweimer@redhat.com> | 2021-04-21 19:49:50 +0200 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2021-04-21 19:49:50 +0200 |
commit | f79f2065817e080f65f3c3a2fee966f5a97f1746 (patch) | |
tree | 1ee9c2bccdd0360bf9ef31e1cc7b662abcac32a5 /nptl/libc-cleanup.c | |
parent | 5715c29e91076800418833f2196f2082f439da75 (diff) | |
download | glibc-f79f2065817e080f65f3c3a2fee966f5a97f1746.tar.gz glibc-f79f2065817e080f65f3c3a2fee966f5a97f1746.tar.xz glibc-f79f2065817e080f65f3c3a2fee966f5a97f1746.zip |
nptl: Move legacy unwinding implementation into libc
It is still used internally. Since unwinding is now available unconditionally, avoid indirect calls through function pointers loaded from the stack by inlining the non-cancellation cleanup code. This avoids a regression in security hardening. The out-of-line __libc_cleanup_routine implementation is no longer needed because the inline definition is now static __always_inline. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'nptl/libc-cleanup.c')
-rw-r--r-- | nptl/libc-cleanup.c | 64 |
1 files changed, 61 insertions, 3 deletions
diff --git a/nptl/libc-cleanup.c b/nptl/libc-cleanup.c index 61f97eceda..14ccfe9285 100644 --- a/nptl/libc-cleanup.c +++ b/nptl/libc-cleanup.c @@ -17,11 +17,69 @@ <https://www.gnu.org/licenses/>. */ #include "pthreadP.h" +#include <tls.h> +#include <libc-lock.h> +void +__libc_cleanup_push_defer (struct _pthread_cleanup_buffer *buffer) +{ + struct pthread *self = THREAD_SELF; + + buffer->__prev = THREAD_GETMEM (self, cleanup); + + int cancelhandling = THREAD_GETMEM (self, cancelhandling); + + /* Disable asynchronous cancellation for now. */ + if (__glibc_unlikely (cancelhandling & CANCELTYPE_BITMASK)) + while (1) + { + int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, + cancelhandling + & ~CANCELTYPE_BITMASK, + cancelhandling); + if (__glibc_likely (curval == cancelhandling)) + /* Successfully replaced the value. */ + break; + + /* Prepare for the next round. */ + cancelhandling = curval; + } + + buffer->__canceltype = (cancelhandling & CANCELTYPE_BITMASK + ? PTHREAD_CANCEL_ASYNCHRONOUS + : PTHREAD_CANCEL_DEFERRED); + + THREAD_SETMEM (self, cleanup, buffer); +} +libc_hidden_def (__libc_cleanup_push_defer) void -__libc_cleanup_routine (struct __pthread_cleanup_frame *f) +__libc_cleanup_pop_restore (struct _pthread_cleanup_buffer *buffer) { - if (f->__do_it) - f->__cancel_routine (f->__cancel_arg); + struct pthread *self = THREAD_SELF; + + THREAD_SETMEM (self, cleanup, buffer->__prev); + + int cancelhandling; + if (__builtin_expect (buffer->__canceltype != PTHREAD_CANCEL_DEFERRED, 0) + && ((cancelhandling = THREAD_GETMEM (self, cancelhandling)) + & CANCELTYPE_BITMASK) == 0) + { + while (1) + { + int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, + cancelhandling + | CANCELTYPE_BITMASK, + cancelhandling); + if (__glibc_likely (curval == cancelhandling)) + /* Successfully replaced the value. */ + break; + + /* Prepare for the next round. */ + cancelhandling = curval; + } + + CANCELLATION_P (self); + } } +libc_hidden_def (__libc_cleanup_pop_restore) |