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