shim_exit.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /* Copyright (C) 2014 Stony Brook University
  2. This file is part of Graphene Library OS.
  3. Graphene Library OS is free software: you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public License
  5. as published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. Graphene Library OS is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  13. /*
  14. * shim_exit.c
  15. *
  16. * Implementation of system call "exit" and "exit_group".
  17. */
  18. #include <shim_internal.h>
  19. #include <shim_table.h>
  20. #include <shim_thread.h>
  21. #include <shim_fs.h>
  22. #include <shim_handle.h>
  23. #include <shim_ipc.h>
  24. #include <shim_utils.h>
  25. #include <shim_checkpoint.h>
  26. #include <pal.h>
  27. #include <pal_error.h>
  28. #include <errno.h>
  29. #include <sys/syscall.h>
  30. #include <sys/mman.h>
  31. #include <asm/prctl.h>
  32. #include <linux/futex.h>
  33. void release_robust_list (struct robust_list_head * head);
  34. int thread_exit(struct shim_thread* self, bool send_ipc, int** clear_child_tid_pal_ptr) {
  35. bool sent_exit_msg = false;
  36. /* Chia-Che: Broadcast exit message as early as possible,
  37. so other process can start early on responding. */
  38. if (self->in_vm && send_ipc) {
  39. ipc_cld_exit_send(self->ppid, self->tid, self->exit_code, self->term_signal);
  40. sent_exit_msg = true;
  41. }
  42. lock(&self->lock);
  43. if (!self->is_alive) {
  44. debug("thread %d is dead\n", self->tid);
  45. out:
  46. unlock(&self->lock);
  47. return 0;
  48. }
  49. #ifdef PROFILE
  50. self->exit_time = GET_PROFILE_INTERVAL();
  51. #endif
  52. int exit_code = self->exit_code;
  53. self->is_alive = false;
  54. if (is_internal(self))
  55. goto out;
  56. struct shim_handle_map * handle_map = self->handle_map;
  57. struct shim_handle * exec = self->exec;
  58. struct shim_thread * parent = self->parent;
  59. self->handle_map = NULL;
  60. self->exec = NULL;
  61. if (parent) {
  62. assert(parent != self);
  63. assert(parent->child_exit_event);
  64. debug("thread exits, notifying thread %d\n", parent->tid);
  65. lock(&parent->lock);
  66. LISTP_DEL_INIT(self, &parent->children, siblings);
  67. LISTP_ADD_TAIL(self, &parent->exited_children, siblings);
  68. if (!self->in_vm) {
  69. debug("deliver SIGCHLD (thread = %d, exitval = %d)\n",
  70. self->tid, exit_code);
  71. siginfo_t info;
  72. memset(&info, 0, sizeof(siginfo_t));
  73. info.si_signo = SIGCHLD;
  74. info.si_pid = self->tid;
  75. info.si_uid = self->uid;
  76. info.si_status = (exit_code & 0xff) << 8;
  77. append_signal(parent, SIGCHLD, &info, true);
  78. }
  79. unlock(&parent->lock);
  80. DkEventSet(parent->child_exit_event);
  81. } else if (!sent_exit_msg) {
  82. debug("parent not here, need to tell another process\n");
  83. ipc_cld_exit_send(self->ppid, self->tid, self->exit_code, self->term_signal);
  84. }
  85. struct robust_list_head * robust_list = (void *) self->robust_list;
  86. self->robust_list = NULL;
  87. unlock(&self->lock);
  88. if (handle_map)
  89. put_handle_map(handle_map);
  90. if (exec)
  91. put_handle(exec);
  92. if (robust_list)
  93. release_robust_list(robust_list);
  94. if (parent && self->in_vm && self->clear_child_tid) {
  95. /* ask Async Helper thread to wake up parent when this child thread finally exits;
  96. * we must alloc clear_child_tids on heap instead of this thread's stack; it is
  97. * freed in release_clear_child_id() */
  98. struct clear_child_tid_struct* clear_child_tids = malloc(sizeof(*clear_child_tids));
  99. if (clear_child_tids) {
  100. clear_child_tids->clear_child_tid = self->clear_child_tid;
  101. clear_child_tids->clear_child_tid_val_pal = 1; /* any non-zero value suffices */
  102. install_async_event(NULL, 0, &release_clear_child_id, clear_child_tids);
  103. if (clear_child_tid_pal_ptr) {
  104. /* caller wants to performs DkThreadExit() and needs to know which address
  105. * PAL must set to inform the Async Helper thread */
  106. *clear_child_tid_pal_ptr = &clear_child_tids->clear_child_tid_val_pal;
  107. }
  108. }
  109. }
  110. DkEventSet(self->exit_event);
  111. return 0;
  112. }
  113. /* note that term_signal argument may contain WCOREDUMP bit (0x80) */
  114. noreturn void thread_or_process_exit(int error_code, int term_signal) {
  115. struct shim_thread * cur_thread = get_cur_thread();
  116. cur_thread->exit_code = -error_code;
  117. cur_process.exit_code = term_signal ? term_signal : error_code;
  118. cur_thread->term_signal = term_signal;
  119. int* clear_child_tid_pal = NULL;
  120. if (cur_thread->in_vm)
  121. thread_exit(cur_thread, true, &clear_child_tid_pal);
  122. if (check_last_thread(cur_thread)) {
  123. DkThreadExit(clear_child_tid_pal);
  124. }
  125. struct shim_thread * async_thread = terminate_async_helper();
  126. if (async_thread)
  127. /* TODO: wait for the thread to exit in host.
  128. * This is tracked by the following issue.
  129. * https://github.com/oscarlab/graphene/issues/440
  130. */
  131. put_thread(async_thread); /* free resources of the thread */
  132. struct shim_thread * ipc_thread = terminate_ipc_helper();
  133. if (ipc_thread)
  134. /* TODO: wait for the thread to exit in host.
  135. * This is tracked by the following issue.
  136. * https://github.com/oscarlab/graphene/issues/440
  137. */
  138. put_thread(ipc_thread); /* free resources of the thread */
  139. shim_clean_and_exit(0);
  140. }
  141. noreturn int shim_do_exit_group (int error_code)
  142. {
  143. INC_PROFILE_OCCURENCE(syscall_use_ipc);
  144. struct shim_thread * cur_thread = get_cur_thread();
  145. assert(!is_internal(cur_thread));
  146. /* If exit_group() is invoked multiple times, only a single invocation proceeds past this
  147. * point. Kill signals are delivered asynchronously, which will eventually kick the execution
  148. * out of this loop.*/
  149. static struct atomic_int first = ATOMIC_INIT(0);
  150. if (atomic_cmpxchg(&first, 0, 1) == 1) {
  151. while (1)
  152. DkThreadYieldExecution();
  153. }
  154. if (debug_handle)
  155. sysparser_printf("---- shim_exit_group (returning %d)\n", error_code);
  156. #ifndef ALIAS_VFORK_AS_FORK
  157. if (cur_thread->dummy) {
  158. cur_thread->term_signal = 0;
  159. thread_exit(cur_thread, true, NULL);
  160. switch_dummy_thread(cur_thread);
  161. }
  162. #endif
  163. debug("now kill other threads in the process\n");
  164. do_kill_proc(cur_thread->tgid, cur_thread->tgid, SIGKILL, false);
  165. /* This loop ensures that the current thread, which issues exit_group(), wins in setting the
  166. * process's exit code. thread_or_process_exit() first sets the exit_code before updating the
  167. * thread's state to "dead". Once check_last_thread() indicates that the current thread is the
  168. * last thread, all the children will already have set thread->exit_code. Hence, this thread's
  169. * execution of thread_or_process_exit() gets to determine the final exit_code, which is the
  170. * desired outcome. */
  171. while (check_last_thread(cur_thread)) {
  172. DkThreadYieldExecution();
  173. }
  174. debug("now exit the process\n");
  175. #ifdef PROFILE
  176. if (ENTER_TIME)
  177. SAVE_PROFILE_INTERVAL_SINCE(syscall_exit_group, ENTER_TIME);
  178. #endif
  179. thread_or_process_exit(error_code, 0);
  180. }
  181. noreturn int shim_do_exit (int error_code)
  182. {
  183. INC_PROFILE_OCCURENCE(syscall_use_ipc);
  184. struct shim_thread * cur_thread = get_cur_thread();
  185. __UNUSED(cur_thread);
  186. assert(!is_internal(cur_thread));
  187. if (debug_handle)
  188. sysparser_printf("---- shim_exit (returning %d)\n", error_code);
  189. #ifndef ALIAS_VFORK_AS_FORK
  190. if (cur_thread->dummy) {
  191. cur_thread->term_signal = 0;
  192. thread_exit(cur_thread, true, NULL);
  193. switch_dummy_thread(cur_thread);
  194. }
  195. #endif
  196. #ifdef PROFILE
  197. if (ENTER_TIME)
  198. SAVE_PROFILE_INTERVAL_SINCE(syscall_exit, ENTER_TIME);
  199. #endif
  200. thread_or_process_exit(error_code, 0);
  201. }