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 14246 : 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 14246 : fullname = expand_dynamic_library_name(filename);
114 :
115 : /* Load the shared library, unless we already did */
116 14246 : lib_handle = internal_load_library(fullname);
117 :
118 : /* Return handle if caller wants it */
119 14238 : if (filehandle)
120 11706 : *filehandle = lib_handle;
121 :
122 : /* Look up the function within the library. */
123 14238 : retval = dlsym(lib_handle, funcname);
124 :
125 14238 : 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 14232 : pfree(fullname);
132 14232 : 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 2278 : load_file(const char *filename, bool restricted)
145 : {
146 : char *fullname;
147 :
148 : /* Apply security restriction if requested */
149 2278 : if (restricted)
150 0 : check_restricted_library_name(filename);
151 :
152 : /* Expand the possibly-abbreviated filename to an exact path name */
153 2278 : fullname = expand_dynamic_library_name(filename);
154 :
155 : /* Load the shared library */
156 2278 : (void) internal_load_library(fullname);
157 :
158 2278 : pfree(fullname);
159 2278 : }
160 :
161 : /*
162 : * Lookup a function whose library file is already loaded.
163 : * Return NULL if not found.
164 : */
165 : void *
166 11700 : lookup_external_function(void *filehandle, const char *funcname)
167 : {
168 11700 : 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 21406 : 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 32752 : for (file_scanner = file_list;
196 22044 : file_scanner != NULL &&
197 22044 : strcmp(libname, file_scanner->filename) != 0;
198 11346 : file_scanner = file_scanner->next)
199 : ;
200 :
201 21406 : if (file_scanner == NULL)
202 : {
203 : /*
204 : * Check for same files - different paths (ie, symlink or link)
205 : */
206 10708 : 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 17136 : for (file_scanner = file_list;
213 6436 : file_scanner != NULL &&
214 6436 : !SAME_INODE(stat_buf, *file_scanner);
215 6436 : file_scanner = file_scanner->next)
216 : ;
217 : }
218 :
219 21398 : if (file_scanner == NULL)
220 : {
221 : /*
222 : * File not loaded yet.
223 : */
224 : file_scanner = (DynamicFileList *)
225 10700 : malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
226 10700 : if (file_scanner == NULL)
227 0 : ereport(ERROR,
228 : (errcode(ERRCODE_OUT_OF_MEMORY),
229 : errmsg("out of memory")));
230 :
231 53500 : MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
232 10700 : strcpy(file_scanner->filename, libname);
233 10700 : file_scanner->device = stat_buf.st_dev;
234 : #ifndef WIN32
235 10700 : file_scanner->inode = stat_buf.st_ino;
236 : #endif
237 10700 : file_scanner->next = NULL;
238 :
239 10700 : file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
240 10700 : 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 10700 : magic_func = (PGModuleMagicFunction)
253 10700 : dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
254 10700 : if (magic_func)
255 : {
256 10700 : const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
257 :
258 10700 : if (magic_data_ptr->len != magic_data.len ||
259 10700 : 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 10700 : PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
288 10700 : if (PG_init)
289 5772 : (*PG_init) ();
290 :
291 : /* OK to link it into list */
292 10700 : if (file_list == NULL)
293 7246 : file_list = file_scanner;
294 : else
295 3454 : file_tail->next = file_scanner;
296 10700 : file_tail = file_scanner;
297 : }
298 :
299 21398 : 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 : /* translator: %s is a variable name and %d its values */
362 0 : _("Server has %s = %d, library has %d."),
363 : "FUNC_MAX_ARGS", magic_data.funcmaxargs,
364 : module_magic_data->funcmaxargs);
365 : }
366 0 : if (module_magic_data->indexmaxkeys != magic_data.indexmaxkeys)
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 : "INDEX_MAX_KEYS", magic_data.indexmaxkeys,
374 : module_magic_data->indexmaxkeys);
375 : }
376 0 : if (module_magic_data->namedatalen != magic_data.namedatalen)
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 = %d, library has %d."),
383 : "NAMEDATALEN", magic_data.namedatalen,
384 : module_magic_data->namedatalen);
385 : }
386 0 : if (module_magic_data->float8byval != magic_data.float8byval)
387 : {
388 0 : if (details.len)
389 0 : appendStringInfoChar(&details, '\n');
390 0 : appendStringInfo(&details,
391 : /* translator: %s is a variable name and %d its values */
392 0 : _("Server has %s = %s, library has %s."),
393 0 : "FLOAT8PASSBYVAL", magic_data.float8byval ? "true" : "false",
394 0 : module_magic_data->float8byval ? "true" : "false");
395 : }
396 :
397 0 : if (details.len == 0)
398 0 : appendStringInfoString(&details,
399 0 : _("Magic block has unexpected length or padding difference."));
400 :
401 0 : ereport(ERROR,
402 : (errmsg("incompatible library \"%s\": magic block mismatch",
403 : libname),
404 : errdetail_internal("%s", details.data)));
405 : }
406 :
407 :
408 : /*
409 : * If name contains a slash, check if the file exists, if so return
410 : * the name. Else (no slash) try to expand using search path (see
411 : * find_in_dynamic_libpath below); if that works, return the fully
412 : * expanded file name. If the previous failed, append DLSUFFIX and
413 : * try again. If all fails, just return the original name.
414 : *
415 : * The result will always be freshly palloc'd.
416 : */
417 : static char *
418 16524 : expand_dynamic_library_name(const char *name)
419 : {
420 : bool have_slash;
421 : char *new;
422 : char *full;
423 :
424 : Assert(name);
425 :
426 16524 : have_slash = (first_dir_separator(name) != NULL);
427 :
428 16524 : if (!have_slash)
429 : {
430 4194 : full = find_in_dynamic_libpath(name);
431 4194 : if (full)
432 0 : return full;
433 : }
434 : else
435 : {
436 12330 : full = substitute_libpath_macro(name);
437 12330 : if (pg_file_exists(full))
438 906 : return full;
439 11424 : pfree(full);
440 : }
441 :
442 15618 : new = psprintf("%s%s", name, DLSUFFIX);
443 :
444 15618 : if (!have_slash)
445 : {
446 4194 : full = find_in_dynamic_libpath(new);
447 4194 : pfree(new);
448 4194 : if (full)
449 4186 : return full;
450 : }
451 : else
452 : {
453 11424 : full = substitute_libpath_macro(new);
454 11424 : pfree(new);
455 11424 : if (pg_file_exists(full))
456 11424 : return full;
457 0 : pfree(full);
458 : }
459 :
460 : /*
461 : * If we can't find the file, just return the string as-is. The ensuing
462 : * load attempt will fail and report a suitable message.
463 : */
464 8 : return pstrdup(name);
465 : }
466 :
467 : /*
468 : * Check a restricted library name. It must begin with "$libdir/plugins/"
469 : * and there must not be any directory separators after that (this is
470 : * sufficient to prevent ".." style attacks).
471 : */
472 : static void
473 0 : check_restricted_library_name(const char *name)
474 : {
475 0 : if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
476 0 : first_dir_separator(name + 16) != NULL)
477 0 : ereport(ERROR,
478 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
479 : errmsg("access to library \"%s\" is not allowed",
480 : name)));
481 0 : }
482 :
483 : /*
484 : * Substitute for any macros appearing in the given string.
485 : * Result is always freshly palloc'd.
486 : */
487 : static char *
488 32142 : substitute_libpath_macro(const char *name)
489 : {
490 : const char *sep_ptr;
491 :
492 : Assert(name != NULL);
493 :
494 : /* Currently, we only recognize $libdir at the start of the string */
495 32142 : if (name[0] != '$')
496 906 : return pstrdup(name);
497 :
498 31236 : if ((sep_ptr = first_dir_separator(name)) == NULL)
499 8388 : sep_ptr = name + strlen(name);
500 :
501 31236 : if (strlen("$libdir") != sep_ptr - name ||
502 31236 : strncmp(name, "$libdir", strlen("$libdir")) != 0)
503 0 : ereport(ERROR,
504 : (errcode(ERRCODE_INVALID_NAME),
505 : errmsg("invalid macro name in dynamic library path: %s",
506 : name)));
507 :
508 31236 : return psprintf("%s%s", pkglib_path, sep_ptr);
509 : }
510 :
511 :
512 : /*
513 : * Search for a file called 'basename' in the colon-separated search
514 : * path Dynamic_library_path. If the file is found, the full file name
515 : * is returned in freshly palloc'd memory. If the file is not found,
516 : * return NULL.
517 : */
518 : static char *
519 8388 : find_in_dynamic_libpath(const char *basename)
520 : {
521 : const char *p;
522 : size_t baselen;
523 :
524 : Assert(basename != NULL);
525 : Assert(first_dir_separator(basename) == NULL);
526 : Assert(Dynamic_library_path != NULL);
527 :
528 8388 : p = Dynamic_library_path;
529 8388 : if (strlen(p) == 0)
530 0 : return NULL;
531 :
532 8388 : baselen = strlen(basename);
533 :
534 : for (;;)
535 0 : {
536 : size_t len;
537 : char *piece;
538 : char *mangled;
539 : char *full;
540 :
541 8388 : piece = first_path_var_separator(p);
542 8388 : if (piece == p)
543 0 : ereport(ERROR,
544 : (errcode(ERRCODE_INVALID_NAME),
545 : errmsg("zero-length component in parameter \"dynamic_library_path\"")));
546 :
547 8388 : if (piece == NULL)
548 8388 : len = strlen(p);
549 : else
550 0 : len = piece - p;
551 :
552 8388 : piece = palloc(len + 1);
553 8388 : strlcpy(piece, p, len + 1);
554 :
555 8388 : mangled = substitute_libpath_macro(piece);
556 8388 : pfree(piece);
557 :
558 8388 : canonicalize_path(mangled);
559 :
560 : /* only absolute paths */
561 8388 : if (!is_absolute_path(mangled))
562 0 : ereport(ERROR,
563 : (errcode(ERRCODE_INVALID_NAME),
564 : errmsg("component in parameter \"dynamic_library_path\" is not an absolute path")));
565 :
566 8388 : full = palloc(strlen(mangled) + 1 + baselen + 1);
567 8388 : sprintf(full, "%s/%s", mangled, basename);
568 8388 : pfree(mangled);
569 :
570 8388 : elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);
571 :
572 8388 : if (pg_file_exists(full))
573 4186 : return full;
574 :
575 4202 : pfree(full);
576 :
577 4202 : if (p[len] == '\0')
578 4202 : break;
579 : else
580 0 : p += len + 1;
581 : }
582 :
583 4202 : return NULL;
584 : }
585 :
586 :
587 : /*
588 : * Find (or create) a rendezvous variable that one dynamically
589 : * loaded library can use to meet up with another.
590 : *
591 : * On the first call of this function for a particular varName,
592 : * a "rendezvous variable" is created with the given name.
593 : * The value of the variable is a void pointer (initially set to NULL).
594 : * Subsequent calls with the same varName just return the address of
595 : * the existing variable. Once created, a rendezvous variable lasts
596 : * for the life of the process.
597 : *
598 : * Dynamically loaded libraries can use rendezvous variables
599 : * to find each other and share information: they just need to agree
600 : * on the variable name and the data it will point to.
601 : */
602 : void **
603 3536 : find_rendezvous_variable(const char *varName)
604 : {
605 : static HTAB *rendezvousHash = NULL;
606 :
607 : rendezvousHashEntry *hentry;
608 : bool found;
609 :
610 : /* Create a hashtable if we haven't already done so in this process */
611 3536 : if (rendezvousHash == NULL)
612 : {
613 : HASHCTL ctl;
614 :
615 3530 : ctl.keysize = NAMEDATALEN;
616 3530 : ctl.entrysize = sizeof(rendezvousHashEntry);
617 3530 : rendezvousHash = hash_create("Rendezvous variable hash",
618 : 16,
619 : &ctl,
620 : HASH_ELEM | HASH_STRINGS);
621 : }
622 :
623 : /* Find or create the hashtable entry for this varName */
624 3536 : hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
625 : varName,
626 : HASH_ENTER,
627 : &found);
628 :
629 : /* Initialize to NULL if first time */
630 3536 : if (!found)
631 3536 : hentry->varValue = NULL;
632 :
633 3536 : return &hentry->varValue;
634 : }
635 :
636 : /*
637 : * Estimate the amount of space needed to serialize the list of libraries
638 : * we have loaded.
639 : */
640 : Size
641 886 : EstimateLibraryStateSpace(void)
642 : {
643 : DynamicFileList *file_scanner;
644 886 : Size size = 1;
645 :
646 2188 : for (file_scanner = file_list;
647 : file_scanner != NULL;
648 1302 : file_scanner = file_scanner->next)
649 1302 : size = add_size(size, strlen(file_scanner->filename) + 1);
650 :
651 886 : return size;
652 : }
653 :
654 : /*
655 : * Serialize the list of libraries we have loaded to a chunk of memory.
656 : */
657 : void
658 886 : SerializeLibraryState(Size maxsize, char *start_address)
659 : {
660 : DynamicFileList *file_scanner;
661 :
662 2188 : for (file_scanner = file_list;
663 : file_scanner != NULL;
664 1302 : file_scanner = file_scanner->next)
665 : {
666 : Size len;
667 :
668 1302 : len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
669 : Assert(len < maxsize);
670 1302 : maxsize -= len;
671 1302 : start_address += len;
672 : }
673 886 : start_address[0] = '\0';
674 886 : }
675 :
676 : /*
677 : * Load every library the serializing backend had loaded.
678 : */
679 : void
680 2712 : RestoreLibraryState(char *start_address)
681 : {
682 7594 : while (*start_address != '\0')
683 : {
684 4882 : internal_load_library(start_address);
685 4882 : start_address += strlen(start_address) + 1;
686 : }
687 2712 : }
|