agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feed[PATCH v1 2/3] Allow building only trusted or untrusted PL/Perl.
5+ messages / 2 participants
[nested] [flat]
* [PATCH v1 2/3] Allow building only trusted or untrusted PL/Perl.
@ 2022-05-17 20:28 Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 0 replies; 5+ messages in thread
From: Nathan Bossart @ 2022-05-17 20:28 UTC (permalink / raw)
Presently, when the --with-perl configuration option is used, both
trusted and untrusted PL/Perl are built. However, some users may
only want to build one or the other. This change introduces an
optional argument that can be used to do so. If
--with-perl='trusted' is specified, only trusted PL/Perl is built.
If --with-perl='untrusted' is specified, only untrusted PL/Perl is
built. If --with-perl is given without an argument, both trusted
and untrusted PL/Perl are built.
---
configure | 47 ++++++++++++++++++++++---
configure.ac | 32 ++++++++++++++++-
contrib/bool_plperl/Makefile | 24 ++++++++++---
contrib/hstore_plperl/Makefile | 24 ++++++++++---
contrib/jsonb_plperl/Makefile | 24 ++++++++++---
doc/src/sgml/installation.sgml | 23 +++++++++++-
src/Makefile.global.in | 2 ++
src/include/pg_config.h.in | 6 ++++
src/pl/plperl/GNUmakefile | 37 +++++++++++++------
src/pl/plperl/expected/plperl_setup.out | 15 +++-----
src/pl/plperl/expected/plperlu.out | 27 ++++++++++++++
src/pl/plperl/plperl.c | 10 ++++++
src/pl/plperl/sql/plperl_setup.sql | 11 +++---
src/pl/plperl/sql/plperlu.sql | 29 +++++++++++++++
14 files changed, 265 insertions(+), 46 deletions(-)
diff --git a/configure b/configure
index 7dec6b7bf9..faa8b1a2e3 100755
--- a/configure
+++ b/configure
@@ -723,6 +723,8 @@ with_krb_srvnam
krb_srvtab
with_gssapi
with_python
+PERL_UNTRUSTED
+PERL_TRUSTED
with_perl
with_tcl
ICU_LIBS
@@ -1563,7 +1565,8 @@ Optional Packages:
--with-icu build with ICU support
--with-tcl build Tcl modules (PL/Tcl)
--with-tclconfig=DIR tclConfig.sh is in DIR
- --with-perl build Perl modules (PL/Perl)
+ --with-perl[=TRUSTWORTHINESS]
+ build Perl modules (PL/Perl)
--with-python build Python modules (PL/Python)
--with-gssapi build with GSSAPI support
--with-krb-srvnam=NAME default service principal name in Kerberos (GSSAPI)
@@ -8152,26 +8155,62 @@ if test "${with_perl+set}" = set; then :
withval=$with_perl;
case $withval in
yes)
- :
+
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=yes
+
;;
no)
:
;;
*)
- as_fn_error $? "no argument expected for --with-perl option" "$LINENO" 5
+ with_perl=yes
+
+ if test "$withval" = trusted ; then
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=yes
+ else
+ as_fn_error $? "invalid --with-perl value: argument must be omitted or specified as 'trusted' or 'untrusted'" "$LINENO" 5
+ fi
+
;;
esac
else
with_perl=no
-
fi
+
+if test "$with_perl" = yes; then
+
+ if test "$PERL_TRUSTED" = yes ; then
+
+$as_echo "#define USE_PERL 1" >>confdefs.h
+
+ fi
+ if test "$PERL_UNTRUSTED" = yes ; then
+
+$as_echo "#define USE_PERLU 1" >>confdefs.h
+
+ fi
+
+else
+
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=no
+
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_perl" >&5
$as_echo "$with_perl" >&6; }
+
+
#
# Optionally build Python modules (PL/Python)
#
diff --git a/configure.ac b/configure.ac
index d093fb88dd..0dd440131b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -823,9 +823,39 @@ PGAC_ARG_REQ(with, tclconfig, [DIR], [tclConfig.sh is in DIR])
# Optionally build Perl modules (PL/Perl)
#
AC_MSG_CHECKING([whether to build Perl modules])
-PGAC_ARG_BOOL(with, perl, no, [build Perl modules (PL/Perl)])
+PGAC_ARG_OPTARG(with, perl,
+[TRUSTWORTHINESS], [build Perl modules (PL/Perl)],
+[
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=yes
+],
+[
+ if test "$withval" = trusted ; then
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=yes
+ else
+ AC_MSG_ERROR([invalid --with-perl value: argument must be omitted or specified as 'trusted' or 'untrusted'])
+ fi
+],
+[
+ if test "$PERL_TRUSTED" = yes ; then
+ AC_DEFINE([USE_PERL], 1, [Define to 1 to build with trusted Perl support. (--with-perl='trusted')])
+ fi
+ if test "$PERL_UNTRUSTED" = yes ; then
+ AC_DEFINE([USE_PERLU], 1, [Define to 1 to build with untrusted Perl support. (--with-perl='untrusted')])
+ fi
+],
+[
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=no
+])
AC_MSG_RESULT([$with_perl])
AC_SUBST(with_perl)
+AC_SUBST(PERL_TRUSTED)
+AC_SUBST(PERL_UNTRUSTED)
#
# Optionally build Python modules (PL/Python)
diff --git a/contrib/bool_plperl/Makefile b/contrib/bool_plperl/Makefile
index efe1de986b..ae23e7e3f1 100644
--- a/contrib/bool_plperl/Makefile
+++ b/contrib/bool_plperl/Makefile
@@ -8,10 +8,12 @@ PGFILEDESC = "bool_plperl - bool transform for plperl"
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl
-EXTENSION = bool_plperlu bool_plperl
-DATA = bool_plperlu--1.0.sql bool_plperl--1.0.sql
-
-REGRESS = bool_plperl bool_plperlu
+# We do not yet know whether we are building with trusted PL/Perl, untrusted
+# PL/Perl, or both, so for now we assume we are just building trusted PL/Perl.
+# We'll adjust these later on if this assumption was accurate.
+EXTENSION = bool_plperl
+DATA = bool_plperl--1.0.sql
+REGRESS = bool_plperl
ifdef USE_PGXS
PG_CONFIG = pg_config
@@ -37,3 +39,17 @@ endif
# As with plperl we need to include the perl_includespec directory last.
override CPPFLAGS := $(CPPFLAGS) $(perl_embed_ccflags) $(perl_includespec)
+
+# Since we now know whether we are building trusted PL/Perl, untrusted PL/Perl,
+# or both, we should adjust the relevant variables accordingly.
+ifeq ($(PERL_TRUSTED), no)
+ override undefine EXTENSION
+ override undefine DATA
+ override undefine REGRESS
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ override EXTENSION += bool_plperlu
+ override DATA += bool_plperlu--1.0.sql
+ override REGRESS += bool_plperlu
+endif
diff --git a/contrib/hstore_plperl/Makefile b/contrib/hstore_plperl/Makefile
index 9065f16408..32af5049a9 100644
--- a/contrib/hstore_plperl/Makefile
+++ b/contrib/hstore_plperl/Makefile
@@ -6,11 +6,13 @@ OBJS = \
hstore_plperl.o
PGFILEDESC = "hstore_plperl - hstore transform for plperl"
+# We do not yet know whether we are building with trusted PL/Perl, untrusted
+# PL/Perl, or both, so for now we assume we are just building trusted PL/Perl.
+# We'll adjust these later on if this assumption was accurate.
+EXTENSION = hstore_plperl
+DATA = hstore_plperl--1.0.sql
+REGRESS = hstore_plperl create_transform
-EXTENSION = hstore_plperl hstore_plperlu
-DATA = hstore_plperl--1.0.sql hstore_plperlu--1.0.sql
-
-REGRESS = hstore_plperl hstore_plperlu create_transform
EXTRA_INSTALL = contrib/hstore
ifdef USE_PGXS
@@ -39,3 +41,17 @@ endif
# As with plperl we need to include the perl_includespec directory last.
override CPPFLAGS := $(CPPFLAGS) $(perl_embed_ccflags) $(perl_includespec)
+
+# Since we now know whether we are building trusted PL/Perl, untrusted PL/Perl,
+# or both, we should adjust the relevant variables accordingly.
+ifeq ($(PERL_TRUSTED), no)
+ override undefine EXTENSION
+ override undefine DATA
+ override undefine REGRESS
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ override EXTENSION += hstore_plperlu
+ override DATA += hstore_plperlu--1.0.sql
+ override REGRESS += hstore_plperlu
+endif
diff --git a/contrib/jsonb_plperl/Makefile b/contrib/jsonb_plperl/Makefile
index ba9480e819..218bd51c9e 100644
--- a/contrib/jsonb_plperl/Makefile
+++ b/contrib/jsonb_plperl/Makefile
@@ -8,10 +8,12 @@ PGFILEDESC = "jsonb_plperl - jsonb transform for plperl"
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl
-EXTENSION = jsonb_plperlu jsonb_plperl
-DATA = jsonb_plperlu--1.0.sql jsonb_plperl--1.0.sql
-
-REGRESS = jsonb_plperl jsonb_plperlu
+# We do not yet know whether we are building with trusted PL/Perl, untrusted
+# PL/Perl, or both, so for now we assume we are just building trusted PL/Perl.
+# We'll adjust these later on if this assumption was accurate.
+EXTENSION = jsonb_plperl
+DATA = jsonb_plperl--1.0.sql
+REGRESS = jsonb_plperl
SHLIB_LINK += $(filter -lm, $(LIBS))
@@ -39,3 +41,17 @@ endif
# As with plperl we need to include the perl_includespec directory last.
override CPPFLAGS := $(CPPFLAGS) $(perl_embed_ccflags) $(perl_includespec)
+
+# Since we now know whether we are building trusted PL/Perl, untrusted PL/Perl,
+# or both, we should adjust the relevant variables accordingly.
+ifeq ($(PERL_TRUSTED), no)
+ override undefine EXTENSION
+ override undefine DATA
+ override undefine REGRESS
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ override EXTENSION += jsonb_plperlu
+ override DATA += jsonb_plperlu--1.0.sql
+ override REGRESS += jsonb_plperlu
+endif
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index c585078029..4377e9d51a 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -873,10 +873,31 @@ build-postgresql:
</varlistentry>
<varlistentry>
- <term><option>--with-perl</option></term>
+ <term><option>--with-perl<optional>=<replaceable>TRUSTWORTHINESS</replaceable></optional></option></term>
<listitem>
<para>
Build the <application>PL/Perl</application> server-side language.
+ <replaceable>TRUSTWORTHINESS</replaceable> is an optional argument and,
+ if provided, must be one of:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <option>trusted</option> to build only trusted
+ <application>PL/Perl</application>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <option>untrusted</option> to build only untrusted
+ <application>PL/Perl</application>
+ (<application>PL/PerlU</application>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ If <replaceable>TRUSTWORTHINESS</replaceable> is not specified, both
+ trusted and untrusted <application>PL/Perl</application> will be built.
</para>
</listitem>
</varlistentry>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 051718e4fe..53f367ca7a 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -526,6 +526,8 @@ GENHTML = @GENHTML@
DEF_PGPORT = @default_port@
WANTED_LANGUAGES = @WANTED_LANGUAGES@
+PERL_TRUSTED = @PERL_TRUSTED@
+PERL_UNTRUSTED = @PERL_UNTRUSTED@
##########################################################################
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index cdd742cb55..2779f5f671 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -931,6 +931,12 @@
/* Define to 1 to build with PAM support. (--with-pam) */
#undef USE_PAM
+/* Define to 1 to build with trusted Perl support. (--with-perl='trusted') */
+#undef USE_PERL
+
+/* Define to 1 to build with untrusted Perl support. (--with-perl='untrusted') */
+#undef USE_PERLU
+
/* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
#undef USE_SLICING_BY_8_CRC32C
diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile
index a2e6410f53..d01201bbac 100644
--- a/src/pl/plperl/GNUmakefile
+++ b/src/pl/plperl/GNUmakefile
@@ -27,8 +27,13 @@ NAME = plperl
OBJS = plperl.o SPI.o Util.o $(WIN32RES)
-DATA = plperl.control plperl--1.0.sql \
- plperlu.control plperlu--1.0.sql
+ifeq ($(PERL_TRUSTED), yes)
+ DATA += plperl.control plperl--1.0.sql
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ DATA += plperlu.control plperlu--1.0.sql
+endif
PERLCHUNKS = plc_perlboot.pl plc_trusted.pl
@@ -56,14 +61,26 @@ endif # win32
SHLIB_LINK = $(perl_embed_ldflags)
REGRESS_OPTS = --dbname=$(PL_TESTDB)
-REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \
- plperl_elog plperl_util plperl_init plperlu plperl_array \
- plperl_call plperl_transaction
-# if Perl can support two interpreters in one backend,
-# test plperl-and-plperlu cases
-ifneq ($(PERL),)
-ifeq ($(shell $(PERL) -V:usemultiplicity), usemultiplicity='define';)
- REGRESS += plperl_plperlu
+
+ifeq ($(PERL_TRUSTED), yes)
+ REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \
+ plperl_elog plperl_util plperl_init plperl_array plperl_call \
+ plperl_transaction
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ REGRESS += plperlu
+endif
+
+ifeq ($(PERL_TRUSTED), yes)
+ifeq ($(PERL_UNTRUSTED), yes)
+ # if Perl can support two interpreters in one backend,
+ # test plperl-and-plperlu cases
+ ifneq ($(PERL),)
+ ifeq ($(shell $(PERL) -V:usemultiplicity), usemultiplicity='define';)
+ REGRESS += plperl_plperlu
+ endif
+ endif
endif
endif
diff --git a/src/pl/plperl/expected/plperl_setup.out b/src/pl/plperl/expected/plperl_setup.out
index 5234febefd..d0682325b2 100644
--- a/src/pl/plperl/expected/plperl_setup.out
+++ b/src/pl/plperl/expected/plperl_setup.out
@@ -1,18 +1,15 @@
--
--- Install the plperl and plperlu extensions
+-- Install plperl
--
-- Before going ahead with the to-be-tested installations, verify that
--- a non-superuser is allowed to install plperl (but not plperlu) when
--- suitable permissions have been granted.
+-- a non-superuser is allowed to install plperl when suitable permissions
+-- have been granted.
CREATE USER regress_user1;
CREATE USER regress_user2;
SET ROLE regress_user1;
CREATE EXTENSION plperl; -- fail
ERROR: permission denied to create extension "plperl"
HINT: Must have CREATE privilege on current database to create this extension.
-CREATE EXTENSION plperlu; -- fail
-ERROR: permission denied to create extension "plperlu"
-HINT: Must be superuser to create this extension.
RESET ROLE;
DO $$
begin
@@ -22,9 +19,6 @@ end;
$$;
SET ROLE regress_user1;
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu; -- fail
-ERROR: permission denied to create extension "plperlu"
-HINT: Must be superuser to create this extension.
CREATE SCHEMA plperl_setup_scratch;
SET search_path = plperl_setup_scratch;
GRANT ALL ON SCHEMA plperl_setup_scratch TO regress_user2;
@@ -68,6 +62,5 @@ RESET ROLE;
DROP OWNED BY regress_user1;
DROP USER regress_user1;
DROP USER regress_user2;
--- Now install the versions that will be used by subsequent test scripts.
+-- Now install the version that will be used by subsequent test scripts.
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu;
diff --git a/src/pl/plperl/expected/plperlu.out b/src/pl/plperl/expected/plperlu.out
index a3edb38497..f759466a90 100644
--- a/src/pl/plperl/expected/plperlu.out
+++ b/src/pl/plperl/expected/plperlu.out
@@ -1,5 +1,32 @@
+-- Before going ahead with the to-be-tested installations, verify that
+-- only superusers can install plperlu.
+CREATE USER regress_user1;
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+ERROR: permission denied to create extension "plperlu"
+HINT: Must be superuser to create this extension.
+RESET ROLE;
+DO $$
+begin
+ execute format('grant create on database %I to regress_user1',
+ current_database());
+end;
+$$;
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+ERROR: permission denied to create extension "plperlu"
+HINT: Must be superuser to create this extension.
+RESET ROLE;
+DO $$
+begin
+ execute format('revoke create on database %I from regress_user1',
+ current_database());
+end;
+$$;
+DROP ROLE regress_user1;
-- Use ONLY plperlu tests here. For plperl/plerlu combined tests
-- see plperl_plperlu.sql
+CREATE EXTENSION plperlu;
-- This test tests setting on_plperlu_init after loading plperl
LOAD 'plperl';
-- Test plperl.on_plperlu_init gets run
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 9bc6793a30..ff5f9641d5 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -445,6 +445,7 @@ _PG_init(void)
* OK since the worst result would be an error. Your code oughta pass
* use_strict anyway ;-)
*/
+#ifdef USE_PERL
DefineCustomStringVariable("plperl.on_plperl_init",
gettext_noop("Perl initialization code to execute once when plperl is first used."),
NULL,
@@ -452,7 +453,9 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
+#endif /* USE_PERL */
+#ifdef USE_PERLU
DefineCustomStringVariable("plperl.on_plperlu_init",
gettext_noop("Perl initialization code to execute once when plperlu is first used."),
NULL,
@@ -460,6 +463,7 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
+#endif /* USE_PERLU */
MarkGUCPrefixReserved("plperl");
@@ -2039,6 +2043,7 @@ plperl_validator_internal(PG_FUNCTION_ARGS, bool trusted)
* There are three externally visible pieces to plperl: plperl_call_handler,
* plperl_inline_handler, and plperl_validator.
*/
+#ifdef USE_PERL
PG_FUNCTION_INFO_V1(plperl_call_handler);
@@ -2064,11 +2069,14 @@ plperl_validator(PG_FUNCTION_ARGS)
return plperl_validator_internal(fcinfo, true);
}
+#endif /* USE_PERL */
+
/*
* plperlu likewise requires three externally visible functions:
* plperlu_call_handler, plperlu_inline_handler, and plperlu_validator.
*/
+#ifdef USE_PERLU
PG_FUNCTION_INFO_V1(plperlu_call_handler);
@@ -2095,6 +2103,8 @@ plperlu_validator(PG_FUNCTION_ARGS)
return plperl_validator_internal(fcinfo, false);
}
+#endif /* USE_PERLU */
+
/*
* Uses mkfunc to create a subroutine whose text is
diff --git a/src/pl/plperl/sql/plperl_setup.sql b/src/pl/plperl/sql/plperl_setup.sql
index a89cf56617..eb29cacdac 100644
--- a/src/pl/plperl/sql/plperl_setup.sql
+++ b/src/pl/plperl/sql/plperl_setup.sql
@@ -1,10 +1,10 @@
--
--- Install the plperl and plperlu extensions
+-- Install plperl
--
-- Before going ahead with the to-be-tested installations, verify that
--- a non-superuser is allowed to install plperl (but not plperlu) when
--- suitable permissions have been granted.
+-- a non-superuser is allowed to install plperl when suitable permissions
+-- have been granted.
CREATE USER regress_user1;
CREATE USER regress_user2;
@@ -12,7 +12,6 @@ CREATE USER regress_user2;
SET ROLE regress_user1;
CREATE EXTENSION plperl; -- fail
-CREATE EXTENSION plperlu; -- fail
RESET ROLE;
@@ -26,7 +25,6 @@ $$;
SET ROLE regress_user1;
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu; -- fail
CREATE SCHEMA plperl_setup_scratch;
SET search_path = plperl_setup_scratch;
GRANT ALL ON SCHEMA plperl_setup_scratch TO regress_user2;
@@ -68,6 +66,5 @@ DROP OWNED BY regress_user1;
DROP USER regress_user1;
DROP USER regress_user2;
--- Now install the versions that will be used by subsequent test scripts.
+-- Now install the version that will be used by subsequent test scripts.
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu;
diff --git a/src/pl/plperl/sql/plperlu.sql b/src/pl/plperl/sql/plperlu.sql
index be43df5d90..1e050e8237 100644
--- a/src/pl/plperl/sql/plperlu.sql
+++ b/src/pl/plperl/sql/plperlu.sql
@@ -1,5 +1,34 @@
+-- Before going ahead with the to-be-tested installations, verify that
+-- only superusers can install plperlu.
+CREATE USER regress_user1;
+
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+RESET ROLE;
+
+DO $$
+begin
+ execute format('grant create on database %I to regress_user1',
+ current_database());
+end;
+$$;
+
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+RESET ROLE;
+
+DO $$
+begin
+ execute format('revoke create on database %I from regress_user1',
+ current_database());
+end;
+$$;
+
+DROP ROLE regress_user1;
+
-- Use ONLY plperlu tests here. For plperl/plerlu combined tests
-- see plperl_plperlu.sql
+CREATE EXTENSION plperlu;
-- This test tests setting on_plperlu_init after loading plperl
LOAD 'plperl';
--
2.25.1
--ReaqsoxgOBHFXBhH
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v1-0003-Allow-building-only-trusted-or-untrusted-PL-Tcl.patch"
^ permalink raw reply [nested|flat] 5+ messages in thread
* [PATCH v1 2/3] Allow building only trusted or untrusted PL/Perl.
@ 2022-05-17 20:28 Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 0 replies; 5+ messages in thread
From: Nathan Bossart @ 2022-05-17 20:28 UTC (permalink / raw)
Presently, when the --with-perl configuration option is used, both
trusted and untrusted PL/Perl are built. However, some users may
only want to build one or the other. This change introduces an
optional argument that can be used to do so. If
--with-perl='trusted' is specified, only trusted PL/Perl is built.
If --with-perl='untrusted' is specified, only untrusted PL/Perl is
built. If --with-perl is given without an argument, both trusted
and untrusted PL/Perl are built.
---
configure | 47 ++++++++++++++++++++++---
configure.ac | 32 ++++++++++++++++-
contrib/bool_plperl/Makefile | 24 ++++++++++---
contrib/hstore_plperl/Makefile | 24 ++++++++++---
contrib/jsonb_plperl/Makefile | 24 ++++++++++---
doc/src/sgml/installation.sgml | 23 +++++++++++-
src/Makefile.global.in | 2 ++
src/include/pg_config.h.in | 6 ++++
src/pl/plperl/GNUmakefile | 37 +++++++++++++------
src/pl/plperl/expected/plperl_setup.out | 15 +++-----
src/pl/plperl/expected/plperlu.out | 27 ++++++++++++++
src/pl/plperl/plperl.c | 10 ++++++
src/pl/plperl/sql/plperl_setup.sql | 11 +++---
src/pl/plperl/sql/plperlu.sql | 29 +++++++++++++++
14 files changed, 265 insertions(+), 46 deletions(-)
diff --git a/configure b/configure
index 7dec6b7bf9..faa8b1a2e3 100755
--- a/configure
+++ b/configure
@@ -723,6 +723,8 @@ with_krb_srvnam
krb_srvtab
with_gssapi
with_python
+PERL_UNTRUSTED
+PERL_TRUSTED
with_perl
with_tcl
ICU_LIBS
@@ -1563,7 +1565,8 @@ Optional Packages:
--with-icu build with ICU support
--with-tcl build Tcl modules (PL/Tcl)
--with-tclconfig=DIR tclConfig.sh is in DIR
- --with-perl build Perl modules (PL/Perl)
+ --with-perl[=TRUSTWORTHINESS]
+ build Perl modules (PL/Perl)
--with-python build Python modules (PL/Python)
--with-gssapi build with GSSAPI support
--with-krb-srvnam=NAME default service principal name in Kerberos (GSSAPI)
@@ -8152,26 +8155,62 @@ if test "${with_perl+set}" = set; then :
withval=$with_perl;
case $withval in
yes)
- :
+
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=yes
+
;;
no)
:
;;
*)
- as_fn_error $? "no argument expected for --with-perl option" "$LINENO" 5
+ with_perl=yes
+
+ if test "$withval" = trusted ; then
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=yes
+ else
+ as_fn_error $? "invalid --with-perl value: argument must be omitted or specified as 'trusted' or 'untrusted'" "$LINENO" 5
+ fi
+
;;
esac
else
with_perl=no
-
fi
+
+if test "$with_perl" = yes; then
+
+ if test "$PERL_TRUSTED" = yes ; then
+
+$as_echo "#define USE_PERL 1" >>confdefs.h
+
+ fi
+ if test "$PERL_UNTRUSTED" = yes ; then
+
+$as_echo "#define USE_PERLU 1" >>confdefs.h
+
+ fi
+
+else
+
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=no
+
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_perl" >&5
$as_echo "$with_perl" >&6; }
+
+
#
# Optionally build Python modules (PL/Python)
#
diff --git a/configure.ac b/configure.ac
index d093fb88dd..0dd440131b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -823,9 +823,39 @@ PGAC_ARG_REQ(with, tclconfig, [DIR], [tclConfig.sh is in DIR])
# Optionally build Perl modules (PL/Perl)
#
AC_MSG_CHECKING([whether to build Perl modules])
-PGAC_ARG_BOOL(with, perl, no, [build Perl modules (PL/Perl)])
+PGAC_ARG_OPTARG(with, perl,
+[TRUSTWORTHINESS], [build Perl modules (PL/Perl)],
+[
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=yes
+],
+[
+ if test "$withval" = trusted ; then
+ PERL_TRUSTED=yes
+ PERL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=yes
+ else
+ AC_MSG_ERROR([invalid --with-perl value: argument must be omitted or specified as 'trusted' or 'untrusted'])
+ fi
+],
+[
+ if test "$PERL_TRUSTED" = yes ; then
+ AC_DEFINE([USE_PERL], 1, [Define to 1 to build with trusted Perl support. (--with-perl='trusted')])
+ fi
+ if test "$PERL_UNTRUSTED" = yes ; then
+ AC_DEFINE([USE_PERLU], 1, [Define to 1 to build with untrusted Perl support. (--with-perl='untrusted')])
+ fi
+],
+[
+ PERL_TRUSTED=no
+ PERL_UNTRUSTED=no
+])
AC_MSG_RESULT([$with_perl])
AC_SUBST(with_perl)
+AC_SUBST(PERL_TRUSTED)
+AC_SUBST(PERL_UNTRUSTED)
#
# Optionally build Python modules (PL/Python)
diff --git a/contrib/bool_plperl/Makefile b/contrib/bool_plperl/Makefile
index efe1de986b..ae23e7e3f1 100644
--- a/contrib/bool_plperl/Makefile
+++ b/contrib/bool_plperl/Makefile
@@ -8,10 +8,12 @@ PGFILEDESC = "bool_plperl - bool transform for plperl"
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl
-EXTENSION = bool_plperlu bool_plperl
-DATA = bool_plperlu--1.0.sql bool_plperl--1.0.sql
-
-REGRESS = bool_plperl bool_plperlu
+# We do not yet know whether we are building with trusted PL/Perl, untrusted
+# PL/Perl, or both, so for now we assume we are just building trusted PL/Perl.
+# We'll adjust these later on if this assumption was accurate.
+EXTENSION = bool_plperl
+DATA = bool_plperl--1.0.sql
+REGRESS = bool_plperl
ifdef USE_PGXS
PG_CONFIG = pg_config
@@ -37,3 +39,17 @@ endif
# As with plperl we need to include the perl_includespec directory last.
override CPPFLAGS := $(CPPFLAGS) $(perl_embed_ccflags) $(perl_includespec)
+
+# Since we now know whether we are building trusted PL/Perl, untrusted PL/Perl,
+# or both, we should adjust the relevant variables accordingly.
+ifeq ($(PERL_TRUSTED), no)
+ override undefine EXTENSION
+ override undefine DATA
+ override undefine REGRESS
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ override EXTENSION += bool_plperlu
+ override DATA += bool_plperlu--1.0.sql
+ override REGRESS += bool_plperlu
+endif
diff --git a/contrib/hstore_plperl/Makefile b/contrib/hstore_plperl/Makefile
index 9065f16408..32af5049a9 100644
--- a/contrib/hstore_plperl/Makefile
+++ b/contrib/hstore_plperl/Makefile
@@ -6,11 +6,13 @@ OBJS = \
hstore_plperl.o
PGFILEDESC = "hstore_plperl - hstore transform for plperl"
+# We do not yet know whether we are building with trusted PL/Perl, untrusted
+# PL/Perl, or both, so for now we assume we are just building trusted PL/Perl.
+# We'll adjust these later on if this assumption was accurate.
+EXTENSION = hstore_plperl
+DATA = hstore_plperl--1.0.sql
+REGRESS = hstore_plperl create_transform
-EXTENSION = hstore_plperl hstore_plperlu
-DATA = hstore_plperl--1.0.sql hstore_plperlu--1.0.sql
-
-REGRESS = hstore_plperl hstore_plperlu create_transform
EXTRA_INSTALL = contrib/hstore
ifdef USE_PGXS
@@ -39,3 +41,17 @@ endif
# As with plperl we need to include the perl_includespec directory last.
override CPPFLAGS := $(CPPFLAGS) $(perl_embed_ccflags) $(perl_includespec)
+
+# Since we now know whether we are building trusted PL/Perl, untrusted PL/Perl,
+# or both, we should adjust the relevant variables accordingly.
+ifeq ($(PERL_TRUSTED), no)
+ override undefine EXTENSION
+ override undefine DATA
+ override undefine REGRESS
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ override EXTENSION += hstore_plperlu
+ override DATA += hstore_plperlu--1.0.sql
+ override REGRESS += hstore_plperlu
+endif
diff --git a/contrib/jsonb_plperl/Makefile b/contrib/jsonb_plperl/Makefile
index ba9480e819..218bd51c9e 100644
--- a/contrib/jsonb_plperl/Makefile
+++ b/contrib/jsonb_plperl/Makefile
@@ -8,10 +8,12 @@ PGFILEDESC = "jsonb_plperl - jsonb transform for plperl"
PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl
-EXTENSION = jsonb_plperlu jsonb_plperl
-DATA = jsonb_plperlu--1.0.sql jsonb_plperl--1.0.sql
-
-REGRESS = jsonb_plperl jsonb_plperlu
+# We do not yet know whether we are building with trusted PL/Perl, untrusted
+# PL/Perl, or both, so for now we assume we are just building trusted PL/Perl.
+# We'll adjust these later on if this assumption was accurate.
+EXTENSION = jsonb_plperl
+DATA = jsonb_plperl--1.0.sql
+REGRESS = jsonb_plperl
SHLIB_LINK += $(filter -lm, $(LIBS))
@@ -39,3 +41,17 @@ endif
# As with plperl we need to include the perl_includespec directory last.
override CPPFLAGS := $(CPPFLAGS) $(perl_embed_ccflags) $(perl_includespec)
+
+# Since we now know whether we are building trusted PL/Perl, untrusted PL/Perl,
+# or both, we should adjust the relevant variables accordingly.
+ifeq ($(PERL_TRUSTED), no)
+ override undefine EXTENSION
+ override undefine DATA
+ override undefine REGRESS
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ override EXTENSION += jsonb_plperlu
+ override DATA += jsonb_plperlu--1.0.sql
+ override REGRESS += jsonb_plperlu
+endif
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index c585078029..4377e9d51a 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -873,10 +873,31 @@ build-postgresql:
</varlistentry>
<varlistentry>
- <term><option>--with-perl</option></term>
+ <term><option>--with-perl<optional>=<replaceable>TRUSTWORTHINESS</replaceable></optional></option></term>
<listitem>
<para>
Build the <application>PL/Perl</application> server-side language.
+ <replaceable>TRUSTWORTHINESS</replaceable> is an optional argument and,
+ if provided, must be one of:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <option>trusted</option> to build only trusted
+ <application>PL/Perl</application>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <option>untrusted</option> to build only untrusted
+ <application>PL/Perl</application>
+ (<application>PL/PerlU</application>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ If <replaceable>TRUSTWORTHINESS</replaceable> is not specified, both
+ trusted and untrusted <application>PL/Perl</application> will be built.
</para>
</listitem>
</varlistentry>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 051718e4fe..53f367ca7a 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -526,6 +526,8 @@ GENHTML = @GENHTML@
DEF_PGPORT = @default_port@
WANTED_LANGUAGES = @WANTED_LANGUAGES@
+PERL_TRUSTED = @PERL_TRUSTED@
+PERL_UNTRUSTED = @PERL_UNTRUSTED@
##########################################################################
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index cdd742cb55..2779f5f671 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -931,6 +931,12 @@
/* Define to 1 to build with PAM support. (--with-pam) */
#undef USE_PAM
+/* Define to 1 to build with trusted Perl support. (--with-perl='trusted') */
+#undef USE_PERL
+
+/* Define to 1 to build with untrusted Perl support. (--with-perl='untrusted') */
+#undef USE_PERLU
+
/* Define to 1 to use software CRC-32C implementation (slicing-by-8). */
#undef USE_SLICING_BY_8_CRC32C
diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile
index a2e6410f53..d01201bbac 100644
--- a/src/pl/plperl/GNUmakefile
+++ b/src/pl/plperl/GNUmakefile
@@ -27,8 +27,13 @@ NAME = plperl
OBJS = plperl.o SPI.o Util.o $(WIN32RES)
-DATA = plperl.control plperl--1.0.sql \
- plperlu.control plperlu--1.0.sql
+ifeq ($(PERL_TRUSTED), yes)
+ DATA += plperl.control plperl--1.0.sql
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ DATA += plperlu.control plperlu--1.0.sql
+endif
PERLCHUNKS = plc_perlboot.pl plc_trusted.pl
@@ -56,14 +61,26 @@ endif # win32
SHLIB_LINK = $(perl_embed_ldflags)
REGRESS_OPTS = --dbname=$(PL_TESTDB)
-REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \
- plperl_elog plperl_util plperl_init plperlu plperl_array \
- plperl_call plperl_transaction
-# if Perl can support two interpreters in one backend,
-# test plperl-and-plperlu cases
-ifneq ($(PERL),)
-ifeq ($(shell $(PERL) -V:usemultiplicity), usemultiplicity='define';)
- REGRESS += plperl_plperlu
+
+ifeq ($(PERL_TRUSTED), yes)
+ REGRESS = plperl_setup plperl plperl_lc plperl_trigger plperl_shared \
+ plperl_elog plperl_util plperl_init plperl_array plperl_call \
+ plperl_transaction
+endif
+
+ifeq ($(PERL_UNTRUSTED), yes)
+ REGRESS += plperlu
+endif
+
+ifeq ($(PERL_TRUSTED), yes)
+ifeq ($(PERL_UNTRUSTED), yes)
+ # if Perl can support two interpreters in one backend,
+ # test plperl-and-plperlu cases
+ ifneq ($(PERL),)
+ ifeq ($(shell $(PERL) -V:usemultiplicity), usemultiplicity='define';)
+ REGRESS += plperl_plperlu
+ endif
+ endif
endif
endif
diff --git a/src/pl/plperl/expected/plperl_setup.out b/src/pl/plperl/expected/plperl_setup.out
index 5234febefd..d0682325b2 100644
--- a/src/pl/plperl/expected/plperl_setup.out
+++ b/src/pl/plperl/expected/plperl_setup.out
@@ -1,18 +1,15 @@
--
--- Install the plperl and plperlu extensions
+-- Install plperl
--
-- Before going ahead with the to-be-tested installations, verify that
--- a non-superuser is allowed to install plperl (but not plperlu) when
--- suitable permissions have been granted.
+-- a non-superuser is allowed to install plperl when suitable permissions
+-- have been granted.
CREATE USER regress_user1;
CREATE USER regress_user2;
SET ROLE regress_user1;
CREATE EXTENSION plperl; -- fail
ERROR: permission denied to create extension "plperl"
HINT: Must have CREATE privilege on current database to create this extension.
-CREATE EXTENSION plperlu; -- fail
-ERROR: permission denied to create extension "plperlu"
-HINT: Must be superuser to create this extension.
RESET ROLE;
DO $$
begin
@@ -22,9 +19,6 @@ end;
$$;
SET ROLE regress_user1;
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu; -- fail
-ERROR: permission denied to create extension "plperlu"
-HINT: Must be superuser to create this extension.
CREATE SCHEMA plperl_setup_scratch;
SET search_path = plperl_setup_scratch;
GRANT ALL ON SCHEMA plperl_setup_scratch TO regress_user2;
@@ -68,6 +62,5 @@ RESET ROLE;
DROP OWNED BY regress_user1;
DROP USER regress_user1;
DROP USER regress_user2;
--- Now install the versions that will be used by subsequent test scripts.
+-- Now install the version that will be used by subsequent test scripts.
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu;
diff --git a/src/pl/plperl/expected/plperlu.out b/src/pl/plperl/expected/plperlu.out
index a3edb38497..f759466a90 100644
--- a/src/pl/plperl/expected/plperlu.out
+++ b/src/pl/plperl/expected/plperlu.out
@@ -1,5 +1,32 @@
+-- Before going ahead with the to-be-tested installations, verify that
+-- only superusers can install plperlu.
+CREATE USER regress_user1;
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+ERROR: permission denied to create extension "plperlu"
+HINT: Must be superuser to create this extension.
+RESET ROLE;
+DO $$
+begin
+ execute format('grant create on database %I to regress_user1',
+ current_database());
+end;
+$$;
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+ERROR: permission denied to create extension "plperlu"
+HINT: Must be superuser to create this extension.
+RESET ROLE;
+DO $$
+begin
+ execute format('revoke create on database %I from regress_user1',
+ current_database());
+end;
+$$;
+DROP ROLE regress_user1;
-- Use ONLY plperlu tests here. For plperl/plerlu combined tests
-- see plperl_plperlu.sql
+CREATE EXTENSION plperlu;
-- This test tests setting on_plperlu_init after loading plperl
LOAD 'plperl';
-- Test plperl.on_plperlu_init gets run
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 9bc6793a30..ff5f9641d5 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -445,6 +445,7 @@ _PG_init(void)
* OK since the worst result would be an error. Your code oughta pass
* use_strict anyway ;-)
*/
+#ifdef USE_PERL
DefineCustomStringVariable("plperl.on_plperl_init",
gettext_noop("Perl initialization code to execute once when plperl is first used."),
NULL,
@@ -452,7 +453,9 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
+#endif /* USE_PERL */
+#ifdef USE_PERLU
DefineCustomStringVariable("plperl.on_plperlu_init",
gettext_noop("Perl initialization code to execute once when plperlu is first used."),
NULL,
@@ -460,6 +463,7 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
+#endif /* USE_PERLU */
MarkGUCPrefixReserved("plperl");
@@ -2039,6 +2043,7 @@ plperl_validator_internal(PG_FUNCTION_ARGS, bool trusted)
* There are three externally visible pieces to plperl: plperl_call_handler,
* plperl_inline_handler, and plperl_validator.
*/
+#ifdef USE_PERL
PG_FUNCTION_INFO_V1(plperl_call_handler);
@@ -2064,11 +2069,14 @@ plperl_validator(PG_FUNCTION_ARGS)
return plperl_validator_internal(fcinfo, true);
}
+#endif /* USE_PERL */
+
/*
* plperlu likewise requires three externally visible functions:
* plperlu_call_handler, plperlu_inline_handler, and plperlu_validator.
*/
+#ifdef USE_PERLU
PG_FUNCTION_INFO_V1(plperlu_call_handler);
@@ -2095,6 +2103,8 @@ plperlu_validator(PG_FUNCTION_ARGS)
return plperl_validator_internal(fcinfo, false);
}
+#endif /* USE_PERLU */
+
/*
* Uses mkfunc to create a subroutine whose text is
diff --git a/src/pl/plperl/sql/plperl_setup.sql b/src/pl/plperl/sql/plperl_setup.sql
index a89cf56617..eb29cacdac 100644
--- a/src/pl/plperl/sql/plperl_setup.sql
+++ b/src/pl/plperl/sql/plperl_setup.sql
@@ -1,10 +1,10 @@
--
--- Install the plperl and plperlu extensions
+-- Install plperl
--
-- Before going ahead with the to-be-tested installations, verify that
--- a non-superuser is allowed to install plperl (but not plperlu) when
--- suitable permissions have been granted.
+-- a non-superuser is allowed to install plperl when suitable permissions
+-- have been granted.
CREATE USER regress_user1;
CREATE USER regress_user2;
@@ -12,7 +12,6 @@ CREATE USER regress_user2;
SET ROLE regress_user1;
CREATE EXTENSION plperl; -- fail
-CREATE EXTENSION plperlu; -- fail
RESET ROLE;
@@ -26,7 +25,6 @@ $$;
SET ROLE regress_user1;
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu; -- fail
CREATE SCHEMA plperl_setup_scratch;
SET search_path = plperl_setup_scratch;
GRANT ALL ON SCHEMA plperl_setup_scratch TO regress_user2;
@@ -68,6 +66,5 @@ DROP OWNED BY regress_user1;
DROP USER regress_user1;
DROP USER regress_user2;
--- Now install the versions that will be used by subsequent test scripts.
+-- Now install the version that will be used by subsequent test scripts.
CREATE EXTENSION plperl;
-CREATE EXTENSION plperlu;
diff --git a/src/pl/plperl/sql/plperlu.sql b/src/pl/plperl/sql/plperlu.sql
index be43df5d90..1e050e8237 100644
--- a/src/pl/plperl/sql/plperlu.sql
+++ b/src/pl/plperl/sql/plperlu.sql
@@ -1,5 +1,34 @@
+-- Before going ahead with the to-be-tested installations, verify that
+-- only superusers can install plperlu.
+CREATE USER regress_user1;
+
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+RESET ROLE;
+
+DO $$
+begin
+ execute format('grant create on database %I to regress_user1',
+ current_database());
+end;
+$$;
+
+SET ROLE regress_user1;
+CREATE EXTENSION plperlu; -- fail
+RESET ROLE;
+
+DO $$
+begin
+ execute format('revoke create on database %I from regress_user1',
+ current_database());
+end;
+$$;
+
+DROP ROLE regress_user1;
+
-- Use ONLY plperlu tests here. For plperl/plerlu combined tests
-- see plperl_plperlu.sql
+CREATE EXTENSION plperlu;
-- This test tests setting on_plperlu_init after loading plperl
LOAD 'plperl';
--
2.25.1
--ReaqsoxgOBHFXBhH
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v1-0003-Allow-building-only-trusted-or-untrusted-PL-Tcl.patch"
^ permalink raw reply [nested|flat] 5+ messages in thread
* [PATCH v1 3/3] Allow building only trusted or untrusted PL/Tcl.
@ 2022-05-18 21:33 Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 0 replies; 5+ messages in thread
From: Nathan Bossart @ 2022-05-18 21:33 UTC (permalink / raw)
Presently, when the --with-tcl configuration option is used, both
trusted and untrusted PL/Tcl are built. However, some users may
only want to build one or the other. This change introduces an
optional argument that can be used to do so. If
--with-tcl='trusted' is specified, only trusted PL/Tcl is built.
If --with-tcl='untrusted' is specified, only untrusted PL/Tcl is
built. If --with-tcl is given without an argument, both trusted
and untrusted PL/Tcl are built.
---
configure | 47 +++++++++++++++++++++++++++++++---
configure.ac | 32 ++++++++++++++++++++++-
doc/src/sgml/installation.sgml | 23 ++++++++++++++++-
src/Makefile.global.in | 2 ++
src/include/pg_config.h.in | 6 +++++
src/pl/tcl/Makefile | 12 ++++++---
src/pl/tcl/pltcl.c | 16 ++++++++++--
7 files changed, 126 insertions(+), 12 deletions(-)
diff --git a/configure b/configure
index faa8b1a2e3..046fc4dcf0 100755
--- a/configure
+++ b/configure
@@ -726,6 +726,8 @@ with_python
PERL_UNTRUSTED
PERL_TRUSTED
with_perl
+TCL_UNTRUSTED
+TCL_TRUSTED
with_tcl
ICU_LIBS
ICU_CFLAGS
@@ -1563,7 +1565,8 @@ Optional Packages:
--with-CC=CMD set compiler (deprecated)
--with-llvm build with LLVM based JIT support
--with-icu build with ICU support
- --with-tcl build Tcl modules (PL/Tcl)
+ --with-tcl[=TRUSTWORTHINESS]
+ build Tcl modules (PL/Tcl)
--with-tclconfig=DIR tclConfig.sh is in DIR
--with-perl[=TRUSTWORTHINESS]
build Perl modules (PL/Perl)
@@ -8097,26 +8100,62 @@ if test "${with_tcl+set}" = set; then :
withval=$with_tcl;
case $withval in
yes)
- :
+
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=yes
+
;;
no)
:
;;
*)
- as_fn_error $? "no argument expected for --with-tcl option" "$LINENO" 5
+ with_tcl=yes
+
+ if test "$withval" = trusted ; then
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=yes
+ else
+ as_fn_error $? "invalid --with-tcl value: argument must be omitted or specified as 'trusted' or 'untrusted'" "$LINENO" 5
+ fi
+
;;
esac
else
with_tcl=no
-
fi
+
+if test "$with_tcl" = yes; then
+
+ if test "$TCL_TRUSTED" = yes ; then
+
+$as_echo "#define USE_TCL 1" >>confdefs.h
+
+ fi
+ if test "$TCL_UNTRUSTED" = yes ; then
+
+$as_echo "#define USE_TCLU 1" >>confdefs.h
+
+ fi
+
+else
+
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=no
+
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_tcl" >&5
$as_echo "$with_tcl" >&6; }
+
+
# We see if the path to the Tcl/Tk configuration scripts is specified.
# This will override the use of tclsh to find the paths to search.
diff --git a/configure.ac b/configure.ac
index 0dd440131b..8e7d96a6fe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -810,9 +810,39 @@ fi
# Optionally build Tcl modules (PL/Tcl)
#
AC_MSG_CHECKING([whether to build with Tcl])
-PGAC_ARG_BOOL(with, tcl, no, [build Tcl modules (PL/Tcl)])
+PGAC_ARG_OPTARG(with, tcl,
+[TRUSTWORTHINESS], [build Tcl modules (PL/Tcl)],
+[
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=yes
+],
+[
+ if test "$withval" = trusted ; then
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=yes
+ else
+ AC_MSG_ERROR([invalid --with-tcl value: argument must be omitted or specified as 'trusted' or 'untrusted'])
+ fi
+],
+[
+ if test "$TCL_TRUSTED" = yes ; then
+ AC_DEFINE([USE_TCL], 1, [Define to 1 to build with trusted Tcl support. (--with-tcl='trusted')])
+ fi
+ if test "$TCL_UNTRUSTED" = yes ; then
+ AC_DEFINE([USE_TCLU], 1, [Define to 1 to build with untrusted Tcl support. (--with-tcl='untrusted')])
+ fi
+],
+[
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=no
+])
AC_MSG_RESULT([$with_tcl])
AC_SUBST([with_tcl])
+AC_SUBST(TCL_TRUSTED)
+AC_SUBST(TCL_UNTRUSTED)
# We see if the path to the Tcl/Tk configuration scripts is specified.
# This will override the use of tclsh to find the paths to search.
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 4377e9d51a..8d96152dd2 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -912,10 +912,31 @@ build-postgresql:
</varlistentry>
<varlistentry>
- <term><option>--with-tcl</option></term>
+ <term><option>--with_tcl<optional>=<replaceable>TRUSTWORTHINESS</replaceable></optional></option></term>
<listitem>
<para>
Build the <application>PL/Tcl</application> server-side language.
+ <replaceable>TRUSTWORTHINESS</replaceable> is an optional argument and,
+ if provided, must be one of:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <option>trusted</option> to build only trusted
+ <application>PL/Tcl</application>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <option>untrusted</option> to build only untrusted
+ <application>PL/Tcl</application>
+ (<application>PL/TclU</application>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ If <replaceable>TRUSTWORTHINESS</replaceable> is not specified, both
+ trusted and untrusted <application>PL/Tcl</application> will be built.
</para>
</listitem>
</varlistentry>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 53f367ca7a..0262ee6d96 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -528,6 +528,8 @@ DEF_PGPORT = @default_port@
WANTED_LANGUAGES = @WANTED_LANGUAGES@
PERL_TRUSTED = @PERL_TRUSTED@
PERL_UNTRUSTED = @PERL_UNTRUSTED@
+TCL_TRUSTED = @TCL_TRUSTED@
+TCL_UNTRUSTED = @TCL_UNTRUSTED@
##########################################################################
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 2779f5f671..c7eb244050 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -955,6 +955,12 @@
/* Define to select SysV-style shared memory. */
#undef USE_SYSV_SHARED_MEMORY
+/* Define to 1 to build with trusted Tcl support. (--with-tcl='trusted') */
+#undef USE_TCL
+
+/* Define to 1 to build with untrusted Tcl support. (--with-tcl='untrusted') */
+#undef USE_TCLU
+
/* Define to select unnamed POSIX semaphores. */
#undef USE_UNNAMED_POSIX_SEMAPHORES
diff --git a/src/pl/tcl/Makefile b/src/pl/tcl/Makefile
index 25e65189b6..842d425969 100644
--- a/src/pl/tcl/Makefile
+++ b/src/pl/tcl/Makefile
@@ -26,11 +26,15 @@ OBJS = \
$(WIN32RES) \
pltcl.o
-DATA = pltcl.control pltcl--1.0.sql \
- pltclu.control pltclu--1.0.sql
+ifeq ($(TCL_TRUSTED), yes)
+ DATA += pltcl.control pltcl--1.0.sql
+ REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=pltcl
+ REGRESS = pltcl_setup pltcl_queries pltcl_trigger pltcl_call pltcl_start_proc pltcl_subxact pltcl_unicode pltcl_transaction
+endif
-REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=pltcl
-REGRESS = pltcl_setup pltcl_queries pltcl_trigger pltcl_call pltcl_start_proc pltcl_subxact pltcl_unicode pltcl_transaction
+ifeq ($(TCL_UNTRUSTED), yes)
+ DATA += pltclu.control pltclu--1.0.sql
+endif
# Tcl on win32 ships with import libraries only for Microsoft Visual C++,
# which are not compatible with mingw gcc. Therefore we need to build a
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 0dd6d8ab2c..57c5fc79fb 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -459,6 +459,7 @@ _PG_init(void)
/************************************************************
* Define PL/Tcl's custom GUCs
************************************************************/
+#ifdef USE_TCL
DefineCustomStringVariable("pltcl.start_proc",
gettext_noop("PL/Tcl function to call once when pltcl is first used."),
NULL,
@@ -466,6 +467,10 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
+ MarkGUCPrefixReserved("pltcl");
+#endif /* USE_TCL */
+
+#ifdef USE_TCLU
DefineCustomStringVariable("pltclu.start_proc",
gettext_noop("PL/TclU function to call once when pltclu is first used."),
NULL,
@@ -473,9 +478,8 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
-
- MarkGUCPrefixReserved("pltcl");
MarkGUCPrefixReserved("pltclu");
+#endif /* USE_TCLU */
pltcl_pm_init_done = true;
}
@@ -690,6 +694,8 @@ start_proc_error_callback(void *arg)
* call this function for execution of
* PL/Tcl procedures.
**********************************************************************/
+#ifdef USE_TCL
+
PG_FUNCTION_INFO_V1(pltcl_call_handler);
/* keep non-static */
@@ -699,6 +705,10 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
return pltcl_handler(fcinfo, true);
}
+#endif /* USE_TCL */
+
+#ifdef USE_TCLU
+
/*
* Alternative handler for unsafe functions
*/
@@ -711,6 +721,8 @@ pltclu_call_handler(PG_FUNCTION_ARGS)
return pltcl_handler(fcinfo, false);
}
+#endif /* USE_TCLU */
+
/**********************************************************************
* pltcl_handler() - Handler for function and trigger calls, for
--
2.25.1
--ReaqsoxgOBHFXBhH--
^ permalink raw reply [nested|flat] 5+ messages in thread
* [PATCH v1 3/3] Allow building only trusted or untrusted PL/Tcl.
@ 2022-05-18 21:33 Nathan Bossart <nathandbossart@gmail.com>
0 siblings, 0 replies; 5+ messages in thread
From: Nathan Bossart @ 2022-05-18 21:33 UTC (permalink / raw)
Presently, when the --with-tcl configuration option is used, both
trusted and untrusted PL/Tcl are built. However, some users may
only want to build one or the other. This change introduces an
optional argument that can be used to do so. If
--with-tcl='trusted' is specified, only trusted PL/Tcl is built.
If --with-tcl='untrusted' is specified, only untrusted PL/Tcl is
built. If --with-tcl is given without an argument, both trusted
and untrusted PL/Tcl are built.
---
configure | 47 +++++++++++++++++++++++++++++++---
configure.ac | 32 ++++++++++++++++++++++-
doc/src/sgml/installation.sgml | 23 ++++++++++++++++-
src/Makefile.global.in | 2 ++
src/include/pg_config.h.in | 6 +++++
src/pl/tcl/Makefile | 12 ++++++---
src/pl/tcl/pltcl.c | 16 ++++++++++--
7 files changed, 126 insertions(+), 12 deletions(-)
diff --git a/configure b/configure
index faa8b1a2e3..046fc4dcf0 100755
--- a/configure
+++ b/configure
@@ -726,6 +726,8 @@ with_python
PERL_UNTRUSTED
PERL_TRUSTED
with_perl
+TCL_UNTRUSTED
+TCL_TRUSTED
with_tcl
ICU_LIBS
ICU_CFLAGS
@@ -1563,7 +1565,8 @@ Optional Packages:
--with-CC=CMD set compiler (deprecated)
--with-llvm build with LLVM based JIT support
--with-icu build with ICU support
- --with-tcl build Tcl modules (PL/Tcl)
+ --with-tcl[=TRUSTWORTHINESS]
+ build Tcl modules (PL/Tcl)
--with-tclconfig=DIR tclConfig.sh is in DIR
--with-perl[=TRUSTWORTHINESS]
build Perl modules (PL/Perl)
@@ -8097,26 +8100,62 @@ if test "${with_tcl+set}" = set; then :
withval=$with_tcl;
case $withval in
yes)
- :
+
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=yes
+
;;
no)
:
;;
*)
- as_fn_error $? "no argument expected for --with-tcl option" "$LINENO" 5
+ with_tcl=yes
+
+ if test "$withval" = trusted ; then
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=yes
+ else
+ as_fn_error $? "invalid --with-tcl value: argument must be omitted or specified as 'trusted' or 'untrusted'" "$LINENO" 5
+ fi
+
;;
esac
else
with_tcl=no
-
fi
+
+if test "$with_tcl" = yes; then
+
+ if test "$TCL_TRUSTED" = yes ; then
+
+$as_echo "#define USE_TCL 1" >>confdefs.h
+
+ fi
+ if test "$TCL_UNTRUSTED" = yes ; then
+
+$as_echo "#define USE_TCLU 1" >>confdefs.h
+
+ fi
+
+else
+
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=no
+
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_tcl" >&5
$as_echo "$with_tcl" >&6; }
+
+
# We see if the path to the Tcl/Tk configuration scripts is specified.
# This will override the use of tclsh to find the paths to search.
diff --git a/configure.ac b/configure.ac
index 0dd440131b..8e7d96a6fe 100644
--- a/configure.ac
+++ b/configure.ac
@@ -810,9 +810,39 @@ fi
# Optionally build Tcl modules (PL/Tcl)
#
AC_MSG_CHECKING([whether to build with Tcl])
-PGAC_ARG_BOOL(with, tcl, no, [build Tcl modules (PL/Tcl)])
+PGAC_ARG_OPTARG(with, tcl,
+[TRUSTWORTHINESS], [build Tcl modules (PL/Tcl)],
+[
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=yes
+],
+[
+ if test "$withval" = trusted ; then
+ TCL_TRUSTED=yes
+ TCL_UNTRUSTED=no
+ elif test "$withval" = untrusted ; then
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=yes
+ else
+ AC_MSG_ERROR([invalid --with-tcl value: argument must be omitted or specified as 'trusted' or 'untrusted'])
+ fi
+],
+[
+ if test "$TCL_TRUSTED" = yes ; then
+ AC_DEFINE([USE_TCL], 1, [Define to 1 to build with trusted Tcl support. (--with-tcl='trusted')])
+ fi
+ if test "$TCL_UNTRUSTED" = yes ; then
+ AC_DEFINE([USE_TCLU], 1, [Define to 1 to build with untrusted Tcl support. (--with-tcl='untrusted')])
+ fi
+],
+[
+ TCL_TRUSTED=no
+ TCL_UNTRUSTED=no
+])
AC_MSG_RESULT([$with_tcl])
AC_SUBST([with_tcl])
+AC_SUBST(TCL_TRUSTED)
+AC_SUBST(TCL_UNTRUSTED)
# We see if the path to the Tcl/Tk configuration scripts is specified.
# This will override the use of tclsh to find the paths to search.
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 4377e9d51a..8d96152dd2 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -912,10 +912,31 @@ build-postgresql:
</varlistentry>
<varlistentry>
- <term><option>--with-tcl</option></term>
+ <term><option>--with_tcl<optional>=<replaceable>TRUSTWORTHINESS</replaceable></optional></option></term>
<listitem>
<para>
Build the <application>PL/Tcl</application> server-side language.
+ <replaceable>TRUSTWORTHINESS</replaceable> is an optional argument and,
+ if provided, must be one of:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <option>trusted</option> to build only trusted
+ <application>PL/Tcl</application>
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <option>untrusted</option> to build only untrusted
+ <application>PL/Tcl</application>
+ (<application>PL/TclU</application>)
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ If <replaceable>TRUSTWORTHINESS</replaceable> is not specified, both
+ trusted and untrusted <application>PL/Tcl</application> will be built.
</para>
</listitem>
</varlistentry>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 53f367ca7a..0262ee6d96 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -528,6 +528,8 @@ DEF_PGPORT = @default_port@
WANTED_LANGUAGES = @WANTED_LANGUAGES@
PERL_TRUSTED = @PERL_TRUSTED@
PERL_UNTRUSTED = @PERL_UNTRUSTED@
+TCL_TRUSTED = @TCL_TRUSTED@
+TCL_UNTRUSTED = @TCL_UNTRUSTED@
##########################################################################
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 2779f5f671..c7eb244050 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -955,6 +955,12 @@
/* Define to select SysV-style shared memory. */
#undef USE_SYSV_SHARED_MEMORY
+/* Define to 1 to build with trusted Tcl support. (--with-tcl='trusted') */
+#undef USE_TCL
+
+/* Define to 1 to build with untrusted Tcl support. (--with-tcl='untrusted') */
+#undef USE_TCLU
+
/* Define to select unnamed POSIX semaphores. */
#undef USE_UNNAMED_POSIX_SEMAPHORES
diff --git a/src/pl/tcl/Makefile b/src/pl/tcl/Makefile
index 25e65189b6..842d425969 100644
--- a/src/pl/tcl/Makefile
+++ b/src/pl/tcl/Makefile
@@ -26,11 +26,15 @@ OBJS = \
$(WIN32RES) \
pltcl.o
-DATA = pltcl.control pltcl--1.0.sql \
- pltclu.control pltclu--1.0.sql
+ifeq ($(TCL_TRUSTED), yes)
+ DATA += pltcl.control pltcl--1.0.sql
+ REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=pltcl
+ REGRESS = pltcl_setup pltcl_queries pltcl_trigger pltcl_call pltcl_start_proc pltcl_subxact pltcl_unicode pltcl_transaction
+endif
-REGRESS_OPTS = --dbname=$(PL_TESTDB) --load-extension=pltcl
-REGRESS = pltcl_setup pltcl_queries pltcl_trigger pltcl_call pltcl_start_proc pltcl_subxact pltcl_unicode pltcl_transaction
+ifeq ($(TCL_UNTRUSTED), yes)
+ DATA += pltclu.control pltclu--1.0.sql
+endif
# Tcl on win32 ships with import libraries only for Microsoft Visual C++,
# which are not compatible with mingw gcc. Therefore we need to build a
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 0dd6d8ab2c..57c5fc79fb 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -459,6 +459,7 @@ _PG_init(void)
/************************************************************
* Define PL/Tcl's custom GUCs
************************************************************/
+#ifdef USE_TCL
DefineCustomStringVariable("pltcl.start_proc",
gettext_noop("PL/Tcl function to call once when pltcl is first used."),
NULL,
@@ -466,6 +467,10 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
+ MarkGUCPrefixReserved("pltcl");
+#endif /* USE_TCL */
+
+#ifdef USE_TCLU
DefineCustomStringVariable("pltclu.start_proc",
gettext_noop("PL/TclU function to call once when pltclu is first used."),
NULL,
@@ -473,9 +478,8 @@ _PG_init(void)
NULL,
PGC_SUSET, 0,
NULL, NULL, NULL);
-
- MarkGUCPrefixReserved("pltcl");
MarkGUCPrefixReserved("pltclu");
+#endif /* USE_TCLU */
pltcl_pm_init_done = true;
}
@@ -690,6 +694,8 @@ start_proc_error_callback(void *arg)
* call this function for execution of
* PL/Tcl procedures.
**********************************************************************/
+#ifdef USE_TCL
+
PG_FUNCTION_INFO_V1(pltcl_call_handler);
/* keep non-static */
@@ -699,6 +705,10 @@ pltcl_call_handler(PG_FUNCTION_ARGS)
return pltcl_handler(fcinfo, true);
}
+#endif /* USE_TCL */
+
+#ifdef USE_TCLU
+
/*
* Alternative handler for unsafe functions
*/
@@ -711,6 +721,8 @@ pltclu_call_handler(PG_FUNCTION_ARGS)
return pltcl_handler(fcinfo, false);
}
+#endif /* USE_TCLU */
+
/**********************************************************************
* pltcl_handler() - Handler for function and trigger calls, for
--
2.25.1
--ReaqsoxgOBHFXBhH--
^ permalink raw reply [nested|flat] 5+ messages in thread
* [PATCH v4 2/8] Address space reservation for shared memory
@ 2024-10-16 18:21 Dmitrii Dolgov <9erthalion6@gmail.com>
0 siblings, 0 replies; 5+ messages in thread
From: Dmitrii Dolgov @ 2024-10-16 18:21 UTC (permalink / raw)
Currently the kernel is responsible to chose an address, where to place each
shared memory mapping, which is the lowest possible address that do not clash
with any other mappings. This is considered to be the most portable approach,
but one of the downsides is that there is no place to resize allocated mappings
anymore. Here is how it looks like for one mapping in /proc/$PID/maps,
/dev/zero represents the anonymous shared memory we talk about:
00400000-00490000 /path/bin/postgres
...
012d9000-0133e000 [heap]
7f443a800000-7f470a800000 /dev/zero (deleted)
7f470a800000-7f471831d000 /usr/lib/locale/locale-archive
7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2
...
7f471aef2000-7f471aef9000 /dev/shm/PostgreSQL.3859891842
7f471aef9000-7f471aefa000 /SYSV007dbf7d (deleted)
By specifying the mapping address directly it's possible to place the
mapping in a way that leaves room for resizing. The idea is:
* To reserve some address space via mmap'ing a large chunk of memory
with PROT_NONE and MAP_NORESERVE. This way we prepare a playground for
preparing shared memory layout without risking anything interfering
with that.
* To slice the reserved space up into sections, one to use for each
shared segment.
* Allocate shared memory segments out of corresponding slices and
leaving unclaimed space in between them. This is implemented via
mmap'ing memory at a specified address from the reserved space with
MAP_FIXED.
The result looks like this:
012d9000-0133e000 [heap]
7f443a800000-7f444196c000 /dev/zero (deleted)
7f444196c000-7f470a800000 # reserved space
7f470a800000-7f471831d000 /usr/lib/locale/locale-archive
7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2
Things like address space randomization should not be a problem in this
context, since the randomization is applied to the mmap base, which is
one per process.
This approach also do not impact the actual memory usage as reported by
the kernel. Here is the output of /proc/$PID/status for the master
version with shared_buffers = 128 MB:
// Peak virtual memory size, which is described as total pages
// mapped in mm_struct. It corresponds to the mapped reserved space
// and is the only number that grows with it.
VmPeak: 2043192 kB
// Size of memory portions. It contains RssAnon + RssFile + RssShmem
VmRSS: 22908 kB
// Size of resident anonymous memory
RssAnon: 768 kB
// Size of resident file mappings
RssFile: 10364 kB
// Size of resident shmem memory (includes SysV shm, mapping of tmpfs and
// shared anonymous mappings)
RssShmem: 11776 kB
Here is the same for the patch when reserving 20GB of space:
VmPeak: 21250648 kB
VmRSS: 22948 kB
RssAnon: 768 kB
RssFile: 10404 kB
RssShmem: 11776 kB
Cgroup v2 doesn't have any problems with that as well. To verify a new cgroup
was created with the memory limit 256 MB, then PostgreSQL was launched withing
this cgroup with shared_buffers = 128 MB:
$ cd /sys/fs/cgroup
$ mkdir postgres
$ cd postres
$ echo 268435456 > memory.max
$ echo $MASTER_PID_SHELL > cgroup.procs
# postgres from the master branch has being successfully launched
# from that shell
$ cat memory.current
17465344 (~16.6 MB)
# stop postgres
$ echo $PATCH_PID_SHELL > cgroup.procs
# postgres from the patch has being successfully launched from that shell
$ cat memory.current
17637376 (~16.8 MB)
To control the amount of space reserved a new GUC max_available_memory
is introduced. Ideally it should be based on the maximum available
memory, hense the name.
---
src/backend/port/sysv_shmem.c | 284 ++++++++++++++++++++++++----
src/backend/port/win32_shmem.c | 2 +-
src/backend/storage/ipc/ipci.c | 5 +-
src/backend/utils/init/globals.c | 1 +
src/backend/utils/misc/guc_tables.c | 14 ++
src/include/storage/pg_shmem.h | 4 +-
6 files changed, 271 insertions(+), 39 deletions(-)
diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c
index 56af0231d24..a0f03ff868f 100644
--- a/src/backend/port/sysv_shmem.c
+++ b/src/backend/port/sysv_shmem.c
@@ -108,6 +108,66 @@ static AnonymousMapping Mappings[ANON_MAPPINGS];
/* Keeps track of used mapping segments */
static int next_free_segment = 0;
+/*
+ * Anonymous mapping placing (/dev/zero (deleted) below) looks like this:
+ *
+ * 00400000-00490000 /path/bin/postgres
+ * ...
+ * 012d9000-0133e000 [heap]
+ * 7f443a800000-7f470a800000 /dev/zero (deleted)
+ * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive
+ * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2
+ * ...
+ * 7f471aef2000-7f471aef9000 /dev/shm/PostgreSQL.3859891842
+ * 7f471aef9000-7f471aefa000 /SYSV007dbf7d (deleted)
+ * ...
+ *
+ * We would like to place multiple mappings in such a way, that there will be
+ * enough space between them in the address space to be able to resize up to
+ * certain size, but without counting towards the total memory consumption.
+ *
+ * To achieve that we first reserve some shared memory address space by
+ * mmap'ing a segment of MaxAvailableMemory size with PROT_NONE and
+ * MAP_NORESERVE (these flags allow to make sure this space will not be used by
+ * anything else, yet do not count against memory limits). Having the reserved
+ * space, we allocate out of it actual chunks of shared memory as usual,
+ * updating a pointer to the current available reserved space for the next
+ * allocation with the gap between segments in mind.
+ *
+ * The result would look like this:
+ *
+ * 012d9000-0133e000 [heap]
+ * 7f4426f54000-7f442e010000 /dev/zero (deleted)
+ * 7f442e010000-7f443a800000 # reserved empty space
+ * 7f443a800000-7f444196c000 /dev/zero (deleted)
+ * 7f444196c000-7f470a800000 # reserved empty space
+ * 7f470a800000-7f471831d000 /usr/lib/locale/locale-archive
+ * 7f4718400000-7f4718401000 /usr/lib64/libicudata.so.74.2
+ * [...]
+ *
+ * The reserved space pointer is calculated to slice up the total reserved
+ * space into fixed fractions of address space for each segment, as specified
+ * in the SHMEM_RESIZE_RATIO array.
+ */
+static double SHMEM_RESIZE_RATIO[1] = {
+ 1.0, /* MAIN_SHMEM_SLOT */
+};
+
+/*
+ * Offset from the beginning of the reserved space, which indicates currently
+ * available range. New shared memory segments have to be allocated at this
+ * offset related to the reserved space.
+ */
+static Size reserved_offset = 0;
+
+/*
+ * Flag telling that we have decided to use huge pages.
+ *
+ * XXX: It's possible to use GetConfigOption("huge_pages_status", false, false)
+ * instead, but it feels like an overkill.
+ */
+static bool huge_pages_on = false;
+
static void *InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size);
static void IpcMemoryDetach(int status, Datum shmaddr);
static void IpcMemoryDelete(int status, Datum shmId);
@@ -626,39 +686,198 @@ check_huge_page_size(int *newval, void **extra, GucSource source)
*
* This function will modify mapping size to the actual size of the allocation,
* if it ends up allocating a segment that is larger than requested.
+ *
+ * Note that we do not switch from huge pages to regular pages in this
+ * function, this decision was already made in ReserveAnonymousMemory and we
+ * stick to it.
*/
static void
-CreateAnonymousSegment(AnonymousMapping *mapping)
+CreateAnonymousSegment(AnonymousMapping *mapping, Pointer base)
{
Size allocsize = mapping->shmem_size;
void *ptr = MAP_FAILED;
int mmap_errno = 0;
+ int mmap_flags = PG_MMAP_FLAGS;
#ifndef MAP_HUGETLB
- /* PGSharedMemoryCreate should have dealt with this case */
- Assert(huge_pages != HUGE_PAGES_ON);
+ /* ReserveAnonymousMemory should have dealt with this case */
+ Assert(huge_pages != HUGE_PAGES_ON && !huge_pages_on);
#else
- if (huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY)
+ if (huge_pages_on)
{
- /*
- * Round up the request size to a suitable large value.
- */
Size hugepagesize;
- int mmap_flags;
+ /* Make sure nothing is messed up */
+ Assert(huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY);
+
+ /* Round up the request size to a suitable large value */
GetHugePageSize(&hugepagesize, &mmap_flags);
if (allocsize % hugepagesize != 0)
allocsize += hugepagesize - (allocsize % hugepagesize);
+ mmap_flags = PG_MMAP_FLAGS | mmap_flags;
+ }
+#endif
+
+ elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p",
+ MappingName(mapping->shmem_segment), allocsize, base + reserved_offset);
+
+ /*
+ * Try to create mapping at an address out of the reserved range, which
+ * will allow to extend it later. Use reserved_offset to allocate the
+ * segment, then update currently available reserved range.
+ *
+ * If the last step has failed, fallback to the regular mapping
+ * creation and signal that shared buffers could not be resized without
+ * a restart.
+ */
+ ptr = mmap(base + reserved_offset, allocsize, PROT_READ | PROT_WRITE,
+ mmap_flags | MAP_FIXED, -1, 0);
+ mmap_errno = errno;
+
+ if (ptr == MAP_FAILED)
+ {
+ DebugMappings();
+ elog(DEBUG1, "segment[%s]: mmap(%zu) at address %p failed: %m, "
+ "fallback to the non-resizable allocation",
+ MappingName(mapping->shmem_segment), allocsize, base + reserved_offset);
+
ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
- PG_MMAP_FLAGS | mmap_flags, -1, 0);
+ PG_MMAP_FLAGS, -1, 0);
+ mmap_errno = errno;
+ }
+ else
+ {
+ Size total_reserved = (Size) MaxAvailableMemory * BLCKSZ;
+
+ reserved_offset += total_reserved * SHMEM_RESIZE_RATIO[next_free_segment];
+ }
+
+ if (ptr == MAP_FAILED)
+ {
+ errno = mmap_errno;
+ DebugMappings();
+ ereport(FATAL,
+ (errmsg("segment[%s]: could not map anonymous shared memory: %m",
+ MappingName(mapping->shmem_segment)),
+ (mmap_errno == ENOMEM) ?
+ errhint("This error usually means that PostgreSQL's request "
+ "for a shared memory segment exceeded available memory, "
+ "swap space, or huge pages. To reduce the request size "
+ "(currently %zu bytes), reduce PostgreSQL's shared "
+ "memory usage, perhaps by reducing \"shared_buffers\" or "
+ "\"max_connections\".",
+ allocsize) : 0));
+ }
+
+ mapping->shmem = ptr;
+ mapping->shmem_size = allocsize;
+}
+
+/*
+ * ReserveAnonymousMemory
+ *
+ * Reserve shared memory address space, from which shared memory segments are
+ * going to be sliced out. The goal of this exercise is to support segments
+ * resizing, for which we need a reserved space free of potential clashes with
+ * other mmap'd areas that are not under our control. Reservation is done via
+ * mmap, and will not allocate any memory until it will be actually used, and
+ * MAP_NORESERVE allows to make it not counting againt kernel reservation
+ * limits (e.g. in cgroups or for huge pages). Do not get confused because of
+ * MAP_NORESERVE -- we need to reserve some space, but not the actual memory,
+ * and that is that this flag is about.
+ *
+ * Note, that with MAP_NORESERVE a reservation with hugetlb will succeed even
+ * if there is actually not enough huge pages. Hence this function is
+ * responsible for deciding whether to use huge pages or not. To achieve that
+ * we need to probe first and try to allocate needed memory for all segments --
+ * if this succeeds, we unmap the probe segment and use hugetlb; if it fails,
+ * we proceed with the regular memory.
+ */
+void *
+ReserveAnonymousMemory(Size reserve_size)
+{
+ Size allocsize = reserve_size;
+ void *ptr = MAP_FAILED;
+ int mmap_errno = 0;
+
+ /* Complain if hugepages demanded but we can't possibly support them */
+#if !defined(MAP_HUGETLB)
+ if (huge_pages == HUGE_PAGES_ON)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("huge pages not supported on this platform")));
+#else
+ if (huge_pages == HUGE_PAGES_ON || huge_pages == HUGE_PAGES_TRY)
+ {
+ Size hugepagesize, total_size = 0;
+ int mmap_flags;
+
+ GetHugePageSize(&hugepagesize, &mmap_flags);
+
+ /*
+ * Figure out how much memory is needed for all segments, keeping in
+ * mind that for every segment this value will be rounding up by the
+ * huge page size. The resulting value will be used to probe memory and
+ * decide whether we will allocate huge pages or not.
+ *
+ * We could actually have a mix and match of segments with and without
+ * huge pages. But in that case we need to have multiple reservation
+ * spaces to use corresponding memory (hugetlb adress space reserved
+ * for hugetlb segments, regular memory for others), and it doesn't
+ * seem to worth the complexity for now.
+ */
+ for(int segment = 0; segment < ANON_MAPPINGS; segment++)
+ {
+ int numSemas;
+ Size segment_size = CalculateShmemSize(&numSemas, segment);
+
+ if (segment_size % hugepagesize != 0)
+ segment_size += hugepagesize - (segment_size % hugepagesize);
+
+ total_size += segment_size;
+ }
+
+ /* Map total amount of memory to test its availability. */
+ elog(DEBUG1, "reserving space: probe mmap(%zu) with MAP_HUGETLB",
+ total_size);
+ ptr = mmap(NULL, total_size, PROT_NONE,
+ PG_MMAP_FLAGS | MAP_ANONYMOUS | mmap_flags, -1, 0);
mmap_errno = errno;
if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
{
- DebugMappings();
- elog(DEBUG1, "segment[%s]: mmap(%zu) with MAP_HUGETLB failed, huge pages disabled: %m",
- MappingName(mapping->shmem_segment), allocsize);
+ /* No huge pages, we will go with the regular page size */
+ elog(DEBUG1, "reserving space: probe mmap(%zu) with MAP_HUGETLB "
+ "failed, huge pages disabled: %m", total_size);
+ }
+ else
+ {
+ /*
+ * All fine, unmap the temporary segment and proceed with reserving
+ * using huge pages.
+ */
+ if (munmap(ptr, total_size) < 0)
+ elog(LOG, "reservice space: munmap(%p, %zu) failed: %m",
+ ptr, total_size);
+
+ /* Round up the requested size to a suitable large value. */
+ if (allocsize % hugepagesize != 0)
+ allocsize += hugepagesize - (allocsize % hugepagesize);
+
+ elog(DEBUG1, "reserving space: mmap(%zu) with MAP_HUGETLB",
+ allocsize);
+ ptr = mmap(NULL, allocsize, PROT_NONE,
+ PG_MMAP_FLAGS | MAP_ANONYMOUS | MAP_NORESERVE | mmap_flags,
+ -1, 0);
+ mmap_errno = errno;
+
+ /* This should not happen, but handle errors anyway */
+ if (huge_pages == HUGE_PAGES_TRY && ptr == MAP_FAILED)
+ {
+ elog(DEBUG1, "reserving space: mmap(%zu) with MAP_HUGETLB "
+ "failed, huge pages disabled: %m", allocsize);
+ }
}
}
#endif
@@ -666,10 +885,12 @@ CreateAnonymousSegment(AnonymousMapping *mapping)
/*
* Report whether huge pages are in use. This needs to be tracked before
* the second mmap() call if attempting to use huge pages failed
- * previously.
+ * previously. At this point ptr is either pointing to the probe segment,
+ * if we couldn't mmap it, or the reservation space.
*/
SetConfigOption("huge_pages_status", (ptr == MAP_FAILED) ? "off" : "on",
PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);
+ huge_pages_on = ptr != MAP_FAILED;
if (ptr == MAP_FAILED && huge_pages != HUGE_PAGES_ON)
{
@@ -677,10 +898,11 @@ CreateAnonymousSegment(AnonymousMapping *mapping)
* Use the original size, not the rounded-up value, when falling back
* to non-huge pages.
*/
- allocsize = mapping->shmem_size;
- ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE,
- PG_MMAP_FLAGS, -1, 0);
- mmap_errno = errno;
+ allocsize = reserve_size;
+
+ elog(DEBUG1, "reserving space: mmap(%zu)", allocsize);
+ ptr = mmap(NULL, allocsize, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
}
if (ptr == MAP_FAILED)
@@ -688,20 +910,18 @@ CreateAnonymousSegment(AnonymousMapping *mapping)
errno = mmap_errno;
DebugMappings();
ereport(FATAL,
- (errmsg("segment[%s]: could not map anonymous shared memory: %m",
- MappingName(mapping->shmem_segment)),
+ (errmsg("reserving space: could not map anonymous shared "
+ "memory: %m"),
(mmap_errno == ENOMEM) ?
errhint("This error usually means that PostgreSQL's request "
- "for a shared memory segment exceeded available memory, "
- "swap space, or huge pages. To reduce the request size "
- "(currently %zu bytes), reduce PostgreSQL's shared "
- "memory usage, perhaps by reducing \"shared_buffers\" or "
- "\"max_connections\".",
+ "for a reserved shared memory address space exceeded "
+ "available memory, swap space, or huge pages. To "
+ "reduce the request reservation size (currently %zu "
+ "bytes), reduce PostgreSQL's \"maximum_shared_buffers\".",
allocsize) : 0));
}
- mapping->shmem = ptr;
- mapping->shmem_size = allocsize;
+ return ptr;
}
/*
@@ -740,7 +960,7 @@ AnonymousShmemDetach(int status, Datum arg)
*/
PGShmemHeader *
PGSharedMemoryCreate(Size size,
- PGShmemHeader **shim)
+ PGShmemHeader **shim, Pointer base)
{
IpcMemoryKey NextShmemSegID;
void *memAddress;
@@ -760,14 +980,6 @@ PGSharedMemoryCreate(Size size,
errmsg("could not stat data directory \"%s\": %m",
DataDir)));
- /* Complain if hugepages demanded but we can't possibly support them */
-#if !defined(MAP_HUGETLB)
- if (huge_pages == HUGE_PAGES_ON)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("huge pages not supported on this platform")));
-#endif
-
/* For now, we don't support huge pages in SysV memory */
if (huge_pages == HUGE_PAGES_ON && shared_memory_type != SHMEM_TYPE_MMAP)
ereport(ERROR,
@@ -782,7 +994,7 @@ PGSharedMemoryCreate(Size size,
if (shared_memory_type == SHMEM_TYPE_MMAP)
{
/* On success, mapping data will be modified. */
- CreateAnonymousSegment(mapping);
+ CreateAnonymousSegment(mapping, base);
next_free_segment++;
diff --git a/src/backend/port/win32_shmem.c b/src/backend/port/win32_shmem.c
index 4dee856d6bd..ce719f1b412 100644
--- a/src/backend/port/win32_shmem.c
+++ b/src/backend/port/win32_shmem.c
@@ -205,7 +205,7 @@ EnableLockPagesPrivilege(int elevel)
*/
PGShmemHeader *
PGSharedMemoryCreate(Size size,
- PGShmemHeader **shim)
+ PGShmemHeader **shim, Pointer base)
{
void *memAddress;
PGShmemHeader *hdr;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 8b38e985327..076888c0172 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -203,9 +203,12 @@ CreateSharedMemoryAndSemaphores(void)
PGShmemHeader *seghdr;
Size size;
int numSemas;
+ void *base;
Assert(!IsUnderPostmaster);
+ base = ReserveAnonymousMemory((Size) MaxAvailableMemory * BLCKSZ);
+
for(int segment = 0; segment < ANON_MAPPINGS; segment++)
{
/* Compute the size of the shared-memory block */
@@ -217,7 +220,7 @@ CreateSharedMemoryAndSemaphores(void)
*
* XXX: Do multiple shims are needed, one per segment?
*/
- seghdr = PGSharedMemoryCreate(size, &shim);
+ seghdr = PGSharedMemoryCreate(size, &shim, base);
/*
* Make sure that huge pages are never reported as "unknown" while the
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 2152aad97d9..1d42a5856c0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -140,6 +140,7 @@ int max_parallel_maintenance_workers = 2;
* register background workers.
*/
int NBuffers = 16384;
+int MaxAvailableMemory = 131072;
int MaxConnections = 100;
int max_worker_processes = 8;
int max_parallel_workers = 8;
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 4eaeca89f2c..dede37f7905 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -2364,6 +2364,20 @@ struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"max_available_memory", PGC_SIGHUP, RESOURCES_MEM,
+ gettext_noop("Sets the upper limit for the shared_buffers value."),
+ gettext_noop("Shared memory could be resized at runtime, this "
+ "parameters sets the upper limit for it, beyond which "
+ "resizing would not be supported. Normally this value "
+ "would be the same as the total available memory."),
+ GUC_UNIT_BLOCKS
+ },
+ &MaxAvailableMemory,
+ 131072, 16, INT_MAX / 2,
+ NULL, NULL, NULL
+ },
+
{
{"vacuum_buffer_usage_limit", PGC_USERSET, RESOURCES_MEM,
gettext_noop("Sets the buffer pool size for VACUUM, ANALYZE, and autovacuum."),
diff --git a/src/include/storage/pg_shmem.h b/src/include/storage/pg_shmem.h
index 138078c29c5..4a83e255652 100644
--- a/src/include/storage/pg_shmem.h
+++ b/src/include/storage/pg_shmem.h
@@ -60,6 +60,7 @@ extern PGDLLIMPORT ShmemSegment Segments[ANON_MAPPINGS];
extern PGDLLIMPORT int shared_memory_type;
extern PGDLLIMPORT int huge_pages;
extern PGDLLIMPORT int huge_page_size;
+extern PGDLLIMPORT int MaxAvailableMemory;
/* Possible values for huge_pages and huge_pages_status */
typedef enum
@@ -100,10 +101,11 @@ extern void PGSharedMemoryNoReAttach(void);
#endif
extern PGShmemHeader *PGSharedMemoryCreate(Size size,
- PGShmemHeader **shim);
+ PGShmemHeader **shim, Pointer base);
extern bool PGSharedMemoryIsInUse(unsigned long id1, unsigned long id2);
extern void PGSharedMemoryDetach(void);
extern void GetHugePageSize(Size *hugepagesize, int *mmap_flags);
+void *ReserveAnonymousMemory(Size reserve_size);
/* The main segment, contains everything except buffer blocks and related data. */
#define MAIN_SHMEM_SEGMENT 0
--
2.45.1
--vninua6xybvzgrci
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename="v4-0003-Introduce-multiple-shmem-segments-for-shared-buff.patch"
^ permalink raw reply [nested|flat] 5+ messages in thread
end of thread, other threads:[~2024-10-16 18:21 UTC | newest]
Thread overview: 5+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2022-05-17 20:28 [PATCH v1 2/3] Allow building only trusted or untrusted PL/Perl. Nathan Bossart <nathandbossart@gmail.com>
2022-05-17 20:28 [PATCH v1 2/3] Allow building only trusted or untrusted PL/Perl. Nathan Bossart <nathandbossart@gmail.com>
2022-05-18 21:33 [PATCH v1 3/3] Allow building only trusted or untrusted PL/Tcl. Nathan Bossart <nathandbossart@gmail.com>
2022-05-18 21:33 [PATCH v1 3/3] Allow building only trusted or untrusted PL/Tcl. Nathan Bossart <nathandbossart@gmail.com>
2024-10-16 18:21 [PATCH v4 2/8] Address space reservation for shared memory Dmitrii Dolgov <9erthalion6@gmail.com>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox