ustfork: Initialize libc pointers in constructor
[lttng-ust.git] / src / lib / lttng-ust-fork / ustfork.c
1 /*
2 * SPDX-License-Identifier: LGPL-2.1-only
3 *
4 * Copyright (C) 2009 Pierre-Marc Fournier
5 * Copyright (C) 2011-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 */
7
8 /* Has to be included first to override dlfcn.h */
9 #include <common/compat/dlfcn.h>
10
11 #include <unistd.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <signal.h>
15 #include <sched.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <errno.h>
19
20 #include <pthread.h>
21
22 #include <lttng/ust-fork.h>
23
24 #include <urcu/uatomic.h>
25
26 #include "common/macros.h"
27
28 struct libc_pointer {
29 void **procedure;
30 const char *symbol;
31 };
32
33 #define DEFINE_LIBC_POINTER(name) { (void**)&plibc_## name, #name }
34
35 #ifdef __linux__
36
37 struct user_desc;
38
39 static int (*plibc_clone)(int (*fn)(void *), void *child_stack,
40 int flags, void *arg, pid_t *ptid,
41 struct user_desc *tls, pid_t *ctid) = NULL;
42
43 static int (*plibc_setns)(int fd, int nstype) = NULL;
44
45 static int (*plibc_setresgid)(gid_t rgid, gid_t egid, gid_t sgid) = NULL;
46
47 static int (*plibc_setresuid)(uid_t ruid, uid_t euid, uid_t suid) = NULL;
48
49 static int (*plibc_unshare)(int flags) = NULL;
50
51 #elif defined (__FreeBSD__)
52
53 static pid_t (*plibc_rfork)(int flags) = NULL;
54
55 #endif
56
57 static int (*plibc_daemon)(int nochdir, int noclose) = NULL;
58
59 static pid_t (*plibc_fork)(void) = NULL;
60
61 static int (*plibc_setegid)(gid_t egid) = NULL;
62
63 static int (*plibc_seteuid)(uid_t euid) = NULL;
64
65 static int (*plibc_setgid)(gid_t gid) = NULL;
66
67 static int (*plibc_setregid)(gid_t rgid, gid_t egid) = NULL;
68
69 static int (*plibc_setreuid)(uid_t ruid, uid_t euid) = NULL;
70
71 static int (*plibc_setuid)(uid_t uid) = NULL;
72
73 static void lttng_ust_fork_wrapper_ctor(void)
74 __attribute__((constructor));
75
76 static pthread_mutex_t initialization_guard = PTHREAD_MUTEX_INITIALIZER;
77 static bool was_initialized = false;
78
79 /*
80 * Must be called with initialization_guard held.
81 */
82 static void initialize(void)
83 {
84 const struct libc_pointer libc_pointers[] = {
85 #ifdef __linux__
86 DEFINE_LIBC_POINTER(clone),
87 DEFINE_LIBC_POINTER(setns),
88 DEFINE_LIBC_POINTER(setresgid),
89 DEFINE_LIBC_POINTER(setresuid),
90 DEFINE_LIBC_POINTER(unshare),
91 #elif defined (__FreeBSD__)
92 DEFINE_LIBC_POINTER(rfork),
93 #endif
94 DEFINE_LIBC_POINTER(daemon),
95 DEFINE_LIBC_POINTER(fork),
96 DEFINE_LIBC_POINTER(setegid),
97 DEFINE_LIBC_POINTER(seteuid),
98 DEFINE_LIBC_POINTER(setgid),
99 DEFINE_LIBC_POINTER(setregid),
100 DEFINE_LIBC_POINTER(setreuid),
101 DEFINE_LIBC_POINTER(setuid),
102 };
103
104 size_t k;
105
106 for (k = 0; k < LTTNG_ARRAY_SIZE(libc_pointers); ++k) {
107 void *procedure = dlsym(RTLD_NEXT, libc_pointers[k].symbol);
108
109 if (NULL == procedure) {
110 fprintf(stderr,
111 "libustfork: unable to find \"%s\" symbol\n",
112 libc_pointers[k].symbol);
113 continue;
114 }
115
116 uatomic_set(libc_pointers[k].procedure, procedure);
117 }
118 }
119
120 /*
121 * Lazy initialization is required because it is possible for a shared library
122 * to have a constructor that is executed before our constructor, which could
123 * call some libc functions that we are wrapping.
124 *
125 * It is also possible for this library constructor to create a thread using the
126 * raw system call. Therefore, the lazy initialization must be multi-thread safe.
127 */
128 static void *lazy_initialize(void **pfunc)
129 {
130 void *func = uatomic_read(pfunc);
131
132 /*
133 * If *pfunc != NULL, then it is assumed that some thread has already
134 * called the initialization routine.
135 */
136 if (caa_likely(func)) {
137 goto out;
138 }
139
140 pthread_mutex_lock(&initialization_guard);
141 if (!was_initialized) {
142 initialize();
143 was_initialized = true;
144 }
145 func = *pfunc;
146 pthread_mutex_unlock(&initialization_guard);
147 out:
148 return func;
149 }
150
151 #define LAZY_INITIALIZE_OR_NOSYS(ptr) \
152 ({ \
153 void *ret; \
154 \
155 ret = lazy_initialize((void**)&(ptr)); \
156 if (NULL == ret) { \
157 errno = ENOSYS; \
158 return -1; \
159 } \
160 \
161 ret; \
162 })
163
164 static void lttng_ust_fork_wrapper_ctor(void)
165 {
166 /*
167 * Using fork here because it is defined on all supported OS.
168 */
169 (void) lazy_initialize((void**)&plibc_fork);
170 }
171
172 pid_t fork(void)
173 {
174 sigset_t sigset;
175 pid_t retval;
176 int saved_errno;
177
178 pid_t (*func)(void) = LAZY_INITIALIZE_OR_NOSYS(plibc_fork);
179
180 lttng_ust_before_fork(&sigset);
181 /* Do the real fork */
182 retval = func();
183 saved_errno = errno;
184 if (retval == 0) {
185 /* child */
186 lttng_ust_after_fork_child(&sigset);
187 } else {
188 lttng_ust_after_fork_parent(&sigset);
189 }
190 errno = saved_errno;
191 return retval;
192 }
193
194 int daemon(int nochdir, int noclose)
195 {
196 sigset_t sigset;
197 int retval;
198 int saved_errno;
199
200 int (*func)(int, int) = LAZY_INITIALIZE_OR_NOSYS(plibc_daemon);
201
202 lttng_ust_before_fork(&sigset);
203 /* Do the real daemon call */
204 retval = func(nochdir, noclose);
205 saved_errno = errno;
206 if (retval == 0) {
207 /* child, parent called _exit() directly */
208 lttng_ust_after_fork_child(&sigset);
209 } else {
210 /* on error in the parent */
211 lttng_ust_after_fork_parent(&sigset);
212 }
213 errno = saved_errno;
214 return retval;
215 }
216
217 int setuid(uid_t uid)
218 {
219 int retval;
220 int saved_errno;
221
222 int (*func)(uid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setuid);
223
224 /* Do the real setuid */
225 retval = func(uid);
226 saved_errno = errno;
227
228 lttng_ust_after_setuid();
229
230 errno = saved_errno;
231 return retval;
232 }
233
234 int setgid(gid_t gid)
235 {
236 int retval;
237 int saved_errno;
238
239 int (*func)(gid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setgid);
240
241 /* Do the real setgid */
242 retval = func(gid);
243 saved_errno = errno;
244
245 lttng_ust_after_setgid();
246
247 errno = saved_errno;
248 return retval;
249 }
250
251 int seteuid(uid_t euid)
252 {
253 int retval;
254 int saved_errno;
255
256 int (*func)(uid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_seteuid);
257
258 /* Do the real seteuid */
259 retval = func(euid);
260 saved_errno = errno;
261
262 lttng_ust_after_seteuid();
263
264 errno = saved_errno;
265 return retval;
266 }
267
268 int setegid(gid_t egid)
269 {
270 int retval;
271 int saved_errno;
272
273 int (*func)(gid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setegid);
274
275 /* Do the real setegid */
276 retval = func(egid);
277 saved_errno = errno;
278
279 lttng_ust_after_setegid();
280
281 errno = saved_errno;
282 return retval;
283 }
284
285 int setreuid(uid_t ruid, uid_t euid)
286 {
287 int retval;
288 int saved_errno;
289
290 int (*func)(uid_t, uid_t) =
291 LAZY_INITIALIZE_OR_NOSYS(plibc_setreuid);
292
293 /* Do the real setreuid */
294 retval = func(ruid, euid);
295 saved_errno = errno;
296
297 lttng_ust_after_setreuid();
298
299 errno = saved_errno;
300 return retval;
301 }
302
303 int setregid(gid_t rgid, gid_t egid)
304 {
305 int retval;
306 int saved_errno;
307
308 int (*func)(gid_t, gid_t) = LAZY_INITIALIZE_OR_NOSYS(plibc_setregid);
309
310 /* Do the real setregid */
311 retval = func(rgid, egid);
312 saved_errno = errno;
313
314 lttng_ust_after_setregid();
315
316 errno = saved_errno;
317 return retval;
318 }
319
320 #ifdef __linux__
321
322 struct ustfork_clone_info {
323 int (*fn)(void *);
324 void *arg;
325 sigset_t sigset;
326 };
327
328 static int clone_fn(void *arg)
329 {
330 struct ustfork_clone_info *info = (struct ustfork_clone_info *) arg;
331
332 /* clone is now done and we are in child */
333 lttng_ust_after_fork_child(&info->sigset);
334 return info->fn(info->arg);
335 }
336
337 int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...)
338 {
339 /* var args */
340 pid_t *ptid;
341 struct user_desc *tls;
342 pid_t *ctid;
343 /* end of var args */
344 va_list ap;
345 int retval;
346 int saved_errno;
347
348 va_start(ap, arg);
349 ptid = va_arg(ap, pid_t *);
350 tls = va_arg(ap, struct user_desc *);
351 ctid = va_arg(ap, pid_t *);
352 va_end(ap);
353
354 int (*func)(int (*)(void *), void *, int , void *, pid_t *,
355 struct user_desc *, pid_t *) =
356 LAZY_INITIALIZE_OR_NOSYS(plibc_clone);
357
358 if (flags & CLONE_VM) {
359 /*
360 * Creating a thread, no need to intervene, just pass on
361 * the arguments.
362 */
363 retval = func(fn, child_stack, flags, arg, ptid,
364 tls, ctid);
365 saved_errno = errno;
366 } else {
367 /* Creating a real process, we need to intervene. */
368 struct ustfork_clone_info info = { .fn = fn, .arg = arg };
369
370 lttng_ust_before_fork(&info.sigset);
371 retval = func(clone_fn, child_stack, flags, &info,
372 ptid, tls, ctid);
373 saved_errno = errno;
374 /* The child doesn't get here. */
375 lttng_ust_after_fork_parent(&info.sigset);
376 }
377 errno = saved_errno;
378 return retval;
379 }
380
381 int setns(int fd, int nstype)
382 {
383 int retval;
384 int saved_errno;
385
386 int (*func)(int, int) = LAZY_INITIALIZE_OR_NOSYS(plibc_setns);
387
388 /* Do the real setns */
389 retval = func(fd, nstype);
390 saved_errno = errno;
391
392 lttng_ust_after_setns();
393
394 errno = saved_errno;
395 return retval;
396 }
397
398 int unshare(int flags)
399 {
400 int retval;
401 int saved_errno;
402
403 int (*func)(int) = LAZY_INITIALIZE_OR_NOSYS(plibc_unshare);
404
405 /* Do the real setns */
406 retval = func(flags);
407 saved_errno = errno;
408
409 lttng_ust_after_unshare();
410
411 errno = saved_errno;
412 return retval;
413 }
414
415 int setresuid(uid_t ruid, uid_t euid, uid_t suid)
416 {
417 int retval;
418 int saved_errno;
419
420 int (*func)(uid_t, uid_t, uid_t) =
421 LAZY_INITIALIZE_OR_NOSYS(plibc_setresuid);
422
423 /* Do the real setresuid */
424 retval = func(ruid, euid, suid);
425 saved_errno = errno;
426
427 lttng_ust_after_setresuid();
428
429 errno = saved_errno;
430 return retval;
431 }
432
433 int setresgid(gid_t rgid, gid_t egid, gid_t sgid)
434 {
435 int retval;
436 int saved_errno;
437
438 int (*func)(gid_t, gid_t, gid_t) =
439 LAZY_INITIALIZE_OR_NOSYS(plibc_setresgid);
440
441 /* Do the real setresgid */
442 retval = func(rgid, egid, sgid);
443 saved_errno = errno;
444
445 lttng_ust_after_setresgid();
446
447 errno = saved_errno;
448 return retval;
449 }
450
451 #elif defined (__FreeBSD__)
452
453 pid_t rfork(int flags)
454 {
455 sigset_t sigset;
456 pid_t retval;
457 int saved_errno;
458
459 pid_t (*func)(int) = LAZY_INITIALIZE_OR_NOSYS(plibc_rfork);
460
461 lttng_ust_before_fork(&sigset);
462 /* Do the real rfork */
463 retval = func(flags);
464 saved_errno = errno;
465 if (retval == 0) {
466 /* child */
467 lttng_ust_after_fork_child(&sigset);
468 } else {
469 lttng_ust_after_fork_parent(&sigset);
470 }
471 errno = saved_errno;
472 return retval;
473 }
474
475 /*
476 * On BSD, no need to override vfork, because it runs in the context of
477 * the parent, with parent waiting until execve or exit is executed in
478 * the child.
479 */
480
481 #else
482 #warning "Unknown OS. You might want to ensure that fork/clone/vfork/fork handling is complete."
483 #endif
This page took 0.038397 seconds and 4 git commands to generate.