Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * dfmgr.c
4 : * Dynamic function manager code.
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/fmgr/dfmgr.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include <sys/stat.h>
18 :
19 : #ifndef WIN32
20 : #include <dlfcn.h>
21 :
22 : /*
23 : * On macOS, <dlfcn.h> insists on including <stdbool.h>. If we're not
24 : * using stdbool, undef bool to undo the damage.
25 : */
26 : #ifndef PG_USE_STDBOOL
27 : #ifdef bool
28 : #undef bool
29 : #endif
30 : #endif
31 : #endif /* !WIN32 */
32 :
33 : #include "fmgr.h"
34 : #include "lib/stringinfo.h"
35 : #include "miscadmin.h"
36 : #include "storage/fd.h"
37 : #include "storage/shmem.h"
38 : #include "utils/hsearch.h"
39 :
40 :
41 : /* signature for PostgreSQL-specific library init function */
42 : typedef void (*PG_init_t) (void);
43 :
44 : /* hashtable entry for rendezvous variables */
45 : typedef struct
46 : {
47 : char varName[NAMEDATALEN]; /* hash key (must be first) */
48 : void *varValue;
49 : } rendezvousHashEntry;
50 :
51 : /*
52 : * List of dynamically loaded files (kept in malloc'd memory).
53 : */
54 :
55 : typedef struct df_files
56 : {
57 : struct df_files *next; /* List link */
58 : dev_t device; /* Device file is on */
59 : #ifndef WIN32 /* ensures we never again depend on this under
60 : * win32 */
61 : ino_t inode; /* Inode number of file */
62 : #endif
63 : void *handle; /* a handle for pg_dl* functions */
64 : char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
65 : } DynamicFileList;
66 :
67 : static DynamicFileList *file_list = NULL;
68 : static DynamicFileList *file_tail = NULL;
69 :
70 : /* stat() call under Win32 returns an st_ino field, but it has no meaning */
71 : #ifndef WIN32
72 : #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
73 : #else
74 : #define SAME_INODE(A,B) false
75 : #endif
76 :
77 : char *Dynamic_library_path;
78 :
79 : static void *internal_load_library(const char *libname);
80 : static void incompatible_module_error(const char *libname,
81 : const Pg_magic_struct *module_magic_data) pg_attribute_noreturn();
82 : static char *expand_dynamic_library_name(const char *name);
83 : static void check_restricted_library_name(const char *name);
84 : static char *substitute_libpath_macro(const char *name);
85 : static char *find_in_dynamic_libpath(const char *basename);
86 :
87 : /* Magic structure that module needs to match to be accepted */
88 : static const Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA;
89 :
90 :
91 : /*
92 : * Load the specified dynamic-link library file, and look for a function
93 : * named funcname in it.
94 : *
95 : * If the function is not found, we raise an error if signalNotFound is true,
96 : * else return NULL. Note that errors in loading the library
97 : * will provoke ereport() regardless of signalNotFound.
98 : *
99 : * If filehandle is not NULL, then *filehandle will be set to a handle
100 : * identifying the library file. The filehandle can be used with
101 : * lookup_external_function to lookup additional functions in the same file
102 : * at less cost than repeating load_external_function.
103 : */
104 : void *
105 13414 : load_external_function(const char *filename, const char *funcname,
106 : bool signalNotFound, void **filehandle)
107 : {
108 : char *fullname;
109 : void *lib_handle;
110 : void *retval;
111 :
112 : /* Expand the possibly-abbreviated filename to an exact path name */
113 13414 : fullname = expand_dynamic_library_name(filename);
114 :
115 : /* Load the shared library, unless we already did */
116 13414 : lib_handle = internal_load_library(fullname);
117 :
118 : /* Return handle if caller wants it */
119 13406 : if (filehandle)
120 10686 : *filehandle = lib_handle;
121 :
122 : /* Look up the function within the library. */
123 13406 : retval = dlsym(lib_handle, funcname);
124 :
125 13406 : if (retval == NULL && signalNotFound)
126 6 : ereport(ERROR,
127 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
128 : errmsg("could not find function \"%s\" in file \"%s\"",
129 : funcname, fullname)));
130 :
131 13400 : pfree(fullname);
132 13400 : return retval;
133 : }
134 :
135 : /*
136 : * This function loads a shlib file without looking up any particular
137 : * function in it. If the same shlib has previously been loaded,
138 : * unload and reload it.
139 : *
140 : * When 'restricted' is true, only libraries in the presumed-secure
141 : * directory $libdir/plugins may be referenced.
142 : */
143 : void
144 2174 : load_file(const char *filename, bool restricted)
145 : {
146 : char *fullname;
147 :
148 : /* Apply security restriction if requested */
149 2174 : if (restricted)
150 0 : check_restricted_library_name(filename);
151 :
152 : /* Expand the possibly-abbreviated filename to an exact path name */
153 2174 : fullname = expand_dynamic_library_name(filename);
154 :
155 : /* Load the shared library */
156 2174 : (void) internal_load_library(fullname);
157 :
158 2174 : pfree(fullname);
159 2174 : }
160 :
161 : /*
162 : * Lookup a function whose library file is already loaded.
163 : * Return NULL if not found.
164 : */
165 : void *
166 10680 : lookup_external_function(void *filehandle, const char *funcname)
167 : {
168 10680 : return dlsym(filehandle, funcname);
169 : }
170 :
171 :
172 : /*
173 : * Load the specified dynamic-link library file, unless it already is
174 : * loaded. Return the pg_dl* handle for the file.
175 : *
176 : * Note: libname is expected to be an exact name for the library file.
177 : *
178 : * NB: There is presently no way to unload a dynamically loaded file. We might
179 : * add one someday if we can convince ourselves we have safe protocols for un-
180 : * hooking from hook function pointers, releasing custom GUC variables, and
181 : * perhaps other things that are definitely unsafe currently.
182 : */
183 : static void *
184 20384 : internal_load_library(const char *libname)
185 : {
186 : DynamicFileList *file_scanner;
187 : PGModuleMagicFunction magic_func;
188 : char *load_error;
189 : struct stat stat_buf;
190 : PG_init_t PG_init;
191 :
192 : /*
193 : * Scan the list of loaded FILES to see if the file has been loaded.
194 : */
195 31960 : for (file_scanner = file_list;
196 21794 : file_scanner != NULL &&
197 21794 : strcmp(libname, file_scanner->filename) != 0;
198 11576 : file_scanner = file_scanner->next)
199 : ;
200 :
201 20384 : if (file_scanner == NULL)
202 : {
203 : /*
204 : * Check for same files - different paths (ie, symlink or link)
205 : */
206 10166 : if (stat(libname, &stat_buf) == -1)
207 8 : ereport(ERROR,
208 : (errcode_for_file_access(),
209 : errmsg("could not access file \"%s\": %m",
210 : libname)));
211 :
212 16586 : for (file_scanner = file_list;
213 6428 : file_scanner != NULL &&
214 6428 : !SAME_INODE(stat_buf, *file_scanner);
215 6428 : file_scanner = file_scanner->next)
216 : ;
217 : }
218 :
219 20376 : if (file_scanner == NULL)
220 : {
221 : /*
222 : * File not loaded yet.
223 : */
224 : file_scanner = (DynamicFileList *)
225 10158 : malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
226 10158 : if (file_scanner == NULL)
227 0 : ereport(ERROR,
228 : (errcode(ERRCODE_OUT_OF_MEMORY),
229 : errmsg("out of memory")));
230 :
231 50790 : MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
232 10158 : strcpy(file_scanner->filename, libname);
233 10158 : file_scanner->device = stat_buf.st_dev;
234 : #ifndef WIN32
235 10158 : file_scanner->inode = stat_buf.st_ino;
236 : #endif
237 10158 : file_scanner->next = NULL;
238 :
239 10158 : file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
240 10158 : if (file_scanner->handle == NULL)
241 : {
242 0 : load_error = dlerror();
243 0 : free(file_scanner);
244 : /* errcode_for_file_access might not be appropriate here? */
245 0 : ereport(ERROR,
246 : (errcode_for_file_access(),
247 : errmsg("could not load library \"%s\": %s",
248 : libname, load_error)));
249 : }
250 :
251 : /* Check the magic function to determine compatibility */
252 10158 : magic_func = (PGModuleMagicFunction)
253 10158 : dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
254 10158 : if (magic_func)
255 : {
256 10158 : const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
257 :
258 10158 : if (magic_data_ptr->len != magic_data.len ||
259 10158 : memcmp(magic_data_ptr, &magic_data, magic_data.len) != 0)
260 : {
261 : /* copy data block before unlinking library */
262 0 : Pg_magic_struct module_magic_data = *magic_data_ptr;
263 :
264 : /* try to close library */
265 0 : dlclose(file_scanner->handle);
266 0 : free(file_scanner);
267 :
268 : /* issue suitable complaint */
269 0 : incompatible_module_error(libname, &module_magic_data);
270 : }
271 : }
272 : else
273 : {
274 : /* try to close library */
275 0 : dlclose(file_scanner->handle);
276 0 : free(file_scanner);
277 : /* complain */
278 0 : ereport(ERROR,
279 : (errmsg("incompatible library \"%s\": missing magic block",
280 : libname),
281 : errhint("Extension libraries are required to use the PG_MODULE_MAGIC macro.")));
282 : }
283 :
284 : /*
285 : * If the library has a _PG_init() function, call it.
286 : */
287 10158 : PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
288 10158 : if (PG_init)
289 5468 : (*PG_init) ();
290 :
291 : /* OK to link it into list */
292 10158 : if (file_list == NULL)
293 6714 : file_list = file_scanner;
294 : else
295 3444 : file_tail->next = file_scanner;
296 10158 : file_tail = file_scanner;
297 : }
298 :
299 20376 : return file_scanner->handle;
300 : }
301 :
302 : /*
303 : * Report a suitable error for an incompatible magic block.
304 : */
305 : static void
306 0 : incompatible_module_error(const char *libname,
307 : const Pg_magic_struct *module_magic_data)
308 : {
309 : StringInfoData details;
310 :
311 : /*
312 : * If the version doesn't match, just report that, because the rest of the
313 : * block might not even have the fields we expect.
314 : */
315 0 : if (magic_data.version != module_magic_data->version)
316 : {
317 : char library_version[32];
318 :
319 0 : if (module_magic_data->version >= 1000)
320 0 : snprintf(library_version, sizeof(library_version), "%d",
321 0 : module_magic_data->version / 100);
322 : else
323 0 : snprintf(library_version, sizeof(library_version), "%d.%d",
324 0 : module_magic_data->version / 100,
325 0 : module_magic_data->version % 100);
326 0 : ereport(ERROR,
327 : (errmsg("incompatible library \"%s\": version mismatch",
328 : libname),
329 : errdetail("Server is version %d, library is version %s.",
330 : magic_data.version / 100, library_version)));
331 : }
332 :
333 : /*
334 : * Similarly, if the ABI extra field doesn't match, error out. Other
335 : * fields below might also mismatch, but that isn't useful information if
336 : * you're using the wrong product altogether.
337 : */
338 0 : if (strcmp(module_magic_data->abi_extra, magic_data.abi_extra) != 0)
339 : {
340 0 : ereport(ERROR,
341 : (errmsg("incompatible library \"%s\": ABI mismatch",
342 : libname),
343 : errdetail("Server has ABI \"%s\", library has \"%s\".",
344 : magic_data.abi_extra,
345 : module_magic_data->abi_extra)));
346 : }
347 :
348 : /*
349 : * Otherwise, spell out which fields don't agree.
350 : *
351 : * XXX this code has to be adjusted any time the set of fields in a magic
352 : * block change!
353 : */
354 0 : initStringInfo(&details);
355 :
356 0 : if (module_magic_data->funcmaxargs != magic_data.funcmaxargs)
357 : {
358 0 : if (details.len)
359 0 : appendStringInfoChar(&details, '\n');
360 0 : appendStringInfo(&details,
361 0 : _("Server has FUNC_MAX_ARGS = %d, library has %d."),
362 : magic_data.funcmaxargs,
363 : module_magic_data->funcmaxargs);
364 : }
365 0 : if (module_magic_data->indexmaxkeys != magic_data.indexmaxkeys)
366 : {
367 0 : if (details.len)
368 0 : appendStringInfoChar(&details, '\n');
369 0 : appendStringInfo(&details,
370 0 : _("Server has INDEX_MAX_KEYS = %d, library has %d."),
371 : magic_data.indexmaxkeys,
372 : module_magic_data->indexmaxkeys);
373 : }
374 0 : if (module_magic_data->namedatalen != magic_data.namedatalen)
375 : {
376 0 : if (details.len)
377 0 : appendStringInfoChar(&details, '\n');
378 0 : appendStringInfo(&details,
379 0 : _("Server has NAMEDATALEN = %d, library has %d."),
380 : magic_data.namedatalen,
381 : module_magic_data->namedatalen);
382 : }
383 0 : if (module_magic_data->float8byval != magic_data.float8byval)
384 : {
385 0 : if (details.len)
386 0 : appendStringInfoChar(&details, '\n');
387 0 : appendStringInfo(&details,
388 0 : _("Server has FLOAT8PASSBYVAL = %s, library has %s."),
389 0 : magic_data.float8byval ? "true" : "false",
390 0 : module_magic_data->float8byval ? "true" : "false");
391 : }
392 :
393 0 : if (details.len == 0)
394 0 : appendStringInfoString(&details,
395 0 : _("Magic block has unexpected length or padding difference."));
396 :
397 0 : ereport(ERROR,
398 : (errmsg("incompatible library \"%s\": magic block mismatch",
399 : libname),
400 : errdetail_internal("%s", details.data)));
401 : }
402 :
403 :
404 : /*
405 : * If name contains a slash, check if the file exists, if so return
406 : * the name. Else (no slash) try to expand using search path (see
407 : * find_in_dynamic_libpath below); if that works, return the fully
408 : * expanded file name. If the previous failed, append DLSUFFIX and
409 : * try again. If all fails, just return the original name.
410 : *
411 : * The result will always be freshly palloc'd.
412 : */
413 : static char *
414 15588 : expand_dynamic_library_name(const char *name)
415 : {
416 : bool have_slash;
417 : char *new;
418 : char *full;
419 :
420 : Assert(name);
421 :
422 15588 : have_slash = (first_dir_separator(name) != NULL);
423 :
424 15588 : if (!have_slash)
425 : {
426 4014 : full = find_in_dynamic_libpath(name);
427 4014 : if (full)
428 0 : return full;
429 : }
430 : else
431 : {
432 11574 : full = substitute_libpath_macro(name);
433 11574 : if (pg_file_exists(full))
434 1142 : return full;
435 10432 : pfree(full);
436 : }
437 :
438 14446 : new = psprintf("%s%s", name, DLSUFFIX);
439 :
440 14446 : if (!have_slash)
441 : {
442 4014 : full = find_in_dynamic_libpath(new);
443 4014 : pfree(new);
444 4014 : if (full)
445 4006 : return full;
446 : }
447 : else
448 : {
449 10432 : full = substitute_libpath_macro(new);
450 10432 : pfree(new);
451 10432 : if (pg_file_exists(full))
452 10432 : return full;
453 0 : pfree(full);
454 : }
455 :
456 : /*
457 : * If we can't find the file, just return the string as-is. The ensuing
458 : * load attempt will fail and report a suitable message.
459 : */
460 8 : return pstrdup(name);
461 : }
462 :
463 : /*
464 : * Check a restricted library name. It must begin with "$libdir/plugins/"
465 : * and there must not be any directory separators after that (this is
466 : * sufficient to prevent ".." style attacks).
467 : */
468 : static void
469 0 : check_restricted_library_name(const char *name)
470 : {
471 0 : if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
472 0 : first_dir_separator(name + 16) != NULL)
473 0 : ereport(ERROR,
474 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
475 : errmsg("access to library \"%s\" is not allowed",
476 : name)));
477 0 : }
478 :
479 : /*
480 : * Substitute for any macros appearing in the given string.
481 : * Result is always freshly palloc'd.
482 : */
483 : static char *
484 30034 : substitute_libpath_macro(const char *name)
485 : {
486 : const char *sep_ptr;
487 :
488 : Assert(name != NULL);
489 :
490 : /* Currently, we only recognize $libdir at the start of the string */
491 30034 : if (name[0] != '$')
492 1142 : return pstrdup(name);
493 :
494 28892 : if ((sep_ptr = first_dir_separator(name)) == NULL)
495 8028 : sep_ptr = name + strlen(name);
496 :
497 28892 : if (strlen("$libdir") != sep_ptr - name ||
498 28892 : strncmp(name, "$libdir", strlen("$libdir")) != 0)
499 0 : ereport(ERROR,
500 : (errcode(ERRCODE_INVALID_NAME),
501 : errmsg("invalid macro name in dynamic library path: %s",
502 : name)));
503 :
504 28892 : return psprintf("%s%s", pkglib_path, sep_ptr);
505 : }
506 :
507 :
508 : /*
509 : * Search for a file called 'basename' in the colon-separated search
510 : * path Dynamic_library_path. If the file is found, the full file name
511 : * is returned in freshly palloc'd memory. If the file is not found,
512 : * return NULL.
513 : */
514 : static char *
515 8028 : find_in_dynamic_libpath(const char *basename)
516 : {
517 : const char *p;
518 : size_t baselen;
519 :
520 : Assert(basename != NULL);
521 : Assert(first_dir_separator(basename) == NULL);
522 : Assert(Dynamic_library_path != NULL);
523 :
524 8028 : p = Dynamic_library_path;
525 8028 : if (strlen(p) == 0)
526 0 : return NULL;
527 :
528 8028 : baselen = strlen(basename);
529 :
530 : for (;;)
531 0 : {
532 : size_t len;
533 : char *piece;
534 : char *mangled;
535 : char *full;
536 :
537 8028 : piece = first_path_var_separator(p);
538 8028 : if (piece == p)
539 0 : ereport(ERROR,
540 : (errcode(ERRCODE_INVALID_NAME),
541 : errmsg("zero-length component in parameter dynamic_library_path")));
542 :
543 8028 : if (piece == NULL)
544 8028 : len = strlen(p);
545 : else
546 0 : len = piece - p;
547 :
548 8028 : piece = palloc(len + 1);
549 8028 : strlcpy(piece, p, len + 1);
550 :
551 8028 : mangled = substitute_libpath_macro(piece);
552 8028 : pfree(piece);
553 :
554 8028 : canonicalize_path(mangled);
555 :
556 : /* only absolute paths */
557 8028 : if (!is_absolute_path(mangled))
558 0 : ereport(ERROR,
559 : (errcode(ERRCODE_INVALID_NAME),
560 : errmsg("component in parameter dynamic_library_path is not an absolute path")));
561 :
562 8028 : full = palloc(strlen(mangled) + 1 + baselen + 1);
563 8028 : sprintf(full, "%s/%s", mangled, basename);
564 8028 : pfree(mangled);
565 :
566 8028 : elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);
567 :
568 8028 : if (pg_file_exists(full))
569 4006 : return full;
570 :
571 4022 : pfree(full);
572 :
573 4022 : if (p[len] == '\0')
574 4022 : break;
575 : else
576 0 : p += len + 1;
577 : }
578 :
579 4022 : return NULL;
580 : }
581 :
582 :
583 : /*
584 : * Find (or create) a rendezvous variable that one dynamically
585 : * loaded library can use to meet up with another.
586 : *
587 : * On the first call of this function for a particular varName,
588 : * a "rendezvous variable" is created with the given name.
589 : * The value of the variable is a void pointer (initially set to NULL).
590 : * Subsequent calls with the same varName just return the address of
591 : * the existing variable. Once created, a rendezvous variable lasts
592 : * for the life of the process.
593 : *
594 : * Dynamically loaded libraries can use rendezvous variables
595 : * to find each other and share information: they just need to agree
596 : * on the variable name and the data it will point to.
597 : */
598 : void **
599 3404 : find_rendezvous_variable(const char *varName)
600 : {
601 : static HTAB *rendezvousHash = NULL;
602 :
603 : rendezvousHashEntry *hentry;
604 : bool found;
605 :
606 : /* Create a hashtable if we haven't already done so in this process */
607 3404 : if (rendezvousHash == NULL)
608 : {
609 : HASHCTL ctl;
610 :
611 3398 : ctl.keysize = NAMEDATALEN;
612 3398 : ctl.entrysize = sizeof(rendezvousHashEntry);
613 3398 : rendezvousHash = hash_create("Rendezvous variable hash",
614 : 16,
615 : &ctl,
616 : HASH_ELEM | HASH_STRINGS);
617 : }
618 :
619 : /* Find or create the hashtable entry for this varName */
620 3404 : hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
621 : varName,
622 : HASH_ENTER,
623 : &found);
624 :
625 : /* Initialize to NULL if first time */
626 3404 : if (!found)
627 3404 : hentry->varValue = NULL;
628 :
629 3404 : return &hentry->varValue;
630 : }
631 :
632 : /*
633 : * Estimate the amount of space needed to serialize the list of libraries
634 : * we have loaded.
635 : */
636 : Size
637 826 : EstimateLibraryStateSpace(void)
638 : {
639 : DynamicFileList *file_scanner;
640 826 : Size size = 1;
641 :
642 2066 : for (file_scanner = file_list;
643 : file_scanner != NULL;
644 1240 : file_scanner = file_scanner->next)
645 1240 : size = add_size(size, strlen(file_scanner->filename) + 1);
646 :
647 826 : return size;
648 : }
649 :
650 : /*
651 : * Serialize the list of libraries we have loaded to a chunk of memory.
652 : */
653 : void
654 826 : SerializeLibraryState(Size maxsize, char *start_address)
655 : {
656 : DynamicFileList *file_scanner;
657 :
658 2066 : for (file_scanner = file_list;
659 : file_scanner != NULL;
660 1240 : file_scanner = file_scanner->next)
661 : {
662 : Size len;
663 :
664 1240 : len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
665 : Assert(len < maxsize);
666 1240 : maxsize -= len;
667 1240 : start_address += len;
668 : }
669 826 : start_address[0] = '\0';
670 826 : }
671 :
672 : /*
673 : * Load every library the serializing backend had loaded.
674 : */
675 : void
676 2642 : RestoreLibraryState(char *start_address)
677 : {
678 7438 : while (*start_address != '\0')
679 : {
680 4796 : internal_load_library(start_address);
681 4796 : start_address += strlen(start_address) + 1;
682 : }
683 2642 : }
|