int main(int argc, char* argv[]) { signal(SIGUSR1, sighandler); signal(SIGUSR2, sighandler); tgkill(getpid(), gettid(), SIGUSR1); tgkill(getpid(), gettid(), SIGUSR2); test_assert(2 == num_signals_caught); atomic_puts("EXIT-SUCCESS"); return 0; }
int raise(int sig) { // Protect ourselves against stale cached PID/TID values by fetching them via syscall. // http://b/37769298 pid_t pid = syscall(__NR_getpid); pid_t tid = syscall(__NR_gettid); return tgkill(pid, tid, sig); }
static int r_debug_native_kill (RDebug *dbg, int pid, int tid, int sig) { int ret = false; if (pid == 0) pid = dbg->pid; #if __WINDOWS__ && !__CYGWIN__ ret = w32_terminate_process (dbg, pid); #else #if 0 if (thread) { // XXX this is linux>2.5 specific..ugly if (dbg->tid>0 && (ret = tgkill (dbg->pid, dbg->tid, sig))) { if (ret != -1) ret = true; } } else { #endif if ((r_sandbox_kill (pid, sig) != -1)) ret = true; if (errno == 1) ret = -true; // EPERM #if 0 // } #endif #endif return ret; } struct r_debug_desc_plugin_t r_debug_desc_plugin_native; static int r_debug_native_init (RDebug *dbg) { dbg->h->desc = r_debug_desc_plugin_native; #if __WINDOWS__ && !__CYGWIN__ return w32_dbg_init (); #elif __APPLE__ return xnu_init (); #else return true; #endif }
int main(void) { signal(SIGUSR1, sighandler); signal(SIGUSR2, sighandler); tgkill(getpid(), sys_gettid(), SIGUSR1); tgkill(getpid(), sys_gettid(), SIGUSR2); test_assert(2 == num_signals_caught); syscall(SYS_tkill, sys_gettid(), SIGUSR1); syscall(SYS_tkill, sys_gettid(), SIGUSR2); test_assert(4 == num_signals_caught); atomic_puts("EXIT-SUCCESS"); return 0; }
/* * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void*) { // It's possible somebody cleared the SA_SIGINFO flag, which would mean // our "info" arg holds an undefined value. if (!have_siginfo(signal_number)) { info = NULL; } log_signal_summary(signal_number, info); send_debuggerd_packet(info); // Remove our net so we fault for real when we return. signal(signal_number, SIG_DFL); // These signals are not re-thrown when we resume. This means that // crashing due to (say) SIGPIPE doesn't work the way you'd expect it // to. We work around this by throwing them manually. We don't want // to do this for *all* signals because it'll screw up the si_addr for // faults like SIGSEGV. It does screw up the si_code, which is why we // passed that to debuggerd above. switch (signal_number) { case SIGABRT: case SIGFPE: case SIGPIPE: #if defined(SIGSTKFLT) case SIGSTKFLT: #endif case SIGTRAP: tgkill(getpid(), gettid(), signal_number); break; default: // SIGILL, SIGBUS, SIGSEGV break; } }
static void * start (void *arg) { tgkill (getpid (), gettid (), SIGUSR1); assert (0); return NULL; }
int pthread_kill(pthread_t t, int sig) { ErrnoRestorer errno_restorer; ErrnoRestorer_init(&errno_restorer); pthread_accessor thread; pthread_accessor_init(&thread, t); if (pthread_accessor_get(&thread) == NULL) { pthread_accessor_fini(&thread); ErrnoRestorer_fini(&errno_restorer); return ESRCH; } // There's a race here, but it's one we share with all other C libraries. pid_t tid = pthread_accessor_get(&thread)->tid; pthread_accessor_Unlock(&thread); int rc = tgkill(getpid(), tid, sig); if (rc == -1) { pthread_accessor_fini(&thread); int ret = errno; ErrnoRestorer_fini(&errno_restorer); return ret; } pthread_accessor_fini(&thread); ErrnoRestorer_fini(&errno_restorer); return 0; }
static bool thread_signal(thread_t * thread, int signo) { debug("Sending signal %s to %s.", str_signal(signo), str_thread(thread)); if (tgkill(thread->process->pid, thread->pid, signo) != 0) { thread_handle_ptrace_error(thread, "sending signal to", errno); return false; } else return true; }
static void *fn(void *arg) { struct timespec rem, req = { .tv_sec = 1, .tv_nsec = 0 }; other_tid = gettid(); // The syscall will be interrupted by the main thread if (nanosleep(&req, &rem) != -1) TEST_EXIT(1); if (errno != EINTR) TEST_EXIT(1); // The handler for the signal must have run after the nanosleep() // syscall was interrupted if (!signal_serviced) TEST_EXIT(1); // Test is interrupted before timer has expired, there must // be some time remaining if (!rem.tv_sec && !rem.tv_nsec) TEST_EXIT(1); return 0; } int main(void) { struct sigaction act = { .sa_handler = handler, .sa_flags = 0, }; if (sigaction(SIGUSR1, &act, NULL)) TEST_EXIT(1); pthread_t thread; if (pthread_create(&thread, NULL, fn, NULL)) TEST_EXIT(1); // The other thread startup might be delayed depending on the // scheduler's prolicy while (other_tid == -1) sched_yield(); // Interrupt the other thread which is blocking in a sleep operation if (tgkill(getpid(), other_tid, SIGUSR1)) TEST_EXIT(1); // Join to make sure there other thread was interrupted with // some time remaining if (pthread_join(thread, NULL)) TEST_EXIT(1); TEST_EXIT(0); }
bool BacktraceThread::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { if (ucontext) { // Unwind using an already existing ucontext. return impl_->Unwind(num_ignore_frames, ucontext); } // Prevent multiple threads trying to set the trigger action on different // threads at the same time. if (pthread_mutex_lock(&g_sigaction_mutex) < 0) { BACK_LOGW("sigaction failed: %s", strerror(errno)); return false; } ThreadEntry* entry = ThreadEntry::Get(Pid(), Tid()); entry->Lock(); struct sigaction act, oldact; memset(&act, 0, sizeof(act)); act.sa_sigaction = SignalHandler; act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; sigemptyset(&act.sa_mask); if (sigaction(THREAD_SIGNAL, &act, &oldact) != 0) { BACK_LOGW("sigaction failed %s", strerror(errno)); entry->Unlock(); ThreadEntry::Remove(entry); pthread_mutex_unlock(&g_sigaction_mutex); return false; } if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) { BACK_LOGW("tgkill %d failed: %s", Tid(), strerror(errno)); sigaction(THREAD_SIGNAL, &oldact, NULL); entry->Unlock(); ThreadEntry::Remove(entry); pthread_mutex_unlock(&g_sigaction_mutex); return false; } // Wait for the thread to get the ucontext. entry->Wait(0); // After the thread has received the signal, allow other unwinders to // continue. sigaction(THREAD_SIGNAL, &oldact, NULL); pthread_mutex_unlock(&g_sigaction_mutex); bool unwind_done = impl_->Unwind(num_ignore_frames, entry->GetUcontext()); // Tell the signal handler to exit and release the entry. entry->Wake(); return unwind_done; }
bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) { entry->state = STATE_WAITING; if (tgkill(Pid(), Tid(), SIGURG) != 0) { BACK_LOGW("tgkill failed %s", strerror(errno)); return false; } // Allow up to ten seconds for the dump to start. int wait_millis = 10000; int32_t state; while (true) { state = android_atomic_acquire_load(&entry->state); if (state != STATE_WAITING) { break; } if (wait_millis--) { usleep(1000); } else { break; } } bool cancelled = false; if (state == STATE_WAITING) { if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state) == 0) { BACK_LOGW("Cancelled dump of thread %d", entry->tid); state = STATE_CANCEL; cancelled = true; } else { state = android_atomic_acquire_load(&entry->state); } } // Wait for at most ten seconds for the cancel or dump to finish. wait_millis = 10000; while (android_atomic_acquire_load(&entry->state) != STATE_DONE) { if (wait_millis--) { usleep(1000); } else { BACK_LOGW("Didn't finish thread unwind in 60 seconds."); break; } } return !cancelled; }
int pthread_kill(pthread_t t, int sig) { ErrnoRestorer errno_restorer; pthread_accessor thread(t); if (thread.get() == NULL) { return ESRCH; } // There's a race here, but it's one we share with all other C libraries. pid_t tid = thread->tid; thread.Unlock(); int rc = tgkill(getpid(), tid, sig); if (rc == -1) { return errno; } return 0; }
static int thread_signal(pid_t pid, pid_t tid, int sig) { int status; int ret; ret = tgkill(pid, tid, sig); if (ret == -1) return -1; for(;;) { int stop_sig = 0; if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) == -1) return -1; if (WIFEXITED(status)) return -1; if (WIFSTOPPED(status)) { stop_sig = WSTOPSIG(status); if (stop_sig == sig) break; if (stop_sig == SIGTRAP) stop_sig = 0; else if (stop_sig == SIGSTOP) stop_sig = 0; } if (ptrace(PTRACE_CONT, tid, 0, stop_sig) == -1) { fprintf(stderr, "%s:%d ptrace CONT (%s)\n", __FUNCTION__, __LINE__, strerror(errno)); return -1; } } return 0; }
/* * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ void debugger_signal_handler(int n, siginfo_t* info, void*) { char msgbuf[128]; /* * It's possible somebody cleared the SA_SIGINFO flag, which would mean * our "info" arg holds an undefined value. */ if (!haveSiginfo(n)) { info = NULL; } logSignalSummary(n, info); pid_t tid = gettid(); int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); if (s >= 0) { /* debugger knows our pid from the credentials on the * local socket but we need to tell it our tid. It * is paranoid and will verify that we are giving a tid * that's actually in our process */ int ret; debugger_msg_t msg; msg.action = DEBUGGER_ACTION_CRASH; msg.tid = tid; ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))); if (ret == sizeof(msg)) { /* if the write failed, there is no point to read on * the file descriptor. */ ret = TEMP_FAILURE_RETRY(read(s, &tid, 1)); int saved_errno = errno; notify_gdb_of_libraries(); errno = saved_errno; } if (ret < 0) { /* read or write failed -- broken connection? */ format_buffer(msgbuf, sizeof(msgbuf), "Failed while talking to debuggerd: %s", strerror(errno)); __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf); } close(s); } else { /* socket failed; maybe process ran out of fds */ format_buffer(msgbuf, sizeof(msgbuf), "Unable to open connection to debuggerd: %s", strerror(errno)); __libc_android_log_write(ANDROID_LOG_FATAL, "libc", msgbuf); } /* remove our net so we fault for real when we return */ signal(n, SIG_DFL); /* * These signals are not re-thrown when we resume. This means that * crashing due to (say) SIGPIPE doesn't work the way you'd expect it * to. We work around this by throwing them manually. We don't want * to do this for *all* signals because it'll screw up the address for * faults like SIGSEGV. */ switch (n) { case SIGABRT: case SIGFPE: case SIGPIPE: #ifdef SIGSTKFLT case SIGSTKFLT: #endif (void) tgkill(getpid(), gettid(), n); break; default: // SIGILL, SIGBUS, SIGSEGV break; } }
/* * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ void debuggerd_signal_handler(int n, siginfo_t* info, void*) { /* * It's possible somebody cleared the SA_SIGINFO flag, which would mean * our "info" arg holds an undefined value. */ if (!have_siginfo(n)) { info = NULL; } log_signal_summary(n, info); pid_t tid = gettid(); int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); if (s >= 0) { // debuggerd knows our pid from the credentials on the // local socket but we need to tell it the tid of the crashing thread. // debuggerd will be paranoid and verify that we sent a tid // that's actually in our process. debugger_msg_t msg; msg.action = DEBUGGER_ACTION_CRASH; msg.tid = tid; msg.abort_msg_address = reinterpret_cast<uintptr_t>(gAbortMessage); int ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))); if (ret == sizeof(msg)) { #ifdef HAVE_AEE_FEATURE int tmppid = getpid(); __libc_format_log(ANDROID_LOG_FATAL, "libc", "Send stop signal to pid:%d in %s", tmppid, __func__); kill(tmppid, SIGSTOP); #endif // if the write failed, there is no point trying to read a response. ret = TEMP_FAILURE_RETRY(read(s, &tid, 1)); int saved_errno = errno; notify_gdb_of_libraries(); errno = saved_errno; } if (ret < 0) { /* read or write failed -- broken connection? */ __libc_format_log(ANDROID_LOG_FATAL, "libc", "Failed while talking to debuggerd: %s", strerror(errno)); } close(s); } else { /* socket failed; maybe process ran out of fds */ __libc_format_log(ANDROID_LOG_FATAL, "libc", "Unable to open connection to debuggerd: %s", strerror(errno)); } /* remove our net so we fault for real when we return */ signal(n, SIG_DFL); /* * These signals are not re-thrown when we resume. This means that * crashing due to (say) SIGPIPE doesn't work the way you'd expect it * to. We work around this by throwing them manually. We don't want * to do this for *all* signals because it'll screw up the address for * faults like SIGSEGV. */ switch (n) { case SIGABRT: case SIGFPE: case SIGPIPE: #ifdef SIGSTKFLT case SIGSTKFLT: #endif (void) tgkill(getpid(), gettid(), n); break; default: // SIGILL, SIGBUS, SIGSEGV break; } }
/* Kill the thread of the tid in current process */ int tkill(int tid, int sig) { return tgkill(getpid(), tid, sig); }
static void handler (int signo) /* step-0 */ { /* step-0 */ var++; /* step-1 */ tgkill (getpid (), gettid (), SIGUSR1); /* step-2 */ }
int main (int argc, char **argv) { int i; int standalone = 0; struct sigaction act; if (argc == 2 && strcmp (argv[1], "-s") == 0) standalone = 1; else assert (argc == 1); setbuf (stdout, NULL); timed_mutex_lock (&thread1_tid_mutex); timed_mutex_lock (&thread2_tid_mutex); timed_mutex_lock (&terminate_mutex); errno = 0; memset (&act, 0, sizeof (act)); act.sa_sigaction = handler; act.sa_flags = SA_RESTART | SA_SIGINFO; i = sigemptyset (&act.sa_mask); assert_perror (errno); assert (i == 0); i = sigaction (SIGUSR1, &act, NULL); assert_perror (errno); assert (i == 0); i = sigaction (SIGUSR2, &act, NULL); assert_perror (errno); assert (i == 0); pthread_barrier_init (&threads_started_barrier, NULL, 3); i = pthread_create (&thread1, NULL, thread1_func, NULL); assert (i == 0); i = pthread_create (&thread2, NULL, thread2_func, NULL); assert (i == 0); if (!standalone) { tracer = proc_ulong ("/proc/self/status", "TracerPid:\t"); if (tracer == 0) { fprintf (stderr, "The testcase must be run by GDB!\n"); exit (EXIT_FAILURE); } if (tracer != getppid ()) { fprintf (stderr, "The testcase parent must be our GDB tracer!\n"); exit (EXIT_FAILURE); } } /* SIGCONT our debugger in the case of our crash as we would deadlock otherwise. */ atexit (cleanup); /* Wait until all threads are seen running. On Linux (at least), new threads start stopped, and the debugger must resume them. Need to wait for that before stopping GDB. */ pthread_barrier_wait (&threads_started_barrier); printf ("Stopping GDB PID %lu.\n", (unsigned long) tracer); if (tracer) { i = kill (tracer, SIGSTOP); assert (i == 0); state_wait (tracer, "T (stopped)"); } /* Threads are now waiting at timed_mutex_lock (thread1_tid_mutex) and so they could not trigger the signals before GDB is unstopped later. Threads get resumed by the pthread_cond_wait below. Use `while' loops for protection against spurious pthread_cond_wait wakeups. */ printf ("Waiting till the threads initialize their TIDs.\n"); while (thread1_tid == 0) { i = pthread_cond_wait (&thread1_tid_cond, &thread1_tid_mutex); assert (i == 0); } while (thread2_tid == 0) { i = pthread_cond_wait (&thread2_tid_cond, &thread2_tid_mutex); assert (i == 0); } printf ("Thread 1 TID = %lu, thread 2 TID = %lu, PID = %lu.\n", (unsigned long) thread1_tid, (unsigned long) thread2_tid, (unsigned long) getpid ()); errno = 0; i = tgkill (getpid (), thread1_tid, SIGUSR1); assert_perror (errno); assert (i == 0); i = tgkill (getpid (), thread1_tid, SIGUSR2); assert_perror (errno); assert (i == 0); i = tgkill (getpid (), thread2_tid, SIGUSR1); assert_perror (errno); assert (i == 0); i = tgkill (getpid (), thread2_tid, SIGUSR2); assert_perror (errno); assert (i == 0); printf ("Waiting till the threads are trapped by the signals.\n"); if (tracer) { /* s390x-unknown-linux-gnu will fail with "R (running)". */ state_wait (thread1_tid, "t (tracing stop)"); state_wait (thread2_tid, "t (tracing stop)"); } cleanup (); printf ("Joining the threads.\n"); i = pthread_mutex_unlock (&terminate_mutex); assert (i == 0); i = pthread_join (thread1, NULL); assert (i == 0); i = pthread_join (thread2, NULL); assert (i == 0); printf ("Exiting.\n"); /* break-at-exit */ return EXIT_SUCCESS; }
static void stage_capability_test(void) { char tmp1[128]; char tmp2[128]; memset(tmp1, 0, sizeof(tmp1)); memset(tmp2, 0, sizeof(tmp2)); capability = "inet_tcp_create"; set_capability(); if (write_policy()) { int fd = socket(AF_INET, SOCK_STREAM, 0); show_result(fd, 1); if (fd != EOF) close(fd); delete_policy(); fd = socket(AF_INET, SOCK_STREAM, 0); show_result(fd, 0); if (fd != EOF) close(fd); } unset_capability(); { int fd1 = socket(AF_INET, SOCK_STREAM, 0); int fd2 = socket(AF_INET, SOCK_STREAM, 0); int fd3 = socket(AF_INET, SOCK_STREAM, 0); int fd4 = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; socklen_t size = sizeof(addr); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(0); bind(fd1, (struct sockaddr *) &addr, sizeof(addr)); bind(fd2, (struct sockaddr *) &addr, sizeof(addr)); bind(fd3, (struct sockaddr *) &addr, sizeof(addr)); bind(fd4, (struct sockaddr *) &addr, sizeof(addr)); getsockname(fd1, (struct sockaddr *) &addr, &size); capability = "inet_tcp_listen"; set_capability(); if (write_policy()) { show_result(listen(fd1, 5), 1); delete_policy(); show_result(listen(fd2, 5), 0); } unset_capability(); capability = "inet_tcp_connect"; set_capability(); if (write_policy()) { show_result(connect(fd3, (struct sockaddr *) &addr, sizeof(addr)), 1); delete_policy(); show_result(connect(fd4, (struct sockaddr *) &addr, sizeof(addr)), 0); } unset_capability(); if (fd1 != EOF) close(fd1); if (fd2 != EOF) close(fd2); if (fd3 != EOF) close(fd3); if (fd4 != EOF) close(fd4); } capability = "use_inet_udp"; set_capability(); if (write_policy()) { int fd = socket(AF_INET, SOCK_DGRAM, 0); show_result(fd, 1); if (fd != EOF) close(fd); delete_policy(); fd = socket(AF_INET, SOCK_DGRAM, 0); show_result(fd, 0); if (fd != EOF) close(fd); } unset_capability(); capability = "use_inet_ip"; set_capability(); if (write_policy()) { int fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); show_result(fd, 1); if (fd != EOF) close(fd); delete_policy(); fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); show_result(fd, 0); if (fd != EOF) close(fd); } unset_capability(); capability = "use_route"; set_capability(); if (write_policy()) { int fd = socket(AF_ROUTE, SOCK_RAW, 0); show_result(fd, 1); if (fd != EOF) close(fd); delete_policy(); fd = socket(AF_ROUTE, SOCK_RAW, 0); show_result(fd, 0); if (fd != EOF) close(fd); } unset_capability(); capability = "use_packet"; set_capability(); if (write_policy()) { int fd = socket(AF_PACKET, SOCK_RAW, 0); show_result(fd, 1); if (fd != EOF) close(fd); delete_policy(); fd = socket(AF_PACKET, SOCK_RAW, 0); show_result(fd, 0); if (fd != EOF) close(fd); } unset_capability(); capability = "use_kernel_module"; set_capability(); if (write_policy()) { if (!is_kernel26) show_result((int) create_module("", 0), 1); show_result(init_module("", NULL), 1); show_result(delete_module(""), 1); delete_policy(); if (!is_kernel26) show_result((int) create_module("", 0), 0); show_result(init_module("", NULL), 0); show_result(delete_module(""), 0); } unset_capability(); capability = "create_fifo"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/XXXXXX"); close(mkstemp(tmp1)); unlink(tmp1); show_result(mknod(tmp1, S_IFIFO, 0), 1); unlink(tmp1); delete_policy(); show_result(mknod(tmp1, S_IFIFO, 0), 0); unlink(tmp1); } unset_capability(); capability = "create_block_dev"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/XXXXXX"); close(mkstemp(tmp1)); unlink(tmp1); show_result(mknod(tmp1, S_IFBLK, MKDEV(1, 0)), 1); unlink(tmp1); delete_policy(); show_result(mknod(tmp1, S_IFBLK, MKDEV(1, 0)), 0); unlink(tmp1); } unset_capability(); capability = "create_char_dev"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/XXXXXX"); close(mkstemp(tmp1)); unlink(tmp1); show_result(mknod(tmp1, S_IFCHR, MKDEV(1, 3)), 1); unlink(tmp1); delete_policy(); show_result(mknod(tmp1, S_IFCHR, MKDEV(1, 3)), 0); unlink(tmp1); } unset_capability(); capability = "create_unix_socket"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/XXXXXX"); close(mkstemp(tmp1)); unlink(tmp1); show_result(mknod(tmp1, S_IFSOCK, 0), 1); unlink(tmp1); delete_policy(); show_result(mknod(tmp1, S_IFSOCK, 0), 0); unlink(tmp1); } if (write_policy()) { struct sockaddr_un addr; int fd1 = socket(AF_UNIX, SOCK_STREAM, 0); int fd2 = socket(AF_UNIX, SOCK_STREAM, 0); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strcpy(tmp1, "/tmp/XXXXXX"); strncpy(addr.sun_path, tmp1, sizeof(addr.sun_path) - 1); show_result(bind(fd1, (struct sockaddr *) &addr, sizeof(addr)), 1); unlink(tmp1); delete_policy(); show_result(bind(fd2, (struct sockaddr *) &addr, sizeof(addr)), 0); unlink(tmp1); if (fd1 != EOF) close(fd1); if (fd2 != EOF) close(fd2); } unset_capability(); capability = "SYS_MOUNT"; set_capability(); if (write_policy()) { show_result(mount("/", "/", "tmpfs", 0, NULL), 1); delete_policy(); show_result(mount("/", "/", "tmpfs", 0, NULL), 0); } unset_capability(); capability = "SYS_UMOUNT"; set_capability(); if (write_policy()) { mount("/tmp", "/tmp", "tmpfs", 0, NULL); show_result(umount("/tmp"), 1); delete_policy(); mount("/tmp", "/tmp", "tmpfs", 0, NULL); show_result(umount("/"), 0); } unset_capability(); capability = "SYS_REBOOT"; set_capability(); if (write_policy()) { FILE *fp = fopen("/proc/sys/kernel/ctrl-alt-del", "a+"); unsigned int c; if (fp && fscanf(fp, "%u", &c) == 1) { show_result(reboot(LINUX_REBOOT_CMD_CAD_ON), 1); delete_policy(); show_result(reboot(LINUX_REBOOT_CMD_CAD_ON), 0); fprintf(fp, "%u\n", c); } else { /* Use invalid value */ show_result(reboot(0x0000C0DE), 1); delete_policy(); show_result(reboot(0x0000C0DE), 0); } if (fp) fclose(fp); } unset_capability(); capability = "SYS_CHROOT"; set_capability(); if (write_policy()) { show_result(chroot("/"), 1); delete_policy(); show_result(chroot("/"), 0); } unset_capability(); capability = "SYS_PIVOT_ROOT"; set_capability(); if (write_policy()) { int error; char *stack = malloc(8192); pid_t pid = clone(child, stack + (8192 / 2), CLONE_NEWNS, NULL); while (waitpid(pid, &error, __WALL) == EOF && errno == EINTR) error += 0; /* Dummy. */ errno = WIFEXITED(error) ? WEXITSTATUS(error) : -1; show_result(errno ? EOF : 0, 1); delete_policy(); pid = clone(child, stack + (8192 / 2), CLONE_NEWNS, NULL); while (waitpid(pid, &error, __WALL) == EOF && errno == EINTR) error += 0; /* Dummy. */ errno = WIFEXITED(error) ? WEXITSTATUS(error) : -1; show_result(errno ? EOF : 0, 0); free(stack); } unset_capability(); signal(SIGINT, SIG_IGN); capability = "SYS_KILL"; set_capability(); if (write_policy()) { show_result(kill(pid, SIGINT), 1); show_result(tkill(gettid(), SIGINT), 1); #ifdef __NR_tgkill if (is_kernel26) show_result(tgkill(pid, gettid(), SIGINT), 1); #endif delete_policy(); show_result(kill(pid, SIGINT), 0); show_result(tkill(gettid(), SIGINT), 0); #ifdef __NR_tgkill if (is_kernel26) show_result(tgkill(pid, gettid(), SIGINT), 0); #endif } unset_capability(); signal(SIGINT, SIG_DFL); capability = "SYS_KEXEC_LOAD"; set_capability(); if (write_policy()) { #ifdef __NR_sys_kexec_load if (is_kernel26) show_result(sys_kexec_load(0, 0, NULL, 0), 1); #endif delete_policy(); #ifdef __NR_sys_kexec_load if (is_kernel26) show_result(sys_kexec_load(0, 0, NULL, 0), 0); #endif } unset_capability(); capability = "SYS_VHANGUP"; set_capability(); if (write_policy()) { int pty_fd = EOF, status = 0; int pipe_fd[2] = { EOF, EOF }; pipe(pipe_fd); switch (forkpty(&pty_fd, NULL, NULL, NULL)) { case 0: errno = 0; vhangup(); /* Unreachable if vhangup() succeeded. */ status = errno; write(pipe_fd[1], &status, sizeof(status)); _exit(0); case -1: fprintf(stderr, "forkpty() failed.\n"); break; default: close(pipe_fd[1]); read(pipe_fd[0], &status, sizeof(status)); wait(NULL); close(pipe_fd[0]); close(pty_fd); errno = status; show_result(status ? EOF : 0, 1); } delete_policy(); status = 0; pipe(pipe_fd); switch (forkpty(&pty_fd, NULL, NULL, NULL)) { case 0: errno = 0; vhangup(); /* Unreachable if vhangup() succeeded. */ status = errno; write(pipe_fd[1], &status, sizeof(status)); _exit(0); case -1: fprintf(stderr, "forkpty() failed.\n"); break; default: close(pipe_fd[1]); read(pipe_fd[0], &status, sizeof(status)); wait(NULL); close(pipe_fd[0]); close(pty_fd); errno = status; show_result(status ? EOF : 0, 0); } } unset_capability(); capability = "SYS_TIME"; set_capability(); if (write_policy()) { struct timeval tv; struct timezone tz; struct timex buf; time_t now = time(NULL); show_result(stime(&now), 1); gettimeofday(&tv, &tz); show_result(settimeofday(&tv, &tz), 1); memset(&buf, 0, sizeof(buf)); buf.modes = 0x100; /* Use invalid value so that the clock won't change. */ show_result(adjtimex(&buf), 1); delete_policy(); now = time(NULL); show_result(stime(&now), 0); gettimeofday(&tv, &tz); show_result(settimeofday(&tv, &tz), 0); memset(&buf, 0, sizeof(buf)); buf.modes = 0x100; /* Use invalid value so that the clock won't change. */ show_result(adjtimex(&buf), 0); } unset_capability(); capability = "SYS_NICE"; set_capability(); if (write_policy()) { show_result(nice(0), 1); show_result(setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid)), 1); delete_policy(); show_result(nice(0), 0); show_result(setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid)), 0); } unset_capability(); capability = "SYS_SETHOSTNAME"; set_capability(); if (write_policy()) { char buffer[4096]; memset(buffer, 0, sizeof(buffer)); gethostname(buffer, sizeof(buffer) - 1); show_result(sethostname(buffer, strlen(buffer)), 1); getdomainname(buffer, sizeof(buffer) - 1); show_result(setdomainname(buffer, strlen(buffer)), 1); delete_policy(); gethostname(buffer, sizeof(buffer) - 1); show_result(sethostname(buffer, strlen(buffer)), 0); getdomainname(buffer, sizeof(buffer) - 1); show_result(setdomainname(buffer, strlen(buffer)), 0); } unset_capability(); capability = "SYS_LINK"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/link_source_XXXXXX"); close(mkstemp(tmp1)); strcpy(tmp2, "/tmp/link_target_XXXXXX"); show_result(link(tmp1, tmp2), 1); unlink(tmp2); unlink(tmp1); delete_policy(); strcpy(tmp1, "/tmp/link_source_XXXXXX"); close(mkstemp(tmp1)); strcpy(tmp2, "/tmp/link_target_XXXXXX"); show_result(link(tmp1, tmp2), 0); unlink(tmp2); unlink(tmp1); } unset_capability(); capability = "SYS_SYMLINK"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/symlink_target_XXXXXX"); close(mkstemp(tmp1)); strcpy(tmp2, "/tmp/symlink_source_XXXXXX"); show_result(symlink(tmp1, tmp2), 1); unlink(tmp2); unlink(tmp1); delete_policy(); strcpy(tmp1, "/tmp/symlink_target_XXXXXX"); close(mkstemp(tmp1)); strcpy(tmp2, "/tmp/symlink_source_XXXXXX"); show_result(symlink(tmp1, tmp2), 0); unlink(tmp2); unlink(tmp1); } unset_capability(); capability = "SYS_RENAME"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/rename_old_XXXXXX"); close(mkstemp(tmp1)); strcpy(tmp2, "/tmp/rename_new_XXXXXX"); show_result(rename(tmp1, tmp2), 1); unlink(tmp2); unlink(tmp1); delete_policy(); strcpy(tmp1, "/tmp/rename_old_XXXXXX"); close(mkstemp(tmp1)); strcpy(tmp2, "/tmp/rename_new_XXXXXX"); show_result(rename(tmp1, tmp2), 0); unlink(tmp2); unlink(tmp1); } unset_capability(); capability = "SYS_UNLINK"; set_capability(); if (write_policy()) { strcpy(tmp1, "/tmp/unlinkXXXXXX"); close(mkstemp(tmp1)); show_result(unlink(tmp1), 1); delete_policy(); strcpy(tmp1, "/tmp/unlinkXXXXXX"); close(mkstemp(tmp1)); show_result(unlink(tmp1), 0); } unset_capability(); unlink(tmp1); capability = "SYS_CHMOD"; set_capability(); if (write_policy()) { show_result(chmod("/dev/null", 0222), 1); delete_policy(); show_result(chmod("/dev/null", 0444), 0); } unset_capability(); chmod("/dev/null", 0666); capability = "SYS_CHOWN"; set_capability(); if (write_policy()) { show_result(chown("/dev/null", 1, 1), 1); delete_policy(); show_result(chown("/dev/null", 2, 2), 0); } unset_capability(); chown("/dev/null", 0, 0); capability = "SYS_IOCTL"; set_capability(); if (0 && write_policy()) { int fd = open("/dev/null", O_RDONLY); show_result(ioctl(fd, 0 /* Use invalid value so that nothing happen. */), 1); delete_policy(); show_result(ioctl(fd, 0 /* Use invalid value so that nothing happen. */), 0); close(fd); } if (write_policy()) { struct ifreq ifreq; int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); memset(&ifreq, 0, sizeof(ifreq)); snprintf(ifreq.ifr_name, sizeof(ifreq.ifr_name) - 1, "lo"); show_result(ioctl(fd, 35123, &ifreq), 1); delete_policy(); show_result(ioctl(fd, 35123, &ifreq), 0); close(fd); } unset_capability(); capability = "SYS_PTRACE"; set_capability(); if (write_policy()) { int status = 0; int pipe_fd[2] = { EOF, EOF }; pipe(pipe_fd); switch (fork()) { case 0: errno = 0; ptrace(PTRACE_TRACEME, 0, NULL, NULL); status = errno; write(pipe_fd[1], &status, sizeof(status)); _exit(0); case -1: fprintf(stderr, "fork() failed.\n"); break; default: close(pipe_fd[1]); read(pipe_fd[0], &status, sizeof(status)); wait(NULL); close(pipe_fd[0]); errno = status; show_result(status ? EOF : 0, 1); } delete_policy(); status = 0; pipe(pipe_fd); switch (fork()) { case 0: errno = 0; ptrace(PTRACE_TRACEME, 0, NULL, NULL); status = errno; write(pipe_fd[1], &status, sizeof(status)); _exit(0); case -1: fprintf(stderr, "fork() failed.\n"); break; default: close(pipe_fd[1]); read(pipe_fd[0], &status, sizeof(status)); wait(NULL); close(pipe_fd[0]); errno = status; show_result(status ? EOF : 0, 0); } } unset_capability(); }
ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth) { if (tid == gettid()) { return unwind_backtrace(backtrace, ignore_depth + 1, max_depth); } ALOGV("Unwinding thread %d from thread %d.", tid, gettid()); // TODO: there's no tgkill(2) on Mac OS, so we'd either need the // mach_port_t or the pthread_t rather than the tid. #if defined(CORKSCREW_HAVE_ARCH) && !defined(__APPLE__) struct sigaction act; struct sigaction oact; memset(&act, 0, sizeof(act)); act.sa_sigaction = unwind_backtrace_thread_signal_handler; act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; sigemptyset(&act.sa_mask); pthread_mutex_lock(&g_unwind_signal_mutex); map_info_t* milist = acquire_my_map_info_list(); ssize_t frames = -1; if (!sigaction(SIGURG, &act, &oact)) { g_unwind_signal_state.map_info_list = milist; g_unwind_signal_state.backtrace = backtrace; g_unwind_signal_state.ignore_depth = ignore_depth; g_unwind_signal_state.max_depth = max_depth; g_unwind_signal_state.returned_frames = 0; android_atomic_release_store(tid, &g_unwind_signal_state.tid_state); // Signal the specific thread that we want to dump. int32_t tid_state = tid; if (tgkill(getpid(), tid, SIGURG)) { ALOGV("Failed to send SIGURG to thread %d.", tid); } else { // Wait for the other thread to start dumping the stack, or time out. int wait_millis = 250; for (;;) { tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); if (tid_state != tid) { break; } if (wait_millis--) { ALOGV("Waiting for thread %d to start dumping the stack...", tid); usleep(1000); } else { ALOGV("Timed out waiting for thread %d to start dumping the stack.", tid); break; } } } // Try to cancel the dump if it has not started yet. if (tid_state == tid) { if (!android_atomic_acquire_cas(tid, STATE_CANCEL, &g_unwind_signal_state.tid_state)) { ALOGV("Canceled thread %d stack dump.", tid); tid_state = STATE_CANCEL; } else { tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); } } // Wait indefinitely for the dump to finish or be canceled. // We cannot apply a timeout here because the other thread is accessing state that // is owned by this thread, such as milist. It should not take very // long to take the dump once started. while (tid_state == STATE_DUMPING) { ALOGV("Waiting for thread %d to finish dumping the stack...", tid); usleep(1000); tid_state = android_atomic_acquire_load(&g_unwind_signal_state.tid_state); } if (tid_state == STATE_DONE) { frames = g_unwind_signal_state.returned_frames; } sigaction(SIGURG, &oact, NULL); } release_my_map_info_list(milist); pthread_mutex_unlock(&g_unwind_signal_mutex); return frames; #else return -1; #endif }
/* Comments by Gene: * Here, syscall is the wrapper, and the call to syscall would be _real_syscall * We would add a special case for SYS_gettid, while all others default as below * It depends on the idea that arguments are stored in registers, whose * natural size is: sizeof(void*) * So, we pass six arguments to syscall, and it will ignore any extra arguments * I believe that all Linux system calls have no more than 7 args. * clone() is an example of one with 7 arguments. * If we discover system calls for which the 7 args strategy doesn't work, * we can special case them. * * XXX: DO NOT USE JTRACE/JNOTE/JASSERT in this function; even better, do not * use any STL here. (--Kapil) */ extern "C" long int syscall(long int sys_num, ... ) { long int ret; va_list ap; va_start(ap, sys_num); switch ( sys_num ) { case SYS_gettid: { ret = gettid(); break; } case SYS_tkill: { SYSCALL_GET_ARGS_2(int, tid, int, sig); ret = tkill(tid, sig); break; } case SYS_tgkill: { SYSCALL_GET_ARGS_3(int, tgid, int, tid, int, sig); ret = tgkill(tgid, tid, sig); break; } case SYS_getpid: { ret = getpid(); break; } case SYS_getppid: { ret = getppid(); break; } case SYS_getpgrp: { ret = getpgrp(); break; } case SYS_getpgid: { SYSCALL_GET_ARG(pid_t,pid); ret = getpgid(pid); break; } case SYS_setpgid: { SYSCALL_GET_ARGS_2(pid_t,pid,pid_t,pgid); ret = setpgid(pid, pgid); break; } case SYS_getsid: { SYSCALL_GET_ARG(pid_t,pid); ret = getsid(pid); break; } case SYS_setsid: { ret = setsid(); break; } case SYS_kill: { SYSCALL_GET_ARGS_2(pid_t,pid,int,sig); ret = kill(pid, sig); break; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9)) case SYS_waitid: { //SYSCALL_GET_ARGS_4(idtype_t,idtype,id_t,id,siginfo_t*,infop,int,options); SYSCALL_GET_ARGS_4(int,idtype,id_t,id,siginfo_t*,infop,int,options); ret = waitid((idtype_t)idtype, id, infop, options); break; } #endif case SYS_wait4: { SYSCALL_GET_ARGS_4(pid_t,pid,__WAIT_STATUS,status,int,options, struct rusage*,rusage); ret = wait4(pid, status, options, rusage); break; } #ifdef __i386__ case SYS_waitpid: { SYSCALL_GET_ARGS_3(pid_t,pid,int*,status,int,options); ret = waitpid(pid, status, options); break; } #endif case SYS_setgid: { SYSCALL_GET_ARG(gid_t,gid); ret = setgid(gid); break; } case SYS_setuid: { SYSCALL_GET_ARG(uid_t,uid); ret = setuid(uid); break; } #ifndef DISABLE_SYS_V_IPC # ifdef __x86_64__ // These SYS_xxx are only defined for 64-bit Linux case SYS_shmget: { SYSCALL_GET_ARGS_3(key_t,key,size_t,size,int,shmflg); ret = shmget(key, size, shmflg); break; } case SYS_shmat: { SYSCALL_GET_ARGS_3(int,shmid,const void*,shmaddr,int,shmflg); ret = (unsigned long) shmat(shmid, shmaddr, shmflg); break; } case SYS_shmdt: { SYSCALL_GET_ARG(const void*,shmaddr); ret = shmdt(shmaddr); break; } case SYS_shmctl: { SYSCALL_GET_ARGS_3(int,shmid,int,cmd,struct shmid_ds*,buf); ret = shmctl(shmid, cmd, buf); break; } # endif #endif default: { SYSCALL_GET_ARGS_7(void*, arg1, void*, arg2, void*, arg3, void*, arg4, void*, arg5, void*, arg6, void*, arg7); ret = _real_syscall(sys_num, arg1, arg2, arg3, arg4, arg5, arg6, arg7); break; } } va_end(ap); return ret; }