ustfork: Initialize libc pointers in constructor
[lttng-ust.git] / src / lib / lttng-ust-fork / ustfork.c
... / ...
CommitLineData
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
28struct 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
37struct user_desc;
38
39static 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
43static int (*plibc_setns)(int fd, int nstype) = NULL;
44
45static int (*plibc_setresgid)(gid_t rgid, gid_t egid, gid_t sgid) = NULL;
46
47static int (*plibc_setresuid)(uid_t ruid, uid_t euid, uid_t suid) = NULL;
48
49static int (*plibc_unshare)(int flags) = NULL;
50
51#elif defined (__FreeBSD__)
52
53static pid_t (*plibc_rfork)(int flags) = NULL;
54
55#endif
56
57static int (*plibc_daemon)(int nochdir, int noclose) = NULL;
58
59static pid_t (*plibc_fork)(void) = NULL;
60
61static int (*plibc_setegid)(gid_t egid) = NULL;
62
63static int (*plibc_seteuid)(uid_t euid) = NULL;
64
65static int (*plibc_setgid)(gid_t gid) = NULL;
66
67static int (*plibc_setregid)(gid_t rgid, gid_t egid) = NULL;
68
69static int (*plibc_setreuid)(uid_t ruid, uid_t euid) = NULL;
70
71static int (*plibc_setuid)(uid_t uid) = NULL;
72
73static void lttng_ust_fork_wrapper_ctor(void)
74 __attribute__((constructor));
75
76static pthread_mutex_t initialization_guard = PTHREAD_MUTEX_INITIALIZER;
77static bool was_initialized = false;
78
79/*
80 * Must be called with initialization_guard held.
81 */
82static 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 */
128static 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);
147out:
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
164static 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
172pid_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
194int 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
217int 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
234int 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
251int 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
268int 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
285int 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
303int 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
322struct ustfork_clone_info {
323 int (*fn)(void *);
324 void *arg;
325 sigset_t sigset;
326};
327
328static 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
337int 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
381int 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
398int 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
415int 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
433int 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
453pid_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.023954 seconds and 4 git commands to generate.