int attribute_hidden __pthread_enable_asynccancel (void) { struct pthread *self = THREAD_SELF; int oldval; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); oldval = THREAD_GETMEM (self, cancelhandling); pthread_log_record (oldval, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); oldval = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { oldval = THREAD_GETMEM (self, cancelhandling); } while (1) { int newval = oldval | CANCELTYPE_BITMASK; if (newval == oldval) break; int curval; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); pthread_log_record (curval, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); curval = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); } if (__builtin_expect (curval == oldval, 1)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); } break; } /* Prepare the next round. */ oldval = curval; } return oldval; }
void __cleanup_fct_attribute __pthread_register_cancel_defer (__pthread_unwind_buf_t *buf) { struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; struct pthread *self = THREAD_SELF; /* Store old info. */ ibuf->priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf); ibuf->priv.data.cleanup = THREAD_GETMEM (self, cleanup); int cancelhandling; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); cancelhandling = THREAD_GETMEM (self, cancelhandling); pthread_log_record (cancelhandling, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); cancelhandling = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { cancelhandling = THREAD_GETMEM (self, cancelhandling); } /* Disable asynchronous cancellation for now. */ if (__builtin_expect (cancelhandling & CANCELTYPE_BITMASK, 0)) while (1) { int curval; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, cancelhandling & ~CANCELTYPE_BITMASK, cancelhandling); pthread_log_record (curval, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); curval = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, cancelhandling & ~CANCELTYPE_BITMASK, cancelhandling); } if (__builtin_expect (curval == cancelhandling, 1)) /* Successfully replaced the value. */ break; /* Prepare for the next round. */ cancelhandling = curval; } ibuf->priv.data.canceltype = (cancelhandling & CANCELTYPE_BITMASK ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED); /* Store the new cleanup handler info. */ THREAD_SETMEM (self, cleanup_jmp_buf, (struct pthread_unwind_buf *) buf); }
void internal_function attribute_hidden __libc_disable_asynccancel (int oldtype) { /* If asynchronous cancellation was enabled before we do not have anything to do. */ if (oldtype & CANCELTYPE_BITMASK) return; struct pthread *self = THREAD_SELF; #ifdef THREAD_ATOMIC_AND THREAD_ATOMIC_AND (self, cancelhandling, ~CANCELTYPE_BITMASK); #else int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = oldval & ~CANCELTYPE_BITMASK; if (newval == oldval) break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) break; /* Prepare the next round. */ oldval = curval; } #endif }
/* The next two functions are similar to pthread_setcanceltype() but more specialized for the use in the cancelable functions like write(). They do not need to check parameters etc. */ int attribute_hidden __pthread_enable_asynccancel (void) { struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = oldval | CANCELTYPE_BITMASK; if (newval == oldval) break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); } break; } /* Prepare the next round. */ oldval = curval; } return oldval; }
void __cleanup_fct_attribute __pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf) { struct pthread *self = THREAD_SELF; struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; THREAD_SETMEM (self, cleanup_jmp_buf, ibuf->priv.data.prev); int cancelhandling; if (ibuf->priv.data.canceltype != PTHREAD_CANCEL_DEFERRED && ((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); } }
void __cleanup_fct_attribute __pthread_unregister_cancel_restore (__pthread_unwind_buf_t *buf) { struct pthread *self = THREAD_SELF; struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; THREAD_SETMEM (self, cleanup_jmp_buf, ibuf->priv.data.prev); int cancelhandling; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); cancelhandling = THREAD_GETMEM (self, cancelhandling); pthread_log_record (cancelhandling, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); cancelhandling = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { cancelhandling = THREAD_GETMEM (self, cancelhandling); } if (ibuf->priv.data.canceltype != PTHREAD_CANCEL_DEFERRED && (cancelhandling & CANCELTYPE_BITMASK) == 0) { while (1) { int curval; if (is_recording()) { pthread_log_record (0, PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling, 1); curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, cancelhandling | CANCELTYPE_BITMASK, cancelhandling); pthread_log_record (curval, PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling, 0); } else if (is_replaying()) { pthread_log_replay (PTHREAD_CANCELHANDLING_ENTER, (u_long) &self->cancelhandling); curval = pthread_log_replay (PTHREAD_CANCELHANDLING_EXIT, (u_long) &self->cancelhandling); } else { curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, cancelhandling | CANCELTYPE_BITMASK, cancelhandling); } if (__builtin_expect (curval == cancelhandling, 1)) /* Successfully replaced the value. */ break; /* Prepare for the next round. */ cancelhandling = curval; } CANCELLATION_P (self); } }
/* The next two functions are similar to pthread_setcanceltype() but more specialized for the use in the cancelable functions like write(). They do not need to check parameters etc. */ int attribute_hidden __libc_enable_asynccancel (void) { struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = oldval | CANCELTYPE_BITMASK; if (__builtin_expect ((oldval & CANCELED_BITMASK) != 0, 0)) { /* If we are already exiting or if PTHREAD_CANCEL_DISABLED, stop right here. */ if ((oldval & (EXITING_BITMASK | CANCELSTATE_BITMASK)) != 0) break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval != oldval, 0)) { /* Somebody else modified the word, try again. */ oldval = curval; continue; } THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); /* NOTREACHED */ } int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) break; /* Prepare the next round. */ oldval = curval; } return oldval; }
/* For asynchronous cancellation we use a signal. This is the handler. */ static void sigcancel_handler (int sig, siginfo_t *si, void *ctx) { #ifdef __ASSUME_CORRECT_SI_PID /* Determine the process ID. It might be negative if the thread is in the middle of a fork() call. */ pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); if (__builtin_expect (pid < 0, 0)) pid = -pid; #endif /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGCANCEL #ifdef __ASSUME_CORRECT_SI_PID /* Kernels before 2.5.75 stored the thread ID and not the process ID in si_pid so we skip this test. */ || si->si_pid != pid #endif || si->si_code != SI_TKILL) return; struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { /* We are canceled now. When canceled by another thread this flag is already set but if the signal is directly send (internally or from another process) is has to be done here. */ int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; if (oldval == newval || (oldval & EXITING_BITMASK) != 0) /* Already canceled or exiting. */ break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (curval == oldval) { /* Set the return value. */ THREAD_SETMEM (self, result, PTHREAD_CANCELED); /* Make sure asynchronous cancellation is still enabled. */ if ((newval & CANCELTYPE_BITMASK) != 0) /* Run the registered destructors and terminate the thread. */ __do_cancel (); break; } oldval = curval; } }
int __pthread_setcanceltype (int type, int *oldtype) { if (type < PTHREAD_CANCEL_DEFERRED || type > PTHREAD_CANCEL_ASYNCHRONOUS) return EINVAL; #ifndef SIGCANCEL if (type == PTHREAD_CANCEL_ASYNCHRONOUS) return ENOTSUP; #endif volatile struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = (type == PTHREAD_CANCEL_ASYNCHRONOUS ? oldval | CANCELTYPE_BITMASK : oldval & ~CANCELTYPE_BITMASK); /* Store the old value. */ if (oldtype != NULL) *oldtype = ((oldval & CANCELTYPE_BITMASK) ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED); /* Avoid doing unnecessary work. The atomic operation can potentially be expensive if the memory has to be locked and remote cache lines have to be invalidated. */ if (oldval == newval) break; /* Update the cancel handling word. This has to be done atomically since other bits could be modified as well. */ int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__glibc_likely (curval == oldval)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) { THREAD_SETMEM (self, result, PTHREAD_CANCELED); __do_cancel (); } break; } /* Prepare for the next round. */ oldval = curval; } return 0; }
int attribute_protected __pthread_setcancelstate ( int state, int *oldstate) { volatile struct pthread *self; if (state < PTHREAD_CANCEL_ENABLE || state > PTHREAD_CANCEL_DISABLE) return EINVAL; self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { int newval = (state == PTHREAD_CANCEL_DISABLE ? oldval | CANCELSTATE_BITMASK : oldval & ~CANCELSTATE_BITMASK); /* Store the old value. */ if (oldstate != NULL) *oldstate = ((oldval & CANCELSTATE_BITMASK) ? PTHREAD_CANCEL_DISABLE : PTHREAD_CANCEL_ENABLE); /* Avoid doing unnecessary work. The atomic operation can potentially be expensive if the memory has to be locked and remote cache lines have to be invalidated. */ if (oldval == newval) break; /* Update the cancel handling word. This has to be done atomically since other bits could be modified as well. */ int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) { if (CANCEL_ENABLED_AND_CANCELED_AND_ASYNCHRONOUS (newval)) __do_cancel (); break; } /* Prepare for the next round. */ oldval = curval; } return 0; }
/* For asynchronous cancellation we use a signal. This is the handler. */ static void sighandler_setxid (int sig, siginfo_t *si, void *ctx) { #ifdef __ASSUME_CORRECT_SI_PID /* Determine the process ID. It might be negative if the thread is in the middle of a fork() call. */ pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); if (__builtin_expect (pid < 0, 0)) pid = -pid; #endif /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGSETXID #ifdef __ASSUME_CORRECT_SI_PID /* Kernels before 2.5.75 stored the thread ID and not the process ID in si_pid so we skip this test. */ || si->si_pid != pid #endif || si->si_code != SI_TKILL) return; INTERNAL_SYSCALL_DECL (err); INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, err, 3, __xidcmd->id[0], __xidcmd->id[1], __xidcmd->id[2]); /* Reset the SETXID flag. */ struct pthread *self = THREAD_SELF; int flags, newval; do { flags = THREAD_GETMEM (self, cancelhandling); newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, flags & ~SETXID_BITMASK, flags); } while (flags != newval); /* And release the futex. */ self->setxid_futex = 1; lll_futex_wake (&self->setxid_futex, 1, LLL_PRIVATE); if (atomic_decrement_val (&__xidcmd->cntr) == 0) lll_futex_wake (&__xidcmd->cntr, 1, LLL_PRIVATE); }
/* We use the SIGSETXID signal in the setuid, setgid, etc. implementations to tell each thread to call the respective setxid syscall on itself. This is the handler. */ static void sighandler_setxid (int sig, siginfo_t *si, void *ctx) { /* Determine the process ID. It might be negative if the thread is in the middle of a fork() call. */ pid_t pid = THREAD_GETMEM (THREAD_SELF, pid); int result; if (__glibc_unlikely (pid < 0)) pid = -pid; /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGSETXID || si->si_pid != pid || si->si_code != SI_TKILL) return; INTERNAL_SYSCALL_DECL (err); result = INTERNAL_SYSCALL_NCS (__xidcmd->syscall_no, err, 3, __xidcmd->id[0], __xidcmd->id[1], __xidcmd->id[2]); int error = 0; if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result, err))) error = INTERNAL_SYSCALL_ERRNO (result, err); __nptl_setxid_error (__xidcmd, error); /* Reset the SETXID flag. */ struct pthread *self = THREAD_SELF; int flags, newval; do { flags = THREAD_GETMEM (self, cancelhandling); newval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, flags & ~SETXID_BITMASK, flags); } while (flags != newval); /* And release the futex. */ self->setxid_futex = 1; futex_wake (&self->setxid_futex, 1, FUTEX_PRIVATE); if (atomic_decrement_val (&__xidcmd->cntr) == 0) futex_wake ((unsigned int *) &__xidcmd->cntr, 1, FUTEX_PRIVATE); }
/* For asynchronous cancellation we use a signal. This is the handler. */ static void sigcancel_handler (int sig, siginfo_t *si, void *ctx) { /* Safety check. It would be possible to call this function for other signals and send a signal from another process. This is not correct and might even be a security problem. Try to catch as many incorrect invocations as possible. */ if (sig != SIGCANCEL || si->si_pid != __getpid() || si->si_code != SI_TKILL) return; struct pthread *self = THREAD_SELF; int oldval = THREAD_GETMEM (self, cancelhandling); while (1) { /* We are canceled now. When canceled by another thread this flag is already set but if the signal is directly send (internally or from another process) is has to be done here. */ int newval = oldval | CANCELING_BITMASK | CANCELED_BITMASK; if (oldval == newval || (oldval & EXITING_BITMASK) != 0) /* Already canceled or exiting. */ break; int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (curval == oldval) { /* Set the return value. */ THREAD_SETMEM (self, result, PTHREAD_CANCELED); /* Make sure asynchronous cancellation is still enabled. */ if ((newval & CANCELTYPE_BITMASK) != 0) /* Run the registered destructors and terminate the thread. */ __do_cancel (); break; } oldval = curval; } }
void internal_function attribute_hidden __internal_pthread_disable_asynccancel (int oldtype) { /* If asynchronous cancellation was enabled before we do not have anything to do. */ if (oldtype & CANCELTYPE_BITMASK) return; struct pthread *self = THREAD_SELF; int newval; int oldval; oldval = THREAD_GETMEM (self, cancelhandling); while (1) { newval = oldval & ~CANCELTYPE_BITMASK; int curval; curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, newval, oldval); if (__builtin_expect (curval == oldval, 1)) break; /* Prepare the next round. */ oldval = curval; } /* We cannot return when we are being canceled. Upon return the thread might be things which would have to be undone. The following loop should loop until the cancellation signal is delivered. */ while (__builtin_expect ((newval & (CANCELING_BITMASK | CANCELED_BITMASK)) == CANCELING_BITMASK, 0)) { lll_futex_wait (&self->cancelhandling, newval, LLL_PRIVATE); newval = THREAD_GETMEM (self, cancelhandling); } }
void attribute_protected _pthread_cleanup_push_defer ( struct _pthread_cleanup_buffer *buffer, void (*routine) (void *), void *arg) { struct pthread *self = THREAD_SELF; buffer->__routine = routine; buffer->__arg = arg; buffer->__prev = THREAD_GETMEM (self, cleanup); int cancelhandling = THREAD_GETMEM (self, cancelhandling); /* Disable asynchronous cancellation for now. */ if (__builtin_expect (cancelhandling & CANCELTYPE_BITMASK, 0)) while (1) { int curval = THREAD_ATOMIC_CMPXCHG_VAL (self, cancelhandling, cancelhandling & ~CANCELTYPE_BITMASK, cancelhandling); if (__builtin_expect (curval == cancelhandling, 1)) /* 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); }
void attribute_protected _pthread_cleanup_pop_restore ( struct _pthread_cleanup_buffer *buffer, int execute) { 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 (__builtin_expect (curval == cancelhandling, 1)) /* Successfully replaced the value. */ break; /* Prepare for the next round. */ cancelhandling = curval; } CANCELLATION_P (self); } /* If necessary call the cleanup routine after we removed the current cleanup block from the list. */ if (execute) buffer->__routine (buffer->__arg); }
void __cleanup_fct_attribute __pthread_register_cancel_defer (__pthread_unwind_buf_t *buf) { struct pthread_unwind_buf *ibuf = (struct pthread_unwind_buf *) buf; struct pthread *self = THREAD_SELF; /* Store old info. */ ibuf->priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf); ibuf->priv.data.cleanup = 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; } ibuf->priv.data.canceltype = (cancelhandling & CANCELTYPE_BITMASK ? PTHREAD_CANCEL_ASYNCHRONOUS : PTHREAD_CANCEL_DEFERRED); /* Store the new cleanup handler info. */ THREAD_SETMEM (self, cleanup_jmp_buf, (struct pthread_unwind_buf *) buf); }