1 /*
2 * Copyright 2014 The Native Client Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7 #include <assert.h>
8 #include <errno.h>
9
10 #include "native_client/src/nonsfi/linux/linux_pthread_private.h"
11 #include "native_client/src/nonsfi/linux/linux_syscall_defines.h"
12 #include "native_client/src/nonsfi/linux/linux_syscall_structs.h"
13 #include "native_client/src/nonsfi/linux/linux_syscall_wrappers.h"
14 #include "native_client/src/nonsfi/linux/linux_sys_private.h"
15 #include "native_client/src/public/linux_syscalls/sched.h"
16 #include "native_client/src/public/linux_syscalls/sys/syscall.h"
17 #include "native_client/src/untrusted/nacl/nacl_irt.h"
18 #include "native_client/src/untrusted/nacl/nacl_thread.h"
19 #include "native_client/src/untrusted/pthread/pthread_internal.h"
20
21 /* Convert a return value of a Linux syscall to the one of an IRT call. */
irt_return_call(uintptr_t result)22 static uint32_t irt_return_call(uintptr_t result) {
23 if (linux_is_error_result(result))
24 return -result;
25 return 0;
26 }
27
nacl_irt_thread_create_v0_2(void (* start_func)(void),void * stack,void * thread_ptr,nacl_irt_tid_t * child_tid)28 static int nacl_irt_thread_create_v0_2(void (*start_func)(void), void *stack,
29 void *thread_ptr,
30 nacl_irt_tid_t *child_tid) {
31 /*
32 * We do not use CLONE_CHILD_CLEARTID as we do not want any
33 * non-private futex signaling. Also, NaCl ABI does not require us
34 * to signal the futex on stack_flag.
35 */
36 int flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
37 CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS |
38 CLONE_PARENT_SETTID);
39 /*
40 * In order to avoid allowing clone with and without CLONE_PARENT_SETTID, if
41 * |child_tid| is NULL, we provide a valid pointer whose value will be
42 * ignored.
43 */
44 nacl_irt_tid_t ignored;
45 void *ptid = (child_tid != NULL) ? child_tid : &ignored;
46
47 /*
48 * linux_clone_wrapper expects start_func's type is "int (*)(void *)".
49 * Although |start_func| has type "void (*)(void)", the type mismatching
50 * will not cause a problem. Passing a dummy |arg| (= 0) does nothing there.
51 * Also, start_func will never return.
52 */
53 return irt_return_call(linux_clone_wrapper(
54 (uintptr_t) start_func, /* arg */ 0, flags, stack,
55 ptid, thread_ptr, /* ctid */ NULL));
56 }
57
nacl_irt_thread_create(void (* start_func)(void),void * stack,void * thread_ptr)58 static int nacl_irt_thread_create(void (*start_func)(void), void *stack,
59 void *thread_ptr) {
60 nacl_irt_tid_t child_tid;
61 return nacl_irt_thread_create_v0_2(start_func, stack, thread_ptr, &child_tid);
62 }
63
nacl_irt_thread_exit(int32_t * stack_flag)64 static void nacl_irt_thread_exit(int32_t *stack_flag) {
65 /*
66 * We fill zero to stack_flag by ourselves instead of relying
67 * on CLONE_CHILD_CLEARTID. We do everything in the following inline
68 * assembly because we need to make sure we will never touch stack.
69 *
70 * We will set the stack pointer to zero at the beginning of the
71 * assembly code just in case an async signal arrives after setting
72 * *stack_flag=0 but before calling the syscall, so that any signal
73 * handler crashes rather than running on a stack that has been
74 * reallocated to another thread.
75 */
76 #if defined(__i386__)
77 __asm__ __volatile__("mov $0, %%esp\n"
78 "movl $0, (%[stack_flag])\n"
79 "int $0x80\n"
80 "hlt\n"
81 :: [stack_flag]"r"(stack_flag), "a"(__NR_exit));
82 #elif defined(__arm__)
83 __asm__ __volatile__("mov sp, #0\n"
84 "mov r7, #0\n"
85 "str r7, [%[stack_flag]]\n"
86 "mov r7, %[sysno]\n"
87 "svc #0\n"
88 "bkpt #0\n"
89 :: [stack_flag]"r"(stack_flag), [sysno]"i"(__NR_exit)
90 : "r7");
91 #else
92 # error Unsupported architecture
93 #endif
94 }
95
nacl_irt_thread_nice(const int nice)96 static int nacl_irt_thread_nice(const int nice) {
97 return 0;
98 }
99
get_irt_tdb(void * thread_ptr)100 static struct nc_combined_tdb *get_irt_tdb(void *thread_ptr) {
101 struct nc_combined_tdb *tdb = (void *) ((uintptr_t) thread_ptr +
102 __nacl_tp_tdb_offset(sizeof(*tdb)));
103 return tdb;
104 }
105
106 /*
107 * This is the real first entry point for new threads.
108 * Based on code from src/untrusted/irt/irt_thread.c
109 */
irt_start_thread()110 static void irt_start_thread() {
111 struct nc_combined_tdb *tdb = get_irt_tdb(__nacl_read_tp());
112
113 /*
114 * Fetch the user's start routine.
115 */
116 void *(*user_start)(void *) = tdb->tdb.start_func;
117
118 /*
119 * Now do per-thread initialization for the IRT-private C library state.
120 */
121 __newlib_thread_init();
122
123 /*
124 * Finally, run the user code.
125 */
126 (*user_start)(tdb->tdb.state);
127
128 /*
129 * That should never return. Crash hard if it does.
130 */
131 __builtin_trap();
132 }
133
134 /*
135 * Creates a thread and initializes the IRT-private TLS area.
136 * Based on code from src/untrusted/irt/irt_thread.c
137 */
nacl_user_thread_create(void * (* start_func)(void *),void * stack,void * thread_ptr,nacl_irt_tid_t * child_tid)138 int nacl_user_thread_create(void *(*start_func)(void *), void *stack,
139 void *thread_ptr, nacl_irt_tid_t *child_tid) {
140 struct nc_combined_tdb *tdb;
141
142 /*
143 * Before we start the thread, allocate the IRT-private TLS area for it.
144 */
145 size_t combined_size = __nacl_tls_combined_size(sizeof(*tdb));
146 void *combined_area = malloc(combined_size);
147 if (combined_area == NULL)
148 return EAGAIN;
149
150 /*
151 * Note that __nacl_tls_initialize_memory() is not reversible,
152 * because it takes a pointer that need not be aligned and can
153 * return a pointer that is aligned. In order to
154 * free(combined_area) later, we must save the value of
155 * combined_area.
156 */
157 void *irt_tp = __nacl_tls_initialize_memory(combined_area, sizeof(*tdb));
158 tdb = get_irt_tdb(irt_tp);
159 __nc_initialize_unjoinable_thread(tdb);
160 tdb->tdb.irt_thread_data = combined_area;
161 tdb->tdb.start_func = start_func;
162 tdb->tdb.state = thread_ptr;
163
164 return nacl_irt_thread_create_v0_2(irt_start_thread, stack, irt_tp,
165 child_tid);
166 }
167
168 /*
169 * Destroys a thread created by nacl_user_thread_create.
170 * Based on code from src/untrusted/irt/irt_thread.c
171 */
nacl_user_thread_exit(int32_t * stack_flag)172 void nacl_user_thread_exit(int32_t *stack_flag) {
173 struct nc_combined_tdb *tdb = get_irt_tdb(__nacl_read_tp());
174
175 __nc_tsd_exit();
176
177 /*
178 * Sanity check: Check that this function was not called on a thread
179 * created by the IRT's internal pthread_create(). For such
180 * threads, irt_thread_data == NULL.
181 */
182 assert(tdb->tdb.irt_thread_data != NULL);
183
184 free(tdb->tdb.irt_thread_data);
185
186 nacl_irt_thread_exit(stack_flag);
187 }
188
__nc_initialize_interfaces(void)189 void __nc_initialize_interfaces(void) {
190 const struct nacl_irt_thread init = {
191 nacl_irt_thread_create,
192 nacl_irt_thread_exit,
193 nacl_irt_thread_nice,
194 };
195 __libnacl_irt_thread = init;
196 }
197