/* sigwait -- synchronously wait for a signal */ int __pthread_sigwait(const sigset_t * set, int * sig) { __volatile__ pthread_descr self = thread_self(); sigset_t mask; int s; sigjmp_buf jmpbuf; struct sigaction sa; /* Get ready to block all signals except those in set and the cancellation signal. Also check that handlers are installed on all signals in set, and if not, install our dummy handler. This is conformant to POSIX: "The effect of sigwait() on the signal actions for the signals in set is unspecified." */ __sigfillset(&mask); sigdelset(&mask, __pthread_sig_cancel); for (s = 1; s < NSIG; s++) { if (sigismember(set, s) && s != __pthread_sig_restart && s != __pthread_sig_cancel && s != __pthread_sig_debug) { sigdelset(&mask, s); if (__sighandler[s].old == (arch_sighandler_t) SIG_ERR || __sighandler[s].old == (arch_sighandler_t) SIG_DFL || __sighandler[s].old == (arch_sighandler_t) SIG_IGN) { sa.sa_handler = __pthread_null_sighandler; __sigfillset(&sa.sa_mask); sa.sa_flags = 0; sigaction(s, &sa, NULL); } } } /* Test for cancellation */ if (sigsetjmp(jmpbuf, 1) == 0) { THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf); if (! (THREAD_GETMEM(self, p_canceled) && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) { /* Reset the signal count */ THREAD_SETMEM(self, p_signal, 0); /* Say we're in sigwait */ THREAD_SETMEM(self, p_sigwaiting, 1); /* Unblock the signals and wait for them */ sigsuspend(&mask); } } THREAD_SETMEM(self, p_cancel_jmp, NULL); /* The signals are now reblocked. Check for cancellation */ pthread_testcancel(); /* We should have self->p_signal != 0 and equal to the signal received */ *sig = THREAD_GETMEM(self, p_signal); return 0; }
static int __NC(sigwait)(const sigset_t *set, int *sig) { sigset_t tmp_mask; struct sigaction saved[NSIG]; struct sigaction action; int save_errno; int this; /* Prepare set. */ __sigfillset (&tmp_mask); /* Unblock all signals in the SET and register our nice handler. */ action.sa_handler = ignore_signal; action.sa_flags = 0; __sigfillset (&action.sa_mask); /* Block all signals for handler. */ /* Make sure we recognize error conditions by setting WAS_SIG to a value which does not describe a legal signal number. */ was_sig = -1; for (this = 1; this < NSIG; ++this) if (__sigismember (set, this)) { /* Unblock this signal. */ __sigdelset (&tmp_mask, this); /* Register temporary action handler. */ /* In Linux (as of 2.6.25), fails only if sig is SIGKILL or SIGSTOP */ /* (so, will it work correctly if set has, say, SIGSTOP?) */ if (sigaction (this, &action, &saved[this]) != 0) goto restore_handler; } /* Now we can wait for signals. */ __NC(sigsuspend)(&tmp_mask); restore_handler: save_errno = errno; while (--this >= 1) if (__sigismember (set, this)) /* We ignore errors here since we must restore all handlers. */ sigaction (this, &saved[this], NULL); __set_errno (save_errno); /* Store the result and return. */ *sig = was_sig; return was_sig == -1 ? -1 : 0; }
/* Cause an abnormal program termination with core-dump. */ void abort(void) { sigset_t sigset; /* Make sure we acquire the lock before proceeding. */ LOCK; /* Unmask SIGABRT to be sure we can get it */ if (__sigemptyset(&sigset) == 0 && __sigaddset(&sigset, SIGABRT) == 0) { sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *) NULL); } /* If we are using stdio, try to shut it down. At the very least, * this will attempt to commit all buffered writes. It may also * unbuffer all writable files, or close them outright. * Check the stdio routines for details. */ if (_stdio_term) _stdio_term(); while (1) { /* Try to suicide with a SIGABRT. */ if (been_there_done_that == 0) { been_there_done_that++; UNLOCK; raise(SIGABRT); LOCK; } /* Still here? Try to remove any signal handlers. */ if (been_there_done_that == 1) { struct sigaction act; been_there_done_that++; memset (&act, '\0', sizeof (struct sigaction)); act.sa_handler = SIG_DFL; __sigfillset (&act.sa_mask); act.sa_flags = 0; sigaction (SIGABRT, &act, NULL); } /* Still here? Try to suicide with an illegal instruction */ if (been_there_done_that == 2) { been_there_done_that++; ABORT_INSTRUCTION; } /* Still here? Try to at least exit */ if (been_there_done_that == 3) { been_there_done_that++; _exit (127); } /* Still here? We're screwed. Sleepy time. Good night */ while (1) /* Try for ever and ever. */ ABORT_INSTRUCTION; } }
int __profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale) { struct sigaction act; struct itimerval timer; #ifndef IS_IN_rtld static struct sigaction oact; static struct itimerval otimer; # define oact_ptr &oact # define otimer_ptr &otimer if (sample_buffer == NULL) { /* Disable profiling. */ if (samples == NULL) /* Wasn't turned on. */ return 0; if (__setitimer (ITIMER_PROF, &otimer, NULL) < 0) return -1; samples = NULL; return __sigaction (SIGPROF, &oact, NULL); } if (samples) { /* Was already turned on. Restore old timer and signal handler first. */ if (__setitimer (ITIMER_PROF, &otimer, NULL) < 0 || __sigaction (SIGPROF, &oact, NULL) < 0) return -1; } #else /* In ld.so profiling should never be disabled once it runs. */ //assert (sample_buffer != NULL); # define oact_ptr NULL # define otimer_ptr NULL #endif samples = sample_buffer; nsamples = size / sizeof *samples; pc_offset = offset; pc_scale = scale; act.sa_handler = (sighandler_t) &profil_counter; act.sa_flags = SA_RESTART; __sigfillset (&act.sa_mask); if (__sigaction (SIGPROF, &act, oact_ptr) < 0) return -1; timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 1000000 / __profile_frequency (); timer.it_interval = timer.it_value; return __setitimer (ITIMER_PROF, &timer, otimer_ptr); }
/* Cause an abnormal program termination with core-dump. */ void abort(void) { sigset_t sigset; /* Make sure we acquire the lock before proceeding. */ LOCK; /* Unmask SIGABRT to be sure we can get it */ if (__sigemptyset(&sigset) == 0 && __sigaddset(&sigset, SIGABRT) == 0) { sigprocmask(SIG_UNBLOCK, &sigset, (sigset_t *) NULL); } while (1) { /* Try to suicide with a SIGABRT. */ if (been_there_done_that == 0) { been_there_done_that++; UNLOCK; raise(SIGABRT); LOCK; } /* Still here? Try to remove any signal handlers. */ if (been_there_done_that == 1) { struct sigaction act; been_there_done_that++; memset (&act, '\0', sizeof (struct sigaction)); act.sa_handler = SIG_DFL; __sigfillset (&act.sa_mask); act.sa_flags = 0; sigaction (SIGABRT, &act, NULL); } /* Still here? Try to suicide with an illegal instruction */ if (been_there_done_that == 2) { been_there_done_that++; ABORT_INSTRUCTION; } /* Still here? Try to at least exit */ if (been_there_done_that == 3) { been_there_done_that++; _exit (127); } /* Still here? We're screwed. Sleepy time. Good night */ while (1) /* Try for ever and ever. */ ABORT_INSTRUCTION; } }
/* Set all signals in SET. */ int sigfillset (sigset_t *set) { #if 0 /* is it really required by standards?! */ if (set == NULL) { __set_errno (EINVAL); return -1; } #endif __sigfillset (set); /* If the implementation uses a cancellation signal don't set the bit. */ #ifdef SIGCANCEL __sigdelset (set, SIGCANCEL); #endif /* Likewise for the signal to implement setxid. */ #ifdef SIGSETXID __sigdelset (set, SIGSETXID); #endif return 0; }
static inline void __hardened_gentoo_fail(void) { #define MESSAGE_BUFSIZ 512 static pid_t pid; static int plen, i, hlen; static char message[MESSAGE_BUFSIZ]; /* <11> is LOG_USER|LOG_ERR. A dummy date for loggers to skip over. */ static const char msg_header[] = "<11>" __DATE__ " " __TIME__ " glibc-gentoo-hardened-check: "; static const char msg_ssd[] = "*** " ERROR_MSG " detected ***: "; static const char msg_terminated[] = " terminated; "; static const char msg_report[] = "report to " REPORT_BUGS_TO "\n"; static const char msg_unknown[] = "<unknown>"; static int log_socket, connect_result; static struct sockaddr_un sock; static unsigned long int socketargs[4]; /* Build socket address */ sock.sun_family = AF_UNIX; i = 0; while (path_log[i] != '\0' && i < sizeof(sock.sun_path) - 1) { sock.sun_path[i] = path_log[i]; ++i; } sock.sun_path[i] = '\0'; /* Try SOCK_DGRAM connection to syslog */ connect_result = -1; DO_SOCKET(log_socket, AF_UNIX, SOCK_DGRAM, 0); if (log_socket != -1) DO_CONNECT(connect_result, log_socket, &sock, sizeof(sock)); if (connect_result == -1) { if (log_socket != -1) INLINE_SYSCALL(close, 1, log_socket); /* Try SOCK_STREAM connection to syslog */ DO_SOCKET(log_socket, AF_UNIX, SOCK_STREAM, 0); if (log_socket != -1) DO_CONNECT(connect_result, log_socket, &sock, sizeof(sock)); } /* Build message. Messages are generated both in the old style and new style, * so that log watchers that are configured for the old-style message continue * to work. */ #define strconcat(str) \ ({ \ i = 0; \ while ((str[i] != '\0') && ((i + plen) < (MESSAGE_BUFSIZ - 1))) { \ message[plen + i] = str[i]; \ ++i; \ } \ plen += i; \ }) /* Tersely log the failure */ plen = 0; strconcat(msg_header); hlen = plen; strconcat(msg_ssd); if (__progname != NULL) strconcat(__progname); else strconcat(msg_unknown); strconcat(msg_terminated); strconcat(msg_report); /* Write out error message to STDERR, to syslog if open */ INLINE_SYSCALL(write, 3, STDERR_FILENO, message + hlen, plen - hlen); if (connect_result != -1) { INLINE_SYSCALL(write, 3, log_socket, message, plen); INLINE_SYSCALL(close, 1, log_socket); } /* Time to kill self since we have no idea what is going on */ pid = INLINE_SYSCALL(getpid, 0); if (ENABLE_SSP_SMASH_DUMPS_CORE) { /* Remove any user-supplied handler for SIGABRT, before using it. */ #if 0 /* * Note: Disabled because some programs catch & process their * own crashes. We've already enabled this code path which * means we want to let core dumps happen. */ static struct sigaction default_abort_act; default_abort_act.sa_handler = SIG_DFL; default_abort_act.sa_sigaction = NULL; __sigfillset(&default_abort_act.sa_mask); default_abort_act.sa_flags = 0; if (DO_SIGACTION(SIGABRT, &default_abort_act, NULL) == 0) INLINE_SYSCALL(kill, 2, pid, SIGABRT); #endif /* Use abort() directly. http://crbug.com/406598 */ abort(); } /* SIGKILL is only signal which cannot be caught */ INLINE_SYSCALL(kill, 2, pid, SIGKILL); /* In case the kill didn't work, exit anyway. * The loop prevents gcc thinking this routine returns. */ while (1) INLINE_SYSCALL(exit, 1, 137); }
int attribute_noreturn __pthread_manager(void *arg) { int reqfd = (int) (long int) arg; #ifdef USE_SELECT struct timeval tv; fd_set fd; #else struct pollfd ufd; #endif sigset_t manager_mask; int n; struct pthread_request request; /* If we have special thread_self processing, initialize it. */ #ifdef INIT_THREAD_SELF INIT_THREAD_SELF(&__pthread_manager_thread, 1); #endif /* Set the error variable. */ __pthread_manager_thread.p_errnop = &__pthread_manager_thread.p_errno; __pthread_manager_thread.p_h_errnop = &__pthread_manager_thread.p_h_errno; #ifdef __UCLIBC_HAS_XLOCALE__ /* Initialize thread's locale to the global locale. */ __pthread_manager_thread.locale = __global_locale; #endif /* __UCLIBC_HAS_XLOCALE__ */ /* Block all signals except __pthread_sig_cancel and SIGTRAP */ __sigfillset(&manager_mask); sigdelset(&manager_mask, __pthread_sig_cancel); /* for thread termination */ sigdelset(&manager_mask, SIGTRAP); /* for debugging purposes */ if (__pthread_threads_debug && __pthread_sig_debug > 0) sigdelset(&manager_mask, __pthread_sig_debug); sigprocmask(SIG_SETMASK, &manager_mask, NULL); /* Raise our priority to match that of main thread */ __pthread_manager_adjust_prio(__pthread_main_thread->p_priority); /* Synchronize debugging of the thread manager */ n = TEMP_FAILURE_RETRY(read(reqfd, (char *)&request, sizeof(request))); #ifndef USE_SELECT ufd.fd = reqfd; ufd.events = POLLIN; #endif /* Enter server loop */ while(1) { #ifdef USE_SELECT tv.tv_sec = 2; tv.tv_usec = 0; FD_ZERO (&fd); FD_SET (reqfd, &fd); n = select (reqfd + 1, &fd, NULL, NULL, &tv); #else PDEBUG("before poll\n"); n = poll(&ufd, 1, 2000); PDEBUG("after poll\n"); #endif /* Check for termination of the main thread */ if (getppid() == 1) { pthread_kill_all_threads(SIGKILL, 0); _exit(0); } /* Check for dead children */ if (terminated_children) { terminated_children = 0; pthread_reap_children(); } /* Read and execute request */ #ifdef USE_SELECT if (n == 1) #else if (n == 1 && (ufd.revents & POLLIN)) #endif { PDEBUG("before read\n"); n = read(reqfd, (char *)&request, sizeof(request)); PDEBUG("after read, n=%d\n", n); switch(request.req_kind) { case REQ_CREATE: PDEBUG("got REQ_CREATE\n"); request.req_thread->p_retcode = pthread_handle_create((pthread_t *) &request.req_thread->p_retval, request.req_args.create.attr, request.req_args.create.fn, request.req_args.create.arg, &request.req_args.create.mask, request.req_thread->p_pid, request.req_thread->p_report_events, &request.req_thread->p_eventbuf.eventmask); PDEBUG("restarting %p\n", request.req_thread); restart(request.req_thread); break; case REQ_FREE: PDEBUG("got REQ_FREE\n"); pthread_handle_free(request.req_args.free.thread_id); break; case REQ_PROCESS_EXIT: PDEBUG("got REQ_PROCESS_EXIT from %p, exit code = %d\n", request.req_thread, request.req_args.exit.code); pthread_handle_exit(request.req_thread, request.req_args.exit.code); break; case REQ_MAIN_THREAD_EXIT: PDEBUG("got REQ_MAIN_THREAD_EXIT\n"); main_thread_exiting = 1; /* Reap children in case all other threads died and the signal handler went off before we set main_thread_exiting to 1, and therefore did not do REQ_KICK. */ pthread_reap_children(); if (__pthread_main_thread->p_nextlive == __pthread_main_thread) { restart(__pthread_main_thread); /* The main thread will now call exit() which will trigger an __on_exit handler, which in turn will send REQ_PROCESS_EXIT to the thread manager. In case you are wondering how the manager terminates from its loop here. */ } break; case REQ_POST: PDEBUG("got REQ_POST\n"); sem_post(request.req_args.post); break; case REQ_DEBUG: PDEBUG("got REQ_DEBUG\n"); /* Make gdb aware of new thread and gdb will restart the new thread when it is ready to handle the new thread. */ if (__pthread_threads_debug && __pthread_sig_debug > 0) { PDEBUG("about to call raise(__pthread_sig_debug)\n"); raise(__pthread_sig_debug); } case REQ_KICK: /* This is just a prod to get the manager to reap some threads right away, avoiding a potential delay at shutdown. */ break; } } } }
/* Cause an abnormal program termination with core-dump. */ void abort (void) { struct sigaction act; sigset_t sigs; /* First acquire the lock. */ __libc_lock_lock_recursive (lock); /* Now it's for sure we are alone. But recursive calls are possible. */ /* Unlock SIGABRT. */ if (stage == 0) { ++stage; if (__sigemptyset (&sigs) == 0 && __sigaddset (&sigs, SIGABRT) == 0) __sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL); } /* Flush all streams. We cannot close them now because the user might have registered a handler for SIGABRT. */ if (stage == 1) { ++stage; fflush (NULL); } /* Send signal which possibly calls a user handler. */ if (stage == 2) { /* This stage is special: we must allow repeated calls of `abort' when a user defined handler for SIGABRT is installed. This is risky since the `raise' implementation might also fail but I don't see another possibility. */ int save_stage = stage; stage = 0; __libc_lock_unlock_recursive (lock); raise (SIGABRT); __libc_lock_lock_recursive (lock); stage = save_stage + 1; } /* There was a handler installed. Now remove it. */ if (stage == 3) { ++stage; memset (&act, '\0', sizeof (struct sigaction)); act.sa_handler = SIG_DFL; __sigfillset (&act.sa_mask); act.sa_flags = 0; __sigaction (SIGABRT, &act, NULL); } /* Now close the streams which also flushes the output the user defined handler might has produced. */ if (stage == 4) { ++stage; __fcloseall (); } /* Try again. */ if (stage == 5) { ++stage; raise (SIGABRT); } /* Now try to abort using the system specific command. */ if (stage == 6) { ++stage; ABORT_INSTRUCTION; } /* If we can't signal ourselves and the abort instruction failed, exit. */ if (stage == 7) { ++stage; _exit (127); } /* If even this fails try to use the provided instruction to crash or otherwise make sure we never return. */ while (1) /* Try for ever and ever. */ ABORT_INSTRUCTION; }
int lckpwdf (void) { sigset_t saved_set; /* Saved set of caught signals. */ struct sigaction saved_act; /* Saved signal action. */ sigset_t new_set; /* New set of caught signals. */ struct sigaction new_act; /* New signal action. */ struct flock fl; /* Information struct for locking. */ int result; if (lock_fd != -1) /* Still locked by own process. */ return -1; /* Prevent problems caused by multiple threads. */ __UCLIBC_MUTEX_LOCK(mylock); lock_fd = open (_PATH_PASSWD, O_WRONLY | O_CLOEXEC); if (lock_fd == -1) { goto DONE; } #ifndef __ASSUME_O_CLOEXEC /* Make sure file gets correctly closed when process finished. */ fcntl (lock_fd, F_SETFD, FD_CLOEXEC); #endif /* Now we have to get exclusive write access. Since multiple process could try this we won't stop when it first fails. Instead we set a timeout for the system call. Once the timer expires it is likely that there are some problems which cannot be resolved by waiting. (sa_flags have no SA_RESTART. Thus SIGALRM will EINTR fcntl(F_SETLKW) It is important that we don't change the signal state. We must restore the old signal behaviour. */ memset (&new_act, '\0', sizeof (new_act)); new_act.sa_handler = noop_handler; __sigfillset (&new_act.sa_mask); /* Install new action handler for alarm and save old. * This never fails in Linux. */ sigaction (SIGALRM, &new_act, &saved_act); /* Now make sure the alarm signal is not blocked. */ __sigemptyset (&new_set); __sigaddset (&new_set, SIGALRM); sigprocmask (SIG_UNBLOCK, &new_set, &saved_set); /* Start timer. If we cannot get the lock in the specified time we get a signal. */ alarm (TIMEOUT); /* Try to get the lock. */ memset (&fl, '\0', sizeof (fl)); if (F_WRLCK) fl.l_type = F_WRLCK; if (SEEK_SET) fl.l_whence = SEEK_SET; result = fcntl (lock_fd, F_SETLKW, &fl); /* Clear alarm. */ alarm (0); sigprocmask (SIG_SETMASK, &saved_set, NULL); sigaction (SIGALRM, &saved_act, NULL); if (result < 0) { close(lock_fd); lock_fd = -1; } DONE: __UCLIBC_MUTEX_UNLOCK(mylock); return 0; /* TODO: return result? */ }
int __lckpwdf (void) { int flags; sigset_t saved_set; /* Saved set of caught signals. */ struct sigaction saved_act; /* Saved signal action. */ sigset_t new_set; /* New set of caught signals. */ struct sigaction new_act; /* New signal action. */ struct flock fl; /* Information struct for locking. */ int result; if (lock_fd != -1) /* Still locked by own process. */ return -1; /* Prevent problems caused by multiple threads. */ __libc_lock_lock (lock); int oflags = O_WRONLY | O_CREAT; #ifdef O_CLOEXEC oflags |= O_CLOEXEC; #endif lock_fd = __open (PWD_LOCKFILE, oflags, 0600); if (lock_fd == -1) /* Cannot create lock file. */ RETURN_CLOSE_FD (-1); #ifndef __ASSUME_O_CLOEXEC # ifdef O_CLOEXEC if (__have_o_cloexec <= 0) # endif { /* Make sure file gets correctly closed when process finished. */ flags = __fcntl (lock_fd, F_GETFD, 0); if (flags == -1) /* Cannot get file flags. */ RETURN_CLOSE_FD (-1); # ifdef O_CLOEXEC if (__have_o_cloexec == 0) __have_o_cloexec = (flags & FD_CLOEXEC) == 0 ? -1 : 1; if (__have_o_cloexec < 0) # endif { flags |= FD_CLOEXEC; /* Close on exit. */ if (__fcntl (lock_fd, F_SETFD, flags) < 0) /* Cannot set new flags. */ RETURN_CLOSE_FD (-1); } } #endif /* Now we have to get exclusive write access. Since multiple process could try this we won't stop when it first fails. Instead we set a timeout for the system call. Once the timer expires it is likely that there are some problems which cannot be resolved by waiting. It is important that we don't change the signal state. We must restore the old signal behaviour. */ memset (&new_act, '\0', sizeof (struct sigaction)); new_act.sa_handler = noop_handler; __sigfillset (&new_act.sa_mask); new_act.sa_flags = 0ul; /* Install new action handler for alarm and save old. */ if (__sigaction (SIGALRM, &new_act, &saved_act) < 0) /* Cannot install signal handler. */ RETURN_CLOSE_FD (-1); /* Now make sure the alarm signal is not blocked. */ __sigemptyset (&new_set); __sigaddset (&new_set, SIGALRM); if (__sigprocmask (SIG_UNBLOCK, &new_set, &saved_set) < 0) RETURN_RESTORE_HANDLER (-1); /* Start timer. If we cannot get the lock in the specified time we get a signal. */ alarm (TIMEOUT); /* Try to get the lock. */ memset (&fl, '\0', sizeof (struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; result = __fcntl (lock_fd, F_SETLKW, &fl); RETURN_CLEAR_ALARM (result); }