From 01b2168c90f121b5be7da72786c051588c43c128 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Tue, 30 May 2006 14:09:32 +0000 Subject: [PATCH] Add pgmagic header block to store compile-time constants: It now only checks four things: Major version number (7.4 or 8.1 for example) NAMEDATALEN FUNC_MAX_ARGS INDEX_MAX_KEYS The three constants were chosen because: 1. We document them in the config page in the docs 2. We mark them as changable in pg_config_manual.h 3. Changing any of these will break some of the more popular modules: FUNC_MAX_ARGS changes fmgr interface, every module uses this NAMEDATALEN changes syscache interface, every PL as well as tsearch uses this INDEX_MAX_KEYS breaks tsearch and anything using GiST. Martijn van Oosterhout --- doc/src/sgml/xfunc.sgml | 29 +++++++++++++- src/backend/utils/fmgr/dfmgr.c | 47 +++++++++++++++++++++- src/include/pgmagic.h | 73 ++++++++++++++++++++++++++++++++++ src/test/regress/regress.c | 5 ++- 4 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 src/include/pgmagic.h diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 27cb8c90ca..1f03d7cd90 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,4 +1,4 @@ - + User-Defined Functions @@ -1148,6 +1148,13 @@ CREATE FUNCTION square_root(double precision) RETURNS double precision that fails as well, the load will fail. + + After the module has been found, PostgreSQL looks for its magic block. + This block contains information about the environment a module was + compiled in. The server uses this to verify the module was compiled + under the same assumptions and environment as the backend. + + The user ID the PostgreSQL server runs as must be able to traverse the path to the file you intend to @@ -1951,6 +1958,26 @@ concat_text(PG_FUNCTION_ARGS) + + + To ensure your module is not loaded into an incompatible backend, it + is recommended to include a magic block. To do this you must include + the header pgmagic.h and declare the block as + follows: + + + +#include "pgmagic.h" + +PG_MODULE_MAGIC; + + + + If the module consists of multiple source files, this only needs to + be done in one of them. + + + Symbol names defined within object files must not conflict diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c index 582c1b0565..5379b89902 100644 --- a/src/backend/utils/fmgr/dfmgr.c +++ b/src/backend/utils/fmgr/dfmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.82 2006/03/05 15:58:46 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.83 2006/05/30 14:09:32 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,7 @@ #include "dynloader.h" #include "miscadmin.h" #include "utils/dynamic_loader.h" - +#include "pgmagic.h" /* * List of dynamically loaded files (kept in malloc'd memory). @@ -60,6 +60,9 @@ static char *find_in_dynamic_libpath(const char *basename); static char *expand_dynamic_library_name(const char *name); static char *substitute_libpath_macro(const char *name); +/* Magic structure that module needs to match to be accepted */ +static Pg_magic_struct magic_data = PG_MODULE_MAGIC_DATA; + /* * Load the specified dynamic-link library file, and look for a function * named funcname in it. (funcname can be NULL to just load the file.) @@ -116,6 +119,7 @@ load_external_function(char *filename, char *funcname, if (file_scanner == NULL) { + PGModuleMagicFunction magic_func; /* * File not loaded yet. */ @@ -146,6 +150,45 @@ load_external_function(char *filename, char *funcname, fullname, load_error))); } + /* Check the magic function to determine compatability */ + magic_func = pg_dlsym( file_scanner->handle, PG_MAGIC_FUNCTION_NAME_STRING ); + if( magic_func ) + { + Pg_magic_struct *module_magic_data = magic_func(); + if( module_magic_data->len != magic_data.len || + memcmp( module_magic_data, &magic_data, magic_data.len ) != 0 ) + { + pg_dlclose( file_scanner->handle ); + + if( module_magic_data->len != magic_data.len ) + ereport(ERROR, + (errmsg("incompatible library \"%s\": Magic block length mismatch", + fullname))); + if( module_magic_data->version != magic_data.version ) + ereport(ERROR, + (errmsg("incompatible library \"%s\": Version mismatch", + fullname), + errdetail("Expected %d.%d, got %d.%d", + magic_data.version/100, magic_data.version % 100, + module_magic_data->version/100, module_magic_data->version % 100))); + + if( module_magic_data->magic != magic_data.magic ) + ereport(ERROR, + (errmsg("incompatible library \"%s\": Magic constant mismatch", + fullname), + errdetail("Expected 0x%08X, got 0x%08X", + magic_data.magic, magic_data.magic))); + /* Should never get here */ + ereport(ERROR,(errmsg("incompatible library \"%s\": Reason unknown", + fullname))); + } + } + else + /* Currently we do not penalize modules for not having a + magic block, it would break every external module in + existance. At some point though... */ + ereport(LOG, (errmsg("external library \"%s\" did not have magic block", fullname ))); + /* OK to link it into list */ if (file_list == NULL) file_list = file_scanner; diff --git a/src/include/pgmagic.h b/src/include/pgmagic.h new file mode 100644 index 0000000000..456804618c --- /dev/null +++ b/src/include/pgmagic.h @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------- + * + * pgmagic.h + * Defines a magic block that can mark a module in a way so show that + * it is compatible with the server it is being loaded into. + * + * This file is intended to be included into modules that wish to load + * themselves into the backend. All they need to do is include this header + * into one of the source files and include the line: + * + * PG_MODULE_MAGIC; + * + * The trailing semi-colon is optional. To work with versions of PostgreSQL + * that do not support this, you may put an #ifdef/endif block around it. + * + * Note, there is space available, particularly in the bitfield part. If it + * turns out that a change has happened within a major release that would + * require all modules to be recompiled, just setting one unused bit there + * will do the trick. + * + * Originally written by Martijn van Oosterhout + * + * $PostgreSQL: pgsql/src/include/pgmagic.h,v 1.1 2006/05/30 14:09:32 momjian Exp $ + * + *------------------------------------------------------------------------- + */ + +#ifndef PGMAGIC_H +#define PGMAGIC_H + +#include "c.h" + +/* The main structure in which the magic is stored. the length field is used + * to detect major changes */ + +typedef struct { + int len; + int version; + int magic; +} Pg_magic_struct; + +/* Declare the module magic function. It needs to be a function as the dlsym + * in the backend is only guarenteed to work on functions, not data */ + +typedef Pg_magic_struct *(*PGModuleMagicFunction) (void); + +#define PG_MAGIC_FUNCTION_NAME Pg_magic_func +#define PG_MAGIC_FUNCTION_NAME_STRING "Pg_magic_func" + +#define PG_MODULE_MAGIC \ +extern DLLIMPORT Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \ +Pg_magic_struct * \ +PG_MAGIC_FUNCTION_NAME(void) \ +{ \ + static Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; \ + return &Pg_magic_data; \ +} + + /* Common user adjustable constants */ +#define PG_MODULE_MAGIC_CONST \ + ((INDEX_MAX_KEYS << 0) + \ + (FUNC_MAX_ARGS << 8) + \ + (NAMEDATALEN << 16)) + +/* Finally, the actual data block */ +#define PG_MODULE_MAGIC_DATA \ +{ \ + sizeof(Pg_magic_struct), \ + PG_VERSION_NUM / 100, /* Major version of postgres */ \ + PG_MODULE_MAGIC_CONST, /* Constants users can configure */ \ +} + +#endif /* PGMAGIC_H */ diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c index 7c58f950cb..0f37f19204 100644 --- a/src/test/regress/regress.c +++ b/src/test/regress/regress.c @@ -1,5 +1,5 @@ /* - * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.65 2006/01/11 20:12:43 tgl Exp $ + * $PostgreSQL: pgsql/src/test/regress/regress.c,v 1.66 2006/05/30 14:09:32 momjian Exp $ */ #include "postgres.h" @@ -9,6 +9,7 @@ #include "utils/geo_decls.h" /* includes */ #include "executor/executor.h" /* For GetAttributeByName */ #include "commands/sequence.h" /* for nextval() */ +#include "pgmagic.h" #define P_MAXDIG 12 #define LDELIM '(' @@ -27,7 +28,7 @@ extern int oldstyle_length(int n, text *t); extern Datum int44in(PG_FUNCTION_ARGS); extern Datum int44out(PG_FUNCTION_ARGS); - +PG_MODULE_MAGIC; /* * Distance from a point to a path */