Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * dfmgr.c
4 : * Dynamic function manager code.
5 : *
6 : * Portions Copyright (c) 1996-2023, 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/shmem.h"
37 : #include "utils/hsearch.h"
38 :
39 :
40 : /* signature for PostgreSQL-specific library init function */
41 : typedef void (*PG_init_t) (void);
42 :
43 : /* hashtable entry for rendezvous variables */
44 : typedef struct
45 : {
46 : char varName[NAMEDATALEN]; /* hash key (must be first) */
47 : void *varValue;
48 : } rendezvousHashEntry;
49 :
50 : /*
51 : * List of dynamically loaded files (kept in malloc'd memory).
52 : */
53 :
54 : typedef struct df_files
55 : {
56 : struct df_files *next; /* List link */
57 : dev_t device; /* Device file is on */
58 : #ifndef WIN32 /* ensures we never again depend on this under
59 : * win32 */
60 : ino_t inode; /* Inode number of file */
61 : #endif
62 : void *handle; /* a handle for pg_dl* functions */
63 : char filename[FLEXIBLE_ARRAY_MEMBER]; /* Full pathname of file */
64 : } DynamicFileList;
65 :
66 : static DynamicFileList *file_list = NULL;
67 : static DynamicFileList *file_tail = NULL;
68 :
69 : /* stat() call under Win32 returns an st_ino field, but it has no meaning */
70 : #ifndef WIN32
71 : #define SAME_INODE(A,B) ((A).st_ino == (B).inode && (A).st_dev == (B).device)
72 : #else
73 : #define SAME_INODE(A,B) false
74 : #endif
75 :
76 : char *Dynamic_library_path;
77 :
78 : static void *internal_load_library(const char *libname);
79 : static void incompatible_module_error(const char *libname,
80 : const Pg_magic_struct *module_magic_data) pg_attribute_noreturn();
81 : static bool file_exists(const char *name);
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 12480 : 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 12480 : fullname = expand_dynamic_library_name(filename);
114 :
115 : /* Load the shared library, unless we already did */
116 12480 : lib_handle = internal_load_library(fullname);
117 :
118 : /* Return handle if caller wants it */
119 12472 : if (filehandle)
120 9910 : *filehandle = lib_handle;
121 :
122 : /* Look up the function within the library. */
123 12472 : retval = dlsym(lib_handle, funcname);
124 :
125 12472 : 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 12466 : pfree(fullname);
132 12466 : 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 1886 : load_file(const char *filename, bool restricted)
145 : {
146 : char *fullname;
147 :
148 : /* Apply security restriction if requested */
149 1886 : if (restricted)
150 0 : check_restricted_library_name(filename);
151 :
152 : /* Expand the possibly-abbreviated filename to an exact path name */
153 1886 : fullname = expand_dynamic_library_name(filename);
154 :
155 : /* Load the shared library */
156 1886 : (void) internal_load_library(fullname);
157 :
158 1886 : pfree(fullname);
159 1886 : }
160 :
161 : /*
162 : * Lookup a function whose library file is already loaded.
163 : * Return NULL if not found.
164 : */
165 : void *
166 9904 : lookup_external_function(void *filehandle, const char *funcname)
167 : {
168 9904 : 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 19076 : 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 30446 : for (file_scanner = file_list;
196 21126 : file_scanner != NULL &&
197 21126 : strcmp(libname, file_scanner->filename) != 0;
198 11370 : file_scanner = file_scanner->next)
199 : ;
200 :
201 19076 : if (file_scanner == NULL)
202 : {
203 : /*
204 : * Check for same files - different paths (ie, symlink or link)
205 : */
206 9320 : 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 15610 : for (file_scanner = file_list;
213 6298 : file_scanner != NULL &&
214 6298 : !SAME_INODE(stat_buf, *file_scanner);
215 6298 : file_scanner = file_scanner->next)
216 : ;
217 : }
218 :
219 19068 : if (file_scanner == NULL)
220 : {
221 : /*
222 : * File not loaded yet.
223 : */
224 : file_scanner = (DynamicFileList *)
225 9312 : malloc(offsetof(DynamicFileList, filename) + strlen(libname) + 1);
226 9312 : if (file_scanner == NULL)
227 0 : ereport(ERROR,
228 : (errcode(ERRCODE_OUT_OF_MEMORY),
229 : errmsg("out of memory")));
230 :
231 46560 : MemSet(file_scanner, 0, offsetof(DynamicFileList, filename));
232 9312 : strcpy(file_scanner->filename, libname);
233 9312 : file_scanner->device = stat_buf.st_dev;
234 : #ifndef WIN32
235 9312 : file_scanner->inode = stat_buf.st_ino;
236 : #endif
237 9312 : file_scanner->next = NULL;
238 :
239 9312 : file_scanner->handle = dlopen(file_scanner->filename, RTLD_NOW | RTLD_GLOBAL);
240 9312 : 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 9312 : magic_func = (PGModuleMagicFunction)
253 9312 : dlsym(file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING);
254 9312 : if (magic_func)
255 : {
256 9312 : const Pg_magic_struct *magic_data_ptr = (*magic_func) ();
257 :
258 9312 : if (magic_data_ptr->len != magic_data.len ||
259 9312 : 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 9312 : PG_init = (PG_init_t) dlsym(file_scanner->handle, "_PG_init");
288 9312 : if (PG_init)
289 4874 : (*PG_init) ();
290 :
291 : /* OK to link it into list */
292 9312 : if (file_list == NULL)
293 5978 : file_list = file_scanner;
294 : else
295 3334 : file_tail->next = file_scanner;
296 9312 : file_tail = file_scanner;
297 : }
298 :
299 19068 : 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 : static bool
404 27610 : file_exists(const char *name)
405 : {
406 : struct stat st;
407 :
408 : Assert(name != NULL);
409 :
410 27610 : if (stat(name, &st) == 0)
411 14358 : return !S_ISDIR(st.st_mode);
412 13252 : else if (!(errno == ENOENT || errno == ENOTDIR || errno == EACCES))
413 0 : ereport(ERROR,
414 : (errcode_for_file_access(),
415 : errmsg("could not access file \"%s\": %m", name)));
416 :
417 13252 : return false;
418 : }
419 :
420 :
421 : /*
422 : * If name contains a slash, check if the file exists, if so return
423 : * the name. Else (no slash) try to expand using search path (see
424 : * find_in_dynamic_libpath below); if that works, return the fully
425 : * expanded file name. If the previous failed, append DLSUFFIX and
426 : * try again. If all fails, just return the original name.
427 : *
428 : * The result will always be freshly palloc'd.
429 : */
430 : static char *
431 14366 : expand_dynamic_library_name(const char *name)
432 : {
433 : bool have_slash;
434 : char *new;
435 : char *full;
436 :
437 : Assert(name);
438 :
439 14366 : have_slash = (first_dir_separator(name) != NULL);
440 :
441 14366 : if (!have_slash)
442 : {
443 3578 : full = find_in_dynamic_libpath(name);
444 3578 : if (full)
445 0 : return full;
446 : }
447 : else
448 : {
449 10788 : full = substitute_libpath_macro(name);
450 10788 : if (file_exists(full))
451 1122 : return full;
452 9666 : pfree(full);
453 : }
454 :
455 13244 : new = psprintf("%s%s", name, DLSUFFIX);
456 :
457 13244 : if (!have_slash)
458 : {
459 3578 : full = find_in_dynamic_libpath(new);
460 3578 : pfree(new);
461 3578 : if (full)
462 3570 : return full;
463 : }
464 : else
465 : {
466 9666 : full = substitute_libpath_macro(new);
467 9666 : pfree(new);
468 9666 : if (file_exists(full))
469 9666 : return full;
470 0 : pfree(full);
471 : }
472 :
473 : /*
474 : * If we can't find the file, just return the string as-is. The ensuing
475 : * load attempt will fail and report a suitable message.
476 : */
477 8 : return pstrdup(name);
478 : }
479 :
480 : /*
481 : * Check a restricted library name. It must begin with "$libdir/plugins/"
482 : * and there must not be any directory separators after that (this is
483 : * sufficient to prevent ".." style attacks).
484 : */
485 : static void
486 0 : check_restricted_library_name(const char *name)
487 : {
488 0 : if (strncmp(name, "$libdir/plugins/", 16) != 0 ||
489 0 : first_dir_separator(name + 16) != NULL)
490 0 : ereport(ERROR,
491 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
492 : errmsg("access to library \"%s\" is not allowed",
493 : name)));
494 0 : }
495 :
496 : /*
497 : * Substitute for any macros appearing in the given string.
498 : * Result is always freshly palloc'd.
499 : */
500 : static char *
501 27610 : substitute_libpath_macro(const char *name)
502 : {
503 : const char *sep_ptr;
504 :
505 : Assert(name != NULL);
506 :
507 : /* Currently, we only recognize $libdir at the start of the string */
508 27610 : if (name[0] != '$')
509 1122 : return pstrdup(name);
510 :
511 26488 : if ((sep_ptr = first_dir_separator(name)) == NULL)
512 7156 : sep_ptr = name + strlen(name);
513 :
514 26488 : if (strlen("$libdir") != sep_ptr - name ||
515 26488 : strncmp(name, "$libdir", strlen("$libdir")) != 0)
516 0 : ereport(ERROR,
517 : (errcode(ERRCODE_INVALID_NAME),
518 : errmsg("invalid macro name in dynamic library path: %s",
519 : name)));
520 :
521 26488 : return psprintf("%s%s", pkglib_path, sep_ptr);
522 : }
523 :
524 :
525 : /*
526 : * Search for a file called 'basename' in the colon-separated search
527 : * path Dynamic_library_path. If the file is found, the full file name
528 : * is returned in freshly palloc'd memory. If the file is not found,
529 : * return NULL.
530 : */
531 : static char *
532 7156 : find_in_dynamic_libpath(const char *basename)
533 : {
534 : const char *p;
535 : size_t baselen;
536 :
537 : Assert(basename != NULL);
538 : Assert(first_dir_separator(basename) == NULL);
539 : Assert(Dynamic_library_path != NULL);
540 :
541 7156 : p = Dynamic_library_path;
542 7156 : if (strlen(p) == 0)
543 0 : return NULL;
544 :
545 7156 : baselen = strlen(basename);
546 :
547 : for (;;)
548 0 : {
549 : size_t len;
550 : char *piece;
551 : char *mangled;
552 : char *full;
553 :
554 7156 : piece = first_path_var_separator(p);
555 7156 : if (piece == p)
556 0 : ereport(ERROR,
557 : (errcode(ERRCODE_INVALID_NAME),
558 : errmsg("zero-length component in parameter \"dynamic_library_path\"")));
559 :
560 7156 : if (piece == NULL)
561 7156 : len = strlen(p);
562 : else
563 0 : len = piece - p;
564 :
565 7156 : piece = palloc(len + 1);
566 7156 : strlcpy(piece, p, len + 1);
567 :
568 7156 : mangled = substitute_libpath_macro(piece);
569 7156 : pfree(piece);
570 :
571 7156 : canonicalize_path(mangled);
572 :
573 : /* only absolute paths */
574 7156 : if (!is_absolute_path(mangled))
575 0 : ereport(ERROR,
576 : (errcode(ERRCODE_INVALID_NAME),
577 : errmsg("component in parameter \"dynamic_library_path\" is not an absolute path")));
578 :
579 7156 : full = palloc(strlen(mangled) + 1 + baselen + 1);
580 7156 : sprintf(full, "%s/%s", mangled, basename);
581 7156 : pfree(mangled);
582 :
583 7156 : elog(DEBUG3, "find_in_dynamic_libpath: trying \"%s\"", full);
584 :
585 7156 : if (file_exists(full))
586 3570 : return full;
587 :
588 3586 : pfree(full);
589 :
590 3586 : if (p[len] == '\0')
591 3586 : break;
592 : else
593 0 : p += len + 1;
594 : }
595 :
596 3586 : return NULL;
597 : }
598 :
599 :
600 : /*
601 : * Find (or create) a rendezvous variable that one dynamically
602 : * loaded library can use to meet up with another.
603 : *
604 : * On the first call of this function for a particular varName,
605 : * a "rendezvous variable" is created with the given name.
606 : * The value of the variable is a void pointer (initially set to NULL).
607 : * Subsequent calls with the same varName just return the address of
608 : * the existing variable. Once created, a rendezvous variable lasts
609 : * for the life of the process.
610 : *
611 : * Dynamically loaded libraries can use rendezvous variables
612 : * to find each other and share information: they just need to agree
613 : * on the variable name and the data it will point to.
614 : */
615 : void **
616 3036 : find_rendezvous_variable(const char *varName)
617 : {
618 : static HTAB *rendezvousHash = NULL;
619 :
620 : rendezvousHashEntry *hentry;
621 : bool found;
622 :
623 : /* Create a hashtable if we haven't already done so in this process */
624 3036 : if (rendezvousHash == NULL)
625 : {
626 : HASHCTL ctl;
627 :
628 3030 : ctl.keysize = NAMEDATALEN;
629 3030 : ctl.entrysize = sizeof(rendezvousHashEntry);
630 3030 : rendezvousHash = hash_create("Rendezvous variable hash",
631 : 16,
632 : &ctl,
633 : HASH_ELEM | HASH_STRINGS);
634 : }
635 :
636 : /* Find or create the hashtable entry for this varName */
637 3036 : hentry = (rendezvousHashEntry *) hash_search(rendezvousHash,
638 : varName,
639 : HASH_ENTER,
640 : &found);
641 :
642 : /* Initialize to NULL if first time */
643 3036 : if (!found)
644 3036 : hentry->varValue = NULL;
645 :
646 3036 : return &hentry->varValue;
647 : }
648 :
649 : /*
650 : * Estimate the amount of space needed to serialize the list of libraries
651 : * we have loaded.
652 : */
653 : Size
654 804 : EstimateLibraryStateSpace(void)
655 : {
656 : DynamicFileList *file_scanner;
657 804 : Size size = 1;
658 :
659 2010 : for (file_scanner = file_list;
660 : file_scanner != NULL;
661 1206 : file_scanner = file_scanner->next)
662 1206 : size = add_size(size, strlen(file_scanner->filename) + 1);
663 :
664 804 : return size;
665 : }
666 :
667 : /*
668 : * Serialize the list of libraries we have loaded to a chunk of memory.
669 : */
670 : void
671 804 : SerializeLibraryState(Size maxsize, char *start_address)
672 : {
673 : DynamicFileList *file_scanner;
674 :
675 2010 : for (file_scanner = file_list;
676 : file_scanner != NULL;
677 1206 : file_scanner = file_scanner->next)
678 : {
679 : Size len;
680 :
681 1206 : len = strlcpy(start_address, file_scanner->filename, maxsize) + 1;
682 : Assert(len < maxsize);
683 1206 : maxsize -= len;
684 1206 : start_address += len;
685 : }
686 804 : start_address[0] = '\0';
687 804 : }
688 :
689 : /*
690 : * Load every library the serializing backend had loaded.
691 : */
692 : void
693 2592 : RestoreLibraryState(char *start_address)
694 : {
695 7302 : while (*start_address != '\0')
696 : {
697 4710 : internal_load_library(start_address);
698 4710 : start_address += strlen(start_address) + 1;
699 : }
700 2592 : }
|