The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
dl.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program 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 General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 37717f9f82c82f4dbd6ede12de7e9f66a357e939 $
19  *
20  * @file src/lib/util/dl.c
21  * @brief Wrappers around dlopen to manage loading shared objects at runtime.
22  *
23  * @copyright 2016-2019 The FreeRADIUS server project
24  * @copyright 2016-2019 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25  */
26 RCSID("$Id: 37717f9f82c82f4dbd6ede12de7e9f66a357e939 $")
27 
28 #include <freeradius-devel/server/dl_module.h>
29 #include <freeradius-devel/server/log.h>
30 #include <freeradius-devel/util/atexit.h>
31 #include <freeradius-devel/util/debug.h>
32 
33 #include <freeradius-devel/util/paths.h>
34 #include <freeradius-devel/util/syserror.h>
35 
36 
37 #ifdef HAVE_VALGRIND_H
38 # include <valgrind.h>
39 #else
40 # define RUNNING_ON_VALGRIND 0
41 #endif
42 
43 #ifndef RTLD_NOW
44 # define RTLD_NOW (0)
45 #endif
46 #ifndef RTLD_LOCAL
47 # define RTLD_LOCAL (0)
48 #endif
49 
50 /** Symbol dependent initialisation callback
51  *
52  * Call this function when the dl is loaded for the first time.
53  */
54 typedef struct dl_symbol_init_s dl_symbol_init_t;
56  fr_dlist_t entry; //!< Entry into the list of 'init' symbol callbacks.
57 
58  unsigned int priority; //!< Call priority
59  char const *symbol; //!< to search for. May be NULL in which case func is always called.
60  dl_onload_t func; //!< to call when symbol is found in a dl's symbol table.
61  void *uctx; //!< User data to pass to func.
62 };
63 
64 /** Symbol dependent free callback
65  *
66  * Call this function before the dl is unloaded.
67  */
68 typedef struct dl_symbol_free_s dl_symbol_free_t;
70  fr_dlist_t entry; //!< Entry into the list of 'free' symbol callbacks.
71 
72  unsigned int priority; //!< Call priority
73  char const *symbol; //!< to search for. May be NULL in which case func is always called.
74  dl_unload_t func; //!< to call when symbol is found in a dl's symbol table.
75  void *uctx; //!< User data to pass to func.
76 };
77 
78 /** A dynamic loader
79  *
80  */
81 struct dl_loader_s {
82  char *lib_dir; //!< Where the libraries live.
83 
84  /** Linked list of symbol init callbacks
85  *
86  * @note Is linked list to retain insertion order. We don't expect huge numbers
87  * of callbacks so there shouldn't be efficiency issues.
88  */
90 
91  /** Linked list of symbol free callbacks
92  *
93  * @note Is linked list to retain insertion order. We don't expect huge numbers
94  * of callbacks so there shouldn't be efficiency issues.
95  */
97 
98  bool do_dlclose; //!< dlclose modules when we're done with them.
99 
100  bool do_static; //!< Do all symbol resolution using the special
101  ///< RTLD_DEFAULT handle, instead of attempting
102  ///< to load modules using dlopen(). This is
103  ///< useful when FreeRADIUS has been built as
104  ///< a monolithic binary.
105 
106  fr_rb_tree_t *tree; //!< Tree of shared objects loaded.
107 
108  void *uctx; //!< dl private extension data.
109 
110  bool uctx_free; //!< Free uctx when dl_loader_t is freed.
111 
112  bool defer_symbol_init; //!< Do not call dl_symbol_init in dl_loader_init.
113 };
114 
115 /** Global search path, prepended all dlopen paths
116  */
117 static char *dl_global_libdir = NULL;
118 
119 static int8_t dl_symbol_init_cmp(void const *one, void const *two)
120 {
121  dl_symbol_init_t const *a = one, *b = two;
122  int ret;
123 
124  fr_assert(a && b);
125 
126  ret = ((void *)a->func > (void *)b->func) - ((void *)a->func < (void *)b->func);
127  if (ret != 0) return ret;
128 
129  ret = (a->symbol && !b->symbol) - (!a->symbol && b->symbol);
130  if (ret != 0) return ret;
131 
132  if (!a->symbol && !b->symbol) return 0;
133 
134 #ifdef STATIC_ANALYZER
135  if (!fr_cond_assert(a->symbol && b->symbol)) return 0; /* Bug in clang scan ? */
136 #endif
137 
138  ret = strcmp(a->symbol, b->symbol);
139  return CMP(ret, 0);
140 }
141 
142 static int8_t dl_symbol_free_cmp(void const *one, void const *two)
143 {
144  dl_symbol_free_t const *a = one, *b = two;
145  int ret;
146 
147  fr_assert(a && b);
148 
149  ret = ((void *)a->func > (void *)b->func) - ((void *)a->func < (void *)b->func);
150  if (ret != 0) return ret;
151 
152  ret = (a->symbol && !b->symbol) - (!a->symbol && b->symbol);
153  if (ret != 0) return ret;
154 
155  if (!a->symbol && !b->symbol) return 0;
156 
157 #ifdef STATIC_ANALYZER
158  if (!fr_cond_assert(a->symbol && b->symbol)) return 0; /* Bug in clang scan ? */
159 #endif
160 
161  ret = strcmp(a->symbol, b->symbol);
162  return CMP(ret, 0);
163 }
164 
165 /** Compare the name of two dl_t
166  *
167  */
168 static int8_t dl_handle_cmp(void const *one, void const *two)
169 {
170  int ret;
171 
172  ret = strcmp(((dl_t const *)one)->name, ((dl_t const *)two)->name);
173  return CMP(ret, 0);
174 }
175 
176 /** Utility function to dlopen the library containing a particular symbol
177  *
178  * @note Not really part of our 'dl' API, just a convenience function.
179  *
180  * @param[in] sym_name to resolve.
181  * @param[in] flags to pass to dlopen.
182  * @return
183  * - NULL on error.
184  * - A new handle on success.
185  */
186 void *dl_open_by_sym(char const *sym_name, int flags)
187 {
188  Dl_info info;
189  void *sym;
190  void *handle;
191 
192  /*
193  * Resolve the test symbol in our own symbol space by
194  * iterating through all the libraries.
195  * This might be slow. Don't do this at runtime!
196  */
197  sym = dlsym(RTLD_DEFAULT, sym_name);
198  if (!sym) {
199  fr_strerror_printf("Can't resolve symbol %s", sym_name);
200  return NULL;
201  }
202 
203  /*
204  * Lookup the library the symbol belongs to
205  */
206  if (dladdr(sym, &info) == 0) {
207  fr_strerror_printf("Failed retrieving info for \"%s\" (%p)", sym_name, sym);
208  return NULL;
209  }
210 
211  handle = dlopen(info.dli_fname, flags);
212  if (!handle) {
213  fr_strerror_printf("Failed loading \"%s\": %s", info.dli_fname, dlerror());
214  return NULL;
215  }
216 
217  return handle;
218 }
219 
220 /** Walk over the registered init callbacks, searching for the symbols they depend on
221  *
222  * Allows code outside of the dl API to register initialisation functions that get
223  * executed depending on whether the dl exports a particular symbol.
224  *
225  * This cuts down the amount of boilerplate code in 'mod_load' functions.
226  *
227  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
228  * @param[in] dl to search for symbols in.
229  * @return
230  * - 0 continue walking.
231  * - -1 error.
232  */
234 {
235  dl_symbol_init_t *init = NULL;
236  void *sym = NULL;
237  char buffer[256];
238 
239  while ((init = fr_dlist_next(&dl_loader->sym_init, init))) {
240  if (init->symbol) {
241  char *p;
242 
243  snprintf(buffer, sizeof(buffer), "%s_%s", dl->name, init->symbol);
244 
245  /*
246  * '-' is not a valid symbol character in
247  * C. But "libfreeradius-radius" is a
248  * valid library name. So we hash things together.
249  */
250  for (p = buffer; *p != '\0'; p++) {
251  if (*p == '-') *p = '_';
252  }
253 
254  sym = dlsym(dl->handle, buffer);
255  if (!sym) {
256  continue;
257  }
258  }
259 
260  if (init->func(dl, sym, init->uctx) < 0) {
261  fr_strerror_printf("Initialiser \"%s\" failed", buffer);
262  return -1;
263  }
264  }
265 
266  return 0;
267 }
268 
269 /** Walk over the registered init callbacks, searching for the symbols they depend on
270  *
271  * Allows code outside of the dl API to register free functions that get
272  * executed depending on whether the dl exports a particular symbol.
273  *
274  * This cuts down the amount of boilerplate code in 'mod_unload' functions.
275  *
276  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
277  * @param[in] dl to search for symbols in.
278  */
280 {
281  dl_symbol_free_t *free = NULL;
282  void *sym = NULL;
283 
284  while ((free = fr_dlist_next(&dl_loader->sym_free, free))) {
285  if (free->symbol) {
286  char *sym_name = NULL;
287 
288  sym_name = talloc_typed_asprintf(NULL, "%s_%s", dl->name, free->symbol);
289  if (!sym_name) return -1;
290 
291  sym = dlsym(dl->handle, sym_name);
292  talloc_free(sym_name);
293 
294  if (!sym) continue;
295  }
296 
297  free->func(dl, sym, free->uctx);
298  }
299 
300  return 0;
301 }
302 
303 /** Register a callback to execute when a dl with a particular symbol is first loaded
304  *
305  * @note Will replace ctx data for callbacks with the same symbol/func.
306  *
307  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
308  * @param[in] priority Execution priority. Callbacks with a higher priority get
309  * called first.
310  * @param[in] symbol that determines whether func should be called. "<modname>_" is
311  * added as a prefix to the symbol. The prefix is added because
312  * some dls are loaded with RTLD_GLOBAL into the global symbol
313  * space, so the symbols they export must be unique.
314  * May be NULL to always call the function.
315  * @param[in] func to register. Called when dl is loaded.
316  * @param[in] uctx to pass to func.
317  * @return
318  * - 0 on success (or already registered).
319  * - -1 on failure.
320  */
322  char const *symbol, dl_onload_t func, void *uctx)
323 {
325 
327 
328  n = talloc(dl_loader, dl_symbol_init_t);
329  if (unlikely(!n)) return -1;
330  *n = (dl_symbol_init_t){
331  .priority = priority,
332  .symbol = symbol,
333  .func = func,
334  .uctx = uctx
335  };
336 
338  if (p->priority < priority) {
340  n = NULL;
341  break;
342  }
343  }
345 
346  return 0;
347 }
348 
349 /** Unregister an callback that was to be executed when a dl was first loaded
350  *
351  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
352  * @param[in] symbol the callback is attached to.
353  * @param[in] func the callback.
354  */
356 {
357  dl_symbol_init_t *found = NULL, find = { .symbol = symbol, .func = func };
358 
359  while ((found = fr_dlist_next(&dl_loader->sym_init, found)) && (dl_symbol_init_cmp(&find, found) != 0));
360  if (found) {
362  talloc_free(found);
363  }
364 }
365 
366 /** Register a callback to execute when a dl with a particular symbol is unloaded
367  *
368  * @note Will replace ctx data for callbacks with the same symbol/func.
369  *
370  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
371  * @param[in] priority Execution priority. Callbacks with a higher priority get
372  * called first.
373  * @param[in] symbol that determines whether func should be called. "<modname>_" is
374  * added as a prefix to the symbol. The prefix is added because
375  * some dls are loaded with RTLD_GLOBAL into the global symbol
376  * space, so the symbols they export must be unique.
377  * May be NULL to always call the function.
378  * @param[in] func to register. Called then dl is unloaded.
379  * @param[in] uctx to pass to func.
380  * @return
381  * - 0 on success (or already registered).
382  * - -1 on failure.
383  */
385  char const *symbol, dl_unload_t func, void *uctx)
386 {
388 
390 
391  n = talloc(dl_loader, dl_symbol_free_t);
392  if (!n) return -1;
393 
394  *n = (dl_symbol_free_t){
395  .priority = priority,
396  .symbol = symbol,
397  .func = func,
398  .uctx = uctx
399  };
400 
402  if (p->priority < priority) {
404  n = NULL;
405  break;
406  }
407  }
409 
410  return 0;
411 }
412 
413 /** Unregister an callback that was to be executed when a dl was unloaded
414  *
415  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
416  * @param[in] symbol the callback is attached to.
417  * @param[in] func the callback.
418  */
420 {
421  dl_symbol_free_t *found = NULL, find = { .symbol = symbol, .func = func };
422 
423  while ((found = fr_dlist_next(&dl_loader->sym_free, found)) && (dl_symbol_free_cmp(&find, found) != 0));
424  if (found) {
426  talloc_free(found);
427  }
428 }
429 
430 /** Free a dl
431  *
432  * Close dl's dlhandle, unloading it.
433  *
434  * @param[in] dl to close.
435  * @return 0.
436  */
437 static int _dl_free(dl_t *dl)
438 {
439  dl = talloc_get_type_abort(dl, dl_t);
440 
442 
443  /*
444  * Only dlclose() handle if we're *NOT* running under valgrind
445  * as it unloads the symbols valgrind needs.
446  */
447  if (dl->loader->do_dlclose) dlclose(dl->handle); /* ignore any errors */
448 
449  dl->handle = NULL;
450 
451  if (dl->in_tree) fr_rb_delete(dl->loader->tree, dl);
452 
453  return 0;
454 }
455 
456 /** Search for a dl's shared object in various locations
457  *
458  * @note You must call dl_symbol_init when ready to call autoloader callbacks.
459  *
460  * @param[in] dl_loader Tree of dynamically loaded libraries, and callbacks.
461  * @param[in] name of library to load. May be a relative path.
462  * @param[in] uctx Data to store within the dl_t.
463  * @param[in] uctx_free talloc_free the passed in uctx data if this
464  * dl_t is freed.
465  * @return
466  * - A new dl_t on success, or a pointer to an existing
467  * one with the reference count increased.
468  * - NULL on error.
469  */
470 dl_t *dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
471 {
472  int flags = RTLD_NOW;
473  void *handle = NULL;
474  char const *search_path;
475  dl_t *dl;
476 
477  /*
478  * There's already something in the tree,
479  * just return that instead.
480  */
481  dl = fr_rb_find(dl_loader->tree, &(dl_t){ .name = name });
482  if (dl) {
483  talloc_increase_ref_count(dl);
484  return dl;
485  }
486 
487  /*
488  * Get a reference to the internal RTLD_HANDLE,
489  * which is used to search through all current loaded
490  * modules.
491  *
492  * This is useful for static builds which don't actually
493  * want to dlopen anything, and just want to look at the
494  * current global symbol table to load everything
495  * from there.
496  */
497  if (dl_loader->do_static) {
498  handle = dlopen(NULL, 0);
499  if (!handle) {
500  fr_strerror_printf("Failed opening RTLD_DEFAULT handle - %s", dlerror());
501  return NULL;
502  }
503  goto do_symbol_check;
504  }
505 
506  flags |= RTLD_LOCAL;
507 
508  /*
509  * Forces dlopened libraries to resolve symbols within
510  * their local symbol tables instead of the global symbol
511  * table.
512  *
513  * May help resolve issues with symbol conflicts.
514  */
515 #if defined(RTLD_DEEPBIND) && !defined(__SANITIZE_ADDRESS__)
516  flags |= RTLD_DEEPBIND;
517 #endif
518 
519  fr_strerror_clear(); /* clear error buffer */
520 
521  /*
522  * Bind all the symbols *NOW* so we don't hit errors later
523  */
524  flags |= RTLD_NOW;
525 
526  search_path = dl_search_path(dl_loader);
527 
528  /*
529  * Prefer loading our libraries by absolute path.
530  */
531  if (search_path) {
532  char *ctx, *paths, *path;
533  char *p;
534  char *dlerror_txt = NULL;
535 
537 
538  ctx = paths = talloc_typed_strdup(NULL, search_path);
539  while ((path = strsep(&paths, ":")) != NULL) {
540  /*
541  * Trim the trailing slash
542  */
543  p = strrchr(path, '/');
544  if (p && ((p[1] == '\0') || (p[1] == ':'))) *p = '\0';
545 
546  path = talloc_typed_asprintf(ctx, "%s/%s%s", path, name, DL_EXTENSION);
547  handle = dlopen(path, flags);
548  talloc_free(path);
549  if (handle) break;
550 
551  /*
552  * There's no dlopenat(), so one can't use it and later
553  * check acessatat() to avoid toctou.
554  *
555  * The only indication of why dlopen() failed is thus the
556  * contents of the string dlerror() returns, but all the
557  * man page says about that is that it's "human readable",
558  * NUL-terminated, and doesn't end with a newline.
559  *
560  * The following attempts to use the dlerror() output to
561  * determine whether dlopen() failed because path doesn't
562  * exist. This goes beyond what the API specifies (Hyrum's
563  * Law strikes again!), but the alternatives are
564  *
565  * 1. looking into the data structure dlerror() uses
566  * 2. let the toctou remain
567  *
568  * both of which seem worse.
569  */
570 
571  /*
572  * If the file doesn't exist, continue with the next element
573  * of "path". Otherwise, stop looking for more libraries
574  * and instead complain about access permissions.
575  */
576  dlerror_txt = dlerror();
577 
578  /*
579  * Yes, this really is the only way of getting the errno
580  * from the dlopen API.
581  */
582  if (strstr(dlerror_txt, fr_syserror_simple(ENOENT)) != NULL) {
583 #ifndef __linux__
584  int access_mode = R_OK | X_OK;
585 
586 # ifdef AT_ACCESS
587  access_mode |= AT_ACCESS;
588 # endif
589  if (access(path, access_mode) < 0 && errno == ENOENT) continue;
590 #endif
591  fr_strerror_printf_push("Access check failed: %s", dlerror_txt);
592  break;
593  }
594 
595  /*
596  * We're reliant on dlerror_txt from the loop,
597  * because once dlerror() is called the error
598  * is cleared.
599  *
600  * We need to push every error because we
601  * don't know which one would be useful
602  * in diagnosing the underlying cause of the
603  * load failure.
604  */
605  fr_strerror_printf_push("%s", dlerror_txt ? dlerror_txt : "unknown dlopen error");
606  }
607 
608  /*
609  * No element of "path" had the library. Return
610  * the error from the last dlopen().
611  */
612  if (!handle) {
613  talloc_free(ctx);
614  return NULL;
615  }
616 
617  fr_strerror_clear(); /* Don't leave spurious errors in the buffer */
618 
619  talloc_free(ctx);
620  } else {
621  char buffer[2048];
622 
623  strlcpy(buffer, name, sizeof(buffer));
624  /*
625  * FIXME: Make this configurable...
626  */
627  strlcat(buffer, DL_EXTENSION, sizeof(buffer));
628 
629  handle = dlopen(buffer, flags);
630  if (!handle) {
631  char *error = dlerror();
632 
633  /*
634  * Append the error
635  */
636  fr_strerror_printf_push("%s", error);
637  return NULL;
638  }
639  }
640 
641 do_symbol_check:
642  dl = talloc(dl_loader, dl_t);
643  if (unlikely(!dl)) {
644  dlclose(handle);
645  return NULL;
646  }
647  *dl = (dl_t){
649  .handle = handle,
650  .loader = dl_loader,
651  .uctx = uctx,
652  .uctx_free = uctx_free
653  };
654  talloc_set_destructor(dl, _dl_free);
655 
657  if (!dl->in_tree) {
658  talloc_free(dl);
659  return NULL;
660  }
661 
663 
664  return dl;
665 }
666 
667 /** "free" a dl handle, possibly actually freeing it, and unloading the library
668  *
669  * This function should be used to explicitly free a dl.
670  *
671  * Because dls are reference counted, it may not actually free the memory
672  * or unload the library, but it will reduce the reference count.
673  *
674  * @return
675  * - 0 if the dl was actually freed.
676  * - >0 the number of remaining references.
677  */
678 int dl_free(dl_t const *dl)
679 {
680  if (!dl) return 0;
681 
683 }
684 
686 {
687  int ret = 0;
688 
689  if (dl_loader->uctx_free) {
690  ret = talloc_free(dl_loader->uctx);
691  if (ret != 0) goto finish;
692  }
693 
694  /*
695  * Prevent freeing if we still have dls loaded
696  * We do reference counting, we know exactly what
697  * should still be active.
698  */
699  if (fr_rb_num_elements(dl_loader->tree) > 0) {
700 #ifndef NDEBUG
702  void *data;
703 
704  /*
705  * Yes, this is the correct call order
706  */
707  for (data = fr_rb_iter_init_inorder(&iter, dl_loader->tree);
708  data;
709  data = fr_rb_iter_next_inorder(&iter)) {
710  dl_t *dl = talloc_get_type_abort(data, dl_t);
711 
712  fr_strerror_printf_push(" %s (%zu)", dl->name, talloc_reference_count(dl));
713  }
714 
715  fr_strerror_printf_push("Refusing to cleanup dl loader, the following dynamically loaded "
716  "libraries are still in use:");
717 #endif
718  ret = -1;
719  goto finish;
720  }
721 
722 finish:
723  return ret;
724 }
725 
726 /** Return current library path
727  *
728  */
730 {
731  char *env;
732  fr_sbuff_t *search_path = NULL;
733 
734  /*
735  * The search path in this order [env:][global:]dl_loader->lib_dir
736  */
737 
738  /*
739  * Create a thread-local extensible buffer to
740  * store library search_path data.
741  *
742  * This is created once per-thread (the first time
743  * this function is called), and freed when the
744  * thread exits.
745  */
746  FR_SBUFF_TALLOC_THREAD_LOCAL(&search_path, 16, PATH_MAX);
747 
748  /*
749  * Apple removed support for DYLD_LIBRARY_PATH in rootless mode.
750  */
751  env = getenv("FR_LIBRARY_PATH");
752  if (env && fr_sbuff_in_sprintf(search_path, "%s:", env) < 0) return NULL;
753 
754  if (dl_global_libdir && fr_sbuff_in_sprintf(search_path, "%s:", dl_global_libdir) < 0) return NULL;
755 
756  if (fr_sbuff_in_strcpy(search_path, dl_loader->lib_dir) < 0) return NULL;
757 
758  return fr_sbuff_start(search_path);
759 }
760 
761 /** Set the global library path
762  *
763  * @param[in] lib_dir ":" separated list of paths to search for libraries in.
764  * @return
765  * - 0 on success.
766  * - -1 on failure.
767  */
768 int dl_search_global_path_set(char const *lib_dir)
769 {
770  if (dl_global_libdir) TALLOC_FREE(dl_global_libdir);
771 
772  dl_global_libdir = talloc_typed_strdup(NULL, lib_dir);
773  if (!dl_global_libdir) {
774  fr_strerror_const("Failed allocating memory for global dl search path");
775  return -1;
776  }
777 
778  fr_atexit_global_once(NULL, fr_atexit_talloc_free, dl_global_libdir);
779 
780  return 0;
781 }
782 
783 /** Set the current library path
784  *
785  * @param[in] dl_loader to add search path component for.
786  * @param[in] lib_dir A ":" separated list of paths to search for libraries in.
787  */
788 int dl_search_path_set(dl_loader_t *dl_loader, char const *lib_dir)
789 {
790  char const *old;
791 
792  old = dl_loader->lib_dir;
793 
794  dl_loader->lib_dir = talloc_strdup(dl_loader, lib_dir);
795  if (!dl_loader->lib_dir) {
796  fr_strerror_const("Failed allocating memory for dl search path");
797  return -1;
798  }
799 
800  talloc_const_free(old);
801 
802  return 0;
803 }
804 
805 /** Append a new search path component to the library search path
806  *
807  * @param[in] dl_loader to add search path component for.
808  * @param[in] lib_dir to add. Does not require a ":" prefix.
809  * @return
810  * - 0 on success.
811  * - -1 on failure.
812  */
813 int dl_search_path_prepend(dl_loader_t *dl_loader, char const *lib_dir)
814 {
815  char *new;
816 
817  if (!dl_loader->lib_dir) {
818  dl_loader->lib_dir = talloc_strdup(dl_loader->lib_dir, lib_dir);
819  if (!dl_loader->lib_dir) {
820  oom:
821  fr_strerror_const("Failed allocating memory for dl search path");
822  return -1;
823  }
824  return 0;
825  }
826 
827  new = talloc_asprintf(dl_loader->lib_dir, "%s:%s", lib_dir, dl_loader->lib_dir);
828  if (!new) goto oom;
829 
830  dl_loader->lib_dir = new;
831 
832  return 0;
833 }
834 
835 /** Append a new search path component to the library search path
836  *
837  * @param[in] dl_loader to add search path component for.
838  * @param[in] lib_dir to add. Does not require a ":" prefix.
839  * @return
840  * - 0 on success.
841  * - -1 on failure.
842  */
843 int dl_search_path_append(dl_loader_t *dl_loader, char const *lib_dir)
844 {
845  char *new;
846 
847  if (!dl_loader->lib_dir) {
848  dl_loader->lib_dir = talloc_strdup(dl_loader->lib_dir, lib_dir);
849  if (!dl_loader->lib_dir) {
850  oom:
851  fr_strerror_const("Failed allocating memory for dl search path");
852  return -1;
853  }
854  return 0;
855  }
856 
857  new = talloc_asprintf_append_buffer(dl_loader->lib_dir, ":%s", lib_dir);
858  if (!new) goto oom;
859 
860  dl_loader->lib_dir = new;
861 
862  return 0;
863 }
864 
865 /** Retrieve the uctx from a dl_loader
866  *
867  */
869 {
870  return dl_loader->uctx;
871 }
872 
873 /** Initialise structures needed by the dynamic linker
874  *
875  * @param[in] ctx To bind lifetime of dl_loader_t too.
876  * @param[in] uctx API client opaque data to store in dl_loader_t.
877  * @param[in] uctx_free Call talloc_free() on uctx when the dl_loader_t
878  * is freed.
879  * @param[in] defer_symbol_init If true, it is up to the caller to call
880  * #dl_symbol_init after calling #dl_by_name.
881  * This prevents any of the registered callbacks
882  * from executing until #dl_symbol_init is
883  * called explicitly.
884  */
885 dl_loader_t *dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
886 {
888 
889  dl_loader = talloc_zero(NULL, dl_loader_t);
890  if (!dl_loader) {
891  fr_strerror_const("Failed allocating dl_loader");
892  return NULL;
893  }
894 
896  if (!dl_loader->tree) {
897  fr_strerror_const("Failed initialising dl->tree");
898  error:
899  TALLOC_FREE(dl_loader);
900  return NULL;
901  }
902 
904 
905  dl_loader->lib_dir = talloc_strdup(dl_loader, fr_path_default_lib_dir());
906  if (!dl_loader->lib_dir) {
907  fr_strerror_const("Failed allocating memory for dl search path");
908  goto error;
909  }
910 
911  talloc_set_destructor(dl_loader, _dl_loader_free);
912 
913  /*
914  * Run this now to avoid bizarre issues
915  * with the talloc atexit handlers firing
916  * in the child, and that causing issues.
917  */
919  dl_loader->uctx = uctx;
920  dl_loader->uctx_free = uctx_free;
921  dl_loader->defer_symbol_init = defer_symbol_init;
922 
923  /*
924  * It's not clear yet whether we still want
925  * some dynamic loading capability in
926  * emscripten, so keep this as a potentially
927  * runtime toggle for now.
928  */
929 #ifdef __EMSCRIPTEN__
930  dl_loader->do_static = true;
931 #else
932  dl_loader->do_static = false;
933 #endif
936 
937  return dl_loader;
938 }
939 
940 /** Runtime override for doing static or dynamic module loading
941  *
942  * @param[in] dl_loader to configure.
943  * @param[in] do_static If true, all dlopen calls result in a
944  * reference to RTLD_DEFAULT being returned
945  * which allows all the dynamic loading
946  * infrastructure to worth correctly with
947  * a monolithic binary.
948  * @return The previous value of do_static
949  */
951 {
952  bool old = dl_loader->do_static;
953 
954  dl_loader->do_static = do_static;
955 
956  return old;
957 }
958 
959 /** Called from a debugger to print information about a dl_loader
960  *
961  */
963 {
964  FR_FAULT_LOG("dl_loader %p", dl);
965  FR_FAULT_LOG("lib_dir : %s", dl->lib_dir);
966  FR_FAULT_LOG("do_dlclose : %s", dl->do_dlclose ? "yes" : "no");
967  FR_FAULT_LOG("uctx : %p", dl->uctx);
968  FR_FAULT_LOG("uctx_free : %s", dl->do_dlclose ? "yes" : "no");
969  FR_FAULT_LOG("defer_symbol_init : %s", dl->defer_symbol_init ? "yes" : "no");
970 
971  fr_dlist_foreach(&dl->sym_init, dl_symbol_init_t, sym) {
972  FR_FAULT_LOG("symbol_init %s", sym->symbol ? sym->symbol : "<base>");
973  FR_FAULT_LOG("\tpriority : %u", sym->priority);
974  FR_FAULT_LOG("\tfunc : %p", sym->func);
975  FR_FAULT_LOG("\tuctx : %p", sym->uctx);
976  }
977 
978  fr_dlist_foreach(&dl->sym_free, dl_symbol_free_t, sym) {
979  FR_FAULT_LOG("symbol_free %s", sym->symbol ? sym->symbol : "<base>");
980  FR_FAULT_LOG("\tpriority : %u", sym->priority);
981  FR_FAULT_LOG("\tfunc : %p", sym->func);
982  FR_FAULT_LOG("\tuctx : %p", sym->uctx);
983  }
984 }
static int const char char buffer[256]
Definition: acutest.h:574
int n
Definition: acutest.h:577
#define fr_atexit_global_once(_init, _free, _uctx)
Definition: atexit.h:211
static dl_loader_t * dl_loader
Definition: fuzzer.c:43
static dl_t * dl
Definition: fuzzer.c:42
#define RCSID(id)
Definition: build.h:444
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition: build.h:110
#define unlikely(_x)
Definition: build.h:378
int fr_get_lsan_state(void)
Definition: debug.c:261
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:137
#define FR_FAULT_LOG(_fmt,...)
Definition: debug.h:49
struct dl_symbol_free_s dl_symbol_free_t
Symbol dependent free callback.
Definition: dl.c:68
struct dl_symbol_init_s dl_symbol_init_t
Symbol dependent initialisation callback.
Definition: dl.c:54
bool do_dlclose
dlclose modules when we're done with them.
Definition: dl.c:98
#define RTLD_LOCAL
Definition: dl.c:47
dl_loader_t * dl_loader_init(TALLOC_CTX *ctx, void *uctx, bool uctx_free, bool defer_symbol_init)
Initialise structures needed by the dynamic linker.
Definition: dl.c:885
char const * symbol
to search for. May be NULL in which case func is always called.
Definition: dl.c:59
void * uctx
dl private extension data.
Definition: dl.c:108
dl_onload_t func
to call when symbol is found in a dl's symbol table.
Definition: dl.c:60
bool uctx_free
Free uctx when dl_loader_t is freed.
Definition: dl.c:110
dl_t * dl_by_name(dl_loader_t *dl_loader, char const *name, void *uctx, bool uctx_free)
Search for a dl's shared object in various locations.
Definition: dl.c:470
int dl_search_path_append(dl_loader_t *dl_loader, char const *lib_dir)
Append a new search path component to the library search path.
Definition: dl.c:843
static char * dl_global_libdir
Global search path, prepended all dlopen paths.
Definition: dl.c:117
int dl_search_global_path_set(char const *lib_dir)
Set the global library path.
Definition: dl.c:768
int dl_symbol_free_cb_register(dl_loader_t *dl_loader, unsigned int priority, char const *symbol, dl_unload_t func, void *uctx)
Register a callback to execute when a dl with a particular symbol is unloaded.
Definition: dl.c:384
static int _dl_free(dl_t *dl)
Free a dl.
Definition: dl.c:437
bool dl_loader_set_static(dl_loader_t *dl_loader, bool do_static)
Runtime override for doing static or dynamic module loading.
Definition: dl.c:950
static int8_t dl_symbol_init_cmp(void const *one, void const *two)
Definition: dl.c:119
int dl_search_path_prepend(dl_loader_t *dl_loader, char const *lib_dir)
Append a new search path component to the library search path.
Definition: dl.c:813
void * dl_open_by_sym(char const *sym_name, int flags)
Utility function to dlopen the library containing a particular symbol.
Definition: dl.c:186
static int8_t dl_symbol_free_cmp(void const *one, void const *two)
Definition: dl.c:142
fr_rb_tree_t * tree
Tree of shared objects loaded.
Definition: dl.c:106
void * uctx
User data to pass to func.
Definition: dl.c:75
static int _dl_loader_free(dl_loader_t *dl_loader)
Definition: dl.c:685
char const * dl_search_path(dl_loader_t *dl_loader)
Return current library path.
Definition: dl.c:729
void * uctx
User data to pass to func.
Definition: dl.c:61
int dl_symbol_init_cb_register(dl_loader_t *dl_loader, unsigned int priority, char const *symbol, dl_onload_t func, void *uctx)
Register a callback to execute when a dl with a particular symbol is first loaded.
Definition: dl.c:321
static int8_t dl_handle_cmp(void const *one, void const *two)
Compare the name of two dl_t.
Definition: dl.c:168
int dl_search_path_set(dl_loader_t *dl_loader, char const *lib_dir)
Set the current library path.
Definition: dl.c:788
#define RTLD_NOW
Definition: dl.c:44
#define RUNNING_ON_VALGRIND
Definition: dl.c:40
fr_dlist_t entry
Entry into the list of 'free' symbol callbacks.
Definition: dl.c:70
bool do_static
Do all symbol resolution using the special RTLD_DEFAULT handle, instead of attempting to load modules...
Definition: dl.c:100
int dl_free(dl_t const *dl)
"free" a dl handle, possibly actually freeing it, and unloading the library
Definition: dl.c:678
void * dl_loader_uctx(dl_loader_t *dl_loader)
Retrieve the uctx from a dl_loader.
Definition: dl.c:868
int dl_symbol_init(dl_loader_t *dl_loader, dl_t const *dl)
Walk over the registered init callbacks, searching for the symbols they depend on.
Definition: dl.c:233
char * lib_dir
Where the libraries live.
Definition: dl.c:82
bool defer_symbol_init
Do not call dl_symbol_init in dl_loader_init.
Definition: dl.c:112
unsigned int priority
Call priority.
Definition: dl.c:72
fr_dlist_t entry
Entry into the list of 'init' symbol callbacks.
Definition: dl.c:56
void dl_symbol_free_cb_unregister(dl_loader_t *dl_loader, char const *symbol, dl_unload_t func)
Unregister an callback that was to be executed when a dl was unloaded.
Definition: dl.c:419
void dl_symbol_init_cb_unregister(dl_loader_t *dl_loader, char const *symbol, dl_onload_t func)
Unregister an callback that was to be executed when a dl was first loaded.
Definition: dl.c:355
void dl_loader_debug(dl_loader_t *dl)
Called from a debugger to print information about a dl_loader.
Definition: dl.c:962
fr_dlist_head_t sym_init
Linked list of symbol init callbacks.
Definition: dl.c:89
dl_unload_t func
to call when symbol is found in a dl's symbol table.
Definition: dl.c:74
fr_dlist_head_t sym_free
Linked list of symbol free callbacks.
Definition: dl.c:96
char const * symbol
to search for. May be NULL in which case func is always called.
Definition: dl.c:73
static int dl_symbol_free(dl_loader_t *dl_loader, dl_t const *dl)
Walk over the registered init callbacks, searching for the symbols they depend on.
Definition: dl.c:279
unsigned int priority
Call priority.
Definition: dl.c:58
A dynamic loader.
Definition: dl.c:81
bool in_tree
Whether this dl is registered in the dl_tree.
Definition: dl.h:67
dl_loader_t * loader
Loader that owns this dl.
Definition: dl.h:63
struct dl_s dl_t
Module handle.
int(* dl_onload_t)(dl_t const *module, void *symbol, void *user_ctx)
Callback to call when a module is first loaded.
Definition: dl.h:79
void * handle
Handle returned by dlopen.
Definition: dl.h:62
void * uctx
API client's opaque data.
Definition: dl.h:65
char const * name
Name of the module e.g. sql.
Definition: dl.h:61
void(* dl_unload_t)(dl_t const *module, void *symbol, void *user_ctx)
Callback when a module is destroyed.
Definition: dl.h:88
Module handle.
Definition: dl.h:58
#define DL_EXTENSION
Definition: dl_module.h:57
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
#define fr_dlist_foreach(_list_head, _type, _iter)
Iterate over the contents of a list.
Definition: dlist.h:94
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
static int fr_dlist_insert_before(fr_dlist_head_t *list_head, void *pos, void *ptr)
Insert an item before an item already in the list.
Definition: dlist.h:450
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
Head of a doubly linked list.
Definition: dlist.h:51
Entry in a doubly linked list.
Definition: dlist.h:41
free(array)
talloc_free(reap)
char * strsep(char **stringp, char const *delim)
Definition: missing.c:123
char const * fr_path_default_lib_dir(void)
Return the default lib dir.
Definition: paths.c:43
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition: rb.c:775
void * fr_rb_iter_next_inorder(fr_rb_iter_inorder_t *iter)
Return the next node.
Definition: rb.c:844
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition: rb.c:624
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition: rb.c:736
void * fr_rb_iter_init_inorder(fr_rb_iter_inorder_t *iter, fr_rb_tree_t *tree)
Initialise an in-order iterator.
Definition: rb.c:818
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rb.c:576
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition: rb.h:246
Iterator structure for in-order traversal of an rbtree.
Definition: rb.h:321
The main red black tree structure.
Definition: rb.h:73
static char const * name
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition: sbuff.c:1419
ssize_t fr_sbuff_in_sprintf(fr_sbuff_t *sbuff, char const *fmt,...)
Print using a fmt string to an sbuff.
Definition: sbuff.c:1554
#define fr_sbuff_start(_sbuff_or_marker)
#define FR_SBUFF_TALLOC_THREAD_LOCAL(_out, _init, _max)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
fr_assert(0)
init
Enter the EAP-IDENTITY state.
Definition: state_machine.c:90
size_t strlcat(char *dst, char const *src, size_t siz)
Definition: strlcat.c:35
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
char const * fr_syserror_simple(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:291
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:333
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition: talloc.c:167
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
int talloc_decrease_ref_count(void const *ptr)
Decrease the reference count on a ptr.
Definition: talloc.c:708
#define talloc_get_type_abort_const
Definition: talloc.h:270
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition: talloc.h:212
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition: strerror.c:577
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition: strerror.h:84
#define fr_strerror_const(_msg)
Definition: strerror.h:223
static fr_slen_t data
Definition: value.h:1259